Aude has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/176804

Change subject: Use TermLookup in LinkBegin hook
......................................................................

Use TermLookup in LinkBegin hook

(aude) minimally changes the tests here and then
make test refactoring a follow up patch.

Bug: T72767
Change-Id: I8b696c91f8f5c0ddef3df29a82a1f97206b2eb91
---
M repo/includes/Hook/LinkBeginHookHandler.php
M repo/includes/WikibaseRepo.php
M repo/includes/content/EntityContentFactory.php
A repo/includes/store/PageEntityIdLookup.php
M repo/tests/phpunit/includes/Hook/LinkBeginHookHandlerTest.php
M repo/tests/phpunit/includes/content/EntityContentFactoryTest.php
6 files changed, 221 insertions(+), 116 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase 
refs/changes/04/176804/1

diff --git a/repo/includes/Hook/LinkBeginHookHandler.php 
b/repo/includes/Hook/LinkBeginHookHandler.php
index 39160a5..e883743 100644
--- a/repo/includes/Hook/LinkBeginHookHandler.php
+++ b/repo/includes/Hook/LinkBeginHookHandler.php
@@ -4,16 +4,14 @@
 
 use DummyLinker;
 use Html;
-use IContextSource;
 use Language;
-use MWContentSerializationException;
+use OutputPage;
 use RequestContext;
 use Title;
-use Wikibase\EntityContent;
-use Wikibase\LanguageFallbackChainFactory;
-use Wikibase\Repo\Content\EntityContentFactory;
+use Wikibase\LanguageFallbackChain;
+use Wikibase\Lib\Store\TermLookup;
+use Wikibase\Repo\Store\PageEntityIdLookup;
 use Wikibase\Repo\WikibaseRepo;
-use WikiPage;
 
 /**
  * @since 0.5
@@ -23,29 +21,38 @@
 class LinkBeginHookHandler {
 
        /**
-        * @var EntityContentFactory
+        * @var PageEntityIdLookup
         */
-       private $entityContentFactory;
+       private $entityIdLookup;
 
        /**
-        * @var LanguageFallbackChainFactory
+        * @var TermLookup
         */
-       private $languageFallbackChainFactory;
+       private $termLookup;
 
        /**
-        * @var IContextSource
+        * @var LanguageFallbackChain
         */
-       private $context;
+       private $languageFallback;
+
+       /**
+        * @var Language
+        */
+       private $pageLanguage;
 
        /**
         * @return LinkBeginHookHandler
         */
-       public static function newFromGlobalState() {
-               $entityContentFactory = 
WikibaseRepo::getDefaultInstance()->getEntityContentFactory();
-               $languageFallbackChainFactory = 
WikibaseRepo::getDefaultInstance()->getLanguageFallbackChainFactory();
+       private static function newFromGlobalState() {
                $context = RequestContext::getMain();
 
-               return new self( $entityContentFactory, 
$languageFallbackChainFactory, $context );
+               $entityIdLookup = 
WikibaseRepo::getDefaultInstance()->getPageEntityIdLookup();
+               $termLookup = 
WikibaseRepo::getDefaultInstance()->getTermLookup();
+
+               $languageFallbackChainFactory = 
WikibaseRepo::getDefaultInstance()->getLanguageFallbackChainFactory();
+               $languageFallbackChain = 
$languageFallbackChainFactory->newFromContext( $context );
+
+               return new self( $entityIdLookup, $termLookup, 
$languageFallbackChain, $context->getLanguage() );
        }
 
        /**
@@ -66,120 +73,126 @@
                &$options, &$ret
        ) {
                $handler = self::newFromGlobalState();
-               return $handler->doOnLinkBegin( $target, $html, $customAttribs 
);
+               $context = RequestContext::getMain();
+
+               $handler->doOnLinkBegin( $target, $html, $customAttribs, 
$context->getOutput() );
+
+               return true;
        }
 
        /**
-        * @param EntityContentFactory $entityContentFactory
-        * @param LanguageFallbackChainFactory $languageFallbackChainFactory
-        * @param IContextSource $context
+        * @param PageEntityIdLookup $entityIdLookup
+        * @param TermLookup $termLookup
+        * @param LanguageFallbackChain $languageFallback
+        * @param Language $pageLanguage
+        *
+        * @todo: Would be nicer to take a LabelLookup instead of TermLookup + 
FallbackChain.
+        *        But LabelLookup does not support descriptions at the moment.
         */
        public function __construct(
-               EntityContentFactory $entityContentFactory,
-               LanguageFallbackChainFactory $languageFallbackChainFactory,
-               IContextSource $context
+               PageEntityIdLookup $entityIdLookup,
+               TermLookup $termLookup,
+               LanguageFallbackChain $languageFallback,
+               Language $pageLanguage
        ) {
-               $this->entityContentFactory = $entityContentFactory;
-               $this->languageFallbackChainFactory = 
$languageFallbackChainFactory;
-               $this->context = $context;
+               $this->entityIdLookup = $entityIdLookup;
+               $this->termLookup = $termLookup;
+               $this->languageFallback = $languageFallback;
+               $this->pageLanguage = $pageLanguage;
        }
 
        /**
         * @param Title $target
         * @param string &$html
         * @param array &$customAttribs
-        *
-        * @return boolean
+        * @param OutputPage $out
         */
-       public function doOnLinkBegin( Title $target, &$html, array 
&$customAttribs ) {
+       public function doOnLinkBegin( Title $target, &$html, array 
&$customAttribs, OutputPage $out ) {
                wfProfileIn( __METHOD__ );
 
-               if ( !$this->onSpecialPage() || !$this->isEntityContent( 
$target ) ) {
+               $currentTitle = $out->getTitle();
+
+               if ( $currentTitle === null || !$currentTitle->isSpecialPage() 
) {
+                       // Note: this may not work right with special page 
transclusion. If $out->getTitle()
+                       // doesn't return the transcluded special page's title, 
the transcluded text will
+                       // not have entity IDs resolved to labels.
                        wfProfileOut( __METHOD__ );
-                       return true;
+                       return;
                }
 
                // if custom link text is given, there is no point in 
overwriting it
                // but not if it is similar to the plain title
                if ( $html !== null && $target->getFullText() !== $html ) {
                        wfProfileOut( __METHOD__ );
-                       return true;
+                       return;
                }
 
-               $entity = $this->getEntityForTitle( $target );
+               $entityId = $this->entityIdLookup->getPageEntityId( $target );
 
-               if ( !$entity ) {
+               if ( !$entityId ) {
                        wfProfileOut( __METHOD__ );
-                       return true;
+                       return;
                }
 
-               // Try to find the most preferred available language to display 
data in current context.
-               $languageFallbackChain = 
$this->languageFallbackChainFactory->newFromContext( $this->context );
+               //@todo: only fetch the labels we need for the fallback chain
+               $labels = $this->termLookup->getLabels( $entityId );
+               $descriptions = $this->termLookup->getDescriptions( $entityId );
 
-               $labelData = 
$languageFallbackChain->extractPreferredValueOrAny( $entity->getLabels() );
+               $labelData = $this->getPreferredTerm( $labels );
+               $descriptionData = $this->getPreferredTerm( $descriptions );
 
-               if ( $labelData ) {
-                       $labelText = $labelData['value'];
-                       $labelLang = Language::factory( $labelData['language'] 
);
-               } else {
-                       $labelText = '';
-                       $labelLang = $this->context->getLanguage();
-               }
-
-               $descriptionData = 
$languageFallbackChain->extractPreferredValueOrAny(
-                       $entity->getDescriptions()
-               );
-
-               $html = $this->getHtml( $target, $labelLang, $labelText );
+               $html = $this->getHtml( $target, $labelData );
 
                $customAttribs['title'] = $this->getTitleAttribute(
                        $target,
-                       $labelLang,
-                       $labelText,
+                       $labelData,
                        $descriptionData
                );
 
                // add wikibase styles in all cases, so we can format the link 
properly:
-               $this->context->getOutput()->addModuleStyles( array( 
'wikibase.common' ) );
+               $out->addModuleStyles( array( 'wikibase.common' ) );
 
                wfProfileOut( __METHOD__ );
-               return true;
        }
 
-       private function onSpecialPage() {
-               // Title is temporarily set to special pages Title in case of 
special page inclusion!
-               // Therefore we can just check whether the page is a special 
page and
-               // if not, disable the behavior.
-               $currentTitle = $this->context->getTitle();
+       private function getPreferredTerm( $termsByLanguage ) {
+               if ( empty( $termsByLanguage ) ) {
+                       return null;
+               }
 
-               return $currentTitle !== null && $currentTitle->isSpecialPage();
+               return $this->languageFallback->extractPreferredValueOrAny(
+                       $termsByLanguage
+               );
        }
 
-       private function isEntityContent( Title $title ) {
-               return $this->entityContentFactory->isEntityContentModel( 
$title->getContentModel() );
-       }
-
-       private function getEntityForTitle( Title $title ) {
-               $page = new WikiPage( $title );
-
-               try {
-                       $content = $page->getContent();
-
-                       // Failed, can't continue. This could happen because 
the content is empty
-                       // (page doesn't exist), e.g. after item was deleted.
-
-                       // TODO: resolve redirect, show redirect info in link
-                       if ( $content instanceof EntityContent && 
!$content->isRedirect() ) {
-                               return $content->getEntity();
-                       }
-               } catch ( MWContentSerializationException $ex ) {
-                       // if this fails, it's not horrible.
-                       wfWarn( 'Failed to get entity object for [[' . 
$title->getFullText() . ']]'
-                                       . ': ' . $ex->getMessage() );
+       /**
+        * @param array $termData A term record as returned by
+        * LanguageFallbackChain::extractPreferredValueOrAny(),
+        * containing the 'value' and 'language' fields, or null
+        * or an empty array.
+        *
+        * @see LanguageFallbackChain::extractPreferredValueOrAny
+        *
+        * @return array list( string $text, Language $language )
+        */
+       private function extractTextAndLanguage( $termData ) {
+               if ( $termData ) {
+                       return array(
+                               $termData['value'],
+                               Language::factory( $termData['language'] )
+                       );
+               } else {
+                       return array(
+                               '',
+                               $this->pageLanguage
+                       );
                }
        }
 
-       private function getHtml( Title $title, $labelLang, $labelText ) {
+       private function getHtml( Title $title, $labelData ) {
+               /** @var Language $labelLang */
+               list( $labelText, $labelLang ) = $this->extractTextAndLanguage( 
$labelData );
+
                $idHtml = Html::openElement( 'span', array( 'class' => 
'wb-itemlink-id' ) )
                        . wfMessage(
                                'wikibase-itemlink-id-wrapper',
@@ -203,26 +216,27 @@
                        . Html::closeElement( 'span' );
        }
 
-       private function getTitleAttribute( Title $title, $labelLang, 
$labelText, $descriptionData ) {
-               if ( $descriptionData ) {
-                       $descriptionText = $descriptionData['value'];
-                       $descriptionLang = Language::factory( 
$descriptionData['language'] );
-               } else {
-                       $descriptionText = '';
-                       $descriptionLang = $this->context->getLanguage();
-               }
+       private function getTitleAttribute( Title $title, $labelData, 
$descriptionData ) {
+               /** @var Language $labelLang */
+               /** @var Language $descriptionLang */
+
+               list( $labelText, $labelLang ) = $this->extractTextAndLanguage( 
$labelData );
+               list( $descriptionText, $descriptionLang ) = 
$this->extractTextAndLanguage( $descriptionData );
 
                // Set title attribute for constructed link, and make tricks 
with the directionality to get it right
                $titleText = ( $labelText !== '' )
-                       ? $labelLang->getDirMark() . $labelText . 
$this->context->getLanguage()->getDirMark()
+                       ? $labelLang->getDirMark() . $labelText
+                               . $this->pageLanguage->getDirMark()
                        : $title->getPrefixedText();
+
+               $descriptionText = $descriptionLang->getDirMark() . 
$descriptionText
+                       . $this->pageLanguage->getDirMark();
 
                return ( $descriptionText !== '' ) ?
                        wfMessage(
                                'wikibase-itemlink-title',
                                $titleText,
-                               $descriptionLang->getDirMark() . 
$descriptionText
-                                       . 
$this->context->getLanguage()->getDirMark()
+                               $descriptionText
                        )->inContentLanguage()->text() :
                        $titleText; // no description, just display the title 
then
        }
diff --git a/repo/includes/WikibaseRepo.php b/repo/includes/WikibaseRepo.php
index 2b89aa0..7ee6e9b 100644
--- a/repo/includes/WikibaseRepo.php
+++ b/repo/includes/WikibaseRepo.php
@@ -67,6 +67,7 @@
 use Wikibase\Repo\Notifications\DatabaseChangeTransmitter;
 use Wikibase\Repo\Notifications\DummyChangeTransmitter;
 use Wikibase\Repo\Store\EntityPermissionChecker;
+use Wikibase\Repo\Store\PageEntityIdLookup;
 use Wikibase\Repo\View\EntityViewFactory;
 use Wikibase\Settings;
 use Wikibase\SettingsArray;
@@ -279,6 +280,15 @@
        /**
         * @since 0.5
         *
+        * @return PageEntityIdLookup
+        */
+       public function getPageEntityIdLookup() {
+               return $this->getEntityContentFactory();
+       }
+
+       /**
+        * @since 0.5
+        *
         * @param string $uncached Flag string, set to 'uncached' to get an 
uncached direct lookup service.
         *
         * @return \Wikibase\Lib\Store\EntityRevisionLookup
diff --git a/repo/includes/content/EntityContentFactory.php 
b/repo/includes/content/EntityContentFactory.php
index 1bc4454..466449f 100644
--- a/repo/includes/content/EntityContentFactory.php
+++ b/repo/includes/content/EntityContentFactory.php
@@ -8,6 +8,7 @@
 use Revision;
 use Status;
 use Title;
+use TitleValue;
 use User;
 use Wikibase\DataModel\Entity\Entity;
 use Wikibase\DataModel\Entity\EntityId;
@@ -15,6 +16,7 @@
 use Wikibase\Lib\Store\EntityRedirect;
 use Wikibase\Lib\Store\EntityTitleLookup;
 use Wikibase\Repo\Store\EntityPermissionChecker;
+use Wikibase\Repo\Store\PageEntityIdLookup;
 
 /**
  * Factory for EntityContent objects.
@@ -25,7 +27,7 @@
  * @author Jeroen De Dauw < [email protected] >
  * @author Daniel Kinzler
  */
-class EntityContentFactory implements EntityTitleLookup, 
EntityPermissionChecker {
+class EntityContentFactory implements EntityTitleLookup, PageEntityIdLookup, 
EntityPermissionChecker {
 
        /**
         * @since 0.5
@@ -88,6 +90,27 @@
        }
 
        /**
+        * Returns the ID of the entity associated with the given page title.
+        *
+        * @note There is no guarantee that the EntityId returned by this 
method refers to
+        * an existing entity.
+        *
+        * @param Title $title
+        *
+        * @return EntityId|null
+        */
+       public function getPageEntityId( Title $title ) {
+               $contentModel = $title->getContentModel();
+               $handler = ContentHandler::getForModelID( $contentModel );
+
+               if ( $handler instanceof EntityHandler ) {
+                       return $handler->getIdForTitle( $title );
+               } else {
+                       return null;
+               }
+       }
+
+       /**
         * Determines what namespace is suitable for the given type of entities.
         *
         * @since 0.5
diff --git a/repo/includes/store/PageEntityIdLookup.php 
b/repo/includes/store/PageEntityIdLookup.php
new file mode 100644
index 0000000..aeca200
--- /dev/null
+++ b/repo/includes/store/PageEntityIdLookup.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Wikibase\Repo\Store;
+
+use Title;
+use Wikibase\DataModel\Entity\EntityId;
+
+/**
+ * Service interface for a lookup that provides access to the ID of the entity
+ * stored on a given page.
+ *
+ * The mapping may be implemented programmatically (e.g. by parsing the title 
as an EntityId),
+ * or it may be based on an explicit mapping (e.g. in a database table).
+ *
+ * @since 0.5
+ *
+ * @licence GNU GPL v2+
+ * @author Daniel Kinzler
+ */
+interface PageEntityIdLookup {
+
+       /**
+        * Returns the ID of the entity stored on the page identified by $title.
+        *
+        * @note There is no guarantee that the EntityId returned by this 
method refers to
+        * an existing entity.
+        *
+        * @param Title $title
+        *
+        * @todo: Switch this to using TitleValue once we can easily get the 
content model and
+        * handler based on a TitleValue.
+        *
+        * @return EntityId|null
+        */
+       public function getPageEntityId( Title $title );
+
+}
diff --git a/repo/tests/phpunit/includes/Hook/LinkBeginHookHandlerTest.php 
b/repo/tests/phpunit/includes/Hook/LinkBeginHookHandlerTest.php
index 0d48d8b..2df87b1 100644
--- a/repo/tests/phpunit/includes/Hook/LinkBeginHookHandlerTest.php
+++ b/repo/tests/phpunit/includes/Hook/LinkBeginHookHandlerTest.php
@@ -36,9 +36,9 @@
        private static $noLabelItemId = null;
 
        /**
-        * @var EntityTitleLookup
+        * @var EntityContentFactory
         */
-       private $entityTitleLookup = null;
+       private $entityContentFactory = null;
 
        protected function setUp() {
                parent::setUp();
@@ -46,7 +46,7 @@
                $language = Language::factory( 'en' );
 
                $this->setMwGlobals( array(
-                       'wgLanguageCode' =>  'en',
+                       'wgLanguageCode' => 'en',
                        'wgLang' => $language,
                        'wgContLang' => $language
                ) );
@@ -76,12 +76,12 @@
                $contextTitle = Title::newFromText( 'Special:Recentchanges' );
                $linkBeginHookHandler = $this->getLinkBeginHookHandler( 
$contextTitle );
 
-               $title = $this->getEntityTitleLookup()->getTitleForId( 
self::$itemId );
+               $title = $this->getEntityContentFactory()->getTitleForId( 
self::$itemId );
 
                $html = $title->getFullText();
                $customAttribs = array();
 
-               $linkBeginHookHandler->doOnLinkBegin( $title, $html, 
$customAttribs );
+               $linkBeginHookHandler->doOnLinkBegin( $title, $html, 
$customAttribs, $this->getOutput( $title ) );
 
                $expectedHtml = '<span class="wb-itemlink">'
                        . '<span class="wb-itemlink-label" lang="en" 
dir="ltr">linkbegin-label</span> '
@@ -97,13 +97,13 @@
        public function testDoOnLinkBegin_onNonSpecialPage() {
                $linkBeginHookHandler = $this->getLinkBeginHookHandler( 
Title::newMainPage() );
 
-               $title = $this->getEntityTitleLookup()->getTitleForId( 
self::$itemId );
+               $title = $this->getEntityContentFactory()->getTitleForId( 
self::$itemId );
 
                $titleText = $title->getFullText();
                $html = $titleText;
                $customAttribs = array();
 
-               $linkBeginHookHandler->doOnLinkBegin( $title, $html, 
$customAttribs );
+               $linkBeginHookHandler->doOnLinkBegin( $title, $html, 
$customAttribs, $this->getOutput( $title ) );
 
                $this->assertEquals( $titleText, $html );
                $this->assertEquals( array(), $customAttribs );
@@ -119,7 +119,7 @@
                $html = $titleText;
                $customAttribs = array();
 
-               $linkBeginHookHandler->doOnLinkBegin( $title, $html, 
$customAttribs );
+               $linkBeginHookHandler->doOnLinkBegin( $title, $html, 
$customAttribs, $this->getOutput( $title ) );
 
                $this->assertEquals( $titleText, $html );
                $this->assertEquals( array(), $customAttribs );
@@ -130,13 +130,13 @@
                $linkBeginHookHandler = $this->getLinkBeginHookHandler( 
$contextTitle );
 
                $itemId = ItemId::newFromNumber( mt_rand( 0, 9999999999 ) );
-               $title = $this->getEntityTitleLookup()->getTitleForId( $itemId 
);
+               $title = $this->getEntityContentFactory()->getTitleForId( 
$itemId );
 
                $titleText = $title->getFullText();
                $html = $titleText;
                $customAttribs = array();
 
-               $linkBeginHookHandler->doOnLinkBegin( $title, $html, 
$customAttribs );
+               $linkBeginHookHandler->doOnLinkBegin( $title, $html, 
$customAttribs, $this->getOutput( $title ) );
 
                $this->assertEquals( $titleText, $html );
                $this->assertEquals( array(), $customAttribs );
@@ -146,12 +146,12 @@
                $contextTitle = Title::newFromText( 'Special:Recentchanges' );
                $linkBeginHookHandler = $this->getLinkBeginHookHandler( 
$contextTitle );
 
-               $title = $this->getEntityTitleLookup()->getTitleForId( 
self::$noLabelItemId );
+               $title = $this->getEntityContentFactory()->getTitleForId( 
self::$noLabelItemId );
 
                $html = $title->getFullText();
                $customAttribs = array();
 
-               $linkBeginHookHandler->doOnLinkBegin( $title, $html, 
$customAttribs );
+               $linkBeginHookHandler->doOnLinkBegin( $title, $html, 
$customAttribs, $this->getOutput( $title ) );
 
                $expected = '<span class="wb-itemlink">'
                        . '<span class="wb-itemlink-label" lang="en" 
dir="ltr"></span> '
@@ -163,25 +163,36 @@
                $this->assertContains( 
self::$noLabelItemId->getSerialization(), $customAttribs['title'] );
        }
 
-       private function getEntityTitleLookup() {
-               if ( $this->entityTitleLookup === null ) {
+       private function getEntityContentFactory() {
+               if ( $this->entityContentFactory === null ) {
                        $wikibaseRepo = WikibaseRepo::getDefaultInstance();
-                       $this->entityTitleLookup = 
$wikibaseRepo->getEntityTitleLookup();
+                       $this->entityContentFactory = 
$wikibaseRepo->getEntityContentFactory();
                }
 
-               return $this->entityTitleLookup;
+               return $this->entityContentFactory;
+       }
+
+       private function getOutput( Title $title ) {
+               return $this->getContext( $title )->getOutput();
+       }
+
+       private function getContext( Title $title ) {
+               $context = RequestContext::getMain();
+               $context->setTitle( $title );
+
+               return $context;
        }
 
        private function getLinkBeginHookHandler( Title $title ) {
-               $context = RequestContext::getMain();
-               $context->setTitle( $title );
+               $context = $this->getContext( $title );
 
                $wikibaseRepo = WikibaseRepo::getDefaultInstance();
 
                return new LinkBeginHookHandler(
-                       $this->getEntityTitleLookup(),
-                       $wikibaseRepo->getLanguageFallbackChainFactory(),
-                       $context
+                       $wikibaseRepo->getEntityContentFactory(),
+                       $wikibaseRepo->getTermLookup(),
+                       
$wikibaseRepo->getLanguageFallbackChainFactory()->newFromContext( $context ),
+                       $context->getLanguage()
                );
 
        }
diff --git a/repo/tests/phpunit/includes/content/EntityContentFactoryTest.php 
b/repo/tests/phpunit/includes/content/EntityContentFactoryTest.php
index 460cb3f..3bac18a 100644
--- a/repo/tests/phpunit/includes/content/EntityContentFactoryTest.php
+++ b/repo/tests/phpunit/includes/content/EntityContentFactoryTest.php
@@ -3,6 +3,7 @@
 namespace Wikibase\Test;
 
 use ContentHandler;
+use Title;
 use Wikibase\DataModel\Entity\Entity;
 use Wikibase\DataModel\Entity\Item;
 use Wikibase\DataModel\Entity\ItemId;
@@ -82,6 +83,15 @@
                $this->assertEquals( 'Q42', $title->getText() );
        }
 
+       public function testGetPageEntityId() {
+               $factory = $this->newFactory();
+
+               $title = Title::makeTitle( $factory->getNamespaceForType( 
Item::ENTITY_TYPE ), 'Q42' );
+               $entityId = $factory->getPageEntityId( $title );
+
+               $this->assertEquals( 'Q42', $entityId->getSerialization() );
+       }
+
        public function testGetNamespaceForType() {
                $factory = $this->newFactory();
                $id = new ItemId( 'Q42' );

-- 
To view, visit https://gerrit.wikimedia.org/r/176804
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8b696c91f8f5c0ddef3df29a82a1f97206b2eb91
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Aude <[email protected]>
Gerrit-Reviewer: Daniel Kinzler <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to