From ed1db088974bbf17d7fcef7e3173d9889d7e46f2 Mon Sep 17 00:00:00 2001 From: Mark Holmquist Date: Mon, 6 Jul 2015 15:38:05 -0500 Subject: [PATCH] Add mw.Upload for easy tracking of uploads 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 | 1 + resources/Resources.php | 7 + resources/src/mediawiki/mediawiki.Upload.js | 260 ++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 resources/src/mediawiki/mediawiki.Upload.js diff --git a/maintenance/jsduck/categories.json b/maintenance/jsduck/categories.json index 96c05a1e8d..1d7a1ce2d8 100644 --- a/maintenance/jsduck/categories.json +++ b/maintenance/jsduck/categories.json @@ -22,6 +22,7 @@ "name": "General", "classes": [ "mw.Title", + "mw.Upload*", "mw.Uri", "mw.RegExp", "mw.messagePoster.*", diff --git a/resources/Resources.php b/resources/Resources.php index c6f25ac827..19c28788c3 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -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 index 0000000000..197ea6fc6a --- /dev/null +++ b/resources/src/mediawiki/mediawiki.Upload.js @@ -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 ) ); -- 2.20.1