Add mw.Upload for easy tracking of uploads
authorMark Holmquist <mtraceur@member.fsf.org>
Mon, 6 Jul 2015 20:38:05 +0000 (15:38 -0500)
committerBartosz Dziewoński <matma.rex@gmail.com>
Mon, 13 Jul 2015 17:52:35 +0000 (17:52 +0000)
This class will be used in an mw-ooui upload widget, as well
as several other places, to create a simpler upload pipeline.

Bug: T103413
Change-Id: Ifbfa626421b1b55ecaa522c5e5ef1f7ea45c6527

maintenance/jsduck/categories.json
resources/Resources.php
resources/src/mediawiki/mediawiki.Upload.js [new file with mode: 0644]

index 96c05a1..1d7a1ce 100644 (file)
@@ -22,6 +22,7 @@
                                "name": "General",
                                "classes": [
                                        "mw.Title",
+                                       "mw.Upload*",
                                        "mw.Uri",
                                        "mw.RegExp",
                                        "mw.messagePoster.*",
index c6f25ac..19c2878 100644 (file)
@@ -1059,6 +1059,13 @@ return array(
                ),
                'targets' => array( 'desktop', 'mobile' ),
        ),
+       'mediawiki.Upload' => array(
+               'scripts' => 'resources/src/mediawiki/mediawiki.Upload.js',
+               'dependencies' => array(
+                       'dom-level2-shim',
+                       'mediawiki.api.upload',
+               ),
+       ),
        'mediawiki.toc' => array(
                'scripts' => 'resources/src/mediawiki/mediawiki.toc.js',
                'dependencies' => 'mediawiki.cookie',
diff --git a/resources/src/mediawiki/mediawiki.Upload.js b/resources/src/mediawiki/mediawiki.Upload.js
new file mode 100644 (file)
index 0000000..197ea6f
--- /dev/null
@@ -0,0 +1,260 @@
+( function ( mw, $ ) {
+       var UP;
+
+       /**
+        * @class mw.Upload
+        *
+        * Used to represent an upload in progress on the frontend.
+        * Most of the functionality is implemented in mw.Api.plugin.upload,
+        * but this model class will tie it together as well as let you perform
+        * actions in a logical way.
+        *
+        * @constructor
+        * @param {Object} apiconfig Passed to the constructor of mw.Api.
+        */
+       function Upload( apiconfig ) {
+               this.api = new mw.Api( apiconfig );
+
+               this.watchlist = false;
+               this.text = '';
+               this.comment = '';
+               this.filename = null;
+               this.file = null;
+               this.state = Upload.State.NEW;
+       }
+
+       UP = Upload.prototype;
+
+       /**
+        * Set the text of the file page, to be created on file upload.
+        * @param {string} text
+        */
+       UP.setText = function ( text ) {
+               this.text = text;
+       };
+
+       /**
+        * Set the filename, to be finalized on upload.
+        * @param {string} filename
+        */
+       UP.setFilename = function ( filename ) {
+               this.filename = filename;
+       };
+
+       /**
+        * Sets the filename based on the filename as it was on the upload.
+        */
+       UP.setFilenameFromFile = function () {
+               if ( this.file.nodeType && this.file.nodeType === Node.ELEMENT_NODE ) {
+                       // File input element, use getBasename to cut out the path
+                       this.setFilename( this.getBasename( this.file.value ) );
+               } else if ( this.file.name && this.file.lastModified ) {
+                       // HTML5 FileAPI File object, but use getBasename to be safe
+                       this.setFilename( this.getBasename( this.file.name ) );
+               }
+       };
+
+       /**
+        * Set the file to be uploaded.
+        * @param {HTMLInputElement|File} file
+        */
+       UP.setFile = function ( file ) {
+               this.file = file;
+       };
+
+       /**
+        * Set whether the file should be watchlisted after upload.
+        * @param {boolean} watchlist
+        */
+       UP.setWatchlist = function ( watchlist ) {
+               this.watchlist = watchlist;
+       };
+
+       /**
+        * Set the edit comment for the upload.
+        * @param {string} comment
+        */
+       UP.setComment = function ( comment ) {
+               this.comment = comment;
+       };
+
+       /**
+        * Get the text of the file page, to be created on file upload.
+        * @return {string}
+        */
+       UP.getText = function () {
+               return this.text;
+       };
+
+       /**
+        * Get the filename, to be finalized on upload.
+        * @return {string}
+        */
+       UP.getFilename = function () {
+               return this.filename;
+       };
+
+       /**
+        * Get the file being uploaded.
+        * @return {HTMLInputElement|File}
+        */
+       UP.getFile = function () {
+               return this.file;
+       };
+
+       /**
+        * Get the boolean for whether the file will be watchlisted after upload.
+        * @return {boolean}
+        */
+       UP.getWatchlist = function () {
+               return this.watchlist;
+       };
+
+       /**
+        * Get the current value of the edit comment for the upload.
+        * @return {string}
+        */
+       UP.getComment = function () {
+               return this.comment;
+       };
+
+       /**
+        * Gets the base filename from a path name.
+        * @param {string} path
+        * @return {string}
+        */
+       UP.getBasename = function ( path ) {
+               if ( path === undefined || path === null ) {
+                       return '';
+               }
+
+               // Find the index of the last path separator in the
+               // path, and add 1. Then, take the entire string after that.
+               return path.slice(
+                       Math.max(
+                               path.lastIndexOf( '/' ),
+                               path.lastIndexOf( '\\' )
+                       ) + 1
+               );
+       };
+
+       /**
+        * Gets the state of the upload.
+        * @return {mw.Upload.State}
+        */
+       UP.getState = function () {
+               return this.state;
+       };
+
+       /**
+        * Upload the file directly.
+        * @return {jQuery.Promise}
+        */
+       UP.upload = function () {
+               var upload = this;
+
+               if ( !this.file ) {
+                       return $.Deferred().reject( 'No file to upload. Call setFile to add one.' );
+               }
+
+               if ( !this.filename ) {
+                       return $.Deferred().reject( 'No filename set. Call setFilename to add one.' );
+               }
+
+               this.state = Upload.State.UPLOADING;
+
+               return this.api.upload( this.file, {
+                       watchlist: ( this.watchlist === true ) ? 1 : undefined,
+                       comment: this.comment,
+                       filename: this.filename,
+                       text: this.text
+               } ).then( function ( result ) {
+                       upload.state = Upload.State.UPLOADED;
+                       return result;
+               }, function () {
+                       upload.state = Upload.State.ERROR;
+               } );
+       };
+
+       /**
+        * Upload the file to the stash to be completed later.
+        * @return {jQuery.Promise}
+        */
+       UP.uploadToStash = function () {
+               var upload = this;
+
+               if ( !this.file ) {
+                       return $.Deferred().reject( 'No file to upload. Call setFile to add one.' );
+               }
+
+               if ( !this.filename ) {
+                       this.setFilenameFromFile();
+               }
+
+               this.state = Upload.State.UPLOADING;
+
+               this.stashPromise = this.api.uploadToStash( this.file, {
+                       filename: this.filename
+               } ).then( function ( finishStash ) {
+                       upload.state = Upload.State.STASHED;
+                       return finishStash;
+               }, function () {
+                       upload.state = Upload.State.ERROR;
+               } );
+
+               return this.stashPromise;
+       };
+
+       /**
+        * Finish a stash upload.
+        * @return {jQuery.Promise}
+        */
+       UP.finishStashUpload = function () {
+               var upload = this;
+
+               if ( !this.stashPromise ) {
+                       return $.Deferred().reject( 'This upload has not been stashed, please upload it to the stash first.' );
+               }
+
+               return this.stashPromise.then( function ( finishStash ) {
+                       upload.state = Upload.State.UPLOADING;
+
+                       return finishStash( {
+                               watchlist: ( upload.watchlist === true ) ? 1 : undefined,
+                               comment: upload.getComment(),
+                               filename: upload.getFilename(),
+                               text: upload.getText()
+                       } ).then( function () {
+                               upload.state = Upload.State.UPLOADED;
+                       }, function () {
+                               upload.state = Upload.State.ERROR;
+                       } );
+               } );
+       };
+
+       /**
+        * @enum mw.Upload.State
+        * State of uploads represented in simple terms.
+        */
+       Upload.State = {
+               /** Upload not yet started */
+               NEW: 0,
+
+               /** Upload finished, but there was a warning */
+               WARNING: 1,
+
+               /** Upload finished, but there was an error */
+               ERROR: 2,
+
+               /** Upload in progress */
+               UPLOADING: 3,
+
+               /** Upload finished, but not published, call #finishStashUpload */
+               STASHED: 4,
+
+               /** Upload finished and published */
+               UPLOADED: 5
+       };
+
+       mw.Upload = Upload;
+}( mediaWiki, jQuery ) );