Yurik has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/332089 )

Change subject: Fix cross-wiki title normalization
......................................................................

Fix cross-wiki title normalization

* Remove a nasty hack of using a well known interwiki name
* Capitalize title

Bug: T155214
Change-Id: I72ca4c5ca36b7e04f1f002ad4df74dff3b2600b8
---
M includes/JCSingleton.php
M tests/phpunit/JCTitleParsingTest.php
2 files changed, 84 insertions(+), 37 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/JsonConfig 
refs/changes/89/332089/1

diff --git a/includes/JCSingleton.php b/includes/JCSingleton.php
index 797cd90..73d32a8 100644
--- a/includes/JCSingleton.php
+++ b/includes/JCSingleton.php
@@ -7,9 +7,11 @@
 use Exception;
 use GenderCache;
 use Html;
+use Interwiki;
 use Language;
 use MalformedTitleException;
 use MapCacheLRU;
+use MediaWiki\Interwiki\InterwikiLookup;
 use MediaWiki\Linker\LinkTarget;
 use MediaWikiTitleCodec;
 use TitleParser;
@@ -53,6 +55,11 @@
         * @var MapCacheLRU[] contains a cache of recently requested content 
objects as namespace => MapCacheLRU
         */
        static $mapCacheLru = [];
+
+       /**
+        * @var Language cached invariant language to use for all titles parsing
+        */
+       static $language;
 
        /**
         * @var TitleParser cached invariant title parser
@@ -138,6 +145,7 @@
                        self::getConfVal( $conf, 'cacheKey', '' );
                        self::getConfVal( $conf, 'flaggedRevs', false );
                        self::getConfVal( $conf, 'license', false );
+                       self::getConfVal( $conf, 'capitalize', true );
                        $islocal = self::getConfVal( $conf, 'isLocal', true );
 
                        // Decide if matching configs should be stored on this 
wiki
@@ -171,15 +179,15 @@
 
                        // Too lazy to write proper error messages for all 
parameters.
                        if ( ( isset( $conf->nsTalk ) && !is_string( 
$conf->nsTalk ) ) || !is_string( $conf->pattern ) ||
-                            !is_bool( $islocal ) || !is_int( $conf->cacheExp ) 
||
-                            !is_string( $conf->cacheKey ) || !is_bool( 
$conf->flaggedRevs )
+                                !is_bool( $islocal ) || !is_int( 
$conf->cacheExp ) ||
+                                !is_string( $conf->cacheKey ) || !is_bool( 
$conf->flaggedRevs )
                        ) {
                                $warnFunc( "JsonConfig: Invalid type of one of 
the parameters in \$wgJsonConfigs['$confId'], please check documentation" );
                                continue;
                        }
                        if ( isset( $remote ) ) {
                                if ( !is_string( $remote->url ) || !is_string( 
$remote->username ) ||
-                                    !is_string( $remote->password )
+                                        !is_string( $remote->password )
                                ) {
                                        $warnFunc( "JsonConfig: Invalid type of 
one of the parameters in \$wgJsonConfigs['$confId']['remote'], please check 
documentation" );
                                        continue;
@@ -187,7 +195,7 @@
                        }
                        if ( isset( $store ) ) {
                                if ( !is_bool( $store->cacheNewValue ) || 
!is_string( $store->notifyUrl ) ||
-                                    !is_string( $store->notifyUsername ) || 
!is_string( $store->notifyPassword )
+                                        !is_string( $store->notifyUsername ) 
|| !is_string( $store->notifyPassword )
                                ) {
                                        $warnFunc( "JsonConfig: Invalid type of 
one of the parameters in \$wgJsonConfigs['$confId']['store'], please check 
documentation" );
                                        continue;
@@ -202,7 +210,7 @@
                                                if ( !array_key_exists( $ns, 
$namespaces ) ) {
                                                        $namespaces[$ns] = 
false;
                                                }
-                                   } elseif ( $ns === NS_CONFIG ) {
+                                       } elseif ( $ns === NS_CONFIG ) {
                                                $warnFunc( "JsonConfig: 
Parameter 'nsName' in \$wgJsonConfigs['$confId'] is not " .
                                                                   "supported 
for namespace == NS_CONFIG ($ns)" );
                                        } else {
@@ -213,7 +221,7 @@
                                                                           "if 
given, nsName must be a string" );
                                                        continue;
                                                } elseif ( array_key_exists( 
$ns, $namespaces ) &&
-                                                          $namespaces[$ns] !== 
null
+                                                                  
$namespaces[$ns] !== null
                                                ) {
                                                        if ( $namespaces[$ns] 
!== $nsName ||
                                                                 
$namespaces[$ns + 1] !== $nsTalk
@@ -458,30 +466,39 @@
                                $cache = self::$titleMapCacheLru[$namespace];
                        }
 
+                       if ( !self::$language ) {
+                               self::$language = Language::factory( 'en' );
+                       }
+
                        // Parse string if needed
                        // TODO: should the string parsing also be cached?
                        if ( is_string( $value ) ) {
                                if ( !self::$titleParser ) {
                                        self::$titleParser =
-                                               new MediaWikiTitleCodec( 
Language::factory( 'en' ), new GenderCache() );
+                                               new MediaWikiTitleCodec(
+                                                       self::$language,
+                                                       new GenderCache(),
+                                                       [],
+                                                       new 
FauxInterwikiLookup() );
                                }
-                               // Major hack, but until MediaWikiTitleCodec 
has global state, I can't think of a
-                               // better way. Interwiki prefixes are a special 
case for title parsing:
+                               // Interwiki prefixes are a special case for 
title parsing:
                                // first letter is not capitalized, namespaces 
are not resolved, etc.
                                // So we prepend an interwiki prefix to fool 
title codec, and later remove it.
-                               global $wgJsonConfigInterwikiPrefix;
                                try {
-                                       $value = $wgJsonConfigInterwikiPrefix . 
':' . $value;
+                                       $value = 
FauxInterwikiLookup::MagicInterwikiPrefix . ':' . $value;
                                        $parts = 
self::$titleParser->splitTitleString( $value );
+
+                                       // Defensive coding - ensure the 
parsing has proceeded as expected
                                        if ( $parts['dbkey'] === '' || 
$parts['namespace'] !== 0 ||
                                                 $parts['fragment'] !== '' || 
$parts['local_interwiki'] !== false ||
-                                                $parts['interwiki'] !== 
$wgJsonConfigInterwikiPrefix
+                                                $parts['interwiki'] !== 
FauxInterwikiLookup::MagicInterwikiPrefix
                                        ) {
                                                return null;
                                        }
                                } catch ( MalformedTitleException $e ) {
                                        return null;
                                }
+
                                $dbKey = $parts['dbkey'];
                        } else {
                                $dbKey = $value->getDBkey();
@@ -497,7 +514,8 @@
                                foreach ( $map[$namespace] as $conf ) {
                                        $re = $conf->pattern;
                                        if ( !$re || preg_match( $re, $dbKey ) 
) {
-                                               $result = new JCTitle( 
$namespace, $dbKey, $conf );
+                                               $dbKey2 = $conf->capitalize ? 
self::$language->ucfirst( $dbKey ) : $dbKey;
+                                               $result = new JCTitle( 
$namespace, $dbKey2, $conf );
                                                break;
                                        }
                                }
@@ -540,16 +558,16 @@
                        if ( $name === false ) { // must be already declared
                                if ( !array_key_exists( $ns, $namespaces ) ) {
                                        wfLogWarning( "JsonConfig: Invalid 
\$wgJsonConfigs: Namespace $ns " .
-                                                     "has not been declared by 
core or other extensions" );
+                                                                 "has not been 
declared by core or other extensions" );
                                }
                        } elseif ( array_key_exists( $ns, $namespaces ) ) {
                                wfLogWarning( "JsonConfig: Invalid 
\$wgJsonConfigs: Namespace $ns => '$name' " .
-                                       "is already declared as 
'$namespaces[$ns]'" );
+                                                         "is already declared 
as '$namespaces[$ns]'" );
                        } else {
                                $key = array_search( $name, $namespaces );
                                if ( $key !== false ) {
                                        wfLogWarning( "JsonConfig: Invalid 
\$wgJsonConfigs: Namespace $ns => '$name' " .
-                                               "has identical name with the 
namespace #$key" );
+                                                                 "has 
identical name with the namespace #$key" );
                                } else {
                                        $namespaces[$ns] = $name;
                                }
@@ -757,7 +775,7 @@
                // todo/fixme? We should probably add ext.jsonConfig style to 
only those pages that pass parseTitle()
                $handler = ContentHandler::getForModelID( 
$title->getContentModel() );
                if ( $handler->getDefaultFormat() === CONTENT_FORMAT_JSON ||
-                       self::parseTitle( $title )
+                        self::parseTitle( $title )
                ) {
                        $out->addModuleStyles( 'ext.jsonConfig' );
                }
@@ -816,7 +834,7 @@
        }
 
        public static function onPageContentSaveComplete( /** @noinspection 
PhpUnusedParameterInspection */ $article, $user, $content, $summary, $isMinor, 
$isWatch,
-               $section, $flags, $revision, $status, $baseRevId ) {
+                                                                               
                                                                                
                                                $section, $flags, $revision, 
$status, $baseRevId ) {
                return self::onArticleChangeComplete( $article, $content );
        }
 
@@ -830,7 +848,7 @@
 
        public static function onTitleMoveComplete( /** @noinspection 
PhpUnusedParameterInspection */ $title, $newTitle, $wgUser, $pageid, $redirid, 
$reason ) {
                return self::onArticleChangeComplete( $title ) ||
-                      self::onArticleChangeComplete( $newTitle );
+                          self::onArticleChangeComplete( $newTitle );
        }
 
        /**
@@ -920,3 +938,50 @@
                return $isStorage;
        }
 }
+
+class FauxInterwikiLookup implements InterwikiLookup {
+
+       /**
+        * This class will only accept this string as a valid interwiki
+        */
+       const MagicInterwikiPrefix = 'Xyz';
+
+       /**
+        * Check whether an interwiki prefix exists
+        *
+        * @param string $prefix Interwiki prefix to use
+        * @return bool Whether it exists
+        */
+       public function isValidInterwiki( $prefix ) {
+               return (bool) $this->fetch( $prefix );
+       }
+
+       /**
+        * Fetch an Interwiki object
+        *
+        * @param string $prefix Interwiki prefix to use
+        * @return Interwiki|null|bool
+        */
+       public function fetch( $prefix ) {
+               if ( $prefix !== self::MagicInterwikiPrefix ) {
+                       return false;
+               }
+               return new Interwiki( self::MagicInterwikiPrefix );
+       }
+
+       /**
+        * Returns all interwiki prefixes
+        *
+        * @param string|null $local If set, limits output to local/non-local 
interwikis
+        * @return string[] List of prefixes
+        */
+       public function getAllPrefixes( $local = null ) {
+               return ( $local === null || $local === false ) ? [ 
self::MagicInterwikiPrefix ] : [ ];
+       }
+
+       /**
+        * Purge the in-process and persistent object cache for an interwiki 
prefix
+        * @param string $prefix
+        */
+       public function invalidateCache( $prefix ) {}
+}
diff --git a/tests/phpunit/JCTitleParsingTest.php 
b/tests/phpunit/JCTitleParsingTest.php
index d329854..050f4d3 100644
--- a/tests/phpunit/JCTitleParsingTest.php
+++ b/tests/phpunit/JCTitleParsingTest.php
@@ -24,24 +24,6 @@
                        'wgMetaNamespace' => 'Project',
                        'wgLocalInterwikis' => [ 'localtestiw' ],
                        'wgCapitalLinks' => true,
-
-                       // Make sure we use the matching prefix
-                       'wgJsonConfigInterwikiPrefix' => 'remotetestiw',
-
-                       // NOTE: this is why global state is evil.
-                       // TODO: refactor access to the interwiki codes so it 
can be injected.
-                       'wgHooks' => [
-                               'InterwikiLoadPrefix' => [
-                                       function ( $prefix, &$data ) {
-                                               if ( $prefix === 'localtestiw' 
) {
-                                                       $data = [ 'iw_url' => 
'localtestiw' ];
-                                               } elseif ( $prefix === 
'remotetestiw' ) {
-                                                       $data = [ 'iw_url' => 
'remotetestiw' ];
-                                               }
-                                               return false;
-                                       }
-                               ]
-                       ]
                ] );
                $this->setUserLang( 'en' );
                $this->setContentLang( 'en' );

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I72ca4c5ca36b7e04f1f002ad4df74dff3b2600b8
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/JsonConfig
Gerrit-Branch: master
Gerrit-Owner: Yurik <yu...@wikimedia.org>

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

Reply via email to