Skip to content

Conversation

lezama
Copy link
Contributor

@lezama lezama commented Sep 23, 2025

Proposed changes

This PR replaces the legacy AJAX-based CSV export with a modern REST API endpoint for Jetpack Forms responses.

Changes:

  • Add new /wp/v2/feedback/export REST endpoint for CSV exports
  • Remove legacy AJAX handler wp_ajax_feedback_export and the download_feedback_as_csv() method
  • Update Jetpack Forms dashboard to use REST API with @wordpress/api-fetch
  • Support all existing filters: selected items, search, status, date ranges, and source/parent

Other information

Testing instructions

  1. Navigate to Jetpack → Forms in WP Admin
  2. Test exporting with various filters:
    • Export all responses (no selection)
    • Select specific responses and export
    • Filter by source/form and export
    • Apply search filters and export
    • Filter by date range and export
  3. Verify CSV downloads correctly with expected data
  4. Confirm permission checks work (test with non-admin user)

Product impact

  • No user-facing changes - export functionality remains the same
  • Improved reliability and error handling for exports
  • Better compatibility with modern WordPress environments

Does this pull request change what data or activity we track or use?

No

Checklist

  • I have written a clear PR title and description
  • I have tested my changes
  • My code follows the WordPress coding standards
  • My code passes the linter

Copy link
Contributor

github-actions bot commented Sep 23, 2025

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack), and enable the forms/add-rest-export-endpoint branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack forms/add-rest-export-endpoint

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@github-actions github-actions bot added [Block] Contact Form Form block (also see Contact Form label) [Feature] Contact Form [Package] Forms [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ labels Sep 23, 2025
Copy link
Contributor

github-actions bot commented Sep 23, 2025

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • 🔴 Add a "[Status]" label (In Progress, Needs Review, ...).
  • 🔴 Add a "[Type]" label (Bug, Enhancement, Janitorial, Task).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!


Jetpack plugin:

The Jetpack plugin has different release cadences depending on the platform:

  • WordPress.com Simple releases happen as soon as you deploy your changes after merging this PR (PCYsg-Jjm-p2).
  • WoA releases happen weekly.
  • Releases to self-hosted sites happen monthly:
    • Scheduled release: October 7, 2025
    • Code freeze: October 6, 2025

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.

@github-actions github-actions bot added the [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. label Sep 23, 2025
@lezama lezama force-pushed the forms/add-rest-export-endpoint branch from 49302ee to 0ff05d9 Compare September 23, 2025 01:09
@lezama lezama requested a review from a team September 23, 2025 01:10
Copy link

jp-launch-control bot commented Sep 23, 2025

Code Coverage Summary

Coverage changed in 4 files.

File Coverage Δ% Δ Uncovered
projects/packages/forms/src/contact-form/class-contact-form-endpoint.php 666/755 (88.21%) -0.59% 16 💔
projects/packages/forms/src/contact-form/class-contact-form-plugin.php 425/1318 (32.25%) -0.24% 12 💔
projects/packages/forms/src/dashboard/hooks/use-export-responses.ts 0/44 (0.00%) 0.00% 2 ❤️‍🩹
projects/packages/forms/src/dashboard/inbox/export-responses/csv.tsx 0/7 (0.00%) 0.00% -9 💚

Full summary · PHP report · JS report

If appropriate, add one of these labels to override the failing coverage check: Covered by non-unit tests Use to ignore the Code coverage requirement check when E2Es or other non-unit tests cover the code Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR I don't care about code coverage for this PR Use this label to ignore the check for insufficient code coveage.

@manzoorwanijk
Copy link
Member

Isn't it a convention that the REST endpoints always return JSON?

What was the problem with AJAX method that we are trying to solve here?

We could also make use of the admin_post_{$action} action.

@manzoorwanijk
Copy link
Member

Core REST API Handbook mentions

The REST API uses JSON exclusively as the request and response format, including error responses.

It also says

The WordPress REST API provides an interface for applications to interact with your WordPress site by sending and receiving data as JSON (JavaScript Object Notation) objects.

@lezama
Copy link
Contributor Author

lezama commented Sep 23, 2025

@manzoorwanijk Thanks for the feedback. You are right about REST conventions.

In this version, we return JSON for errors and CSV on success. While admin_post_{$action} is conventional, clients using @wordpress/api-fetch benefit from REST’s built-in auth handling, instead of adding boilerplate for an AJAX nonce. Is there a helper or way to use existing AJAX endpoints within @wordpress packages?

@lezama lezama changed the title Forms: Add REST API endpoint for exporting responses WIP: Forms: Add REST API endpoint for exporting responses Sep 23, 2025
@lezama lezama force-pushed the forms/add-rest-export-endpoint branch from 233fb69 to 06c5d76 Compare September 23, 2025 18:36
@lezama lezama changed the title WIP: Forms: Add REST API endpoint for exporting responses Forms: Add REST API endpoint for exporting responses Sep 23, 2025
@enejb
Copy link
Member

enejb commented Sep 23, 2025

I tested this and it works as expected. 🥳

I tested exporting.
selected, all, inbox, spam, trash.
Filters by selected date, by selected source.

Exporting Google Sheets ( for regressions )

It would be good to add an endpoint test for this PR. So that we have something that checks for regressions.

@manzoorwanijk
Copy link
Member

In this version, we return JSON for errors and CSV on success. While admin_post_{$action} is conventional, clients using @wordpress/api-fetch benefit from REST’s built-in auth handling, instead of adding boilerplate for an AJAX nonce. Is there a helper or way to use existing AJAX endpoints within @wordpress packages?

If you wish to laverage the REST authentication for api-fetch, then you can return a URL to admin-post action mentioned above. So, it can pass only the $feedback_ids and $post_id to the admin post which should make the admin post action simpler. You can also add a nonce to the URL.

Then update Contact_Form_Plugin::download_feedback_as_csv() to be a callback to admin_post_feedback_export, which verifies the nonce and receives the feedback IDs to export, which should be easy to sanitize. Then in API fetch, you can pass the content-type header or parse: false as you already did when you try to download the file.

@lezama
Copy link
Contributor Author

lezama commented Sep 24, 2025

Thanks for the feedback @manzoorwanijk.

Updated the implementation to follow your suggestion about using admin-post for file downloads. The REST endpoint now returns JSON with a download_url that points to an admin-post handler.

@lezama lezama requested review from enejb and a team September 24, 2025 13:01
@enejb
Copy link
Member

enejb commented Sep 24, 2025

I retested this changes and it works well. It would be good to show some sort of in progress button while the URL is being returned. This will help the user not click the button multiple times.

Lets add a test for the new endpoint and then I think this is ready to ship! Nice work.

Copy link
Member

@manzoorwanijk manzoorwanijk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for implementing the suggestions. This looks much better now. I have some further suggestions for improvement, some of which you may ignore if you prefer.

$data = $this->get_feedback_entries_from_post();
public function admin_post_feedback_export() {
$feedback_ids_str = sanitize_text_field( wp_unslash( $_GET['feedback_ids'] ?? '' ) );
$post_id = sanitize_text_field( wp_unslash( $_GET['post_id'] ?? '' ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't pass the post_id when generating the URL from the REST endpoint. Do we?

window.location.href = response.download_url;
return response;
}
throw new Error( 'Invalid response: missing download URL' );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to translate this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say not yet since it would only show up in the dev console for now.

return new WP_Error( 'no_responses', __( 'No responses found', 'jetpack-forms' ), array( 'status' => 404 ) );
}

$feedback_ids = wp_list_pluck( $feedback_posts, 'ID' );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add 'fields' => 'ids' to $query_args above to get rid of this step and make it a bit faster.

@simison
Copy link
Member

simison commented Sep 25, 2025

It would be good to show some sort of in progress button while the URL is being returned. This will help the user not click the button multiple times.

Button has disabled and isBusy states for this purpose, here's a PR adding isExporting flag on top of this PR, you can either merge into this or deploy separately, how you prefer:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Contact Form Form block (also see Contact Form label) [Feature] Contact Form [Package] Forms [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. [Tests] Includes Tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants