summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/UploadWizard/resources/controller/uw.controller.Details.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/UploadWizard/resources/controller/uw.controller.Details.js')
-rw-r--r--www/wiki/extensions/UploadWizard/resources/controller/uw.controller.Details.js372
1 files changed, 372 insertions, 0 deletions
diff --git a/www/wiki/extensions/UploadWizard/resources/controller/uw.controller.Details.js b/www/wiki/extensions/UploadWizard/resources/controller/uw.controller.Details.js
new file mode 100644
index 00000000..f64b4ca0
--- /dev/null
+++ b/www/wiki/extensions/UploadWizard/resources/controller/uw.controller.Details.js
@@ -0,0 +1,372 @@
+/*
+ * 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 ( mw, uw, $, OO ) {
+ /**
+ * Represents the details step in the wizard.
+ *
+ * @class
+ * @extends uw.controller.Step
+ * @param {mw.Api} api
+ * @param {Object} config UploadWizard config object.
+ */
+ uw.controller.Details = function UWControllerDetails( api, config ) {
+ uw.controller.Step.call(
+ this,
+ new uw.ui.Details()
+ .on( 'start-details', this.startDetails.bind( this ) )
+ .on( 'finalize-details-after-removal', this.moveNext.bind( this ) ),
+ api,
+ config
+ );
+
+ this.stepName = 'details';
+ this.finishState = 'complete';
+
+ this.queue = new uw.ConcurrentQueue( {
+ count: this.config.maxSimultaneousConnections,
+ action: this.transitionOne.bind( this )
+ } );
+ };
+
+ OO.inheritClass( uw.controller.Details, uw.controller.Step );
+
+ /**
+ * Move to this step.
+ *
+ * @param {mw.UploadWizardUpload[]} uploads List of uploads being carried forward.
+ */
+ uw.controller.Details.prototype.load = function ( uploads ) {
+ var controller = this;
+
+ uw.controller.Step.prototype.load.call( this, uploads );
+
+ // make sure queue is empty before starting this step
+ this.queue.abortExecuting();
+
+ $.each( this.uploads, function ( i, upload ) {
+ var serialized;
+
+ // get existing details
+ serialized = upload.details ? upload.details.getSerialized() : null;
+
+ controller.createDetails( upload );
+ if ( upload.file.fromURL || ( upload.deedChooser && upload.deedChooser.deed.name === 'custom' ) ) {
+ upload.details.useCustomDeedChooser();
+ }
+ upload.details.attach();
+
+ // restore earlier details (user may have started inputting details,
+ // then went back some steps, and now got here again)
+ if ( serialized ) {
+ upload.details.setSerialized( serialized );
+ }
+ } );
+
+ // Show the widget allowing to copy selected metadata if there's more than one successful upload
+ if ( this.config.copyMetadataFeature ) {
+ this.addCopyMetadataFeature();
+ }
+ };
+
+ uw.controller.Details.prototype.moveNext = function () {
+ this.removeErrorUploads();
+
+ uw.controller.Step.prototype.moveNext.call( this );
+ };
+
+ uw.controller.Details.prototype.addCopyMetadataFeature = function () {
+ var first,
+ // uploads can only be edited when they're in a certain state:
+ // a flat out upload failure or a completed upload can not be edited
+ invalidStates = [ 'aborted', 'error', 'complete' ],
+ invalids = this.getUploadStatesCount( invalidStates ),
+ valids = this.uploads.length - invalids;
+
+ // no point in having this feature if there's no target to copy to
+ if ( valids < 2 ) {
+ return;
+ }
+
+ // The first upload is not necessarily the one we want to copy from
+ // E.g. the first upload could've gone through successfully, but the
+ // rest failed because of abusefilter (or another recoverable error), in
+ // which case we'll want the "copy" feature to appear below the 2nd
+ // upload (or the first not-yet-completed not flat-out-failed upload)
+ $.each( this.uploads, function ( i, upload ) {
+ if ( upload && invalidStates.indexOf( upload.state ) === -1 ) {
+ first = upload;
+ return false;
+ }
+ } );
+
+ // could not find a source upload to copy from
+ if ( !first ) {
+ return;
+ }
+
+ this.copyMetadataWidget = new uw.CopyMetadataWidget( {
+ copyFrom: first,
+ // Include the "source" upload in the targets too
+ copyTo: this.uploads
+ } );
+
+ $( first.details.div ).after( this.copyMetadataWidget.$element );
+ };
+
+ uw.controller.Details.prototype.removeCopyMetadataFeature = function () {
+ if ( this.copyMetadataWidget ) {
+ this.copyMetadataWidget.$element.remove();
+ }
+ };
+
+ /**
+ * @param {mw.UploadWizardUpload} upload
+ */
+ uw.controller.Details.prototype.createDetails = function ( upload ) {
+ upload.details = new mw.UploadWizardDetails( upload, $( '#mwe-upwiz-macro-files' ) );
+ };
+
+ /**
+ * Start details submit.
+ * TODO move the rest of the logic here from mw.UploadWizard
+ */
+ uw.controller.Details.prototype.startDetails = function () {
+ var details = this;
+
+ this.valid().done( function ( valid ) {
+ if ( valid ) {
+ details.ui.hideEndButtons();
+ details.submit();
+ } else {
+ details.showErrors();
+ }
+ } );
+ };
+
+ /**
+ * Check details for validity.
+ *
+ * @return {jQuery.Promise}
+ */
+ uw.controller.Details.prototype.valid = function () {
+ var detailsController = this,
+ // validityPromises will hold all promises for all uploads;
+ // prefilling with a bogus promise (no warnings & errors) to
+ // ensure $.when always resolves with an array of multiple
+ // results (if there's just 1, it would otherwise have just
+ // that one's arguments, instead of a multi-dimensional array
+ // of upload warnings & failures)
+ validityPromises = [ $.Deferred().resolve( [], [] ).promise() ],
+ titles = [];
+
+ $.each( this.uploads, function ( i, upload ) {
+ // Update any error/warning messages about all DetailsWidgets
+ var promise = upload.details.checkValidity( true ).then( function () {
+ var warnings = [],
+ errors = [],
+ title;
+
+ $.each( arguments, function ( i, result ) {
+ warnings = warnings.concat( result[ 0 ] );
+ errors = errors.concat( result[ 1 ] );
+ } );
+
+ // Seen this title before?
+ title = upload.details.getTitle();
+ if ( title ) {
+ title = title.getName() + '.' + mw.Title.normalizeExtension( title.getExtension() );
+ if ( titles[ title ] ) {
+ // Don't submit. Instead, set an error in details step.
+ upload.details.setDuplicateTitleError();
+ errors.push( mw.message( 'mwe-upwiz-error-title-duplicate' ) );
+ } else {
+ titles[ title ] = true;
+ }
+ }
+
+ return $.Deferred().resolve( warnings, errors ).promise();
+ } );
+
+ // Will hold an array of validation promises, one for each upload
+ validityPromises.push( promise );
+ } );
+
+ // validityPromises is an array of promises that each resolve with [warnings, errors]
+ // for each upload - now iterate them all to figure out if we can proceed
+ return $.when.apply( $, validityPromises ).then( function () {
+ var warnings = [],
+ errors = [];
+
+ $.each( arguments, function ( i, result ) {
+ warnings = warnings.concat( result[ 0 ] );
+ errors = errors.concat( result[ 1 ] );
+ } );
+
+ if ( errors.length > 0 ) {
+ return $.Deferred().resolve( false );
+ }
+
+ if ( warnings.length > 0 ) {
+ // Update warning count before dialog
+ detailsController.showErrors();
+ return detailsController.confirmationDialog( warnings );
+ }
+
+ return $.Deferred().resolve( true );
+ } );
+ };
+
+ uw.controller.Details.prototype.confirmationDialog = function ( warnings ) {
+ var i,
+ $message = $( '<p>' ).text( mw.message( 'mwe-upwiz-dialog-warning' ).text() ),
+ $ul = $( '<ul>' );
+
+ // parse warning messages
+ warnings = warnings.map( function ( warning ) {
+ return warning.text();
+ } );
+
+ // omit duplicates
+ warnings = warnings.filter( function ( warning, i, warnings ) {
+ return warnings.indexOf( warning ) === i;
+ } );
+
+ for ( i = 0; i < warnings.length; i++ ) {
+ $ul.append( $( '<li>' ).text( warnings[ i ] ) );
+ }
+
+ return OO.ui.confirm( $message.append( $ul ), {
+ title: mw.message( 'mwe-upwiz-dialog-title' ).text()
+ } );
+ };
+
+ uw.controller.Details.prototype.canTransition = function ( upload ) {
+ return (
+ uw.controller.Step.prototype.canTransition.call( this, upload ) &&
+ upload.state === this.stepName
+ );
+ };
+
+ /**
+ * Perform this step's changes on one upload.
+ *
+ * @param {mw.UploadWizardUpload} upload
+ * @return {jQuery.Promise}
+ */
+ uw.controller.Details.prototype.transitionOne = function ( upload ) {
+ return upload.details.submit();
+ };
+
+ /**
+ * Perform this step's changes on all uploads.
+ *
+ * @return {jQuery.Promise}
+ */
+ uw.controller.Details.prototype.transitionAll = function () {
+ var
+ deferred = $.Deferred(),
+ details = this;
+
+ $.each( this.uploads, function ( i, upload ) {
+ if ( details.canTransition( upload ) ) {
+ details.queue.addItem( upload );
+ }
+ } );
+
+ this.queue.on( 'complete', deferred.resolve );
+ this.queue.startExecuting();
+
+ return deferred.promise();
+ };
+
+ /**
+ * Submit details to the API.
+ *
+ * @return {jQuery.Promise}
+ */
+ uw.controller.Details.prototype.submit = function () {
+ var details = this;
+
+ $.each( this.uploads, function ( i, upload ) {
+ // Clear error state
+ if ( upload.state === 'error' || upload.state === 'recoverable-error' ) {
+ upload.state = details.stepName;
+ }
+
+ // Set details view to have correct title
+ upload.details.setVisibleTitle( upload.details.getTitle().getMain() );
+ } );
+
+ // Disable edit interface
+ this.ui.disableEdits();
+ this.removeCopyMetadataFeature();
+
+ return this.transitionAll().then( function () {
+ details.showErrors();
+
+ if ( details.showNext() ) {
+ details.moveNext();
+ }
+ } );
+ };
+
+ /**
+ * Show warnings and errors in the form.
+ * See UI class for more.
+ */
+ uw.controller.Details.prototype.showErrors = function () {
+ this.ui.enableEdits();
+
+ this.removeCopyMetadataFeature();
+ this.addCopyMetadataFeature();
+
+ this.ui.showWarnings(); // Scroll to the warning first so that any errors will have precedence
+ this.ui.showErrors();
+ };
+
+ /**
+ * Handler for when an upload is removed.
+ *
+ * @param {mw.UploadWizardUpload} upload
+ */
+ uw.controller.Details.prototype.removeUpload = function ( upload ) {
+ uw.controller.Step.prototype.removeUpload.call( this, upload );
+
+ this.queue.removeItem( upload );
+
+ if ( upload.details && upload.details.div ) {
+ upload.details.div.remove();
+ }
+
+ if ( this.uploads.length === 0 ) {
+ // If we have no more uploads, go to the "Upload" step. (This will go to "Thanks" step,
+ // which will skip itself in load() because there are no uploads left.)
+ uw.eventFlowLogger.logSkippedStep( this.stepName );
+ this.moveNext();
+ return;
+ }
+
+ this.removeCopyMetadataFeature();
+ // Make sure we still have more multiple uploads adding the
+ // copy feature again
+ if ( this.config.copyMetadataFeature ) {
+ this.addCopyMetadataFeature();
+ }
+ };
+
+}( mediaWiki, mediaWiki.uploadWizard, jQuery, OO ) );