4 * mw.Upload.Dialog encapsulates the process of uploading a file
5 * to MediaWiki using the {@link mw.Upload mw.Upload} model.
6 * The dialog emits events that can be used to get the stashed
7 * upload and the final file. It can be extended to accept
8 * additional fields from the user for specific scenarios like
9 * for Commons, or campaigns.
13 * The {@link OO.ui.ProcessDialog dialog} has three steps-
15 * - **Upload**: Has a {@link OO.ui.SelectFileWidget field} to get the
18 * - **Information**: Has a {@link OO.ui.FormLayout form} to
19 * collect metadata. This can be extended.
21 * - **Insert**: Has details on how to use the file that was uploaded.
23 * Each step has a form associated with it defined in
24 * {@link mw.Upload.Dialog#renderUploadForm renderUploadForm},
25 * {@link mw.Upload.Dialog#renderInfoForm renderInfoForm}, and
26 * {@link mw.Upload.Dialog#renderInsertForm renderInfoForm}. The
27 * {@link mw.Upload.Dialog#getFile getFile},
28 * {@link mw.Upload.Dialog#getFilename getFilename}, and
29 * {@link mw.Upload.Dialog#getText getText} methods are used to get
30 * the information filled in these forms, required to call
31 * {@link mw.Upload mw.Upload}.
35 * To use, setup a {@link OO.ui.WindowManager window manager} like for normal
38 * var uploadDialog = new mw.Upload.Dialog( { size: 'small' } );
39 * var windowManager = new OO.ui.WindowManager();
40 * $( 'body' ).append( windowManager.$element );
41 * windowManager.addWindows( [ uploadDialog ] );
42 * windowManager.openWindow( uploadDialog );
44 * The dialog's closing promise,
45 * {@link mw.Upload.Dialog#event-fileUploaded fileUploaded},
46 * and {@link mw.Upload.Dialog#event-fileSaved fileSaved} events can
47 * be used to get details of the upload
51 * To extend using {@link mw.Upload mw.Upload}, override
52 * {@link mw.Upload.Dialog#renderInfoForm renderInfoForm} to render
53 * the form required for the specific use-case. Update the
54 * {@link mw.Upload.Dialog#getFilename getFilename}, and
55 * {@link mw.Upload.Dialog#getText getText} methods to return data
56 * from your newly created form. If you added new fields you'll also have
57 * to update the {@link #getTeardownProcess} method.
59 * If you plan to use a different upload model, apart from what is mentioned
60 * above, you'll also have to override the
61 * {@link mw.Upload.Dialog#getUploadObject getUploadObject} method to
62 * return the new model. The {@link mw.Upload.Dialog#saveFile saveFile}, and
63 * the {@link mw.Upload.Dialog#uploadFile uploadFile} methods need to be
64 * overriden to use the new model and data returned from the forms.
66 * @class mw.Upload.Dialog
68 * @extends OO.ui.ProcessDialog
70 mw
.Upload
.Dialog = function ( config
) {
72 mw
.Upload
.Dialog
.parent
.call( this, config
);
77 OO
.inheritClass( mw
.Upload
.Dialog
, OO
.ui
.ProcessDialog
);
79 /* Static Properties */
86 mw
.Upload
.Dialog
.static.title
= mw
.msg( 'upload-dialog-title' );
92 mw
.Upload
.Dialog
.static.actions
= [
96 label
: mw
.msg( 'upload-dialog-button-cancel' ),
97 modes
: [ 'upload', 'insert', 'save' ]
100 flags
: [ 'primary', 'progressive' ],
101 label
: mw
.msg( 'upload-dialog-button-done' ),
106 flags
: [ 'primary', 'constructive' ],
107 label
: mw
.msg( 'upload-dialog-button-save' ),
112 flags
: [ 'primary', 'progressive' ],
113 label
: mw
.msg( 'upload-dialog-button-upload' ),
123 * @property {OO.ui.FormLayout} uploadForm
124 * The form rendered in the first step to get the file object.
125 * Rendered in {@link mw.Upload.Dialog#renderUploadForm renderUploadForm}.
129 * @property {OO.ui.FormLayout} infoForm
130 * The form rendered in the second step to get metadata.
131 * Rendered in {@link mw.Upload.Dialog#renderInfoForm renderInfoForm}
135 * @property {OO.ui.FormLayout} insertForm
136 * The form rendered in the third step to show usage
137 * Rendered in {@link mw.Upload.Dialog#renderInsertForm renderInsertForm}
143 * A `fileUploaded` event is emitted from the
144 * {@link mw.Upload.Dialog#uploadFile uploadFile} method.
146 * @event fileUploaded
150 * A `fileSaved` event is emitted from the
151 * {@link mw.Upload.Dialog#saveFile saveFile} method.
161 mw
.Upload
.Dialog
.prototype.initialize = function () {
162 mw
.Upload
.Dialog
.parent
.prototype.initialize
.call( this );
164 this.renderUploadForm();
165 this.renderInfoForm();
166 this.renderInsertForm();
168 this.uploadFormPanel
= new OO
.ui
.PanelLayout( {
171 content
: [ this.uploadForm
]
173 this.infoFormPanel
= new OO
.ui
.PanelLayout( {
176 content
: [ this.infoForm
]
178 this.insertFormPanel
= new OO
.ui
.PanelLayout( {
181 content
: [ this.insertForm
]
184 this.panels
= new OO
.ui
.StackLayout();
185 this.panels
.addItems( [
186 this.uploadFormPanel
,
191 this.$body
.append( this.panels
.$element
);
197 mw
.Upload
.Dialog
.prototype.getBodyHeight = function () {
202 * Switch between the panels.
204 * @param {string} panel Panel name: 'upload', 'info', 'insert'
206 mw
.Upload
.Dialog
.prototype.switchPanels = function ( panel
) {
209 this.panels
.setItem( this.uploadFormPanel
);
210 this.actions
.setMode( 'upload' );
213 this.panels
.setItem( this.infoFormPanel
);
214 this.actions
.setMode( 'save' );
217 this.panels
.setItem( this.insertFormPanel
);
218 this.actions
.setMode( 'insert' );
226 mw
.Upload
.Dialog
.prototype.getSetupProcess = function ( data
) {
227 return mw
.Upload
.Dialog
.parent
.prototype.getSetupProcess
.call( this, data
)
229 this.upload
= this.getUploadObject();
230 this.switchPanels( 'upload' );
231 this.actions
.setAbilities( { upload
: false } );
238 mw
.Upload
.Dialog
.prototype.getActionProcess = function ( action
) {
241 if ( action
=== 'upload' ) {
242 return new OO
.ui
.Process( function () {
243 dialog
.filenameWidget
.setValue( dialog
.getFile().name
);
244 dialog
.switchPanels( 'info' );
245 dialog
.actions
.setAbilities( { save
: false } );
246 return dialog
.uploadFile();
249 if ( action
=== 'save' ) {
250 return new OO
.ui
.Process( dialog
.saveFile() );
252 if ( action
=== 'insert' ) {
253 return new OO
.ui
.Process( function () {
254 dialog
.close( dialog
.upload
);
257 if ( action
=== 'cancel' ) {
258 return new OO
.ui
.Process( dialog
.close() );
261 return mw
.Upload
.Dialog
.parent
.prototype.getActionProcess
.call( this, action
);
267 mw
.Upload
.Dialog
.prototype.getTeardownProcess = function ( data
) {
268 return mw
.Upload
.Dialog
.parent
.prototype.getTeardownProcess
.call( this, data
)
270 // Clear the values of all fields
271 this.selectFileWidget
.setValue( null );
272 this.filenameWidget
.setValue( null ).setValidityFlag( true );
273 this.descriptionWidget
.setValue( null ).setValidityFlag( true );
274 this.filenameUsageWidget
.setValue( null );
281 * Get the upload model object required for this dialog. Can be
282 * extended to different models.
284 * @return {mw.Upload}
286 mw
.Upload
.Dialog
.prototype.getUploadObject = function () {
287 return new mw
.Upload();
291 * Uploads the file that was added in the upload form. Uses
292 * {@link mw.Upload.Dialog#getFile getFile} to get the HTML5
296 * @fires fileUploaded
297 * @return {jQuery.Promise}
299 mw
.Upload
.Dialog
.prototype.uploadFile = function () {
301 file
= this.getFile();
302 this.upload
.setFile( file
);
303 this.uploadPromise
= this.upload
.uploadToStash();
304 this.uploadPromise
.then( function () {
305 dialog
.emit( 'fileUploaded' );
308 return this.uploadPromise
;
312 * Saves the stash finalizes upload. Uses
313 * {@link mw.Upload.Dialog#getFilename getFilename}, and
314 * {@link mw.Upload.Dialog#getText getText} to get details from
319 * @returns {jQuery.Promise} Rejects the promise with an
320 * {@link OO.ui.Error error}, or resolves if the upload was successful.
322 mw
.Upload
.Dialog
.prototype.saveFile = function () {
324 promise
= $.Deferred();
326 this.upload
.setFilename( this.getFilename() );
327 this.upload
.setText( this.getText() );
329 this.uploadPromise
.always( function () {
331 if ( dialog
.upload
.getState() === mw
.Upload
.State
.ERROR
) {
332 promise
.reject( new OO
.ui
.Error( mw
.msg( 'upload-dialog-error' ) ) );
336 if ( dialog
.upload
.getState() === mw
.Upload
.State
.WARNING
) {
337 promise
.reject( new OO
.ui
.Error( mw
.msg( 'upload-dialog-error' ) ) );
341 dialog
.upload
.finishStashUpload().then( function () {
344 if ( dialog
.upload
.getState() === mw
.Upload
.State
.ERROR
) {
345 promise
.reject( new OO
.ui
.Error( mw
.msg( 'upload-dialog-error' ) ) );
349 if ( dialog
.upload
.getState() === mw
.Upload
.State
.WARNING
) {
350 promise
.reject( new OO
.ui
.Error( mw
.msg( 'upload-dialog-warning' ) ) );
354 // Normalize page name and localise the 'File:' prefix
355 name
= new mw
.Title( 'File:' + dialog
.upload
.getFilename() ).toString();
356 dialog
.filenameUsageWidget
.setValue( '[[' + name
+ ']]' );
357 dialog
.switchPanels( 'insert' );
360 dialog
.emit( 'fileSaved' );
364 return promise
.promise();
370 * Renders and returns the upload form and sets the
371 * {@link mw.Upload.Dialog#uploadForm uploadForm} property.
372 * Validates the form and
373 * {@link OO.ui.ActionSet#setAbilities sets abilities}
374 * for the dialog accordingly.
377 * @returns {OO.ui.FormLayout}
379 mw
.Upload
.Dialog
.prototype.renderUploadForm = function () {
383 this.selectFileWidget
= new OO
.ui
.SelectFileWidget();
384 fieldset
= new OO
.ui
.FieldsetLayout( { label
: mw
.msg( 'upload-dialog-label-select-file' ) } );
385 fieldset
.addItems( [ this.selectFileWidget
] );
386 this.uploadForm
= new OO
.ui
.FormLayout( { items
: [ fieldset
] } );
389 this.selectFileWidget
.on( 'change', function ( value
) {
390 dialog
.actions
.setAbilities( { upload
: !!value
} );
393 return this.uploadForm
;
397 * Renders and returns the information form for collecting
398 * metadata and sets the {@link mw.Upload.Dialog#infoForm infoForm}
400 * Validates the form and
401 * {@link OO.ui.ActionSet#setAbilities sets abilities}
402 * for the dialog accordingly.
405 * @returns {OO.ui.FormLayout}
407 mw
.Upload
.Dialog
.prototype.renderInfoForm = function () {
411 this.filenameWidget
= new OO
.ui
.TextInputWidget( {
412 indicator
: 'required',
416 this.descriptionWidget
= new OO
.ui
.TextInputWidget( {
417 indicator
: 'required',
424 fieldset
= new OO
.ui
.FieldsetLayout( {
425 label
: mw
.msg( 'upload-dialog-label-infoform-title' )
428 new OO
.ui
.FieldLayout( this.filenameWidget
, {
429 label
: mw
.msg( 'upload-dialog-label-infoform-name' ),
432 new OO
.ui
.FieldLayout( this.descriptionWidget
, {
433 label
: mw
.msg( 'upload-dialog-label-infoform-description' ),
437 this.infoForm
= new OO
.ui
.FormLayout( { items
: [ fieldset
] } );
440 function checkValidity() {
441 var validityPromises
= [
442 dialog
.filenameWidget
.isValid(),
443 dialog
.descriptionWidget
.isValid()
446 $.when
.apply( $, validityPromises
).done( function () {
448 values
= Array
.prototype.slice
.apply( arguments
);
449 allValid
= values
.every( function ( value
) {
453 dialog
.actions
.setAbilities( { save
: allValid
} );
456 this.filenameWidget
.on( 'change', checkValidity
);
457 this.descriptionWidget
.on( 'change', checkValidity
);
459 return this.infoForm
;
463 * Renders and returns the insert form to show file usage and
464 * sets the {@link mw.Upload.Dialog#insertForm insertForm} property.
467 * @returns {OO.ui.FormLayout}
469 mw
.Upload
.Dialog
.prototype.renderInsertForm = function () {
472 this.filenameUsageWidget
= new OO
.ui
.TextInputWidget();
473 fieldset
= new OO
.ui
.FieldsetLayout( {
474 label
: mw
.msg( 'upload-dialog-label-usage-title' )
477 new OO
.ui
.FieldLayout( this.filenameUsageWidget
, {
478 label
: mw
.msg( 'upload-dialog-label-usage-filename' ),
482 this.insertForm
= new OO
.ui
.FormLayout( { items
: [ fieldset
] } );
484 return this.insertForm
;
490 * Gets the file object from the
491 * {@link mw.Upload.Dialog#uploadForm upload form}.
494 * @returns {File|null}
496 mw
.Upload
.Dialog
.prototype.getFile = function () {
497 return this.selectFileWidget
.getValue();
501 * Gets the file name from the
502 * {@link mw.Upload.Dialog#infoForm information form}.
507 mw
.Upload
.Dialog
.prototype.getFilename = function () {
508 return this.filenameWidget
.getValue();
512 * Gets the page text from the
513 * {@link mw.Upload.Dialog#infoForm information form}.
518 mw
.Upload
.Dialog
.prototype.getText = function () {
519 return this.descriptionWidget
.getValue();
521 }( jQuery
, mediaWiki
) );