WordPress.org

Make WordPress Core

Changeset 46382


Ignore:
Timestamp:
10/03/2019 09:00:10 PM (6 weeks 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.