jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/404679 )

Change subject: Integrate all Client's repository settings into a single setting
......................................................................


Integrate all Client's repository settings into a single setting

This renames "foreignRepositories" setting to "repositories",
and moves the configuration of the "local" repository, to
the "repositories" setting.

Resubmitting I2eee36256260490d7b3ddf6c83389f810239835c
with proper handling  of "old" "local" repository setting

Bug: T153767
Change-Id: Ibbdb5d0317ef741b4d755b932b4317e3b3bd2a07
---
M client/config/WikibaseClient.default.php
M client/config/WikibaseClient.example.php
M client/includes/Hooks/UpdateRepoHookHandlers.php
M client/includes/WikibaseClient.php
M client/maintenance/updateSubscriptions.php
M client/tests/phpunit/ClientDefaultsTest.php
M 
client/tests/phpunit/includes/DataAccess/DataAccessSnakFormatterOutputFormatTest.php
M client/tests/phpunit/includes/Usage/UsageTrackingIntegrationTest.php
A client/tests/phpunit/includes/WikibaseClientRepositorySettingsTest.php
M docs/federation.wiki
M docs/options.wiki
M lib/includes/WikibaseSettings.php
12 files changed, 392 insertions(+), 105 deletions(-)

Approvals:
  WMDE-leszek: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/client/config/WikibaseClient.default.php 
b/client/config/WikibaseClient.default.php
index 312edac..7380d59 100644
--- a/client/config/WikibaseClient.default.php
+++ b/client/config/WikibaseClient.default.php
@@ -90,8 +90,6 @@
                // repo and clients for multiwiki setups.
                'sharedCacheType' => $GLOBALS['wgMainCacheType'],
 
-               'foreignRepositories' => [],
-
                // Enable writing of term_full_entity_id column in wb_terms 
table.
                'writeFullEntityIdColumn' => function ( SettingsArray $settings 
) {
                        return $settings->hasSetting( 'hasFullEntityIdColumn' ) 
?
@@ -132,6 +130,30 @@
                return WikibaseSettings::isRepoEnabled();
        };
 
+       $defaults['repositories'] = function ( SettingsArray $settings ) {
+               // XXX: Default to having Items in the main namespace, and 
properties in NS 120.
+               // That is the live setup at wikidata.org, it is NOT consistent 
with the example settings!
+               // FIXME: throw an exception, instead of making assumptions 
that may brak the site in strange ways!
+               $entityNamespaces = [
+                       'item' => 0,
+                       'property' => 120
+               ];
+               if ( $settings->getSetting( 'thisWikiIsTheRepo' ) ) {
+                       $entityNamespaces = 
WikibaseSettings::getRepoSettings()->getSetting( 'entityNamespaces' );
+               }
+
+               return [
+                       '' => [
+                               // Use false (meaning the local wiki's 
database) if this wiki is the repo,
+                               // otherwise default to null (meaning we can't 
access the repo's DB directly).
+                               'repoDatabase' => $settings->getSetting( 
'thisWikiIsTheRepo' ) ? false : null,
+                               'baseUri' => $settings->getSetting( 'repoUrl' ) 
. '/entity/',
+                               'entityNamespaces' => $entityNamespaces,
+                               'prefixMapping' => [ '' => '' ],
+                       ]
+               ];
+       };
+
        $defaults['repoSiteName'] = function ( SettingsArray $settings ) {
                // This uses $wgSitename if this wiki is the repo.  Otherwise, 
set this to
                // either an i18n message key and the message will be used, if 
it exists.
@@ -144,10 +166,6 @@
                return $settings->getSetting( 'thisWikiIsTheRepo' ) ? 
$GLOBALS['wgServer'] : '//www.wikidata.org';
        };
 
-       $defaults['repoConceptBaseUri'] = function ( SettingsArray $settings ) {
-               return $settings->getSetting( 'repoUrl' ) . '/entity/';
-       };
-
        $defaults['repoArticlePath'] = function ( SettingsArray $settings ) {
                // use $wgArticlePath if this wiki is the repo, otherwise 
default to /wiki/$1
                return $settings->getSetting( 'thisWikiIsTheRepo' ) ? 
$GLOBALS['wgArticlePath'] : '/wiki/$1';
@@ -158,32 +176,12 @@
                return $settings->getSetting( 'thisWikiIsTheRepo' ) ? 
$GLOBALS['wgScriptPath'] : '/w';
        };
 
-       $defaults['repoDatabase'] = function ( SettingsArray $settings ) {
-               // Use false (meaning the local wiki's database) if this wiki 
is the repo,
-               // otherwise default to null (meaning we can't access the 
repo's DB directly).
-               return $settings->getSetting( 'thisWikiIsTheRepo' ) ? false : 
null;
-       };
-
-       $defaults['entityNamespaces'] = function ( SettingsArray $settings ) {
-               if ( $settings->getSetting( 'thisWikiIsTheRepo' ) ) {
-                       return WikibaseSettings::getRepoSettings()->getSetting( 
'entityNamespaces' );
-               } else {
-                       // XXX: Default to having Items in the main namespace, 
and properties in NS 120.
-                       // That is the live setup at wikidata.org, it is NOT 
consistent with the example settings!
-                       // FIXME: throw an exception, instead of making 
assumptions that may brak the site in strange ways!
-                       return [
-                               'item' => 0,
-                               'property' => 120
-                       ];
-               }
-       };
-
        $defaults['repoNamespaces'] = function ( SettingsArray $settings ) {
                if ( $settings->getSetting( 'thisWikiIsTheRepo' ) ) {
                        // if this is the repo wiki, look up the namespace 
names based on the entityNamespaces setting
                        $namespaceNames = array_map(
                                [ MWNamespace::class, 'getCanonicalName' ],
-                               $settings->getSetting( 'entityNamespaces' )
+                               
WikibaseSettings::getRepoSettings()->getSetting( 'entityNamespaces' )
                        );
                        return $namespaceNames;
                } else {
@@ -198,10 +196,11 @@
        };
 
        $defaults['changesDatabase'] = function ( SettingsArray $settings ) {
-               // Per default, the database for tracking changes is the repo's 
database.
+               // Per default, the database for tracking changes is the local 
repo's database.
                // Note that the value for the repoDatabase setting may be 
calculated dynamically,
-               // see above.
-               return $settings->getSetting( 'repoDatabase' );
+               // see above in 'repositories' setting.
+               $repositorySettings = $settings->getSetting( 'repositories' );
+               return $repositorySettings['']['repoDatabase'];
        };
 
        $defaults['siteGlobalID'] = function ( SettingsArray $settings ) {
@@ -213,9 +212,12 @@
        $defaults['repoSiteId'] = function( SettingsArray $settings ) {
                // If repoDatabase is set, then default is same as repoDatabase
                // otherwise, defaults to siteGlobalID
-               return ( $settings->getSetting( 'repoDatabase' ) === false )
+               $repositorySettings = $settings->getSetting( 'repositories' );
+               $repoDatabase = $repositorySettings['']['repoDatabase'];
+
+               return ( $repoDatabase === false )
                        ? $settings->getSetting( 'siteGlobalID' )
-                       : $settings->getSetting( 'repoDatabase' );
+                       : $repoDatabase;
        };
 
        $defaults['siteGroup'] = function ( SettingsArray $settings ) {
diff --git a/client/config/WikibaseClient.example.php 
b/client/config/WikibaseClient.example.php
index 7a5b509..52a9969 100644
--- a/client/config/WikibaseClient.example.php
+++ b/client/config/WikibaseClient.example.php
@@ -46,22 +46,22 @@
        // default to what you have $wgScriptPath set in the client.
        $wgWBClientSettings['repoScriptPath'] = "/w";
 
-       // Database name of the repository, for direct access from the client.
-       // repoDatabase and changesDatabase will generally be the same.
-       // This requires the given database name to be known to LBFactory, see
-       // $wgLBFactoryConf below.
-       $wgWBClientSettings['repoDatabase'] = "repo";
-
        // Tell the client which namespace ID on the repo holds which type of 
entity.
        $baseRepoNs = 120;
 
        define( 'WB_REPO_NS_ITEM', $baseRepoNs );
        define( 'WB_REPO_NS_PROPERTY', $baseRepoNs + 2 );
 
-       // Tell Wikibase which namespace on the repo to use for which kind of 
entity
-       $wgWBClientSettings['entityNamespaces'] = [
-               'item' => WB_REPO_NS_ITEM,
-               'property' => WB_REPO_NS_PROPERTY
+       $wgWBClientSettings['repositories'] = [
+               '' => [
+                       'repoDatabase' => 'repo',
+                       'baseUri' => $wgWBClientSettings['repoUrl'] . '/entity',
+                       'entityNamespaces' => [
+                               'item' => WB_REPO_NS_ITEM,
+                               'property' => WB_REPO_NS_PROPERTY
+                       ],
+                       'prefixMapping' => [ '' => '' ],
+               ]
        ];
 }
 
diff --git a/client/includes/Hooks/UpdateRepoHookHandlers.php 
b/client/includes/Hooks/UpdateRepoHookHandlers.php
index aaf87ee..591ff21 100644
--- a/client/includes/Hooks/UpdateRepoHookHandlers.php
+++ b/client/includes/Hooks/UpdateRepoHookHandlers.php
@@ -64,7 +64,7 @@
 
                $namespaceChecker = $wikibaseClient->getNamespaceChecker();
 
-               $repoDB = $settings->getSetting( 'repoDatabase' );
+               $repoDB = 
$wikibaseClient->getRepositoryDefinitions()->getDatabaseNames()[''];
                $jobQueueGroup = JobQueueGroup::singleton( $repoDB );
 
                if ( !$jobQueueGroup ) {
@@ -78,7 +78,7 @@
                        $namespaceChecker,
                        $jobQueueGroup,
                        $siteLinkLookup,
-                       $settings->getSetting( 'repoDatabase' ),
+                       $repoDB,
                        $settings->getSetting( 'siteGlobalID' ),
                        $settings->getSetting( 'propagateChangesToRepo' )
                );
diff --git a/client/includes/WikibaseClient.php 
b/client/includes/WikibaseClient.php
index c433758..82f5244 100644
--- a/client/includes/WikibaseClient.php
+++ b/client/includes/WikibaseClient.php
@@ -531,17 +531,13 @@
         */
        public function getStore() {
                if ( $this->store === null ) {
-                       // NOTE: $repoDatabase is null per default, meaning no 
direct access to the repo's
-                       // database. If $repoDatabase is false, the local wiki 
IS the repository. Otherwise,
-                       // $repoDatabase needs to be a logical database name 
that LBFactory understands.
-                       $repoDatabase = $this->settings->getSetting( 
'repoDatabase' );
                        $this->store = new DirectSqlStore(
                                $this->getEntityChangeFactory(),
                                $this->getEntityIdParser(),
                                $this->getEntityIdComposer(),
                                $this->getEntityNamespaceLookup(),
                                $this->getWikibaseServices(),
-                               $repoDatabase,
+                               
$this->getRepositoryDefinitions()->getDatabaseNames()[''],
                                $this->getContentLanguage()->getCode()
                        );
                }
@@ -671,16 +667,31 @@
         * @return RepositoryDefinitions
         */
        private static function getRepositoryDefinitionsFromSettings( 
SettingsArray $settings ) {
-               // FIXME: It might no longer be needed to check different 
settings (repoDatabase vs foreignRepositories)
-               // once repository settings are unified, see: T153767.
-               $definitions = [ '' => [
-                       'database' => $settings->getSetting( 'repoDatabase' ),
-                       'base-uri' => $settings->getSetting( 
'repoConceptBaseUri' ),
-                       'prefix-mapping' => [ '' => '' ],
-                       'entity-namespaces' => $settings->getSetting( 
'entityNamespaces' ),
-               ] ];
+               $definitions = [];
 
-               foreach ( $settings->getSetting( 'foreignRepositories' ) as 
$repository => $repositorySettings ) {
+               // Backwards compatibility: if the old "foreignRepositories" 
settings is there,
+               // use its values.
+               $repoSettingsArray = $settings->hasSetting( 
'foreignRepositories' )
+                       ? $settings->getSetting( 'foreignRepositories' )
+                       : $settings->getSetting( 'repositories' );
+
+               // Backwards compatibility: if settings of the "local" 
repository
+               // are not defined in the "repositories" settings but with 
individual settings,
+               // fallback to old single-repo settings
+               if ( $settings->hasSetting( 'repoDatabase' )
+                       && $settings->hasSetting( 'entityNamespaces' )
+                       && $settings->hasSetting( 'repoConceptBaseUri' )
+               ) {
+                       $definitions = [ '' => [
+                               'database' => $settings->getSetting( 
'repoDatabase' ),
+                               'base-uri' => $settings->getSetting( 
'repoConceptBaseUri' ),
+                               'prefix-mapping' => [ '' => '' ],
+                               'entity-namespaces' => $settings->getSetting( 
'entityNamespaces' ),
+                       ] ];
+                       unset( $repoSettingsArray[''] );
+               }
+
+               foreach ( $repoSettingsArray as $repository => 
$repositorySettings ) {
                        $definitions[$repository] = [
                                'database' => 
$repositorySettings['repoDatabase'],
                                'base-uri' => $repositorySettings['baseUri'],
@@ -842,8 +853,10 @@
         * @return EntityIdParser
         */
        private function getRepoItemUriParser() {
+               // B/C compatibility, should be removed soon
+               // TODO: Move to check repo that has item entity not the 
default repo
                return new SuffixEntityIdParser(
-                       $this->settings->getSetting( 'repoConceptBaseUri' ),
+                       
$this->getRepositoryDefinitions()->getConceptBaseUris()[''],
                        new ItemIdParser()
                );
        }
diff --git a/client/maintenance/updateSubscriptions.php 
b/client/maintenance/updateSubscriptions.php
index ac240d1..4dee496 100644
--- a/client/maintenance/updateSubscriptions.php
+++ b/client/maintenance/updateSubscriptions.php
@@ -58,7 +58,7 @@
 
                $wikibaseClient = WikibaseClient::getDefaultInstance();
                $settings = $wikibaseClient->getSettings();
-               $repoDB = $settings->getSetting( 'repoDatabase' );
+               $repoDB = 
$wikibaseClient->getRepositoryDefinitions()->getDatabaseNames()[''];
                $clientId = $settings->getSetting( 'siteGlobalID' );
 
                $idParser = $wikibaseClient->getEntityIdParser();
diff --git a/client/tests/phpunit/ClientDefaultsTest.php 
b/client/tests/phpunit/ClientDefaultsTest.php
index 16612a7..4a01b7c 100644
--- a/client/tests/phpunit/ClientDefaultsTest.php
+++ b/client/tests/phpunit/ClientDefaultsTest.php
@@ -60,7 +60,16 @@
                                        'repoArticlePath' => '/wiki/$1', // 
hardcoded default
                                        'repoScriptPath' => '/w', // hardcoded 
default
                                        'siteGlobalID' => 'mw_mywiki',
-                                       'repoDatabase' => null,
+                                       'repositories' => [
+                                               '' => [
+                                                       'repoDatabase' => null,
+                                                       'baseUri' => 
'//www.wikidata.org/entity/',
+                                                       'entityNamespaces' => [
+                                                               'item' => 0,
+                                                               'property' => 
120,
+                                                       ],
+                                               ],
+                                       ],
                                        'changesDatabase' => null,
                                        'sharedCacheKeyPrefix' => 
'wikibase_shared/' . rawurlencode( WBL_VERSION ) . '-mw_mywiki',
                                ]
@@ -93,8 +102,10 @@
                                        'sharedCacheKeyPrefix' => 'foo:WBL/' . 
rawurlencode( WBL_VERSION ),
                                ]
                        ],
+               ];
 
-                       [ // #3: local repo, no values set
+               if ( WikibaseSettings::isRepoEnabled() ) {
+                       $cases[] = [ // #3: local repo, no values set
                                [ // $settings
                                ],
                                [ // $wg
@@ -102,6 +113,9 @@
                                        'wgArticlePath' => '/mywiki',
                                        'wgScriptPath' => '/mediawiki',
                                        'wgDBname' => 'mw_mywiki',
+                                       'wgWBRepoSettings' => [
+                                               'entityNamespaces' => [ 'item' 
=> 303 ],
+                                       ],
                                ],
                                true, // $repoIsLocal
                                [ // $expected
@@ -109,25 +123,36 @@
                                        'repoArticlePath' => '/mywiki',
                                        'repoScriptPath' => '/mediawiki',
                                        'siteGlobalID' => 'mw_mywiki',
-                                       'repoDatabase' => false,
+                                       'repositories' => [
+                                               '' => [
+                                                       'repoDatabase' => false,
+                                                       'baseUri' => 
'http://www.acme.com/entity/',
+                                               ],
+                                       ],
                                        'changesDatabase' => false,
                                        'sharedCacheKeyPrefix' => 
'wikibase_shared/' . rawurlencode( WBL_VERSION ) . '-mw_mywiki',
                                ]
-                       ],
+                       ];
+               }
 
-                       [ // #4: derive changesDatabase
-                               [ // $settings
-                                       'repoDatabase' => 'mw_foowiki',
+               $cases[] = [ // #4: derive changesDatabase
+                       [ // $settings
+                               'repositories' => [
+                                       '' => [
+                                               'repoDatabase' => 'mw_foowiki'
+                                       ],
                                ],
-                               [ // $wg
-                               ],
-                               false, // $repoIsLocal
-                               [ // $expected
-                                       'repoDatabase' => 'mw_foowiki',
-                                       'changesDatabase' => 'mw_foowiki',
-                               ]
                        ],
-                       [ // #5: sharedCacheKeyPrefix explicitly set
+                       [ // $wg
+                       ],
+                       false, // $repoIsLocal
+                       [ // $expected
+                               'changesDatabase' => 'mw_foowiki',
+                       ]
+               ];
+
+               if ( WikibaseSettings::isRepoEnabled() ) {
+                       $cases[] = [ // #5: sharedCacheKeyPrefix explicitly set
                                [ // $settings
                                        'sharedCacheKeyPrefix' => 
'wikibase_shared/wikidata_1_25wmf24'
                                ],
@@ -136,6 +161,7 @@
                                        'wgArticlePath' => '/mywiki',
                                        'wgScriptPath' => '/mediawiki',
                                        'wgDBname' => 'mw_mywiki',
+                                       'wgWBRepoSettings' => [ 
'entityNamespaces' => [ 'item' => 303 ] ],
                                ],
                                true, // $repoIsLocal
                                [ // $expected
@@ -143,22 +169,26 @@
                                        'repoArticlePath' => '/mywiki',
                                        'repoScriptPath' => '/mediawiki',
                                        'siteGlobalID' => 'mw_mywiki',
-                                       'repoDatabase' => false,
                                        'changesDatabase' => false,
                                        'sharedCacheKeyPrefix' => 
'wikibase_shared/wikidata_1_25wmf24',
                                ]
+                       ];
+               }
+
+               $cases[] = [ // #6: derive repoNamespaces and entityNamespaces
+                       [ // $settings
                        ],
-                       [ // #6: derive repoNamespaces and entityNamespaces
-                               [ // $settings
-                               ],
-                               [ // $wg
-                               ],
-                               false, // $repoIsLocal
-                               [ // $expected
-                                       'repoNamespaces' => [ 'item' => '', 
'property' => 'Property' ],
-                                       'entityNamespaces' => [ 'item' => 0, 
'property' => 120 ],
-                               ]
+                       [ // $wg
                        ],
+                       false, // $repoIsLocal
+                       [ // $expected
+                               'repoNamespaces' => [ 'item' => '', 'property' 
=> 'Property' ],
+                               'repositories' => [
+                                       '' => [
+                                               'entityNamespaces' => [ 'item' 
=> 0, 'property' => 120 ],
+                                       ],
+                               ],
+                       ]
                ];
 
                if ( WikibaseSettings::isRepoEnabled() ) {
@@ -172,7 +202,11 @@
                                true, // $repoIsLocal
                                [ // $expected
                                        'repoNamespaces' => $namespaceNames,
-                                       'entityNamespaces' => $entityNamespaces,
+                                       'repositories' => [
+                                               '' => [
+                                                       'entityNamespaces' => 
$entityNamespaces,
+                                               ],
+                                       ],
                                ]
                        ];
                }
@@ -198,8 +232,21 @@
 
                foreach ( $expected as $key => $exp ) {
                        $actual = $settings->getSetting( $key );
+
+                       if ( $key === 'repositories' ) {
+                               $this->assertRepositorySettingsEqual( $exp, 
$actual );
+                               continue;
+                       }
+
                        $this->assertSame( $exp, $actual, "Setting $key" );
                }
        }
 
+       private function assertRepositorySettingsEqual( $expected, $actual ) {
+               foreach ( $expected as $repoName => $expectedRepoSettings ) {
+                       $actualToCompare = array_intersect_key( 
$actual[$repoName], $expectedRepoSettings );
+                       $this->assertSame( $expectedRepoSettings, 
$actualToCompare );
+               }
+       }
+
 }
diff --git 
a/client/tests/phpunit/includes/DataAccess/DataAccessSnakFormatterOutputFormatTest.php
 
b/client/tests/phpunit/includes/DataAccess/DataAccessSnakFormatterOutputFormatTest.php
index 47d864f..bee2326 100644
--- 
a/client/tests/phpunit/includes/DataAccess/DataAccessSnakFormatterOutputFormatTest.php
+++ 
b/client/tests/phpunit/includes/DataAccess/DataAccessSnakFormatterOutputFormatTest.php
@@ -125,8 +125,8 @@
         * @return array[]
         */
        private function getGenericSnaks() {
-               $settings = WikibaseClient::getDefaultInstance()->getSettings();
-               $repoConceptBaseUri = $settings->getSetting( 
'repoConceptBaseUri' );
+               $repositories = 
WikibaseClient::getDefaultInstance()->getRepositoryDefinitions();
+               $repoConceptBaseUri = $repositories->getConceptBaseUris()[''];
 
                $p4 = new PropertyId( 'P4' );
                $sampleUrl = 
'https://www.wikidata.org/w/index.php?title=Q2013&action=history';
diff --git 
a/client/tests/phpunit/includes/Usage/UsageTrackingIntegrationTest.php 
b/client/tests/phpunit/includes/Usage/UsageTrackingIntegrationTest.php
index 6b99c5a..29dcb30 100644
--- a/client/tests/phpunit/includes/Usage/UsageTrackingIntegrationTest.php
+++ b/client/tests/phpunit/includes/Usage/UsageTrackingIntegrationTest.php
@@ -53,9 +53,10 @@
 
                parent::setUp();
 
-               $settings = WikibaseClient::getDefaultInstance()->getSettings();
+               $wikibaseClient = WikibaseClient::getDefaultInstance();
+               $settings = $wikibaseClient->getSettings();
                $this->oldAllowDataTransclusion = $settings->getSetting( 
'allowDataTransclusion' );
-               $this->oldEntityNamespaces = $settings->getSetting( 
'entityNamespaces' );
+               $this->oldEntityNamespaces = 
$wikibaseClient->getRepositoryDefinitions()->getEntityNamespaces();
                $settings->setSetting( 'allowDataTransclusion', true );
                $settings->setSetting( 'entityNamespaces', [ 'item' => 0 ] );
 
diff --git 
a/client/tests/phpunit/includes/WikibaseClientRepositorySettingsTest.php 
b/client/tests/phpunit/includes/WikibaseClientRepositorySettingsTest.php
new file mode 100644
index 0000000..2fd42b1
--- /dev/null
+++ b/client/tests/phpunit/includes/WikibaseClientRepositorySettingsTest.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace Wikibase\Client\Tests;
+
+use Wikibase\Client\WikibaseClient;
+
+/**
+ * @covers Wikibase\Client\WikibaseClient
+ *
+ * @group Wikibase
+ * @group WikibaseClient
+ *
+ * @license GPL-2.0+
+ */
+class WikibaseClientRepositorySettingsTest extends \MediaWikiTestCase {
+
+       public function 
testGivenOldRepositorySettings_individualSettingsAreUsedForLocalRepo() {
+               $clientSettings = $this->getDefaultSettings();
+
+               $clientSettings['repoDatabase'] = 'foodb';
+               $clientSettings['entityNamespaces'] = [ 'item' => 303, 
'property' => 808 ];
+               $clientSettings['repoConceptBaseUri'] = 
'http://foo.oof/entity/';
+
+               $this->setMwGlobals( 'wgWBClientSettings', $clientSettings );
+               $this->setMwGlobals( 'wgHooks', [] );
+
+               $client = WikibaseClient::getDefaultInstance( 'reset' );
+               $repositoryDefinitions = $client->getRepositoryDefinitions();
+
+               $this->assertEquals(
+                       [ '' => 'foodb' ],
+                       $repositoryDefinitions->getDatabaseNames()
+               );
+               $this->assertEquals(
+                       [ 'item' => 303, 'property' => 808 ],
+                       $repositoryDefinitions->getEntityNamespaces()
+               );
+               $this->assertEquals(
+                       [ '' => 'http://foo.oof/entity/' ],
+                       $repositoryDefinitions->getConceptBaseUris()
+               );
+       }
+
+       public function 
testGivenOldForeignRepositorySettings_oldSettingsAreUsedForForeignRepository() {
+               $clientSettings = $this->getDefaultSettings();
+
+               $clientSettings['foreignRepositories'] = [
+                       'coolrepo' => [
+                               'repoDatabase' => 'cooldb',
+                               'entityNamespaces' => [ 'cool' => 666 ],
+                               'baseUri' => 'http://soooo.cooooool/entity/',
+                               'prefixMapping' => [ '' => '' ],
+                       ]
+               ];
+               $clientSettings['repoDatabase'] = 'foodb';
+               $clientSettings['entityNamespaces'] = [ 'item' => 303, 
'property' => 808 ];
+               $clientSettings['repoConceptBaseUri'] = 
'http://foo.oof/entity/';
+
+               $this->setMwGlobals( 'wgWBClientSettings', $clientSettings );
+               $this->setMwGlobals( 'wgHooks', [] );
+
+               $client = WikibaseClient::getDefaultInstance( 'reset' );
+               $repositoryDefinitions = $client->getRepositoryDefinitions();
+
+               $this->assertEquals(
+                       [ '' => 'foodb', 'coolrepo' => 'cooldb' ],
+                       $repositoryDefinitions->getDatabaseNames()
+               );
+               $this->assertEquals(
+                       [ 'item' => 303, 'property' => 808, 'cool' => 666 ],
+                       $repositoryDefinitions->getEntityNamespaces()
+               );
+               $this->assertEquals(
+                       [ '' => 'http://foo.oof/entity/', 'coolrepo' => 
'http://soooo.cooooool/entity/' ],
+                       $repositoryDefinitions->getConceptBaseUris()
+               );
+       }
+
+       public function 
testGivenOnlyRepositorySettingPresent_theSettingIsUsedToDefineRepositories() {
+               $clientSettings = $this->getDefaultSettings();
+
+               $clientSettings['repositories'] = [
+                       '' => [
+                               'repoDatabase' => 'foodb',
+                               'entityNamespaces' => [ 'item' => 303, 
'property' => 808 ],
+                               'baseUri' => 'http://foo.oof/entity/',
+                               'prefixMapping' => [ '' => '' ],
+                       ],
+                       'coolrepo' => [
+                               'repoDatabase' => 'cooldb',
+                               'entityNamespaces' => [ 'cool' => 666 ],
+                               'baseUri' => 'http://soooo.cooooool/entity/',
+                               'prefixMapping' => [ '' => '' ],
+                       ]
+               ];
+               unset( $clientSettings['repoDatabase'] );
+               unset( $clientSettings['entityNamespaces'] );
+               unset( $clientSettings['repoConceptBaseUri'] );
+               unset( $clientSettings['foreignRepositories'] );
+
+               $this->setMwGlobals( 'wgWBClientSettings', $clientSettings );
+               $this->setMwGlobals( 'wgHooks', [] );
+
+               $client = WikibaseClient::getDefaultInstance( 'reset' );
+               $repositoryDefinitions = $client->getRepositoryDefinitions();
+
+               $this->assertEquals(
+                       [ '' => 'foodb', 'coolrepo' => 'cooldb' ],
+                       $repositoryDefinitions->getDatabaseNames()
+               );
+               $this->assertEquals(
+                       [ 'item' => 303, 'property' => 808, 'cool' => 666 ],
+                       $repositoryDefinitions->getEntityNamespaces()
+               );
+               $this->assertEquals(
+                       [ '' => 'http://foo.oof/entity/', 'coolrepo' => 
'http://soooo.cooooool/entity/' ],
+                       $repositoryDefinitions->getConceptBaseUris()
+               );
+       }
+
+       public function 
testGivenCustomEntityTypeAssignedToParticularRepo_entityNamespaceOnlyDefinedForThisRepo()
 {
+               $clientSettings = $this->getDefaultSettings();
+
+               $clientSettings['repositories'] = [
+                       '' => [
+                               'repoDatabase' => 'foodb',
+                               'entityNamespaces' => [ 'item' => 303, 
'property' => 808 ],
+                               'baseUri' => 'http://foo.oof/entity/',
+                               'prefixMapping' => [ '' => '' ],
+                       ],
+                       'coolrepo' => [
+                               'repoDatabase' => 'cooldb',
+                               'entityNamespaces' => [ 'cool' => 666 ],
+                               'baseUri' => 'http://soooo.cooooool/entity/',
+                               'prefixMapping' => [ '' => '' ],
+                       ]
+               ];
+
+               $this->setMwGlobals( 'wgWBClientSettings', $clientSettings );
+               $this->setMwGlobals( 'wgHooks', [
+                       'WikibaseEntityNamespaces' => [
+                               function ( &$namespaces ) {
+                                       $namespaces['cool'] = 666;
+                               },
+                       ]
+               ] );
+
+               $client = WikibaseClient::getDefaultInstance( 'reset' );
+               $repositoryDefinitions = $client->getRepositoryDefinitions();
+
+               $this->assertEquals( 666, 
$repositoryDefinitions->getEntityNamespaces()['cool'] );
+               $this->assertEquals( [ 'cool' ], 
$repositoryDefinitions->getEntityTypesPerRepository()['coolrepo'] );
+       }
+
+       public function 
testGivenNoRepoAssignedForCustomEntityType_entityNamespaceOnlyDefinedForLocalRepo()
 {
+               $clientSettings = $this->getDefaultSettings();
+
+               $clientSettings['repositories'] = [
+                       '' => [
+                               'repoDatabase' => 'foodb',
+                               'entityNamespaces' => [ 'item' => 303, 
'property' => 808 ],
+                               'baseUri' => 'http://foo.oof/entity/',
+                               'prefixMapping' => [ '' => '' ],
+                       ],
+               ];
+
+               $this->setMwGlobals( 'wgWBClientSettings', $clientSettings );
+               $this->setMwGlobals( 'wgHooks', [
+                       'WikibaseEntityNamespaces' => [
+                               function ( &$namespaces ) {
+                                       $namespaces['cool'] = 666;
+                               },
+                       ]
+               ] );
+
+               $client = WikibaseClient::getDefaultInstance( 'reset' );
+               $repositoryDefinitions = $client->getRepositoryDefinitions();
+
+               $this->assertEquals( 666, 
$repositoryDefinitions->getEntityNamespaces()['cool'] );
+               $this->assertEquals( [ 'item', 'property', 'cool' ], 
$repositoryDefinitions->getEntityTypesPerRepository()[''] );
+       }
+
+       private function getDefaultSettings() {
+               return array_merge(
+                       require __DIR__ . 
'/../../../../lib/config/WikibaseLib.default.php',
+                       require __DIR__ . 
'/../../../config/WikibaseClient.default.php'
+               );
+       }
+
+}
diff --git a/docs/federation.wiki b/docs/federation.wiki
index 240e1f6..decfb0c 100644
--- a/docs/federation.wiki
+++ b/docs/federation.wiki
@@ -5,7 +5,7 @@
 
 As of March 2017, in order to enable access to entities from federated 
repositories both Repo and Client components must be enabled. Also as of March 
2017, accessing data of foreign entities relies on the shared database access 
(databases of federated repositories must be in the same database cluster).
 * Local repository is configured as documented in docs/options.wiki.
-* Configuration of foreign repositories is done using the 
''foreignRepositories'' setting in $wgWBRepoSettings or $wgWBClientSettings, as 
documented in the file docs/options.wiki.
+* Configuration of foreign repositories is done using the 
''foreignRepositories'' setting in $wgWBRepoSettings or ''repositories'' in 
$wgWBClientSettings, as documented in the file docs/options.wiki.
 * In order to correctly link entities from foreign repositories, the local 
wiki must have MediaWiki interwiki prefixes configured for each foreign 
repository. As of March 2017, the interwiki prefix must be the same as the name 
used for the foreign repository. If there is no interwiki prefix configured for 
the wiki containing the foreign repository, it can be added e.g. by adding a 
row to the <code>interwiki</code> database table, or by using 
[[Special:Interwiki]] if the Interwiki extension is enabled on the local wiki.
 
 Note that as of March 2017 it is only possible for Wikibase to use entities 
from a single repository, either local or foreign. For instance, it is not 
possible to use both local and foreign items in statements.
diff --git a/docs/options.wiki b/docs/options.wiki
index fee5d1e..7ad1e8a 100644
--- a/docs/options.wiki
+++ b/docs/options.wiki
@@ -8,16 +8,10 @@
 
 === Basic Settings ===
 
-;entityNamespaces: Defines which kind of entity is managed in which namespace. 
It is given as an associative array mapping entity types such as 
<code>'item'</code> to namespace IDs. Mapping must be done for each type of 
entity that should be supported.
 ;changesDatabase: The database that changes are recorded to for processing by 
clients. This must be set to a symbolic database identifier that MediaWiki's 
LBFactory class understands; <code>false</code> means that the wiki's own 
database shall be used. '''Note''' that on the client, this setting should 
usually be the same as the '''repoDatabase''' setting.
 ;siteLinkGroups: The site groups to use in sitelinks. Must correspond to a 
value used to give the site group in the MediaWiki <code>sites</code> table. 
Default is <code>array( 'wikipedia' )</code>. This defines which groups of 
sites can be linked to Wikibase items.
 :'''Note''' that this setting replaces the old ''siteLinkGroup'' setting, 
which only allowed for a single group.
 ;specialSiteLinkGroups: This maps one or more site groups into a single 
"special" group. This is useful if sites from multiple site groups should be 
shown in a single "special" section on item pages, instead of one section per 
site group. To show these site-groups you have to add the group "special" to 
the '''siteLinkGroups''' setting (see above).
-;foreignRepositories: An associative array mapping foreign repository names to 
settings relevant to the particular repository. Each repository's settings are 
an associative array containing the following keys:
-:;'entityNamespaces': A map of entity type identifiers (strings) that the 
local wiki supports from the foreign repository to namespaces identifiers 
(numbers) related to pages of entities of the given type on foreign 
repository's wiki..
-:;'repoDatabase': A symbolic database identifier (string) that MediaWiki's 
LBFactory class understands.
-:;'baseUri': A base URI (string) for concept URIs. It should contain scheme 
and authority part of the URI.
-:;'prefixMapping': A prefix mapping array, see also: 
docs/foreign-entity-ids.wiki in the DataModel component.
 
 === Expert Settings ===
 
@@ -36,6 +30,7 @@
 
 === Basic Settings ===
 
+;entityNamespaces: Defines which kind of entity is managed in which namespace. 
It is given as an associative array mapping entity types such as 
<code>'item'</code> to namespace IDs. Mapping must be done for each type of 
entity that should be supported.
 ;dataRightsUrl: URL to link to license for data contents. Defaults to 
<code>$wgRightsUrl</code> setting.
 ;dataRightsText: Text for data license link. Defaults to 
<code>$wgRightsText</code> setting.
 ;sparqlEndpoint: URL to the service description of the SPARQL end point for 
the repository. Defaults to null, meaning there is no SPARQL endpoint.
@@ -44,6 +39,11 @@
 ;conceptBaseUri: Base URI for building concept URIs (for example used in Rdf 
output). This has to include the protocol and domain, only an entity identifier 
will be appended.
 ;preferredGeoDataProperties: List of properties (by ID string), in order of 
preference, that are considered when finding primary coordinates for the 
GeoData extension on an entity. Defaults to an empty array.
 ;localClientDatabases: An array of locally accessible client databases, for 
use by the <code>dispatchChanges.php</code> script. This setting determines to 
which wikis changes are pushed directly. It must be given either as an 
associative array, mapping global site IDs to logical database names, or, of 
the database names are the same as the site IDs, as a list of databases. The 
default is an empty array, indicating no local client databases.
+;foreignRepositories: An associative array mapping foreign repository names to 
settings relevant to the particular repository. Each repository's settings are 
an associative array containing the following keys:
+:;'entityNamespaces': A map of entity type identifiers (strings) that the 
local wiki supports from the foreign repository to namespaces identifiers 
(numbers) related to pages of entities of the given type on foreign 
repository's wiki..
+:;'repoDatabase': A symbolic database identifier (string) that MediaWiki's 
LBFactory class understands.
+:;'baseUri': A base URI (string) for concept URIs. It should contain scheme 
and authority part of the URI.
+:;'prefixMapping': A prefix mapping array, see also: 
docs/foreign-entity-ids.wiki in the DataModel component.
 
 === Expert Settings ===
 
@@ -79,9 +79,12 @@
 
 ;namespaces: List of namespaces on the client wiki that should have access to 
repository items. Default: <code>array()</code> (treated as setting is not set, 
ie. namespaces are enabled).
 ;excludeNamespaces: List of namespaces on the client wiki to disable wikibase 
links, etc. for.  Default: <code>array()</code>. Example: <code>array( 
NS_USER_TALK )</code>.
+;repositories: An associative array mapping repository names to settings 
relevant to the particular repository. Local repository is identified using the 
empty string as its name. Each repository's settings are an associative array 
containing the following keys:
+:;'entityNamespaces': A map of entity type identifiers (strings) that the 
local wiki supports from the foreign repository to namespaces identifiers 
(numbers) related to pages of entities of the given type on foreign 
repository's wiki..
+:;'repoDatabase': A symbolic database identifier (string) that MediaWiki's 
LBFactory class understands. Note that <code>false</code> would mean "this 
wiki's database"!
+:;'baseUri': A base URI (string) for concept URIs. It should contain scheme 
and authority part of the URI.
+:;'prefixMapping': A prefix mapping array, see also: 
docs/foreign-entity-ids.wiki in the DataModel component.
 ;repoUrl: The repository's base URL, including the schema (protocol) and 
domain; This URL can be protocol-relative. Default is 
<code>'//wikidata.org'</code>.
-:'''Note:''' This may be removed once we can get this information from the 
sites table.
-;repoConceptBaseUri: The base of the repository's concept URIs. Default is 
<code>'<i>repoUrl</i>/entity/'</code>.
 :'''Note:''' This may be removed once we can get this information from the 
sites table.
 ;repoScriptPath: The repository's script path. Default is $wgScriptPath, 
assuming that the repo's script path is the same as this wiki's script path.
 :'''Note:''' This may be removed once we can get this information from the 
sites table.
@@ -93,8 +96,6 @@
 ;siteGroup: This site's site group (e.g. <code>'wikipedia'</code> or 
<code>'wikivoyage'</code>) as used in the sites table. The setting is optional 
and falls back to site store lookup. For performance reasons, it may be 
desireable to set this explicitly to avoid lookups.
 ;repoSiteId: Site ID of connected repository wiki. Default is to assume client 
and repo, so this setting defaults to siteGlobalID.
 ;repoSiteName: Site name of the connected repository wiki. Default is to 
assume client and repo are same wiki, so defaults to global $wgSitename 
setting.  If not the same wiki, defaults to 'Wikidata'. This setting can also 
be set to an i18n message key and will be handled as a message, if the message 
key exists so that the repo site name can be translatable.
-;repoDatabase: The logical name of the repository database, in a form that 
LBFactory can understand. If not <code>null</code>, the client wiki will access 
the repository's database directly, instead of locally caching information 
received via change notifications. Default: <code>null</code>. Note that 
<code>false</code> would mean "this wiki's database"!
-:'''Note:''' This is currently required to be not <code>null</code>, since 
local caching is not fully implemented.
 ;repoNamespaces: An array telling the client wiki which namespaces on the 
repository are used for which entity type. This is given as an associative 
array mapping entity type IDs such as Item::ENTITY_TYPE, to namespace names. 
This information is used when constructing links to entities on the repository. 
Default (items in main namespace):
 <poem>
           array(
diff --git a/lib/includes/WikibaseSettings.php 
b/lib/includes/WikibaseSettings.php
index ad5abdc..b5057de 100644
--- a/lib/includes/WikibaseSettings.php
+++ b/lib/includes/WikibaseSettings.php
@@ -65,7 +65,10 @@
                }
 
                $settings = self::getSettings( 'wgWBClientSettings' );
-               $settings->setSetting( 'entityNamespaces', 
self::buildEntityNamespaceConfigurations( $settings ) );
+
+               $entityNamespaces = self::buildEntityNamespaceConfigurations( 
$settings );
+               self::applyEntityNamespacesToSettings( $settings, 
$entityNamespaces );
+
                return $settings;
        }
 
@@ -96,16 +99,46 @@
         * @return int[] An array mapping entity type identifiers to namespace 
numbers.
         */
        private static function buildEntityNamespaceConfigurations( 
SettingsArray $settings ) {
-               if ( !$settings->hasSetting( 'entityNamespaces' ) ) {
+               if ( !$settings->hasSetting( 'repositories' ) && 
!$settings->hasSetting( 'entityNamespaces' ) ) {
                        throw new MWException( 'Wikibase: Incomplete 
configuration: '
                                . 'The \'entityNamespaces\' setting has to be 
set to an '
                                . 'array mapping entity types to namespace IDs. 
'
                                . 'See Wikibase.example.php for details and 
examples.' );
                }
 
-               $namespaces = $settings->getSetting( 'entityNamespaces' );
+               $namespaces = $settings->hasSetting( 'entityNamespaces' )
+                       ? $settings->getSetting( 'entityNamespaces' )
+                       : self::getEntityNamespacesFromRepositorySettings( 
$settings->getSetting( 'repositories' ) );
+
                Hooks::run( 'WikibaseEntityNamespaces', [ &$namespaces ] );
                return $namespaces;
        }
 
+       private static function getEntityNamespacesFromRepositorySettings( 
array $repositorySettings ) {
+               return array_reduce(
+                       $repositorySettings,
+                       function ( array $result, array $repoSettings ) {
+                               return array_merge( $result, 
$repoSettings['entityNamespaces'] );
+                       },
+                       []
+               );
+       }
+
+       private static function applyEntityNamespacesToSettings( SettingsArray 
$settings, array $entityNamespaces ) {
+               if ( $settings->hasSetting( 'entityNamespaces' ) ) {
+                       $settings->setSetting( 'entityNamespaces', 
$entityNamespaces );
+                       return;
+               }
+
+               $repositorySettings = $settings->getSetting( 'repositories' );
+               $namespacesDefinedForRepositories = 
self::getEntityNamespacesFromRepositorySettings( $repositorySettings );
+
+               $namespacesInNoRepository = array_diff_key( $entityNamespaces, 
$namespacesDefinedForRepositories );
+
+               if ( $namespacesInNoRepository ) {
+                       $repositorySettings['']['entityNamespaces'] += 
$namespacesInNoRepository;
+                       $settings->setSetting( 'repositories', 
$repositorySettings );
+               }
+       }
+
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/404679
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ibbdb5d0317ef741b4d755b932b4317e3b3bd2a07
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: WMDE-leszek <leszek.mani...@wikimedia.de>
Gerrit-Reviewer: Ladsgroup <ladsgr...@gmail.com>
Gerrit-Reviewer: WMDE-leszek <leszek.mani...@wikimedia.de>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to