5 * mw.ForeignStructuredUpload.BookletLayout encapsulates the process
6 * of uploading a file to MediaWiki using the mw.ForeignStructuredUpload model.
8 * var uploadDialog = new mw.Upload.Dialog( {
9 * bookletClass: mw.ForeignStructuredUpload.BookletLayout,
14 * var windowManager = new OO.ui.WindowManager();
15 * $( 'body' ).append( windowManager.$element );
16 * windowManager.addWindows( [ uploadDialog ] );
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.
24 mw
.ForeignStructuredUpload
.BookletLayout = function ( config
) {
25 config
= config
|| {};
27 mw
.ForeignStructuredUpload
.BookletLayout
.parent
.call( this, config
);
29 this.target
= config
.target
;
34 OO
.inheritClass( mw
.ForeignStructuredUpload
.BookletLayout
, mw
.Upload
.BookletLayout
);
41 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.initialize = function () {
42 var deferred
= $.Deferred();
43 mw
.ForeignStructuredUpload
.BookletLayout
.parent
.prototype.initialize
.call( this )
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
49 // Can't reuse the same object, CategorySelector calls #abort on its mw.Api instance
50 this.categoriesWidget
.api
= new mw
.ForeignApi( api
.apiUrl
);
55 return deferred
.promise();
59 * Returns a {@link mw.ForeignStructuredUpload mw.ForeignStructuredUpload}
60 * with the {@link #cfg-target target} specified in config.
65 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.createUpload = function () {
66 return new mw
.ForeignStructuredUpload( this.target
);
74 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.renderUploadForm = function () {
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' );
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
;
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
97 return this[ 'renderUploadForm' + this.bucket
]();
101 * Test option 1, the original one. See T120867.
103 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.renderUploadForm1 = function () {
104 var fieldset
, $ownWorkMessage
, $notOwnWorkMessage
,
106 ownWorkMessage
, notOwnWorkMessage
, notOwnWorkLocal
,
107 validTargets
= mw
.config
.get( 'wgForeignUploadTargets' ),
108 target
= this.target
|| validTargets
[ 0 ] || 'local',
111 // Temporary override to make my life easier during A/B test
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
);
124 if ( !ownWorkMessage
.exists() ) {
125 ownWorkMessage
= mw
.message( 'foreign-structured-upload-form-label-own-work-message-default' );
127 if ( !notOwnWorkMessage
.exists() ) {
128 notOwnWorkMessage
= mw
.message( 'foreign-structured-upload-form-label-not-own-work-message-default' );
130 if ( !notOwnWorkLocal
.exists() ) {
131 notOwnWorkLocal
= mw
.message( 'foreign-structured-upload-form-label-not-own-work-local-default' );
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() )
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
148 this.selectFileWidget
= new OO
.ui
.SelectFileWidget();
149 this.messageLabel
= new OO
.ui
.LabelWidget( {
150 label
: $notOwnWorkMessage
152 this.ownWorkCheckbox
= new OO
.ui
.CheckboxInputWidget().on( 'change', function ( on
) {
153 layout
.messageLabel
.toggle( !on
);
156 fieldset
= new OO
.ui
.FieldsetLayout();
158 new OO
.ui
.FieldLayout( this.selectFileWidget
, {
160 label
: mw
.msg( 'upload-form-label-select-file' )
162 new OO
.ui
.FieldLayout( this.ownWorkCheckbox
, {
164 label
: $( '<div>' ).append(
165 $( '<p>' ).text( mw
.msg( 'foreign-structured-upload-form-label-own-work' ) ),
169 new OO
.ui
.FieldLayout( this.messageLabel
, {
173 this.uploadForm
= new OO
.ui
.FormLayout( { items
: [ fieldset
] } );
175 onUploadFormChange = function () {
176 var file
= this.selectFileWidget
.getValue(),
177 ownWork
= this.ownWorkCheckbox
.isSelected(),
178 valid
= !!file
&& ownWork
;
179 this.emit( 'uploadValid', valid
);
183 this.selectFileWidget
.on( 'change', onUploadFormChange
.bind( this ) );
184 this.ownWorkCheckbox
.on( 'change', onUploadFormChange
.bind( this ) );
186 return this.uploadForm
;
190 * Test option 2, idea A from T121021. See T120867.
192 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.renderUploadForm2 = function () {
193 var fieldset
, checkboxes
, fields
, onUploadFormChange
;
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()
204 new OO
.ui
.FieldLayout( this.selectFileWidget
, {
206 label
: mw
.msg( 'upload-form-label-select-file' )
208 new OO
.ui
.FieldLayout( new OO
.ui
.LabelWidget( {
209 label
: mw
.message( 'foreign-structured-upload-form-2-label-intro' ).parseDom()
213 new OO
.ui
.FieldLayout( checkboxes
[ 0 ], {
216 'mw-foreignStructuredUpload-bookletLayout-withicon',
217 'mw-foreignStructuredUpload-bookletLayout-ownwork'
219 label
: mw
.message( 'foreign-structured-upload-form-2-label-ownwork' ).parseDom()
221 new OO
.ui
.FieldLayout( checkboxes
[ 1 ], {
224 'mw-foreignStructuredUpload-bookletLayout-withicon',
225 'mw-foreignStructuredUpload-bookletLayout-noderiv'
227 label
: mw
.message( 'foreign-structured-upload-form-2-label-noderiv' ).parseDom()
229 new OO
.ui
.FieldLayout( checkboxes
[ 2 ], {
232 'mw-foreignStructuredUpload-bookletLayout-withicon',
233 'mw-foreignStructuredUpload-bookletLayout-useful'
235 label
: mw
.message( 'foreign-structured-upload-form-2-label-useful' ).parseDom()
237 new OO
.ui
.FieldLayout( checkboxes
[ 3 ], {
240 'mw-foreignStructuredUpload-bookletLayout-withicon',
241 'mw-foreignStructuredUpload-bookletLayout-ccbysa'
243 label
: mw
.message( 'foreign-structured-upload-form-2-label-ccbysa' ).parseDom()
245 new OO
.ui
.FieldLayout( new OO
.ui
.LabelWidget( {
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' ) )
255 fieldset
= new OO
.ui
.FieldsetLayout( { items
: fields
} );
256 this.uploadForm
= new OO
.ui
.FormLayout( { items
: [ fieldset
] } );
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
266 onUploadFormChange = function () {
267 var file
= this.selectFileWidget
.getValue(),
268 checks
= checkboxes
.every( function ( checkbox
) {
269 return checkbox
.isSelected();
271 valid
= !!file
&& checks
;
272 this.emit( 'uploadValid', valid
);
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 ) );
282 return this.uploadForm
;
286 * Test option 3, idea D from T121021. See T120867.
288 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.renderUploadForm3 = function () {
289 var ownWorkCheckbox
, fieldset
, yesMsg
, noMsg
, selects
, selectFields
,
290 alternativeField
, fields
, onUploadFormChange
;
292 this.selectFileWidget
= new OO
.ui
.SelectFileWidget();
293 this.ownWorkCheckbox
= ownWorkCheckbox
= new OO
.ui
.CheckboxInputWidget();
295 yesMsg
= mw
.message( 'foreign-structured-upload-form-3-label-yes' ).text();
296 noMsg
= mw
.message( 'foreign-structured-upload-form-3-label-no' ).text();
298 new OO
.ui
.RadioSelectWidget( {
300 new OO
.ui
.RadioOptionWidget( { data
: false, label
: yesMsg
} ),
301 new OO
.ui
.RadioOptionWidget( { data
: true, label
: noMsg
} )
304 new OO
.ui
.RadioSelectWidget( {
306 new OO
.ui
.RadioOptionWidget( { data
: true, label
: yesMsg
} ),
307 new OO
.ui
.RadioOptionWidget( { data
: false, label
: noMsg
} )
310 new OO
.ui
.RadioSelectWidget( {
312 new OO
.ui
.RadioOptionWidget( { data
: false, label
: yesMsg
} ),
313 new OO
.ui
.RadioOptionWidget( { data
: true, label
: noMsg
} )
318 this.licenseSelectFields
= selectFields
= [
319 new OO
.ui
.FieldLayout( selects
[ 0 ], {
321 classes
: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
322 label
: mw
.message( 'foreign-structured-upload-form-3-label-question-website' ).parseDom()
324 new OO
.ui
.FieldLayout( selects
[ 1 ], {
326 classes
: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
327 label
: mw
.message( 'foreign-structured-upload-form-3-label-question-ownwork' ).parseDom()
329 new OO
.ui
.FieldLayout( selects
[ 2 ], {
331 classes
: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
332 label
: mw
.message( 'foreign-structured-upload-form-3-label-question-noderiv' ).parseDom()
336 alternativeField
= new OO
.ui
.FieldLayout( new OO
.ui
.LabelWidget( {
337 label
: mw
.message( 'foreign-structured-upload-form-3-label-alternative' ).parseDom()
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
) {
352 if ( isRightAnswer
) {
353 selectFields
[ i
+ 1 ].toggle( true );
355 selectFields
.slice( i
+ 1 ).forEach( function ( field
) {
356 field
.fieldWidget
.selectItem( null );
357 field
.toggle( false );
364 new OO
.ui
.FieldLayout( this.selectFileWidget
, {
366 label
: mw
.msg( 'upload-form-label-select-file' )
372 new OO
.ui
.FieldLayout( ownWorkCheckbox
, {
373 classes
: [ 'mw-foreignStructuredUpload-bookletLayout-checkbox' ],
375 label
: mw
.message( 'foreign-structured-upload-form-label-own-work-message-shared' ).parseDom()
379 // Must be done late, after it's been associated with the FieldLayout
380 ownWorkCheckbox
.setDisabled( true );
382 fieldset
= new OO
.ui
.FieldsetLayout( { items
: fields
} );
383 this.uploadForm
= new OO
.ui
.FormLayout( { items
: [ fieldset
] } );
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
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();
399 valid
= !!file
&& checkbox
&& rightAnswers
;
400 ownWorkCheckbox
.setDisabled( !rightAnswers
);
401 if ( !rightAnswers
) {
402 ownWorkCheckbox
.setSelected( false );
404 this.emit( 'uploadValid', valid
);
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 ) );
414 return this.uploadForm
;
418 * Test option 4, idea E from T121021. See T120867.
420 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.renderUploadForm4 = function () {
421 var fieldset
, $guide
;
422 this.renderUploadForm1();
423 fieldset
= this.uploadForm
.getItems()[ 0 ];
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' );
431 // Note the index, we insert after the SelectFileWidget field
433 new OO
.ui
.FieldLayout( new OO
.ui
.Widget( {
440 // Hook for custom styles
441 fieldset
.getItems()[ 2 ].$element
.addClass( 'mw-foreignStructuredUpload-bookletLayout-guide-checkbox' );
443 // Streamline: remove mention of local Special:Upload
444 fieldset
.getItems()[ 3 ].$element
.find( 'p' ).last().remove();
446 return this.uploadForm
;
452 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.onUploadFormChange = function () {};
457 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.renderInfoForm = function () {
460 this.filenameWidget
= new OO
.ui
.TextInputWidget( {
464 this.descriptionWidget
= new OO
.ui
.TextInputWidget( {
470 this.dateWidget
= new mw
.widgets
.DateInputWidget( {
471 $overlay
: this.$overlay
,
473 mustBeBefore
: moment().add( 1, 'day' ).locale( 'en' ).format( 'YYYY-MM-DD' ) // Tomorrow
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
481 fieldset
= new OO
.ui
.FieldsetLayout( {
482 label
: mw
.msg( 'upload-form-label-infoform-title' )
485 new OO
.ui
.FieldLayout( this.filenameWidget
, {
486 label
: mw
.msg( 'upload-form-label-infoform-name' ),
489 new OO
.ui
.FieldLayout( this.descriptionWidget
, {
490 label
: mw
.msg( 'upload-form-label-infoform-description' ),
493 new OO
.ui
.FieldLayout( this.categoriesWidget
, {
494 label
: mw
.msg( 'foreign-structured-upload-form-label-infoform-categories' ),
497 new OO
.ui
.FieldLayout( this.dateWidget
, {
498 label
: mw
.msg( 'foreign-structured-upload-form-label-infoform-date' ),
502 this.infoForm
= new OO
.ui
.FormLayout( { items
: [ fieldset
] } );
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 ) );
509 return this.infoForm
;
515 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.onInfoFormChange = function () {
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 );
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();
548 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.clear = function () {
549 mw
.ForeignStructuredUpload
.BookletLayout
.parent
.prototype.clear
.call( this );
551 if ( this.ownWorkCheckbox
) {
552 this.ownWorkCheckbox
.setSelected( false );
554 if ( this.licenseCheckboxes
) {
555 this.licenseCheckboxes
.forEach( function ( checkbox
) {
556 checkbox
.setSelected( false );
559 if ( this.licenseSelectFields
) {
560 this.licenseSelectFields
.forEach( function ( field
, i
) {
561 field
.fieldWidget
.selectItem( null );
563 field
.toggle( false );
568 this.categoriesWidget
.setItemsFromData( [] );
569 this.dateWidget
.setValue( '' ).setValidityFlag( true );
572 }( jQuery
, mediaWiki
) );