Make WordPress Core

Opened 24 hours ago

Last modified 2 hours ago

#65367 new defect (bug)

REST API: expose per-attachment output format and progressive flags for client-side media processing

Reported by: adamsilverstein's profile adamsilverstein Owned by:
Milestone: 7.1 Priority: normal
Severity: normal Version:
Component: REST API Keywords: has-patch has-unit-tests
Focuses: Cc:

Description

Client-side media processing encodes sub-sizes (and, when applicable, the threshold-scaled "scaled" copy) in the browser before uploading. To match server-side output, the browser needs two pieces of information that today are not exposed on the attachment response:

  • The output MIME type a given file should be saved as, per the image_editor_output_format filter. The filter is filename- and MIME-aware (( array $formats, string $filename, string $mime_type )), so a value computed in the REST index without a real filename is incorrect for any non-trivial site.
  • Whether progressive / interlaced encoding is requested for that MIME type, per the image_save_progressive filter.

Today these values are surfaced (in Gutenberg) on the REST API root index with empty filename and image_save_progressive evaluated against hard-coded MIME types. That is wrong: the filter contract requires the real filename and MIME of the file being saved, and plugins can return different decisions per file. The bypass-labelled Gutenberg PR #75793 moved this information to the per-attachment upload response in the Gutenberg plugin; this ticket backports the same change to Core.

Proposed change

Add two readonly fields to WP_REST_Attachments_Controller, in the edit
context, alongside the existing exif_orientation field:

"image_output_format": "image/webp",
"image_save_progressive": false
  • image_output_format returns the resolved output MIME type when image_editor_output_format maps the source MIME to a different one (e.g. JPEG → WebP), or null when no conversion is needed. The filter is called with the real attached filename and MIME type, so plugins can make per-file decisions.
  • image_save_progressive returns the boolean result of the image_save_progressive filter for the attachment's MIME type.
  • Both fields are evaluated only for image attachments (wp_attachment_is_image()).
  • prepare_item_for_response() and the upload / sideload code paths recompute the values after lifting the temporary __return_empty_array / __return_zero guards that the controller installs around the initial response, so the values surfaced reflect the site's real configuration.

This deprecates the legacy generic surfacing of image_output_formats, jpeg_interlaced, png_interlaced, and gif_interlaced on the REST API root index (which used empty filenames and hard-coded MIME types). Those entries should be removed when the new per-attachment fields land.

Patch / PRs

Tests

New coverage in tests/phpunit/tests/rest-api/rest-attachments-controller.php:

  • test_image_output_format_schema - field is present, nullable string, readonly, edit-context.
  • test_image_save_progressive_schema - field is present, boolean, readonly, edit-context.
  • test_image_output_format_default - returns null when no filter remaps the source MIME.
  • test_image_output_format_with_filter - JPEG→WebP filter is reflected in the response and uses the real attached filename.
  • test_image_save_progressive_default - returns false for JPEG with no filter.
  • test_image_save_progressive_with_filter - a MIME-aware image_save_progressive filter is honored.
  • test_get_item_schema - property count updated to match the two new fields.
  • Existing sideload / finalize test coverage extended to assert the recomputed values after the controller's internal guards are removed.

Change History (3)

This ticket was mentioned in PR #12007 on WordPress/wordpress-develop by @adamsilverstein.


24 hours ago
#1

  • Keywords has-patch has-unit-tests added; needs-patch needs-unit-tests removed

## Summary

Adds two readonly fields to WP_REST_Attachments_Controller, in the edit context, alongside the existing exif_orientation field:

  • image_output_format — returns the resolved output MIME type when image_editor_output_format maps the source MIME to a different one (e.g. JPEG → WebP), or null when no conversion is needed. The filter is invoked with the real attached filename and MIME type so plugins can make per-file decisions, mirroring the way WP_Image_Editor::set_quality() resolves the output format.
  • image_save_progressive — returns the boolean result of the image_save_progressive filter for the attachment's MIME type.

Both fields are evaluated only for image attachments (wp_attachment_is_image()).

## Why

Client-side media processing encodes sub-sizes (and the threshold-scaled "scaled" copy) in the browser. To match server-side output, the browser needs to know the output MIME type and whether to use progressive encoding for the specific file being saved. Those decisions are filename- and MIME-aware (image_editor_output_format signature is ( array $formats, string $filename, string $mime_type )), so values computed at the REST API root index — without a real filename — are incorrect for any non-trivial site.

Putting the resolved values on the per-attachment response gives the client real context to act on, mirroring the existing exif_orientation pattern.

## Trac ticket

https://core.trac.wordpress.org/ticket/65367

## Backport from Gutenberg

## Tests

New coverage in tests/phpunit/tests/rest-api/rest-attachments-controller.php:

  • test_image_output_format_and_progressive_schema — both fields are present, typed correctly, readonly, edit-context only.
  • test_image_output_format_and_progressive_defaults_in_create_response — JPEG default response has image_output_format = null and image_save_progressive = false.
  • test_image_output_format_with_custom_filter — a JPEG→WebP image_editor_output_format filter is reflected in the response, and the filter sees the real attached filename and MIME type.
  • test_image_save_progressive_with_custom_filter — a MIME-aware image_save_progressive filter is honored.
  • test_image_output_format_skipped_for_non_image — non-image attachments do not surface the fields.
  • Schema property count updated from 32 to 34.

Local results: WP_Test_REST_Attachments_Controller runs green (134 tests, 809 assertions, 2 skipped). PHPCS clean.

## Follow-up

A follow-up could remove the now-redundant index-level image_output_formats, jpeg_interlaced, png_interlaced, gif_interlaced fields from WP_REST_Server::get_index(), but that needs coordinated updates to JS preloads in site-editor.php and edit-form-blocks.php and is left for a separate change.

@adamsilverstein commented on PR #12007:


23 hours ago
#2

Added a follow-up commit that completes the #75793 migration in Core: it removes the now-redundant, file-less image_output_formats, jpeg_interlaced, png_interlaced, and gif_interlaced keys from WP_REST_Server::get_index() (and updates the generated qunit fixture).

Those values were exposed on the REST root index without per-file context, so image_editor_output_format ran with an empty filename and couldn't make per-file decisions. They're now superseded by the per-attachment image_output_format / image_save_progressive response fields added in this PR. image_sizes and image_size_threshold remain on the index since they don't need file context.

This also resolves the failing Gutenberg media processing test (the plugin asserts those keys are absent from the index). A temporary stopgap that disables those assertions until this merges is in WordPress/gutenberg#78788.

#3 @westonruter
2 hours ago

  • Milestone changed from Awaiting Review to 7.1
Note: See TracTickets for help on using tickets.