diff --git a/src/js/_enqueues/vendor/plupload/handlers.js b/src/js/_enqueues/vendor/plupload/handlers.js index f225c49970..fbf1012f9c 100644 --- a/src/js/_enqueues/vendor/plupload/handlers.js +++ b/src/js/_enqueues/vendor/plupload/handlers.js @@ -431,9 +431,19 @@ jQuery( document ).ready( function( $ ) { tryAgain = function( up, error ) { var file = error.file; var times; + var id; - if ( ! file || ! file.id ) { - wpQueueError( error.message || pluploadL10n.default_error ); + if ( ! error || ! error.responseHeaders ) { + wpQueueError( pluploadL10n.http_error_image ); + return; + } + + id = error.responseHeaders.match( /x-wp-upload-attachment-id:\s*(\d+)/i ); + + if ( id && id[1] ) { + id = id[1]; + } else { + wpQueueError( pluploadL10n.http_error_image ); return; } @@ -449,8 +459,8 @@ jQuery( document ).ready( function( $ ) { dataType: 'json', data: { action: 'media-create-image-subsizes', - _wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce, - _wp_temp_upload_ref: file.id, + _wpnonce: wpUploaderInit.multipart_params._wpnonce, + attachment_id: id, _wp_upload_failed_cleanup: true, } }); @@ -478,8 +488,8 @@ jQuery( document ).ready( function( $ ) { data: { action: 'media-create-image-subsizes', _wpnonce: wpUploaderInit.multipart_params._wpnonce, - _wp_temp_upload_ref: file.id, - _legasy_support: 'true', + attachment_id: id, + _legacy_support: 'true', } }).done( function( response ) { var message; @@ -589,21 +599,6 @@ jQuery( document ).ready( function( $ ) { uploader.bind( 'UploadComplete', function() { uploadComplete(); }); - - /** - * When uploading images add a file reference used to retrieve the attachment_id - * if the uploading fails due to a server timeout of out of memoty (HTTP 500) error. - * - * @param {plupload.Uploader} up Uploader instance. - * @param {plupload.File} file File for uploading. - */ - uploader.bind( 'BeforeUpload', function( up, file ) { - if ( file.type && file.type.indexOf( 'image/' ) === 0 ) { - up.settings.multipart_params._wp_temp_upload_ref = file.id; - } else { - up.settings.multipart_params._wp_temp_upload_ref = ''; - } - } ); }; if ( typeof( wpUploaderInit ) == 'object' ) { diff --git a/src/js/_enqueues/vendor/plupload/wp-plupload.js b/src/js/_enqueues/vendor/plupload/wp-plupload.js index 67e44226e6..b42c98cf9b 100644 --- a/src/js/_enqueues/vendor/plupload/wp-plupload.js +++ b/src/js/_enqueues/vendor/plupload/wp-plupload.js @@ -119,9 +119,19 @@ window.wp = window.wp || {}; */ tryAgain = function( message, data, file ) { var times; + var id; - if ( ! file || ! file.id ) { - error( pluploadL10n.upload_failed, data, file, 'no-retry' ); + if ( ! data || ! data.responseHeaders ) { + error( pluploadL10n.http_error_image, data, file, 'no-retry' ); + return; + } + + id = data.responseHeaders.match( /x-wp-upload-attachment-id:\s*(\d+)/i ); + + if ( id && id[1] ) { + id = id[1]; + } else { + error( pluploadL10n.http_error_image, data, file, 'no-retry' ); return; } @@ -138,7 +148,7 @@ window.wp = window.wp || {}; data: { action: 'media-create-image-subsizes', _wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce, - _wp_temp_upload_ref: file.id, + attachment_id: id, _wp_upload_failed_cleanup: true, } }); @@ -161,7 +171,7 @@ window.wp = window.wp || {}; data: { action: 'media-create-image-subsizes', _wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce, - _wp_temp_upload_ref: file.id, // Used to find the new attachment_id. + attachment_id: id, } }).done( function( response ) { if ( response.success ) { @@ -315,21 +325,6 @@ window.wp = window.wp || {}; $('#' + this.uploader.id + '_html5_container').hide(); } - /** - * When uploading images add a reference used to retrieve the attachment_id. - * Used if the uploading fails due to a server timeout of out of memoty error (HTTP 500). - * - * @param {plupload.Uploader} up Uploader instance. - * @param {plupload.File} file File for uploading. - */ - this.uploader.bind( 'BeforeUpload', function( up, file ) { - if ( file.type && file.type.indexOf( 'image/' ) === 0 ) { - up.settings.multipart_params._wp_temp_upload_ref = file.id; - } else { - up.settings.multipart_params._wp_temp_upload_ref = ''; - } - } ); - /** * After files were filtered and added to the queue, create a model for each. * diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 17f0243897..773be72833 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -2422,39 +2422,19 @@ function wp_ajax_media_create_image_subsizes() { wp_send_json_error( array( 'message' => __( 'Sorry, you are not allowed to upload files.' ) ) ); } - if ( ! empty( $_POST['_wp_temp_upload_ref'] ) ) { - // Uploading of images usually fails while creating the sub-sizes, either because of a timeout or out of memory. - // At this point the file has been uploaded and an attachment post created, but because of the PHP fatal error - // the cliend doesn't know the attachment ID yet. - // To be able to find the new attachment_id in these cases we temporarily store an upload reference sent by the client - // in the original upload request. It is used to save a transient with the attachment_id as value. - // That reference currently is Plupload's `file.id` but can be any sufficiently random alpha-numeric string. - $attachment_id = _wp_get_upload_ref_attachment_id( $_POST['_wp_temp_upload_ref'] ); - } else { - wp_send_json_error( array( 'message' => __( 'Invalid file reference.' ) ) ); - } - - if ( empty( $attachment_id ) ) { + if ( empty( $_POST['attachment_id'] ) ) { wp_send_json_error( array( 'message' => __( 'Upload failed. Please reload and try again.' ) ) ); } + $attachment_id = (int) $_POST['attachment_id']; + if ( ! empty( $_POST['_wp_upload_failed_cleanup'] ) ) { // Upload failed. Cleanup. - if ( wp_attachment_is_image( $attachment_id ) ) { + if ( wp_attachment_is_image( $attachment_id ) && current_user_can( 'delete_post', $attachment_id ) ) { $attachment = get_post( $attachment_id ); - // Posted at most 10 min ago. + // Created at most 10 min ago. if ( $attachment && ( time() - strtotime( $attachment->post_date_gmt ) < 600 ) ) { - /** - * Runs when an image upload fails during the post-processing phase, - * and the newly created attachment post is about to be deleted. - * - * @since 5.3.0 - * - * @param int $attachment_id The attachment post ID. - */ - do_action( 'wp_upload_failed_cleanup', $attachment_id ); - wp_delete_attachment( $attachment_id, true ); wp_send_json_success(); } @@ -2465,7 +2445,7 @@ function wp_ajax_media_create_image_subsizes() { // The js that handles the response would need to also handle HTTP 500 errors. wp_update_image_subsizes( $attachment_id ); - if ( ! empty( $_POST['_legasy_support'] ) ) { + if ( ! empty( $_POST['_legacy_support'] ) ) { // The old (inline) uploader. Only needs the attachment_id. $response = array( 'id' => $attachment_id ); } else { @@ -2478,8 +2458,6 @@ function wp_ajax_media_create_image_subsizes() { } // At this point the image has been uploaded successfully. - _wp_clear_upload_ref( $_POST['_wp_temp_upload_ref'] ); - wp_send_json_success( $response ); } diff --git a/src/wp-admin/includes/file.php b/src/wp-admin/includes/file.php index 9cd626ec0b..3812dc8000 100644 --- a/src/wp-admin/includes/file.php +++ b/src/wp-admin/includes/file.php @@ -981,64 +981,6 @@ function wp_handle_sideload( &$file, $overrides = false, $time = null ) { return _wp_handle_upload( $file, $overrides, $time, $action ); } -/** - * Temporarily stores the client upload reference in a transient. - * - * @since 5.3.0 - * @access private - * - * @param string $upload_ref The upload reference sent by the client. - * @param int $attachment_id Attachment post ID. - * @return bool Whether the transient was set. - */ -function _wp_set_upload_ref( $upload_ref, $attachment_id ) { - $upload_ref = preg_replace( '/[^a-zA-Z0-9_]/', '', $upload_ref ); - - if ( ! empty( $upload_ref ) ) { - return set_transient( '_wp_temp_image_ref:' . $upload_ref, $attachment_id, HOUR_IN_SECONDS ); - } - - return false; -} - -/** - * Get attachment post ID from an upload reference. - * - * @since 5.3.0 - * @access private - * - * @param string $upload_ref The upload reference sent by the client. - * @return int The attachemtn post ID. Zero if the upload reference has expired or doesn't exist. - */ -function _wp_get_upload_ref_attachment_id( $upload_ref ) { - $upload_ref = preg_replace( '/[^a-zA-Z0-9_]/', '', $upload_ref ); - - if ( ! empty( $upload_ref ) ) { - return (int) get_transient( '_wp_temp_image_ref:' . $upload_ref ); - } - - return 0; -} - -/** - * Remove the transient that stores a temporary upload reference. - * - * @since 5.3.0 - * @access private - * - * @param string $upload_ref The upload reference sent by the client. - * @return bool Whether the transient was removed. - */ -function _wp_clear_upload_ref( $upload_ref ) { - $upload_ref = preg_replace( '/[^a-zA-Z0-9_]/', '', $upload_ref ); - - if ( ! empty( $upload_ref ) ) { - return delete_transient( '_wp_temp_image_ref:' . $upload_ref ); - } - - return false; -} - /** * Downloads a URL to a local temporary file using the WordPress HTTP API. * diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 664b83519f..1582ceb3df 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -251,6 +251,12 @@ function wp_create_image_subsizes( $file, $attachment_id ) { return $image_meta; } + // Set a custom header with the attachment_id. + // Used by the browser/client to resume creating image sub-sizes after a PHP fatal error. + if ( ! headers_sent() ) { + header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id ); + } + // Resize the image $resized = $editor->resize( $threshold, $threshold ); $rotated = null; @@ -273,6 +279,8 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { $image_meta['image_meta']['orientation'] = 1; } + + wp_update_attachment_metadata( $attachment_id, $image_meta ); } else { // TODO: handle errors. } @@ -289,6 +297,10 @@ function wp_create_image_subsizes( $file, $attachment_id ) { return $image_meta; } + if ( ! headers_sent() ) { + header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id ); + } + // Rotate the image $rotated = $editor->maybe_exif_rotate(); @@ -303,6 +315,8 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { $image_meta['image_meta']['orientation'] = 1; } + + wp_update_attachment_metadata( $attachment_id, $image_meta ); } else { // TODO: handle errors. } @@ -386,6 +400,12 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { return $image_meta; } + // Set a custom header with the attachment_id. + // Used by the browser/client to resume creating image sub-sizes after a PHP fatal error. + if ( ! headers_sent() ) { + header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id ); + } + // If stored EXIF data exists, rotate the source image before creating sub-sizes. if ( ! empty( $image_meta['image_meta'] ) ) { $rotated = $editor->maybe_exif_rotate(); diff --git a/src/wp-admin/includes/media.php b/src/wp-admin/includes/media.php index 6987acb166..18e87baa71 100644 --- a/src/wp-admin/includes/media.php +++ b/src/wp-admin/includes/media.php @@ -314,7 +314,6 @@ function media_handle_upload( $file_id, $post_id, $post_data = array(), $overrid $title = sanitize_text_field( $name ); $content = ''; $excerpt = ''; - $_ref = false; if ( preg_match( '#^audio#', $type ) ) { $meta = wp_read_audio_metadata( $file ); @@ -409,20 +408,9 @@ function media_handle_upload( $file_id, $post_id, $post_data = array(), $overrid $attachment_id = wp_insert_attachment( $attachment, $file, $post_id, true ); if ( ! is_wp_error( $attachment_id ) ) { - // If an image, keep the upload reference until all image sub-sizes are created. - if ( ! empty( $_POST['_wp_temp_upload_ref'] ) && wp_attachment_is_image( $attachment_id ) ) { - $_ref = _wp_set_upload_ref( $_POST['_wp_temp_upload_ref'], $attachment_id ); - } - // The image sub-sizes are created during wp_generate_attachment_metadata(). // This is generally slow and may cause timeouts or out of memory errors. wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file ) ); - - // At this point the image is uploaded successfully even if there were specific errors or some sub-sizes were not created. - // The transient is not needed any more. - if ( $_ref ) { - _wp_clear_upload_ref( $_POST['_wp_temp_upload_ref'] ); - } } return $attachment_id;