Import: Handle uploads with sha1 starting with 0 properly
[mediawiki.git] / resources / src / mediawiki / mediawiki.Upload.js
blobd80b4ebcc762da255a241754a062cdee4ccda426
1 ( function ( mw, $ ) {
2         var UP;
4         /**
5          * @class mw.Upload
6          *
7          * Used to represent an upload in progress on the frontend.
8          * Most of the functionality is implemented in mw.Api.plugin.upload,
9          * but this model class will tie it together as well as let you perform
10          * actions in a logical way.
11          *
12          * A simple example:
13          *
14          *     var file = new OO.ui.SelectFileWidget(),
15          *       button = new OO.ui.ButtonWidget( { label: 'Save' } ),
16          *       upload = new mw.Upload;
17          *
18          *     button.on( 'click', function () {
19          *       upload.setFile( file.getValue() );
20          *       upload.setFilename( file.getValue().name );
21          *       upload.upload();
22          *     } );
23          *
24          *     $( 'body' ).append( file.$element, button.$element );
25          *
26          * You can also choose to {@link #uploadToStash stash the upload} and
27          * {@link #finishStashUpload finalize} it later:
28          *
29          *     var file, // Some file object
30          *       upload = new mw.Upload,
31          *       stashPromise = $.Deferred();
32          *
33          *     upload.setFile( file );
34          *     upload.uploadToStash().then( function () {
35          *       stashPromise.resolve();
36          *     } );
37          *
38          *     stashPromise.then( function () {
39          *       upload.setFilename( 'foo' );
40          *       upload.setText( 'bar' );
41          *       upload.finishStashUpload().then( function () {
42          *         console.log( 'Done!' );
43          *       } );
44          *     } );
45          *
46          * @constructor
47          * @param {Object|mw.Api} [apiconfig] A mw.Api object (or subclass), or configuration
48          *     to pass to the constructor of mw.Api.
49          */
50         function Upload( apiconfig ) {
51                 this.api = ( apiconfig instanceof mw.Api ) ? apiconfig : new mw.Api( apiconfig );
53                 this.watchlist = false;
54                 this.text = '';
55                 this.comment = '';
56                 this.filename = null;
57                 this.file = null;
58                 this.setState( Upload.State.NEW );
60                 this.imageinfo = undefined;
61         }
63         UP = Upload.prototype;
65         /**
66          * Set the text of the file page, to be created on file upload.
67          *
68          * @param {string} text
69          */
70         UP.setText = function ( text ) {
71                 this.text = text;
72         };
74         /**
75          * Set the filename, to be finalized on upload.
76          *
77          * @param {string} filename
78          */
79         UP.setFilename = function ( filename ) {
80                 this.filename = filename;
81         };
83         /**
84          * Sets the filename based on the filename as it was on the upload.
85          */
86         UP.setFilenameFromFile = function () {
87                 var file = this.getFile();
88                 if ( !file ) {
89                         return;
90                 }
91                 if ( file.nodeType && file.nodeType === Node.ELEMENT_NODE ) {
92                         // File input element, use getBasename to cut out the path
93                         this.setFilename( this.getBasename( file.value ) );
94                 } else if ( file.name ) {
95                         // HTML5 FileAPI File object, but use getBasename to be safe
96                         this.setFilename( this.getBasename( file.name ) );
97                 } else {
98                         // If we ever implement uploading files from clipboard, they might not have a name
99                         this.setFilename( '?' );
100                 }
101         };
103         /**
104          * Set the file to be uploaded.
105          *
106          * @param {HTMLInputElement|File} file
107          */
108         UP.setFile = function ( file ) {
109                 this.file = file;
110         };
112         /**
113          * Set whether the file should be watchlisted after upload.
114          *
115          * @param {boolean} watchlist
116          */
117         UP.setWatchlist = function ( watchlist ) {
118                 this.watchlist = watchlist;
119         };
121         /**
122          * Set the edit comment for the upload.
123          *
124          * @param {string} comment
125          */
126         UP.setComment = function ( comment ) {
127                 this.comment = comment;
128         };
130         /**
131          * Get the text of the file page, to be created on file upload.
132          *
133          * @return {string}
134          */
135         UP.getText = function () {
136                 return this.text;
137         };
139         /**
140          * Get the filename, to be finalized on upload.
141          *
142          * @return {string}
143          */
144         UP.getFilename = function () {
145                 return this.filename;
146         };
148         /**
149          * Get the file being uploaded.
150          *
151          * @return {HTMLInputElement|File}
152          */
153         UP.getFile = function () {
154                 return this.file;
155         };
157         /**
158          * Get the boolean for whether the file will be watchlisted after upload.
159          *
160          * @return {boolean}
161          */
162         UP.getWatchlist = function () {
163                 return this.watchlist;
164         };
166         /**
167          * Get the current value of the edit comment for the upload.
168          *
169          * @return {string}
170          */
171         UP.getComment = function () {
172                 return this.comment;
173         };
175         /**
176          * Gets the base filename from a path name.
177          *
178          * @param {string} path
179          * @return {string}
180          */
181         UP.getBasename = function ( path ) {
182                 if ( path === undefined || path === null ) {
183                         return '';
184                 }
186                 // Find the index of the last path separator in the
187                 // path, and add 1. Then, take the entire string after that.
188                 return path.slice(
189                         Math.max(
190                                 path.lastIndexOf( '/' ),
191                                 path.lastIndexOf( '\\' )
192                         ) + 1
193                 );
194         };
196         /**
197          * Sets the state and state details (if any) of the upload.
198          *
199          * @param {mw.Upload.State} state
200          * @param {Object} stateDetails
201          */
202         UP.setState = function ( state, stateDetails ) {
203                 this.state = state;
204                 this.stateDetails = stateDetails;
205         };
207         /**
208          * Gets the state of the upload.
209          *
210          * @return {mw.Upload.State}
211          */
212         UP.getState = function () {
213                 return this.state;
214         };
216         /**
217          * Gets details of the current state.
218          *
219          * @return {string}
220          */
221         UP.getStateDetails = function () {
222                 return this.stateDetails;
223         };
225         /**
226          * Get the imageinfo object for the finished upload.
227          * Only available once the upload is finished! Don't try to get it
228          * beforehand.
229          *
230          * @return {Object|undefined}
231          */
232         UP.getImageInfo = function () {
233                 return this.imageinfo;
234         };
236         /**
237          * Upload the file directly.
238          *
239          * @return {jQuery.Promise}
240          */
241         UP.upload = function () {
242                 var upload = this;
244                 if ( !this.getFile() ) {
245                         return $.Deferred().reject( 'No file to upload. Call setFile to add one.' );
246                 }
248                 if ( !this.getFilename() ) {
249                         return $.Deferred().reject( 'No filename set. Call setFilename to add one.' );
250                 }
252                 this.setState( Upload.State.UPLOADING );
254                 return this.api.upload( this.getFile(), {
255                         watchlist: ( this.getWatchlist() ) ? 1 : undefined,
256                         comment: this.getComment(),
257                         filename: this.getFilename(),
258                         text: this.getText()
259                 } ).then( function ( result ) {
260                         upload.setState( Upload.State.UPLOADED );
261                         upload.imageinfo = result.upload.imageinfo;
262                         return result;
263                 }, function ( errorCode, result ) {
264                         if ( result && result.upload && result.upload.warnings ) {
265                                 upload.setState( Upload.State.WARNING, result );
266                         } else {
267                                 upload.setState( Upload.State.ERROR, result );
268                         }
269                         return $.Deferred().reject( errorCode, result );
270                 } );
271         };
273         /**
274          * Upload the file to the stash to be completed later.
275          *
276          * @return {jQuery.Promise}
277          */
278         UP.uploadToStash = function () {
279                 var upload = this;
281                 if ( !this.getFile() ) {
282                         return $.Deferred().reject( 'No file to upload. Call setFile to add one.' );
283                 }
285                 if ( !this.getFilename() ) {
286                         this.setFilenameFromFile();
287                 }
289                 this.setState( Upload.State.UPLOADING );
291                 this.stashPromise = this.api.uploadToStash( this.getFile(), {
292                         filename: this.getFilename()
293                 } ).then( function ( finishStash ) {
294                         upload.setState( Upload.State.STASHED );
295                         return finishStash;
296                 }, function ( errorCode, result ) {
297                         if ( result && result.upload && result.upload.warnings ) {
298                                 upload.setState( Upload.State.WARNING, result );
299                         } else {
300                                 upload.setState( Upload.State.ERROR, result );
301                         }
302                         return $.Deferred().reject( errorCode, result );
303                 } );
305                 return this.stashPromise;
306         };
308         /**
309          * Finish a stash upload.
310          *
311          * @return {jQuery.Promise}
312          */
313         UP.finishStashUpload = function () {
314                 var upload = this;
316                 if ( !this.stashPromise ) {
317                         return $.Deferred().reject( 'This upload has not been stashed, please upload it to the stash first.' );
318                 }
320                 return this.stashPromise.then( function ( finishStash ) {
321                         upload.setState( Upload.State.UPLOADING );
323                         return finishStash( {
324                                 bucket: upload.bucket, // Automatically ignored if undefined
325                                 watchlist: ( upload.getWatchlist() ) ? 1 : undefined,
326                                 comment: upload.getComment(),
327                                 filename: upload.getFilename(),
328                                 text: upload.getText()
329                         } ).then( function ( result ) {
330                                 upload.setState( Upload.State.UPLOADED );
331                                 upload.imageinfo = result.upload.imageinfo;
332                                 return result;
333                         }, function ( errorCode, result ) {
334                                 if ( result && result.upload && result.upload.warnings ) {
335                                         upload.setState( Upload.State.WARNING, result );
336                                 } else {
337                                         upload.setState( Upload.State.ERROR, result );
338                                 }
339                                 return $.Deferred().reject( errorCode, result );
340                         } );
341                 } );
342         };
344         /**
345          * @enum mw.Upload.State
346          * State of uploads represented in simple terms.
347          */
348         Upload.State = {
349                 /** Upload not yet started */
350                 NEW: 0,
352                 /** Upload finished, but there was a warning */
353                 WARNING: 1,
355                 /** Upload finished, but there was an error */
356                 ERROR: 2,
358                 /** Upload in progress */
359                 UPLOADING: 3,
361                 /** Upload finished, but not published, call #finishStashUpload */
362                 STASHED: 4,
364                 /** Upload finished and published */
365                 UPLOADED: 5
366         };
368         mw.Upload = Upload;
369 }( mediaWiki, jQuery ) );