Addshore has uploaded a new change for review.
https://gerrit.wikimedia.org/r/313979
Change subject: Initial commit
......................................................................
Initial commit
Change-Id: Ia9a0190f4b992471b32ec796b7876ec9f3ac8ff1
---
A .gitreview
A README.md
A extension.json
A i18n/en.json
A i18n/qqq.json
A src/InterwikiSorter.php
A src/InterwikiSortingHookHandlers.php
A src/InterwikiSortingHooks.php
A tests/phpunit/InterwikiSorterTest.php
9 files changed, 447 insertions(+), 0 deletions(-)
git pull
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/InterwikiSorting
refs/changes/79/313979/1
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..a20e8b0
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,6 @@
+[gerrit]
+host=gerrit.wikimedia.org
+port=29418
+project=mediawiki/extensions/Cognate.git
+defaultbranch=master
+defaultrebase=0
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..08c8f0a
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# InterwikiSorting extension
\ No newline at end of file
diff --git a/extension.json b/extension.json
new file mode 100644
index 0000000..c7c9b07
--- /dev/null
+++ b/extension.json
@@ -0,0 +1,33 @@
+{
+ "name": "InterwikiSorting",
+ "version": "0.0.1",
+ "author": [
+ "WMDE"
+ ],
+ "url": "https://www.mediawiki.org/wiki/Extension:InterwikiSorting",
+ "descriptionmsg": "interwikisorting-desc",
+ "license-name": "GPL-2.0+",
+ "type": "other",
+ "config": {
+ "InterwikiSortingNamespaces": [],
+ "InterwikiSortingAlwaysSort": false,
+ "InterwikiSortingSort": "code",
+ "InterwikiSortingSortPrepend": [],
+ "InterwikiSortingInterwikiSortOrders": []
+ },
+ "AutoloadClasses": {
+ "InterwikiSorting\\InterwikiSorter": "src/InterwikiSorter.php",
+ "InterwikiSorting\\InterwikiSortingHooks": "src/InterwikiSortingHooks.php",
+ "InterwikiSorting\\InterwikiSortingHookHandlers":
"src/InterwikiSortingHookHandlers.php"
+ },
+ "Hooks": {
+ "InterwikiSortingHooks": [
"InterwikiSorting\\InterwikiSortingHooks::onContentAlterParserOutput" ],
+ "UnitTestsList": [
"InterwikiSorting\\InterwikiSortingHooks::onUnitTestsList" ]
+ },
+ "MessagesDirs": {
+ "InterwikiSorting": [
+ "i18n"
+ ]
+ },
+ "manifest_version": 1
+}
\ No newline at end of file
diff --git a/i18n/en.json b/i18n/en.json
new file mode 100644
index 0000000..1264bf0
--- /dev/null
+++ b/i18n/en.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Addshore"
+ ]
+ },
+ "interwikisorting-desc": "Sort interwiki links."
+}
\ No newline at end of file
diff --git a/i18n/qqq.json b/i18n/qqq.json
new file mode 100644
index 0000000..158978c
--- /dev/null
+++ b/i18n/qqq.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Addshore"
+ ]
+ },
+ "interwikisorting-desc":
"{{desc|name=Cognate|url=https://www.mediawiki.org/wiki/Extension:InterwikiSorting}}"
+}
diff --git a/src/InterwikiSorter.php b/src/InterwikiSorter.php
new file mode 100644
index 0000000..02ced82
--- /dev/null
+++ b/src/InterwikiSorter.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace InterwikiSorting;
+
+/**
+ * Language sorting utility functions.
+ *
+ * @license GPL-2.0+
+ * @author Nikola Smolenski <[email protected]>
+ * @author Katie Filbert < [email protected] >
+ * @author Thiemo Mättig
+ */
+class InterwikiSorter {
+
+ /**
+ * @see Documentation of "sort" and "interwikiSortOrders" options in
docs/options.wiki.
+ */
+ const SORT_CODE = 'code';
+
+ /**
+ * @var array[]
+ */
+ private $sortOrders;
+
+ /**
+ * @var string
+ */
+ private $sort;
+
+ /**
+ * @var string[]
+ */
+ private $sortPrepend;
+
+ /**
+ * @var int[]|null
+ */
+ private $sortOrder = null;
+
+ /**
+ * @param string $sort
+ * @param array[] $sortOrders
+ * @param string[] $sortPrepend
+ */
+ public function __construct( $sort, array $sortOrders = array(), array
$sortPrepend = array() ) {
+ $this->sort = $sort;
+ $this->sortOrders = $sortOrders;
+ $this->sortPrepend = $sortPrepend;
+ }
+
+ /**
+ * Sort an array of links in-place
+ * @version Copied from InterlanguageExtension rev 114818
+ *
+ * @param string[] $links
+ *
+ * @return string[]
+ */
+ public function sortLinks( array $links ) {
+ if ( $this->sortOrder === null ) {
+ $this->sortOrder = $this->buildSortOrder( $this->sort,
$this->sortOrders );
+ }
+
+ // Prepare the array for sorting.
+ foreach ( $links as $k => $langLink ) {
+ $links[$k] = explode( ':', $langLink, 2 );
+ }
+
+ usort( $links, array( $this, 'compareLinks' ) );
+
+ // Restore the sorted array.
+ foreach ( $links as $k => $langLink ) {
+ $links[$k] = implode( ':', $langLink );
+ }
+
+ return $links;
+ }
+
+ /**
+ * usort() callback function, compares the links on the basis of
$sortOrder
+ *
+ * @param string[] $a
+ * @param string[] $b
+ *
+ * @return int
+ */
+ private function compareLinks( array $a, array $b ) {
+ $a = $a[0];
+ $b = $b[0];
+
+ if ( $a === $b ) {
+ return 0;
+ }
+
+ $aIndex = array_key_exists( $a, $this->sortOrder ) ?
$this->sortOrder[$a] : null;
+ $bIndex = array_key_exists( $b, $this->sortOrder ) ?
$this->sortOrder[$b] : null;
+
+ if ( $aIndex === $bIndex ) {
+ // If we encounter multiple unknown languages, which
may happen if the sort table is not
+ // updated, we list them alphabetically.
+ return strcmp( $a, $b );
+ } elseif ( $aIndex === null ) {
+ // Unknown languages must go under the known languages.
+ return 1;
+ } elseif ( $bIndex === null ) {
+ return -1;
+ } else {
+ return $aIndex - $bIndex;
+ }
+ }
+
+ /**
+ * Build sort order to be used by compareLinks().
+ *
+ * @param string $sort
+ * @param array[] $sortOrders
+ *
+ * @return int[]
+ */
+ private function buildSortOrder( $sort, array $sortOrders ) {
+ if ( $sort === self::SORT_CODE ) {
+ // The concept of known/unknown languages is irrelevant
in strict code order.
+ $sortOrder = array();
+ } elseif ( !array_key_exists( $sort, $sortOrders ) ) {
+ // Something went wrong, but we can use default "code"
order.
+ wfDebugLog(
+ __CLASS__,
+ __FUNCTION__ . ': Invalid or unknown sort order
specified for interwiki links.'
+ );
+ $sortOrder = array();
+ } else {
+ $sortOrder = $sortOrders[$sort];
+ }
+
+ if ( $this->sortPrepend !== array() ) {
+ $sortOrder = array_unique( array_merge(
$this->sortPrepend, $sortOrder ) );
+ }
+
+ return array_flip( $sortOrder );
+ }
+
+}
diff --git a/src/InterwikiSortingHookHandlers.php
b/src/InterwikiSortingHookHandlers.php
new file mode 100644
index 0000000..069518f
--- /dev/null
+++ b/src/InterwikiSortingHookHandlers.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace InterwikiSorting;
+
+use MediaWiki\MediaWikiServices;
+use ParserOutput;
+use Title;
+
+class ParserOutputUpdateHookHandlers {
+
+ /**
+ * @var InterwikiSorter
+ */
+ private $interwikiSorter;
+
+ /**
+ * @var bool
+ */
+ private $alwaysSort;
+
+ /**
+ * @var int[]
+ */
+ private $namespaces;
+
+ public static function newFromGlobalState() {
+ $config = MediaWikiServices::getInstance()->getMainConfig();
+
+ return new ParserOutputUpdateHookHandlers(
+ new InterwikiSorter(
+ $config->get( 'InterwikiSortingSort' ),
+ $config->get( 'InterwikiSortOrders' ),
+ $config->get( 'InterwikiSortingSortPrepend' )
+ ),
+ $config->get( 'InterwikiSortingNamespaces' ),
+ $config->get( 'InterwikiSortingAlwaysSort' )
+ );
+ }
+
+ /**
+ * @param InterwikiSorter $sorter
+ * @param int[] $namespaces
+ * @param boolean $alwaysSort
+ */
+ public function __construct(
+ InterwikiSorter $sorter,
+ $namespaces,
+ $alwaysSort
+ ) {
+ $this->interwikiSorter = $sorter;
+ $this->namespaces = $namespaces;
+ $this->alwaysSort = $alwaysSort;
+ }
+
+ /**
+ * Hook runs after internal parsing
+ * @see
https://www.mediawiki.org/wiki/Manual:Hooks/ContentAlterParserOutput
+ *
+ * @param Title $title
+ * @param ParserOutput $parserOutput
+ *
+ * @return bool
+ */
+ public function doContentAlterParserOutput( Title $title, ParserOutput
$parserOutput ) {
+ if ( !$title->inNamespaces( $this->namespaces ) ) {
+ // shorten out
+ return true;
+ }
+
+ if ( $this->alwaysSort ) {
+ $interwikiLinks = $parserOutput->getLanguageLinks();
+ $sortedLinks = $this->interwikiSorter->sortLinks(
$interwikiLinks );
+ $parserOutput->setLanguageLinks( $sortedLinks );
+ }
+
+ return true;
+ }
+
+}
diff --git a/src/InterwikiSortingHooks.php b/src/InterwikiSortingHooks.php
new file mode 100644
index 0000000..22e0dd3
--- /dev/null
+++ b/src/InterwikiSortingHooks.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace InterwikiSorting;
+
+use Content;
+use ParserOutput;
+use Title;
+
+class InterwikiSortingHooks {
+
+ /**
+ * @param Content $content
+ * @param Title $title
+ * @param ParserOutput $parserOutput
+ */
+ public static function onContentAlterParserOutput(
+ Content $content,
+ Title $title,
+ ParserOutput $parserOutput
+ ) {
+ // 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;
+ }
+
+ $handler = ParserOutputUpdateHookHandlers::newFromGlobalState();
+ $handler->doContentAlterParserOutput( $title, $parserOutput );
+ }
+
+ public static function onUnitTestsList( &$files ) {
+ $files = array_merge( $files, __DIR__ . '/tests/phpunit' );
+ return true;
+ }
+
+}
diff --git a/tests/phpunit/InterwikiSorterTest.php
b/tests/phpunit/InterwikiSorterTest.php
new file mode 100644
index 0000000..6a1e630
--- /dev/null
+++ b/tests/phpunit/InterwikiSorterTest.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace InterwikiSorting\Tests;
+
+use InterwikiSorting\InterwikiSorter;
+
+/**
+ * @covers InterwikiSorting\InterwikiSorter
+ *
+ * @license GPL-2.0+
+ * @author Katie Filbert < [email protected] >
+ */
+class InterwikiSorterTest extends \PHPUnit_Framework_TestCase {
+
+ public function sortOrdersProvider() {
+ return array(
+ 'alphabetic' => array( 'ar', 'de', 'en', 'fr', 'ks',
'rn', 'ky', 'hu', 'ja', 'pt' ),
+ 'alphabetic_revised' => array( 'ar', 'de', 'en', 'fr',
'ks', 'ky', 'rn', 'hu', 'ja', 'pt' ),
+ 'alphabetic_sr' => array( 'ar', 'de', 'en', 'fr', 'ky',
'rn', 'ks', 'ja', 'hu', 'pt' ),
+ 'mycustomorder' => array( 'de', 'ja', 'pt', 'hu', 'en'
),
+ );
+ }
+
+ public function constructorProvider() {
+ $sortOrders = $this->sortOrdersProvider();
+ return array(
+ array( 'code', $sortOrders, array() ),
+ array( 'code', $sortOrders, array( 'en' ) )
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ */
+ public function testConstructor( $sort, $sortOrders, $sortPrepend ) {
+ $interwikiSorter = new InterwikiSorter( $sort, $sortOrders,
$sortPrepend );
+ $this->assertInstanceOf( InterwikiSorter::class,
$interwikiSorter );
+ }
+
+ public function sortLinksProvider() {
+ $sortOrders = $this->sortOrdersProvider();
+ $links = array( 'fr', 'ky', 'hu', 'ar', 'ks', 'ja', 'de', 'en',
'pt', 'rn' );
+
+ return array(
+ array(
+ $links, 'code', $sortOrders, array(),
+ array( 'ar', 'de', 'en', 'fr', 'hu', 'ja',
'ks', 'ky', 'pt', 'rn' )
+ ),
+ array(
+ $links, 'code', $sortOrders, array( 'en' ),
+ array( 'en', 'ar', 'de', 'fr', 'hu', 'ja',
'ks', 'ky', 'pt', 'rn' )
+ ),
+ array(
+ $links, 'alphabetic', $sortOrders, array(),
+ $sortOrders['alphabetic']
+ ),
+ array(
+ $links, 'alphabetic', $sortOrders, array( 'en',
'ja' ),
+ array( 'en', 'ja', 'ar', 'de','fr', 'ks', 'rn',
'ky', 'hu', 'pt' )
+ ),
+ array(
+ $links, 'alphabetic_revised', $sortOrders,
array(),
+ $sortOrders['alphabetic_revised']
+ ),
+ array(
+ $links, 'alphabetic_revised', $sortOrders,
array( 'hu' ),
+ array( 'hu', 'ar', 'de', 'en', 'fr', 'ks',
'ky', 'rn', 'ja', 'pt' )
+ ),
+ array(
+ array( 'ja', 'de', 'pt', 'en', 'hu' ),
'mycustomorder', $sortOrders, array(),
+ $sortOrders['mycustomorder']
+ ),
+ array(
+ array( 'x2', 'x1', 'x3' ),
+ 'alphabetic',
+ array( 'alphabetic' => array() ),
+ array(),
+ array( 'x1', 'x2', 'x3' )
+ ),
+ array(
+ array( 'x2', 'x1', 'en', 'de', 'a2', 'a1' ),
+ 'alphabetic',
+ $sortOrders,
+ array(),
+ array( 'de', 'en', 'a1', 'a2', 'x1', 'x2' )
+ ),
+ array(
+ array( 'f', 'd', 'b', 'a', 'c', 'e' ),
+ 'alphabetic',
+ array( 'alphabetic' => array( 'c', 'a' ) ),
+ array( 'e' ),
+ array( 'e', 'c', 'a', 'b', 'd', 'f' )
+ ),
+ 'Strict code order' => array(
+ array( 'f', 'd', 'b', 'a', 'c', 'e' ),
+ 'code',
+ array( 'alphabetic' => array( 'c', 'a' ) ), //
this should be ignored
+ array( 'e' ), // prepend
+ array( 'e', 'a', 'b', 'c', 'd', 'f' )
+ ),
+ 'Code w/o alphabetic' => array(
+ array( 'c', 'b', 'a' ),
+ 'code',
+ array(),
+ array(),
+ array( 'a', 'b', 'c' )
+ ),
+ array(
+ array( 'a', 'b', 'k', 'x' ),
+ 'alphabetic',
+ array( 'alphabetic' => array( 'x', 'k', 'a' ) ),
+ array(),
+ array( 'x', 'k', 'a', 'b' )
+ ),
+ 'Fall back to code order' => array(
+ array( 'b', 'a' ),
+ 'invalid',
+ array(),
+ array(),
+ array( 'a', 'b' )
+ )
+ );
+ }
+
+ /**
+ * @dataProvider sortLinksProvider
+ */
+ public function testSortLinks( array $links, $sort, array $sortOrders,
$sortPrepend, $expected ) {
+ $interwikiSorter = new InterwikiSorter( $sort, $sortOrders,
$sortPrepend );
+ $sortedLinks = $interwikiSorter->sortLinks( $links );
+ $this->assertEquals( $expected, $sortedLinks );
+ }
+
+}
--
To view, visit https://gerrit.wikimedia.org/r/313979
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia9a0190f4b992471b32ec796b7876ec9f3ac8ff1
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/InterwikiSorting
Gerrit-Branch: master
Gerrit-Owner: Addshore <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits