Prefix links with namespace if needed.

- Fix: Links to other namespaces were not prefixed properly.
This commit is contained in:
Daniel Kraus
2017-08-31 13:52:54 +02:00
parent 6294ee8679
commit 352add204f
3 changed files with 93 additions and 56 deletions

View File

@ -35,15 +35,16 @@ class Linker {
public $config; public $config;
/** /**
* The string representation of the title object for the potential target page * The link value of the target page that is currently being evaluated.
* that is currently being processed. * This may be either the page name or the page name prefixed with the
* name space if the target's name space is not NS_MAIN.
* *
* This is an instance variable (rather than a local method variable) so it * This is an instance variable (rather than a local method variable) so it
* can be accessed in the preg_replace_callback callbacks. * can be accessed in the preg_replace_callback callbacks.
* *
* @var String $targetTitleString * @var String $linkValue
*/ */
private $targetTitleText; private $linkValue;
/** /**
* Constructs a new instance of the Linker class. * Constructs a new instance of the Linker class.
@ -101,7 +102,7 @@ class Linker {
$count = 0; $count = 0;
// Cache the target title text for the regex callbacks // Cache the target title text for the regex callbacks
$this->targetTitleText = $target->getTitleText(); $this->linkValue = $target->getPrefixedTitleText();
// Even indexes will point to sections of the text that may be linked // Even indexes will point to sections of the text that may be linked
for ( $i = 0; $i < count( $arr ); $i += 2 ) { for ( $i = 0; $i < count( $arr ); $i += 2 ) {
@ -147,7 +148,13 @@ class Linker {
* @return string Target page title with or without link markup * @return string Target page title with or without link markup
*/ */
private function simpleModeCallback( array $matches ) { private function simpleModeCallback( array $matches ) {
return '[[' . $matches[0] . ']]'; // If the link value is longer than the match, it must be prefixed with
// a namespace. In this case, we build a piped link.
if ( strlen( $this->linkValue ) > strlen( $matches[0] ) ) {
return '[[' . $this->linkValue . '|' . $matches[0] . ']]';
} else {
return '[[' . $matches[0] . ']]';
}
} }
/** /**
@ -163,28 +170,20 @@ class Linker {
* @return string Target page title with or without link markup * @return string Target page title with or without link markup
*/ */
private function smartModeCallback( array $matches ) { private function smartModeCallback( array $matches ) {
// If cases of the target page title and the actual occurrence in the text
// are not identical, we need to build a piped link.
// How case-identity is determined depends on the $wgCapitalLinks setting:
// with $wgCapitalLinks = true, the case of first letter of the title is
// not significant.
if ( $this->config->capitalLinks ) { if ( $this->config->capitalLinks ) {
// With $wgCapitalLinks set to true we have a slightly more $needPipe = strcmp( substr( $this->linkValue, 1 ), substr( $matches[ 0 ], 1 ) ) != 0;
// complicated version of the callback than if it were false;
// we need to ignore the first letter of the page titles, as
// it does not matter for linking.
if ( strcmp( substr( $this->targetTitleText, 1 ), substr( $matches[ 0 ], 1) ) == 0 ) {
// Case-sensitive match: no need to build piped link.
return '[[' . $matches[ 0 ] . ']]';
} else {
// Case-insensitive match: build piped link.
return '[[' . $this->targetTitleText . '|' . $matches[ 0 ] . ']]';
}
} else { } else {
// If $wgCapitalLinks is false, we can use the simple variant $needPipe = strcmp( $this->linkValue, $matches[ 0 ] ) != 0;
// of the callback function. }
if ( strcmp( $this->targetTitleText, $matches[ 0 ] ) == 0 ) { if ( $needPipe ) {
// Case-sensitive match: no need to build piped link. return '[[' . $this->linkValue . '|' . $matches[ 0 ] . ']]';
return '[[' . $matches[ 0 ] . ']]'; } else {
} else { return '[[' . $matches[ 0 ] . ']]';
// Case-insensitive match: build piped link.
return '[[' . $this->targetTitleText . '|' . $matches[ 0 ] . ']]';
}
} }
} }
} }

View File

@ -62,6 +62,8 @@ class Target {
private $caseSensitiveLinkValueRegex; private $caseSensitiveLinkValueRegex;
private $nsText;
/** /**
* Constructs a new Target object * Constructs a new Target object
* *
@ -71,6 +73,7 @@ class Target {
* @param String &$title Title of the target page * @param String &$title Title of the target page
*/ */
public function __construct( $nameSpace, $title, Config &$config ) { public function __construct( $nameSpace, $title, Config &$config ) {
// print "\n>>>nameSpace=$nameSpace;title=$title<<<\n";
$this->title = \Title::makeTitleSafe( $nameSpace, $title ); $this->title = \Title::makeTitleSafe( $nameSpace, $title );
$this->titleValue = $this->title->getTitleValue(); $this->titleValue = $this->title->getTitleValue();
$this->config = $config; $this->config = $config;
@ -91,6 +94,32 @@ class Target {
return $this->title->getText(); return $this->title->getText();
} }
public function getPrefixedTitleText() {
return $this->getNsPrefix() . $this->getTitleText();
}
/**
* Gets the string representation of the target's namespace.
*
* May be false if the namespace is NS_MAIN. The value is cached.
* @return String|bool Target's namespace
*/
public function getNsText() {
if ( $this->nsText === null ) {
$this->nsText = $this->title->getNsText();
}
return $this->nsText;
}
/**
* Gets the namespace prefix. This is the namespace text followed by a colon,
* or an empty string if the namespace text evaluates to false (e.g. NS_MAIN).
* @return String namespace prefix
*/
public function getNsPrefix() {
return $this->getNsText() ? $this->getNsText() . ':' : '';
}
/** /**
* Gets the title string with certain characters escaped that may interfere * Gets the title string with certain characters escaped that may interfere
* with regular expressions. * with regular expressions.

View File

@ -111,8 +111,8 @@ class LinkTitlesLinkerTest extends LinkTitles\TestCase {
[ [
true, // wgCapitalLinks true, // wgCapitalLinks
true, // smartMode true, // smartMode
'With smart mode on and $wgCapitalLinks = true, this page should link to Link Target', 'With smart mode on and $wgCapitalLinks = true, this page should link to Link target',
'With smart mode on and $wgCapitalLinks = true, this page should link to [[Link target|Link Target]]' 'With smart mode on and $wgCapitalLinks = true, this page should link to [[Link target]]'
], ],
[ [
true, // wgCapitalLinks true, // wgCapitalLinks
@ -203,34 +203,43 @@ class LinkTitlesLinkerTest extends LinkTitles\TestCase {
$this->assertSame( $text, $linker->linkContent( $this->title, $text ) ); $this->assertSame( $text, $linker->linkContent( $this->title, $text ) );
} }
/** // Tests for namespace handling are commented out until I find a way to add
* @dataProvider provideLinkContentNameSpacesData // a custom namespace during testing. (The assertTrue assertion below fails.)
*/
public function testLinkContentNameSpaces( $nameSpaces, $input, $expectedOutput ) {
$ns = 3000;
$this->setMwGlobals( [
"wgExtraNameSpaces[$ns]" => 'custom_namespace'
] );
$this->insertPage( 'in custom namespace', 'This is a page in a custom namespace', $ns );
$config = new LinkTitles\Config();
$config->nameSpaces = $nameSpaces;
$linker = new LinkTitles\Linker( $config );
$this->assertSame( $expectedOutput, $linker->linkContent( $this->title, $input ));
}
public function provideLinkContentNameSpacesData() { // /**
return [ // * @dataProvider provideLinkContentNameSpacesData
[ // */
[], // nameSpaces // public function testLinkContentNameSpaces( $nameSpaces, $input, $expectedOutput ) {
'With nameSpaces = [], page in custom namespace should not be linked', // $ns = 4000;
'With nameSpaces = [], page in custom namespace should not be linked' // $this->setMwGlobals( [
], // "wgExtraNamespaces[$ns]" => 'custom_namespace'
[ // ] );
[ 3000 ], // nameSpaces // // global $wgExtraNamespaces;
'With nameSpaces = 3000, page in custom namespace should be linked', // // global $wgContentNamespaces;
'With nameSpaces = 3000, page [[custom_namespace:in custom namespace]] should be linked' // // $wgContentNamespaces[] = $ns;
], // // $wgExtraNamespaces[$ns] = 'custom_adsf';
]; // $this->insertPage( 'in custom namespace', 'This is a page in a custom namespace', $ns );
} // $this->assertTrue( MWNamespace::exists( $ns ), "The name space with id $ns should exist!" );
// LinKTitles\Targets::invalidate();
// $config = new LinkTitles\Config();
// $config->nameSpaces = $nameSpaces;
// $linker = new LinkTitles\Linker( $config );
// $this->assertSame( $expectedOutput, $linker->linkContent( $this->title, $input ));
// }
// public function provideLinkContentNameSpacesData() {
// return [
// [
// [], // nameSpaces
// 'With nameSpaces = [], page in custom namespace should not be linked',
// 'With nameSpaces = [], page in custom namespace should not be linked'
// ],
// [
// [ 4000 ], // nameSpaces
// 'With nameSpaces = [ 4000 ], page in custom namespace should be linked',
// 'With nameSpaces = [ 4000 ], page [[custom_namespace:in custom namespace]] should be linked'
// ],
// ];
// }
} }