Localisation updates from https://translatewiki.net.
[mediawiki.git] / resources / src / mediawiki.Upload.js
blobc942a50c62c4ff40bdfa3f669f023d33fc27f987
1 ( function () {
2         /**
3          * @classdesc Upload to a wiki. Most of the functionality is implemented
4          * in {@link mw.Api#upload} and friends, but this model class will tie it
5          * together as well as let you perform actions in a logical way.
6          *
7          * A simple example:
8          * ```
9          * var file = new OO.ui.SelectFileWidget(),
10          *   button = new OO.ui.ButtonWidget( { label: 'Save' } ),
11          *   upload = new mw.Upload;
12          *
13          * button.on( 'click', () => {
14          *   upload.setFile( file.getValue() );
15          *   upload.setFilename( file.getValue().name );
16          *   upload.upload();
17          * } );
18          *
19          * $( document.body ).append( file.$element, button.$element );
20          * ```
21          * You can also choose to {@link mw.Upload#uploadToStash stash the upload}
22          * and {@link mw.Upload#finishStashUpload finalize} it later:
23          * ```
24          * var file, // Some file object
25          *   upload = new mw.Upload,
26          *   stashPromise = $.Deferred();
27          *
28          * upload.setFile( file );
29          * upload.uploadToStash().then( () => {
30          *   stashPromise.resolve();
31          * } );
32          *
33          * stashPromise.then( () => {
34          *   upload.setFilename( 'foo' );
35          *   upload.setText( 'bar' );
36          *   upload.finishStashUpload().then( () => {
37          *     console.log( 'Done!' );
38          *   } );
39          * } );
40          * ```
41          * @class mw.Upload
42          *
43          * @constructor
44          * @description Used to represent an upload in progress on the frontend.
45          * @param {Object|mw.Api} [apiconfig] A mw.Api object (or subclass), or configuration
46          *     to pass to the constructor of mw.Api.
47          */
48         function Upload( apiconfig ) {
49                 this.api = ( apiconfig instanceof mw.Api ) ? apiconfig : new mw.Api( apiconfig );
51                 this.watchlist = false;
52                 this.text = '';
53                 this.comment = '';
54                 this.filename = null;
55                 this.file = null;
56                 this.setState( Upload.State.NEW );
58                 this.imageinfo = undefined;
59         }
61         const UP = Upload.prototype;
63         /**
64          * Get the mw.Api instance used by this Upload object.
65          *
66          * @name mw.Upload.prototype.getApi
67          * @method
68          * @return {jQuery.Promise<mw.Api>}
69          */
70         UP.getApi = function () {
71                 return $.Deferred().resolve( this.api ).promise();
72         };
74         /**
75          * Set the text of the file page, to be created on file upload.
76          *
77          * @name mw.Upload.prototype.setText
78          * @method
79          * @param {string} text
80          */
81         UP.setText = function ( text ) {
82                 this.text = text;
83         };
85         /**
86          * Set the filename, to be finalized on upload.
87          *
88          * @name mw.Upload.prototype.setFilename
89          * @method
90          * @param {string} filename
91          */
92         UP.setFilename = function ( filename ) {
93                 this.filename = filename;
94         };
96         /**
97          * Set the stashed file to finish uploading.
98          *
99          * @name mw.Upload.prototype.setFilekey
100          * @method
101          * @param {string} filekey
102          */
103         UP.setFilekey = function ( filekey ) {
104                 this.setState( Upload.State.STASHED );
105                 this.stashPromise = $.Deferred().resolve( ( data ) => this.api.uploadFromStash( filekey, data ) );
106         };
108         /**
109          * Sets the filename based on the filename as it was on the upload.
110          *
111          * @name mw.Upload.prototype.setFilenameFromFile
112          * @method
113          */
114         UP.setFilenameFromFile = function () {
115                 const file = this.getFile();
116                 if ( !file ) {
117                         return;
118                 }
119                 if ( file.nodeType && file.nodeType === Node.ELEMENT_NODE ) {
120                         // File input element, use getBasename to cut out the path
121                         this.setFilename( this.getBasename( file.value ) );
122                 } else if ( file.name ) {
123                         // HTML5 FileAPI File object, but use getBasename to be safe
124                         this.setFilename( this.getBasename( file.name ) );
125                 } else {
126                         // If we ever implement uploading files from clipboard, they might not have a name
127                         this.setFilename( '?' );
128                 }
129         };
131         /**
132          * Set the file to be uploaded.
133          *
134          * @name mw.Upload.prototype.setFile
135          * @method
136          * @param {HTMLInputElement|File|Blob} file
137          */
138         UP.setFile = function ( file ) {
139                 this.file = file;
140         };
142         /**
143          * Set whether the file should be watchlisted after upload.
144          *
145          * @name mw.Upload.prototype.setWatchlist
146          * @method
147          * @param {boolean} watchlist
148          */
149         UP.setWatchlist = function ( watchlist ) {
150                 this.watchlist = watchlist;
151         };
153         /**
154          * Set the edit comment for the upload.
155          *
156          * @name mw.Upload.prototype.setComment
157          * @method
158          * @param {string} comment
159          */
160         UP.setComment = function ( comment ) {
161                 this.comment = comment;
162         };
164         /**
165          * Get the text of the file page, to be created on file upload.
166          *
167          * @name mw.Upload.prototype.getText
168          * @method
169          * @return {string}
170          */
171         UP.getText = function () {
172                 return this.text;
173         };
175         /**
176          * Get the filename, to be finalized on upload.
177          *
178          * @name mw.Upload.prototype.getFilename
179          * @method
180          * @return {string}
181          */
182         UP.getFilename = function () {
183                 return this.filename;
184         };
186         /**
187          * Get the file being uploaded.
188          *
189          * @name mw.Upload.prototype.getFile
190          * @method
191          * @return {HTMLInputElement|File|Blob}
192          */
193         UP.getFile = function () {
194                 return this.file;
195         };
197         /**
198          * Get the boolean for whether the file will be watchlisted after upload.
199          *
200          * @name mw.Upload.prototype.getWatchlist
201          * @method
202          * @return {boolean}
203          */
204         UP.getWatchlist = function () {
205                 return this.watchlist;
206         };
208         /**
209          * Get the current value of the edit comment for the upload.
210          *
211          * @name mw.Upload.prototype.getComment
212          * @method
213          * @return {string}
214          */
215         UP.getComment = function () {
216                 return this.comment;
217         };
219         /**
220          * Gets the base filename from a path name.
221          *
222          * @name mw.Upload.prototype.getBasename
223          * @method
224          * @param {string} path
225          * @return {string}
226          */
227         UP.getBasename = function ( path ) {
228                 if ( path === undefined || path === null ) {
229                         return '';
230                 }
232                 // Find the index of the last path separator in the
233                 // path, and add 1. Then, take the entire string after that.
234                 return path.slice(
235                         Math.max(
236                                 path.lastIndexOf( '/' ),
237                                 path.lastIndexOf( '\\' )
238                         ) + 1
239                 );
240         };
242         /**
243          * Sets the state and state details (if any) of the upload.
244          *
245          * @name mw.Upload.prototype.setState
246          * @method
247          * @param {mw.Upload.State} state
248          * @param {Object} stateDetails
249          */
250         UP.setState = function ( state, stateDetails ) {
251                 this.state = state;
252                 this.stateDetails = stateDetails;
253         };
255         /**
256          * Gets the state of the upload.
257          *
258          * @name mw.Upload.prototype.getState
259          * @method
260          * @return {mw.Upload.State}
261          */
262         UP.getState = function () {
263                 return this.state;
264         };
266         /**
267          * Gets details of the current state.
268          *
269          * @name mw.Upload.prototype.getStateDetails
270          * @method
271          * @return {string}
272          */
273         UP.getStateDetails = function () {
274                 return this.stateDetails;
275         };
277         /**
278          * Get the imageinfo object for the finished upload.
279          * Only available once the upload is finished! Don't try to get it
280          * beforehand.
281          *
282          * @name mw.Upload.prototype.getImageInfo
283          * @method
284          * @return {Object|undefined}
285          */
286         UP.getImageInfo = function () {
287                 return this.imageinfo;
288         };
290         /**
291          * Upload the file directly.
292          *
293          * @name mw.Upload.prototype.upload
294          * @method
295          * @return {jQuery.Promise}
296          */
297         UP.upload = function () {
298                 if ( !this.getFile() ) {
299                         return $.Deferred().reject( 'No file to upload. Call setFile to add one.' );
300                 }
302                 if ( !this.getFilename() ) {
303                         return $.Deferred().reject( 'No filename set. Call setFilename to add one.' );
304                 }
306                 this.setState( Upload.State.UPLOADING );
308                 return this.api.chunkedUpload( this.getFile(), {
309                         watchlist: ( this.getWatchlist() ) ? 1 : undefined,
310                         comment: this.getComment(),
311                         filename: this.getFilename(),
312                         text: this.getText()
313                 } ).then( ( result ) => {
314                         this.setState( Upload.State.UPLOADED );
315                         this.imageinfo = result.upload.imageinfo;
316                         return result;
317                 }, ( errorCode, result ) => {
318                         if ( result && result.upload && result.upload.warnings ) {
319                                 this.setState( Upload.State.WARNING, result );
320                         } else {
321                                 this.setState( Upload.State.ERROR, result );
322                         }
323                         return $.Deferred().reject( errorCode, result );
324                 } );
325         };
327         /**
328          * Upload the file to the stash to be completed later.
329          *
330          * @name mw.Upload.prototype.uploadToStash
331          * @method
332          * @return {jQuery.Promise}
333          */
334         UP.uploadToStash = function () {
335                 if ( !this.getFile() ) {
336                         return $.Deferred().reject( 'No file to upload. Call setFile to add one.' );
337                 }
339                 if ( !this.getFilename() ) {
340                         this.setFilenameFromFile();
341                 }
343                 this.setState( Upload.State.UPLOADING );
345                 this.stashPromise = this.api.chunkedUploadToStash( this.getFile(), {
346                         ignorewarnings: true,
347                         filename: this.getFilename()
348                 } ).then( ( finishStash ) => {
349                         this.setState( Upload.State.STASHED );
350                         return finishStash;
351                 }, ( errorCode, result ) => {
352                         if ( result && result.upload && result.upload.warnings ) {
353                                 this.setState( Upload.State.WARNING, result );
354                         } else {
355                                 this.setState( Upload.State.ERROR, result );
356                         }
357                         return $.Deferred().reject( errorCode, result );
358                 } );
360                 return this.stashPromise;
361         };
363         /**
364          * Finish a stash upload.
365          *
366          * @name mw.Upload.prototype.finishStashUpload
367          * @method
368          * @return {jQuery.Promise}
369          */
370         UP.finishStashUpload = function () {
371                 if ( !this.stashPromise ) {
372                         return $.Deferred().reject( 'This upload has not been stashed, please upload it to the stash first.' );
373                 }
375                 return this.stashPromise.then( ( finishStash ) => {
376                         this.setState( Upload.State.UPLOADING );
378                         return finishStash( {
379                                 ignorewarnings: false,
380                                 watchlist: ( this.getWatchlist() ) ? 1 : undefined,
381                                 comment: this.getComment(),
382                                 filename: this.getFilename(),
383                                 text: this.getText()
384                         } ).then( ( result ) => {
385                                 this.setState( Upload.State.UPLOADED );
386                                 this.imageinfo = result.upload.imageinfo;
387                                 return result;
388                         }, ( errorCode, result ) => {
389                                 if ( result && result.upload && result.upload.warnings ) {
390                                         this.setState( Upload.State.WARNING, result );
391                                 } else {
392                                         this.setState( Upload.State.ERROR, result );
393                                 }
394                                 return $.Deferred().reject( errorCode, result );
395                         } );
396                 } );
397         };
399         mw.Upload = Upload;
401         /**
402          * @enum
403          *
404          * State of uploads represented in simple terms.
405          */
406         mw.Upload.State = {
407                 /** Upload not yet started */
408                 NEW: 0,
410                 /** Upload finished, but there was a warning */
411                 WARNING: 1,
413                 /** Upload finished, but there was an error */
414                 ERROR: 2,
416                 /** Upload in progress */
417                 UPLOADING: 3,
419                 /** Upload finished, but not published, call #finishStashUpload */
420                 STASHED: 4,
422                 /** Upload finished and published */
423                 UPLOADED: 5
424         };
425 }() );