Merge "CurlHttpRequest: Follow redirects even under open_basedir"
[lhc/web/wiklou.git] / resources / src / mediawiki / mediawiki.Upload.BookletLayout.js
1 ( function ( $, mw ) {
2
3 /**
4 * mw.Upload.BookletLayout encapsulates the process of uploading a file
5 * to MediaWiki using the {@link mw.Upload upload model}.
6 * The booklet 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.
10 *
11 * ## Structure
12 *
13 * The {@link OO.ui.BookletLayout booklet layout} has three steps:
14 *
15 * - **Upload**: Has a {@link OO.ui.SelectFileWidget field} to get the file object.
16 *
17 * - **Information**: Has a {@link OO.ui.FormLayout form} to collect metadata. This can be
18 * extended.
19 *
20 * - **Insert**: Has details on how to use the file that was uploaded.
21 *
22 * Each step has a form associated with it defined in
23 * {@link #renderUploadForm renderUploadForm},
24 * {@link #renderInfoForm renderInfoForm}, and
25 * {@link #renderInsertForm renderInfoForm}. The
26 * {@link #getFile getFile},
27 * {@link #getFilename getFilename}, and
28 * {@link #getText getText} methods are used to get
29 * the information filled in these forms, required to call
30 * {@link mw.Upload mw.Upload}.
31 *
32 * ## Usage
33 *
34 * See the {@link mw.Upload.Dialog upload dialog}.
35 *
36 * The {@link #event-fileUploaded fileUploaded},
37 * and {@link #event-fileSaved fileSaved} events can
38 * be used to get details of the upload.
39 *
40 * ## Extending
41 *
42 * To extend using {@link mw.Upload mw.Upload}, override
43 * {@link #renderInfoForm renderInfoForm} to render
44 * the form required for the specific use-case. Update the
45 * {@link #getFilename getFilename}, and
46 * {@link #getText getText} methods to return data
47 * from your newly created form. If you added new fields you'll also have
48 * to update the {@link #clear} method.
49 *
50 * If you plan to use a different upload model, apart from what is mentioned
51 * above, you'll also have to override the
52 * {@link #createUpload createUpload} method to
53 * return the new model. The {@link #saveFile saveFile}, and
54 * the {@link #uploadFile uploadFile} methods need to be
55 * overriden to use the new model and data returned from the forms.
56 *
57 * @class
58 * @extends OO.ui.BookletLayout
59 *
60 * @constructor
61 * @param {Object} config Configuration options
62 */
63 mw.Upload.BookletLayout = function ( config ) {
64 // Parent constructor
65 mw.Upload.BookletLayout.parent.call( this, config );
66
67 this.renderUploadForm();
68 this.renderInfoForm();
69 this.renderInsertForm();
70
71 this.addPages( [
72 new OO.ui.PageLayout( 'upload', {
73 scrollable: true,
74 padded: true,
75 content: [ this.uploadForm ]
76 } ),
77 new OO.ui.PageLayout( 'info', {
78 scrollable: true,
79 padded: true,
80 content: [ this.infoForm ]
81 } ),
82 new OO.ui.PageLayout( 'insert', {
83 scrollable: true,
84 padded: true,
85 content: [ this.insertForm ]
86 } )
87 ] );
88 };
89
90 /* Setup */
91
92 OO.inheritClass( mw.Upload.BookletLayout, OO.ui.BookletLayout );
93
94 /* Events */
95
96 /**
97 * The file has finished uploading
98 *
99 * @event fileUploaded
100 */
101
102 /**
103 * The file has been saved to the database
104 *
105 * @event fileSaved
106 */
107
108 /**
109 * The upload form has changed
110 *
111 * @event uploadValid
112 * @param {boolean} isValid The form is valid
113 */
114
115 /**
116 * The info form has changed
117 *
118 * @event infoValid
119 * @param {boolean} isValid The form is valid
120 */
121
122 /* Properties */
123
124 /**
125 * @property {OO.ui.FormLayout} uploadForm
126 * The form rendered in the first step to get the file object.
127 * Rendered in {@link #renderUploadForm renderUploadForm}.
128 */
129
130 /**
131 * @property {OO.ui.FormLayout} infoForm
132 * The form rendered in the second step to get metadata.
133 * Rendered in {@link #renderInfoForm renderInfoForm}
134 */
135
136 /**
137 * @property {OO.ui.FormLayout} insertForm
138 * The form rendered in the third step to show usage
139 * Rendered in {@link #renderInsertForm renderInsertForm}
140 */
141
142 /* Methods */
143
144 /**
145 * Initialize for a new upload
146 */
147 mw.Upload.BookletLayout.prototype.initialize = function () {
148 this.clear();
149 this.upload = this.createUpload();
150 this.setPage( 'upload' );
151 };
152
153 /**
154 * Create a new upload model
155 *
156 * @protected
157 * @return {mw.Upload} Upload model
158 */
159 mw.Upload.BookletLayout.prototype.createUpload = function () {
160 return new mw.Upload();
161 };
162
163 /* Uploading */
164
165 /**
166 * Uploads the file that was added in the upload form. Uses
167 * {@link #getFile getFile} to get the HTML5
168 * file object.
169 *
170 * @protected
171 * @fires fileUploaded
172 * @return {jQuery.Promise}
173 */
174 mw.Upload.BookletLayout.prototype.uploadFile = function () {
175 var file = this.getFile();
176
177 this.filenameWidget.setValue( file.name );
178 this.setPage( 'info' );
179
180 this.upload.setFile( file );
181 this.uploadPromise = this.upload.uploadToStash();
182 this.uploadPromise.then( this.emit.bind( this, 'fileUploaded' ) );
183
184 return this.uploadPromise;
185 };
186
187 /**
188 * Saves the stash finalizes upload. Uses
189 * {@link #getFilename getFilename}, and
190 * {@link #getText getText} to get details from
191 * the form.
192 *
193 * @protected
194 * @fires fileSaved
195 * @returns {jQuery.Promise} Rejects the promise with an
196 * {@link OO.ui.Error error}, or resolves if the upload was successful.
197 */
198 mw.Upload.BookletLayout.prototype.saveFile = function () {
199 var layout = this,
200 deferred = $.Deferred();
201
202 this.upload.setFilename( this.getFilename() );
203 this.upload.setText( this.getText() );
204
205 this.uploadPromise.always( function () {
206
207 if ( layout.upload.getState() === mw.Upload.State.ERROR ) {
208 deferred.reject( new OO.ui.Error( mw.msg( 'upload-process-error' ) ) );
209 return false;
210 }
211
212 if ( layout.upload.getState() === mw.Upload.State.WARNING ) {
213 deferred.reject( new OO.ui.Error( mw.msg( 'upload-process-error' ) ) );
214 return false;
215 }
216
217 layout.upload.finishStashUpload().always( function () {
218 var name;
219
220 if ( layout.upload.getState() === mw.Upload.State.ERROR ) {
221 deferred.reject( new OO.ui.Error( mw.msg( 'upload-process-error' ) ) );
222 return false;
223 }
224
225 if ( layout.upload.getState() === mw.Upload.State.WARNING ) {
226 deferred.reject( new OO.ui.Error( mw.msg( 'upload-process-warning' ) ) );
227 return false;
228 }
229
230 // Normalize page name and localise the 'File:' prefix
231 name = new mw.Title( 'File:' + layout.upload.getFilename() ).toString();
232 layout.filenameUsageWidget.setValue( '[[' + name + ']]' );
233 layout.setPage( 'insert' );
234
235 deferred.resolve();
236 layout.emit( 'fileSaved' );
237 } );
238 } );
239
240 return deferred.promise();
241 };
242
243 /* Form renderers */
244
245 /**
246 * Renders and returns the upload form and sets the
247 * {@link #uploadForm uploadForm} property.
248 *
249 * @protected
250 * @fires selectFile
251 * @returns {OO.ui.FormLayout}
252 */
253 mw.Upload.BookletLayout.prototype.renderUploadForm = function () {
254 var fieldset;
255
256 this.selectFileWidget = new OO.ui.SelectFileWidget();
257 fieldset = new OO.ui.FieldsetLayout( { label: mw.msg( 'upload-form-label-select-file' ) } );
258 fieldset.addItems( [ this.selectFileWidget ] );
259 this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
260
261 // Validation
262 this.selectFileWidget.on( 'change', this.onUploadFormChange.bind( this ) );
263
264 return this.uploadForm;
265 };
266
267 /**
268 * Handle change events to the upload form
269 *
270 * @protected
271 * @fires uploadValid
272 */
273 mw.Upload.BookletLayout.prototype.onUploadFormChange = function () {
274 this.emit( 'uploadValid', !!this.selectFileWidget.getValue() );
275 };
276
277 /**
278 * Renders and returns the information form for collecting
279 * metadata and sets the {@link #infoForm infoForm}
280 * property.
281 *
282 * @protected
283 * @returns {OO.ui.FormLayout}
284 */
285 mw.Upload.BookletLayout.prototype.renderInfoForm = function () {
286 var fieldset;
287
288 this.filenameWidget = new OO.ui.TextInputWidget( {
289 indicator: 'required',
290 required: true,
291 validate: /.+/
292 } );
293 this.descriptionWidget = new OO.ui.TextInputWidget( {
294 indicator: 'required',
295 required: true,
296 validate: /.+/,
297 multiline: true,
298 autosize: true
299 } );
300
301 fieldset = new OO.ui.FieldsetLayout( {
302 label: mw.msg( 'upload-form-label-infoform-title' )
303 } );
304 fieldset.addItems( [
305 new OO.ui.FieldLayout( this.filenameWidget, {
306 label: mw.msg( 'upload-form-label-infoform-name' ),
307 align: 'top'
308 } ),
309 new OO.ui.FieldLayout( this.descriptionWidget, {
310 label: mw.msg( 'upload-form-label-infoform-description' ),
311 align: 'top'
312 } )
313 ] );
314 this.infoForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
315
316 this.filenameWidget.on( 'change', this.onInfoFormChange.bind( this ) );
317 this.descriptionWidget.on( 'change', this.onInfoFormChange.bind( this ) );
318
319 return this.infoForm;
320 };
321
322 /**
323 * Handle change events to the info form
324 *
325 * @protected
326 * @fires infoValid
327 */
328 mw.Upload.BookletLayout.prototype.onInfoFormChange = function () {
329 var layout = this;
330 $.when(
331 this.filenameWidget.getValidity(),
332 this.descriptionWidget.getValidity()
333 ).done( function () {
334 layout.emit( 'infoValid', true );
335 } ).fail( function () {
336 layout.emit( 'infoValid', false );
337 } );
338 };
339
340 /**
341 * Renders and returns the insert form to show file usage and
342 * sets the {@link #insertForm insertForm} property.
343 *
344 * @protected
345 * @returns {OO.ui.FormLayout}
346 */
347 mw.Upload.BookletLayout.prototype.renderInsertForm = function () {
348 var fieldset;
349
350 this.filenameUsageWidget = new OO.ui.TextInputWidget();
351 fieldset = new OO.ui.FieldsetLayout( {
352 label: mw.msg( 'upload-form-label-usage-title' )
353 } );
354 fieldset.addItems( [
355 new OO.ui.FieldLayout( this.filenameUsageWidget, {
356 label: mw.msg( 'upload-form-label-usage-filename' ),
357 align: 'top'
358 } )
359 ] );
360 this.insertForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
361
362 return this.insertForm;
363 };
364
365 /* Getters */
366
367 /**
368 * Gets the file object from the
369 * {@link #uploadForm upload form}.
370 *
371 * @protected
372 * @returns {File|null}
373 */
374 mw.Upload.BookletLayout.prototype.getFile = function () {
375 return this.selectFileWidget.getValue();
376 };
377
378 /**
379 * Gets the file name from the
380 * {@link #infoForm information form}.
381 *
382 * @protected
383 * @returns {string}
384 */
385 mw.Upload.BookletLayout.prototype.getFilename = function () {
386 return this.filenameWidget.getValue();
387 };
388
389 /**
390 * Gets the page text from the
391 * {@link #infoForm information form}.
392 *
393 * @protected
394 * @returns {string}
395 */
396 mw.Upload.BookletLayout.prototype.getText = function () {
397 return this.descriptionWidget.getValue();
398 };
399
400 /* Setters */
401
402 /**
403 * Clear the values of all fields
404 *
405 * @protected
406 */
407 mw.Upload.BookletLayout.prototype.clear = function () {
408 this.selectFileWidget.setValue( null );
409 this.filenameWidget.setValue( null ).setValidityFlag( true );
410 this.descriptionWidget.setValue( null ).setValidityFlag( true );
411 this.filenameUsageWidget.setValue( null );
412 };
413
414 }( jQuery, mediaWiki ) );