Make WordPress Core


Ignore:
Timestamp:
10/07/2019 05:04:49 PM (7 years ago)
Author:
azaozz
Message:

REST API: Add support for continuing the post-processing of images after upload. Flow:

  1. POST /wp/v2/media.
  2. If the upload failed (HTTP 500 error), look for a response header with X-WP-Upload-Attachment-ID header that contains the newly created attachment ID.
  3. POST /wp/v2/media/{id}/post-process with { "action": "create-image-subsizes" }. This request may still fail, but it will save its progress.
  4. On continued failure, DELETE /wp/v2/media/{id} to give up on the upload and instruct the user to resize their image before uploading.

Props TimothyBlynJacobs.
Fixes #47987.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php

    r45932 r46422  
    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(
     29                                        'id'     => array(
     30                                                'description' => __( 'Unique identifier for the object.' ),
     31                                                'type'        => 'integer',
     32                                        ),
     33                                        'action' => array(
     34                                                'type'     => 'string',
     35                                                'enum'     => array( 'create-image-subsizes' ),
     36                                                'required' => true,
     37                                        ),
     38                                ),
     39                        )
     40                );
     41        }
     42
    1943        /**
    2044         * Determines the allowed query_vars for a get_items() response and
     
    101125                }
    102126
     127                $insert = $this->insert_attachment( $request );
     128
     129                if ( is_wp_error( $insert ) ) {
     130                        return $insert;
     131                }
     132
     133                // Extract by name.
     134                $attachment_id = $insert['attachment_id'];
     135                $file          = $insert['file'];
     136
     137                if ( isset( $request['alt_text'] ) ) {
     138                        update_post_meta( $attachment_id, '_wp_attachment_image_alt', sanitize_text_field( $request['alt_text'] ) );
     139                }
     140
     141                $attachment    = get_post( $attachment_id );
     142                $fields_update = $this->update_additional_fields_for_object( $attachment, $request );
     143
     144                if ( is_wp_error( $fields_update ) ) {
     145                        return $fields_update;
     146                }
     147
     148                $request->set_param( 'context', 'edit' );
     149
     150                /**
     151                 * Fires after a single attachment is completely created or updated via the REST API.
     152                 *
     153                 * @since 5.0.0
     154                 *
     155                 * @param WP_Post         $attachment Inserted or updated attachment object.
     156                 * @param WP_REST_Request $request    Request object.
     157                 * @param bool            $creating   True when creating an attachment, false when updating.
     158                 */
     159                do_action( 'rest_after_insert_attachment', $attachment, $request, true );
     160
     161                if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
     162                        // Set a custom header with the attachment_id.
     163                        // Used by the browser/client to resume creating image sub-sizes after a PHP fatal error.
     164                        header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id );
     165                }
     166
     167                // Include admin function to get access to wp_generate_attachment_metadata().
     168                require_once ABSPATH . 'wp-admin/includes/media.php';
     169
     170                // Post-process the upload (create image sub-sizes, make PDF thumbnalis, etc.) and insert attachment meta.
     171                // At this point the server may run out of resources and post-processing of uploaded images may fail.
     172                wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file ) );
     173
     174                $response = $this->prepare_item_for_response( $attachment, $request );
     175                $response = rest_ensure_response( $response );
     176                $response->set_status( 201 );
     177                $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $attachment_id ) ) );
     178
     179                return $response;
     180        }
     181
     182        /**
     183         * Inserts the attachment post in the database. Does not update the attachment meta.
     184         *
     185         * @since 5.3.0
     186         *
     187         * @param WP_REST_Request $request
     188         * @return array|WP_Error
     189         */
     190        protected function insert_attachment( $request ) {
    103191                // Get the file via $_FILES or raw data.
    104192                $files   = $request->get_file_params();
     
    139227                }
    140228
    141                 $attachment                 = $this->prepare_item_for_database( $request );
     229                $attachment = $this->prepare_item_for_database( $request );
     230
    142231                $attachment->post_mime_type = $type;
    143232                $attachment->guid           = $url;
     
    156245                                $id->add_data( array( 'status' => 400 ) );
    157246                        }
     247
    158248                        return $id;
    159249                }
     
    173263                do_action( 'rest_insert_attachment', $attachment, $request, true );
    174264
    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;
     265                return array(
     266                        'attachment_id' => $id,
     267                        'file'          => $file,
     268                );
    209269        }
    210270
     
    252312
    253313                return $response;
     314        }
     315
     316        /**
     317         * Performs post processing on an attachment.
     318         *
     319         * @since 5.3.0
     320         *
     321         * @param WP_REST_Request $request
     322         * @return WP_REST_Response|WP_Error
     323         */
     324        public function post_process_item( $request ) {
     325                switch ( $request['action'] ) {
     326                        case 'create-image-subsizes':
     327                                require_once ABSPATH . 'wp-admin/includes/image.php';
     328                                wp_update_image_subsizes( $request['id'] );
     329                                break;
     330                }
     331
     332                $request['context'] = 'edit';
     333
     334                return $this->prepare_item_for_response( get_post( $request['id'] ), $request );
     335        }
     336
     337        /**
     338         * Checks if a given request can perform post processing on an attachment.
     339         *
     340         * @sicne 5.3.0
     341         *
     342         * @param WP_REST_Request $request Full details about the request.
     343         * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
     344         */
     345        public function post_process_item_permissions_check( $request ) {
     346                return $this->update_item_permissions_check( $request );
    254347        }
    255348
     
    379472                if ( in_array( 'source_url', $fields, true ) ) {
    380473                        $data['source_url'] = wp_get_attachment_url( $post->ID );
     474                }
     475
     476                if ( in_array( 'missing_image_sizes', $fields, true ) ) {
     477                        require_once ABSPATH . 'wp-admin/includes/image.php';
     478                        $data['missing_image_sizes'] = array_keys( wp_get_missing_image_subsizes( $post->ID ) );
    381479                }
    382480
     
    515613                );
    516614
     615                $schema['properties']['missing_image_sizes'] = array(
     616                        'description' => __( 'List of the missing image sizes of the attachment.' ),
     617                        'type'        => 'array',
     618                        'items'       => array( 'type' => 'string' ),
     619                        'context'     => array( 'edit' ),
     620                        'readonly'    => true,
     621                );
     622
    517623                unset( $schema['properties']['password'] );
    518624
Note: See TracChangeset for help on using the changeset viewer.