Use ForeignFileRepo information for foreign uploads
authorMark Holmquist <mtraceur@member.fsf.org>
Tue, 6 Oct 2015 18:13:13 +0000 (13:13 -0500)
committerMark Holmquist <mtraceur@member.fsf.org>
Wed, 7 Oct 2015 15:55:24 +0000 (10:55 -0500)
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
includes/DefaultSettings.php
includes/api/ApiQueryFileRepoInfo.php
includes/resourceloader/ResourceLoaderStartUpModule.php
languages/i18n/en.json
languages/i18n/qqq.json
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js
resources/src/mediawiki/mediawiki.ForeignUpload.js
tests/qunit/suites/resources/mediawiki/mediawiki.ForeignUpload.test.js

index 65e0799..548baea 100644 (file)
@@ -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
index deb85f5..f0fd83a 100644 (file)
@@ -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.
index 057b011..12b9893 100644 (file)
@@ -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() {
index 1857d23..eabafbd 100644 (file)
@@ -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 ) );
index 2005ee8..2901393 100644 (file)
        "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.",
index 3893577..e464d93 100644 (file)
        "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.",
index 3051d52..0807215 100644 (file)
         */
        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 = $( '<p>' ).html(
                                mw.message( 'foreign-structured-upload-form-label-own-work-message-' + target ).parse()
                        ),
index 0929661..a367ee0 100644 (file)
@@ -1,4 +1,4 @@
-( function ( mw, OO ) {
+( function ( mw, OO, $ ) {
        /**
         * @class mw.ForeignUpload
         * @extends mw.Upload
         * 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 );
         * @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 ) );
index 98b9678..169ae37 100644 (file)
@@ -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 ) );