Make WordPress Core

Opened 6 weeks ago

Closed 5 weeks ago

#64804 closed defect (bug) (fixed)

REST API: Add finalize endpoint to `WP_REST_Attachments_Controller` for client-side media processing

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

Description (last modified by westonruter)

When client-side media processing is active, the editor handles image resizing, thumbnail generation, and format conversion in the browser using WebAssembly. This bypasses the wp_generate_attachment_metadata filter that normally fires during wp_generate_attachment_metadata() in wp-admin/includes/image.php.

Many plugins rely on this filter to post-process uploads — for example, adding watermarks, syncing to a CDN, generating custom image sizes, or updating external indexes. When client-side processing is active, these plugins silently stop working because the filter never fires.

Proposed solution

Add a POST /wp/v2/media/{id}/finalize REST API endpoint to WP_REST_Attachments_Controller. This endpoint:

  1. Retrieves the current attachment metadata
  2. Re-applies the wp_generate_attachment_metadata filter with context 'update'
  3. Saves the (potentially modified) metadata back
  4. Returns the updated attachment object

The endpoint is only registered when wp_is_client_side_media_processing_enabled() returns true, so it has zero impact on sites not using client-side processing.

How it works

The client-side media pipeline calls this endpoint after all operations (upload, thumbnail sideloads, scaled image sideload) are complete. No new hooks are needed — the existing wp_generate_attachment_metadata filter is reused. Plugins that already hook into this filter will work without modification; they can distinguish client-side finalization by checking the $context parameter:

<?php
add_filter( 'wp_generate_attachment_metadata', function( $metadata, $attachment_id, $context ) {
    if ( 'update' === $context ) {
        // Called from finalize endpoint after client-side processing.
        my_plugin_process_attachment( $attachment_id, $metadata );
    }
    return $metadata;
}, 10, 3 );

Permissions

The endpoint reuses edit_media_item_permissions_check(), consistent with the existing sideload and edit endpoints. A user must have permission to edit the attachment.

Change History (7)

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


6 weeks ago
#1

  • Keywords has-unit-tests added

## Summary

  • Adds a POST /wp/v2/media/{id}/finalize REST API endpoint to WP_REST_Attachments_Controller
  • The endpoint triggers the wp_generate_attachment_metadata filter with context 'update' after client-side media processing completes
  • Ensures server-side plugins (watermarking, CDN sync, custom sizes, etc.) can post-process attachments when client-side processing is active
  • Only registered when wp_is_client_side_media_processing_enabled() returns true

## Context

When client-side media processing handles image uploads (resizing, thumbnail generation, format conversion), server-side WordPress hooks like wp_generate_attachment_metadata are bypassed. This endpoint is called after all client-side operations (upload, thumbnail generation, sideloads) are complete, re-triggering the filter so plugins continue to work.

### Hook usage example

add_filter( 'wp_generate_attachment_metadata', function( $metadata, $attachment_id, $context ) {
  // $context is 'update' when called from the finalize endpoint.
  if ( 'update' === $context ) {
    // Add watermark, sync to CDN, generate custom sizes, etc.
    my_plugin_process_attachment( $attachment_id, $metadata );
  }
  return $metadata;
}, 10, 3 );

## Changes

File Change
class-wp-rest-attachments-controller.php Add finalize route registration, finalize_item_permissions_check(), and finalize_item() methods
rest-attachments-controller.php (tests) Add tests for finalize endpoint (success, auth required, invalid ID)
rest-schema-setup.php Add finalize route to expected routes list
wp-api-generated.js Add finalize route to API fixtures

## Test plan

  • [ ] Verify finalize endpoint returns 200 with valid attachment
  • [ ] Verify wp_generate_attachment_metadata filter fires with context 'update'
  • [ ] Verify 401 when not authenticated
  • [ ] Verify 404 for invalid attachment ID
  • [ ] Run full REST API test suite

## Related

#2 @westonruter
6 weeks ago

  • Description modified (diff)

#3 @westonruter
6 weeks ago

@adamsilverstein Would it not make sense to just call wp_generate_attachment_metadata() after each image size is sideloaded? That way the metadata will be sure to be created even when no finalize request is made (e.g. the client goes offline). Or is the concern that re-generating the metadata each time would cause relevant plugins to do redundant work?

#4 @adamsilverstein
6 weeks ago

My main goal is to match the existing core hook behavior which I believe means firing once at the end of the process. Some plugins may expect all sub sizes to be in place. Worth reviewing how plugins actually use this hook.

#5 @divyeshpatel01
6 weeks ago

Tested the patch on WordPress trunk.

Applied the patch and verified that the new /wp/v2/media/{id}/finalize endpoint works correctly.

Confirmed that the wp_generate_attachment_metadata filter is trigger with context 'update'.

Unit tests pass and functionality works as expected.

#6 @adamsilverstein
5 weeks ago

  • Keywords commit added

#7 @adamsilverstein
5 weeks ago

  • Owner set to adamsilverstein
  • Resolution set to fixed
  • Status changed from new to closed

In 61982:

REST API: Add finalize endpoint to WP_REST_Attachments_Controller.

Introduce a POST /wp/v2/media/{id}/finalize REST API endpoint that re-triggers the wp_generate_attachment_metadata filter with context 'update' after client-side media processing completes. This ensures server-side plugins (watermarking, CDN sync, custom sizes, etc.) can post-process attachments when client-side processing is active.

The endpoint reuses edit_media_item_permissions_check for authorization and is only registered when wp_is_client_side_media_processing_enabled() returns true.

See https://github.com/WordPress/gutenberg/pull/74913.
See https://github.com/WordPress/gutenberg/issues/74358.

Props adamsilverstein, westonruter, mukesh27, divyeshpatel01.
Fixes #64804.

Note: See TracTickets for help on using tickets.