Merge "Fix spelling mistakes in comments"
[lhc/web/wiklou.git] / resources / src / mediawiki / mediawiki.ForeignStructuredUpload.BookletLayout.js
1 /*global moment */
2 ( function ( $, mw ) {
3
4 /**
5 * mw.ForeignStructuredUpload.BookletLayout encapsulates the process
6 * of uploading a file to MediaWiki using the mw.ForeignStructuredUpload model.
7 *
8 * var uploadDialog = new mw.Upload.Dialog( {
9 * bookletClass: mw.ForeignStructuredUpload.BookletLayout,
10 * booklet: {
11 * target: 'local'
12 * }
13 * } );
14 * var windowManager = new OO.ui.WindowManager();
15 * $( 'body' ).append( windowManager.$element );
16 * windowManager.addWindows( [ uploadDialog ] );
17 *
18 * @class mw.ForeignStructuredUpload.BookletLayout
19 * @uses mw.ForeignStructuredUpload
20 * @extends mw.Upload.BookletLayout
21 * @cfg {string} [target] Used to choose the target repository.
22 * If nothing is passed, the {@link mw.ForeignUpload#property-target default} is used.
23 */
24 mw.ForeignStructuredUpload.BookletLayout = function ( config ) {
25 config = config || {};
26 // Parent constructor
27 mw.ForeignStructuredUpload.BookletLayout.parent.call( this, config );
28
29 this.target = config.target;
30 };
31
32 /* Setup */
33
34 OO.inheritClass( mw.ForeignStructuredUpload.BookletLayout, mw.Upload.BookletLayout );
35
36 /* Uploading */
37
38 /**
39 * @inheritdoc
40 */
41 mw.ForeignStructuredUpload.BookletLayout.prototype.initialize = function () {
42 var deferred = $.Deferred();
43 mw.ForeignStructuredUpload.BookletLayout.parent.prototype.initialize.call( this )
44 .done( function () {
45 // Point the CategorySelector to the right wiki
46 this.upload.apiPromise.done( function ( api ) {
47 // If this is a ForeignApi, it will have a apiUrl, otherwise we don't need to do anything
48 if ( api.apiUrl ) {
49 // Can't reuse the same object, CategorySelector calls #abort on its mw.Api instance
50 this.categoriesWidget.api = new mw.ForeignApi( api.apiUrl );
51 }
52 deferred.resolve();
53 }.bind( this ) );
54 }.bind( this ) );
55 return deferred.promise();
56 };
57
58 /**
59 * Returns a {@link mw.ForeignStructuredUpload mw.ForeignStructuredUpload}
60 * with the {@link #cfg-target target} specified in config.
61 *
62 * @protected
63 * @return {mw.Upload}
64 */
65 mw.ForeignStructuredUpload.BookletLayout.prototype.createUpload = function () {
66 return new mw.ForeignStructuredUpload( this.target );
67 };
68
69 /* Form renderers */
70
71 /**
72 * @inheritdoc
73 */
74 mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm = function () {
75 var
76 query = /[?&]uploadbucket=(\d)/.exec( location.search ),
77 isTestEnabled = !!mw.config.get( 'wgForeignUploadTestEnabled' ),
78 defaultBucket = mw.config.get( 'wgForeignUploadTestDefault' ) || 1,
79 userId = mw.config.get( 'wgUserId' );
80
81 if ( query && query[ 1 ] ) {
82 // Testing and debugging
83 this.shouldRecordBucket = false;
84 this.bucket = Number( query[ 1 ] );
85 } else if ( !userId || !isTestEnabled ) {
86 // a) Anonymous user. This can actually happen, because our software sucks.
87 // b) Test is not enabled on this wiki.
88 // In either case, display the old interface and don't record bucket on uploads.
89 this.shouldRecordBucket = false;
90 this.bucket = defaultBucket;
91 } else {
92 // Regular logged in user on a wiki where the test is running
93 this.shouldRecordBucket = true;
94 this.bucket = ( userId % 4 ) + 1; // 1, 2, 3, 4
95 }
96
97 return this[ 'renderUploadForm' + this.bucket ]();
98 };
99
100 /**
101 * Test option 1, the original one. See T120867.
102 */
103 mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm1 = function () {
104 var fieldset, $ownWorkMessage, $notOwnWorkMessage,
105 onUploadFormChange,
106 ownWorkMessage, notOwnWorkMessage, notOwnWorkLocal,
107 validTargets = mw.config.get( 'wgForeignUploadTargets' ),
108 target = this.target || validTargets[ 0 ] || 'local',
109 layout = this;
110
111 // Temporary override to make my life easier during A/B test
112 target = 'shared';
113
114 // foreign-structured-upload-form-label-own-work-message-local
115 // foreign-structured-upload-form-label-own-work-message-shared
116 ownWorkMessage = mw.message( 'foreign-structured-upload-form-label-own-work-message-' + target );
117 // foreign-structured-upload-form-label-not-own-work-message-local
118 // foreign-structured-upload-form-label-not-own-work-message-shared
119 notOwnWorkMessage = mw.message( 'foreign-structured-upload-form-label-not-own-work-message-' + target );
120 // foreign-structured-upload-form-label-not-own-work-local-local
121 // foreign-structured-upload-form-label-not-own-work-local-shared
122 notOwnWorkLocal = mw.message( 'foreign-structured-upload-form-label-not-own-work-local-' + target );
123
124 if ( !ownWorkMessage.exists() ) {
125 ownWorkMessage = mw.message( 'foreign-structured-upload-form-label-own-work-message-default' );
126 }
127 if ( !notOwnWorkMessage.exists() ) {
128 notOwnWorkMessage = mw.message( 'foreign-structured-upload-form-label-not-own-work-message-default' );
129 }
130 if ( !notOwnWorkLocal.exists() ) {
131 notOwnWorkLocal = mw.message( 'foreign-structured-upload-form-label-not-own-work-local-default' );
132 }
133
134 $ownWorkMessage = $( '<p>' ).html( ownWorkMessage.parse() )
135 .addClass( 'mw-foreignStructuredUpload-bookletLayout-license' );
136 $notOwnWorkMessage = $( '<div>' ).append(
137 $( '<p>' ).html( notOwnWorkMessage.parse() ),
138 $( '<p>' ).html( notOwnWorkLocal.parse() )
139 );
140 $ownWorkMessage.add( $notOwnWorkMessage ).find( 'a' )
141 .attr( 'target', '_blank' )
142 .on( 'click', function ( e ) {
143 // Some stupid code is trying to prevent default on all clicks, which causes the links to
144 // not be openable, don't let it
145 e.stopPropagation();
146 } );
147
148 this.selectFileWidget = new OO.ui.SelectFileWidget();
149 this.messageLabel = new OO.ui.LabelWidget( {
150 label: $notOwnWorkMessage
151 } );
152 this.ownWorkCheckbox = new OO.ui.CheckboxInputWidget().on( 'change', function ( on ) {
153 layout.messageLabel.toggle( !on );
154 } );
155
156 fieldset = new OO.ui.FieldsetLayout();
157 fieldset.addItems( [
158 new OO.ui.FieldLayout( this.selectFileWidget, {
159 align: 'top',
160 label: mw.msg( 'upload-form-label-select-file' )
161 } ),
162 new OO.ui.FieldLayout( this.ownWorkCheckbox, {
163 align: 'inline',
164 label: $( '<div>' ).append(
165 $( '<p>' ).text( mw.msg( 'foreign-structured-upload-form-label-own-work' ) ),
166 $ownWorkMessage
167 )
168 } ),
169 new OO.ui.FieldLayout( this.messageLabel, {
170 align: 'top'
171 } )
172 ] );
173 this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
174
175 onUploadFormChange = function () {
176 var file = this.selectFileWidget.getValue(),
177 ownWork = this.ownWorkCheckbox.isSelected(),
178 valid = !!file && ownWork;
179 this.emit( 'uploadValid', valid );
180 };
181
182 // Validation
183 this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
184 this.ownWorkCheckbox.on( 'change', onUploadFormChange.bind( this ) );
185
186 return this.uploadForm;
187 };
188
189 /**
190 * Test option 2, idea A from T121021. See T120867.
191 */
192 mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm2 = function () {
193 var fieldset, checkboxes, fields, onUploadFormChange;
194
195 this.selectFileWidget = new OO.ui.SelectFileWidget();
196 this.licenseCheckboxes = checkboxes = [
197 new OO.ui.CheckboxInputWidget(),
198 new OO.ui.CheckboxInputWidget(),
199 new OO.ui.CheckboxInputWidget(),
200 new OO.ui.CheckboxInputWidget()
201 ];
202
203 fields = [
204 new OO.ui.FieldLayout( this.selectFileWidget, {
205 align: 'top',
206 label: mw.msg( 'upload-form-label-select-file' )
207 } ),
208 new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
209 label: mw.message( 'foreign-structured-upload-form-2-label-intro' ).parseDom()
210 } ), {
211 align: 'top'
212 } ),
213 new OO.ui.FieldLayout( checkboxes[ 0 ], {
214 align: 'inline',
215 classes: [
216 'mw-foreignStructuredUpload-bookletLayout-withicon',
217 'mw-foreignStructuredUpload-bookletLayout-ownwork'
218 ],
219 label: mw.message( 'foreign-structured-upload-form-2-label-ownwork' ).parseDom()
220 } ),
221 new OO.ui.FieldLayout( checkboxes[ 1 ], {
222 align: 'inline',
223 classes: [
224 'mw-foreignStructuredUpload-bookletLayout-withicon',
225 'mw-foreignStructuredUpload-bookletLayout-noderiv'
226 ],
227 label: mw.message( 'foreign-structured-upload-form-2-label-noderiv' ).parseDom()
228 } ),
229 new OO.ui.FieldLayout( checkboxes[ 2 ], {
230 align: 'inline',
231 classes: [
232 'mw-foreignStructuredUpload-bookletLayout-withicon',
233 'mw-foreignStructuredUpload-bookletLayout-useful'
234 ],
235 label: mw.message( 'foreign-structured-upload-form-2-label-useful' ).parseDom()
236 } ),
237 new OO.ui.FieldLayout( checkboxes[ 3 ], {
238 align: 'inline',
239 classes: [
240 'mw-foreignStructuredUpload-bookletLayout-withicon',
241 'mw-foreignStructuredUpload-bookletLayout-ccbysa'
242 ],
243 label: mw.message( 'foreign-structured-upload-form-2-label-ccbysa' ).parseDom()
244 } ),
245 new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
246 label: $()
247 .add( $( '<p>' ).msg( 'foreign-structured-upload-form-2-label-alternative' ) )
248 .add( $( '<p>' ).msg( 'foreign-structured-upload-form-2-label-termsofuse' )
249 .addClass( 'mw-foreignStructuredUpload-bookletLayout-license' ) )
250 } ), {
251 align: 'top'
252 } )
253 ];
254
255 fieldset = new OO.ui.FieldsetLayout( { items: fields } );
256 this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
257
258 this.uploadForm.$element.find( 'a' )
259 .attr( 'target', '_blank' )
260 .on( 'click', function ( e ) {
261 // Some stupid code is trying to prevent default on all clicks, which causes the links to
262 // not be openable, don't let it
263 e.stopPropagation();
264 } );
265
266 onUploadFormChange = function () {
267 var file = this.selectFileWidget.getValue(),
268 checks = checkboxes.every( function ( checkbox ) {
269 return checkbox.isSelected();
270 } ),
271 valid = !!file && checks;
272 this.emit( 'uploadValid', valid );
273 };
274
275 // Validation
276 this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
277 checkboxes[ 0 ].on( 'change', onUploadFormChange.bind( this ) );
278 checkboxes[ 1 ].on( 'change', onUploadFormChange.bind( this ) );
279 checkboxes[ 2 ].on( 'change', onUploadFormChange.bind( this ) );
280 checkboxes[ 3 ].on( 'change', onUploadFormChange.bind( this ) );
281
282 return this.uploadForm;
283 };
284
285 /**
286 * Test option 3, idea D from T121021. See T120867.
287 */
288 mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm3 = function () {
289 var ownWorkCheckbox, fieldset, yesMsg, noMsg, selects, selectFields,
290 alternativeField, fields, onUploadFormChange;
291
292 this.selectFileWidget = new OO.ui.SelectFileWidget();
293 this.ownWorkCheckbox = ownWorkCheckbox = new OO.ui.CheckboxInputWidget();
294
295 yesMsg = mw.message( 'foreign-structured-upload-form-3-label-yes' ).text();
296 noMsg = mw.message( 'foreign-structured-upload-form-3-label-no' ).text();
297 selects = [
298 new OO.ui.RadioSelectWidget( {
299 items: [
300 new OO.ui.RadioOptionWidget( { data: false, label: yesMsg } ),
301 new OO.ui.RadioOptionWidget( { data: true, label: noMsg } )
302 ]
303 } ),
304 new OO.ui.RadioSelectWidget( {
305 items: [
306 new OO.ui.RadioOptionWidget( { data: true, label: yesMsg } ),
307 new OO.ui.RadioOptionWidget( { data: false, label: noMsg } )
308 ]
309 } ),
310 new OO.ui.RadioSelectWidget( {
311 items: [
312 new OO.ui.RadioOptionWidget( { data: false, label: yesMsg } ),
313 new OO.ui.RadioOptionWidget( { data: true, label: noMsg } )
314 ]
315 } )
316 ];
317
318 this.licenseSelectFields = selectFields = [
319 new OO.ui.FieldLayout( selects[ 0 ], {
320 align: 'top',
321 classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
322 label: mw.message( 'foreign-structured-upload-form-3-label-question-website' ).parseDom()
323 } ),
324 new OO.ui.FieldLayout( selects[ 1 ], {
325 align: 'top',
326 classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
327 label: mw.message( 'foreign-structured-upload-form-3-label-question-ownwork' ).parseDom()
328 } ).toggle( false ),
329 new OO.ui.FieldLayout( selects[ 2 ], {
330 align: 'top',
331 classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
332 label: mw.message( 'foreign-structured-upload-form-3-label-question-noderiv' ).parseDom()
333 } ).toggle( false )
334 ];
335
336 alternativeField = new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
337 label: mw.message( 'foreign-structured-upload-form-3-label-alternative' ).parseDom()
338 } ), {
339 align: 'top'
340 } ).toggle( false );
341
342 // Choosing the right answer to each question shows the next question.
343 // Switching to wrong answer hides all subsequent questions.
344 selects.forEach( function ( select, i ) {
345 select.on( 'choose', function ( selectedOption ) {
346 var isRightAnswer = !!selectedOption.getData();
347 alternativeField.toggle( !isRightAnswer );
348 if ( i + 1 === selectFields.length ) {
349 // Last question
350 return;
351 }
352 if ( isRightAnswer ) {
353 selectFields[ i + 1 ].toggle( true );
354 } else {
355 selectFields.slice( i + 1 ).forEach( function ( field ) {
356 field.fieldWidget.selectItem( null );
357 field.toggle( false );
358 } );
359 }
360 } );
361 } );
362
363 fields = [
364 new OO.ui.FieldLayout( this.selectFileWidget, {
365 align: 'top',
366 label: mw.msg( 'upload-form-label-select-file' )
367 } ),
368 selectFields[ 0 ],
369 selectFields[ 1 ],
370 selectFields[ 2 ],
371 alternativeField,
372 new OO.ui.FieldLayout( ownWorkCheckbox, {
373 classes: [ 'mw-foreignStructuredUpload-bookletLayout-checkbox' ],
374 align: 'inline',
375 label: mw.message( 'foreign-structured-upload-form-label-own-work-message-shared' ).parseDom()
376 } )
377 ];
378
379 // Must be done late, after it's been associated with the FieldLayout
380 ownWorkCheckbox.setDisabled( true );
381
382 fieldset = new OO.ui.FieldsetLayout( { items: fields } );
383 this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
384
385 this.uploadForm.$element.find( 'a' )
386 .attr( 'target', '_blank' )
387 .on( 'click', function ( e ) {
388 // Some stupid code is trying to prevent default on all clicks, which causes the links to
389 // not be openable, don't let it
390 e.stopPropagation();
391 } );
392
393 onUploadFormChange = function () {
394 var file = this.selectFileWidget.getValue(),
395 checkbox = ownWorkCheckbox.isSelected(),
396 rightAnswers = selects.every( function ( select ) {
397 return select.getSelectedItem() && !!select.getSelectedItem().getData();
398 } ),
399 valid = !!file && checkbox && rightAnswers;
400 ownWorkCheckbox.setDisabled( !rightAnswers );
401 if ( !rightAnswers ) {
402 ownWorkCheckbox.setSelected( false );
403 }
404 this.emit( 'uploadValid', valid );
405 };
406
407 // Validation
408 this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
409 this.ownWorkCheckbox.on( 'change', onUploadFormChange.bind( this ) );
410 selects[ 0 ].on( 'choose', onUploadFormChange.bind( this ) );
411 selects[ 1 ].on( 'choose', onUploadFormChange.bind( this ) );
412 selects[ 2 ].on( 'choose', onUploadFormChange.bind( this ) );
413
414 return this.uploadForm;
415 };
416
417 /**
418 * Test option 4, idea E from T121021. See T120867.
419 */
420 mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm4 = function () {
421 var fieldset, $guide;
422 this.renderUploadForm1();
423 fieldset = this.uploadForm.getItems()[ 0 ];
424
425 $guide = mw.template.get( 'mediawiki.ForeignStructuredUpload.BookletLayout', 'guide.html' ).render();
426 $guide.find( '.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good span' )
427 .msg( 'foreign-structured-upload-form-4-label-good' );
428 $guide.find( '.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad span' )
429 .msg( 'foreign-structured-upload-form-4-label-bad' );
430
431 // Note the index, we insert after the SelectFileWidget field
432 fieldset.addItems( [
433 new OO.ui.FieldLayout( new OO.ui.Widget( {
434 $content: $guide
435 } ), {
436 align: 'top'
437 } )
438 ], 1 );
439
440 // Hook for custom styles
441 fieldset.getItems()[ 2 ].$element.addClass( 'mw-foreignStructuredUpload-bookletLayout-guide-checkbox' );
442
443 // Streamline: remove mention of local Special:Upload
444 fieldset.getItems()[ 3 ].$element.find( 'p' ).last().remove();
445
446 return this.uploadForm;
447 };
448
449 /**
450 * @inheritdoc
451 */
452 mw.ForeignStructuredUpload.BookletLayout.prototype.onUploadFormChange = function () {};
453
454 /**
455 * @inheritdoc
456 */
457 mw.ForeignStructuredUpload.BookletLayout.prototype.renderInfoForm = function () {
458 var fieldset;
459
460 this.filenameWidget = new OO.ui.TextInputWidget( {
461 required: true,
462 validate: /.+/
463 } );
464 this.descriptionWidget = new OO.ui.TextInputWidget( {
465 required: true,
466 validate: /.+/,
467 multiline: true,
468 autosize: true
469 } );
470 this.dateWidget = new mw.widgets.DateInputWidget( {
471 $overlay: this.$overlay,
472 required: true,
473 mustBeBefore: moment().add( 1, 'day' ).locale( 'en' ).format( 'YYYY-MM-DD' ) // Tomorrow
474 } );
475 this.categoriesWidget = new mw.widgets.CategorySelector( {
476 // Can't be done here because we don't know the target wiki yet... done in #initialize.
477 // api: new mw.ForeignApi( ... ),
478 $overlay: this.$overlay
479 } );
480
481 fieldset = new OO.ui.FieldsetLayout( {
482 label: mw.msg( 'upload-form-label-infoform-title' )
483 } );
484 fieldset.addItems( [
485 new OO.ui.FieldLayout( this.filenameWidget, {
486 label: mw.msg( 'upload-form-label-infoform-name' ),
487 align: 'top'
488 } ),
489 new OO.ui.FieldLayout( this.descriptionWidget, {
490 label: mw.msg( 'upload-form-label-infoform-description' ),
491 align: 'top'
492 } ),
493 new OO.ui.FieldLayout( this.categoriesWidget, {
494 label: mw.msg( 'foreign-structured-upload-form-label-infoform-categories' ),
495 align: 'top'
496 } ),
497 new OO.ui.FieldLayout( this.dateWidget, {
498 label: mw.msg( 'foreign-structured-upload-form-label-infoform-date' ),
499 align: 'top'
500 } )
501 ] );
502 this.infoForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
503
504 // Validation
505 this.filenameWidget.on( 'change', this.onInfoFormChange.bind( this ) );
506 this.descriptionWidget.on( 'change', this.onInfoFormChange.bind( this ) );
507 this.dateWidget.on( 'change', this.onInfoFormChange.bind( this ) );
508
509 return this.infoForm;
510 };
511
512 /**
513 * @inheritdoc
514 */
515 mw.ForeignStructuredUpload.BookletLayout.prototype.onInfoFormChange = function () {
516 var layout = this;
517 $.when(
518 this.filenameWidget.getValidity(),
519 this.descriptionWidget.getValidity(),
520 this.dateWidget.getValidity()
521 ).done( function () {
522 layout.emit( 'infoValid', true );
523 } ).fail( function () {
524 layout.emit( 'infoValid', false );
525 } );
526 };
527
528 /* Getters */
529
530 /**
531 * @inheritdoc
532 */
533 mw.ForeignStructuredUpload.BookletLayout.prototype.getText = function () {
534 var language = mw.config.get( 'wgContentLanguage' );
535 this.upload.clearDescriptions();
536 this.upload.addDescription( language, this.descriptionWidget.getValue() );
537 this.upload.setDate( this.dateWidget.getValue() );
538 this.upload.clearCategories();
539 this.upload.addCategories( this.categoriesWidget.getItemsData() );
540 return this.upload.getText();
541 };
542
543 /* Setters */
544
545 /**
546 * @inheritdoc
547 */
548 mw.ForeignStructuredUpload.BookletLayout.prototype.clear = function () {
549 mw.ForeignStructuredUpload.BookletLayout.parent.prototype.clear.call( this );
550
551 if ( this.ownWorkCheckbox ) {
552 this.ownWorkCheckbox.setSelected( false );
553 }
554 if ( this.licenseCheckboxes ) {
555 this.licenseCheckboxes.forEach( function ( checkbox ) {
556 checkbox.setSelected( false );
557 } );
558 }
559 if ( this.licenseSelectFields ) {
560 this.licenseSelectFields.forEach( function ( field, i ) {
561 field.fieldWidget.selectItem( null );
562 if ( i !== 0 ) {
563 field.toggle( false );
564 }
565 } );
566 }
567
568 this.categoriesWidget.setItemsFromData( [] );
569 this.dateWidget.setValue( '' ).setValidityFlag( true );
570 };
571
572 }( jQuery, mediaWiki ) );