Release - #2927
Conversation
feat: support XML and CSV exports for form submissions
Bundle Size Diff
|
There was a problem hiding this comment.
Pull request overview
Adds CSV export support for stored form submissions while preserving WordPress XML as the default.
Changes:
- Adds CSV generation and format selection.
- Introduces an accessible export-format dropdown.
- Expands unit and E2E export coverage.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
inc/plugins/class-form-records-export.php |
Generates XML or CSV exports. |
inc/plugins/class-dashboard.php |
Adds the export-format UI and download handling. |
tests/test-form-submissions.php |
Tests XML defaults and CSV output. |
src/blocks/test/e2e/blocks/form.spec.js |
Tests XML and CSV downloads. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if ( ! isset( $input_columns[ $label ] ) ) { | ||
| $input_columns[ $column_key ] = $label; | ||
| } |
| if ( 'file' === $input['type'] && isset( $input['metadata']['name'] ) ) { | ||
| $value = $input['metadata']['name']; | ||
| } | ||
|
|
||
| $row[ $column_key ] = $value; |
| item.addEventListener('click', () => { | ||
| runExport(item.dataset.format); | ||
| closeMenu(); | ||
| }); |
| $this->assertStringContainsString( 'Name', $output ); | ||
| $this->assertStringContainsString( 'Company', $output ); | ||
| $this->assertStringContainsString( 'Ada Lovelace', $output ); | ||
| $this->assertStringContainsString( 'Analytical Engines Ltd', $output ); |
|
Plugin build for cd31288 is ready 🛎️!
|
E2E TestsPlaywright Test Status: See serial and parallel matrix jobs Performance ResultsserverResponse: {"q25":437.9,"q50":464.75,"q75":470.5,"cnt":10}, firstPaint: {"q25":1465.6,"q50":1644.9,"q75":1893.3,"cnt":10}, domContentLoaded: {"q25":3786.1,"q50":3828.5,"q75":3881.4,"cnt":10}, loaded: {"q25":3788.1,"q50":3830.4,"q75":3883.2,"cnt":10}, firstContentfulPaint: {"q25":4325.6,"q50":4368.5,"q75":4404.6,"cnt":10}, firstBlock: {"q25":14964.9,"q50":15028.4,"q75":15105.7,"cnt":10}, type: {"q25":27.76,"q50":29.64,"q75":31.08,"cnt":10}, typeWithoutInspector: {"q25":26.28,"q50":28.3,"q75":30.14,"cnt":10}, typeWithTopToolbar: {"q25":35.01,"q50":36.71,"q75":40.8,"cnt":10}, typeContainer: {"q25":17.13,"q50":18.76,"q75":19.66,"cnt":10}, focus: {"q25":141.84,"q50":151.3,"q75":156.16,"cnt":10}, inserterOpen: {"q25":43.29,"q50":46.09,"q75":50.72,"cnt":10}, inserterSearch: {"q25":17.04,"q50":18,"q75":20.18,"cnt":10}, inserterHover: {"q25":5.29,"q50":5.8,"q75":6.46,"cnt":20}, loadPatterns: {"q25":1871.19,"q50":1903.54,"q75":1945.04,"cnt":10}, listViewOpen: {"q25":261.09,"q50":285.68,"q75":293.05,"cnt":10} |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (6)
inc/plugins/class-form-records-export.php:116
- This second
fputcsv()call also relies on the escape default deprecated in PHP 8.4, potentially emitting a deprecation into the download. Supply the escape parameter explicitly here as well.
$output->fputcsv( $line );
inc/plugins/class-dashboard.php:834
- After a keyboard user activates a format option,
closeMenu()hides the element that still owns focus, leaving focus inside hidden content. Move focus back to the toggle after closing the menu so keyboard and screen-reader navigation resumes at a visible control.
item.addEventListener('click', () => {
runExport(item.dataset.format);
closeMenu();
});
inc/plugins/class-dashboard.php:806
Response.text()decodes and consumes the UTF-8 BOM written by the CSV exporter, so rebuilding the download from that string drops the BOM and defeats the Excel encoding safeguard. Keep the response as bytes (for example, viaresponse.blob()) before assigning the CSV MIME type.
const isCsv = 'csv' === format;
const blob = new Blob([response], {type: isCsv ? 'text/csv;charset=utf-8' : 'text/xml'});
inc/plugins/class-form-records-export.php:284
- Truncating the post ID makes the exported
IDinaccurate once a site reaches IDs above eight digits and can create collisions between submissions. The record ID should be exported in full.
'id' => substr( strval( $record->ID ), -8 ),
inc/plugins/class-form-records-export.php:322
- This key collapses every input sharing a label. In particular, a multiple-file field stores one input per uploaded file with the same label (
src/blocks/frontend/form/index.js:103-118), so each assignment overwrites the previous filename and the CSV silently exports only the last file. Preserve repeated values by aggregating them or by using/disambiguating the stored input IDs.
$parsed[ 'input:' . $input['label'] ] = array(
'label' => $input['label'],
'value' => $value,
);
inc/plugins/class-form-records-export.php:102
- PHP 8.4 deprecates relying on
fputcsv()'s default escape parameter; with displayed deprecations this can also corrupt the generated CSV before its header. Pass an explicit empty escape string, which is supported by the project's PHP 7.4 minimum and produces standards-compliant CSV escaping.
This issue also appears on line 116 of the same file.
$output->fputcsv( array_map( array( $this, 'sanitize_cell' ), array_values( $columns ) ) );
Bumps [codeinwp/themeisle-sdk](https://github.com/Codeinwp/themeisle-sdk) from 3.3.54 to 3.3.58. - [Release notes](https://github.com/Codeinwp/themeisle-sdk/releases) - [Changelog](https://github.com/Codeinwp/themeisle-sdk/blob/v3.3.58/CHANGELOG.md) - [Commits](Codeinwp/themeisle-sdk@v3.3.54...v3.3.58) --- updated-dependencies: - dependency-name: codeinwp/themeisle-sdk dependency-version: 3.3.58 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [codeinwp/themeisle-sdk](https://github.com/Codeinwp/themeisle-sdk) from 3.3.54 to 3.3.58. - [Release notes](https://github.com/Codeinwp/themeisle-sdk/releases) - [Changelog](https://github.com/Codeinwp/themeisle-sdk/blob/v3.3.58/CHANGELOG.md) - [Commits](Codeinwp/themeisle-sdk@v3.3.54...v3.3.58) --- updated-dependencies: - dependency-name: codeinwp/themeisle-sdk dependency-version: 3.3.58 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [codeinwp/themeisle-sdk](https://github.com/Codeinwp/themeisle-sdk) from 3.3.54 to 3.3.58. - [Release notes](https://github.com/Codeinwp/themeisle-sdk/releases) - [Changelog](https://github.com/Codeinwp/themeisle-sdk/blob/v3.3.58/CHANGELOG.md) - [Commits](Codeinwp/themeisle-sdk@v3.3.54...v3.3.58) --- updated-dependencies: - dependency-name: codeinwp/themeisle-sdk dependency-version: 3.3.58 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
…t/codeinwp/themeisle-sdk-3.3.58 build(deps): bump codeinwp/themeisle-sdk from 3.3.54 to 3.3.58
…ocks-animation/development/codeinwp/themeisle-sdk-3.3.58 build(deps): bump codeinwp/themeisle-sdk from 3.3.54 to 3.3.58 in /plugins/blocks-animation
…ocks-css/development/codeinwp/themeisle-sdk-3.3.58 build(deps): bump codeinwp/themeisle-sdk from 3.3.54 to 3.3.58 in /plugins/blocks-css
There was a problem hiding this comment.
🟡 Not ready to approve
CSV byte handling and repeated-field serialization can remove encoding metadata or drop submitted values.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Suppressed comments (3)
inc/plugins/class-form-records-export.php:322
- Keying parsed inputs only by their label overwrites earlier values with the same label. A multi-file field is stored as one input per file with the same label, so this export currently keeps only the last filename; separate fields with duplicate labels lose data the same way. Merge repeated values (or otherwise disambiguate them) instead of replacing the previous entry.
$parsed[ 'input:' . $input['label'] ] = array(
'label' => $input['label'],
'value' => $value,
);
inc/plugins/class-dashboard.php:806
- The response is decoded with
response.text()before this Blob is created, which consumes the UTF-8 BOM written byexport_csv(). The downloaded CSV therefore no longer has the BOM intended to make Excel detect accented text correctly. Preserve the response bytes instead of decoding and re-encoding them.
const blob = new Blob([response], {type: isCsv ? 'text/csv;charset=utf-8' : 'text/xml'});
inc/plugins/class-form-records-export.php:102
- These
fputcsv()calls inherit PHP's default backslash escape character. Submitted text containing a backslash before a quote can then be emitted as non-standard CSV and be misparsed by Excel or other RFC-style readers; relying on the default is also deprecated in PHP 8.4. Configure an empty escape character once on the stream before writing rows.
$output->fputcsv( array_map( array( $this, 'sanitize_cell' ), array_values( $columns ) ) );
- Files reviewed: 4/7 changed files
- Comments generated: 0 new
- Review effort level: Balanced
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
Linked issues
This release will close the following issues once merged:
Public changelog