diff --git a/wp-includes/css/media-views.css b/wp-includes/css/media-views.css
index ed3dd91602..90ee1edb37 100644
--- a/wp-includes/css/media-views.css
+++ b/wp-includes/css/media-views.css
@@ -738,9 +738,14 @@ a.media-modal-close {
}
.media-uploader-status .media-progress-bar {
+ display: none;
width: 100%;
}
+.uploading.media-uploader-status .media-progress-bar {
+ display: block;
+}
+
.attachment-preview .media-progress-bar {
position: absolute;
top: 50%;
@@ -750,17 +755,23 @@ a.media-modal-close {
}
.media-uploader-status {
+ position: relative;
padding-bottom: 10px;
border-bottom: 1px solid #dfdfdf;
box-shadow: 0 1px 0 #fff;
}
.media-uploader-status .upload-details {
+ display: none;
font-size: 12px;
color: #666;
text-shadow: 0 1px 0 #fff;
}
+.uploading.media-uploader-status .upload-details {
+ display: block;
+}
+
.media-uploader-status .upload-detail-separator {
padding: 0 4px;
}
@@ -769,6 +780,47 @@ a.media-modal-close {
color: #464646;
}
+.media-uploader-status .upload-dismiss-errors,
+.media-uploader-status .upload-errors {
+ display: none;
+}
+
+.errors.media-uploader-status .upload-dismiss-errors,
+.errors.media-uploader-status .upload-errors {
+ display: block;
+}
+
+.media-uploader-status .upload-dismiss-errors {
+ position: absolute;
+ top: 0;
+ right: 0;
+ text-decoration: none;
+}
+
+.media-uploader-status .upload-error {
+ margin: 8px 0 0 0;
+ padding: 8px;
+ border: 1px #c00 solid;
+ background: #ffebe8;
+ border-radius: 3px;
+}
+
+.media-uploader-status .upload-error-label {
+ padding: 2px 4px;
+ margin-right: 8px;
+ font-weight: bold;
+ color: #fff;
+ background: #f00;
+ background: -webkit-linear-gradient( top, #e00, #a00 );
+ border-radius: 3px;
+}
+
+.media-uploader-status .upload-error-message {
+ display: block;
+ padding-top: 8px;
+ color: #b44;
+}
+
.uploader-window {
position: fixed;
top: 0;
diff --git a/wp-includes/js/media-views.js b/wp-includes/js/media-views.js
index 798a143af9..a0f762f341 100644
--- a/wp-includes/js/media-views.js
+++ b/wp-includes/js/media-views.js
@@ -32,9 +32,11 @@
}());
// Makes it easier to bind events using transitions.
- media.transition = function( selector ) {
+ media.transition = function( selector, sensitivity ) {
var deferred = $.Deferred();
+ sensitivity = sensitivity || 2000;
+
if ( $.support.transition ) {
if ( ! (selector instanceof $) )
selector = $( selector );
@@ -42,6 +44,9 @@
// Resolve the deferred when the first element finishes animating.
selector.first().one( $.support.transition.end, deferred.resolve );
+ // Just in case the event doesn't trigger, fire a callback.
+ _.delay( deferred.resolve, sensitivity );
+
// Otherwise, execute on the spot.
} else {
deferred.resolve();
@@ -1835,6 +1840,10 @@
className: 'media-uploader-status',
template: media.template('uploader-status'),
+ events: {
+ 'click .upload-dismiss-errors': 'dismiss'
+ },
+
initialize: function() {
this.controller = this.options.controller;
@@ -1844,6 +1853,8 @@
this.queue.on( 'add remove reset change:uploading', this.info, this );
this.errors = wp.Uploader.errors;
+ this.errors.on( 'add remove reset', this.visibility, this );
+ this.errors.on( 'add', this.error, this );
},
dispose: function() {
@@ -1854,6 +1865,7 @@
visibility: function() {
this.$el.toggleClass( 'uploading', !! this.queue.length );
+ this.$el.toggleClass( 'errors', !! this.errors.length );
this.$el.toggle( !! this.queue.length || !! this.errors.length );
},
@@ -1903,10 +1915,36 @@
this.$index.text( index + 1 );
this.$total.text( queue.length );
- this.$filename.html( active ? media.truncate( _.escape( active.get('filename') ), 24 ) : '' );
+ this.$filename.html( active ? this.filename( active.get('filename') ) : '' );
+ },
+
+ filename: function( filename ) {
+ return media.truncate( _.escape( filename ), 24 );
+ },
+
+ error: function( error ) {
+ this.views.add( '.upload-errors', new media.view.UploaderStatusError({
+ filename: this.filename( error.get('file').name ),
+ message: error.get('message')
+ }), { at: 0 });
+ },
+
+ dismiss: function( event ) {
+ var errors = this.views.get('.upload-errors');
+
+ event.preventDefault();
+
+ if ( errors )
+ _.invoke( errors, 'remove' );
+ wp.Uploader.errors.reset();
}
});
+ media.view.UploaderStatusError = media.View.extend({
+ className: 'upload-error',
+ template: media.template('uploader-status-error')
+ });
+
/**
* wp.media.view.Toolbar
*/
diff --git a/wp-includes/js/plupload/wp-plupload.js b/wp-includes/js/plupload/wp-plupload.js
index 79522e59c5..9d2db5fb31 100644
--- a/wp-includes/js/plupload/wp-plupload.js
+++ b/wp-includes/js/plupload/wp-plupload.js
@@ -88,7 +88,7 @@ window.wp = window.wp || {};
file.attachment.destroy();
Uploader.errors.unshift({
- message: message,
+ message: message || pluploadL10n.default_error,
data: data,
file: file
});
@@ -199,7 +199,7 @@ window.wp = window.wp || {};
if ( ! _.isObject( response ) || _.isUndefined( response.success ) )
return error( pluploadL10n.default_error, null, file );
else if ( ! response.success )
- return error( response.data.message, response.data, file );
+ return error( response.data && response.data.message, response.data, file );
_.each(['file','loaded','size','percent'], function( key ) {
file.attachment.unset( key );
diff --git a/wp-includes/media.php b/wp-includes/media.php
index e96bcea0b0..545aee7612 100644
--- a/wp-includes/media.php
+++ b/wp-includes/media.php
@@ -1503,6 +1503,8 @@ function wp_print_media_templates( $attachment ) {
+
+