Physikerwelt has uploaded a new change for review.

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

Change subject: Improve layout and Logic of MLP eval interface
......................................................................

Improve layout and Logic of MLP eval interface

* Update to modern OOUIHTMLForm
* Seperate form handling and specialclass logic
* Remove page completion for non OOUI controls

Change-Id: I84533407b9db6062a5c01c6760e7173dbd95a870
---
M extension.json
A includes/MathHighlighter.php
M includes/MathIdGenerator.php
A includes/MlpEvalForm.php
M includes/special/SpecialMlpEval.php
D modules/ext.MathSearch.autocomplete.js
6 files changed, 361 insertions(+), 213 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MathSearch 
refs/changes/16/255416/1

diff --git a/extension.json b/extension.json
index fcd58d6..9d5035f 100644
--- a/extension.json
+++ b/extension.json
@@ -30,7 +30,9 @@
                "SpecialMathDownloadResult": "SpecialMathDownloadResult.php",
                "MathoidDriver": "includes/MathoidDriver.php",
                "MathosphereDriver": "includes/MathosphereDriver.php",
-               "MathIdGenerator": "includes/MathIdGenerator.php"
+               "MathIdGenerator": "includes/MathIdGenerator.php",
+               "MathHighlighter": "includes/MathHighlighter.php",
+               "MlpEvalForm": "includes/MlpEvalForm.php"
        },
        "AvailableRights": [
                "mathwmcsubmit"
@@ -85,17 +87,9 @@
                                "desktop",
                                "mobile"
                        ]
-               },
-               "ext.MathSearch.special": {
-                       "scripts": [
-                               "ext.MathSearch.autocomplete.js"
-                       ],
-                       "dependencies": [
-                               "jquery.ui.autocomplete"
-                       ]
                }
-
        },
+
        "ResourceFileModulePaths": {
                "localBasePath": "modules",
                "remoteExtPath": "MathSearch/modules"
diff --git a/includes/MathHighlighter.php b/includes/MathHighlighter.php
new file mode 100644
index 0000000..88dd84e
--- /dev/null
+++ b/includes/MathHighlighter.php
@@ -0,0 +1,78 @@
+<?php
+class MathHighlighter {
+       /**
+        *
+        */
+       const WINDOW_SIZE = 1200;
+       private $wikiText;
+
+       /**
+        * MathHighlighter constructor.
+        * @param $fId
+        * @param $revId
+        */
+       public function __construct( $fId, $revId ) {
+               $gen = MathIdGenerator::newFromRevisionId( $revId );
+               $unique = $gen->getUniqueFromId( $fId );
+               $wikiText = $gen->getWikiText();
+               $tagPos = strpos( $wikiText, $unique );
+               $startPos = $this->getStartPos( $tagPos, $wikiText );
+               $length = $this->getEndPos( $tagPos, $wikiText ) - $startPos;
+               $wikiText = substr( $wikiText, $startPos, $length );
+               $tag = $gen->getTagFromId( $fId );
+               $wikiText = str_replace( $unique,
+                       '<span id="theelement" style="background-color: 
yellow">' . $tag[3] . '</span>',
+                       $wikiText );
+               foreach ( $gen->getMathTags() as $key => $content ) {
+                       $wikiText = str_replace( $key, $content[3], $wikiText );
+               }
+               $this->wikiText = "== Extract ==\nStart of the 
extract...\n\n$wikiText\n\n...end of the extract";
+       }
+
+       /**
+        * @param $tagPos
+        * @param $wikiText
+        * @return int
+        */
+       private function getStartPos( $tagPos, $wikiText ) {
+               $startPos = max( $tagPos - round( self::WINDOW_SIZE / 2 ), 0 );
+               if ( $startPos > 0 ) {
+                       // Heuristics to find a reasonable cutting point
+                       $newPos = strpos( $wikiText, "\n", $startPos );
+                       if ( $newPos !== false && ( $newPos - $startPos ) < 
round( self::WINDOW_SIZE / 4 ) ) {
+                               // only change startPos, if it seems reasonable
+                               $startPos = $newPos;
+                       }
+               }
+               return $startPos;
+       }
+
+       /**
+        * @param $wikiText
+        * @param $tagPos
+        * @return bool|int|mixed
+        */
+       private function getEndPos( $tagPos, $wikiText ) {
+               $halfWindow = round( self::WINDOW_SIZE / 2 );
+               $distance2End = strlen( $wikiText ) - $tagPos;
+               if ( $distance2End > $halfWindow ) {
+                       $newPos = strpos( $wikiText, "\n", $tagPos + 
$halfWindow );
+                       if ( $newPos !== false && ( $newPos - $tagPos ) < 
round( 3 / 4 * self::WINDOW_SIZE ) ) {
+                               // only change startPos, if it seems reasonable
+                               return $newPos;
+                       } else {
+                               return $tagPos + $halfWindow;
+                       }
+               } else {
+                       return strlen( $wikiText );
+               }
+       }
+
+       /**
+        * @return string
+        */
+       public function getWikiText() {
+               return $this->wikiText;
+       }
+
+}
diff --git a/includes/MathIdGenerator.php b/includes/MathIdGenerator.php
index 500bd1e..b98a298 100644
--- a/includes/MathIdGenerator.php
+++ b/includes/MathIdGenerator.php
@@ -146,6 +146,16 @@
                        }
                }
        }
+       public function getUniqueFromId( $eid ) {
+               foreach ( $this->mathTags as $key => $mathTag ) {
+                       if ( $eid == $this->formatKey( $key ) ){
+                               return $key;
+                       }
+                       if ( isset( $mathTag[self::ATTRIB_POS]['id'] ) && $eid 
== $mathTag[self::ATTRIB_POS]['id'] ){
+                               return $key;
+                       }
+               }
+       }
 
        /**
         * @param $key
diff --git a/includes/MlpEvalForm.php b/includes/MlpEvalForm.php
new file mode 100644
index 0000000..e6865b5
--- /dev/null
+++ b/includes/MlpEvalForm.php
@@ -0,0 +1,107 @@
+<?php
+
+class MlpEvalForm extends OOUIHTMLForm {
+
+       const OPT_CONTINUE = 0;
+       const OPT_BACK = 1;
+       const OPT_RETRY = 2;
+
+       private $specialPage;
+       private $step;
+
+       /**
+        * MlpEvalForm constructor.
+        * @param SpecialMlpEval $specialPage
+        */
+       public function __construct( SpecialMlpEval $specialPage ) {
+               $this->specialPage = $specialPage;
+               $this->step = $specialPage->getStep();
+               $formDescriptor = array();
+               $this->addPageControls( $formDescriptor );
+               $this->addFormulaControls( $formDescriptor );
+               parent::__construct( $formDescriptor, 
$specialPage->getContext() );
+               $this->mWasSubmitted = false;
+               $this->addHiddenField( 'oldId', $specialPage->getOldId() );
+               $this->setSubmitCallback( function(){return false;
+
+        } );
+               $this->addButton( "pgRst", "Select another random page" );
+
+       }
+
+       private function addPageControls( &$formDescriptor ) {
+               $s = array(
+                       'label'   => 'Select page to evaluate.',
+                       'class'   => 'HTMLTitleTextField',
+                       'readonly' => $this->specialPage->getStep() > 1 ? true 
: false,
+               );
+               if ( $this->specialPage->getStep()>1 ){
+                       $s['readonly'] = true;
+               } else {
+                       $s['default'] = 
$this->specialPage->getRandomPage()->getText();
+               }
+               $formDescriptor['evalPage'] = $s;
+       }
+
+       private function addFormulaControls( $formDescriptor ) {
+               $formDescriptor['snippetSelector'] = array(
+                       'type' => 'radio',
+                       'label' => 'Page to evaluate',
+                       'options' => array(
+                               'Continue with this snippet' => 
self::OPT_CONTINUE,
+                               'Select another snippet from that page' => 
self::OPT_RETRY,
+                               'Go Back to page selection' => self::OPT_BACK
+                       ),
+                       'default' => self::OPT_CONTINUE # The option selected 
by default (identified by value)
+               );
+       }
+
+//     /**
+//      * Processes the submitted Form input
+//      * @param array $formData
+//      * @return bool
+//      */
+//     public function processPageInput( $formData ) {
+//             $title = Title::newFromText( $formData['evalPage'] );
+//             if ( $this->setPage( $title ) ) {
+//                     return true;
+//             } else {
+//                     return $this->lastError;
+//             }
+//     }
+
+//     /**
+//      * @param $req
+//      */
+//     private function runStep( WebRequest $req ) {
+//             switch ( $this->step ) {
+//                     case 1:
+//                             $this->displayPageSelectionForm();
+//                             break;
+//                     case 2:
+//                             $title = Title::newFromText( $req->getVal( 
"wpevalPage" ) );
+//                             $this->setPage( $title );
+//                             $this->displaySelectFormulaForm();
+//                             break;
+//                     case 3;
+//                             switch ( $req->getInt( 'wpsnippetSelector' ) ){
+//                                     case self::OPT_BACK;
+//                                             $this->step = 1;
+//                                             $this->runStep( $req );
+//                                             break;
+//                                     case self::OPT_RETRY:
+//                                             $this->step = 2;
+//                                             $this->runStep( $req );
+//                                             break;
+//                                     case self::OPT_CONTINUE:
+//                                             $this->displayEvaluationForm();
+//                                             
$this->getOutput()->addWikiText( "You made it. All done!".
+//                                                     $req->getVal( 
'wpsnippetSelector' ) );
+//                                             break;
+//                                     default:
+//                             }
+//                             break;
+//             }
+//
+//     }
+}
diff --git a/includes/special/SpecialMlpEval.php 
b/includes/special/SpecialMlpEval.php
index e02f151..c855215 100644
--- a/includes/special/SpecialMlpEval.php
+++ b/includes/special/SpecialMlpEval.php
@@ -12,93 +12,96 @@
  * @ingroup extensions
  */
 class SpecialMlpEval extends SpecialPage {
+       private $selectedMathTag;
+       /**
+        *
+        */
        const MAX_ATTEMPTS = 10;
-       const WINDOW_SIZE = 1200;
-       const OPT_CONTINUE = 0;
-       const OPT_BACK = 1;
-       const OPT_RETRY = 2;
+       /**
+        * @var
+        */
        private $step;
-       private $htmlForm;
+       /**
+        * @var MathIdGenerator
+        */
+       private $mathIdGen;
        /**
         * @var Title
         */
        private $title;
+       /**
+        * @var
+        */
        private $wikitext;
+       /**
+        * @var
+        */
        private $snippet;
+       /**
+        * @var
+        */
        private $mathTags;
-       private $revison;
+       /**
+        * @var
+        */
+       private $oldId;
+       /**
+        * @var bool
+        */
        private $lastError = false;
+       /**
+        * @var
+        */
+       private $fId;
 
        function __construct() {
                parent::__construct( 'MlpEval' );
        }
 
+       private function setStep($step){
+               $this->step = $step;
+               return $step;
+       }
+
+       private function loadData() {
+               $req = $this->getRequest();
+               $revId = $req->getInt( 'oldId' );
+               if ( $revId === 0 ) {
+                       return $this->setStep( 1 );
+               }
+               if ( $this->setRevision( $revId ) === false ) {
+                       return $this->setStep( 1 );
+               }
+               $fId = $req->getText( 'fId' );
+               if ( $fId === '' ) {
+                       return $this->setStep( 2 );
+               }
+               if ( $this->setFId( $fId ) === false ) {
+                       return $this->setStep( 2 );
+               }
+               return $this->setStep( 3 );
+       }
        /**
         * The main function
         */
        public function execute( $par ) {
-               $req = $this->getRequest();
+               $this->loadData();
                $this->setHeaders();
-               $this->step = $req->getInt( 'step', 1 );
-               $this->runStep( $req );
-               $this->printSource( var_export( $req, true ) );
+               $form = new MlpEvalForm($this);
+               $form->prepareForm();
+               $form->show();
+               $this->printSource( var_export( $this->getStep(), true ) );
+               $this->printSource( var_export( $this->getRequest(), true ) );
+               return;
        }
 
-       /**
-        * @param $req
-        */
-       private function runStep( WebRequest $req ) {
-               switch ( $this->step ) {
-                       case 1:
-                               $this->displayPageSelectionForm();
-                               break;
-                       case 2:
-                               $title = Title::newFromText( $req->getVal( 
"wpevalPage" ) );
-                               $this->setPage( $title );
-                               $this->displaySelectFormulaForm();
-                               break;
-                       case 3;
-                               switch ( $req->getInt( 'wpsnippetSelector' ) ){
-                                       case self::OPT_BACK;
-                                               $this->step = 1;
-                                               $this->runStep( $req );
-                                               break;
-                                       case self::OPT_RETRY:
-                                               $this->step = 2;
-                                               $this->runStep( $req );
-                                               break;
-                                       case self::OPT_CONTINUE:
-                                               $this->displayEvaluationForm();
-                                               
$this->getOutput()->addWikiText( "You made it. All done!".
-                                                       $req->getVal( 
'wpsnippetSelector' ) );
-                                               break;
-                                       default:
-                               }
-                               break;
-               }
-
+       public function getStep() {
+               return $this->step;
        }
 
-       private function displayPageSelectionForm() {
-               $formDescriptor = array();
-               $this->getOutput()->addModules( 'ext.MathSearch.special' );
-               $formDescriptor['evalPage'] = array(
-                               'label'   => 'Page to evaluate',
-                               'class'   => 'HTMLTextField',
-                               'default' => $this->getRandomPage()
-               );
 
-               $htmlForm = new HTMLForm( $formDescriptor, $this->getContext() 
);
-               $htmlForm->setSubmitText( 'Select' );
-               $htmlForm->addHiddenField( 'step', 2 );
-               $htmlForm->setSubmitCallback( array( $this, 'processPageInput' 
) );
-               $htmlForm->setHeaderText( "<h2>Step 1: Select a page</h2>" );
-               $htmlForm->prepareForm();
-               $htmlForm->displayForm( '' );
 
-       }
-
-       private function getRandomPage() {
+       public function getRandomPage() {
                $rp = new RandomPage();
                for ( $i = 0; $i < self::MAX_ATTEMPTS; $i ++ ) {
                        $title = $rp->getRandomTitle();
@@ -107,7 +110,7 @@
                                return $title;
                        }
                }
-               $this->log()->warning( "Could not find suitable Page with 
math", $this->lastError );
+               $this->log()->warning( "Could not find suitable page with 
math:". $this->lastError  );
                $this->lastError = "";
                return "";
        }
@@ -139,15 +142,26 @@
         * @return bool
         * @throws MWException
         */
-       private function setRevision( $revId ) {
-               $idGenerator = MathIdGenerator::newFromRevisionId( $revId );
-               $mathTags = $idGenerator->getMathTags();
+       private function setRevision( $revId = 0 ) {
+               if ( $revId == 0 && $this->oldId > 0 ){
+                       $revId = $this->oldId;
+               } else {
+                       $this->oldId = $revId;
+               }
+               if ( $revId == 0 ){
+                       $this->lastError = "no revision id given";
+                       return false;
+               }
+               $gen = MathIdGenerator::newFromRevisionId( $revId );
+               $mathTags = $gen->getMathTags();
                $tagCount = count( $mathTags );
                if ( $tagCount == 0 ) {
                        $this->lastError = "has no math tags";
                        return false;
                }
-               $this->wikitext = $idGenerator->getWikiText();
+               $this->mathIdGen = $gen;
+               $this->title = Revision::newFromId($revId)->getTitle();
+               $this->wikitext = $this->mathIdGen->getWikiText();
                $this->mathTags = $mathTags;
                return true;
 
@@ -163,25 +177,19 @@
        private function displaySelectFormulaForm() {
                $formDescriptor = array();
                $this->enableMathStyles();
-               $formDescriptor['snippetSelector'] = array(
-                               'type' => 'radio',
-                               'label' => 'Page to evaluate',
-                               'options' => array(
-                                               'Continue with this snippet' => 
self::OPT_CONTINUE,
-                                               'Select another snippet from 
that page' => self::OPT_RETRY,
-                                               'Go Back to page selection' => 
self::OPT_BACK
-                               ),
-                               'default' => self::OPT_CONTINUE # The option 
selected by default (identified by value)
-               );
+
                $htmlForm = new HTMLForm( $formDescriptor, $this->getContext() 
);
                $htmlForm->setSubmitText( 'Continue' );
                $htmlForm->addHiddenField( 'step', 3 );
-               $htmlForm->addHiddenField( 'wpevalPage', 
$this->title->getText() );
+//             $htmlForm->addHiddenField( 'wpevalPage', 
$this->title->getText() );
                $htmlForm->setSubmitCallback( array( $this, 
'processFormulaInput' ) );
                $htmlForm->setHeaderText( "<h2>Step 2: Select a formula</h2>" );
                $htmlForm->prepareForm();
                $htmlForm->displayForm( '' );
-               $this->displaySnipped();
+               $this->fId = $this->getRandomFId();
+               $this->printSource($this->fId);
+               $hl = new MathHighlighter($this->fId,$this->oldId);
+               $this->getOutput()->addWikiText($hl->getWikiText());
        }
 
        private function enableMathStyles() {
@@ -191,74 +199,9 @@
                );
        }
 
-       /**
-        * @return array
-        * @throws MWException
-        */
-       private function displaySnipped() {
-               $tagCount = count( $this->mathTags );
-               $this->getOutput()->addWikiText( "Found $tagCount math tags." );
-               $unique = array_rand( $this->mathTags );
-               $tag = $this->mathTags[$unique];
-               $formDescriptor = array();
-               $this->getOutput()->addWikiText( $tag[3] );
-               $tagPos = strpos( $this->wikitext, $unique );
-               $wikiText = $this->wikitext;
-               $startPos = $this->getStartPos( $tagPos, $wikiText );
-               $length = $this->getEndPos( $tagPos, $wikiText ) - $startPos;
-               $wikiText = substr( $wikiText, $startPos, $length );
-               $wikiText = str_replace( $unique,
-                       '<span id="theelement" style="background-color: 
yellow">' . $tag[3] . '</span>',
-                       $wikiText );
-               foreach ( $this->mathTags as $key => $content ) {
-                       $wikiText = str_replace( $key, $content[3], $wikiText );
-               }
-               $this->snippet = "== Extract ==\nStart of the 
extract...\n\n$wikiText\n\n...end of the extract";
-               $this->getOutput()->addWikiText( $this->snippet );
-               $url = $this->title->getLinkURL();
-               $this->getOutput()
-                       ->addHTML( "<a href=\"$url\" target=\"_blank\">Full 
article (new Window)</a>" );
-               return array( $tagCount, $formDescriptor, $wikiText );
-       }
 
-       /**
-        * @param $tagPos
-        * @param $wikiText
-        * @return array
-        */
-       private function getStartPos( $tagPos, $wikiText ) {
-               $startPos = max( $tagPos - round( self::WINDOW_SIZE / 2 ), 0 );
-               if ( $startPos > 0 ) {
-                       // Heuristics to find a reasonable cutting point
-                       $newPos = strpos( $wikiText, "\n", $startPos );
-                       if ( $newPos !== false && ( $newPos - $startPos ) < 
round( self::WINDOW_SIZE / 4 ) ) {
-                               // only change startPos, if it seems reasonable
-                               $startPos = $newPos;
-                       }
-               }
-               return $startPos;
-       }
 
-       /**
-        * @param $wikiText
-        * @param $tagPos
-        * @return bool|int|mixed
-        */
-       private function getEndPos( $tagPos, $wikiText ) {
-               $halfWindow = round( self::WINDOW_SIZE / 2 );
-               $distance2End = strlen( $wikiText ) - $tagPos;
-               if ( $distance2End > $halfWindow ) {
-                       $newPos = strpos( $wikiText, "\n", $tagPos + 
$halfWindow );
-                       if ( $newPos !== false && ( $newPos - $tagPos ) < 
round( 3 / 4 * self::WINDOW_SIZE ) ) {
-                               // only change startPos, if it seems reasonable
-                               return $newPos;
-                       } else {
-                               return $tagPos + $halfWindow;
-                       }
-               } else {
-                       return strlen( $wikiText );
-               }
-       }
+
 
        /**
         *
@@ -270,43 +213,85 @@
                $out->addWikiText( '<source lang="' . $lang . '">' . $src . 
'</source>' );
        }
 
-       /**
-        * Processes the submitted Form input
-        * @return true
-        */
-       public function processFormulaInput() {
-               return true;
-       }
-
-       /**
-        * Processes the submitted Form input
-        * @param array $formData
-        * @return bool
-        */
-       public function processPageInput( $formData ) {
-               if ( gettype( $formData['evalPage'] ) !== "string" ) {
-                       return "Please select a page";
-               }
-               $title = Title::newFromText( $formData['evalPage'] );
-               if ( $this->setPage( $title ) ) {
-                       return true;
-               } else {
-                       return $this->lastError;
-               }
-       }
-
-       public function performSearch() {
-               $out = $this->getOutput();
-               $out->addWikiText( '==Results==' );
-               $out->addWikiText( 'You searched for the following terms:' );
-               return false;
-       }
-
        protected function getGroupName() {
                return 'mathsearch';
        }
 
-       private function displayEvaluationForm() {
+
+       private function setFId($fId = '') {
+               if ( $fId = '' && $this->fId != '' ){
+                       $fId = $this->fId;
+               } else {
+                       $this->fId = $fId;
+               }
+
+               $this->selectedMathTag = $this->mathIdGen->getTagFromId($fId);
        }
 
+       /**
+        * @return array
+        * @throws MWException
+        */
+       private function getRandomFId() {
+               $unique = array_rand( $this->mathTags );
+               return $this->mathIdGen->parserKey2fId( $unique );
+       }
+
+       public function filterPage($simpleTextField, $allData){
+               $allData['revisionId']='HERE AM I';
+               return $simpleTextField;
+       }
+
+       /**
+        * @return mixed
+        */
+       public function getFId() {
+               return $this->fId;
+       }
+
+       /**
+        * @return mixed
+        */
+       public function getOldId() {
+               return $this->oldId;
+       }
+
+       /**
+        * @return Title
+        */
+       public function getTitle() {
+               return $this->title;
+       }
+/*
+ *
+ $out = $this->getOutput();
+
+               $revId = $req->getInt('oldId');
+               if ( $revId ){
+                       if ( $this->setRevision($revId) ) {
+                               $out->addWikiText("Step 1 completed");
+                               $out->addWikiText("You selected $revId" );
+                       }
+               }
+
+
+
+               else {
+               }
+               if( ! $revId ){
+                       if( $req->getText('wpevalPage','') ){
+                               if ( 
$this->setPage(Title::newFromText($req->getText('wpevalPage',''))) );
+                               $this->printSource("revid".$this->oldId);
+                       }
+               }
+               $out->addWikiText( "== Step 1: Select a page ==" );
+
+               $fId = $req->getInt('fId');
+               if ( $fId ){
+                       $this->setFId($fId);
+               }
+               $this->step = $req->getInt( 'step', 1 );
+               $this->runStep( $req );
+               $this->printSource( var_export( $req, true ) );
+ */
 }
diff --git a/modules/ext.MathSearch.autocomplete.js 
b/modules/ext.MathSearch.autocomplete.js
deleted file mode 100644
index 9edea8f..0000000
--- a/modules/ext.MathSearch.autocomplete.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Add autocomplete suggestions to any input.
- * @author Legoktm
- * Mainly from http://jqueryui.com/autocomplete/
- * and resources/mediawiki/mediawiki.searchSuggest.js
- */
-
-( function ( mw, $ ) {
-    $( function () {
-        // mw-massmessage-form-spamlist is the id of the field to autocomplete
-        $( '#mw-input-wpevalPage' ).autocomplete( {
-            source: function( request, response ) {
-                // Create a new Api object (see [[RL/DM#mediawiki.api]]
-                var api = new mw.Api();
-                // Start a "GET" request
-                api.get( {
-                    action: 'opensearch',
-                    search: request.term, // This is the current value of the 
user's input
-                    suggest: ''
-                } ).done( function ( data ) {
-                    response( data[1] ); // set the results as the 
autocomplete options
-                } );
-            }
-        } );
-    } );
-}( mediaWiki, jQuery ) );
\ No newline at end of file

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I84533407b9db6062a5c01c6760e7173dbd95a870
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MathSearch
Gerrit-Branch: master
Gerrit-Owner: Physikerwelt <[email protected]>

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

Reply via email to