Daniel Kinzler has uploaded a new change for review.
https://gerrit.wikimedia.org/r/159078
Change subject: Extract handlers for ParserOutput hooks
......................................................................
Extract handlers for ParserOutput hooks
This factors the handlers for ParserOutput related hooks
out of the general WikibaseCLientHooks class into a separate class
and makes them testable.
Change-Id: I5a76107fb9e02c94d75205738858a57b1175ac56
---
M client/WikibaseClient.hooks.php
M client/WikibaseClient.php
A client/includes/hooks/ParserOutputHooks.php
A client/tests/phpunit/includes/hooks/ParserOutputHooksTest.php
4 files changed, 512 insertions(+), 112 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase
refs/changes/78/159078/1
diff --git a/client/WikibaseClient.hooks.php b/client/WikibaseClient.hooks.php
index cee6c79..1c71230 100644
--- a/client/WikibaseClient.hooks.php
+++ b/client/WikibaseClient.hooks.php
@@ -328,83 +328,6 @@
}
/**
- * Hook runs after internal parsing
- * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserAfterParse
- *
- * @since 0.1
- *
- * @param Parser $parser
- * @param string $text
- * @param StripState $stripState
- *
- * @return bool
- */
- public static function onParserAfterParse( Parser &$parser, &$text,
StripState $stripState ) {
- // this hook tries to access repo SiteLinkTable
- // it interferes with any test that parses something, like a
page or a message
- if ( defined( 'MW_PHPUNIT_TEST' ) ) {
- return true;
- }
-
- if ( !self::isWikibaseEnabled(
$parser->getTitle()->getNamespace() ) ) {
- // shorten out
- return true;
- }
-
- wfProfileIn( __METHOD__ );
-
- // @todo split up the multiple responsibilities here and in
lang link handler
-
- $parserOutput = $parser->getOutput();
-
- // only run this once, for the article content and not
interface stuff
- //FIXME: this also runs for messages in
EditPage::showEditTools! Ugh!
- if ( $parser->getOptions()->getInterfaceMessage() ) {
- wfProfileOut( __METHOD__ );
- return true;
- }
-
- $wikibaseClient = WikibaseClient::getDefaultInstance();
- $settings = $wikibaseClient->getSettings();
-
- $langLinkHandler = new LangLinkHandler(
- $settings->getSetting( 'siteGlobalID' ),
- $wikibaseClient->getNamespaceChecker(),
- $wikibaseClient->getStore()->getSiteLinkTable(),
- $wikibaseClient->getSiteStore(),
- $wikibaseClient->getLangLinkSiteGroup()
- );
-
- $useRepoLinks = $langLinkHandler->useRepoLinks(
$parser->getTitle(), $parser->getOutput() );
-
- try {
- if ( $useRepoLinks ) {
- // add links
- $langLinkHandler->addLinksFromRepository(
$parser->getTitle(), $parser->getOutput() );
- }
-
- $langLinkHandler->updateItemIdProperty(
$parser->getTitle(), $parser->getOutput() );
- } catch ( \Exception $e ) {
- wfWarn( 'Failed to add repo links: ' . $e->getMessage()
);
- }
-
- if ( $useRepoLinks || $settings->getSetting( 'alwaysSort' ) ) {
- // sort links
- $interwikiSorter = new InterwikiSorter(
- $settings->getSetting( 'sort' ),
- $settings->getSetting( 'interwikiSortOrders' ),
- $settings->getSetting( 'sortPrepend' )
- );
- $interwikiLinks = $parserOutput->getLanguageLinks();
- $sortedLinks = $interwikiSorter->sortLinks(
$interwikiLinks );
- $parserOutput->setLanguageLinks( $sortedLinks );
- }
-
- wfProfileOut( __METHOD__ );
- return true;
- }
-
- /**
* Add badges to the language links.
*
* @since 0.5
@@ -517,39 +440,6 @@
$beforePageDisplayHandler->addModules( $out, $skin, $actionName
);
wfProfileOut( __METHOD__ );
-
- return true;
- }
-
- /**
- * Add output page property if repo links are suppressed, and property
for item id
- *
- * @since 0.4
- *
- * @param OutputPage &$out
- * @param ParserOutput $pout
- *
- * @return bool
- */
- public static function onOutputPageParserOutput( OutputPage &$out,
ParserOutput $pout ) {
- if ( !self::isWikibaseEnabled( $out->getTitle()->getNamespace()
) ) {
- // shorten out
- return true;
- }
-
- $langLinkHandler =
WikibaseClient::getDefaultInstance()->getLangLinkHandler();
-
- $noExternalLangLinks =
$langLinkHandler->getNoExternalLangLinks( $pout );
-
- if ( $noExternalLangLinks !== array() ) {
- $out->setProperty( 'noexternallanglinks',
$noExternalLangLinks );
- }
-
- $itemId = $pout->getProperty( 'wikibase_item' );
-
- if ( $itemId !== false ) {
- $out->setProperty( 'wikibase_item', $itemId );
- }
return true;
}
diff --git a/client/WikibaseClient.php b/client/WikibaseClient.php
index 5f775b8..dbb71d5 100644
--- a/client/WikibaseClient.php
+++ b/client/WikibaseClient.php
@@ -91,8 +91,8 @@
$wgHooks['UnitTestsList'][] =
'\Wikibase\ClientHooks::registerUnitTests';
$wgHooks['BaseTemplateToolbox'][] =
'\Wikibase\ClientHooks::onBaseTemplateToolbox';
$wgHooks['OldChangesListRecentChangesLine'][] =
'\Wikibase\ClientHooks::onOldChangesListRecentChangesLine';
- $wgHooks['OutputPageParserOutput'][] =
'\Wikibase\ClientHooks::onOutputPageParserOutput';
- $wgHooks['ParserAfterParse'][] =
'\Wikibase\ClientHooks::onParserAfterParse';
+ $wgHooks['OutputPageParserOutput'][] =
'\Wikibase\Client\Hooks\ParserOutputHooks::onOutputPageParserOutput';
+ $wgHooks['ParserAfterParse'][] =
'\Wikibase\Client\Hooks\ParserOutputHooks::onParserAfterParse';
$wgHooks['ParserFirstCallInit'][] =
'\Wikibase\ClientHooks::onParserFirstCallInit';
$wgHooks['MagicWordwgVariableIDs'][] =
'\Wikibase\ClientHooks::onMagicWordwgVariableIDs';
$wgHooks['ParserGetVariableValueSwitch'][] =
'\Wikibase\ClientHooks::onParserGetVariableValueSwitch';
diff --git a/client/includes/hooks/ParserOutputHooks.php
b/client/includes/hooks/ParserOutputHooks.php
new file mode 100644
index 0000000..0c8dd7e
--- /dev/null
+++ b/client/includes/hooks/ParserOutputHooks.php
@@ -0,0 +1,212 @@
+<?php
+
+namespace Wikibase\Client\Hooks;
+
+use OutputPage;
+use Parser;
+use ParserOutput;
+use StripState;
+use Wikibase\Client\WikibaseClient;
+use Wikibase\InterwikiSorter;
+use Wikibase\LangLinkHandler;
+use Wikibase\NamespaceChecker;
+
+/**
+ * ParserOutput related hook handlers.
+ *
+ * This class has a static interface for use with MediaWiki's hook mechanism;
the static
+ * handler functions will create a new instance of ParserOutputHooks and then
call the
+ * corresponding member function on that.
+ *
+ * @since 0.5.
+ *
+ * @license GPL 2+
+ * @author Katie Filbert < [email protected] >
+ * @author Daniel Kinzler
+ * @author Jeroen De Dauw < [email protected] >
+ * @author Marius Hoch < [email protected] >
+ */
+class ParserOutputHooks {
+
+ /**
+ * @var NamespaceChecker
+ */
+ private $namespaceChecker;
+
+ /**
+ * @var LangLinkHandler
+ */
+ private $langLinkHandler;
+
+ /**
+ * @var InterwikiSorter
+ */
+ private $interwikiSorter;
+
+ /**
+ * @var bool
+ */
+ private $alwaysSort;
+
+ private static function newFromGlobalState() {
+ $wikibaseClient = WikibaseClient::getDefaultInstance();
+ $settings = $wikibaseClient->getSettings();
+
+ $langLinkHandler = new LangLinkHandler(
+ $settings->getSetting( 'siteGlobalID' ),
+ $wikibaseClient->getNamespaceChecker(),
+ $wikibaseClient->getStore()->getSiteLinkTable(),
+ $wikibaseClient->getSiteStore(),
+ $wikibaseClient->getLangLinkSiteGroup()
+ );
+
+ $interwikiSorter = new InterwikiSorter(
+ $settings->getSetting( 'sort' ),
+ $settings->getSetting( 'interwikiSortOrders' ),
+ $settings->getSetting( 'sortPrepend' )
+ );
+
+ return new ParserOutputHooks(
+ $wikibaseClient->getNamespaceChecker(),
+ $langLinkHandler,
+ $interwikiSorter,
+ $settings->getSetting( 'alwaysSort' )
+ );
+ }
+
+ /**
+ * Hook runs after internal parsing
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserAfterParse
+ *
+ * @param Parser $parser
+ * @param string $text
+ * @param StripState $stripState
+ *
+ * @return bool
+ */
+ public static function onParserAfterParse( Parser &$parser, &$text,
StripState $stripState ) {
+ $handler = self::newFromGlobalState();
+ return $handler->doParserAfterParse( $parser, $text,
$stripState );
+ }
+
+ /**
+ * Static handler for the OutputPageParserOutput hook.
+ *
+ * @param OutputPage &$out
+ * @param ParserOutput $pout
+ *
+ * @return bool
+ */
+ public static function onOutputPageParserOutput( OutputPage &$out,
ParserOutput $pout ) {
+ $handler = self::newFromGlobalState();
+ return $handler->doOutputPageParserOutput( $out, $pout );
+ }
+
+ function __construct(
+ NamespaceChecker $namespaceChecker,
+ LangLinkHandler $langLinkHandler,
+ InterwikiSorter $sorter,
+ $alwaysSort
+ ) {
+
+ $this->namespaceChecker = $namespaceChecker;
+ $this->langLinkHandler = $langLinkHandler;
+ $this->interwikiSorter = $sorter;
+ $this->alwaysSort = $alwaysSort;
+ }
+
+
+ /**
+ * @see NamespaceChecker::isWikibaseEnabled
+ *
+ * @param int $namespace
+ *
+ * @return bool
+ */
+ private function isWikibaseEnabled( $namespace ) {
+ return $this->namespaceChecker->isWikibaseEnabled( $namespace );
+ }
+
+ /**
+ * Hook runs after internal parsing
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserAfterParse
+ *
+ * @param Parser $parser
+ * @param string $text
+ * @param StripState $stripState
+ *
+ * @return bool
+ */
+ public function doParserAfterParse( Parser &$parser, &$text, StripState
$stripState ) {
+ if ( !$this->isWikibaseEnabled(
$parser->getTitle()->getNamespace() ) ) {
+ // shorten out
+ return true;
+ }
+
+ wfProfileIn( __METHOD__ );
+
+ // @todo split up the multiple responsibilities here and in
lang link handler
+
+ $parserOutput = $parser->getOutput();
+
+ // only run this once, for the article content and not
interface stuff
+ //FIXME: this also runs for messages in
EditPage::showEditTools! Ugh!
+ if ( $parser->getOptions()->getInterfaceMessage() ) {
+ wfProfileOut( __METHOD__ );
+ return true;
+ }
+
+ $useRepoLinks = $this->langLinkHandler->useRepoLinks(
$parser->getTitle(), $parser->getOutput() );
+
+ try {
+ if ( $useRepoLinks ) {
+ // add links
+ $this->langLinkHandler->addLinksFromRepository(
$parser->getTitle(), $parser->getOutput() );
+ }
+
+ $this->langLinkHandler->updateItemIdProperty(
$parser->getTitle(), $parser->getOutput() );
+ } catch ( \Exception $e ) {
+ wfWarn( 'Failed to add repo links: ' . $e->getMessage()
);
+ }
+
+ if ( $useRepoLinks || $this->alwaysSort ) {
+ $interwikiLinks = $parserOutput->getLanguageLinks();
+ $sortedLinks = $this->interwikiSorter->sortLinks(
$interwikiLinks );
+ $parserOutput->setLanguageLinks( $sortedLinks );
+ }
+
+ wfProfileOut( __METHOD__ );
+ return true;
+ }
+
+ /**
+ * Add output page property if repo links are suppressed, and property
for item id
+ *
+ * @param OutputPage &$out
+ * @param ParserOutput $pout
+ *
+ * @return bool
+ */
+ public function doOutputPageParserOutput( OutputPage &$out,
ParserOutput $pout ) {
+ if ( !$this->isWikibaseEnabled(
$out->getTitle()->getNamespace() ) ) {
+ // shorten out
+ return true;
+ }
+
+ $noExternalLangLinks =
$this->langLinkHandler->getNoExternalLangLinks( $pout );
+
+ if ( $noExternalLangLinks !== array() ) {
+ $out->setProperty( 'noexternallanglinks',
$noExternalLangLinks );
+ }
+
+ $itemId = $pout->getProperty( 'wikibase_item' );
+
+ if ( $itemId !== false ) {
+ $out->setProperty( 'wikibase_item', $itemId );
+ }
+
+ return true;
+ }
+
+}
+
\ No newline at end of file
diff --git a/client/tests/phpunit/includes/hooks/ParserOutputHooksTest.php
b/client/tests/phpunit/includes/hooks/ParserOutputHooksTest.php
new file mode 100644
index 0000000..994f0b5
--- /dev/null
+++ b/client/tests/phpunit/includes/hooks/ParserOutputHooksTest.php
@@ -0,0 +1,298 @@
+<?php
+
+namespace Wikibase\Test;
+
+use FauxRequest;
+use MediaWikiSite;
+use OutputPage;
+use Parser;
+use ParserOptions;
+use ParserOutput;
+use RequestContext;
+use Site;
+use SiteStore;
+use StripState;
+use Title;
+use Wikibase\Client\Hooks\ParserOutputHooks;
+use Wikibase\DataModel\Entity\ItemId;
+use Wikibase\DataModel\SiteLink;
+use Wikibase\InterwikiSorter;
+use Wikibase\LangLinkHandler;
+use Wikibase\Lib\Store\SiteLinkLookup;
+use Wikibase\NamespaceChecker;
+use Wikibase\Settings;
+use Wikibase\SettingsArray;
+
+/**
+ * @covers Wikibase\Client\Hooks\ParserOutputHooks
+ *
+ * @group WikibaseClient
+ * @group Wikibase
+ * @group WikibaseHooks
+ *
+ * @licence GNU GPL v2+
+ * @author Daniel Kinzler
+ */
+class ParserOutputHooksTest extends \MediaWikiTestCase {
+
+ /**
+ * @param string $globalId
+ * @param string $group
+ * @param $language
+ *
+ * @return Site
+ */
+ private function newSite( $globalId, $group, $language ) {
+ $site = new MediaWikiSite();
+ $site->setGlobalId( $globalId );
+ $site->setGroup( $group );
+ $site->setLanguageCode( $language );
+ $site->addNavigationId( $language );
+
+ return $site;
+ }
+
+ /**
+ * @return SiteStore
+ */
+ private function getSiteStore() {
+ $siteStore = new MockSiteStore( array(
+ $this->newSite( 'wikidatawiki', 'wikidata', 'en' ),
+ $this->newSite( 'commonswiki', 'commons', 'en' ),
+ $this->newSite( 'enwiki', 'wikipedia', 'en' ),
+ $this->newSite( 'dewiki', 'wikipedia', 'de' ),
+ ) );
+
+ return $siteStore;
+ }
+
+ /**
+ * @return SiteLinkLookup
+ */
+ private function getSiteLinkLookup() {
+ $lookup = $this->getMock( 'Wikibase\Lib\Store\SiteLinkLookup' );
+
+ $links = array(
+ 'Q1' => array(
+ new SiteLink( 'dewiki', 'Sauerstoff' ),
+ new SiteLink( 'enwiki', 'Oxygen' ),
+ new SiteLink( 'commonswiki', 'Oxygen' ),
+ ),
+ 'Q7' => array(
+ new SiteLink( 'dewiki', 'User:Foo' ),
+ new SiteLink( 'enwiki', 'User:Foo' ),
+ new SiteLink( 'commonswiki', 'User:Foo' ),
+ ),
+ );
+
+ $q1 = new ItemId( 'Q1' );
+
+ $items = array(
+ 'dewiki:Sauerstoff' => $q1,
+ 'enwiki:Oxygen' => $q1,
+ );
+
+ $lookup->expects( $this->any() )
+ ->method( 'getSiteLinksForItem' )
+ ->will( $this->returnCallback( function( ItemId $item )
use ( $links ) {
+ $key = $item->getSerialization();
+ return isset( $links[$key] ) ? $links[$key] :
array();
+ } ) );
+
+ $lookup->expects( $this->any() )
+ ->method( 'getEntityIdForSiteLink' )
+ ->will( $this->returnCallback( function( SiteLink $link
) use ( $items ) {
+ $key = $link->getSiteId() . ':' .
$link->getPageName();
+ return isset( $items[$key] ) ? $items[$key] :
null;
+ } ) );
+
+ return $lookup;
+ }
+
+ /**
+ * @param array $settings
+ *
+ * @return Settings
+ */
+ private function newSettings( array $settings ) {
+ $defaults = array(
+ 'sort' => 'code',
+ 'sortPrepend' => array(),
+ 'interwikiSortOrders' => array( 'alphabetic' => array(
+ 'ar', 'de', 'en', 'sv', 'zh'
+ ) ),
+ 'siteGlobalid' => 'enwiki',
+ 'languageLinkSiteGroup' => 'wikipedia',
+ 'namespaces' => array( NS_MAIN, NS_CATEGORY ),
+ 'alwaysSort' => false,
+ );
+
+ return new SettingsArray( array_merge( $defaults, $settings ) );
+ }
+
+ private function newParserOutputHooks( array $settings = array() ) {
+ $settings = $this->newSettings( $settings );
+
+ $siteId = $settings->getSetting( 'siteGlobalid' );
+ $siteGroup = $settings->getSetting( 'languageLinkSiteGroup' );
+ $namespaces = $settings->getSetting( 'namespaces' );
+
+ $namespaceChecker = new NamespaceChecker( array(), $namespaces
);
+
+ $langLinkHandler = new LangLinkHandler(
+ $siteId,
+ $namespaceChecker,
+ $this->getSiteLinkLookup(),
+ $this->getSiteStore(),
+ $siteGroup
+ );
+
+ $interwikiSorter = new InterwikiSorter(
+ $settings->getSetting( 'sort' ),
+ $settings->getSetting( 'interwikiSortOrders' ),
+ $settings->getSetting( 'sortPrepend' )
+ );
+
+ return new ParserOutputHooks(
+ $namespaceChecker,
+ $langLinkHandler,
+ $interwikiSorter,
+ $settings->getSetting( 'alwaysSort' )
+ );
+
+ }
+
+ private function newParser( Title $title, array $pageProps, array
$extensionData ) {
+ $popt = new ParserOptions();
+ $parser = new Parser();
+
+ $parser->startExternalParse( $title, $popt, Parser::OT_HTML );
+
+ $pout = $parser->getOutput();
+ $this->primeParserOutput( $pout, $pageProps, $extensionData );
+
+ return $parser;
+ }
+
+ private function primeParserOutput( ParserOutput $pout, array
$pageProps, array $extensionData ) {
+ foreach ( $pageProps as $name => $value ) {
+ $pout->setProperty( $name, $value );
+ }
+
+ foreach ( $extensionData as $key => $value ) {
+ $pout->setExtensionData( $key, $value );
+ }
+ }
+
+ public function parserAfterParseProvider() {
+ return array(
+ 'repo-links' => array(
+ Title::makeTitle( NS_MAIN, 'Oxygen' ),
+ 'Q1',
+ array(),
+ array( 'de:Sauerstoff' ),
+ ),
+
+ 'noexternallanglinks=*' => array(
+ Title::makeTitle( NS_MAIN, 'Oxygen' ),
+ 'Q1',
+ array( 'noexternallanglinks' => serialize(
array( '*' ) ) ),
+ array(),
+ ),
+
+ 'noexternallanglinks=de' => array(
+ Title::makeTitle( NS_MAIN, 'Oxygen' ),
+ 'Q1',
+ array( 'noexternallanglinks' => serialize(
array( 'de' ) ) ),
+ array(),
+ ),
+
+ 'noexternallanglinks=ja' => array(
+ Title::makeTitle( NS_MAIN, 'Oxygen' ),
+ 'Q1',
+ array( 'noexternallanglinks' => serialize(
array( 'ja' ) ) ),
+ array( 'de:Sauerstoff' ),
+ ),
+
+ 'no-item' => array(
+ Title::makeTitle( NS_MAIN, 'Plutonium' ),
+ null,
+ array(),
+ array(),
+ ),
+
+ 'ignored-namespace' => array(
+ Title::makeTitle( NS_USER, 'Foo' ),
+ null,
+ array(),
+ array(),
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider parserAfterParseProvider
+ */
+ public function testDoParserAfterParse(
+ Title $title,
+ $expectedItem,
+ array $pagePropsBefore,
+ array $expectedLanguageLinks
+ ) {
+ $parser = $this->newParser( $title, $pagePropsBefore, array() );
+ $handler = $this->newParserOutputHooks();
+
+ $text = '';
+ $stripState = new StripState( 'x' );
+
+ $handler->doParserAfterParse( $parser, $text, $stripState );
+
+ $this->assertEquals( $expectedItem,
$parser->getOutput()->getProperty( 'wikibase_item' ) );
+ $this->assertLanguageLinks( $expectedLanguageLinks,
$parser->getOutput() );
+ }
+
+ public function testDoOutputPageParserOutput() {
+ $title = Title::makeTitle( NS_MAIN, 'Oxygen' );
+
+ $pageProps = array(
+ 'noexternallanglinks' => serialize( array( '*' ) ),
+ 'wikibase_item' => 'Q1',
+ );
+
+ $outputProps = array(
+ 'noexternallanglinks' => array( '*' ),
+ 'wikibase_item' => 'Q1',
+ );
+
+ $handler = $this->newParserOutputHooks();
+
+ $pout = new ParserOutput();
+
+ $context = new RequestContext( new FauxRequest() );
+ $outp = new OutputPage( $context );
+ $outp->setTitle( $title );
+
+ $this->primeParserOutput( $pout, $pageProps, array() );
+
+ $handler->doOutputPageParserOutput( $outp, $pout );
+
+ $this->assertOutputPageProperties( $outputProps, $outp );
+ }
+
+ private function assertOutputPageProperties( array $props, OutputPage
$outp ) {
+ foreach ( $props as $key => $value ) {
+ $this->assertEquals( $value, $outp->getProperty( $key
), 'OutputProperty: ' . $key );
+ }
+ }
+
+ private function assertLanguageLinks( array $links, ParserOutput $pout
) {
+ $actualLinks = $pout->getLanguageLinks();
+
+ foreach ( $links as $link ) {
+ $this->assertContains( $link, $actualLinks,
'LanguageLink: ' );
+ }
+
+ $this->assertSameSize( $links, $actualLinks, 'Unmatched Links!'
);
+ }
+
+}
--
To view, visit https://gerrit.wikimedia.org/r/159078
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I5a76107fb9e02c94d75205738858a57b1175ac56
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits