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

Reply via email to