Merge "Remove __METHOD__ in ServiceWiring return array"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 1 Oct 2018 19:43:43 +0000 (19:43 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 1 Oct 2018 19:43:43 +0000 (19:43 +0000)
42 files changed:
CODE_OF_CONDUCT.md
RELEASE-NOTES-1.32
docs/hooks.txt
includes/content/WikiTextStructure.php
includes/htmlform/fields/HTMLInfoField.php
includes/installer/i18n/en.json
includes/installer/i18n/qqq.json
includes/jobqueue/JobRunner.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/DatabaseSqlite.php
includes/parser/RemexStripTagHandler.php
includes/parser/Sanitizer.php
includes/user/User.php
languages/classes/LanguageKk.php
languages/classes/LanguageKu.php
languages/messages/MessagesAr.php
languages/messages/MessagesAs.php
languages/messages/MessagesBho.php
languages/messages/MessagesBo.php
languages/messages/MessagesCkb.php
languages/messages/MessagesDz.php
languages/messages/MessagesFa.php
languages/messages/MessagesGu.php
languages/messages/MessagesHi.php
languages/messages/MessagesKk_arab.php
languages/messages/MessagesKm.php
languages/messages/MessagesKn.php
languages/messages/MessagesKs_arab.php
languages/messages/MessagesKs_deva.php
languages/messages/MessagesKu_arab.php
languages/messages/MessagesLo.php
languages/messages/MessagesMr.php
languages/messages/MessagesNe.php
languages/messages/MessagesNew.php
languages/messages/MessagesOr.php
languages/messages/MessagesPi.php
languages/messages/MessagesPs.php
languages/messages/MessagesSa.php
languages/messages/MessagesSkr_arab.php
maintenance/includes/DeleteLocalPasswords.php
tests/phpunit/includes/content/WikitextStructureTest.php
tests/phpunit/includes/parser/SanitizerTest.php

index d8e5d08..498acf7 100644 (file)
@@ -1 +1 @@
-The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Code_of_Conduct).
+The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Special:MyLanguage/Code_of_Conduct).
index f50ab00..d875017 100644 (file)
@@ -93,6 +93,9 @@ production.
   This action should be considered deprecated and should not be used directly.
 * Extensions overriding ContentHandler::getUndoContent() will need to be
   updated for the changed method signature.
+* Added a new hook, 'UserGetRightsRemove', which can be used to remove rights
+  from user. Unlike the 'UserGetRights' it will ensure that removed rights
+  will not be reinserted.
 
 === External library changes in 1.32 ===
 
index 063bbe5..d82f56e 100644 (file)
@@ -3783,6 +3783,13 @@ $context: IContextSource object
 $user: User to get rights for
 &$rights: Current rights
 
+'UserGetRightsRemove': Called in User::getRights(). This hook override
+the UserGetRights hook. It can be used to remove rights from user
+and ensure that will not be reinserted by the other hook callbacks
+therefore this hook should not be used to add any rights, use UserGetRights instead.
+$user: User to get rights for
+&$rights: Current rights
+
 'UserGroupsChanged': Called after user groups are changed.
 $user: User whose groups changed
 $added: Groups added
index 1128d7b..a82ffa1 100644 (file)
@@ -161,10 +161,6 @@ class WikiTextStructure {
 
                $this->openingText = $this->extractHeadingBeforeFirstHeading( $text );
 
-               // Add extra spacing around break tags so text crammed together like<br>this
-               // doesn't make one word.
-               $text = str_replace( '<br', "\n<br", $text );
-
                $formatter = new HtmlFormatter( $text );
 
                // Strip elements from the page that we never want in the search text.
index 1376d0c..a98f112 100644 (file)
@@ -75,6 +75,22 @@ class HTMLInfoField extends HTMLFormField {
                return parent::getRaw( $value );
        }
 
+       /**
+        * @param mixed $value
+        * @return OOUI\FieldLayout
+        * @since 1.32
+        */
+       public function getOOUI( $value ) {
+               if ( !empty( $this->mParams['rawrow'] ) ) {
+                       if ( !( $value instanceof OOUI\FieldLayout ) ) {
+                               throw new Exception( "'default' must be a FieldLayout or subclass when using 'rawrow'" );
+                       }
+                       return $value;
+               }
+
+               return parent::getOOUI( $value );
+       }
+
        protected function needsLabel() {
                return false;
        }
index 87bf792..c89be17 100644 (file)
        "config-install-mainpage-failed": "Could not insert main page: $1",
        "config-install-done": "<strong>Congratulations!</strong>\nYou have installed MediaWiki.\n\nThe installer has generated a <code>LocalSettings.php</code> file.\nIt contains all your configuration.\n\nYou will need to download it and put it in the base of your wiki installation (the same directory as index.php). The download should have started automatically.\n\nIf the download was not offered, or if you cancelled it, you can restart the download by clicking the link below:\n\n$3\n\n<strong>Note:</strong> If you do not do this now, this generated configuration file will not be available to you later if you exit the installation without downloading it.\n\nWhen that has been done, you can <strong>[$2 enter your wiki]</strong>.",
        "config-install-done-path": "<strong>Congratulations!</strong>\nYou have installed MediaWiki.\n\nThe installer has generated a <code>LocalSettings.php</code> file.\nIt contains all your configuration.\n\nYou will need to download it and put it at <code>$4</code>. The download should have started automatically.\n\nIf the download was not offered, or if you cancelled it, you can restart the download by clicking the link below:\n\n$3\n\n<strong>Note:</strong> If you do not do this now, this generated configuration file will not be available to you later if you exit the installation without downloading it.\n\nWhen that has been done, you can <strong>[$2 enter your wiki]</strong>.",
-       "config-install-success": "MediaWiki has been successfully installed. You can now\nvisit <$1$2> to view your wiki.\nIf you have questions, check out our frequently asked questions list:\n<https://www.mediawiki.org/wiki/Manual:FAQ> or use one of the\nsupport forums linked on that page.",
+       "config-install-success": "MediaWiki has been successfully installed. You can now visit <$1$2> to view your wiki.\nIf you have questions, check out our frequently asked questions list:\n<https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ> or use one of the\nsupport forums linked on that page.",
        "config-download-localsettings": "Download <code>LocalSettings.php</code>",
        "config-help": "help",
        "config-help-tooltip": "click to expand",
index c868169..e423bcd 100644 (file)
@@ -20,7 +20,8 @@
                        "Jdforrester",
                        "Liuxinyu970226",
                        "Metalhead64",
-                       "Tacsipacsi"
+                       "Tacsipacsi",
+                       "Zoranzoki21"
                ]
        },
        "config-desc": "Short description of the installer.",
        "config-unknown-collation": "Warning messages in the MediaWiki installer for the database type MySQL when an unrecognised collation is used.",
        "config-db-web-account": "Fieldset legend in MediaWiki installer",
        "config-db-web-help": "Help text in MediaWiki installer.",
-       "config-db-web-account-same": "checkbox label",
-       "config-db-web-create": "checkbox label",
+       "config-db-web-account-same": "Checkbox label about setting of database account for web access.",
+       "config-db-web-create": "Checkbox label about creating of new database account.",
        "config-db-web-no-create-privs": "Error message in the MediaWiki installer.",
        "config-mysql-engine": "Field label for MySQL storage engine in the MediaWiki installer.",
        "config-mysql-innodb": "Option for the MySQL storage engine in the MediaWiki installer.",
index dab9b14..1e83167 100644 (file)
@@ -576,12 +576,12 @@ class JobRunner implements LoggerAwareInterface {
                $this->debugCallback( $msg );
 
                // Wait for an exclusive lock to commit
-               if ( !$dbwSerial->lock( 'jobrunner-serial-commit', __METHOD__, 30 ) ) {
+               if ( !$dbwSerial->lock( 'jobrunner-serial-commit', $fnameTrxOwner, 30 ) ) {
                        // This will trigger a rollback in the main loop
                        throw new DBError( $dbwSerial, "Timed out waiting on commit queue." );
                }
-               $unlocker = new ScopedCallback( function () use ( $dbwSerial ) {
-                       $dbwSerial->unlock( 'jobrunner-serial-commit', __METHOD__ );
+               $unlocker = new ScopedCallback( function () use ( $dbwSerial, $fnameTrxOwner ) {
+                       $dbwSerial->unlock( 'jobrunner-serial-commit', $fnameTrxOwner );
                } );
 
                // Wait for the replica DBs to catch up
index e276d09..f37364f 100644 (file)
@@ -1020,13 +1020,35 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        abstract protected function doQuery( $sql );
 
        /**
-        * Determine whether a query writes to the DB.
-        * Should return true if unsure.
+        * Determine whether a query writes to the DB. When in doubt, this returns true.
+        *
+        * Main use cases:
+        *
+        * - Subsequent web requests should not need to wait for replication from
+        *   the master position seen by this web request, unless this request made
+        *   changes to the master. This is handled by ChronologyProtector by checking
+        *   doneWrites() at the end of the request. doneWrites() returns true if any
+        *   query set lastWriteTime; which query() does based on isWriteQuery().
+        *
+        * - Reject write queries to replica DBs, in query().
         *
         * @param string $sql
         * @return bool
         */
        protected function isWriteQuery( $sql ) {
+               // BEGIN and COMMIT queries are considered read queries here.
+               // Database backends and drivers (MySQL, MariaDB, php-mysqli) generally
+               // treat these as write queries, in that their results have "affected rows"
+               // as meta data as from writes, instead of "num rows" as from reads.
+               // But, we treat them as read queries because when reading data (from
+               // either replica or master) we use transactions to enable repeatable-read
+               // snapshots, which ensures we get consistent results from the same snapshot
+               // for all queries within a request. Use cases:
+               // - Treating these as writes would trigger ChronologyProtector (see method doc).
+               // - We use this method to reject writes to replicas, but we need to allow
+               //   use of transactions on replicas for read snapshots. This fine given
+               //   that transactions by themselves don't make changes, only actual writes
+               //   within the transaction matter, which we still detect.
                return !preg_match(
                        '/^(?:SELECT|BEGIN|ROLLBACK|COMMIT|SET|SHOW|EXPLAIN|\(SELECT)\b/i', $sql );
        }
@@ -1041,17 +1063,21 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
        /**
         * Determine whether a SQL statement is sensitive to isolation level.
+        *
         * A SQL statement is considered transactable if its result could vary
         * depending on the transaction isolation level. Operational commands
         * such as 'SET' and 'SHOW' are not considered to be transactable.
         *
+        * Main purpose: Used by query() to decide whether to begin a transaction
+        * before the current query (in DBO_TRX mode, on by default).
+        *
         * @param string $sql
         * @return bool
         */
        protected function isTransactableQuery( $sql ) {
                return !in_array(
                        $this->getQueryVerb( $sql ),
-                       [ 'BEGIN', 'COMMIT', 'ROLLBACK', 'SHOW', 'SET', 'CREATE', 'ALTER' ],
+                       [ 'BEGIN', 'ROLLBACK', 'COMMIT', 'SET', 'SHOW', 'CREATE', 'ALTER' ],
                        true
                );
        }
index c8edc39..9d5eca6 100644 (file)
@@ -297,7 +297,7 @@ class DatabaseSqlite extends Database {
                return $this->query( "ATTACH DATABASE $file AS $name", $fname );
        }
 
-       function isWriteQuery( $sql ) {
+       protected function isWriteQuery( $sql ) {
                return parent::isWriteQuery( $sql ) && !preg_match( '/^(ATTACH|PRAGMA)\b/i', $sql );
        }
 
index 2839147..41c6bf4 100644 (file)
@@ -26,10 +26,18 @@ class RemexStripTagHandler implements TokenHandler {
                $this->text .= substr( $text, $start, $length );
        }
        function startTag( $name, Attributes $attrs, $selfClose, $sourceStart, $sourceLength ) {
-               // Do nothing.
+               // Inject whitespace for typical block-level tags to
+               // prevent merging unrelated<br>words.
+               if ( $this->isBlockLevelTag( $name ) ) {
+                       $this->text .= ' ';
+               }
        }
        function endTag( $name, $sourceStart, $sourceLength ) {
-               // Do nothing.
+               // Inject whitespace for typical block-level tags to
+               // prevent merging unrelated<br>words.
+               if ( $this->isBlockLevelTag( $name ) ) {
+                       $this->text .= ' ';
+               }
        }
        function doctype( $name, $public, $system, $quirks, $sourceStart, $sourceLength ) {
                // Do nothing.
@@ -37,4 +45,63 @@ class RemexStripTagHandler implements TokenHandler {
        function comment( $text, $sourceStart, $sourceLength ) {
                // Do nothing.
        }
+
+       // Per https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements
+       // retrieved on sept 12, 2018. <br> is not block level but was added anyways.
+       // The following is a complete list of all HTML block level elements
+       // (although "block-level" is not technically defined for elements that are
+       // new in HTML5).
+       // Structured as tag => true to allow O(1) membership test.
+       static private $BLOCK_LEVEL_TAGS = [
+               'address' => true,
+               'article' => true,
+               'aside' => true,
+               'blockquote' => true,
+               'br' => true,
+               'canvas' => true,
+               'dd' => true,
+               'div' => true,
+               'dl' => true,
+               'dt' => true,
+               'fieldset' => true,
+               'figcaption' => true,
+               'figure' => true,
+               'figcaption' => true,
+               'footer' => true,
+               'form' => true,
+               'h1' => true,
+               'h2' => true,
+               'h3' => true,
+               'h4' => true,
+               'h5' => true,
+               'h6' => true,
+               'header' => true,
+               'hgroup' => true,
+               'hr' => true,
+               'li' => true,
+               'main' => true,
+               'nav' => true,
+               'noscript' => true,
+               'ol' => true,
+               'output' => true,
+               'p' => true,
+               'pre' => true,
+               'section' => true,
+               'table' => true,
+               'tfoot' => true,
+               'ul' => true,
+               'video' => true,
+       ];
+
+       /**
+        * Detect block level tags. Of course css can make anything a block
+        * level tag, but this is still better than nothing.
+        *
+        * @param string $tagName HTML tag name
+        * @return bool True when tag is an html block level element
+        */
+       private function isBlockLevelTag( $tagName ) {
+               $key = strtolower( trim( $tagName ) );
+               return isset( self::$BLOCK_LEVEL_TAGS[$key] );
+       }
 }
index d885e24..85c71ee 100644 (file)
@@ -1508,10 +1508,10 @@ class Sanitizer {
         * @return string
         */
        private static function normalizeWhitespace( $text ) {
-               return preg_replace(
-                       '/\r\n|[\x20\x0d\x0a\x09]/',
+               return trim( preg_replace(
+                       '/(?:\r\n|[\x20\x0d\x0a\x09])+/',
                        ' ',
-                       $text );
+                       $text ) );
        }
 
        /**
index 89fd99c..a29fbf8 100644 (file)
@@ -3540,6 +3540,7 @@ class User implements IDBAccessObject, UserIdentity {
                                }
                        }
 
+                       Hooks::run( 'UserGetRightsRemove', [ $this, &$this->mRights ] );
                        // Force reindexation of rights when a hook has unset one of them
                        $this->mRights = array_values( array_unique( $this->mRights ) );
 
index 4184508..ea35ac8 100644 (file)
@@ -166,23 +166,23 @@ class KkConverter extends LanguageConverter {
 
                $this->mCyLa2Arab = [
                        # # Punctuation -> Arabic
-                       '/#|№|No\./u' => '؀', # &#x0600;
-                       '/\,/' => '،', # &#x060C;
-                       '/;/' => '؛', # &#x061B;
-                       '/\?/' => '؟', # &#x061F;
-                       '/%/' => '٪', # &#x066A;
-                       '/\*/' => '٭', # &#x066D;
+                       '/#|№|No\./u' => '؀', # U+0600
+                       '/\,/' => '،', # U+060C
+                       '/;/' => '؛', # U+061B
+                       '/\?/' => '؟', # U+061F
+                       '/%/' => '٪', # U+066A
+                       '/\*/' => '٭', # U+066D
                        # # Digits -> Arabic
-                       '/0/' => '۰', # &#x06F0;
-                       '/1/' => '۱', # &#x06F1;
-                       '/2/' => '۲', # &#x06F2;
-                       '/3/' => '۳', # &#x06F3;
-                       '/4/' => '۴', # &#x06F4;
-                       '/5/' => '۵', # &#x06F5;
-                       '/6/' => '۶', # &#x06F6;
-                       '/7/' => '۷', # &#x06F7;
-                       '/8/' => '۸', # &#x06F8;
-                       '/9/' => '۹', # &#x06F9;
+                       '/0/' => '۰', # U+06F0
+                       '/1/' => '۱', # U+06F1
+                       '/2/' => '۲', # U+06F2
+                       '/3/' => '۳', # U+06F3
+                       '/4/' => '۴', # U+06F4
+                       '/5/' => '۵', # U+06F5
+                       '/6/' => '۶', # U+06F6
+                       '/7/' => '۷', # U+06F7
+                       '/8/' => '۸', # U+06F8
+                       '/9/' => '۹', # U+06F9
                        # # Cyrillic -> Arabic
                        '/Аллаһ/ui' => 'ﷲ',
                        '/([АӘЕЁИОӨҰҮЭЮЯЪЬ])е/ui' => '$1يە',
index b90ca41..580f64a 100644 (file)
@@ -57,16 +57,16 @@ class KuConverter extends LanguageConverter {
                '؟' => '?',
 
                # digits
-               '٠' => '0', # &#x0660;
-               '١' => '1', # &#x0661;
-               '٢' => '2', # &#x0662;
-               '٣' => '3', # &#x0663;
-               '٤' => '4', # &#x0664;
-               '٥' => '5', # &#x0665;
-               '٦' => '6', # &#x0666;
-               '٧' => '7', # &#x0667;
-               '٨' => '8', # &#x0668;
-               '٩' => '9', # &#x0669;
+               '٠' => '0', # U+0660
+               '١' => '1', # U+0661
+               '٢' => '2', # U+0662
+               '٣' => '3', # U+0663
+               '٤' => '4', # U+0664
+               '٥' => '5', # U+0665
+               '٦' => '6', # U+0666
+               '٧' => '7', # U+0667
+               '٨' => '8', # U+0668
+               '٩' => '9', # U+0669
        ];
 
        public $mLatinToArabic = [
@@ -130,16 +130,16 @@ class KuConverter extends LanguageConverter {
 
 /*             # deactivated for now, breaks links i.e. in header of Special:Recentchanges :-(
                # digits
-               '0' => '٠', # &#x0660;
-               '1' => '١', # &#x0661;
-               '2' => '٢', # &#x0662;
-               '3' => '٣', # &#x0663;
-               '4' => '٤', # &#x0664;
-               '5' => '٥', # &#x0665;
-               '6' => '٦', # &#x0666;
-               '7' => '٧', # &#x0667;
-               '8' => '٨', # &#x0668;
-               '9' => '٩', # &#x0669;
+               '0' => '٠', # U+0660
+               '1' => '١', # U+0661
+               '2' => '٢', # U+0662
+               '3' => '٣', # U+0663
+               '4' => '٤', # U+0664
+               '5' => '٥', # U+0665
+               '6' => '٦', # U+0666
+               '7' => '٧', # U+0667
+               '8' => '٨', # U+0668
+               '9' => '٩', # U+0669
 */
                ];
 
index 3bc2245..60be21c 100644 (file)
@@ -86,21 +86,21 @@ $dateFormats = [
 ];
 
 $digitTransformTable = [
-       '0' => '٠', # &#x0660;
-       '1' => '١', # &#x0661;
-       '2' => '٢', # &#x0662;
-       '3' => '٣', # &#x0663;
-       '4' => '٤', # &#x0664;
-       '5' => '٥', # &#x0665;
-       '6' => '٦', # &#x0666;
-       '7' => '٧', # &#x0667;
-       '8' => '٨', # &#x0668;
-       '9' => '٩', # &#x0669;
+       '0' => '٠', # U+0660
+       '1' => '١', # U+0661
+       '2' => '٢', # U+0662
+       '3' => '٣', # U+0663
+       '4' => '٤', # U+0664
+       '5' => '٥', # U+0665
+       '6' => '٦', # U+0666
+       '7' => '٧', # U+0667
+       '8' => '٨', # U+0668
+       '9' => '٩', # U+0669
 ];
 
 $separatorTransformTable = [
-       '.' => '٫', # &#x066b;
-       ',' => '٬', # &#x066c;
+       '.' => '٫', # U+066B
+       ',' => '٬', # U+066C
 ];
 
 $namespaceNames = [
index 2516fc7..55a2323 100644 (file)
@@ -152,16 +152,16 @@ $magicWords = [
 ];
 
 $digitTransformTable = [
-       '0' => '০', # &#x09e6;
-       '1' => '১', # &#x09e7;
-       '2' => '২', # &#x09e8;
-       '3' => '৩', # &#x09e9;
-       '4' => '৪', # &#x09ea;
-       '5' => '৫', # &#x09eb;
-       '6' => '৬', # &#x09ec;
-       '7' => '৭', # &#x09ed;
-       '8' => '৮', # &#x09ee;
-       '9' => '৯', # &#x09ef;
+       '0' => '০', # U+09E6
+       '1' => '১', # U+09E7
+       '2' => '২', # U+09E8
+       '3' => '৩', # U+09E9
+       '4' => '৪', # U+09EA
+       '5' => '৫', # U+09EB
+       '6' => '৬', # U+09EC
+       '7' => '৭', # U+09ED
+       '8' => '৮', # U+09EE
+       '9' => '৯', # U+09EF
 ];
 
 $digitGroupingPattern = "##,##,###";
index 548a8d6..8ad1b1e 100644 (file)
@@ -52,14 +52,14 @@ $specialPageAliases = [
 ];
 
 $digitTransformTable = [
-       '0' => '०', # &#x0966;
-       '1' => '१', # &#x0967;
-       '2' => '२', # &#x0968;
-       '3' => '३', # &#x0969;
-       '4' => '४', # &#x096a;
-       '5' => '५', # &#x096b;
-       '6' => '६', # &#x096c;
-       '7' => '७', # &#x096d;
-       '8' => '८', # &#x096e;
-       '9' => '९', # &#x096f;
+       '0' => '०', # U+0966
+       '1' => '१', # U+0967
+       '2' => '२', # U+0968
+       '3' => '३', # U+0969
+       '4' => '४', # U+096A
+       '5' => '५', # U+096B
+       '6' => '६', # U+096C
+       '7' => '७', # U+096D
+       '8' => '८', # U+096E
+       '9' => '९', # U+096F
 ];
index 043f4d1..89cd03c 100644 (file)
@@ -9,14 +9,14 @@
  */
 
 $digitTransformTable = [
-       '0' => '༠', # &#x0f20;
-       '1' => '༡', # &#x0f21;
-       '2' => '༢', # &#x0f22;
-       '3' => '༣', # &#x0f23;
-       '4' => '༤', # &#x0f24;
-       '5' => '༥', # &#x0f25;
-       '6' => '༦', # &#x0f26;
-       '7' => '༧', # &#x0f27;
-       '8' => '༨', # &#x0f28;
-       '9' => '༩', # &#x0f29;
+       '0' => '༠', # U+0F20
+       '1' => '༡', # U+0F21
+       '2' => '༢', # U+0F22
+       '3' => '༣', # U+0F23
+       '4' => '༤', # U+0F24
+       '5' => '༥', # U+0F25
+       '6' => '༦', # U+0F26
+       '7' => '༧', # U+0F27
+       '8' => '༨', # U+0F28
+       '9' => '༩', # U+0F29
 ];
index e615462..a9131fd 100644 (file)
@@ -120,21 +120,21 @@ $magicWords = [
 ];
 
 $digitTransformTable = [
-       '0' => '٠', # &#x0660;
-       '1' => '١', # &#x0661;
-       '2' => '٢', # &#x0662;
-       '3' => '٣', # &#x0663;
-       '4' => '٤', # &#x0664;
-       '5' => '٥', # &#x0665;
-       '6' => '٦', # &#x0666;
-       '7' => '٧', # &#x0667;
-       '8' => '٨', # &#x0668;
-       '9' => '٩', # &#x0669;
+       '0' => '٠', # U+0660
+       '1' => '١', # U+0661
+       '2' => '٢', # U+0662
+       '3' => '٣', # U+0663
+       '4' => '٤', # U+0664
+       '5' => '٥', # U+0665
+       '6' => '٦', # U+0666
+       '7' => '٧', # U+0667
+       '8' => '٨', # U+0668
+       '9' => '٩', # U+0669
 ];
 
 $separatorTransformTable = [
-       '.' => '٫', # &#x066b;
-       ',' => '٬', # &#x066c;
+       '.' => '٫', # U+066B
+       ',' => '٬', # U+066C
 ];
 
 $datePreferences = [
index 50ae191..46015a8 100644 (file)
@@ -9,14 +9,14 @@
  */
 
 $digitTransformTable = [
-       '0' => '༠', # &#x0f20;
-       '1' => '༡', # &#x0f21;
-       '2' => '༢', # &#x0f22;
-       '3' => '༣', # &#x0f23;
-       '4' => '༤', # &#x0f24;
-       '5' => '༥', # &#x0f25;
-       '6' => '༦', # &#x0f26;
-       '7' => '༧', # &#x0f27;
-       '8' => '༨', # &#x0f28;
-       '9' => '༩', # &#x0f29;
+       '0' => '༠', # U+0F20
+       '1' => '༡', # U+0F21
+       '2' => '༢', # U+0F22
+       '3' => '༣', # U+0F23
+       '4' => '༤', # U+0F24
+       '5' => '༥', # U+0F25
+       '6' => '༦', # U+0F26
+       '7' => '༧', # U+0F27
+       '8' => '༨', # U+0F28
+       '9' => '༩', # U+0F29
 ];
index 4e10908..bda468c 100644 (file)
@@ -318,22 +318,22 @@ $magicWords = [
 ];
 
 $digitTransformTable = [
-       '0' => '۰', # &#x06f0;
-       '1' => '۱', # &#x06f1;
-       '2' => '۲', # &#x06f2;
-       '3' => '۳', # &#x06f3;
-       '4' => '۴', # &#x06f4;
-       '5' => '۵', # &#x06f5;
-       '6' => '۶', # &#x06f6;
-       '7' => '۷', # &#x06f7;
-       '8' => '۸', # &#x06f8;
-       '9' => '۹', # &#x06f9;
-       '%' => '٪', # &#x066a;
+       '0' => '۰', # U+06F0
+       '1' => '۱', # U+06F1
+       '2' => '۲', # U+06F2
+       '3' => '۳', # U+06F3
+       '4' => '۴', # U+06F4
+       '5' => '۵', # U+06F5
+       '6' => '۶', # U+06F6
+       '7' => '۷', # U+06F7
+       '8' => '۸', # U+06F8
+       '9' => '۹', # U+06F9
+       '%' => '٪', # U+066A
 ];
 
 $separatorTransformTable = [
-       '.' => '٫', # &#x066b;
-       ',' => '٬', # &#x066c;
+       '.' => '٫', # U+066B
+       ',' => '٬', # U+066C
 ];
 
 /**
index aec3a76..a011808 100644 (file)
@@ -112,16 +112,16 @@ $specialPageAliases = [
 ];
 
 $digitTransformTable = [
-       '0' => '૦', # &#x0ae6;
-       '1' => '૧', # &#x0ae7;
-       '2' => '૨', # &#x0ae8;
-       '3' => '૩', # &#x0ae9;
-       '4' => '૪', # &#x0aea;
-       '5' => '૫', # &#x0aeb;
-       '6' => '૬', # &#x0aec;
-       '7' => '૭', # &#x0aed;
-       '8' => '૮', # &#x0aee;
-       '9' => '૯', # &#x0aef;
+       '0' => '૦', # U+0AE6
+       '1' => '૧', # U+0AE7
+       '2' => '૨', # U+0AE8
+       '3' => '૩', # U+0AE9
+       '4' => '૪', # U+0AEA
+       '5' => '૫', # U+0AEB
+       '6' => '૬', # U+0AEC
+       '7' => '૭', # U+0AED
+       '8' => '૮', # U+0AEE
+       '9' => '૯', # U+0AEF
 ];
 
 $digitGroupingPattern = "##,##,###";
index 5a70a83..5bcad86 100644 (file)
@@ -285,16 +285,16 @@ $magicWords = [
 ];
 
 $digitTransformTable = [
-       '0' => '०', # &#x0966;
-       '1' => '१', # &#x0967;
-       '2' => '२', # &#x0968;
-       '3' => '३', # &#x0969;
-       '4' => '४', # &#x096a;
-       '5' => '५', # &#x096b;
-       '6' => '६', # &#x096c;
-       '7' => '७', # &#x096d;
-       '8' => '८', # &#x096e;
-       '9' => '९', # &#x096f;
+       '0' => '०', # U+0966
+       '1' => '१', # U+0967
+       '2' => '२', # U+0968
+       '3' => '३', # U+0969
+       '4' => '४', # U+096A
+       '5' => '५', # U+096B
+       '6' => '६', # U+096C
+       '7' => '७', # U+096D
+       '8' => '८', # U+096E
+       '9' => '९', # U+096F
 ];
 $linkTrail = "/^([a-z\x{0900}-\x{0963}\x{0966}-\x{A8E0}-\x{A8FF}]+)(.*)$/sDu";
 
index 6032773..2ff8c61 100644 (file)
@@ -17,21 +17,21 @@ $fallback = 'kk-cyrl';
 $rtl = true;
 
 $digitTransformTable = [
-       '0' => '۰', # &#x06f0;
-       '1' => '۱', # &#x06f1;
-       '2' => '۲', # &#x06f2;
-       '3' => '۳', # &#x06f3;
-       '4' => '۴', # &#x06f4;
-       '5' => '۵', # &#x06f5;
-       '6' => '۶', # &#x06f6;
-       '7' => '۷', # &#x06f7;
-       '8' => '۸', # &#x06f8;
-       '9' => '۹', # &#x06f9;
+       '0' => '۰', # U+06F0
+       '1' => '۱', # U+06F1
+       '2' => '۲', # U+06F2
+       '3' => '۳', # U+06F3
+       '4' => '۴', # U+06F4
+       '5' => '۵', # U+06F5
+       '6' => '۶', # U+06F6
+       '7' => '۷', # U+06F7
+       '8' => '۸', # U+06F8
+       '9' => '۹', # U+06F9
 ];
 
 $separatorTransformTable = [
-       '.' => '٫', # &#x066b;
-       ',' => '٬', # &#x066c;
+       '.' => '٫', # U+066B
+       ',' => '٬', # U+066C
 ];
 
 $fallback8bitEncoding = 'windows-1256';
index 299beb8..65577a9 100644 (file)
@@ -61,16 +61,16 @@ $namespaceAliases = [
 ];
 
 $digitTransformTable = [
-       '0' => '០', # &#x17e0;
-       '1' => '១', # &#x17e1;
-       '2' => '២', # &#x17e2;
-       '3' => '៣', # &#x17e3;
-       '4' => '៤', # &#x17e4;
-       '5' => '៥', # &#x17e5;
-       '6' => '៦', # &#x17e6;
-       '7' => '៧', # &#x17e7;
-       '8' => '៨', # &#x17e8;
-       '9' => '៩', # &#x17e9;
+       '0' => '០', # U+17E0
+       '1' => '១', # U+17E1
+       '2' => '២', # U+17E2
+       '3' => '៣', # U+17E3
+       '4' => '៤', # U+17E4
+       '5' => '៥', # U+17E5
+       '6' => '៦', # U+17E6
+       '7' => '៧', # U+17E7
+       '8' => '៨', # U+17E8
+       '9' => '៩', # U+17E9
 ];
 
 $separatorTransformTable = [
index 5fa77e9..0078261 100644 (file)
@@ -49,16 +49,16 @@ $namespaceNames = [
 ];
 
 $digitTransformTable = [
-       '0' => '೦', # &#x0ce6;
-       '1' => '೧', # &#x0ce7;
-       '2' => '೨', # &#x0ce8;
-       '3' => '೩', # &#x0ce9;
-       '4' => '೪', # &#x0cea;
-       '5' => '೫', # &#x0ceb;
-       '6' => '೬', # &#x0cec;
-       '7' => '೭', # &#x0ced;
-       '8' => '೮', # &#x0cee;
-       '9' => '೯', # &#x0cef;
+       '0' => '೦', # U+0CE6
+       '1' => '೧', # U+0CE7
+       '2' => '೨', # U+0CE8
+       '3' => '೩', # U+0CE9
+       '4' => '೪', # U+0CEA
+       '5' => '೫', # U+0CEB
+       '6' => '೬', # U+0CEC
+       '7' => '೭', # U+0CED
+       '8' => '೮', # U+0CEE
+       '9' => '೯', # U+0CEF
 ];
 
 $digitGroupingPattern = "##,##,###";
index 27ac9f1..f4ee3a0 100644 (file)
@@ -33,19 +33,19 @@ $namespaceNames = [
 ];
 
 $digitTransformTable = [
-       '0' => '٠', # &#x0660;
-       '1' => '١', # &#x0661;
-       '2' => '٢', # &#x0662;
-       '3' => '٣', # &#x0663;
-       '4' => '٤', # &#x0664;
-       '5' => '٥', # &#x0665;
-       '6' => '٦', # &#x0666;
-       '7' => '٧', # &#x0667;
-       '8' => '٨', # &#x0668;
-       '9' => '٩', # &#x0669;
+       '0' => '٠', # U+0660
+       '1' => '١', # U+0661
+       '2' => '٢', # U+0662
+       '3' => '٣', # U+0663
+       '4' => '٤', # U+0664
+       '5' => '٥', # U+0665
+       '6' => '٦', # U+0666
+       '7' => '٧', # U+0667
+       '8' => '٨', # U+0668
+       '9' => '٩', # U+0669
 ];
 
 $separatorTransformTable = [
-       '.' => '٫', # &#x066b;
-       ',' => '٬', # &#x066c;
+       '.' => '٫', # U+066B
+       ',' => '٬', # U+066C
 ];
index 55dc004..ad84b77 100644 (file)
@@ -30,14 +30,14 @@ $namespaceNames = [
 ];
 
 $digitTransformTable = [
-       '0' => '०', # &#x0966;
-       '1' => '१', # &#x0967;
-       '2' => '२', # &#x0968;
-       '3' => '३', # &#x0969;
-       '4' => '४', # &#x096a;
-       '5' => '५', # &#x096b;
-       '6' => '६', # &#x096c;
-       '7' => '७', # &#x096d;
-       '8' => '८', # &#x096e;
-       '9' => '९', # &#x096f;
+       '0' => '०', # U+0966
+       '1' => '१', # U+0967
+       '2' => '२', # U+0968
+       '3' => '३', # U+0969
+       '4' => '४', # U+096A
+       '5' => '५', # U+096B
+       '6' => '६', # U+096C
+       '7' => '७', # U+096D
+       '8' => '८', # U+096E
+       '9' => '९', # U+096F
 ];
index 0a80a62..4d53e36 100644 (file)
@@ -19,19 +19,19 @@ $fallback = 'ckb';
 $rtl = true;
 
 $digitTransformTable = [
-       '0' => '٠', # &#x0660;
-       '1' => '١', # &#x0661;
-       '2' => '٢', # &#x0662;
-       '3' => '٣', # &#x0663;
-       '4' => '٤', # &#x0664;
-       '5' => '٥', # &#x0665;
-       '6' => '٦', # &#x0666;
-       '7' => '٧', # &#x0667;
-       '8' => '٨', # &#x0668;
-       '9' => '٩', # &#x0669;
+       '0' => '٠', # U+0660
+       '1' => '١', # U+0661
+       '2' => '٢', # U+0662
+       '3' => '٣', # U+0663
+       '4' => '٤', # U+0664
+       '5' => '٥', # U+0665
+       '6' => '٦', # U+0666
+       '7' => '٧', # U+0667
+       '8' => '٨', # U+0668
+       '9' => '٩', # U+0669
 ];
 
 $separatorTransformTable = [
-       '.' => '٫', # &#x066b;
-       ',' => '٬', # &#x066c;
+       '.' => '٫', # U+066B
+       ',' => '٬', # U+066C
 ];
index 665b222..a3f29da 100644 (file)
@@ -80,14 +80,14 @@ $specialPageAliases = [
 ];
 
 $digitTransformTable = [
-       '0' => '໐', # &#x0ed0;
-       '1' => '໑', # &#x0ed1;
-       '2' => '໒', # &#x0ed2;
-       '3' => '໓', # &#x0ed3;
-       '4' => '໔', # &#x0ed4;
-       '5' => '໕', # &#x0ed5;
-       '6' => '໖', # &#x0ed6;
-       '7' => '໗', # &#x0ed7;
-       '8' => '໘', # &#x0ed8;
-       '9' => '໙', # &#x0ed9;
+       '0' => '໐', # U+0ED0
+       '1' => '໑', # U+0ED1
+       '2' => '໒', # U+0ED2
+       '3' => '໓', # U+0ED3
+       '4' => '໔', # U+0ED4
+       '5' => '໕', # U+0ED5
+       '6' => '໖', # U+0ED6
+       '7' => '໗', # U+0ED7
+       '8' => '໘', # U+0ED8
+       '9' => '໙', # U+0ED9
 ];
index c172d08..03e2f87 100644 (file)
@@ -311,16 +311,16 @@ $magicWords = [
 ];
 
 $digitTransformTable = [
-       '0' => '०', # &#x0966;
-       '1' => '१', # &#x0967;
-       '2' => '२', # &#x0968;
-       '3' => '३', # &#x0969;
-       '4' => '४', # &#x096a;
-       '5' => '५', # &#x096b;
-       '6' => '६', # &#x096c;
-       '7' => '७', # &#x096d;
-       '8' => '८', # &#x096e;
-       '9' => '९', # &#x096f;
+       '0' => '०', # U+0966
+       '1' => '१', # U+0967
+       '2' => '२', # U+0968
+       '3' => '३', # U+0969
+       '4' => '४', # U+096A
+       '5' => '५', # U+096B
+       '6' => '६', # U+096C
+       '7' => '७', # U+096D
+       '8' => '८', # U+096E
+       '9' => '९', # U+096F
 ];
 
 $linkTrail = "/^([\u{0900}-\u{0963}\u{0971}-\u{097F}\u{FEFF}\u{200D}]+)(.*)$/sDu";
index c6ab806..32cf115 100644 (file)
@@ -46,14 +46,14 @@ $specialPageAliases = [
 ];
 
 $digitTransformTable = [
-       '0' => '०', # &#x0966;
-       '1' => '१', # &#x0967;
-       '2' => '२', # &#x0968;
-       '3' => '३', # &#x0969;
-       '4' => '४', # &#x096a;
-       '5' => '५', # &#x096b;
-       '6' => '६', # &#x096c;
-       '7' => '७', # &#x096d;
-       '8' => '८', # &#x096e;
-       '9' => '९', # &#x096f;
+       '0' => '०', # U+0966
+       '1' => '१', # U+0967
+       '2' => '२', # U+0968
+       '3' => '३', # U+0969
+       '4' => '४', # U+096A
+       '5' => '५', # U+096B
+       '6' => '६', # U+096C
+       '7' => '७', # U+096D
+       '8' => '८', # U+096E
+       '9' => '९', # U+096F
 ];
index 65a7595..86ef77b 100644 (file)
@@ -28,14 +28,14 @@ $namespaceNames = [
 ];
 
 $digitTransformTable = [
-       '0' => '०', # &#x0966;
-       '1' => '१', # &#x0967;
-       '2' => '२', # &#x0968;
-       '3' => '३', # &#x0969;
-       '4' => '४', # &#x096a;
-       '5' => '५', # &#x096b;
-       '6' => '६', # &#x096c;
-       '7' => '७', # &#x096d;
-       '8' => '८', # &#x096e;
-       '9' => '९', # &#x096f;
+       '0' => '०', # U+0966
+       '1' => '१', # U+0967
+       '2' => '२', # U+0968
+       '3' => '३', # U+0969
+       '4' => '४', # U+096A
+       '5' => '५', # U+096B
+       '6' => '६', # U+096C
+       '7' => '७', # U+096D
+       '8' => '८', # U+096E
+       '9' => '९', # U+096F
 ];
index d17105a..022da81 100644 (file)
  */
 
 $digitTransformTable = [
-       '0' => '୦', # &#x0b66;
-       '1' => '୧', # &#x0b67;
-       '2' => '୨', # &#x0b68;
-       '3' => '୩', # &#x0b69;
-       '4' => '୪', # &#x0b6a;
-       '5' => '୫', # &#x0b6b;
-       '6' => '୬', # &#x0b6c;
-       '7' => '୭', # &#x0b6d;
-       '8' => '୮', # &#x0b6e;
-       '9' => '୯', # &#x0b6f;
+       '0' => '୦', # U+0B66
+       '1' => '୧', # U+0B67
+       '2' => '୨', # U+0B68
+       '3' => '୩', # U+0B69
+       '4' => '୪', # U+0B6A
+       '5' => '୫', # U+0B6B
+       '6' => '୬', # U+0B6C
+       '7' => '୭', # U+0B6D
+       '8' => '୮', # U+0B6E
+       '9' => '୯', # U+0B6F
 ];
 
 $linkTrail = "/^([a-z\x{0B00}-\x{0B7F}]+)(.*)$/sDu";
index 7062e07..8a3ecd0 100644 (file)
@@ -31,14 +31,14 @@ $namespaceNames = [
 ];
 
 $digitTransformTable = [
-       '0' => '०', # &#x0966;
-       '1' => '१', # &#x0967;
-       '2' => '२', # &#x0968;
-       '3' => '३', # &#x0969;
-       '4' => '४', # &#x096a;
-       '5' => '५', # &#x096b;
-       '6' => '६', # &#x096c;
-       '7' => '७', # &#x096d;
-       '8' => '८', # &#x096e;
-       '9' => '९', # &#x096f;
+       '0' => '०', # U+0966
+       '1' => '१', # U+0967
+       '2' => '२', # U+0968
+       '3' => '३', # U+0969
+       '4' => '४', # U+096A
+       '5' => '५', # U+096B
+       '6' => '६', # U+096C
+       '7' => '७', # U+096D
+       '8' => '८', # U+096E
+       '9' => '९', # U+096F
 ];
index 0b203b0..3f01876 100644 (file)
 $rtl = true;
 
 $digitTransformTable = [
-       '0' => '۰', # &#x1776;
-       '1' => '۱', # &#x1777;
-       '2' => '۲', # &#x1778;
-       '3' => '۳', # &#x1779;
-       '4' => '۴', # &#x1780;
-       '5' => '۵', # &#x1781;
-       '6' => '۶', # &#x1782;
-       '7' => '۷', # &#x1783;
-       '8' => '۸', # &#x1784;
-       '9' => '۹', # &#x1785;
+       '0' => '۰', # U+06F0
+       '1' => '۱', # U+06F1
+       '2' => '۲', # U+06F2
+       '3' => '۳', # U+06F3
+       '4' => '۴', # U+06F4
+       '5' => '۵', # U+06F5
+       '6' => '۶', # U+06F6
+       '7' => '۷', # U+06F7
+       '8' => '۸', # U+06F8
+       '9' => '۹', # U+06F9
 ];
 
 $separatorTransformTable = [
-       '.' => '٫', # &#x066b;
-       ',' => '٬', # &#x066c;
+       '.' => '٫', # U+066B
+       ',' => '٬', # U+066C
 ];
 
 $namespaceNames = [
index 81a8a14..72dec65 100644 (file)
 $fallback = 'hi';
 
 $digitTransformTable = [
-       '0' => '०', # &#x0966;
-       '1' => '१', # &#x0967;
-       '2' => '२', # &#x0968;
-       '3' => '३', # &#x0969;
-       '4' => '४', # &#x096a;
-       '5' => '५', # &#x096b;
-       '6' => '६', # &#x096c;
-       '7' => '७', # &#x096d;
-       '8' => '८', # &#x096e;
-       '9' => '९', # &#x096f;
+       '0' => '०', # U+0966
+       '1' => '१', # U+0967
+       '2' => '२', # U+0968
+       '3' => '३', # U+0969
+       '4' => '४', # U+096A
+       '5' => '५', # U+096B
+       '6' => '६', # U+096C
+       '7' => '७', # U+096D
+       '8' => '८', # U+096E
+       '9' => '९', # U+096F
 ];
 
 $linkPrefixExtension = false;
index 27276aa..f5a7c01 100644 (file)
@@ -13,15 +13,15 @@ $fallback = 'ur, pnb';
 $rtl = true;
 
 $digitTransformTable = [
-       '0' => '۰', # &#x06f0;
-       '1' => '۱', # &#x06f1;
-       '2' => '۲', # &#x06f2;
-       '3' => '۳', # &#x06f3;
-       '4' => '۴', # &#x06f4;
-       '5' => '۵', # &#x06f5;
-       '6' => '۶', # &#x06f6;
-       '7' => '۷', # &#x06f7;
-       '8' => '۸', # &#x06f8;
-       '9' => '۹', # &#x06f9;
-       '%' => '٪', # &#x066a;
+       '0' => '۰', # U+06F0
+       '1' => '۱', # U+06F1
+       '2' => '۲', # U+06F2
+       '3' => '۳', # U+06F3
+       '4' => '۴', # U+06F4
+       '5' => '۵', # U+06F5
+       '6' => '۶', # U+06F6
+       '7' => '۷', # U+06F7
+       '8' => '۸', # U+06F8
+       '9' => '۹', # U+06F9
+       '%' => '٪', # U+066A
 ];
index 02c8bed..747319d 100644 (file)
@@ -143,6 +143,7 @@ ERROR
                        );
                }
                $this->total += $dbw->affectedRows();
+               MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->waitForReplication();
        }
 
        /**
index 88f4d8f..607549f 100644 (file)
@@ -102,9 +102,15 @@ Then we got more<br>text
 END;
                $struct = $this->getStructure( $text );
                $this->assertEquals( "Opening text is opening.", $struct->getOpeningText() );
-               $this->assertEquals( "Opening text is opening.   Then we got more text",
+               $this->assertEquals( "Opening text is opening. Then we got more text",
                        $struct->getMainText() );
                $this->assertEquals( [ "Header table row in table another row in table" ],
                        $struct->getAuxiliaryText() );
        }
+
+       public function testPreservesWordSpacing() {
+               $text = "<dd><dl>foo</dl><dl>bar</dl></dd><p>baz</p>";
+               $struct = $this->getStructure( $text );
+               $this->assertEquals( "foo bar baz", $struct->getMainText() );
+       }
 }
index b5965c4..a8b0f90 100644 (file)
@@ -514,7 +514,7 @@ class SanitizerTest extends MediaWikiTestCase {
        public function provideStripAllTags() {
                return [
                        [ '<p>Foo</p>', 'Foo' ],
-                       [ '<p id="one">Foo</p><p id="two">Bar</p>', 'FooBar' ],
+                       [ '<p id="one">Foo</p><p id="two">Bar</p>', 'Foo Bar' ],
                        [ "<p>Foo</p>\n<p>Bar</p>", 'Foo Bar' ],
                        [ '<p>Hello &lt;strong&gt; wor&#x6c;&#100; caf&eacute;</p>', 'Hello <strong> world café' ],
                        [