EBernhardson has uploaded a new change for review.
https://gerrit.wikimedia.org/r/155805
Change subject: Hygine: Make reference extraction more concrete
......................................................................
Hygine: Make reference extraction more concrete
Change-Id: I8ae467dd2d2a7d3fac8dd33b42d2a7d087a63340
---
M Flow.php
M container.php
M includes/Model/Reference.php
A includes/Parsoid/Extractor/ExtLinkExtractor.php
A includes/Parsoid/Extractor/ImageExtractor.php
A includes/Parsoid/Extractor/PlaceholderExtractor.php
A includes/Parsoid/Extractor/TransclusionExtractor.php
A includes/Parsoid/Extractor/WikiLinkExtractor.php
A includes/Parsoid/ExtractorInterface.php
M includes/Parsoid/ReferenceExtractor.php
A includes/Parsoid/ReferenceFactory.php
M tests/ReferenceExtractorTest.php
12 files changed, 324 insertions(+), 203 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Flow
refs/changes/05/155805/1
diff --git a/Flow.php b/Flow.php
index d69fc9b..5363f70 100755
--- a/Flow.php
+++ b/Flow.php
@@ -76,7 +76,6 @@
$wgAutoloadClasses['Flow\Parsoid\BadImageRemover'] = $dir .
'includes/Parsoid/BadImageRemover.php';
$wgAutoloadClasses['Flow\Anchor'] = $dir . 'includes/Anchor.php';
$wgAutoloadClasses['Flow\BaseUrlGenerator'] = $dir .
'includes/BaseUrlGenerator.php';
-$wgAutoloadClasses['Flow\Parsoid\ReferenceExtractor'] = $dir .
'includes/Parsoid/ReferenceExtractor.php';
$wgAutoloadClasses['Flow\UrlGenerator'] = $dir . 'includes/UrlGenerator.php';
$wgAutoloadClasses['Flow\View'] = $dir . 'includes/View.php';
$wgAutoloadClasses['Flow\BlockFactory'] = $dir . 'includes/BlockFactory.php';
@@ -235,6 +234,14 @@
$wgAutoloadClasses['Flow\Model\WikiReference'] =
"$dir/includes/Model/Reference.php";
$wgAutoloadClasses['Flow\Model\URLReference'] =
"$dir/includes/Model/Reference.php";
$wgAutoloadClasses['Flow\Data\ReferenceRecorder'] =
"$dir/includes/Data/ReferenceRecorder.php";
+$wgAutoloadClasses['Flow\Parsoid\ReferenceExtractor'] = $dir .
'includes/Parsoid/ReferenceExtractor.php';
+$wgAutoloadClasses['Flow\Parsoid\ReferenceFactory'] = $dir .
'includes/Parsoid/ReferenceFactory.php';
+$wgAutoloadClasses['Flow\Parsoid\ExtractorInterface'] = $dir .
'includes/Parsoid/ExtractorInterface.php';
+$wgAutoloadClasses['Flow\Parsoid\Extractor\ExtLinkExtractor'] = $dir .
'includes/Parsoid/Extractor/ExtLinkExtractor.php';
+$wgAutoloadClasses['Flow\Parsoid\Extractor\ImageExtractor'] = $dir .
'includes/Parsoid/Extractor/ImageExtractor.php';
+$wgAutoloadClasses['Flow\Parsoid\Extractor\PlaceholderExtractor'] = $dir .
'includes/Parsoid/Extractor/PlaceholderExtractor.php';
+$wgAutoloadClasses['Flow\Parsoid\Extractor\TransclusionExtractor'] = $dir .
'includes/Parsoid/Extractor/TransclusionExtractor.php';
+$wgAutoloadClasses['Flow\Parsoid\Extractor\WikiLinkExtractor'] = $dir .
'includes/Parsoid/Extractor/WikiLinkExtractor.php';
// Watchlist
$wgAutoloadClasses['Flow\WatchedTopicItems'] =
"$dir/includes/WatchedTopicItems.php";
diff --git a/container.php b/container.php
index 5a867c0..99f4a63 100644
--- a/container.php
+++ b/container.php
@@ -799,91 +799,11 @@
$c['reference.extractor'] = $c->share( function( $c ) {
return new Flow\Parsoid\ReferenceExtractor(
array(
- '//*[starts-with(@typeof, "mw:Image")]' =>
- function( $element ) {
- $imgNode =
$element->getElementsByTagName( 'img' )->item( 0 );
- $data = FormatJson::decode(
$imgNode->getAttribute( 'data-parsoid' ), true );
- $imageName = $data['sa']['resource'];
-
- return array(
- 'refType' => 'file',
- 'targetType' => 'wiki',
- 'target' => $imageName,
- );
- },
- /*
- * Parsoid currently returns images that don't exist
like:
- * <meta typeof="mw:Placeholder"
data-parsoid='{"src":"[[File:Image.png|25px]]","optList":[{"ck":"width","ak":"25px"}],"dsr":[0,23,null,null]}'>
- *
- * Links to those should also be registered, but since
they're
- * different nodes than what we expect above, we'll
have to deal
- * with them ourselves. This may change some day, as
Parsoids
- * codebase has a FIXME "Handle missing images
properly!!"
- */
- '//*[starts-with(@typeof, "mw:Placeholder")]' =>
- function( $element ) {
- $data = FormatJson::decode(
$element->getAttribute( 'data-parsoid' ), true );
- if ( !isset( $data['src'] ) ) {
- return null;
- }
-
- /*
- * Parsoid only gives us the raw source
to play with. Run it
- * through Parser to make sure we're
dealing with an image
- * and get the image name.
- */
- global $wgParser;
- $output = $wgParser->parse(
$data['src'], Title::newFromText( 'Main Page' ), new \ParserOptions );
-
- $file = $output->getImages();
- if ( !$file ) {
- return null;
- }
- // $file looks like array( 'Foo.jpg' =>
1 )
- $image = Title::newFromText( key( $file
), NS_FILE );
-
- return array(
- 'refType' => 'file',
- 'targetType' => 'wiki',
- 'target' =>
$image->getPrefixedDBkey(),
- );
- },
- '//a[@rel="mw:WikiLink"][not(@typeof)]' =>
- function( $element ) {
- $parsoidData = FormatJson::decode(
$element->getAttribute( 'data-parsoid' ), true );
- $linkTarget =
$parsoidData['sa']['href'];
-
- return array(
- 'refType' => 'link',
- 'targetType' => 'wiki',
- 'target' => $linkTarget,
- );
- },
- '//a[@rel="mw:ExtLink"]' =>
- function( $element ) {
- $href = urldecode(
$element->getAttribute( 'href' ) );
-
- return array(
- 'refType' => 'link',
- 'targetType' => 'url',
- 'target' => $href,
- );
- },
- '//*[@typeof="mw:Transclusion"]' =>
- function( $element ) {
- $data = json_decode(
$element->getAttribute( 'data-mw' ) );
- $templateTarget = Title::newFromText(
$data->parts[0]->template->target->wt, NS_TEMPLATE );
-
- if ( !$templateTarget ) {
- return null;
- }
-
- return array(
- 'refType' => 'template',
- 'targetType' => 'wiki',
- 'target' =>
$templateTarget->getPrefixedText(),
- );
- },
+ new Flow\Parsoid\Extractor\ImageExtractor,
+ new Flow\Parsoid\Extractor\PlaceholderExtractor,
+ new Flow\Parsoid\Extractor\WikiLinkExtractor,
+ new Flow\Parsoid\Extractor\ExtLinkExtractor,
+ new Flow\Parsoid\Extractor\TransclusionExtractor,
)
);
} );
diff --git a/includes/Model/Reference.php b/includes/Model/Reference.php
index e2458c5..c7d7912 100644
--- a/includes/Model/Reference.php
+++ b/includes/Model/Reference.php
@@ -6,11 +6,11 @@
use Title;
abstract class Reference {
+ const TYPE_LINK = 'link';
+
protected $workflowId, $title, $objectType, $objectId, $type;
- protected $validTypes = array(
- 'link',
- );
+ protected $validTypes = array( self::TYPE_LINK );
/**
* Standard constructor. Called from subclasses only
@@ -115,6 +115,9 @@
}
class WikiReference extends Reference {
+ const TYPE_FILE = 'file';
+ const TYPE_TEMPLATE = 'template';
+
protected $target;
/**
@@ -130,8 +133,8 @@
$this->validTypes = array_merge( $this->validTypes,
array(
- 'file',
- 'template',
+ self::TYPE_FILE,
+ self::TYPE_TEMPLATE,
)
);
diff --git a/includes/Parsoid/Extractor/ExtLinkExtractor.php
b/includes/Parsoid/Extractor/ExtLinkExtractor.php
new file mode 100644
index 0000000..7a03fc3
--- /dev/null
+++ b/includes/Parsoid/Extractor/ExtLinkExtractor.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Flow\Parsoid\Extractor;
+
+use DOMElement;
+use Flow\Model\Reference;
+use Flow\Parsoid\ExtractorInterface;
+use Flow\Parsoid\ReferenceFactory;
+
+class ExtLinkExtractor implements ExtractorInterface {
+ public function getXPath() {
+ return '//a[@rel="mw:ExtLink"]';
+ }
+
+ public function perform( ReferenceFactory $factory, DOMElement $element
) {
+ return $factory->createUrlReference(
+ Reference::TYPE_LINK,
+ urldecode( $element->getAttribute( 'href' ) )
+ );
+ }
+}
+
diff --git a/includes/Parsoid/Extractor/ImageExtractor.php
b/includes/Parsoid/Extractor/ImageExtractor.php
new file mode 100644
index 0000000..437fdc1
--- /dev/null
+++ b/includes/Parsoid/Extractor/ImageExtractor.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Flow\Parsoid\Extractor;
+
+use DOMElement;
+use Flow\Model\WikiReference;
+use Flow\Parsoid\ExtractorInterface;
+use Flow\Parsoid\ReferenceFactory;
+use FormatJson;
+
+class ImageExtractor implements ExtractorInterface {
+ public function getXPath() {
+ return '//*[starts-with(@typeof, "mw:Image")]';
+ }
+
+ public function perform( ReferenceFactory $factory, DOMElement $element
) {
+ $imgNode = $element->getElementsByTagName( 'img' )->item( 0 );
+ $data = FormatJson::decode( $imgNode->getAttribute(
'data-parsoid' ), true );
+
+ return $factory->createWikiReference( WikiReference::TYPE_FILE,
$data['sa']['resource'] );
+ }
+}
diff --git a/includes/Parsoid/Extractor/PlaceholderExtractor.php
b/includes/Parsoid/Extractor/PlaceholderExtractor.php
new file mode 100644
index 0000000..0ffb9ef
--- /dev/null
+++ b/includes/Parsoid/Extractor/PlaceholderExtractor.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Flow\Parsoid\Extractor;
+
+use DOMElement;
+use Flow\Model\WikiReference;
+use Flow\Parsoid\ExtractorInterface;
+use Flow\Parsoid\ReferenceFactory;
+use FormatJson;
+use ParserOptions;
+use Title;
+
+/*
+ * Parsoid currently returns images that don't exist like:
+ * <meta typeof="mw:Placeholder"
data-parsoid='{"src":"[[File:Image.png|25px]]","optList":[{"ck":"width","ak":"25px"}],"dsr":[0,23,null,null]}'>
+ *
+ * Links to those should also be registered, but since they're
+ * different nodes than what we expect above, we'll have to deal
+ * with them ourselves. This may change some day, as Parsoids
+ * codebase has a FIXME "Handle missing images properly!!"
+ */
+class PlaceholderExtractor implements ExtractorInterface {
+ public function getXPath() {
+ return '//*[starts-with(@typeof, "mw:Placeholder")]';
+ }
+
+ public function perform( ReferenceFactory $factory, DOMElement $element
) {
+ $data = FormatJson::decode( $element->getAttribute(
'data-parsoid' ), true );
+ if ( !isset( $data['src'] ) ) {
+ return null;
+ }
+
+ /*
+ * Parsoid only gives us the raw source to play with. Run it
+ * through Parser to make sure we're dealing with an image
+ * and get the image name.
+ */
+ global $wgParser;
+ $output = $wgParser->parse( $data['src'], Title::newFromText(
'Main Page' ), new ParserOptions );
+
+ $file = $output->getImages();
+ if ( !$file ) {
+ return null;
+ }
+ // $file looks like array( 'Foo.jpg' => 1 )
+ $image = Title::newFromText( key( $file ), NS_FILE );
+
+ $factory->createWikiReference( WikiReference::TYPE_FILE,
$image->getPrefixedDBkey() );
+ }
+}
diff --git a/includes/Parsoid/Extractor/TransclusionExtractor.php
b/includes/Parsoid/Extractor/TransclusionExtractor.php
new file mode 100644
index 0000000..55a3f2f
--- /dev/null
+++ b/includes/Parsoid/Extractor/TransclusionExtractor.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Flow\Parsoid\Extractor;
+
+use DOMElement;
+use Flow\Model\WikiReference;
+use Flow\Parsoid\ExtractorInterface;
+use Flow\Parsoid\ReferenceFactory;
+use FormatJson;
+use Title;
+
+class TransclusionExtractor implements ExtractorInterface {
+ public function getXPath() {
+ return '//*[@typeof="mw:Transclusion"]';
+ }
+
+ public function perform( ReferenceFactory $factory, DOMElement $element
) {
+ $data = FormatJson::decode( $element->getAttribute( 'data-mw' )
);
+ $templateTarget = Title::newFromText(
+ $data->parts[0]->template->target->wt,
+ NS_TEMPLATE
+ );
+
+ if ( !$templateTarget ) {
+ return null;
+ }
+
+ return $factory->createWikiReference(
+ WikiReference::TYPE_TEMPLATE,
+ $templateTarget->getPrefixedText()
+ );
+ }
+}
diff --git a/includes/Parsoid/Extractor/WikiLinkExtractor.php
b/includes/Parsoid/Extractor/WikiLinkExtractor.php
new file mode 100644
index 0000000..5d98860
--- /dev/null
+++ b/includes/Parsoid/Extractor/WikiLinkExtractor.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Flow\Parsoid\Extractor;
+
+use DOMElement;
+use Flow\Model\Reference;
+use Flow\Parsoid\ExtractorInterface;
+use Flow\Parsoid\ReferenceFactory;
+use FormatJson;
+
+class WikiLinkExtractor implements ExtractorInterface {
+ public function getXPath() {
+ return '//a[@rel="mw:WikiLink"][not(@typeof)]';
+ }
+
+ public function perform( ReferenceFactory $factory, DOMElement $element
) {
+ $parsoidData = FormatJson::decode( $element->getAttribute(
'data-parsoid' ), true );
+
+ return $factory->createWikiReference( Reference::TYPE_LINK,
$parsoidData['sa']['href'] );
+ }
+}
diff --git a/includes/Parsoid/ExtractorInterface.php
b/includes/Parsoid/ExtractorInterface.php
new file mode 100644
index 0000000..48408d4
--- /dev/null
+++ b/includes/Parsoid/ExtractorInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Flow\Parsoid;
+
+use DOMElement;
+
+interface ExtractorInterface {
+ /**
+ * @return string
+ */
+ function getXPath();
+
+ /**
+ * @param DOMElement $element
+ * @return array
+ */
+ function perform( ReferenceFactory $factory, DOMElement $element );
+}
diff --git a/includes/Parsoid/ReferenceExtractor.php
b/includes/Parsoid/ReferenceExtractor.php
index 79043a0..e200b6d 100644
--- a/includes/Parsoid/ReferenceExtractor.php
+++ b/includes/Parsoid/ReferenceExtractor.php
@@ -15,86 +15,55 @@
* from Parsoid HTML.
*/
class ReferenceExtractor {
- protected $handlers;
+ /**
+ * @var Extractor\ExtractorInterface[]
+ */
+ protected $extractors;
- public function __construct( $handlers ) {
- $this->handlers = $handlers;
+ /**
+ * @param Extractor\ExtractorInterface[] $extractors
+ */
+ public function __construct( $extractors ) {
+ $this->extractors = $extractors;
}
+ /**
+ * @param Workflow $workflow
+ * @param string $objectType
+ * @param UUID $objectId
+ * @param string $text
+ * @return array
+ */
public function getReferences( Workflow $workflow, $objectType, UUID
$objectId, $text ) {
- $referenceList = $this->extractReferences( $text );
- $references = array();
-
- foreach( $referenceList as $ref ) {
- if ( !$ref ) {
- continue;
- }
-
- $reference = $this->instantiateReference(
- $workflow,
- $objectType,
- $objectId,
- $ref['targetType'],
- $ref['refType'],
- $ref['target']
- );
- if ( $reference ) {
- $references[] = $reference;
- }
- }
-
- return $references;
+ return $this->extractReferences(
+ new ReferenceFactory( $workflow, $objectType, $objectId
),
+ $text
+ );
}
- public function extractReferences( $text ) {
+ protected function extractReferences( ReferenceFactory $factory, $text
) {
$dom = Utils::createDOM( '<?xml encoding="utf-8" ?>' . $text );
$output = array();
$xpath = new DOMXPath( $dom );
- foreach( $this->handlers as $query => $handler ) {
- $elements = $xpath->query( $query );
+ foreach( $this->extractors as $extractor ) {
+ $elements = $xpath->query( $extractor->getXPath() );
if ( ! $elements ) {
- throw new MWException( "No results for $query"
);
+ $class = get_class( $extractor );
+ throw new MWException( "Malformed xpath from
$class: $query" );
}
foreach( $elements as $element ) {
- $output[] = $handler( $element );
+ $ref = $extractor->perform( $factory, $element
);
+ if ( $ref !== null ) {
+ $output[] = $ref;
+ }
}
}
return $output;
- }
-
- public function instantiateReference( Workflow $workflow, $objectType,
UUID $objectId, $targetType, $refType, $value ) {
- if ( $targetType === 'url' ) {
- return new URLReference(
- $workflow->getId(),
- $workflow->getArticleTitle(),
- $objectType,
- $objectId,
- $refType,
- $value
- );
- } elseif ( $targetType === 'wiki' ) {
- $title = Utils::createRelativeTitle( $value,
$workflow->getArticleTitle() );
-
- if ( $title === null ) {
- return null;
- }
-
- return new WikiReference(
- $workflow->getId(),
- $workflow->getArticleTitle(),
- $objectType,
- $objectId,
- $refType,
- $title
- );
- } else {
- throw new InvalidInputException( "Invalid type" );
- }
}
}
diff --git a/includes/Parsoid/ReferenceFactory.php
b/includes/Parsoid/ReferenceFactory.php
new file mode 100644
index 0000000..b453730
--- /dev/null
+++ b/includes/Parsoid/ReferenceFactory.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Flow\Parsoid;
+
+use Flow\Model\URLReference;
+use Flow\Model\UUID;
+use Flow\Model\WikiReference;
+use Flow\Model\Workflow;
+
+class ReferenceFactory {
+ /**
+ * @param Workflow $workflow
+ * @param string $objectType
+ * @param UUID $objectId
+ */
+ public function __construct( Workflow $workflow, $objectType, UUID
$objectId ) {
+ $this->workflowId = $workflow->getId();
+ $this->title = $workflow->getArticleTitle();
+ $this->objectType = $objectType;
+ $this->objectId = $objectId;
+ }
+
+ /**
+ * @param string $refType
+ * @param string $value
+ * @return URLReference
+ */
+ public function createUrlReference( $refType, $value ) {
+ return new URLReference(
+ $this->workflowId,
+ $$this->title,
+ $this->objectType,
+ $this->objectId,
+ $refType,
+ $value
+ );
+ }
+
+ /**
+ * @param string $refType
+ * @param string $value
+ * @return WikiReference|null
+ */
+ public function createWikiReference( $refType, $value ) {
+ $title = Utils::createRelativeTitle( $value, $this->title );
+
+ if ( $title === null ) {
+ return null;
+ }
+
+ return new WikiReference(
+ $this->workflowId,
+ $this->title,
+ $this->objectType,
+ $this->objectId,
+ $refType,
+ $title
+ );
+ }
+}
diff --git a/tests/ReferenceExtractorTest.php b/tests/ReferenceExtractorTest.php
index 5f9a37c..a6175b1 100644
--- a/tests/ReferenceExtractorTest.php
+++ b/tests/ReferenceExtractorTest.php
@@ -5,6 +5,7 @@
use MediaWikiTestCase;
use Flow\Exception\WikitextException;
use Flow\Parsoid\Utils;
+use ReflectionMethod;
use Title;
/**
@@ -27,81 +28,68 @@
return array(
array(
'Normal link',
+ // source wiki text
'[[My page]]',
- array(
- array(
- 'refType' => 'link',
- 'targetType' => 'wiki',
- 'target' => 'My page',
- ),
- )
+ // expected factory method
+ 'createWikiReference',
+ // expected factory arguments
+ array( 'link', 'My page' ),
),
array(
'Link with URL encoding issues',
+ // source wiki text
'[[User talk:Werdna?]]',
- array(
- array(
- 'refType' => 'link',
- 'targetType' => 'wiki',
- 'target' => 'User talk:Werdna?',
- ),
- ),
+ // expected factory method
+ 'createWikiReference',
+ // expected factory arguments
+ array( 'link', 'User talk:Werdna?' ),
),
array(
'Subpage link',
+ // source wiki text
'[[/Subpage]]',
- array(
- array(
- 'refType' => 'link',
- 'targetType' => 'wiki',
- 'target' => '/Subpage'
- ),
- ),
+ // expected factory method
+ 'createWikiReference',
+ // expected factory arguments
+ array( 'link', '/Subpage' ),
+ // ???
'Talk:UTPage',
),
array(
'External link',
+ // source wiki text
'[http://www.google.com Google]',
- array(
- array(
- 'refType' => 'link',
- 'targetType' => 'url',
- 'target' =>
'http://www.google.com',
- ),
- )
+ // expected factory method
+ 'createUrlReference',
+ // exepcted factory arguments
+ array( 'link', 'http://www.google.com' ),
),
array(
'File',
+ // source wiki text
'[[File:Image.png]]',
- array(
- array(
- 'refType' => 'file',
- 'targetType' => 'wiki',
- 'target' => 'File:Image.png',
- ),
- )
+ // expected factory method
+ 'createWikiReference',
+ // expected factory arguments
+ array( 'file', 'File:Image.png' ),
),
array(
'File with parameters',
+ // source wiki text
'[[File:Image.png|25px]]',
- array(
- array(
- 'refType' => 'file',
- 'targetType' => 'wiki',
- 'target' => 'File:Image.png',
- ),
- )
+ // expected factory method
+ 'createWikiReference',
+ // expected factory arguments
+ array( 'file', 'File:Image.png' ),
),
array(
'Template',
+ // source wiki text
'{{Foo}}',
- array(
- array(
- 'refType' => 'template',
- 'targetType' => 'wiki',
- 'target' => 'Template:Foo',
- ),
- )
+ // expected factory method
+ 'createWikiReference',
+ // expected factory arguments
+ array( 'template', 'Template:Foo' ),
),
);
}
@@ -109,13 +97,21 @@
/**
* @dataProvider referenceExtractorProvider
*/
- public function testReferenceExtractor( $description, $wikitext,
$expectedOutput, $page = 'UTPage' ) {
+ public function testReferenceExtractor( $description, $wikitext,
$expectedMethod, $expectedArguments, $page = 'UTPage' ) {
$referenceExtractor = Container::get( 'reference.extractor' );
$html = Utils::convert( 'wt', 'html', $wikitext,
Title::newFromText( $page ) );
+ $factory = $this->getMockBuilder(
'Flow\Parsoid\ReferenceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
- $references = $referenceExtractor->extractReferences( $html );
+ $factory->expects( $this->once() )
+ ->method( $expectedMethod )
+ ->getMatcher()
+ ->parametersMatcher = new
\PHPUnit_Framework_MockObject_Matcher_Parameters( $expectedArguments );
- $this->assertEquals( $references, $expectedOutput, $html );
+ $reflMethod = new ReflectionMethod( $referenceExtractor,
'extractReferences' );
+ $reflMethod->setAccessible( true );
+ $reflMethod->invoke( $referenceExtractor, $factory, $html );
}
}
--
To view, visit https://gerrit.wikimedia.org/r/155805
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I8ae467dd2d2a7d3fac8dd33b42d2a7d087a63340
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: EBernhardson <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits