From: Bartosz DziewoƄski Date: Fri, 11 Dec 2015 02:31:55 +0000 (+0100) Subject: mw.ForeignStructuredUpload.BookletLayout: A/B test of 4 different interfaces X-Git-Tag: 1.31.0-rc.0~8674^2 X-Git-Url: http://git.cyclocoop.org/%22%2C%20generer_url_ecrire%28?a=commitdiff_plain;h=d0e47d475cd7efd7541d51e02ab216a966da8b1f;p=lhc%2Fweb%2Fwiklou.git 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 --- diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 1642c0966a..39b133b4c8 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -535,6 +535,12 @@ $wgUseInstantCommons = false; */ $wgForeignUploadTargets = array(); +/** + * Cross-wiki upload A/B test configuration. + */ +$wgForeignUploadTestEnabled = false; +$wgForeignUploadTestDefault = 1; + /** * File backend structure configuration. * diff --git a/includes/resourceloader/ResourceLoaderStartUpModule.php b/includes/resourceloader/ResourceLoaderStartUpModule.php index 39e4e0bc6f..fc128fb7d0 100644 --- a/includes/resourceloader/ResourceLoaderStartUpModule.php +++ b/includes/resourceloader/ResourceLoaderStartUpModule.php @@ -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 ) ); diff --git a/languages/i18n/en.json b/languages/i18n/en.json index 62405c8320..a1fff73067 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -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 your own creation, not just taken from the Internet", + "foreign-structured-upload-form-2-label-noderiv": "It has to contain no work by anyone else, or inspired by them", + "foreign-structured-upload-form-2-label-useful": "It should be educational and useful for teaching others", + "foreign-structured-upload-form-2-label-ccbysa": "It must be OK to publish forever 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.", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index 837c75f83c..e809e1073e 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -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", diff --git a/resources/Resources.php b/resources/Resources.php index 3d5a563798..02d87d4406 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -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( diff --git a/resources/src/mediawiki/bookletlayout/option2/ccbysa.png b/resources/src/mediawiki/bookletlayout/option2/ccbysa.png new file mode 100644 index 0000000000..507e62db38 Binary files /dev/null and b/resources/src/mediawiki/bookletlayout/option2/ccbysa.png differ diff --git a/resources/src/mediawiki/bookletlayout/option2/noderiv.png b/resources/src/mediawiki/bookletlayout/option2/noderiv.png new file mode 100644 index 0000000000..46766ca57d Binary files /dev/null and b/resources/src/mediawiki/bookletlayout/option2/noderiv.png differ diff --git a/resources/src/mediawiki/bookletlayout/option2/ownwork.png b/resources/src/mediawiki/bookletlayout/option2/ownwork.png new file mode 100644 index 0000000000..aabe59b02e Binary files /dev/null and b/resources/src/mediawiki/bookletlayout/option2/ownwork.png differ diff --git a/resources/src/mediawiki/bookletlayout/option2/useful.png b/resources/src/mediawiki/bookletlayout/option2/useful.png new file mode 100644 index 0000000000..1309eb3b9e Binary files /dev/null and b/resources/src/mediawiki/bookletlayout/option2/useful.png differ diff --git a/resources/src/mediawiki/bookletlayout/option4/camera.png b/resources/src/mediawiki/bookletlayout/option4/camera.png new file mode 100644 index 0000000000..f52391f20a Binary files /dev/null and b/resources/src/mediawiki/bookletlayout/option4/camera.png differ diff --git a/resources/src/mediawiki/bookletlayout/option4/graphics.png b/resources/src/mediawiki/bookletlayout/option4/graphics.png new file mode 100644 index 0000000000..6fcc2c5f98 Binary files /dev/null and b/resources/src/mediawiki/bookletlayout/option4/graphics.png differ diff --git a/resources/src/mediawiki/bookletlayout/option4/guide.html b/resources/src/mediawiki/bookletlayout/option4/guide.html new file mode 100644 index 0000000000..c747851b1a --- /dev/null +++ b/resources/src/mediawiki/bookletlayout/option4/guide.html @@ -0,0 +1,12 @@ +
+
+ +
+
+ +
+
+
+
+ +
diff --git a/resources/src/mediawiki/bookletlayout/option4/search.png b/resources/src/mediawiki/bookletlayout/option4/search.png new file mode 100644 index 0000000000..5dca7fa3f5 Binary files /dev/null and b/resources/src/mediawiki/bookletlayout/option4/search.png differ diff --git a/resources/src/mediawiki/bookletlayout/option4/website.png b/resources/src/mediawiki/bookletlayout/option4/website.png new file mode 100644 index 0000000000..9c1914fba6 Binary files /dev/null and b/resources/src/mediawiki/bookletlayout/option4/website.png differ diff --git a/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.css b/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.css index 4143520801..c50ad7f4db 100644 --- a/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.css +++ b/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.css @@ -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; +} diff --git a/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js b/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js index 859538615c..7ba73bf2f8 100644 --- a/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js +++ b/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js @@ -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 @@ $( '

' ).html( notOwnWorkMessage.parse() ), $( '

' ).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,23 +172,285 @@ ] ); 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; }; /** - * @inheritdoc + * 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( $( '

' ).msg( 'foreign-structured-upload-form-2-label-alternative' ) ) + .add( $( '

' ).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.onUploadFormChange = function () { - var file = this.selectFileWidget.getValue(), - ownWork = this.ownWorkCheckbox.isSelected(), - valid = !!file && ownWork; - this.emit( 'uploadValid', valid ); + 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; }; + /** + * @inheritdoc + */ + 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 ); }; diff --git a/resources/src/mediawiki/mediawiki.Upload.BookletLayout.js b/resources/src/mediawiki/mediawiki.Upload.BookletLayout.js index 740144502c..47f438863f 100644 --- a/resources/src/mediawiki/mediawiki.Upload.BookletLayout.js +++ b/resources/src/mediawiki/mediawiki.Upload.BookletLayout.js @@ -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(); diff --git a/resources/src/mediawiki/mediawiki.Upload.js b/resources/src/mediawiki/mediawiki.Upload.js index 4f8789de7a..d80b4ebcc7 100644 --- a/resources/src/mediawiki/mediawiki.Upload.js +++ b/resources/src/mediawiki/mediawiki.Upload.js @@ -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(),