Parser: Refactor parsing of [[File:...|link=...]] syntax for reusability

Change-Id: I91467297de4b7c532448a4c20b9a0dc8216c7200
This commit is contained in:
Bartosz Dziewoński 2018-05-19 15:29:52 +02:00
parent 66351c7f1b
commit 1c9664d18a
2 changed files with 131 additions and 32 deletions

View file

@ -5143,24 +5143,18 @@ class Parser {
break;
case 'gallery-internal-link':
$linkValue = strip_tags( $this->replaceLinkHoldersText( $match ) );
$chars = self::EXT_LINK_URL_CLASS;
$addr = self::EXT_LINK_ADDR;
$prots = $this->mUrlProtocols;
// check to see if link matches an absolute url, if not then it must be a wiki link.
if ( preg_match( '/^-{R|(.*)}-$/', $linkValue ) ) {
// Result of LanguageConverter::markNoConversion
// invoked on an external link.
$linkValue = substr( $linkValue, 4, -2 );
}
if ( preg_match( "/^($prots)$addr$chars*$/u", $linkValue ) ) {
$link = $linkValue;
$this->mOutput->addExternalLink( $link );
} else {
$localLinkTitle = Title::newFromText( $linkValue );
if ( $localLinkTitle !== null ) {
$this->mOutput->addLink( $localLinkTitle );
$link = $localLinkTitle->getLinkURL();
}
list( $type, $target ) = $this->parseLinkParameter( $linkValue );
if ( $type === 'link-url' ) {
$link = $target;
$this->mOutput->addExternalLink( $target );
} elseif ( $type === 'link-title' ) {
$link = $target->getLinkURL();
$this->mOutput->addLink( $target );
}
break;
default:
@ -5342,29 +5336,16 @@ class Parser {
$value = $this->stripAltText( $value, $holders );
break;
case 'link':
$chars = self::EXT_LINK_URL_CLASS;
$addr = self::EXT_LINK_ADDR;
$prots = $this->mUrlProtocols;
if ( $value === '' ) {
$paramName = 'no-link';
$value = true;
list( $paramName, $value ) = $this->parseLinkParameter( $value );
if ( $paramName ) {
$validated = true;
} elseif ( preg_match( "/^((?i)$prots)/", $value ) ) {
if ( preg_match( "/^((?i)$prots)$addr$chars*$/u", $value, $m ) ) {
$paramName = 'link-url';
$this->mOutput->addExternalLink( $value );
if ( $paramName === 'no-link' ) {
$value = true;
}
if ( $paramName === 'link-url' ) {
if ( $this->mOptions->getExternalLinkTarget() ) {
$params[$type]['link-target'] = $this->mOptions->getExternalLinkTarget();
}
$validated = true;
}
} else {
$linkTitle = Title::newFromText( $value );
if ( $linkTitle ) {
$paramName = 'link-title';
$value = $linkTitle;
$this->mOutput->addLink( $linkTitle );
$validated = true;
}
}
break;
@ -5459,6 +5440,48 @@ class Parser {
return $ret;
}
/**
* Parse the value of 'link' parameter in image syntax (`[[File:Foo.jpg|link=<value>]]`).
*
* Adds an entry to appropriate link tables.
*
* @since 1.32
* @return array of `[ type, target ]`, where:
* - `type` is one of:
* - `null`: Given value is not a valid link target, use default
* - `'no-link'`: Given value is empty, do not generate a link
* - `'link-url'`: Given value is a valid external link
* - `'link-title'`: Given value is a valid internal link
* - `target` is:
* - When `type` is `null` or `'no-link'`: `false`
* - When `type` is `'link-url'`: URL string corresponding to given value
* - When `type` is `'link-title'`: Title object corresponding to given value
*/
public function parseLinkParameter( $value ) {
$chars = self::EXT_LINK_URL_CLASS;
$addr = self::EXT_LINK_ADDR;
$prots = $this->mUrlProtocols;
$type = null;
$target = false;
if ( $value === '' ) {
$type = 'no-link';
} elseif ( preg_match( "/^((?i)$prots)/", $value ) ) {
if ( preg_match( "/^((?i)$prots)$addr$chars*$/u", $value, $m ) ) {
$this->mOutput->addExternalLink( $value );
$type = 'link-url';
$target = $value;
}
} else {
$linkTitle = Title::newFromText( $value );
if ( $linkTitle ) {
$this->mOutput->addLink( $linkTitle );
$type = 'link-title';
$target = $linkTitle;
}
}
return [ $type, $target ];
}
/**
* @param string $caption
* @param LinkHolderArray|bool $holders

View file

@ -215,4 +215,80 @@ class ExtraParserTest extends MediaWikiTestCase {
$result = $parserOutput->getCategoryLinks();
$this->assertEmpty( $result );
}
/**
* @covers Parser::parseLinkParameter
* @dataProvider provideParseLinkParameter
*/
public function testParseLinkParameter( $input, $expected, $expectedLinks, $desc ) {
$this->parser->startExternalParse( Title::newFromText( __FUNCTION__ ),
$this->options, Parser::OT_HTML );
$output = $this->parser->parseLinkParameter( $input );
$this->assertEquals( $expected[0], $output[0], "$desc (type)" );
if ( $expected[0] === 'link-title' ) {
$this->assertTrue( $expected[1]->equals( $output[1] ), "$desc (target)" );
} else {
$this->assertEquals( $expected[1], $output[1], "$desc (target)" );
}
foreach ( $expectedLinks as $func => $expected ) {
$output = $this->parser->getOutput()->$func();
$this->assertEquals( $expected, $output, "$desc ($func)" );
}
}
public static function provideParseLinkParameter() {
return [
[
'',
[ 'no-link', false ],
[],
'Return no link when requested',
],
[
'https://example.com/',
[ 'link-url', 'https://example.com/' ],
[ 'getExternalLinks' => [ 'https://example.com/' => 1 ] ],
'External link',
],
[
'//example.com/',
[ 'link-url', '//example.com/' ],
[ 'getExternalLinks' => [ '//example.com/' => 1 ] ],
'External link',
],
[
'Test',
[ 'link-title', Title::newFromText( 'Test' ) ],
[ 'getLinks' => [ 0 => [ 'Test' => 0 ] ] ],
'Internal link',
],
[
'mw:Test',
[ 'link-title', Title::newFromText( 'mw:Test' ) ],
[ 'getInterwikiLinks' => [ 'mw' => [ 'Test' => 1 ] ] ],
'Internal link (interwiki)',
],
[
'https://',
[ null, false ],
[],
'Invalid link target',
],
[
'<>',
[ null, false ],
[],
'Invalid link target',
],
[
' ',
[ null, false ],
[],
'Invalid link target',
],
];
}
}