Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,26 @@
'schema' => array( $this, 'get_public_item_schema' ),
)
);

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/(?P<id>[\d]+)/finalize',
Copy link
Member

Choose a reason for hiding this comment

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

The brackets aren't necessary.

Suggested change
'/' . $this->rest_base . '/(?P<id>[\d]+)/finalize',
'/' . $this->rest_base . '/(?P<id>\d+)/finalize',

Copy link
Member

Choose a reason for hiding this comment

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

But I guess the other endpoints have the similar pattern.

array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'finalize_item' ),
'permission_callback' => array( $this, 'finalize_item_permissions_check' ),
Copy link
Member

Choose a reason for hiding this comment

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

Shell we use edit_media_item_permissions_check directly as the new method class that function and return the value?

'args' => array(
'id' => array(
'description' => __( 'Unique identifier for the attachment.' ),
'type' => 'integer',
),
),
),
'allow_batch' => $this->allow_batch,
'schema' => array( $this, 'get_public_item_schema' ),
)
);
}
}

Expand Down Expand Up @@ -2176,4 +2196,63 @@

return $filename;
}

/**
* Checks if a given request has access to finalize an attachment.
*
* @since 7.0.0
*
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has access, WP_Error object otherwise.
*/
public function finalize_item_permissions_check( $request ) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
public function finalize_item_permissions_check( $request ) {
public function finalize_item_permissions_check( WP_REST_Request $request ) {

return $this->edit_media_item_permissions_check( $request );
}

/**
* Finalizes an attachment after client-side media processing.
*
* Triggers the 'wp_generate_attachment_metadata' filter so that
* server-side plugins can process the attachment after all client-side
* operations (upload, thumbnail generation, sideloads) are complete.
*
* @since 7.0.0
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure.
*/
public function finalize_item( WP_REST_Request $request ) {
$attachment_id = $request['id'];

$post = $this->get_post( $attachment_id );

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change

if ( is_wp_error( $post ) ) {
return $post;
}

$metadata = wp_get_attachment_metadata( $attachment_id );

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change

if ( ! is_array( $metadata ) ) {
$metadata = array();
}

/** This filter is documented in wp-admin/includes/image.php */
$metadata = apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id, 'update' );

wp_update_attachment_metadata( $attachment_id, $metadata );
Copy link
Member

Choose a reason for hiding this comment

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

I guess it doesn't make sense to check the return value here for failure since it returns false if the updated post meta is the same.


$response_request = new WP_REST_Request(
WP_REST_Server::READABLE,
rest_get_route_for_post( $attachment_id )
);

$response_request['context'] = 'edit';

if ( isset( $request['_fields'] ) ) {
$response_request['_fields'] = $request['_fields'];
}

return $this->prepare_item_for_response( get_post( $attachment_id ), $response_request );
Copy link
Member

Choose a reason for hiding this comment

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

Can this re-use the $post which was obtained above?

Suggested change
return $this->prepare_item_for_response( get_post( $attachment_id ), $response_request );
return $this->prepare_item_for_response( $post, $response_request );

}

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change

}

Check failure on line 2258 in src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php

View workflow job for this annotation

GitHub Actions / Coding standards / PHP checks

The closing brace for the class must go on the next line after the body
83 changes: 83 additions & 0 deletions tests/phpunit/tests/rest-api/rest-attachments-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -3343,4 +3343,87 @@
$basename = wp_basename( $new_file );
$this->assertMatchesRegularExpression( '/canola-scaled-\d+\.jpg$/', $basename, 'Scaled filename should have numeric suffix when file conflicts with a different attachment.' );
}

/**
* Tests that the finalize endpoint triggers wp_generate_attachment_metadata.
*
* @ticket 62243
* @requires function imagejpeg
*/
public function test_finalize_item() {
wp_set_current_user( self::$author_id );

// Create an attachment.
$request = new WP_REST_Request( 'POST', '/wp/v2/media' );
$request->set_header( 'Content-Type', 'image/jpeg' );
$request->set_header( 'Content-Disposition', 'attachment; filename=canola.jpg' );
$request->set_body( file_get_contents( self::$test_file ) );
$response = rest_get_server()->dispatch( $request );
$attachment_id = $response->get_data()['id'];

$this->assertSame( 201, $response->get_status() );

// Track whether wp_generate_attachment_metadata filter fires.
$filter_called = false;
$filter_context = null;
add_filter(
'wp_generate_attachment_metadata',
function ( $metadata, $id, $context ) use ( &$filter_called, &$filter_context ) {
$filter_called = true;
$filter_context = $context;
return $metadata;
},
10,
3
);

// Call the finalize endpoint.
$request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment_id}/finalize" );
$response = rest_get_server()->dispatch( $request );

$this->assertSame( 200, $response->get_status(), 'Finalize endpoint should return 200.' );
$this->assertTrue( $filter_called, 'wp_generate_attachment_metadata filter should have been called.' );
$this->assertSame( 'update', $filter_context, 'Filter context should be "update".' );
}

/**
* Tests that the finalize endpoint requires authentication.
*
* @ticket 62243
* @requires function imagejpeg
*/
public function test_finalize_item_requires_auth() {
wp_set_current_user( self::$author_id );

// Create an attachment.
$request = new WP_REST_Request( 'POST', '/wp/v2/media' );
$request->set_header( 'Content-Type', 'image/jpeg' );
$request->set_header( 'Content-Disposition', 'attachment; filename=canola.jpg' );
$request->set_body( file_get_contents( self::$test_file ) );
$response = rest_get_server()->dispatch( $request );
$attachment_id = $response->get_data()['id'];

// Try finalizing without authentication.
wp_set_current_user( 0 );

$request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment_id}/finalize" );
$response = rest_get_server()->dispatch( $request );

$this->assertErrorResponse( 'rest_cannot_edit_image', $response, 401 );
}

/**
* Tests that the finalize endpoint returns error for invalid attachment ID.
*
* @ticket 62243
*/
public function test_finalize_item_invalid_id() {
wp_set_current_user( self::$author_id );

$request = new WP_REST_Request( 'POST', '/wp/v2/media/999999/finalize' );
$response = rest_get_server()->dispatch( $request );

$this->assertErrorResponse( 'rest_post_invalid_id', $response, 404 );
}

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change

}

Check failure on line 3429 in tests/phpunit/tests/rest-api/rest-attachments-controller.php

View workflow job for this annotation

GitHub Actions / Coding standards / PHP checks

The closing brace for the class must go on the next line after the body
1 change: 1 addition & 0 deletions tests/phpunit/tests/rest-api/rest-schema-setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public function test_expected_routes_in_schema() {
'/wp/v2/media/(?P<id>[\\d]+)/post-process',
'/wp/v2/media/(?P<id>[\\d]+)/edit',
'/wp/v2/media/(?P<id>[\\d]+)/sideload',
'/wp/v2/media/(?P<id>[\\d]+)/finalize',
'/wp/v2/blocks',
'/wp/v2/blocks/(?P<id>[\d]+)',
'/wp/v2/blocks/(?P<id>[\d]+)/autosaves',
Expand Down
20 changes: 20 additions & 0 deletions tests/qunit/fixtures/wp-api-generated.js
Original file line number Diff line number Diff line change
Expand Up @@ -3718,6 +3718,26 @@ mockedApiResponse.Schema = {
}
]
},
"/wp/v2/media/(?P<id>[\\d]+)/finalize": {
"namespace": "wp/v2",
"methods": [
"POST"
],
"endpoints": [
{
"methods": [
"POST"
],
"args": {
"id": {
"description": "Unique identifier for the attachment.",
"type": "integer",
"required": false
}
}
}
]
},
"/wp/v2/menu-items": {
"namespace": "wp/v2",
"methods": [
Expand Down
Loading