Finish stash uploads with upload dialog
Adds a 'filekey' option to the upload dialog's booklet layout so it's possible to complete a stashed upload with the dialog, even if the dialog didn't stash the file. Proof of concept gadget/userscript for testing purposes: https://phabricator.wikimedia.org/P2783 Or use this ImageTweaks patch to test: I8f8421697a6d44ec47b66496ad9ada548c4a7d0b Change-Id: I2cdea08a29cd481eb7fe5cbd83b9e4b6941a6380
This commit is contained in:
parent
541b2e7e5b
commit
11a2791668
6 changed files with 438 additions and 22 deletions
|
|
@ -1196,6 +1196,7 @@ return [
|
|||
'mediawiki.user',
|
||||
'mediawiki.Upload',
|
||||
'mediawiki.jqueryMsg',
|
||||
'mediawiki.widgets.StashedFileWidget'
|
||||
],
|
||||
'messages' => [
|
||||
'upload-form-label-infoform-title',
|
||||
|
|
@ -2237,7 +2238,19 @@ return [
|
|||
'position' => 'top',
|
||||
'targets' => [ 'desktop', 'mobile' ],
|
||||
],
|
||||
|
||||
'mediawiki.widgets.StashedFileWidget' => [
|
||||
'scripts' => [
|
||||
'resources/src/mediawiki.widgets/mw.widgets.StashedFileWidget.js',
|
||||
],
|
||||
'skinStyles' => [
|
||||
'default' => [
|
||||
'resources/src/mediawiki.widgets/mw.widgets.StashedFileWidget.less',
|
||||
],
|
||||
],
|
||||
'dependencies' => [
|
||||
'oojs-ui-core',
|
||||
],
|
||||
],
|
||||
/* es5-shim */
|
||||
'es5-shim' => [
|
||||
'scripts' => [
|
||||
|
|
|
|||
158
resources/src/mediawiki.widgets/mw.widgets.StashedFileWidget.js
Normal file
158
resources/src/mediawiki.widgets/mw.widgets.StashedFileWidget.js
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*!
|
||||
* MediaWiki Widgets - StashedFileWidget class.
|
||||
*
|
||||
* @copyright 2011-2016 MediaWiki Widgets Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
( function ( $, mw, OO ) {
|
||||
|
||||
/**
|
||||
* Accepts a stashed file and displays the information for purposes of
|
||||
* publishing the file at the behest of the user.
|
||||
*
|
||||
* Example use:
|
||||
* var widget = new mw.widgets.StashedFileWidget( {
|
||||
* filekey: '12r9e4rugeec.ddtmmp.1.jpg',
|
||||
* } );
|
||||
*
|
||||
* widget.getValue(); // '12r9e4rugeec.ddtmmp.1.jpg'
|
||||
* widget.setValue( '12r9epfbnskk.knfiy7.1.jpg' );
|
||||
* widget.getValue(); // '12r9epfbnskk.knfiy7.1.jpg'
|
||||
*
|
||||
* Note that this widget will not finish an upload for you. Use mw.Upload
|
||||
* and mw.Upload#setFilekey, then mw.Upload#finishStashUpload to accomplish
|
||||
* that.
|
||||
*
|
||||
* @class mw.widgets.StashedFileWidget
|
||||
* @extends OO.ui.Widget
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Object} config Configuration options
|
||||
* @cfg {string} filekey The filekey of the stashed file.
|
||||
* @cfg {Object} [api] API to use for thumbnails.
|
||||
*/
|
||||
mw.widgets.StashedFileWidget = function MWWStashedFileWidget( config ) {
|
||||
if ( !config.api ) {
|
||||
config.api = new mw.Api();
|
||||
}
|
||||
|
||||
// Parent constructor
|
||||
mw.widgets.StashedFileWidget.parent.call( this, config );
|
||||
|
||||
// Mixin constructors
|
||||
OO.ui.mixin.IconElement.call( this, config );
|
||||
OO.ui.mixin.LabelElement.call( this, config );
|
||||
OO.ui.mixin.PendingElement.call( this, config );
|
||||
|
||||
// Properties
|
||||
this.api = config.api;
|
||||
this.$info = $( '<span>' );
|
||||
this.setValue( config.filekey );
|
||||
this.$label.addClass( 'mw-widgets-stashedFileWidget-label' );
|
||||
this.$info
|
||||
.addClass( 'mw-widgets-stashedFileWidget-info' )
|
||||
.append( this.$icon, this.$label );
|
||||
|
||||
this.$thumbnail = $( '<div>' ).addClass( 'mw-widgets-stashedFileWidget-thumbnail' );
|
||||
this.setPendingElement( this.$thumbnail );
|
||||
|
||||
this.$thumbContain = $( '<div>' )
|
||||
.addClass( 'mw-widgets-stashedFileWidget-thumbnail-container' )
|
||||
.append( this.$thumbnail, this.$info );
|
||||
|
||||
this.$element
|
||||
.addClass( 'mw-widgets-stashedFileWidget' )
|
||||
.append( this.$thumbContain );
|
||||
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
OO.inheritClass( mw.widgets.StashedFileWidget, OO.ui.Widget );
|
||||
OO.mixinClass( mw.widgets.StashedFileWidget, OO.ui.mixin.IconElement );
|
||||
OO.mixinClass( mw.widgets.StashedFileWidget, OO.ui.mixin.LabelElement );
|
||||
OO.mixinClass( mw.widgets.StashedFileWidget, OO.ui.mixin.PendingElement );
|
||||
|
||||
/**
|
||||
* Get the current filekey.
|
||||
*
|
||||
* @return {string|null}
|
||||
*/
|
||||
mw.widgets.StashedFileWidget.prototype.getValue = function () {
|
||||
return this.filekey;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the filekey.
|
||||
*
|
||||
* @param {string|null} filekey
|
||||
*/
|
||||
mw.widgets.StashedFileWidget.prototype.setValue = function ( filekey ) {
|
||||
if ( filekey !== this.filekey ) {
|
||||
this.filekey = filekey;
|
||||
this.updateUI();
|
||||
this.emit( 'change', this.filekey );
|
||||
}
|
||||
};
|
||||
|
||||
mw.widgets.StashedFileWidget.prototype.updateUI = function () {
|
||||
var $label, $filetype;
|
||||
|
||||
if ( this.filekey ) {
|
||||
this.$element.removeClass( 'mw-widgets-stashedFileWidget-empty' );
|
||||
$label = $( [] );
|
||||
$filetype = $( '<span>' )
|
||||
.addClass( 'mw-widgets-stashedFileWidget-fileType' );
|
||||
|
||||
$label = $label.add(
|
||||
$( '<span>' )
|
||||
.addClass( 'mw-widgets-stashedFileWidget-filekey' )
|
||||
.text( this.filekey )
|
||||
).add( $filetype );
|
||||
|
||||
this.setLabel( $label );
|
||||
|
||||
this.pushPending();
|
||||
this.loadAndGetImageUrl().done( function ( url, mime ) {
|
||||
this.$thumbnail.css( 'background-image', 'url( ' + url + ' )' );
|
||||
if ( mime ) {
|
||||
$filetype.text( mime );
|
||||
this.setLabel( $label );
|
||||
}
|
||||
}.bind( this ) ).fail( function () {
|
||||
this.$thumbnail.append(
|
||||
new OO.ui.IconWidget( {
|
||||
icon: 'attachment',
|
||||
classes: [ 'mw-widgets-stashedFileWidget-noThumbnail-icon' ]
|
||||
} ).$element
|
||||
);
|
||||
}.bind( this ) ).always( function () {
|
||||
this.popPending();
|
||||
}.bind( this ) );
|
||||
} else {
|
||||
this.$element.addClass( 'mw-widgets-stashedFileWidget-empty' );
|
||||
this.setLabel( '' );
|
||||
}
|
||||
};
|
||||
|
||||
mw.widgets.StashedFileWidget.prototype.loadAndGetImageUrl = function () {
|
||||
var filekey = this.filekey;
|
||||
|
||||
if ( filekey ) {
|
||||
return this.api.get( {
|
||||
action: 'query',
|
||||
prop: 'stashimageinfo',
|
||||
siifilekey: filekey,
|
||||
siiprop: [ 'size', 'url', 'mime' ],
|
||||
siiurlwidth: 220
|
||||
} ).then( function ( data ) {
|
||||
var sii = data.query.stashimageinfo[ 0 ];
|
||||
|
||||
return $.Deferred().resolve( sii.thumburl, sii.mime );
|
||||
} );
|
||||
}
|
||||
|
||||
return $.Deferred().reject( 'No filekey' );
|
||||
};
|
||||
}( jQuery, mediaWiki, OO ) );
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
.mw-widgets-stashedFileWidget {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
max-width: 50em;
|
||||
margin-right: 0.5em;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.oo-ui-iconElement .mw-widgets-stashedFileWidget-info .mw-widgets-stashedFileWidget-label {
|
||||
left: 2.875em;
|
||||
}
|
||||
|
||||
&.oo-ui-indicatorElement .mw-widgets-stashedFileWidget-info .mw-widgets-stashedFileWidget-label {
|
||||
right: 4.4625em;
|
||||
}
|
||||
}
|
||||
|
||||
.mw-widgets-stashedFileWidget-info {
|
||||
height: 2.4em;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 2px;
|
||||
width: 100%;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
> .mw-widgets-stashedFileWidget-label {
|
||||
line-height: 2.3em;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
text-overflow: ellipsis;
|
||||
left: 0.5em;
|
||||
right: 2.375em;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
> .mw-widgets-stashedFileWidget-fileName {
|
||||
float: left;
|
||||
}
|
||||
> .mw-widgets-stashedFileWidget-fileType {
|
||||
color: #888888;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
> .oo-ui-indicatorElement-indicator,
|
||||
> .oo-ui-iconElement-icon {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
> .oo-ui-indicatorElement-indicator {
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 0.9375em;
|
||||
height: 2.3em;
|
||||
margin-right: 0.775em;
|
||||
}
|
||||
|
||||
> .oo-ui-iconElement-icon {
|
||||
top: 0;
|
||||
width: 1.875em;
|
||||
height: 2.3em;
|
||||
margin-left: 0.5em;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&.oo-ui-widget-disabled {
|
||||
.mw-widgets-stashedFileWidget-info {
|
||||
color: #cccccc;
|
||||
text-shadow: 0 1px 1px #ffffff;
|
||||
border-color: #dddddd;
|
||||
background-color: #f3f3f3;
|
||||
|
||||
> .oo-ui-iconElement-icon,
|
||||
> .oo-ui-indicatorElement-indicator {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mw-widgets-stashedFileWidget-thumbnail-container {
|
||||
cursor: default;
|
||||
height: 5.5em;
|
||||
text-align: left;
|
||||
padding: 0;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
margin-bottom: 0.5em;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
|
||||
.mw-widgets-stashedFileWidget-thumbnail {
|
||||
height: 5.5em;
|
||||
width: 5.5em;
|
||||
position: absolute;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
|
||||
&.oo-ui-pendingElement-pending {
|
||||
background-size: auto;
|
||||
}
|
||||
|
||||
> .mw-widgets-stashedFileWidget-noThumbnail-icon {
|
||||
opacity: 0.4;
|
||||
background-color: #cccccc;
|
||||
height: 5.5em;
|
||||
width: 5.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.mw-widgets-stashedFileWidget-info {
|
||||
border: none;
|
||||
background: none;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
margin-left: 5.5em;
|
||||
|
||||
> .mw-widgets-stashedFileWidget-label {
|
||||
position: relative;
|
||||
|
||||
> .mw-widgets-stashedFileWidget-fileName {
|
||||
display: block;
|
||||
float: none;
|
||||
}
|
||||
|
||||
> .mw-widgets-stashedFileWidget-fileType {
|
||||
display: block;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.mw-widgets-stashedFileWidget-empty {
|
||||
.mw-widgets-stashedFileWidget-thumbnail-container {
|
||||
text-align: center;
|
||||
|
||||
.mw-widgets-stashedFileWidget-thumbnail,
|
||||
.mw-widgets-stashedFileWidget-info {
|
||||
margin: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mw-widgets-stashedFileWidget-label {
|
||||
color: #cccccc;
|
||||
right: 0.5em;
|
||||
}
|
||||
|
||||
&.oo-ui-indicatorElement {
|
||||
.mw-widgets-stashedFileWidget-label {
|
||||
right: 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -347,21 +347,7 @@
|
|||
}
|
||||
|
||||
function finishUpload( moreData ) {
|
||||
data = $.extend( data, moreData );
|
||||
data.filekey = filekey;
|
||||
data.action = 'upload';
|
||||
data.format = 'json';
|
||||
|
||||
if ( !data.filename ) {
|
||||
throw new Error( 'Filename not included in file data.' );
|
||||
}
|
||||
|
||||
return api.postWithEditToken( data ).then( function ( result ) {
|
||||
if ( result.upload && result.upload.warnings ) {
|
||||
return $.Deferred().reject( getFirstKey( result.upload.warnings ), result ).promise();
|
||||
}
|
||||
return result;
|
||||
} );
|
||||
api.uploadFromStash( filekey, $.extend( data, moreData ) );
|
||||
}
|
||||
|
||||
return this.upload( file, { stash: true, filename: data.filename } ).then(
|
||||
|
|
@ -380,6 +366,29 @@
|
|||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Finish an upload in the stash.
|
||||
*
|
||||
* @param {string} filekey
|
||||
* @param {Object} data
|
||||
*/
|
||||
uploadFromStash: function ( filekey, data ) {
|
||||
data.filekey = filekey;
|
||||
data.action = 'upload';
|
||||
data.format = 'json';
|
||||
|
||||
if ( !data.filename ) {
|
||||
throw new Error( 'Filename not included in file data.' );
|
||||
}
|
||||
|
||||
return this.postWithEditToken( data ).then( function ( result ) {
|
||||
if ( result.upload && result.upload.warnings ) {
|
||||
return $.Deferred().reject( getFirstKey( result.upload.warnings ), result ).promise();
|
||||
}
|
||||
return result;
|
||||
} );
|
||||
},
|
||||
|
||||
needToken: function () {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@
|
|||
* @constructor
|
||||
* @param {Object} config Configuration options
|
||||
* @cfg {jQuery} [$overlay] Overlay to use for widgets in the booklet
|
||||
* @cfg {string} [filekey] Sets the stashed file to finish uploading. Overrides most of the file selection process, and fetches a thumbnail from the server.
|
||||
*/
|
||||
mw.Upload.BookletLayout = function ( config ) {
|
||||
// Parent constructor
|
||||
|
|
@ -68,6 +69,8 @@
|
|||
|
||||
this.$overlay = config.$overlay;
|
||||
|
||||
this.filekey = config.filekey;
|
||||
|
||||
this.renderUploadForm();
|
||||
this.renderInfoForm();
|
||||
this.renderInsertForm();
|
||||
|
|
@ -164,8 +167,13 @@
|
|||
|
||||
this.clear();
|
||||
this.upload = this.createUpload();
|
||||
|
||||
this.setPage( 'upload' );
|
||||
|
||||
if ( this.filekey ) {
|
||||
this.setFilekey( this.filekey );
|
||||
}
|
||||
|
||||
return this.upload.getApi().then(
|
||||
function ( api ) {
|
||||
// If the user can't upload anything, don't give them the option to.
|
||||
|
|
@ -217,10 +225,23 @@
|
|||
layout = this,
|
||||
file = this.getFile();
|
||||
|
||||
this.setFilename( file.name );
|
||||
|
||||
this.setPage( 'info' );
|
||||
|
||||
if ( this.filekey ) {
|
||||
if ( file === null ) {
|
||||
// Someone gonna get-a hurt real bad
|
||||
throw new Error( 'filekey not passed into file select widget, which is impossible. Quitting while we\'re behind.' );
|
||||
}
|
||||
|
||||
// Stashed file already uploaded.
|
||||
deferred.resolve();
|
||||
this.uploadPromise = deferred;
|
||||
this.emit( 'fileUploaded' );
|
||||
return deferred;
|
||||
}
|
||||
|
||||
this.setFilename( file.name );
|
||||
|
||||
this.upload.setFile( file );
|
||||
// The original file name might contain invalid characters, so use our sanitized one
|
||||
this.upload.setFilename( this.getFilename() );
|
||||
|
|
@ -402,14 +423,12 @@
|
|||
var fieldset,
|
||||
layout = this;
|
||||
|
||||
this.selectFileWidget = new OO.ui.SelectFileWidget( {
|
||||
showDropTarget: true
|
||||
} );
|
||||
this.selectFileWidget = this.getFileWidget();
|
||||
fieldset = new OO.ui.FieldsetLayout();
|
||||
fieldset.addItems( [ this.selectFileWidget ] );
|
||||
this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
|
||||
|
||||
// Validation
|
||||
// Validation (if the SFW is for a stashed file, this never fires)
|
||||
this.selectFileWidget.on( 'change', this.onUploadFormChange.bind( this ) );
|
||||
|
||||
this.selectFileWidget.on( 'change', function () {
|
||||
|
|
@ -419,6 +438,23 @@
|
|||
return this.uploadForm;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the widget for displaying or inputting the file to upload.
|
||||
*
|
||||
* @return {OO.ui.SelectFileWidget|mw.widgets.StashedFileWidget}
|
||||
*/
|
||||
mw.Upload.BookletLayout.prototype.getFileWidget = function () {
|
||||
if ( this.filekey ) {
|
||||
return new mw.widgets.StashedFileWidget( {
|
||||
filekey: this.filekey
|
||||
} );
|
||||
}
|
||||
|
||||
return new OO.ui.SelectFileWidget( {
|
||||
showDropTarget: true
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the file preview on the info form when a file is added.
|
||||
*
|
||||
|
|
@ -626,6 +662,20 @@
|
|||
this.selectFileWidget.setValue( file );
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the filekey of a file already stashed on the server
|
||||
* as the target of this upload operation.
|
||||
*
|
||||
* @protected
|
||||
* @param {string} filekey
|
||||
*/
|
||||
mw.Upload.BookletLayout.prototype.setFilekey = function ( filekey ) {
|
||||
this.upload.setFilekey( this.filekey );
|
||||
this.selectFileWidget.setValue( filekey );
|
||||
|
||||
this.onUploadFormChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the values of all fields
|
||||
*
|
||||
|
|
|
|||
|
|
@ -91,6 +91,20 @@
|
|||
this.filename = filename;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the stashed file to finish uploading.
|
||||
*
|
||||
* @param {string} filekey
|
||||
*/
|
||||
UP.setFilekey = function ( filekey ) {
|
||||
var upload = this;
|
||||
|
||||
this.setState( Upload.State.STASHED );
|
||||
this.stashPromise = $.Deferred().resolve( function ( data ) {
|
||||
return upload.api.uploadFromStash( filekey, data );
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the filename based on the filename as it was on the upload.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue