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

Reply via email to