From 824e53c6e8c5e7c7dee63cbc95c50a8b00e857dd Mon Sep 17 00:00:00 2001 From: Mark Holmquist Date: Tue, 6 Oct 2015 13:13:13 -0500 Subject: [PATCH] Use ForeignFileRepo information for foreign uploads This seems like a (slightly) cleaner system. We'll need to change some documentation and add some configuration, but at least this will be easier to handle. Bug: T114765 Change-Id: If962fd3066e25e43e745efd29058eae82195bfb1 --- RELEASE-NOTES-1.27 | 1 + includes/DefaultSettings.php | 9 +- includes/api/ApiQueryFileRepoInfo.php | 20 +++- .../ResourceLoaderStartUpModule.php | 2 +- languages/i18n/en.json | 3 + languages/i18n/qqq.json | 5 +- ...i.ForeignStructuredUpload.BookletLayout.js | 5 +- .../src/mediawiki/mediawiki.ForeignUpload.js | 106 ++++++++++++++---- .../mediawiki/mediawiki.ForeignUpload.test.js | 4 +- 9 files changed, 118 insertions(+), 37 deletions(-) diff --git a/RELEASE-NOTES-1.27 b/RELEASE-NOTES-1.27 index 65e07992b3..548baea3b7 100644 --- a/RELEASE-NOTES-1.27 +++ b/RELEASE-NOTES-1.27 @@ -16,6 +16,7 @@ production. 1000 for the latter) are now hard-coded. * $wgDebugDumpSqlLength was removed (deprecated in 1.24). * $wgDebugDBTransactions was removed (deprecated in 1.20). +* $wgRemoteUploadTarget (added in 1.26) removed, replaced by $wgForeignUploadTargets === New features in 1.27 === * $wgDataCenterId and $wgDataCenterRoles where added, which will serve as diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index deb85f5b2b..f0fd83a6d3 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -526,12 +526,11 @@ $wgForeignFileRepos = array(); $wgUseInstantCommons = false; /** - * Name of the remote repository to which users will be allowed to upload - * files in their editors. Used to find a set of message names to describe - * the legal requirements for uploading to that wiki, and suggestions for - * when those requirements are not met. + * Array of foreign file repos (set in $wgForeignFileRepos above) that + * are allowable upload targets. These wikis must have some method of + * authentication (i.e. CentralAuth), and be CORS-enabled for this wiki. */ -$wgRemoteUploadTarget = 'default'; +$wgForeignUploadTargets = array(); /** * File backend structure configuration. diff --git a/includes/api/ApiQueryFileRepoInfo.php b/includes/api/ApiQueryFileRepoInfo.php index 057b011790..12b9893d93 100644 --- a/includes/api/ApiQueryFileRepoInfo.php +++ b/includes/api/ApiQueryFileRepoInfo.php @@ -41,18 +41,26 @@ class ApiQueryFileRepoInfo extends ApiQueryBase { } public function execute() { + $conf = $this->getConfig(); + $params = $this->extractRequestParams(); $props = array_flip( $params['prop'] ); $repos = array(); $repoGroup = $this->getInitialisedRepoGroup(); + $foreignTargets = $conf->get( 'ForeignUploadTargets' ); + + $repoGroup->forEachForeignRepo( function ( $repo ) use ( &$repos, $props, $foreignTargets ) { + $repoProps = $repo->getInfo(); + $repoProps['canUpload'] = in_array( $repoProps['name'], $foreignTargets ); - $repoGroup->forEachForeignRepo( function ( $repo ) use ( &$repos, $props ) { - $repos[] = array_intersect_key( $repo->getInfo(), $props ); + $repos[] = array_intersect_key( $repoProps, $props ); } ); - $repos[] = array_intersect_key( $repoGroup->getLocalRepo()->getInfo(), $props ); + $localInfo = $repoGroup->getLocalRepo()->getInfo(); + $localInfo['canUpload'] = $conf->get( 'EnableUploads' ); + $repos[] = array_intersect_key( $localInfo, $props ); $result = $this->getResult(); ApiResult::setIndexedTagName( $repos, 'repo' ); @@ -85,10 +93,14 @@ class ApiQueryFileRepoInfo extends ApiQueryBase { $props = array_merge( $props, array_keys( $repo->getInfo() ) ); } ); - return array_values( array_unique( array_merge( + $propValues = array_values( array_unique( array_merge( $props, array_keys( $repoGroup->getLocalRepo()->getInfo() ) ) ) ); + + $propValues[] = 'canUpload'; + + return $propValues; } protected function getExamplesMessages() { diff --git a/includes/resourceloader/ResourceLoaderStartUpModule.php b/includes/resourceloader/ResourceLoaderStartUpModule.php index 1857d23ad0..eabafbd540 100644 --- a/includes/resourceloader/ResourceLoaderStartUpModule.php +++ b/includes/resourceloader/ResourceLoaderStartUpModule.php @@ -102,7 +102,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { 'wgResourceLoaderStorageVersion' => $conf->get( 'ResourceLoaderStorageVersion' ), 'wgResourceLoaderStorageEnabled' => $conf->get( 'ResourceLoaderStorageEnabled' ), 'wgResourceLoaderLegacyModules' => self::getLegacyModules(), - 'wgRemoteUploadTarget' => $conf->get( 'RemoteUploadTarget' ), + 'wgForeignUploadTargets' => $conf->get( 'ForeignUploadTargets' ), ); Hooks::run( 'ResourceLoaderGetConfigVars', array( &$vars ) ); diff --git a/languages/i18n/en.json b/languages/i18n/en.json index 2005ee81a2..29013931c8 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -1425,6 +1425,9 @@ "foreign-structured-upload-form-label-own-work": "This is my own work", "foreign-structured-upload-form-label-infoform-categories": "Categories", "foreign-structured-upload-form-label-infoform-date": "Date", + "foreign-structured-upload-form-label-own-work-message-local": "I confirm that I am uploading this file following the terms of service and licensing policies on {{SITENAME}}.", + "foreign-structured-upload-form-label-not-own-work-message-local": "If you are not able to upload this file under the policies of {{SITENAME}}, please close this dialog and try another method.", + "foreign-structured-upload-form-label-not-own-work-local-local": "You may also want to try [[Special:Upload|the default upload page]].", "foreign-structured-upload-form-label-own-work-message-default": "I understand that I am uploading this file to a shared repository. I confirm that I am doing so following the terms of service and licensing policies there.", "foreign-structured-upload-form-label-not-own-work-message-default": "If you are not able to upload this file under the policies of the shared repository, please close this dialog and try another method.", "foreign-structured-upload-form-label-not-own-work-local-default": "You may also want to try using [[Special:Upload|the upload page on {{SITENAME}}]], if this file can be uploaded there under their policies.", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index 3893577dc2..e464d93169 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -1598,9 +1598,12 @@ "foreign-structured-upload-form-label-own-work": "Label for own work toggle", "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.", + "foreign-structured-upload-form-label-not-own-work-message-local": "Message shown by local when a user cannot upload a file to the local wiki.", + "foreign-structured-upload-form-label-not-own-work-local-local": "Suggests uploading a file via Special:Upload instead of using whatever method they're currently using.", "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. $1 is the name of the local 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-wikimediacommons": "Legal message to show when the work is made by the uploader.", "foreign-structured-upload-form-label-not-own-work-message-wikimediacommons": "Message to show when the work isn't owned by the uploader.", "foreign-structured-upload-form-label-not-own-work-local-wikimediacommons": "Message suggesting the user might want to upload a file locally instead of to Wikimedia Commons. $1 is the name of the local wiki.", diff --git a/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js b/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js index 3051d52e73..0807215583 100644 --- a/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js +++ b/resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js @@ -53,7 +53,10 @@ */ mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm = function () { var fieldset, - target = mw.config.get( 'wgRemoteUploadTarget' ), + targets = mw.config.get( 'wgForeignUploadTargets' ), + // Default to using local, but try to use a configured target. + // TODO allow finer configuration of this somehow? + target = ( targets && targets.length ) ? targets[ 0 ] : 'local', $ownWorkMessage = $( '

' ).html( mw.message( 'foreign-structured-upload-form-label-own-work-message-' + target ).parse() ), diff --git a/resources/src/mediawiki/mediawiki.ForeignUpload.js b/resources/src/mediawiki/mediawiki.ForeignUpload.js index 0929661648..a367ee05d5 100644 --- a/resources/src/mediawiki/mediawiki.ForeignUpload.js +++ b/resources/src/mediawiki/mediawiki.ForeignUpload.js @@ -1,4 +1,4 @@ -( function ( mw, OO ) { +( function ( mw, OO, $ ) { /** * @class mw.ForeignUpload * @extends mw.Upload @@ -13,33 +13,71 @@ * instead. * * @constructor - * @param {string} [targetHost="commons.wikimedia.org"] Used to set up the target + * @param {string} [target="local"] Used to set up the target * wiki. If not remote, this class behaves identically to mw.Upload (unless further subclassed) + * Use the same names as set in $wgForeignFileRepos for this. Also, + * make sure there is an entry in the $wgForeignUploadTargets array + * set to "true" for this name. * @param {Object} [apiconfig] Passed to the constructor of mw.ForeignApi or mw.Api, as needed. */ - function ForeignUpload( targetHost, apiconfig ) { - var api; + function ForeignUpload( target, apiconfig ) { + var api, upload = this; - if ( typeof targetHost === 'object' ) { - // targetHost probably wasn't passed in, it must + if ( typeof target === 'object' ) { + // target probably wasn't passed in, it must // be apiconfig - apiconfig = targetHost; - } else { - // targetHost is a useful string, set it here - this.targetHost = targetHost || this.targetHost; + apiconfig = target; + target = undefined; } - if ( location.host !== this.targetHost ) { - api = new mw.ForeignApi( - location.protocol + '//' + this.targetHost + '/w/api.php', - apiconfig - ); + // Resolve defaults etc. - if target isn't passed in, we use + // the default. + this.target = target || this.target; + + // Now we have several different options. + // If the local wiki is the target, then we can skip a bunch of steps + // and just return an mw.Api object, because we don't need any special + // configuration for that. + // However, if the target is a remote wiki, we must check the API + // to confirm that the target is one that this site is configured to + // support. + if ( this.target !== 'local' ) { + api = new mw.Api(); + this.apiPromise = api.get( { + action: 'query', + meta: 'filerepoinfo', + friprop: [ 'name', 'scriptDirUrl', 'canUpload' ] + } ).then( function ( data ) { + var i, repo, + repos = data.query.repos; + + for ( i in repos ) { + repo = repos[ i ]; + + if ( repo.name === upload.target ) { + // This is our target repo. + if ( !repo.canUpload ) { + // But it's not configured correctly. + return $.Deferred().reject( 'repo-cannot-upload' ); + } + + return new mw.ForeignApi( + repo.scriptDirUrl + '/api.php', + apiconfig + ); + } + } + } ); } else { - // We'll ignore the CORS and centralauth stuff if we're on Commons already - api = new mw.Api( apiconfig ); + // We'll ignore the CORS and centralauth stuff if the target is + // the local wiki. + this.apiPromise = $.Deferred().resolve( new mw.Api( apiconfig ) ); } - mw.Upload.call( this, api ); + // Build the upload object without an API - this class overrides the + // actual API call methods to wait for the apiPromise to resolve + // before continuing. + mw.Upload.call( this, null ); } OO.inheritClass( ForeignUpload, mw.Upload ); @@ -48,11 +86,33 @@ * @property targetHost * Used to specify the target repository of the upload. * - * You could override this to point at something that isn't Commons, - * but be sure it has the correct templates and is CORS and CentralAuth - * ready. + * If you set this to something that isn't 'local', you must be sure to + * add that target to $wgForeignUploadTargets in LocalSettings, and the + * repository must be set up to use CORS and CentralAuth. + */ + ForeignUpload.prototype.target = 'local'; + + /** + * Override from mw.Upload to make sure the API info is found and allowed + */ + ForeignUpload.prototype.upload = function () { + var upload = this; + return this.apiPromise.then( function ( api ) { + upload.api = api; + return mw.Upload.prototype.upload.call( upload ); + } ); + }; + + /** + * Override from mw.Upload to make sure the API info is found and allowed */ - ForeignUpload.prototype.targetHost = 'commons.wikimedia.org'; + ForeignUpload.prototype.uploadToStash = function () { + var upload = this; + return this.apiPromise.then( function ( api ) { + upload.api = api; + return mw.Upload.prototype.uploadToStash.call( upload ); + } ); + }; mw.ForeignUpload = ForeignUpload; -}( mediaWiki, OO ) ); +}( mediaWiki, OO, jQuery ) ); diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.ForeignUpload.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.ForeignUpload.test.js index 98b9678142..169ae37194 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.ForeignUpload.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.ForeignUpload.test.js @@ -6,7 +6,7 @@ var upload = new mw.ForeignUpload(); assert.ok( upload, 'The ForeignUpload constructor is working.' ); - assert.strictEqual( upload.targetHost, 'commons.wikimedia.org', 'Default target host is correct' ); - assert.ok( upload.api instanceof mw.ForeignApi, 'API is correctly configured to point at a foreign wiki.' ); + assert.strictEqual( upload.target, 'local', 'Default target host is correct' ); + assert.ok( upload.api instanceof mw.Api, 'API is local because default target is local.' ); } ); }( mediaWiki ) ); -- 2.20.1