diff -r 3d4e9c994f10 -r a86126ab1dd4 wp/wp-includes/js/plupload/wp-plupload.js --- a/wp/wp-includes/js/plupload/wp-plupload.js Tue Oct 22 16:11:46 2019 +0200 +++ b/wp/wp-includes/js/plupload/wp-plupload.js Tue Dec 15 13:49:49 2020 +0100 @@ -33,13 +33,17 @@ */ Uploader = function( options ) { var self = this, - isIE = navigator.userAgent.indexOf('Trident/') != -1 || navigator.userAgent.indexOf('MSIE ') != -1, + isIE, // Not used, back-compat. elements = { container: 'container', browser: 'browse_button', dropzone: 'drop_element' }, - key, error; + tryAgainCount = {}, + tryAgain, + key, + error, + fileUploaded; this.supports = { upload: Uploader.browser.supported @@ -56,10 +60,12 @@ this.plupload = $.extend( true, { multipart_params: {} }, Uploader.defaults ); this.container = document.body; // Set default container. - // Extend the instance with options. - // - // Use deep extend to allow options.plupload to override individual - // default plupload keys. + /* + * Extend the instance with options. + * + * Use deep extend to allow options.plupload to override individual + * default plupload keys. + */ $.extend( true, this, options ); // Proxy all methods so this always refers to the current instance. @@ -95,14 +101,6 @@ return; } - // Make sure flash sends cookies (seems in IE it does without switching to urlstream mode) - if ( ! isIE && 'flash' === plupload.predictRuntime( this.plupload ) && - ( ! this.plupload.required_features || ! this.plupload.required_features.hasOwnProperty( 'send_binary_string' ) ) ) { - - this.plupload.required_features = this.plupload.required_features || {}; - this.plupload.required_features.send_binary_string = true; - } - // Initialize the plupload instance. this.uploader = new plupload.Uploader( this.plupload ); delete this.plupload; @@ -112,16 +110,114 @@ delete this.params; /** + * Attempt to create image sub-sizes when an image was uploaded successfully + * but the server responded with HTTP 5xx error. + * + * @since 5.3.0 + * + * @param {string} message Error message. + * @param {object} data Error data from Plupload. + * @param {plupload.File} file File that was uploaded. + */ + tryAgain = function( message, data, file ) { + var times, id; + + 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; + } + + times = tryAgainCount[ file.id ]; + + if ( times && times > 4 ) { + /* + * The file may have been uploaded and attachment post created, + * but post-processing and resizing failed... + * Do a cleanup then tell the user to scale down the image and upload it again. + */ + $.ajax({ + type: 'post', + url: ajaxurl, + dataType: 'json', + data: { + action: 'media-create-image-subsizes', + _wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce, + attachment_id: id, + _wp_upload_failed_cleanup: true, + } + }); + + error( message, data, file, 'no-retry' ); + return; + } + + if ( ! times ) { + tryAgainCount[ file.id ] = 1; + } else { + tryAgainCount[ file.id ] = ++times; + } + + // Another request to try to create the missing image sub-sizes. + $.ajax({ + type: 'post', + url: ajaxurl, + dataType: 'json', + data: { + action: 'media-create-image-subsizes', + _wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce, + attachment_id: id, + } + }).done( function( response ) { + if ( response.success ) { + fileUploaded( self.uploader, file, response ); + } else { + if ( response.data && response.data.message ) { + message = response.data.message; + } + + error( message, data, file, 'no-retry' ); + } + }).fail( function( jqXHR ) { + // If another HTTP 5xx error, try try again... + if ( jqXHR.status >= 500 && jqXHR.status < 600 ) { + tryAgain( message, data, file ); + return; + } + + error( message, data, file, 'no-retry' ); + }); + } + + /** * Custom error callback. * * Add a new error to the errors collection, so other modules can track * and display errors. @see wp.Uploader.errors. * - * @param {string} message - * @param {object} data - * @param {plupload.File} file File that was uploaded. + * @param {string} message Error message. + * @param {object} data Error data from Plupload. + * @param {plupload.File} file File that was uploaded. + * @param {string} retry Whether to try again to create image sub-sizes. Passing 'no-retry' will prevent it. */ - error = function( message, data, file ) { + error = function( message, data, file, retry ) { + var isImage = file.type && file.type.indexOf( 'image/' ) === 0, + status = data && data.status; + + // If the file is an image and the error is HTTP 5xx try to create sub-sizes again. + if ( retry !== 'no-retry' && isImage && status >= 500 && status < 600 ) { + tryAgain( message, data, file ); + return; + } + if ( file.attachment ) { file.attachment.destroy(); } @@ -136,6 +232,36 @@ }; /** + * After a file is successfully uploaded, update its model. + * + * @param {plupload.Uploader} up Uploader instance. + * @param {plupload.File} file File that was uploaded. + * @param {Object} response Object with response properties. + */ + fileUploaded = function( up, file, response ) { + var complete; + + // Remove the "uploading" UI elements. + _.each( ['file','loaded','size','percent'], function( key ) { + file.attachment.unset( key ); + } ); + + file.attachment.set( _.extend( response.data, { uploading: false } ) ); + + wp.media.model.Attachment.get( response.data.id, file.attachment ); + + complete = Uploader.queue.all( function( attachment ) { + return ! attachment.get( 'uploading' ); + }); + + if ( complete ) { + Uploader.queue.reset(); + } + + self.success( file.attachment ); + } + + /** * After the Uploader has been initialized, initialize some behaviors for the dropzone. * * @param {plupload.Uploader} uploader Uploader instance. @@ -172,11 +298,13 @@ }); dropzone.bind('dragleave.wp-uploader, drop.wp-uploader', function() { - // Using an instant timer prevents the drag-over class from - // being quickly removed and re-added when elements inside the - // dropzone are repositioned. - // - // @see https://core.trac.wordpress.org/ticket/21705 + /* + * Using an instant timer prevents the drag-over class + * from being quickly removed and re-added when elements + * inside the dropzone are repositioned. + * + * @see https://core.trac.wordpress.org/ticket/21705 + */ timer = setTimeout( function() { active = false; dropzone.trigger('dropzone:leave').removeClass('drag-over'); @@ -198,15 +326,21 @@ this.browser.on( 'mouseenter', this.refresh ); } else { this.uploader.disableBrowse( true ); - // If HTML5 mode, hide the auto-created file container. - $('#' + this.uploader.id + '_html5_container').hide(); } + $( self ).on( 'uploader:ready', function() { + $( '.moxie-shim-html5 input[type="file"]' ) + .attr( { + tabIndex: '-1', + 'aria-hidden': 'true' + } ); + } ); + /** * After files were filtered and added to the queue, create a model for each. * - * @param {plupload.Uploader} uploader Uploader instance. - * @param {Array} files Array of file objects that were added to queue by the user. + * @param {plupload.Uploader} up Uploader instance. + * @param {Array} files Array of file objects that were added to queue by the user. */ this.uploader.bind( 'FilesAdded', function( up, files ) { _.each( files, function( file ) { @@ -217,6 +351,15 @@ return; } + if ( file.type === 'image/heic' && up.settings.heic_upload_error ) { + // Show error but do not block uploading. + Uploader.errors.unshift({ + message: pluploadL10n.unsupported_image, + data: {}, + file: file + }); + } + // Generate attributes for a new `Attachment` model. attributes = _.extend({ file: file, @@ -259,13 +402,12 @@ /** * After a file is successfully uploaded, update its model. * - * @param {plupload.Uploader} uploader Uploader instance. + * @param {plupload.Uploader} up Uploader instance. * @param {plupload.File} file File that was uploaded. * @param {Object} response Object with response properties. * @return {mixed} */ this.uploader.bind( 'FileUploaded', function( up, file, response ) { - var complete; try { response = JSON.parse( response.response ); @@ -273,33 +415,21 @@ return error( pluploadL10n.default_error, e, file ); } - if ( ! _.isObject( response ) || _.isUndefined( response.success ) ) + if ( ! _.isObject( response ) || _.isUndefined( response.success ) ) { return error( pluploadL10n.default_error, null, file ); - else if ( ! response.success ) + } else if ( ! response.success ) { return error( response.data && response.data.message, response.data, file ); - - _.each(['file','loaded','size','percent'], function( key ) { - file.attachment.unset( key ); - }); + } - file.attachment.set( _.extend( response.data, { uploading: false }) ); - wp.media.model.Attachment.get( response.data.id, file.attachment ); - - complete = Uploader.queue.all( function( attachment ) { - return ! attachment.get('uploading'); - }); - - if ( complete ) - Uploader.queue.reset(); - - self.success( file.attachment ); + // Success. Update the UI with the new attachment. + fileUploaded( up, file, response ); }); /** * When plupload surfaces an error, send it to the error handler. * - * @param {plupload.Uploader} uploader Uploader instance. - * @param {Object} error Contains code, message and sometimes file and other details. + * @param {plupload.Uploader} up Uploader instance. + * @param {Object} pluploadError Contains code, message and sometimes file and other details. */ this.uploader.bind( 'Error', function( up, pluploadError ) { var message = pluploadL10n.default_error, @@ -338,12 +468,19 @@ 'IMAGE_DIMENSIONS_ERROR': pluploadL10n.image_dimensions_exceeded, 'GENERIC_ERROR': pluploadL10n.upload_failed, 'IO_ERROR': pluploadL10n.io_error, - 'HTTP_ERROR': pluploadL10n.http_error, 'SECURITY_ERROR': pluploadL10n.security_error, 'FILE_SIZE_ERROR': function( file ) { - return pluploadL10n.file_exceeds_size_limit.replace('%s', file.name); - } + return pluploadL10n.file_exceeds_size_limit.replace( '%s', file.name ); + }, + + 'HTTP_ERROR': function( file ) { + if ( file.type && file.type.indexOf( 'image/' ) === 0 ) { + return pluploadL10n.http_error_image; + } + + return pluploadL10n.http_error; + }, }; $.extend( Uploader.prototype, /** @lends wp.Uploader.prototype */{ @@ -396,9 +533,11 @@ node = node.parentNode; } - // If the browser node is not attached to the DOM, use a - // temporary container to house it, as the browser button - // shims require the button to exist in the DOM at all times. + /* + * If the browser node is not attached to the DOM, + * use a temporary container to house it, as the browser button shims + * require the button to exist in the DOM at all times. + */ if ( ! attached ) { id = 'wp-uploader-browser-' + this.uploader.id;