Make WordPress Core

Changeset 46382


Ignore:
Timestamp:
10/03/2019 09:00:10 PM (7 years ago)
Author:
azaozz
Message:

Upload: Fix the method used to create image sub-sizes when uploading fails with a PHP fatal error. Use a custom header to send the new attachment post ID even in HTTP 500 responses instead of an upload reference sent by the client. Also add another cap check and remove the action when deleting an attachment post during a failed upload cleanup.

Props timothyblynjacobs, clorith, azaozz.
Fixes #48200.

Location:
trunk/src
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/js/_enqueues/vendor/plupload/handlers.js

    r46174 r46382  
    432432                var file = error.file;
    433433                var times;
    434 
    435                 if ( ! file || ! file.id ) {
    436                         wpQueueError( error.message || pluploadL10n.default_error );
     434                var id;
     435
     436                if ( ! error || ! error.responseHeaders ) {
     437                        wpQueueError( pluploadL10n.http_error_image );
     438                        return;
     439                }
     440
     441                id = error.responseHeaders.match( /x-wp-upload-attachment-id:\s*(\d+)/i );
     442
     443                if ( id && id[1] ) {
     444                        id = id[1];
     445                } else {
     446                        wpQueueError( pluploadL10n.http_error_image );
    437447                        return;
    438448                }
     
    450460                                data: {
    451461                                        action: 'media-create-image-subsizes',
    452                                         _wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce,
    453                                         _wp_temp_upload_ref: file.id,
     462                                        _wpnonce: wpUploaderInit.multipart_params._wpnonce,
     463                                        attachment_id: id,
    454464                                        _wp_upload_failed_cleanup: true,
    455465                                }
     
    479489                                action: 'media-create-image-subsizes',
    480490                                _wpnonce: wpUploaderInit.multipart_params._wpnonce,
    481                                 _wp_temp_upload_ref: file.id,
    482                                 _legasy_support: 'true',
     491                                attachment_id: id,
     492                                _legacy_support: 'true',
    483493                        }
    484494                }).done( function( response ) {
     
    590600                        uploadComplete();
    591601                });
    592 
    593                 /**
    594                  * When uploading images add a file reference used to retrieve the attachment_id
    595                  * if the uploading fails due to a server timeout of out of memoty (HTTP 500) error.
    596                  *
    597                  * @param {plupload.Uploader} up   Uploader instance.
    598                  * @param {plupload.File}     file File for uploading.
    599                  */
    600                 uploader.bind( 'BeforeUpload', function( up, file ) {
    601                         if ( file.type && file.type.indexOf( 'image/' ) === 0 ) {
    602                                 up.settings.multipart_params._wp_temp_upload_ref = file.id;
    603                         } else {
    604                                 up.settings.multipart_params._wp_temp_upload_ref = '';
    605                         }
    606                 } );
    607602        };
    608603
  • trunk/src/js/_enqueues/vendor/plupload/wp-plupload.js

    r46174 r46382  
    120120                tryAgain = function( message, data, file ) {
    121121                        var times;
    122 
    123                         if ( ! file || ! file.id ) {
    124                                 error( pluploadL10n.upload_failed, data, file, 'no-retry' );
     122                        var id;
     123
     124                        if ( ! data || ! data.responseHeaders ) {
     125                                error( pluploadL10n.http_error_image, data, file, 'no-retry' );
     126                                return;
     127                        }
     128
     129                        id = data.responseHeaders.match( /x-wp-upload-attachment-id:\s*(\d+)/i );
     130
     131                        if ( id && id[1] ) {
     132                                id = id[1];
     133                        } else {
     134                                error( pluploadL10n.http_error_image, data, file, 'no-retry' );
    125135                                return;
    126136                        }
     
    139149                                                action: 'media-create-image-subsizes',
    140150                                                _wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce,
    141                                                 _wp_temp_upload_ref: file.id,
     151                                                attachment_id: id,
    142152                                                _wp_upload_failed_cleanup: true,
    143153                                        }
     
    162172                                        action: 'media-create-image-subsizes',
    163173                                        _wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce,
    164                                         _wp_temp_upload_ref: file.id, // Used to find the new attachment_id.
     174                                        attachment_id: id,
    165175                                }
    166176                        }).done( function( response ) {
     
    317327
    318328                /**
    319                  * When uploading images add a reference used to retrieve the attachment_id.
    320                  * Used if the uploading fails due to a server timeout of out of memoty error (HTTP 500).
    321                  *
    322                  * @param {plupload.Uploader} up   Uploader instance.
    323                  * @param {plupload.File}     file File for uploading.
    324                  */
    325                 this.uploader.bind( 'BeforeUpload', function( up, file ) {
    326                         if ( file.type && file.type.indexOf( 'image/' ) === 0 ) {
    327                                 up.settings.multipart_params._wp_temp_upload_ref = file.id;
    328                         } else {
    329                                 up.settings.multipart_params._wp_temp_upload_ref = '';
    330                         }
    331                 } );
    332 
    333                 /**
    334329                 * After files were filtered and added to the queue, create a model for each.
    335330                 *
  • trunk/src/wp-admin/includes/ajax-actions.php

    r46253 r46382  
    24232423        }
    24242424
    2425         if ( ! empty( $_POST['_wp_temp_upload_ref'] ) ) {
    2426                 // Uploading of images usually fails while creating the sub-sizes, either because of a timeout or out of memory.
    2427                 // At this point the file has been uploaded and an attachment post created, but because of the PHP fatal error
    2428                 // the cliend doesn't know the attachment ID yet.
    2429                 // To be able to find the new attachment_id in these cases we temporarily store an upload reference sent by the client
    2430                 // in the original upload request. It is used to save a transient with the attachment_id as value.
    2431                 // That reference currently is Plupload's `file.id` but can be any sufficiently random alpha-numeric string.
    2432                 $attachment_id = _wp_get_upload_ref_attachment_id( $_POST['_wp_temp_upload_ref'] );
    2433         } else {
    2434                 wp_send_json_error( array( 'message' => __( 'Invalid file reference.' ) ) );
    2435         }
    2436 
    2437         if ( empty( $attachment_id ) ) {
     2425        if ( empty( $_POST['attachment_id'] ) ) {
    24382426                wp_send_json_error( array( 'message' => __( 'Upload failed. Please reload and try again.' ) ) );
    24392427        }
     2428
     2429        $attachment_id = (int) $_POST['attachment_id'];
    24402430
    24412431        if ( ! empty( $_POST['_wp_upload_failed_cleanup'] ) ) {
    24422432                // Upload failed. Cleanup.
    2443                 if ( wp_attachment_is_image( $attachment_id ) ) {
     2433                if ( wp_attachment_is_image( $attachment_id ) && current_user_can( 'delete_post', $attachment_id ) ) {
    24442434                        $attachment = get_post( $attachment_id );
    24452435
    2446                         // Posted at most 10 min ago.
     2436                        // Created at most 10 min ago.
    24472437                        if ( $attachment && ( time() - strtotime( $attachment->post_date_gmt ) < 600 ) ) {
    2448                                 /**
    2449                                  * Runs when an image upload fails during the post-processing phase,
    2450                                  * and the newly created attachment post is about to be deleted.
    2451                                  *
    2452                                  * @since 5.3.0
    2453                                  *
    2454                                  * @param int $attachment_id The attachment post ID.
    2455                                  */
    2456                                 do_action( 'wp_upload_failed_cleanup', $attachment_id );
    2457 
    24582438                                wp_delete_attachment( $attachment_id, true );
    24592439                                wp_send_json_success();
     
    24662446        wp_update_image_subsizes( $attachment_id );
    24672447
    2468         if ( ! empty( $_POST['_legasy_support'] ) ) {
     2448        if ( ! empty( $_POST['_legacy_support'] ) ) {
    24692449                // The old (inline) uploader. Only needs the attachment_id.
    24702450                $response = array( 'id' => $attachment_id );
     
    24792459
    24802460        // At this point the image has been uploaded successfully.
    2481         _wp_clear_upload_ref( $_POST['_wp_temp_upload_ref'] );
    2482 
    24832461        wp_send_json_success( $response );
    24842462}
  • trunk/src/wp-admin/includes/file.php

    r46290 r46382  
    983983
    984984/**
    985  * Temporarily stores the client upload reference in a transient.
    986  *
    987  * @since 5.3.0
    988  * @access private
    989  *
    990  * @param string $upload_ref    The upload reference sent by the client.
    991  * @param int    $attachment_id Attachment post ID.
    992  * @return bool Whether the transient was set.
    993  */
    994 function _wp_set_upload_ref( $upload_ref, $attachment_id ) {
    995         $upload_ref = preg_replace( '/[^a-zA-Z0-9_]/', '', $upload_ref );
    996 
    997         if ( ! empty( $upload_ref ) ) {
    998                 return set_transient( '_wp_temp_image_ref:' . $upload_ref, $attachment_id, HOUR_IN_SECONDS );
    999         }
    1000 
    1001         return false;
    1002 }
    1003 
    1004 /**
    1005  * Get attachment post ID from an upload reference.
    1006  *
    1007  * @since 5.3.0
    1008  * @access private
    1009  *
    1010  * @param string $upload_ref    The upload reference sent by the client.
    1011  * @return int The attachemtn post ID. Zero if the upload reference has expired or doesn't exist.
    1012  */
    1013 function _wp_get_upload_ref_attachment_id( $upload_ref ) {
    1014         $upload_ref = preg_replace( '/[^a-zA-Z0-9_]/', '', $upload_ref );
    1015 
    1016         if ( ! empty( $upload_ref ) ) {
    1017                 return (int) get_transient( '_wp_temp_image_ref:' . $upload_ref );
    1018         }
    1019 
    1020         return 0;
    1021 }
    1022 
    1023 /**
    1024  * Remove the transient that stores a temporary upload reference.
    1025  *
    1026  * @since 5.3.0
    1027  * @access private
    1028  *
    1029  * @param string $upload_ref    The upload reference sent by the client.
    1030  * @return bool Whether the transient was removed.
    1031  */
    1032 function _wp_clear_upload_ref( $upload_ref ) {
    1033         $upload_ref = preg_replace( '/[^a-zA-Z0-9_]/', '', $upload_ref );
    1034 
    1035         if ( ! empty( $upload_ref ) ) {
    1036                 return delete_transient( '_wp_temp_image_ref:' . $upload_ref );
    1037         }
    1038 
    1039         return false;
    1040 }
    1041 
    1042 /**
    1043985 * Downloads a URL to a local temporary file using the WordPress HTTP API.
    1044986 *
  • trunk/src/wp-admin/includes/image.php

    r46368 r46382  
    252252                }
    253253
     254                // Set a custom header with the attachment_id.
     255                // Used by the browser/client to resume creating image sub-sizes after a PHP fatal error.
     256                if ( ! headers_sent() ) {
     257                        header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id );
     258                }
     259
    254260                // Resize the image
    255261                $resized = $editor->resize( $threshold, $threshold );
     
    274280                                        $image_meta['image_meta']['orientation'] = 1;
    275281                                }
     282
     283                                wp_update_attachment_metadata( $attachment_id, $image_meta );
    276284                        } else {
    277285                                // TODO: handle errors.
     
    288296                        // This image cannot be edited.
    289297                        return $image_meta;
     298                }
     299
     300                if ( ! headers_sent() ) {
     301                        header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id );
    290302                }
    291303
     
    304316                                        $image_meta['image_meta']['orientation'] = 1;
    305317                                }
     318
     319                                wp_update_attachment_metadata( $attachment_id, $image_meta );
    306320                        } else {
    307321                                // TODO: handle errors.
     
    385399                // The image cannot be edited.
    386400                return $image_meta;
     401        }
     402
     403        // Set a custom header with the attachment_id.
     404        // Used by the browser/client to resume creating image sub-sizes after a PHP fatal error.
     405        if ( ! headers_sent() ) {
     406                header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id );
    387407        }
    388408
  • trunk/src/wp-admin/includes/media.php

    r46359 r46382  
    315315        $content = '';
    316316        $excerpt = '';
    317         $_ref    = false;
    318317
    319318        if ( preg_match( '#^audio#', $type ) ) {
     
    410409
    411410        if ( ! is_wp_error( $attachment_id ) ) {
    412                 // If an image, keep the upload reference until all image sub-sizes are created.
    413                 if ( ! empty( $_POST['_wp_temp_upload_ref'] ) && wp_attachment_is_image( $attachment_id ) ) {
    414                         $_ref = _wp_set_upload_ref( $_POST['_wp_temp_upload_ref'], $attachment_id );
    415                 }
    416 
    417411                // The image sub-sizes are created during wp_generate_attachment_metadata().
    418412                // This is generally slow and may cause timeouts or out of memory errors.
    419413                wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file ) );
    420 
    421                 // At this point the image is uploaded successfully even if there were specific errors or some sub-sizes were not created.
    422                 // The transient is not needed any more.
    423                 if ( $_ref ) {
    424                         _wp_clear_upload_ref( $_POST['_wp_temp_upload_ref'] );
    425                 }
    426414        }
    427415
Note: See TracChangeset for help on using the changeset viewer.