Ticket #47987: 47987.4.diff
File 47987.4.diff, 14.1 KB (added by , 2 months ago) |
---|
-
src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php
diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index c1be06a2a0..16d1749bae 100644
a b 16 16 */ 17 17 class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 18 18 19 public function register_routes() { 20 parent::register_routes(); 21 register_rest_route( 22 $this->namespace, 23 '/' . $this->rest_base . '/(?P<id>[\d]+)/post-process', 24 array( 25 'methods' => WP_REST_Server::CREATABLE, 26 'callback' => array( $this, 'post_process_item' ), 27 'permission_callback' => array( $this, 'post_process_item_permissions_check' ), 28 'args' => array_merge( 29 array( 30 'id' => array( 31 'description' => __( 'Unique identifier for the object.' ), 32 'type' => 'integer', 33 ), 34 ), 35 $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE, $this->get_post_process_schema() ) 36 ), 37 'schema' => array( $this, 'get_post_process_schema' ), 38 ) 39 ); 40 } 41 19 42 /** 20 43 * Determines the allowed query_vars for a get_items() response and 21 44 * prepares for WP_Query. … … class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 100 123 return new WP_Error( 'rest_invalid_param', __( 'Invalid parent type.' ), array( 'status' => 400 ) ); 101 124 } 102 125 126 $attachment_id = $request->get_header( 'X-WP-PostProcess' ); 127 128 if ( $attachment_id ) { 129 $attachment = get_post( $attachment_id ); 130 131 if ( ! $attachment ) { 132 return new WP_Error( 'rest_post_process_not_found', __( 'The attached file cannot be found.' ), array( 'status' => 400 ) ); 133 } 134 // Try to create image sub-sizes again. 135 // This can still be pretty slow and cause timeout or out of memory errors. 136 // The client would need to also handle HTTP 500 responses. 137 require_once ABSPATH . 'wp-admin/includes/image.php'; 138 wp_update_image_subsizes( $attachment_id ); 139 } else { 140 // Initial upload request. 141 $insert = $this->insert_attachment( $request ); 142 $set_ref = null; 143 144 if ( is_wp_error( $insert ) ) { 145 return $insert; 146 } 147 148 // Extract by name. 149 $attachment_id = $insert['attachment_id']; 150 $file = $insert['file']; 151 152 if ( isset( $request['alt_text'] ) ) { 153 update_post_meta( $attachment_id, '_wp_attachment_image_alt', sanitize_text_field( $request['alt_text'] ) ); 154 } 155 156 $attachment = get_post( $attachment_id ); 157 $fields_update = $this->update_additional_fields_for_object( $attachment, $request ); 158 159 if ( is_wp_error( $fields_update ) ) { 160 return $fields_update; 161 } 162 163 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { 164 // Set a custom header with the attachment_id. 165 // Used by the browser/client to resume creating image sub-sizes after a PHP fatal error. 166 header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id ); 167 } 168 169 // Include admin function to get access to wp_generate_attachment_metadata(). 170 require_once ABSPATH . 'wp-admin/includes/media.php'; 171 172 // Post-process the upload (create image sub-sizes, make PDF thumbnalis, etc.) and insert attachment meta. 173 // At this point the server may run out of resources and post-processing of uploaded images may fail. 174 wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file ) ); 175 } 176 177 // The following runs on initial upload requests and after successfully re-trying to create image sub-sizes. 178 // At this point the file has been uploaded and post-processed successfully. 179 $request->set_param( 'context', 'edit' ); 180 181 /** 182 * Fires after a single attachment is completely created or updated via the REST API. 183 * 184 * @since 5.0.0 185 * 186 * @param WP_Post $attachment Inserted or updated attachment object. 187 * @param WP_REST_Request $request Request object. 188 * @param bool $creating True when creating an attachment, false when updating. 189 */ 190 do_action( 'rest_after_insert_attachment', $attachment, $request, true ); 191 192 $response = $this->prepare_item_for_response( $attachment, $request ); 193 $response = rest_ensure_response( $response ); 194 $response->set_status( 201 ); 195 $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $attachment_id ) ) ); 196 197 return $response; 198 } 199 200 /** 201 * Inserts the attachment post in the database. Does not update the attachment meta. 202 * 203 * @since 5.3.0 204 * 205 * @param WP_REST_Request $request 206 * @return array|WP_Error 207 */ 208 protected function insert_attachment( $request ) { 103 209 // Get the file via $_FILES or raw data. 104 210 $files = $request->get_file_params(); 105 211 $headers = $request->get_headers(); … … class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 138 244 } 139 245 } 140 246 141 $attachment = $this->prepare_item_for_database( $request ); 247 $attachment = $this->prepare_item_for_database( $request ); 248 142 249 $attachment->post_mime_type = $type; 143 250 $attachment->guid = $url; 144 251 … … class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 155 262 } else { 156 263 $id->add_data( array( 'status' => 400 ) ); 157 264 } 265 158 266 return $id; 159 267 } 160 268 … … class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 172 280 */ 173 281 do_action( 'rest_insert_attachment', $attachment, $request, true ); 174 282 175 // Include admin function to get access to wp_generate_attachment_metadata(). 176 require_once ABSPATH . 'wp-admin/includes/media.php'; 177 178 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) ); 179 180 if ( isset( $request['alt_text'] ) ) { 181 update_post_meta( $id, '_wp_attachment_image_alt', sanitize_text_field( $request['alt_text'] ) ); 182 } 183 184 $fields_update = $this->update_additional_fields_for_object( $attachment, $request ); 185 186 if ( is_wp_error( $fields_update ) ) { 187 return $fields_update; 188 } 189 190 $request->set_param( 'context', 'edit' ); 191 192 /** 193 * Fires after a single attachment is completely created or updated via the REST API. 194 * 195 * @since 5.0.0 196 * 197 * @param WP_Post $attachment Inserted or updated attachment object. 198 * @param WP_REST_Request $request Request object. 199 * @param bool $creating True when creating an attachment, false when updating. 200 */ 201 do_action( 'rest_after_insert_attachment', $attachment, $request, true ); 202 203 $response = $this->prepare_item_for_response( $attachment, $request ); 204 $response = rest_ensure_response( $response ); 205 $response->set_status( 201 ); 206 $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $id ) ) ); 207 208 return $response; 283 return array( 284 'attachment_id' => $id, 285 'file' => $file, 286 ); 209 287 } 210 288 211 289 /** … … class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 253 331 return $response; 254 332 } 255 333 334 /** 335 * Performs post processing on an attachment. 336 * 337 * @since 5.3.0 338 * 339 * @param WP_REST_Request $request 340 * @return WP_REST_Response|WP_Error 341 */ 342 public function post_process_item( $request ) { 343 switch ( $request['action'] ) { 344 case 'create-image-subsizes': 345 require_once ABSPATH . 'wp-admin/includes/image.php'; 346 wp_update_image_subsizes( $request['id'] ); 347 break; 348 } 349 350 $request['context'] = 'edit'; 351 352 return $this->prepare_item_for_response( get_post( $request['id'] ), $request ); 353 } 354 355 /** 356 * Checks if a given request can perform post processing on an attachment. 357 * 358 * @sicne 5.3.0 359 * 360 * @param WP_REST_Request $request Full details about the request. 361 * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise. 362 */ 363 public function post_process_item_permissions_check( $request ) { 364 return $this->update_item_permissions_check( $request ); 365 } 366 367 /** 368 * Gets the schema for post processing. 369 * 370 * @since 5.3.0 371 * 372 * @return array 373 */ 374 public function get_post_process_schema() { 375 return array( 376 '$schema' => 'http://json-schema.org/draft-04/schema#', 377 'title' => 'attachment-post-process', 378 'type' => 'object', 379 'properties' => array( 380 'action' => array( 381 'type' => 'string', 382 'enum' => array( 'create-image-subsizes' ), 383 'required' => true, 384 ), 385 ), 386 ); 387 } 388 256 389 /** 257 390 * Prepares a single attachment for create or update. 258 391 * … … class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 380 513 $data['source_url'] = wp_get_attachment_url( $post->ID ); 381 514 } 382 515 516 if ( in_array( 'missing_image_sizes', $fields, true ) ) { 517 require_once ABSPATH . 'wp-admin/includes/image.php'; 518 $data['missing_image_sizes'] = array_keys( wp_get_missing_image_subsizes( $post->ID ) ); 519 } 520 383 521 $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; 384 522 385 523 $data = $this->filter_response_by_context( $data, $context ); … … class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 514 652 'readonly' => true, 515 653 ); 516 654 655 $schema['properties']['missing_image_sizes'] = array( 656 'description' => __( 'List of the missing image sizes of the attachment.' ), 657 'type' => 'array', 658 'items' => array( 'type' => 'string' ), 659 'context' => array( 'edit' ), 660 'readonly' => true, 661 ); 662 517 663 unset( $schema['properties']['password'] ); 518 664 519 665 $this->schema = $schema; -
src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php
diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php index 1bc38124e9..d02e91bdc2 100644
a b abstract class WP_REST_Controller { 587 587 * Retrieves an array of endpoint arguments from the item schema for the controller. 588 588 * 589 589 * @since 4.7.0 590 * @since 5.3.0 Added the $schema parameter. 590 591 * 591 592 * @param string $method Optional. HTTP method of the request. The arguments for `CREATABLE` requests are 592 593 * checked for required values and may fall-back to a given default, this is not done 593 594 * on `EDITABLE` requests. Default WP_REST_Server::CREATABLE. 595 * @param array $schema Optional. Specify the schema to use instead of the item schema. 594 596 * @return array Endpoint arguments. 595 597 */ 596 public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CREATABLE ) { 598 public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CREATABLE, $schema = array() ) { 599 600 if ( ! $schema ) { 601 $schema = $this->get_item_schema(); 602 } 597 603 598 $schema = $this->get_item_schema();599 604 $schema_properties = ! empty( $schema['properties'] ) ? $schema['properties'] : array(); 600 605 $endpoint_args = array(); 601 606 -
tests/phpunit/tests/rest-api/rest-attachments-controller.php
diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php index 556cc6c8be..dc0b61835d 100644
a b class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control 1327 1327 $response = rest_get_server()->dispatch( $request ); 1328 1328 $data = $response->get_data(); 1329 1329 $properties = $data['schema']['properties']; 1330 $this->assertEquals( 2 6, count( $properties ) );1330 $this->assertEquals( 27, count( $properties ) ); 1331 1331 $this->assertArrayHasKey( 'author', $properties ); 1332 1332 $this->assertArrayHasKey( 'alt_text', $properties ); 1333 1333 $this->assertArrayHasKey( 'caption', $properties ); … … class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control 1360 1360 $this->assertArrayHasKey( 'raw', $properties['title']['properties'] ); 1361 1361 $this->assertArrayHasKey( 'rendered', $properties['title']['properties'] ); 1362 1362 $this->assertArrayHasKey( 'type', $properties ); 1363 $this->assertArrayHasKey( 'missing_image_sizes', $properties ); 1363 1364 } 1364 1365 1365 1366 public function test_get_additional_field_registration() { -
tests/phpunit/tests/rest-api/rest-schema-setup.php
diff --git a/tests/phpunit/tests/rest-api/rest-schema-setup.php b/tests/phpunit/tests/rest-api/rest-schema-setup.php index 5c780011d3..51abf95fc1 100644
a b class WP_Test_REST_Schema_Initialization extends WP_Test_REST_TestCase { 99 99 '/wp/v2/pages/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)', 100 100 '/wp/v2/media', 101 101 '/wp/v2/media/(?P<id>[\\d]+)', 102 '/wp/v2/media/(?P<id>[\\d]+)/post-process', 102 103 '/wp/v2/blocks', 103 104 '/wp/v2/blocks/(?P<id>[\d]+)', 104 105 '/wp/v2/blocks/(?P<id>[\d]+)/autosaves', -
tests/qunit/fixtures/wp-api-generated.js
diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 3083545431..b8217e729b 100644
a b mockedApiResponse.Schema = { 2304 2304 } 2305 2305 ] 2306 2306 }, 2307 "/wp/v2/media/(?P<id>[\\d+])/post-process": { 2308 "namespace": "wp/v2", 2309 "methods": [ 2310 "POST" 2311 ], 2312 "endpoints": [ 2313 { 2314 "methods": [ 2315 "POST" 2316 ], 2317 "args": { 2318 "id": { 2319 "required": false, 2320 "description": "Unique identifier for the object.", 2321 "type": "integer" 2322 }, 2323 "action": { 2324 "required": true, 2325 "enum": [ 2326 "create-image-subsizes" 2327 ], 2328 "type": "string" 2329 } 2330 } 2331 } 2332 ] 2333 }, 2307 2334 "/wp/v2/blocks": { 2308 2335 "namespace": "wp/v2", 2309 2336 "methods": [