WordPress.org

Make WordPress Core

Ticket #47987: 47987.4.diff

File 47987.4.diff, 14.1 KB (added by TimothyBlynJacobs, 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  
    1616 */
    1717class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
    1818
     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
    1942        /**
    2043         * Determines the allowed query_vars for a get_items() response and
    2144         * prepares for WP_Query.
    class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 
    100123                        return new WP_Error( 'rest_invalid_param', __( 'Invalid parent type.' ), array( 'status' => 400 ) );
    101124                }
    102125
     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 ) {
    103209                // Get the file via $_FILES or raw data.
    104210                $files   = $request->get_file_params();
    105211                $headers = $request->get_headers();
    class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 
    138244                        }
    139245                }
    140246
    141                 $attachment                 = $this->prepare_item_for_database( $request );
     247                $attachment = $this->prepare_item_for_database( $request );
     248
    142249                $attachment->post_mime_type = $type;
    143250                $attachment->guid           = $url;
    144251
    class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 
    155262                        } else {
    156263                                $id->add_data( array( 'status' => 400 ) );
    157264                        }
     265
    158266                        return $id;
    159267                }
    160268
    class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 
    172280                 */
    173281                do_action( 'rest_insert_attachment', $attachment, $request, true );
    174282
    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                );
    209287        }
    210288
    211289        /**
    class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 
    253331                return $response;
    254332        }
    255333
     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
    256389        /**
    257390         * Prepares a single attachment for create or update.
    258391         *
    class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 
    380513                        $data['source_url'] = wp_get_attachment_url( $post->ID );
    381514                }
    382515
     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
    383521                $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
    384522
    385523                $data = $this->filter_response_by_context( $data, $context );
    class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 
    514652                        'readonly'    => true,
    515653                );
    516654
     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
    517663                unset( $schema['properties']['password'] );
    518664
    519665                $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 { 
    587587         * Retrieves an array of endpoint arguments from the item schema for the controller.
    588588         *
    589589         * @since 4.7.0
     590         * @since 5.3.0 Added the $schema parameter.
    590591         *
    591592         * @param string $method Optional. HTTP method of the request. The arguments for `CREATABLE` requests are
    592593         *                       checked for required values and may fall-back to a given default, this is not done
    593594         *                       on `EDITABLE` requests. Default WP_REST_Server::CREATABLE.
     595         * @param array $schema  Optional. Specify the schema to use instead of the item schema.
    594596         * @return array Endpoint arguments.
    595597         */
    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                }
    597603
    598                 $schema            = $this->get_item_schema();
    599604                $schema_properties = ! empty( $schema['properties'] ) ? $schema['properties'] : array();
    600605                $endpoint_args     = array();
    601606
  • 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 
    13271327                $response   = rest_get_server()->dispatch( $request );
    13281328                $data       = $response->get_data();
    13291329                $properties = $data['schema']['properties'];
    1330                 $this->assertEquals( 26, count( $properties ) );
     1330                $this->assertEquals( 27, count( $properties ) );
    13311331                $this->assertArrayHasKey( 'author', $properties );
    13321332                $this->assertArrayHasKey( 'alt_text', $properties );
    13331333                $this->assertArrayHasKey( 'caption', $properties );
    class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control 
    13601360                $this->assertArrayHasKey( 'raw', $properties['title']['properties'] );
    13611361                $this->assertArrayHasKey( 'rendered', $properties['title']['properties'] );
    13621362                $this->assertArrayHasKey( 'type', $properties );
     1363                $this->assertArrayHasKey( 'missing_image_sizes', $properties );
    13631364        }
    13641365
    13651366        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 { 
    9999                        '/wp/v2/pages/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)',
    100100                        '/wp/v2/media',
    101101                        '/wp/v2/media/(?P<id>[\\d]+)',
     102                        '/wp/v2/media/(?P<id>[\\d]+)/post-process',
    102103                        '/wp/v2/blocks',
    103104                        '/wp/v2/blocks/(?P<id>[\d]+)',
    104105                        '/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 = { 
    23042304                }
    23052305            ]
    23062306        },
     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        },
    23072334        "/wp/v2/blocks": {
    23082335            "namespace": "wp/v2",
    23092336            "methods": [