Re-land the WPFTP feedback export with the review fixes - #3661
Open
maciejpilarski wants to merge 1 commit into
Open
Re-land the WPFTP feedback export with the review fixes#3661maciejpilarski wants to merge 1 commit into
maciejpilarski wants to merge 1 commit into
Conversation
This was referenced Sep 3, 2026
Re-adds the Tools > WPFTP Feedback page and the `wp feedback export` command from #3651, reverted in #3660, with the post-merge review applied: - Date bounds are inclusive and must parse. - The CSV is written verbatim with fputcsv and Jetpack's own esc_csv() guard instead of the shared Export_CSV utility, which rewrote free text. - Status values are validated, --post must be numeric, and an all-test batch is an empty result rather than an error. - New columns: Status and "Logged-in user recorded" (Jetpack only began capturing the submitter in forms package 7.14.0); added columns carry Jetpack's space prefix so they cannot collide with form fields. - CLI: --dir is required and refused inside ABSPATH or WP_CONTENT_DIR, the file is created with 'x' and chmod 0600, the name carries the form and the time, and an empty result exits 0 with a warning. - A dedicated export_feedback_responses capability, granted to administrators, replaces core's export. - Responses are processed in batches of 200 with the Feedback cache cleared between them; the form picker covers every status and shows IDs. Co-authored-by: Destiny Kanno <17694715+Piyopiyo-Kitsune@users.noreply.github.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
maciejpilarski
force-pushed
the
fix/wpftp-feedback-export
branch
from
September 3, 2026 03:19
072a7f6 to
aa3e171
Compare
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
CLI validation can still widen an explicit post filter, and file permissions are not applied atomically.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds secure Jetpack Forms feedback exports with submitter identity through wp-admin and WP-CLI.
Changes:
- Adds batched CSV generation with status, date, form, and user metadata.
- Adds an administrator capability and Tools page.
- Adds a CLI export command with output-directory safeguards.
File summaries
| File | Description |
|---|---|
wporg-learn.php |
Loads the export modules. |
feedback-logged-in-user-export.php |
Implements querying, CSV generation, and admin UI. |
class-feedback-export-cli.php |
Implements the CLI export and file handling. |
capabilities.php |
Grants administrators the export capability. |
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+100
to
+101
| // 'x' refuses to open an existing file, so nothing is ever overwritten. | ||
| $handle = @fopen( $path, 'x' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen, WordPress.PHP.NoSilencedErrors.Discouraged |
Comment on lines
+72
to
+74
| if ( null !== $post && ! ctype_digit( (string) $post ) ) { | ||
| \WP_CLI::error( sprintf( '--post must be a numeric post ID, "%s" given.', $post ) ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Re-lands #3651 (reverted in #3660) with every finding from the post-merge review applied. The feature is unchanged in purpose: a Tools > WPFTP Feedback page and a
wp feedback exportcommand that export Jetpack Forms responses together with the logged-in user who submitted each one, which Jetpack's own CSV export omits. Original work by @Piyopiyo-Kitsune; this PR only changes what the review found.#3660 is merged, so trunk holds none of the original code; this diff is the three plugin files plus two
require_oncelines, and nothing here lands until it is approved.Context
The two WordPress Facilitator Training Program courses run a Jetpack Forms survey asking respondents what role they are in. Matching those self-reported roles against course completions and credentials needs the WordPress.org account behind each response. The review (ten finder angles, verified against the Jetpack Forms source, the shared
Export_CSVmu-plugin utility and WordPress core) found that the merged version altered the exported data and let the CLI widen an export silently. Details in #3660.What changed, by finding
Export content
date_querynow carriesinclusive => true, so aY-m-drange covers its first and last day and a one-day range works.fputcsv, applying only Jetpack'sContact_Form_Plugin::esc_csv()leading-character guard against formula injection. The sharedWordPressdotorg\MU_Plugins\Utilities\Export_CSVis no longer used: its per-cellsanitize_text_field()collapsed paragraph breaks, encoded<and stripped%xx, and its escaping inserted apostrophes before-,+,@and=inside ordinary prose. The response now also sendsContent-Type: text/csv; charset=utf-8, as Jetpack's export does.Status,Logged-in username,Logged-in display name,Logged-in user ID,Logged-in user recorded), so they can never collide with a form field label.logged_in_userkey in their stored content, so an empty username alone cannot tell "not captured" from "not logged in". The column reads whether the key exists.' ' . __( 'ID', 'jetpack-forms' ), with a fall-back to the first column, instead of matching the English literal.WP_Error, so the "No feedback responses found" notice shows instead of awp_die().Arguments that used to widen the export
statusvalues are checked against Jetpack's four real statuses (publish,draft,spam,trash); anything else is rejected.WP_Querydrops the status clause entirely when no requested status is registered, which exported every row.after/beforemust parse; an unparseable bound previously became1970-01-01and matched everything.--postmust be numeric; a non-numeric value previously cast to0, meaning all forms.CLI safety
--diris required, must exist and be writable, and is refused if it lies insideABSPATHorWP_CONTENT_DIR, since the file holds usernames and IP addresses under a predictable name and the current directory was often the web root.fopen( ..., 'x' )andchmod 0600, so it never overwrites and is owner-readable only.all-forms) and the time to the second.Capability and scale
export_feedback_responsescapability, granted to administrators ininc/capabilities.phpalongside the plugin's other custom caps. It can be added to a role explicitly to give a facilitator access without full administrator. The coreexportcapability no longer opens a de-anonymising export.Feedback::clear_cache()between batches and_prime_post_caches()per batch, and the CSV streams to output rather than being built as one string.--posttakes. Untitledjetpack_formparents get a(jetpack_form)label instead of a blank one.Not changed: the form filter still keys on the feedback's
post_parent. Jetpack's newer source-page metadata would need its SQL union filter; the ID in each picker label makes the right value visible in the meantime.Testing
php -lclean on all three files.phpcsclean against this repo'sphpcs.xml.dist(WordPress-Core, Docs, Extra with the repo's exclusions). The onephpcs:ignoreis on thejetpack-formstext domain, which has to match Jetpack's own key.wp feedback export --dir=/tmpin wp-env before merging; the original PR's manual test plan still applies, plus:-after a space comes through unchanged.wp feedback export --status=all --dir=/tmpand--after=nonsenseerror out instead of exporting.wp feedback export --dir=.from the site root is refused.Statuscolumn readsspam.🤖 Generated with Claude Code