resources/ui/steps/uw.ui.Upload.js
/*
* This file is part of the MediaWiki extension UploadWizard.
*
* UploadWizard is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* UploadWizard is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UploadWizard. If not, see <http://www.gnu.org/licenses/>.
*/
( function ( uw ) {
/**
* Represents the UI for the wizard's Upload step.
*
* @class
* @extends uw.ui.Step
* @param {Object} config UploadWizard config object.
*/
uw.ui.Upload = function UWUIUpload( config ) {
var upload = this;
this.config = config;
uw.ui.Step.call(
this,
'file'
);
this.$addFileContainer = $( '<div>' )
.attr( 'id', 'mwe-upwiz-add-file-container' )
.addClass( 'mwe-upwiz-add-files-0' );
this.$uploadCtrl = $( '<div>' )
.attr( 'id', 'mwe-upwiz-upload-ctrls' )
.addClass( 'mwe-upwiz-file ui-helper-clearfix' )
.append( this.$addFileContainer );
this.addFile = new OO.ui.SelectFileWidget( {
classes: [ 'mwe-upwiz-add-file' ],
multiple: true,
showDropTarget: true,
button: {
label: mw.message( 'mwe-upwiz-add-file-0-free' ).text(),
flags: [ 'progressive', 'primary' ]
}
} );
this.addFile.on( 'change', ( files ) => {
upload.emit( 'files-added', files );
upload.addFile.setValue( null );
} );
this.$addFileContainer.append( this.addFile.$element );
if ( this.isFlickrImportEnabled() ) {
this.$flickrAddFileContainer = $( '<div>' )
.attr( 'id', 'mwe-upwiz-upload-ctrl-flickr-container' );
this.$uploadCenterDivide = $( '<p>' )
.attr( 'id', 'mwe-upwiz-upload-ctr-divide' )
.text( mw.message( 'mwe-upwiz-add-flickr-or' ).text() );
this.addFlickrFile = new OO.ui.ButtonWidget( {
id: 'mwe-upwiz-add-flickr-file',
label: mw.message( 'mwe-upwiz-add-file-flickr' ).text(),
flags: 'progressive'
} ).on( 'click', () => {
upload.flickrInterfaceInit();
} );
this.$flickrAddFileContainer.append(
this.$uploadCenterDivide,
this.addFlickrFile.$element
);
this.$addFileContainer
.append( this.$flickrAddFileContainer );
this.$flickrSelectList = $( '<div>' )
.attr( 'id', 'mwe-upwiz-flickr-select-list' );
this.$flickrSelectListContainer = $( '<div>' )
.attr( 'id', 'mwe-upwiz-flickr-select-list-container' )
.addClass( 'ui-corner-all' )
.append(
$( '<div>' )
.text( mw.message(
'mwe-upwiz-multi-file-select2',
config.maxFlickrUploads
) ),
this.$flickrSelectList
);
// Button to move on & upload the files that were selected
this.flickrSelectButton = new OO.ui.ButtonWidget( {
id: 'mwe-upwiz-select-flickr',
label: mw.message( 'mwe-upwiz-add-file-0-free' ).text(),
flags: [ 'progressive', 'primary' ]
} );
this.$flickrSelectListContainer.append( this.flickrSelectButton.$element );
// A container holding a form
this.$flickrContainer = $( '<div>' ).attr( 'id', 'mwe-upwiz-upload-add-flickr-container' );
// Form whose submit event will be listened to and prevented
this.$flickrForm = $( '<form>' ).attr( 'id', 'mwe-upwiz-flickr-url-form' )
.appendTo( this.$flickrContainer )
.on( 'submit', () => {
var checker = new mw.FlickrChecker( upload, upload.flickrSelectButton );
upload.flickrButton.setDisabled( true );
upload.flickrChecker( checker );
// TODO Any particular reason to stopPropagation ?
return false;
} );
// The input that will hold a flickr URL entered by the user; will be appended to a form
this.flickrInput = new OO.ui.TextInputWidget( {
placeholder: mw.message( 'mwe-upwiz-flickr-input-placeholder' ).text()
} );
this.flickrButton = new OO.ui.ButtonInputWidget( {
label: mw.message( 'mwe-upwiz-add-flickr' ).text(),
flags: [ 'progressive', 'primary' ],
type: 'submit'
} );
this.flickrField = new OO.ui.ActionFieldLayout(
this.flickrInput, this.flickrButton, {
align: 'top',
classes: [ 'mwe-upwiz-flickr-field' ]
}
);
this.$flickrForm.append( this.flickrField.$element );
// Add disclaimer
$( '<div>' ).attr( 'id', 'mwe-upwiz-flickr-disclaimer' )
.append(
mw.message( 'mwe-upwiz-flickr-disclaimer1' ).parseDom(),
$( '<br>' ), mw.message( 'mwe-upwiz-flickr-disclaimer2' ).parseDom()
)
.appendTo( this.$flickrContainer );
}
this.nextStepButtonAllOk = new OO.ui.ButtonWidget( {
label: mw.message( 'mwe-upwiz-next-file' ).text(),
flags: [ 'progressive', 'primary' ]
} ).on( 'click', () => {
upload.emit( 'next-step' );
} );
this.retryButtonSomeFailed = new OO.ui.ButtonWidget( {
label: mw.message( 'mwe-upwiz-file-retry' ).text(),
flags: [ 'progressive' ]
} ).on( 'click', () => {
upload.hideEndButtons();
upload.emit( 'retry' );
} );
this.nextStepButtonSomeFailed = new OO.ui.ButtonWidget( {
label: mw.message( 'mwe-upwiz-next-file-despite-failures' ).text(),
flags: [ 'progressive', 'primary' ]
} ).on( 'click', () => {
upload.emit( 'next-step' );
} );
this.retryButtonAllFailed = new OO.ui.ButtonWidget( {
label: mw.message( 'mwe-upwiz-file-retry' ).text(),
flags: [ 'progressive' ]
} ).on( 'click', () => {
upload.hideEndButtons();
upload.emit( 'retry' );
} );
this.$fileList = $( '<div>' )
.attr( 'id', 'mwe-upwiz-filelist' )
.addClass( 'ui-corner-all' );
this.$progress = $( '<div>' )
.attr( 'id', 'mwe-upwiz-progress' )
.addClass( 'ui-helper-clearfix' );
this.addPreviousButton();
this.addNextButton();
};
OO.inheritClass( uw.ui.Upload, uw.ui.Step );
uw.ui.Upload.prototype.showProgressBar = function () {
this.$progress.show();
};
/**
* Updates the interface based on the number of uploads.
*
* @param {boolean} haveUploads Whether there are any uploads at all.
* @param {boolean} fewerThanMax Whether we can add more uploads.
*/
uw.ui.Upload.prototype.updateFileCounts = function ( haveUploads, fewerThanMax ) {
this.$fileList.toggleClass( 'mwe-upwiz-filled-filelist', haveUploads );
this.$addFileContainer.toggleClass( 'mwe-upwiz-add-files-0', !haveUploads );
this.setAddButtonText( haveUploads );
if ( haveUploads ) {
// we have uploads ready to go, so allow us to proceed
this.$addFileContainer.add( this.$buttons ).show();
if ( this.isFlickrImportEnabled() ) {
this.$uploadCenterDivide.hide();
}
// fix the rounded corners on file elements.
// we want them to be rounded only when their edge touched the top or bottom of the filelist.
this.$fileListings = this.$fileList.find( '.filled' );
this.$visibleFileListings = this.$fileListings.find( '.mwe-upwiz-visible-file' );
this.$visibleFileListings.removeClass( 'ui-corner-top ui-corner-bottom' );
this.$visibleFileListings.first().addClass( 'ui-corner-top' );
this.$visibleFileListings.last().addClass( 'ui-corner-bottom' );
this.showNoticeForImageMetadata( true );
// eslint-disable-next-line no-jquery/no-sizzle
this.$fileListings.filter( ':odd' ).addClass( 'odd' );
// eslint-disable-next-line no-jquery/no-sizzle
this.$fileListings.filter( ':even' ).removeClass( 'odd' );
} else {
this.hideEndButtons();
this.showNoticeForImageMetadata( false );
if ( this.isFlickrImportEnabled() ) {
this.$uploadCenterDivide.show();
}
}
this.addFile.setDisabled( !fewerThanMax );
if ( this.isFlickrImportEnabled() ) {
this.addFlickrFile.setDisabled( !fewerThanMax );
}
};
/**
* Changes the initial centered invitation button to something like "add another file"
*
* @param {boolean} more
*/
uw.ui.Upload.prototype.setAddButtonText = function ( more ) {
var msg = 'mwe-upwiz-add-file-',
fmsg = 'mwe-upwiz-add-file-flickr';
if ( more ) {
msg += 'n';
fmsg += '-n';
} else {
msg += '0-free';
}
this.addFile.selectButton.setLabel( mw.message( msg ).text() );
// if Flickr uploading is available to this user, show the "add more files from flickr" button
if ( this.isFlickrImportEnabled() ) {
// changes the flickr add button to "add more files from flickr" if necessary.
this.addFlickrFile.setLabel( mw.message( fmsg ).text() );
// jQuery likes to restore the wrong 'display' value when doing .show()
this.$flickrAddFileContainer.css( 'display', '' );
}
};
uw.ui.Upload.prototype.load = function ( uploads ) {
uw.ui.Step.prototype.load.call( this, uploads );
if ( uploads.length === 0 ) {
this.$fileList.removeClass( 'mwe-upwiz-filled-filelist' );
}
var $noticeMessage = $( '<span>' )
.append(
$( '<strong>' ).text( mw.message( 'mwe-upwiz-metadata-notice-header' ).text() ),
$( '<br>' ),
mw.message( 'mwe-upwiz-metadata-notice-description' ).parseDom()
);
this.notice = new OO.ui.MessageWidget( {
type: 'notice',
icon: 'pageSettings',
classes: [ 'mwe-upwiz-metadata-notice' ],
label: $noticeMessage
} );
this.$div.prepend(
$( '<div>' )
.attr( 'id', 'mwe-upwiz-files' )
.append(
this.$flickrSelectListContainer,
this.$fileList,
this.$uploadCtrl,
this.notice.$element
)
);
this.displayUploads( uploads );
};
uw.ui.Upload.prototype.displayUploads = function ( uploads ) {
var thumbPromise,
$uploadInterfaceDivs = $( [] );
uploads.forEach( ( upload ) => {
// We'll attach all interfaces to the DOM at once rather than one-by-one, for better
// performance
$uploadInterfaceDivs = $uploadInterfaceDivs.add( upload.ui.$div );
} );
// Attach all interfaces to the DOM
this.$fileList.append( $uploadInterfaceDivs );
// Display thumbnails, but not all at once because they're somewhat expensive to generate.
// This will wait for each thumbnail to be complete before starting the next one.
thumbPromise = $.Deferred().resolve();
uploads.forEach( ( upload ) => {
thumbPromise = thumbPromise.then( () => {
var deferred = $.Deferred();
setTimeout( function () {
if ( this.movedFrom ) {
// We're no longer displaying any of these thumbnails, stop
deferred.reject();
}
upload.ui.showThumbnail().done( () => {
deferred.resolve();
} );
} );
return deferred.promise();
} );
} );
};
uw.ui.Upload.prototype.addNextButton = function () {
var ui = this;
this.nextButtonPromise.done( () => {
ui.$buttons.append(
$( '<div>' )
.addClass( 'mwe-upwiz-file-next-all-ok mwe-upwiz-file-endchoice' )
.append(
new OO.ui.HorizontalLayout( {
items: [
new OO.ui.LabelWidget( {
label: mw.message( 'mwe-upwiz-file-all-ok' ).text()
} ),
ui.nextStepButtonAllOk
]
} ).$element
)
);
ui.$buttons.append(
$( '<div>' )
.addClass( 'mwe-upwiz-file-next-some-failed mwe-upwiz-file-endchoice' )
.append(
new OO.ui.HorizontalLayout( {
items: [
new OO.ui.LabelWidget( {
label: mw.message( 'mwe-upwiz-file-some-failed' ).text()
} ),
ui.retryButtonSomeFailed,
ui.nextStepButtonSomeFailed
]
} ).$element
)
);
ui.$buttons.append(
$( '<div>' )
.addClass( 'mwe-upwiz-file-next-all-failed mwe-upwiz-file-endchoice' )
.append(
new OO.ui.HorizontalLayout( {
items: [
new OO.ui.LabelWidget( {
label: mw.message( 'mwe-upwiz-file-all-failed' ).text()
} ),
ui.retryButtonAllFailed
]
} ).$element
)
);
ui.$buttons.append( ui.$progress );
} );
};
/**
* Hide the buttons for moving to the next step.
*/
uw.ui.Upload.prototype.hideEndButtons = function () {
this.$div
.find( '.mwe-upwiz-buttons .mwe-upwiz-file-endchoice' )
.hide();
};
/**
* @param {boolean} show
*/
uw.ui.Upload.prototype.showNoticeForImageMetadata = function ( show ) {
this.$div.find( '.mwe-upwiz-metadata-notice' ).toggle( show );
};
/**
* Shows an error dialog informing the user that some uploads have been omitted
* since they went over the max files limit.
*
* @param {number} filesUploaded The number of files that have been attempted to upload
*/
uw.ui.Upload.prototype.showTooManyFilesError = function ( filesUploaded ) {
mw.errorDialog(
mw.message(
'mwe-upwiz-too-many-files-text',
this.config.maxUploads,
filesUploaded
).text(),
mw.message( 'mwe-upwiz-too-many-files' ).text()
);
};
/**
* Shows an error dialog informing the user that an upload omitted because
* it is too large.
*
* @param {number} maxSize The max upload file size
* @param {number} size The actual upload file size
*/
uw.ui.Upload.prototype.showFileTooLargeError = function ( maxSize, size ) {
mw.errorDialog(
mw.message(
'mwe-upwiz-file-too-large-text',
uw.units.bytes( maxSize ),
uw.units.bytes( size )
).text(),
mw.message( 'mwe-upwiz-file-too-large' ).text()
);
};
/**
* @param {string} filename
* @param {string} extension
*/
uw.ui.Upload.prototype.showBadExtensionError = function ( filename, extension ) {
var $errorMessage = $( '<p>' ).msg( 'mwe-upwiz-upload-error-bad-filename-extension', extension );
this.showFilenameError( $errorMessage );
};
uw.ui.Upload.prototype.showMissingExtensionError = function () {
var $errorMessage = $( '<p>' ).msg( 'mwe-upwiz-upload-error-bad-filename-no-extension' );
this.showFilenameError(
$( '<div>' ).append(
$errorMessage,
$( '<p>' ).msg( 'mwe-upwiz-allowed-filename-extensions' ),
$( '<blockquote>' ).append( $( '<tt>' ).append(
mw.UploadWizard.config.fileExtensions.join( ' ' )
) )
)
);
};
/**
* @param {string} filename
* @param {string} basename
*/
uw.ui.Upload.prototype.showDuplicateError = function ( filename, basename ) {
this.showFilenameError( $( '<p>' ).msg( 'mwe-upwiz-upload-error-duplicate-filename-error', basename ) );
};
/**
* @param {string} filename
*/
uw.ui.Upload.prototype.showUnparseableFilenameError = function ( filename ) {
this.showFilenameError( mw.message( 'mwe-upwiz-unparseable-filename', filename ).escaped() );
};
/**
* Shows an error dialog informing the user that an upload has been omitted
* over its filename.
*
* @param {jQuery|string} message The error message
*/
uw.ui.Upload.prototype.showFilenameError = function ( message ) {
mw.errorDialog( message );
};
/**
* Checks whether flickr import is enabled and the current user has the rights to use it
*
* @return {boolean}
*/
uw.ui.Upload.prototype.isFlickrImportEnabled = function () {
return this.config.UploadFromUrl && this.config.flickrApiKey !== '';
};
/**
* Initiates the Interface to upload media from Flickr.
* Called when the user clicks on the 'Add images from Flickr' button.
*/
uw.ui.Upload.prototype.flickrInterfaceInit = function () {
// Hide containers for selecting files, and show the flickr interface instead
this.$addFileContainer.hide();
this.$flickrAddFileContainer.hide();
this.$flickrContainer.show();
this.flickrSelectButton.$element.show();
this.flickrButton.setDisabled( false );
// Insert form into the page
this.$div.find( '#mwe-upwiz-files' ).prepend( this.$flickrContainer );
this.flickrInput.focus();
};
/**
* Responsible for fetching license of the provided media.
*
* @param {mw.FlickrChecker} checker
*/
uw.ui.Upload.prototype.flickrChecker = function ( checker ) {
var flickrInputUrl = this.flickrInput.getValue();
checker.getLicenses().done( () => {
checker.checkFlickr( flickrInputUrl );
} );
};
/**
* Reset the interface if there is a problem while fetching the images from
* the URL entered by the user.
*/
uw.ui.Upload.prototype.flickrInterfaceReset = function () {
// first destroy it completely, then reshow the add button
this.flickrInterfaceDestroy();
this.flickrButton.setDisabled( false );
this.$flickrContainer.show();
this.flickrSelectButton.$element.show();
};
/**
* Removes the flickr interface.
*/
uw.ui.Upload.prototype.flickrInterfaceDestroy = function () {
this.flickrInput.setValue( '' );
this.$flickrSelectList.empty();
this.$flickrSelectListContainer.off().hide();
this.$flickrContainer.hide();
this.flickrButton.setDisabled( true );
this.flickrSelectButton.$element.hide();
};
}( mw.uploadWizard ) );