From: Kunal Mehta Date: Wed, 27 Apr 2016 02:21:59 +0000 (-0700) Subject: Add interwiki support to LinkTarget and TitleValue X-Git-Tag: 1.31.0-rc.0~7159^2 X-Git-Url: http://git.heureux-cyclage.org/?a=commitdiff_plain;h=9b1f8b4ca331e45c9718097af33f46416c7911f2;p=lhc%2Fweb%2Fwiklou.git Add interwiki support to LinkTarget and TitleValue This adds support to the LinkTarget interface and TitleValue implementation for having an interwiki component, matching the function names used in Title. MediaWikiTitleCodec was updated accordingly. The motivation behind this change is to be able to fully use LinkTarget in the Linker rewrite instead of depending upon Title. Change-Id: I6666b64f0e336aadc7261e7ca87ac2e498c61856 --- diff --git a/includes/linker/LinkTarget.php b/includes/linker/LinkTarget.php index 2764f461ff..7b59751de6 100644 --- a/includes/linker/LinkTarget.php +++ b/includes/linker/LinkTarget.php @@ -73,4 +73,18 @@ interface LinkTarget { * @return LinkTarget */ public function createFragmentTarget( $fragment ); + + /** + * Whether this LinkTarget has an interwiki component + * + * @return bool + */ + public function isExternal(); + + /** + * The interwiki component of this LinkTarget + * + * @return string + */ + public function getInterwiki(); } diff --git a/includes/title/MediaWikiTitleCodec.php b/includes/title/MediaWikiTitleCodec.php index 7c08be4cb3..0e291ede14 100644 --- a/includes/title/MediaWikiTitleCodec.php +++ b/includes/title/MediaWikiTitleCodec.php @@ -98,11 +98,12 @@ class MediaWikiTitleCodec implements TitleFormatter, TitleParser { * @param string $text The page title. Should be valid. Only minimal normalization is applied. * Underscores will be replaced. * @param string $fragment The fragment name (may be empty). + * @param string $interwiki The interwiki name (may be empty). * * @throws InvalidArgumentException If the namespace is invalid * @return string */ - public function formatTitle( $namespace, $text, $fragment = '' ) { + public function formatTitle( $namespace, $text, $fragment = '', $interwiki = '' ) { if ( $namespace !== false ) { $namespace = $this->getNamespaceName( $namespace, $text ); @@ -115,6 +116,10 @@ class MediaWikiTitleCodec implements TitleFormatter, TitleParser { $text = $text . '#' . $fragment; } + if ( $interwiki !== '' ) { + $text = $interwiki . ':' . $text; + } + $text = str_replace( '_', ' ', $text ); return $text; @@ -136,17 +141,17 @@ class MediaWikiTitleCodec implements TitleFormatter, TitleParser { // be refactored to avoid this. $parts = $this->splitTitleString( $text, $defaultNamespace ); - // Interwiki links are not supported by TitleValue - if ( $parts['interwiki'] !== '' ) { - throw new MalformedTitleException( 'title-invalid-interwiki', $text ); - } - // Relative fragment links are not supported by TitleValue if ( $parts['dbkey'] === '' ) { throw new MalformedTitleException( 'title-invalid-empty', $text ); } - return new TitleValue( $parts['namespace'], $parts['dbkey'], $parts['fragment'] ); + return new TitleValue( + $parts['namespace'], + $parts['dbkey'], + $parts['fragment'], + $parts['interwiki'] + ); } /** @@ -168,7 +173,12 @@ class MediaWikiTitleCodec implements TitleFormatter, TitleParser { * @return string */ public function getPrefixedText( LinkTarget $title ) { - return $this->formatTitle( $title->getNamespace(), $title->getText(), '' ); + return $this->formatTitle( + $title->getNamespace(), + $title->getText(), + '', + $title->getInterwiki() + ); } /** @@ -179,7 +189,12 @@ class MediaWikiTitleCodec implements TitleFormatter, TitleParser { * @return string */ public function getFullText( LinkTarget $title ) { - return $this->formatTitle( $title->getNamespace(), $title->getText(), $title->getFragment() ); + return $this->formatTitle( + $title->getNamespace(), + $title->getText(), + $title->getFragment(), + $title->getInterwiki() + ); } /** diff --git a/includes/title/TitleFormatter.php b/includes/title/TitleFormatter.php index 96f396c05c..c081129e27 100644 --- a/includes/title/TitleFormatter.php +++ b/includes/title/TitleFormatter.php @@ -42,10 +42,11 @@ interface TitleFormatter { * @param int|bool $namespace The namespace ID (or false, if the namespace should be ignored) * @param string $text The page title * @param string $fragment The fragment name (may be empty). + * @param string $interwiki The interwiki prefix (may be empty). * * @return string */ - public function formatTitle( $namespace, $text, $fragment = '' ); + public function formatTitle( $namespace, $text, $fragment = '', $interwiki = '' ); /** * Returns the title text formatted for display, without namespace of fragment. diff --git a/includes/title/TitleValue.php b/includes/title/TitleValue.php index c23d6989f4..63c075f1c5 100644 --- a/includes/title/TitleValue.php +++ b/includes/title/TitleValue.php @@ -30,9 +30,6 @@ use Wikimedia\Assert\Assert; * @note In contrast to Title, this is designed to be a plain value object. That is, * it is immutable, does not use global state, and causes no side effects. * - * @note TitleValue represents the title of a local page (or fragment of a page). - * It does not represent a link, and does not support interwiki prefixes etc. - * * @see https://www.mediawiki.org/wiki/Requests_for_comment/TitleValue * @since 1.23 */ @@ -52,6 +49,11 @@ class TitleValue implements LinkTarget { */ protected $fragment; + /** + * @var string + */ + protected $interwiki; + /** * Constructs a TitleValue. * @@ -65,13 +67,15 @@ class TitleValue implements LinkTarget { * @param string $dbkey The page title in valid DBkey form. No normalization is applied. * @param string $fragment The fragment title. Use '' to represent the whole page. * No validation or normalization is applied. + * @param string $interwiki The interwiki component * * @throws InvalidArgumentException */ - public function __construct( $namespace, $dbkey, $fragment = '' ) { + public function __construct( $namespace, $dbkey, $fragment = '', $interwiki = '' ) { Assert::parameterType( 'integer', $namespace, '$namespace' ); Assert::parameterType( 'string', $dbkey, '$dbkey' ); Assert::parameterType( 'string', $fragment, '$fragment' ); + Assert::parameterType( 'string', $interwiki, '$interwiki' ); // Sanity check, no full validation or normalization applied here! Assert::parameter( !preg_match( '/^_|[ \r\n\t]|_$/', $dbkey ), '$dbkey', 'invalid DB key' ); @@ -80,6 +84,7 @@ class TitleValue implements LinkTarget { $this->namespace = $namespace; $this->dbkey = $dbkey; $this->fragment = $fragment; + $this->interwiki = $interwiki; } /** @@ -138,7 +143,32 @@ class TitleValue implements LinkTarget { * @return TitleValue */ public function createFragmentTarget( $fragment ) { - return new TitleValue( $this->namespace, $this->dbkey, $fragment ); + return new TitleValue( + $this->namespace, + $this->dbkey, + $fragment, + $this->interwiki + ); + } + + /** + * Whether it has an interwiki part + * + * @since 1.27 + * @return bool + */ + public function isExternal() { + return $this->interwiki !== ''; + } + + /** + * Returns the interwiki part + * + * @since 1.27 + * @return string + */ + public function getInterwiki() { + return $this->interwiki; } /** @@ -155,6 +185,10 @@ class TitleValue implements LinkTarget { $name .= '#' . $this->fragment; } + if ( $this->interwiki !== '' ) { + $name = $this->interwiki . ':' . $name; + } + return $name; } } diff --git a/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php b/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php index 0bb0afeec0..e321bdb252 100644 --- a/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php +++ b/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php @@ -87,13 +87,14 @@ class MediaWikiTitleCodecTest extends MediaWikiTestCase { public static function provideFormat() { return [ - [ NS_MAIN, 'Foo_Bar', '', 'en', 'Foo Bar' ], - [ NS_USER, 'Hansi_Maier', 'stuff_and_so_on', 'en', 'User:Hansi Maier#stuff and so on' ], - [ false, 'Hansi_Maier', '', 'en', 'Hansi Maier' ], + [ NS_MAIN, 'Foo_Bar', '', '', 'en', 'Foo Bar' ], + [ NS_USER, 'Hansi_Maier', 'stuff_and_so_on', '', 'en', 'User:Hansi Maier#stuff and so on' ], + [ false, 'Hansi_Maier', '', '', 'en', 'Hansi Maier' ], [ NS_USER_TALK, 'hansi__maier', '', + '', 'en', 'User talk:hansi maier', 'User talk:Hansi maier' @@ -101,20 +102,23 @@ class MediaWikiTitleCodecTest extends MediaWikiTestCase { // getGenderCache() provides a mock that considers first // names ending in "a" to be female. - [ NS_USER, 'Lisa_Müller', '', 'de', 'Benutzerin:Lisa Müller' ], + [ NS_USER, 'Lisa_Müller', '', '', 'de', 'Benutzerin:Lisa Müller' ], + [ NS_MAIN, 'FooBar', '', 'remotetestiw', 'en', 'remotetestiw:FooBar' ], ]; } /** * @dataProvider provideFormat */ - public function testFormat( $namespace, $text, $fragment, $lang, $expected, $normalized = null ) { + public function testFormat( $namespace, $text, $fragment, $interwiki, $lang, $expected, + $normalized = null + ) { if ( $normalized === null ) { $normalized = $expected; } $codec = $this->makeCodec( $lang ); - $actual = $codec->formatTitle( $namespace, $text, $fragment ); + $actual = $codec->formatTitle( $namespace, $text, $fragment, $interwiki ); $this->assertEquals( $expected, $actual, 'formatted' ); @@ -123,7 +127,8 @@ class MediaWikiTitleCodecTest extends MediaWikiTestCase { $actual2 = $codec->formatTitle( $parsed->getNamespace(), $parsed->getText(), - $parsed->getFragment() + $parsed->getFragment(), + $parsed->getInterwiki() ); $this->assertEquals( $normalized, $actual2, 'normalized after round trip' ); @@ -293,7 +298,6 @@ class MediaWikiTitleCodecTest extends MediaWikiTestCase { [ 'Talk:File:Foo.jpg' ], [ 'Talk:localtestiw:Foo' ], - [ 'remotetestiw:Foo' ], [ '::1' ], // only valid in user namespace [ 'User::x' ], // leading ":" in a user name is only valid of IPv6 addresses diff --git a/tests/phpunit/includes/title/TitleValueTest.php b/tests/phpunit/includes/title/TitleValueTest.php index 913253bc1b..792255339f 100644 --- a/tests/phpunit/includes/title/TitleValueTest.php +++ b/tests/phpunit/includes/title/TitleValueTest.php @@ -28,49 +28,57 @@ class TitleValueTest extends MediaWikiTestCase { public function goodConstructorProvider() { return [ - [ NS_USER, 'TestThis', 'stuff', true ], - [ NS_USER, 'TestThis', '', false ], + [ NS_USER, 'TestThis', 'stuff', '', true, false ], + [ NS_USER, 'TestThis', '', 'baz', false, true ], ]; } /** * @dataProvider goodConstructorProvider */ - public function testConstruction( $ns, $text, $fragment, $hasFragment ) { - $title = new TitleValue( $ns, $text, $fragment ); + public function testConstruction( $ns, $text, $fragment, $interwiki, $hasFragment, + $hasInterwiki + ) { + $title = new TitleValue( $ns, $text, $fragment, $interwiki ); $this->assertEquals( $ns, $title->getNamespace() ); $this->assertEquals( $text, $title->getText() ); $this->assertEquals( $fragment, $title->getFragment() ); $this->assertEquals( $hasFragment, $title->hasFragment() ); + $this->assertEquals( $interwiki, $title->getInterwiki() ); + $this->assertEquals( $hasInterwiki, $title->isExternal() ); } public function badConstructorProvider() { return [ - [ 'foo', 'title', 'fragment' ], - [ null, 'title', 'fragment' ], - [ 2.3, 'title', 'fragment' ], + [ 'foo', 'title', 'fragment', '' ], + [ null, 'title', 'fragment', '' ], + [ 2.3, 'title', 'fragment', '' ], - [ NS_MAIN, 5, 'fragment' ], - [ NS_MAIN, null, 'fragment' ], - [ NS_MAIN, '', 'fragment' ], - [ NS_MAIN, 'foo bar', '' ], - [ NS_MAIN, 'bar_', '' ], - [ NS_MAIN, '_foo', '' ], - [ NS_MAIN, ' eek ', '' ], + [ NS_MAIN, 5, 'fragment', '' ], + [ NS_MAIN, null, 'fragment', '' ], + [ NS_MAIN, '', 'fragment', '' ], + [ NS_MAIN, 'foo bar', '', '' ], + [ NS_MAIN, 'bar_', '', '' ], + [ NS_MAIN, '_foo', '', '' ], + [ NS_MAIN, ' eek ', '', '' ], - [ NS_MAIN, 'title', 5 ], - [ NS_MAIN, 'title', null ], - [ NS_MAIN, 'title', [] ], + [ NS_MAIN, 'title', 5, '' ], + [ NS_MAIN, 'title', null, '' ], + [ NS_MAIN, 'title', [], '' ], + + [ NS_MAIN, 'title', '', 5 ], + [ NS_MAIN, 'title', null, 5 ], + [ NS_MAIN, 'title', [], 5 ], ]; } /** * @dataProvider badConstructorProvider */ - public function testConstructionErrors( $ns, $text, $fragment ) { + public function testConstructionErrors( $ns, $text, $fragment, $interwiki ) { $this->setExpectedException( 'InvalidArgumentException' ); - new TitleValue( $ns, $text, $fragment ); + new TitleValue( $ns, $text, $fragment, $interwiki ); } public function fragmentTitleProvider() {