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 file object.
17 * - **Information**: Has a {@link OO.ui.FormLayout form} to collect metadata. This can be
20 * - **Insert**: Has details on how to use the file that was uploaded.
22 * Each step has a form associated with it defined in
23 * {@link mw.Upload.Dialog#renderUploadForm renderUploadForm},
24 * {@link mw.Upload.Dialog#renderInfoForm renderInfoForm}, and
25 * {@link mw.Upload.Dialog#renderInsertForm renderInfoForm}. The
26 * {@link mw.Upload.Dialog#getFile getFile},
27 * {@link mw.Upload.Dialog#getFilename getFilename}, and
28 * {@link mw.Upload.Dialog#getText getText} methods are used to get
29 * the information filled in these forms, required to call
30 * {@link mw.Upload mw.Upload}.
34 * To use, setup a {@link OO.ui.WindowManager window manager} like for normal
37 * var uploadDialog = new mw.Upload.Dialog();
38 * var windowManager = new OO.ui.WindowManager();
39 * $( 'body' ).append( windowManager.$element );
40 * windowManager.addWindows( [ uploadDialog ] );
41 * windowManager.openWindow( uploadDialog );
43 * The dialog's closing promise,
44 * {@link mw.Upload.Dialog#event-fileUploaded fileUploaded},
45 * and {@link mw.Upload.Dialog#event-fileSaved fileSaved} events can
46 * be used to get details of the upload.
50 * To extend using {@link mw.Upload mw.Upload}, override
51 * {@link mw.Upload.Dialog#renderInfoForm renderInfoForm} to render
52 * the form required for the specific use-case. Update the
53 * {@link mw.Upload.Dialog#getFilename getFilename}, and
54 * {@link mw.Upload.Dialog#getText getText} methods to return data
55 * from your newly created form. If you added new fields you'll also have
56 * to update the {@link #getTeardownProcess} method.
58 * If you plan to use a different upload model, apart from what is mentioned
59 * above, you'll also have to override the
60 * {@link mw.Upload.Dialog#getUploadObject getUploadObject} method to
61 * return the new model. The {@link mw.Upload.Dialog#saveFile saveFile}, and
62 * the {@link mw.Upload.Dialog#uploadFile uploadFile} methods need to be
63 * overriden to use the new model and data returned from the forms.
65 * @class mw.Upload.Dialog
67 * @extends OO.ui.ProcessDialog
69 mw
.Upload
.Dialog = function ( config
) {
71 mw
.Upload
.Dialog
.parent
.call( this, config
);
76 OO
.inheritClass( mw
.Upload
.Dialog
, OO
.ui
.ProcessDialog
);
78 /* Static Properties */
85 mw
.Upload
.Dialog
.static.title
= mw
.msg( 'upload-dialog-title' );
91 mw
.Upload
.Dialog
.static.actions
= [
95 label
: mw
.msg( 'upload-dialog-button-cancel' ),
96 modes
: [ 'upload', 'insert', 'save' ]
99 flags
: [ 'primary', 'progressive' ],
100 label
: mw
.msg( 'upload-dialog-button-done' ),
105 flags
: [ 'primary', 'constructive' ],
106 label
: mw
.msg( 'upload-dialog-button-save' ),
111 flags
: [ 'primary', 'progressive' ],
112 label
: mw
.msg( 'upload-dialog-button-upload' ),
122 * @property {OO.ui.FormLayout} uploadForm
123 * The form rendered in the first step to get the file object.
124 * Rendered in {@link mw.Upload.Dialog#renderUploadForm renderUploadForm}.
128 * @property {OO.ui.FormLayout} infoForm
129 * The form rendered in the second step to get metadata.
130 * Rendered in {@link mw.Upload.Dialog#renderInfoForm renderInfoForm}
134 * @property {OO.ui.FormLayout} insertForm
135 * The form rendered in the third step to show usage
136 * Rendered in {@link mw.Upload.Dialog#renderInsertForm renderInsertForm}
142 * A `fileUploaded` event is emitted from the
143 * {@link mw.Upload.Dialog#uploadFile uploadFile} method.
145 * @event fileUploaded
149 * A `fileSaved` event is emitted from the
150 * {@link mw.Upload.Dialog#saveFile saveFile} method.
160 mw
.Upload
.Dialog
.prototype.initialize = function () {
161 mw
.Upload
.Dialog
.parent
.prototype.initialize
.call( this );
163 this.renderUploadForm();
164 this.renderInfoForm();
165 this.renderInsertForm();
167 this.uploadFormPanel
= new OO
.ui
.PanelLayout( {
170 content
: [ this.uploadForm
]
172 this.infoFormPanel
= new OO
.ui
.PanelLayout( {
175 content
: [ this.infoForm
]
177 this.insertFormPanel
= new OO
.ui
.PanelLayout( {
180 content
: [ this.insertForm
]
183 this.panels
= new OO
.ui
.StackLayout();
184 this.panels
.addItems( [
185 this.uploadFormPanel
,
190 this.$body
.append( this.panels
.$element
);
196 mw
.Upload
.Dialog
.prototype.getBodyHeight = function () {
201 * Switch between the panels.
203 * @param {string} panel Panel name: 'upload', 'info', 'insert'
205 mw
.Upload
.Dialog
.prototype.switchPanels = function ( panel
) {
208 this.panels
.setItem( this.uploadFormPanel
);
209 this.actions
.setMode( 'upload' );
212 this.panels
.setItem( this.infoFormPanel
);
213 this.actions
.setMode( 'save' );
216 this.panels
.setItem( this.insertFormPanel
);
217 this.actions
.setMode( 'insert' );
225 mw
.Upload
.Dialog
.prototype.getSetupProcess = function ( data
) {
226 return mw
.Upload
.Dialog
.parent
.prototype.getSetupProcess
.call( this, data
)
228 this.upload
= this.getUploadObject();
229 this.switchPanels( 'upload' );
230 this.actions
.setAbilities( { upload
: false } );
237 mw
.Upload
.Dialog
.prototype.getActionProcess = function ( action
) {
240 if ( action
=== 'upload' ) {
241 return new OO
.ui
.Process( function () {
242 dialog
.filenameWidget
.setValue( dialog
.getFile().name
);
243 dialog
.switchPanels( 'info' );
244 dialog
.actions
.setAbilities( { save
: false } );
245 return dialog
.uploadFile();
248 if ( action
=== 'save' ) {
249 return new OO
.ui
.Process( dialog
.saveFile() );
251 if ( action
=== 'insert' ) {
252 return new OO
.ui
.Process( function () {
253 dialog
.close( dialog
.upload
);
256 if ( action
=== 'cancel' ) {
257 return new OO
.ui
.Process( dialog
.close() );
260 return mw
.Upload
.Dialog
.parent
.prototype.getActionProcess
.call( this, action
);
266 mw
.Upload
.Dialog
.prototype.getTeardownProcess = function ( data
) {
267 return mw
.Upload
.Dialog
.parent
.prototype.getTeardownProcess
.call( this, data
)
269 // Clear the values of all fields
270 this.selectFileWidget
.setValue( null );
271 this.filenameWidget
.setValue( null ).setValidityFlag( true );
272 this.descriptionWidget
.setValue( null ).setValidityFlag( true );
273 this.filenameUsageWidget
.setValue( null );
280 * Get the upload model object required for this dialog. Can be
281 * extended to different models.
283 * @return {mw.Upload}
285 mw
.Upload
.Dialog
.prototype.getUploadObject = function () {
286 return new mw
.Upload();
290 * Uploads the file that was added in the upload form. Uses
291 * {@link mw.Upload.Dialog#getFile getFile} to get the HTML5
295 * @fires fileUploaded
296 * @return {jQuery.Promise}
298 mw
.Upload
.Dialog
.prototype.uploadFile = function () {
300 file
= this.getFile();
301 this.upload
.setFile( file
);
302 this.uploadPromise
= this.upload
.uploadToStash();
303 this.uploadPromise
.then( function () {
304 dialog
.emit( 'fileUploaded' );
307 return this.uploadPromise
;
311 * Saves the stash finalizes upload. Uses
312 * {@link mw.Upload.Dialog#getFilename getFilename}, and
313 * {@link mw.Upload.Dialog#getText getText} to get details from
318 * @returns {jQuery.Promise} Rejects the promise with an
319 * {@link OO.ui.Error error}, or resolves if the upload was successful.
321 mw
.Upload
.Dialog
.prototype.saveFile = function () {
323 promise
= $.Deferred();
325 this.upload
.setFilename( this.getFilename() );
326 this.upload
.setText( this.getText() );
328 this.uploadPromise
.always( function () {
330 if ( dialog
.upload
.getState() === mw
.Upload
.State
.ERROR
) {
331 promise
.reject( new OO
.ui
.Error( mw
.msg( 'upload-dialog-error' ) ) );
335 if ( dialog
.upload
.getState() === mw
.Upload
.State
.WARNING
) {
336 promise
.reject( new OO
.ui
.Error( mw
.msg( 'upload-dialog-error' ) ) );
340 dialog
.upload
.finishStashUpload().always( function () {
343 if ( dialog
.upload
.getState() === mw
.Upload
.State
.ERROR
) {
344 promise
.reject( new OO
.ui
.Error( mw
.msg( 'upload-dialog-error' ) ) );
348 if ( dialog
.upload
.getState() === mw
.Upload
.State
.WARNING
) {
349 promise
.reject( new OO
.ui
.Error( mw
.msg( 'upload-dialog-warning' ) ) );
353 // Normalize page name and localise the 'File:' prefix
354 name
= new mw
.Title( 'File:' + dialog
.upload
.getFilename() ).toString();
355 dialog
.filenameUsageWidget
.setValue( '[[' + name
+ ']]' );
356 dialog
.switchPanels( 'insert' );
359 dialog
.emit( 'fileSaved' );
363 return promise
.promise();
369 * Renders and returns the upload form and sets the
370 * {@link mw.Upload.Dialog#uploadForm uploadForm} property.
371 * Validates the form and
372 * {@link OO.ui.ActionSet#setAbilities sets abilities}
373 * for the dialog accordingly.
376 * @returns {OO.ui.FormLayout}
378 mw
.Upload
.Dialog
.prototype.renderUploadForm = function () {
382 this.selectFileWidget
= new OO
.ui
.SelectFileWidget();
383 fieldset
= new OO
.ui
.FieldsetLayout( { label
: mw
.msg( 'upload-dialog-label-select-file' ) } );
384 fieldset
.addItems( [ this.selectFileWidget
] );
385 this.uploadForm
= new OO
.ui
.FormLayout( { items
: [ fieldset
] } );
388 this.selectFileWidget
.on( 'change', function ( value
) {
389 dialog
.actions
.setAbilities( { upload
: !!value
} );
392 return this.uploadForm
;
396 * Renders and returns the information form for collecting
397 * metadata and sets the {@link mw.Upload.Dialog#infoForm infoForm}
399 * Validates the form and
400 * {@link OO.ui.ActionSet#setAbilities sets abilities}
401 * for the dialog accordingly.
404 * @returns {OO.ui.FormLayout}
406 mw
.Upload
.Dialog
.prototype.renderInfoForm = function () {
410 this.filenameWidget
= new OO
.ui
.TextInputWidget( {
411 indicator
: 'required',
415 this.descriptionWidget
= new OO
.ui
.TextInputWidget( {
416 indicator
: 'required',
423 fieldset
= new OO
.ui
.FieldsetLayout( {
424 label
: mw
.msg( 'upload-dialog-label-infoform-title' )
427 new OO
.ui
.FieldLayout( this.filenameWidget
, {
428 label
: mw
.msg( 'upload-dialog-label-infoform-name' ),
431 new OO
.ui
.FieldLayout( this.descriptionWidget
, {
432 label
: mw
.msg( 'upload-dialog-label-infoform-description' ),
436 this.infoForm
= new OO
.ui
.FormLayout( { items
: [ fieldset
] } );
439 function checkValidity() {
441 dialog
.filenameWidget
.getValidity(),
442 dialog
.descriptionWidget
.getValidity()
443 ).done( function () {
444 dialog
.actions
.setAbilities( { save
: true } );
445 } ).fail( function () {
446 dialog
.actions
.setAbilities( { save
: false } );
449 this.filenameWidget
.on( 'change', checkValidity
);
450 this.descriptionWidget
.on( 'change', checkValidity
);
452 return this.infoForm
;
456 * Renders and returns the insert form to show file usage and
457 * sets the {@link mw.Upload.Dialog#insertForm insertForm} property.
460 * @returns {OO.ui.FormLayout}
462 mw
.Upload
.Dialog
.prototype.renderInsertForm = function () {
465 this.filenameUsageWidget
= new OO
.ui
.TextInputWidget();
466 fieldset
= new OO
.ui
.FieldsetLayout( {
467 label
: mw
.msg( 'upload-dialog-label-usage-title' )
470 new OO
.ui
.FieldLayout( this.filenameUsageWidget
, {
471 label
: mw
.msg( 'upload-dialog-label-usage-filename' ),
475 this.insertForm
= new OO
.ui
.FormLayout( { items
: [ fieldset
] } );
477 return this.insertForm
;
483 * Gets the file object from the
484 * {@link mw.Upload.Dialog#uploadForm upload form}.
487 * @returns {File|null}
489 mw
.Upload
.Dialog
.prototype.getFile = function () {
490 return this.selectFileWidget
.getValue();
494 * Gets the file name from the
495 * {@link mw.Upload.Dialog#infoForm information form}.
500 mw
.Upload
.Dialog
.prototype.getFilename = function () {
501 return this.filenameWidget
.getValue();
505 * Gets the page text from the
506 * {@link mw.Upload.Dialog#infoForm information form}.
511 mw
.Upload
.Dialog
.prototype.getText = function () {
512 return this.descriptionWidget
.getValue();
514 }( jQuery
, mediaWiki
) );