mw.ForeignStructuredUpload.BookletLayout: A/B test of 4 different interfaces

It turns out that people click the checkbox affirming that they are
they author of the file and that they release it under CC BY-SA even
when neither of those is true. So we're trying some interfaces that
require a modicum of thought rather than just a click on "I agree".

  Option 1: The form we have right now, with a single checkbox.

  Option 2: Four checkboxes, each with a label explaining one facet of
    the requirements (own work; no pictures of copyrighted work;
    educational/useful; irrevocably released as CC BY-SA).

  Option 3: Some Yes/No questions structured so that 'Yes' is not
    always the right answer to continue uploading.

  Option 4: Longer introduction before a single checkbox (as in option
    1), with examples of good and unacceptable content.

As only logged in users are able to upload files, we're able to bucket
them into four groups by user ID number. When the user completes a
file upload, the bucket number is saved server-side in a change tag by
the companion patch I90cb12c505b2581f36113ec6b4f7bf732f0971b7 (we could
match the user IDs cross-wiki by username, but that sounds painful).

For testing and debugging, add '?uploadbucket=N' to the URL to force
given interface option to appear. Any completed upload won't count
towards the bucket.

Note that for expediency, the tested options all assume uploads to
'shared' repository (that is, Wikimedia Commons). The winner's
messages will be tweaked to work with 'local' and other targets too.

This patch DOES NOT ENABLE THE TEST yet, it just implements the options.
Enabling it on specific wikis can be done via config:
* $wgForeignUploadTestEnabled = true/false (defaults to 'false')
  Whether the test is running.
* $wgForeignUploadTestDefault = 1/2/3/4 (defaults to '1')
  Interface to use when the test is not running (and for anons).

Bug: T120867
Bug: T121021
Change-Id: I557056b867c6a55ef2c9af321eb48893312632a3
This commit is contained in:
Bartosz Dziewoński 2015-12-11 03:31:55 +01:00
parent ecc6a247bd
commit d0e47d475c
18 changed files with 540 additions and 14 deletions

View file

@ -535,6 +535,12 @@ $wgUseInstantCommons = false;
*/
$wgForeignUploadTargets = array();
/**
* Cross-wiki upload A/B test configuration.
*/
$wgForeignUploadTestEnabled = false;
$wgForeignUploadTestDefault = 1;
/**
* File backend structure configuration.
*

View file

@ -104,6 +104,8 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
'wgResourceLoaderLegacyModules' => self::getLegacyModules(),
'wgForeignUploadTargets' => $conf->get( 'ForeignUploadTargets' ),
'wgEnableUploads' => $conf->get( 'EnableUploads' ),
'wgForeignUploadTestEnabled' => $conf->get( 'ForeignUploadTestEnabled' ),
'wgForeignUploadTestDefault' => $conf->get( 'ForeignUploadTestDefault' ),
);
Hooks::run( 'ResourceLoaderGetConfigVars', array( &$vars ) );

View file

@ -1443,6 +1443,21 @@
"foreign-structured-upload-form-label-own-work-message-shared": "I attest that I own the copyright on this file, and agree to irrevocably release this file to Wikimedia Commons under the [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] license, and I agree to the [https://wikimediafoundation.org/wiki/Terms_of_Use Terms of Use].",
"foreign-structured-upload-form-label-not-own-work-message-shared": "If you do not own the copyright on this file, or you wish to release it under a different license, consider using the [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard].",
"foreign-structured-upload-form-label-not-own-work-local-shared": "You may also want to try using [[Special:Upload|the upload page on {{SITENAME}}]], if the site allows the upload of this file under their policies.",
"foreign-structured-upload-form-2-label-intro": "Thank you for donating an image to be used on {{SITENAME}}. You should only continue if it meets several conditions:",
"foreign-structured-upload-form-2-label-ownwork": "It must be entirely <strong>your own creation</strong>, not just taken from the Internet",
"foreign-structured-upload-form-2-label-noderiv": "It has to contain <strong>no work by anyone else</strong>, or inspired by them",
"foreign-structured-upload-form-2-label-useful": "It should be <strong>educational and useful</strong> for teaching others",
"foreign-structured-upload-form-2-label-ccbysa": "It must be <strong>OK to publish forever</strong> on the Internet under the [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] license",
"foreign-structured-upload-form-2-label-alternative": "If not all of the above are true, you may still be able to upload this file using the [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard], as long as it's available under a free license.",
"foreign-structured-upload-form-2-label-termsofuse": "By uploading the file, you attest that you own the copyright on this file, and agree to irrevocably release this file to Wikimedia Commons under the Creative Commons Attribution-ShareAlike 4.0 license, and you agree to the [https://wikimediafoundation.org/wiki/Terms_of_Use Terms of Use].",
"foreign-structured-upload-form-3-label-question-website": "Did you download this image from a website, or get it from an image search?",
"foreign-structured-upload-form-3-label-question-ownwork": "Did you create this image (take the photo, sketch the drawing, etc.) yourself?",
"foreign-structured-upload-form-3-label-question-noderiv": "Does it contain, or is it inspired by, work owned by anyone else, like a logo?",
"foreign-structured-upload-form-3-label-yes": "Yes",
"foreign-structured-upload-form-3-label-no": "No",
"foreign-structured-upload-form-3-label-alternative": "Unfortunately, in this case, this tool does not support uploading this file. You may still be able to upload it using the [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard], as long as it's available under a free license.",
"foreign-structured-upload-form-4-label-good": "Using this tool, you can upload educational graphics you've created and photographs you've taken, that don't contain work owned by someone else.",
"foreign-structured-upload-form-4-label-bad": "You can not upload images found on a search engine or downloaded from other websites.",
"backend-fail-stream": "Could not stream file \"$1\".",
"backend-fail-backup": "Could not backup file \"$1\".",
"backend-fail-notexists": "The file $1 does not exist.",

View file

@ -1607,7 +1607,7 @@
"upload-form-label-infoform-description": "Label for the file description input\n{{Identical|Description}}",
"upload-form-label-usage-title": "Title for the insert form showing how to use the uploaded item.\n{{Identical|Usage}}",
"upload-form-label-usage-filename": "Label for the file name input\n{{Identical|Filename}}",
"foreign-structured-upload-form-label-own-work": "Label for own work toggle",
"foreign-structured-upload-form-label-own-work": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 1.png|thumb]] Label for own work confirmation checkbox",
"foreign-structured-upload-form-label-infoform-categories": "Label for category selector input\n{{Identical|Category}}",
"foreign-structured-upload-form-label-infoform-date": "Label for date input\n{{Identical|Date}}",
"foreign-structured-upload-form-label-own-work-message-local": "Message shown by local when a user affirms that they are allowed to upload a file to the local wiki.",
@ -1616,9 +1616,24 @@
"foreign-structured-upload-form-label-own-work-message-default": "Message shown by default when a user affirms that they are allowed to upload a file to a remote wiki.",
"foreign-structured-upload-form-label-not-own-work-message-default": "Message shown by default when a user cannot upload a file to a remote wiki.",
"foreign-structured-upload-form-label-not-own-work-local-default": "Suggests uploading a file locally instead of to a remote wiki.",
"foreign-structured-upload-form-label-own-work-message-shared": "Legal message to show when the work is made by the uploader.",
"foreign-structured-upload-form-label-not-own-work-message-shared": "Message to show when the work isn't owned by the uploader.",
"foreign-structured-upload-form-label-not-own-work-local-shared": "Message suggesting the user might want to upload a file locally instead of to Wikimedia Commons. $1 is the name of the local wiki.",
"foreign-structured-upload-form-label-own-work-message-shared": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 1.png|thumb]] Legal message, confirming that the user is allowed to upload the file. Almost identical to {{msg-mw|foreign-structured-upload-form-2-label-termsofuse}}.",
"foreign-structured-upload-form-label-not-own-work-message-shared": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 1.png|thumb]] Explains alternatives when the copyright isn't owned by the uploader.",
"foreign-structured-upload-form-label-not-own-work-local-shared": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 1.png|thumb]] Message suggesting the user might want to upload a file locally instead of to Wikimedia Commons.",
"foreign-structured-upload-form-2-label-intro": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Introductory text in cross-wiki upload dialog.",
"foreign-structured-upload-form-2-label-ownwork": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Affirmative statement, used as checkbox label. The user must tick it to continue.",
"foreign-structured-upload-form-2-label-noderiv": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Affirmative statement, used as checkbox label. The user must tick it to continue.",
"foreign-structured-upload-form-2-label-useful": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Affirmative statement, used as checkbox label. The user must tick it to continue.",
"foreign-structured-upload-form-2-label-ccbysa": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Affirmative statement, used as checkbox label. The user must tick it to continue.",
"foreign-structured-upload-form-2-label-alternative": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Explains alternatives when the copyright isn't owned by the uploader.",
"foreign-structured-upload-form-2-label-termsofuse": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Legal message, confirming that the user is allowed to upload the file. Almost identical to {{msg-mw|foreign-structured-upload-form-label-own-work-message-shared}}.",
"foreign-structured-upload-form-3-label-question-website": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] Question to the user, with Yes/No answer ({{msg-mw|foreign-structured-upload-form-3-label-yes}} / {{msg-mw|foreign-structured-upload-form-3-label-no}}). The answer determines whether the user will be able to continue.",
"foreign-structured-upload-form-3-label-question-ownwork": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] Question to the user, with Yes/No answer ({{msg-mw|foreign-structured-upload-form-3-label-yes}} / {{msg-mw|foreign-structured-upload-form-3-label-no}}). The answer determines whether the user will be able to continue.",
"foreign-structured-upload-form-3-label-question-noderiv": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] Question to the user, with Yes/No answer ({{msg-mw|foreign-structured-upload-form-3-label-yes}} / {{msg-mw|foreign-structured-upload-form-3-label-no}}). The answer determines whether the user will be able to continue.",
"foreign-structured-upload-form-3-label-yes": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] {{Identical|Yes}}",
"foreign-structured-upload-form-3-label-no": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] {{Identical|No}}",
"foreign-structured-upload-form-3-label-alternative": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] Explains alternatives when the copyright isn't owned by the uploader.",
"foreign-structured-upload-form-4-label-good": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 4.png|thumb]] Gives examples of good content that is welcome. There is limited space for this text in the interface, please keep it short.",
"foreign-structured-upload-form-4-label-bad": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 4.png|thumb]] Gives examples of bad content that is unacceptable. There is limited space for this text in the interface, please keep it short.",
"backend-fail-stream": "Parameters:\n* $1 - a filename",
"backend-fail-backup": "Parameters:\n* $1 - a filename",
"backend-fail-notexists": "Parameters:\n* $1 - a filename",

View file

@ -1267,6 +1267,24 @@ return array(
'foreign-structured-upload-form-label-own-work-message-local',
'foreign-structured-upload-form-label-not-own-work-message-local',
'foreign-structured-upload-form-label-not-own-work-local-local',
'foreign-structured-upload-form-2-label-intro',
'foreign-structured-upload-form-2-label-ownwork',
'foreign-structured-upload-form-2-label-noderiv',
'foreign-structured-upload-form-2-label-useful',
'foreign-structured-upload-form-2-label-ccbysa',
'foreign-structured-upload-form-2-label-alternative',
'foreign-structured-upload-form-2-label-termsofuse',
'foreign-structured-upload-form-3-label-question-website',
'foreign-structured-upload-form-3-label-question-ownwork',
'foreign-structured-upload-form-3-label-question-noderiv',
'foreign-structured-upload-form-3-label-yes',
'foreign-structured-upload-form-3-label-no',
'foreign-structured-upload-form-3-label-alternative',
'foreign-structured-upload-form-4-label-good',
'foreign-structured-upload-form-4-label-bad',
),
'templates' => array(
'guide.html' => 'resources/src/mediawiki/bookletlayout/option4/guide.html',
),
),
'mediawiki.toc' => array(

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,12 @@
<div class="mw-foreignStructuredUpload-bookletLayout-guide">
<div class="mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good">
<span></span>
</div>
<div class="mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad">
<span></span>
</div>
<div class="mw-foreignStructuredUpload-bookletLayout-guide-image mw-foreignStructuredUpload-bookletLayout-guide-image-camera"></div>
<div class="mw-foreignStructuredUpload-bookletLayout-guide-image mw-foreignStructuredUpload-bookletLayout-guide-image-graphics"></div>
<div class="mw-foreignStructuredUpload-bookletLayout-guide-image mw-foreignStructuredUpload-bookletLayout-guide-image-website"></div>
<div class="mw-foreignStructuredUpload-bookletLayout-guide-image mw-foreignStructuredUpload-bookletLayout-guide-image-search"></div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View file

@ -1,5 +1,141 @@
/* All */
.mw-foreignStructuredUpload-bookletLayout-license {
font-size: 90%;
line-height: 1.4em;
color: #555;
}
/* Option 2 */
.mw-foreignStructuredUpload-bookletLayout-withicon.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline {
background-repeat: no-repeat;
background-size: 3.5em;
min-height: 4em;
margin-bottom: 0.25em;
display: table;
vertical-align: middle;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
}
.mw-foreignStructuredUpload-bookletLayout-withicon.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body {
/* Together with 'display: table' above, and FieldLayout styles, this aligns the text */
/* vertically to the middle. Don't try this at home, kids. */
display: table-row;
}
.mw-foreignStructuredUpload-bookletLayout-ownwork {
/* @embed */
background-image: url(bookletlayout/option2/ownwork.png);
background-position: left center;
padding-left: 4.5em;
}
.mw-foreignStructuredUpload-bookletLayout-noderiv {
/* @embed */
background-image: url(bookletlayout/option2/noderiv.png);
background-position: right center;
padding-right: 4.5em;
}
.mw-foreignStructuredUpload-bookletLayout-useful {
/* @embed */
background-image: url(bookletlayout/option2/useful.png);
background-position: left center;
padding-left: 4.5em;
}
.mw-foreignStructuredUpload-bookletLayout-ccbysa {
/* @embed */
background-image: url(bookletlayout/option2/ccbysa.png);
background-position: right center;
padding-right: 4.5em;
}
/* Option 3 */
.mw-foreignStructuredUpload-bookletLayout-question .oo-ui-radioOptionWidget {
display: inline-block;
margin-right: 2em;
}
.mw-foreignStructuredUpload-bookletLayout-checkbox.oo-ui-fieldLayout-disabled > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
/* No unreadable greys, please. This is the lightest WCAG AA compliant grey. */
color: #707070;
}
/* Option 4 */
.mw-foreignStructuredUpload-bookletLayout-guide {
position: relative;
height: 315px;
}
.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good,
.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad {
display: table;
width: 150px;
height: 140px;
position: absolute;
}
.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good span,
.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad span {
display: table-cell;
vertical-align: middle;
}
.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good {
top: 0;
left: 0;
}
.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad {
bottom: 0;
right: 0;
}
.mw-foreignStructuredUpload-bookletLayout-guide-image {
position: absolute;
width: 200px;
height: 122px;
background-size: 200px;
background-repeat: no-repeat;
border: 1px solid #ccc;
}
.mw-foreignStructuredUpload-bookletLayout-guide-image-camera {
/* @embed */
background-image: url(bookletlayout/option4/camera.png);
top: 0;
right: 80px;
}
.mw-foreignStructuredUpload-bookletLayout-guide-image-graphics {
/* @embed */
background-image: url(bookletlayout/option4/graphics.png);
top: 50px;
right: 0;
}
.mw-foreignStructuredUpload-bookletLayout-guide-image-website {
/* @embed */
background-image: url(bookletlayout/option4/website.png);
left: 0;
bottom: 50px;
}
.mw-foreignStructuredUpload-bookletLayout-guide-image-search {
/* @embed */
background-image: url(bookletlayout/option4/search.png);
left: 80px;
bottom: 0;
}
.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline.mw-foreignStructuredUpload-bookletLayout-guide-checkbox {
/* We're really tight on vertical space. */
margin-bottom: 0;
}

View file

@ -72,12 +72,45 @@
* @inheritdoc
*/
mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm = function () {
var
query = /[?&]uploadbucket=(\d)/.exec( location.search ),
isTestEnabled = !!mw.config.get( 'wgForeignUploadTestEnabled' ),
defaultBucket = mw.config.get( 'wgForeignUploadTestDefault' ) || 1,
userId = mw.config.get( 'wgUserId' );
if ( query && query[ 1 ] ) {
// Testing and debugging
this.shouldRecordBucket = false;
this.bucket = Number( query[ 1 ] );
} else if ( !userId || !isTestEnabled ) {
// a) Anonymous user. This can actually happen, because our software sucks.
// b) Test is not enabled on this wiki.
// In either case, display the old interface and don't record bucket on uploads.
this.shouldRecordBucket = false;
this.bucket = defaultBucket;
} else {
// Regular logged in user on a wiki where the test is running
this.shouldRecordBucket = true;
this.bucket = ( userId % 4 ) + 1; // 1, 2, 3, 4
}
return this[ 'renderUploadForm' + this.bucket ]();
};
/**
* Test option 1, the original one. See T120867.
*/
mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm1 = function () {
var fieldset, $ownWorkMessage, $notOwnWorkMessage,
onUploadFormChange,
ownWorkMessage, notOwnWorkMessage, notOwnWorkLocal,
validTargets = mw.config.get( 'wgForeignUploadTargets' ),
target = this.target || validTargets[ 0 ] || 'local',
layout = this;
// Temporary override to make my life easier during A/B test
target = 'shared';
// foreign-structured-upload-form-label-own-work-message-local
// foreign-structured-upload-form-label-own-work-message-shared
ownWorkMessage = mw.message( 'foreign-structured-upload-form-label-own-work-message-' + target );
@ -104,7 +137,13 @@
$( '<p>' ).html( notOwnWorkMessage.parse() ),
$( '<p>' ).html( notOwnWorkLocal.parse() )
);
$ownWorkMessage.add( $notOwnWorkMessage ).find( 'a' ).attr( 'target', '_blank' );
$ownWorkMessage.add( $notOwnWorkMessage ).find( 'a' )
.attr( 'target', '_blank' )
.on( 'click', function ( e ) {
// Some stupid code is trying to prevent default on all clicks, which causes the links to
// not be openable, don't let it
e.stopPropagation();
} );
this.selectFileWidget = new OO.ui.SelectFileWidget();
this.messageLabel = new OO.ui.LabelWidget( {
@ -133,9 +172,276 @@
] );
this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
onUploadFormChange = function () {
var file = this.selectFileWidget.getValue(),
ownWork = this.ownWorkCheckbox.isSelected(),
valid = !!file && ownWork;
this.emit( 'uploadValid', valid );
};
// Validation
this.selectFileWidget.on( 'change', this.onUploadFormChange.bind( this ) );
this.ownWorkCheckbox.on( 'change', this.onUploadFormChange.bind( this ) );
this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
this.ownWorkCheckbox.on( 'change', onUploadFormChange.bind( this ) );
return this.uploadForm;
};
/**
* Test option 2, idea A from T121021. See T120867.
*/
mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm2 = function () {
var fieldset, checkboxes, fields, onUploadFormChange;
this.selectFileWidget = new OO.ui.SelectFileWidget();
this.licenseCheckboxes = checkboxes = [
new OO.ui.CheckboxInputWidget(),
new OO.ui.CheckboxInputWidget(),
new OO.ui.CheckboxInputWidget(),
new OO.ui.CheckboxInputWidget()
];
fields = [
new OO.ui.FieldLayout( this.selectFileWidget, {
align: 'top',
label: mw.msg( 'upload-form-label-select-file' )
} ),
new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
label: mw.message( 'foreign-structured-upload-form-2-label-intro' ).parseDom()
} ), {
align: 'top'
} ),
new OO.ui.FieldLayout( checkboxes[ 0 ], {
align: 'inline',
classes: [
'mw-foreignStructuredUpload-bookletLayout-withicon',
'mw-foreignStructuredUpload-bookletLayout-ownwork'
],
label: mw.message( 'foreign-structured-upload-form-2-label-ownwork' ).parseDom()
} ),
new OO.ui.FieldLayout( checkboxes[ 1 ], {
align: 'inline',
classes: [
'mw-foreignStructuredUpload-bookletLayout-withicon',
'mw-foreignStructuredUpload-bookletLayout-noderiv'
],
label: mw.message( 'foreign-structured-upload-form-2-label-noderiv' ).parseDom()
} ),
new OO.ui.FieldLayout( checkboxes[ 2 ], {
align: 'inline',
classes: [
'mw-foreignStructuredUpload-bookletLayout-withicon',
'mw-foreignStructuredUpload-bookletLayout-useful'
],
label: mw.message( 'foreign-structured-upload-form-2-label-useful' ).parseDom()
} ),
new OO.ui.FieldLayout( checkboxes[ 3 ], {
align: 'inline',
classes: [
'mw-foreignStructuredUpload-bookletLayout-withicon',
'mw-foreignStructuredUpload-bookletLayout-ccbysa'
],
label: mw.message( 'foreign-structured-upload-form-2-label-ccbysa' ).parseDom()
} ),
new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
label: $()
.add( $( '<p>' ).msg( 'foreign-structured-upload-form-2-label-alternative' ) )
.add( $( '<p>' ).msg( 'foreign-structured-upload-form-2-label-termsofuse' )
.addClass( 'mw-foreignStructuredUpload-bookletLayout-license' ) )
} ), {
align: 'top'
} )
];
fieldset = new OO.ui.FieldsetLayout( { items: fields } );
this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
this.uploadForm.$element.find( 'a' )
.attr( 'target', '_blank' )
.on( 'click', function ( e ) {
// Some stupid code is trying to prevent default on all clicks, which causes the links to
// not be openable, don't let it
e.stopPropagation();
} );
onUploadFormChange = function () {
var file = this.selectFileWidget.getValue(),
checks = checkboxes.every( function ( checkbox ) {
return checkbox.isSelected();
} ),
valid = !!file && checks;
this.emit( 'uploadValid', valid );
};
// Validation
this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
checkboxes[ 0 ].on( 'change', onUploadFormChange.bind( this ) );
checkboxes[ 1 ].on( 'change', onUploadFormChange.bind( this ) );
checkboxes[ 2 ].on( 'change', onUploadFormChange.bind( this ) );
checkboxes[ 3 ].on( 'change', onUploadFormChange.bind( this ) );
return this.uploadForm;
};
/**
* Test option 3, idea D from T121021. See T120867.
*/
mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm3 = function () {
var ownWorkCheckbox, fieldset, yesMsg, noMsg, selects, selectFields,
alternativeField, fields, onUploadFormChange;
this.selectFileWidget = new OO.ui.SelectFileWidget();
this.ownWorkCheckbox = ownWorkCheckbox = new OO.ui.CheckboxInputWidget();
yesMsg = mw.message( 'foreign-structured-upload-form-3-label-yes' ).text();
noMsg = mw.message( 'foreign-structured-upload-form-3-label-no' ).text();
selects = [
new OO.ui.RadioSelectWidget( {
items: [
new OO.ui.RadioOptionWidget( { data: false, label: yesMsg } ),
new OO.ui.RadioOptionWidget( { data: true, label: noMsg } )
]
} ),
new OO.ui.RadioSelectWidget( {
items: [
new OO.ui.RadioOptionWidget( { data: true, label: yesMsg } ),
new OO.ui.RadioOptionWidget( { data: false, label: noMsg } )
]
} ),
new OO.ui.RadioSelectWidget( {
items: [
new OO.ui.RadioOptionWidget( { data: false, label: yesMsg } ),
new OO.ui.RadioOptionWidget( { data: true, label: noMsg } )
]
} )
];
this.licenseSelectFields = selectFields = [
new OO.ui.FieldLayout( selects[ 0 ], {
align: 'top',
classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
label: mw.message( 'foreign-structured-upload-form-3-label-question-website' ).parseDom()
} ),
new OO.ui.FieldLayout( selects[ 1 ], {
align: 'top',
classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
label: mw.message( 'foreign-structured-upload-form-3-label-question-ownwork' ).parseDom()
} ).toggle( false ),
new OO.ui.FieldLayout( selects[ 2 ], {
align: 'top',
classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
label: mw.message( 'foreign-structured-upload-form-3-label-question-noderiv' ).parseDom()
} ).toggle( false )
];
alternativeField = new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
label: mw.message( 'foreign-structured-upload-form-3-label-alternative' ).parseDom()
} ), {
align: 'top'
} ).toggle( false );
// Choosing the right answer to each question shows the next question.
// Switching to wrong answer hides all subsequent questions.
selects.forEach( function ( select, i ) {
select.on( 'choose', function ( selectedOption ) {
var isRightAnswer = !!selectedOption.getData();
alternativeField.toggle( !isRightAnswer );
if ( i + 1 === selectFields.length ) {
// Last question
return;
}
if ( isRightAnswer ) {
selectFields[ i + 1 ].toggle( true );
} else {
selectFields.slice( i + 1 ).forEach( function ( field ) {
field.fieldWidget.selectItem( null );
field.toggle( false );
} );
}
} );
} );
fields = [
new OO.ui.FieldLayout( this.selectFileWidget, {
align: 'top',
label: mw.msg( 'upload-form-label-select-file' )
} ),
selectFields[ 0 ],
selectFields[ 1 ],
selectFields[ 2 ],
alternativeField,
new OO.ui.FieldLayout( ownWorkCheckbox, {
classes: [ 'mw-foreignStructuredUpload-bookletLayout-checkbox' ],
align: 'inline',
label: mw.message( 'foreign-structured-upload-form-label-own-work-message-shared' ).parseDom()
} )
];
// Must be done late, after it's been associated with the FieldLayout
ownWorkCheckbox.setDisabled( true );
fieldset = new OO.ui.FieldsetLayout( { items: fields } );
this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
this.uploadForm.$element.find( 'a' )
.attr( 'target', '_blank' )
.on( 'click', function ( e ) {
// Some stupid code is trying to prevent default on all clicks, which causes the links to
// not be openable, don't let it
e.stopPropagation();
} );
onUploadFormChange = function () {
var file = this.selectFileWidget.getValue(),
checkbox = ownWorkCheckbox.isSelected(),
rightAnswers = selects.every( function ( select ) {
return select.getSelectedItem() && !!select.getSelectedItem().getData();
} ),
valid = !!file && checkbox && rightAnswers;
ownWorkCheckbox.setDisabled( !rightAnswers );
if ( !rightAnswers ) {
ownWorkCheckbox.setSelected( false );
}
this.emit( 'uploadValid', valid );
};
// Validation
this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
this.ownWorkCheckbox.on( 'change', onUploadFormChange.bind( this ) );
selects[ 0 ].on( 'choose', onUploadFormChange.bind( this ) );
selects[ 1 ].on( 'choose', onUploadFormChange.bind( this ) );
selects[ 2 ].on( 'choose', onUploadFormChange.bind( this ) );
return this.uploadForm;
};
/**
* Test option 4, idea E from T121021. See T120867.
*/
mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm4 = function () {
var fieldset, $guide;
this.renderUploadForm1();
fieldset = this.uploadForm.getItems()[ 0 ];
$guide = mw.template.get( 'mediawiki.ForeignStructuredUpload.BookletLayout', 'guide.html' ).render();
$guide.find( '.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good span' )
.msg( 'foreign-structured-upload-form-4-label-good' );
$guide.find( '.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad span' )
.msg( 'foreign-structured-upload-form-4-label-bad' );
// Note the index, we insert after the SelectFileWidget field
fieldset.addItems( [
new OO.ui.FieldLayout( new OO.ui.Widget( {
$content: $guide
} ), {
align: 'top'
} )
], 1 );
// Hook for custom styles
fieldset.getItems()[ 2 ].$element.addClass( 'mw-foreignStructuredUpload-bookletLayout-guide-checkbox' );
// Streamline: remove mention of local Special:Upload
fieldset.getItems()[ 3 ].$element.find( 'p' ).last().remove();
return this.uploadForm;
};
@ -143,12 +449,7 @@
/**
* @inheritdoc
*/
mw.ForeignStructuredUpload.BookletLayout.prototype.onUploadFormChange = function () {
var file = this.selectFileWidget.getValue(),
ownWork = this.ownWorkCheckbox.isSelected(),
valid = !!file && ownWork;
this.emit( 'uploadValid', valid );
};
mw.ForeignStructuredUpload.BookletLayout.prototype.onUploadFormChange = function () {};
/**
* @inheritdoc
@ -247,7 +548,23 @@
mw.ForeignStructuredUpload.BookletLayout.prototype.clear = function () {
mw.ForeignStructuredUpload.BookletLayout.parent.prototype.clear.call( this );
this.ownWorkCheckbox.setSelected( false );
if ( this.ownWorkCheckbox ) {
this.ownWorkCheckbox.setSelected( false );
}
if ( this.licenseCheckboxes ) {
this.licenseCheckboxes.forEach( function ( checkbox ) {
checkbox.setSelected( false );
} );
}
if ( this.licenseSelectFields ) {
this.licenseSelectFields.forEach( function ( field, i ) {
field.fieldWidget.selectItem( null );
if ( i !== 0 ) {
field.toggle( false );
}
} );
}
this.categoriesWidget.setItemsFromData( [] );
this.dateWidget.setValue( '' ).setValidityFlag( true );
};

View file

@ -186,6 +186,10 @@
this.filenameWidget.setValue( file.name );
this.setPage( 'info' );
if ( this.shouldRecordBucket ) {
this.upload.bucket = this.bucket;
}
this.upload.setFile( file );
// Explicitly set the filename so that the old filename isn't used in case of retry
this.upload.setFilenameFromFile();

View file

@ -321,6 +321,7 @@
upload.setState( Upload.State.UPLOADING );
return finishStash( {
bucket: upload.bucket, // Automatically ignored if undefined
watchlist: ( upload.getWatchlist() ) ? 1 : undefined,
comment: upload.getComment(),
filename: upload.getFilename(),