Merge "Remove temporary rcfilters variables"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 1 Oct 2018 23:09:28 +0000 (23:09 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 1 Oct 2018 23:09:28 +0000 (23:09 +0000)
77 files changed:
CODE_OF_CONDUCT.md
RELEASE-NOTES-1.32
composer.json
docs/hooks.txt
includes/EditPage.php
includes/GlobalFunctions.php
includes/OrderedStreamingForkController.php
includes/ServiceWiring.php
includes/SiteStats.php
includes/Storage/NameTableStore.php
includes/Title.php
includes/actions/RawAction.php
includes/api/ApiStashEdit.php
includes/auth/LocalPasswordPrimaryAuthenticationProvider.php
includes/cache/MessageCache.php
includes/changes/OldChangesList.php
includes/content/WikiTextStructure.php
includes/debug/logger/LegacyLogger.php
includes/debug/logger/monolog/LegacyFormatter.php
includes/debug/logger/monolog/LegacyHandler.php
includes/exception/MWException.php
includes/exception/MWExceptionHandler.php
includes/exception/MWExceptionRenderer.php
includes/filerepo/FileRepo.php
includes/filerepo/file/File.php
includes/filerepo/file/ForeignDBFile.php
includes/filerepo/file/LocalFile.php
includes/htmlform/fields/HTMLInfoField.php
includes/installer/i18n/en.json
includes/installer/i18n/qqq.json
includes/interwiki/ClassicInterwikiLookup.php
includes/jobqueue/JobRunner.php
includes/jobqueue/utils/PurgeJobUtils.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/DatabaseMysqlBase.php
includes/libs/rdbms/database/DatabaseSqlite.php
includes/libs/rdbms/loadbalancer/LoadBalancer.php
includes/parser/RemexStripTagHandler.php
includes/parser/Sanitizer.php
includes/skins/Skin.php
includes/specials/SpecialExpandTemplates.php
includes/user/User.php
includes/watcheditem/WatchedItemStore.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/dictionary/mediawiki.dic
maintenance/includes/DeleteLocalPasswords.php
maintenance/populateArchiveRevId.php
tests/phpunit/includes/OutputPageTest.php
tests/phpunit/includes/content/WikitextStructureTest.php
tests/phpunit/includes/db/DatabasePostgresTest.php
tests/phpunit/includes/media/BitmapMetadataHandlerTest.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 60f5e6a..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 ===
 
@@ -323,6 +326,8 @@ because of Phabricator reports.
 * The '--tidy' option to maintenance/parse.php has been removed.  Tidying
   the output is now the default.  Use '--no-tidy' to bypass the tidy
   phase.
+* The global function wfErrorLog, deprecated since 1.25, has now been removed.
+  Use MWLoggerLegacyLogger::emit or UDPTransport.
 
 === Deprecations in 1.32 ===
 * HTMLForm::setSubmitProgressive() is deprecated. No need to call it. Submit
@@ -466,6 +471,9 @@ because of Phabricator reports.
 * QuickTemplate::msgHtml() and BaseTemplate::msgHtml() have been deprecated
   as they promote bad practises. I18n messages should always be properly
   escaped.
+* Skin::getDynamicStylesheetQuery() has been deprecated. It always
+  returns action=raw&ctype=text/css which callers should use directly.
+* Class LegacyFormatter is deprecated.
 
 === Other changes in 1.32 ===
 * (T198811) The following tables have had their UNIQUE indexes turned into
index 7d4b164..0707f04 100644 (file)
@@ -74,7 +74,7 @@
                "wmde/hamcrest-html-matchers": "^0.1.0"
        },
        "suggest": {
-               "ext-apc": "Local data and opcode cache",
+               "ext-apcu": "Local data cache for greatly improved performance",
                "ext-curl": "Improved http communication abilities",
                "ext-fileinfo": "Improved mime magic detection",
                "ext-intl": "ICU integration",
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 7384ca2..8c4b3c8 100644 (file)
@@ -2716,7 +2716,7 @@ ERROR;
         *
         * @param string|null|bool $text Text to unserialize
         * @return Content|bool|null The content object created from $text. If $text was false
-        *   or null, false resp. null will be  returned instead.
+        *   or null, then false or null will be returned instead.
         *
         * @throws MWException If unserializing the text results in a Content
         *   object that is not an instance of TextContent and
index 336cb89..c769b0a 100644 (file)
@@ -1147,26 +1147,6 @@ function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
        MWDebug::warning( $msg, $callerOffset + 1, $level, 'production' );
 }
 
-/**
- * Log to a file without getting "file size exceeded" signals.
- *
- * Can also log to TCP or UDP with the syntax udp://host:port/prefix. This will
- * send lines to the specified port, prefixed by the specified prefix and a space.
- * @since 1.25 support for additional context data
- *
- * @param string $text
- * @param string $file Filename
- * @param array $context Additional logging context data
- * @throws MWException
- * @deprecated since 1.25 Use \MediaWiki\Logger\LegacyLogger::emit or UDPTransport
- */
-function wfErrorLog( $text, $file, array $context = [] ) {
-       wfDeprecated( __METHOD__, '1.25' );
-       $logger = LoggerFactory::getInstance( 'wfErrorLog' );
-       $context['destination'] = $file;
-       $logger->info( trim( $text ), $context );
-}
-
 /**
  * @todo document
  * @todo Move logic to MediaWiki.php
index ff29cb5..11abc81 100644 (file)
@@ -134,9 +134,12 @@ class OrderedStreamingForkController extends ForkController {
         */
        protected function consumeNoFork() {
                while ( !feof( $this->input ) ) {
-                       $line = trim( fgets( $this->input ) );
-                       if ( $line ) {
-                               $result = call_user_func( $this->workCallback, $line );
+                       $data = fgets( $this->input );
+                       if ( $data[ strlen( $data ) - 1 ] == "\n" ) {
+                               $data = substr( $data, 0, -1 );
+                       }
+                       if ( strlen( $data ) !== 0 ) {
+                               $result = call_user_func( $this->workCallback, $data );
                                fwrite( $this->output, "$result\n" );
                        }
                }
@@ -160,8 +163,12 @@ class OrderedStreamingForkController extends ForkController {
                                        $this->updateAvailableSockets( $sockets, $used, $sockets ? 0 : 5 );
                                } while ( !$sockets );
                        }
-                       $data = trim( $data );
-                       if ( !$data ) {
+                       // Strip the trailing \n. The last line of a file might not have a trailing
+                       // \n though
+                       if ( $data[ strlen( $data ) - 1 ] == "\n" ) {
+                               $data = substr( $data, 0, -1 );
+                       }
+                       if ( strlen( $data ) === 0 ) {
                                continue;
                        }
                        $socket = array_pop( $sockets );
index cf2def2..dac3de6 100644 (file)
@@ -278,7 +278,7 @@ return [
                                        // Also test DjVu
                                        $deja = new DjVuImage( $file );
                                        if ( $deja->isValid() ) {
-                                               $logger->info( __METHOD__ . ": detected $file as image/vnd.djvu\n" );
+                                               $logger->info( "Detected $file as image/vnd.djvu\n" );
                                                $mime = 'image/vnd.djvu';
 
                                                return;
index 745c891..e3cb617 100644 (file)
@@ -149,11 +149,12 @@ class SiteStats {
         */
        public static function numberingroup( $group ) {
                $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+               $fname = __METHOD__;
 
                return $cache->getWithSetCallback(
                        $cache->makeKey( 'SiteStats', 'groupcounts', $group ),
                        $cache::TTL_HOUR,
-                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $group ) {
+                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $group, $fname ) {
                                $dbr = self::getLB()->getConnection( DB_REPLICA );
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
@@ -164,7 +165,7 @@ class SiteStats {
                                                'ug_group' => $group,
                                                'ug_expiry IS NULL OR ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() )
                                        ],
-                                       __METHOD__
+                                       $fname
                                );
                        },
                        [ 'pcTTL' => $cache::TTL_PROC_LONG ]
@@ -199,11 +200,12 @@ class SiteStats {
         */
        public static function pagesInNs( $ns ) {
                $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+               $fname = __METHOD__;
 
                return $cache->getWithSetCallback(
                        $cache->makeKey( 'SiteStats', 'page-in-namespace', $ns ),
                        $cache::TTL_HOUR,
-                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $ns ) {
+                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $ns, $fname ) {
                                $dbr = self::getLB()->getConnection( DB_REPLICA );
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
@@ -211,7 +213,7 @@ class SiteStats {
                                        'page',
                                        'COUNT(*)',
                                        [ 'page_namespace' => $ns ],
-                                       __METHOD__
+                                       $fname
                                );
                        },
                        [ 'pcTTL' => $cache::TTL_PROC_LONG ]
index 6c7919d..c1dd09d 100644 (file)
@@ -262,11 +262,12 @@ class NameTableStore {
                if ( array_key_exists( $id, $table ) ) {
                        return $table[$id];
                }
+               $fname = __METHOD__;
 
                $table = $this->cache->getWithSetCallback(
                        $this->getCacheKey(),
                        $this->cacheTTL,
-                       function ( $oldValue, &$ttl, &$setOpts ) use ( $id ) {
+                       function ( $oldValue, &$ttl, &$setOpts ) use ( $id, $fname ) {
                                // Check if cached value is up-to-date enough to have $id
                                if ( is_array( $oldValue ) && array_key_exists( $id, $oldValue ) ) {
                                        // Completely leave the cache key alone
@@ -279,7 +280,7 @@ class NameTableStore {
                                        // Log a fallback to master
                                        if ( $source === DB_MASTER ) {
                                                $this->logger->info(
-                                                       __METHOD__ . 'falling back to master select from ' .
+                                                       $fname . ' falling back to master select from ' .
                                                        $this->table . ' with id ' . $id
                                                );
                                        }
index 59164e0..5b0c3bc 100644 (file)
@@ -4778,7 +4778,39 @@ class Title implements LinkTarget {
        }
 
        /**
-        * Get the default message text or false if the message doesn't exist
+        * Get the default (plain) message contents for an page that overrides an
+        * interface message key.
+        *
+        * Primary use cases:
+        *
+        * - Article:
+        *    - Show default when viewing the page. The Article::getSubstituteContent
+        *      method displays the default message content, instead of the
+        *      'noarticletext' placeholder message normally used.
+        *
+        * - EditPage:
+        *    - Title of edit page. When creating an interface message override,
+        *      the editor is told they are "Editing the page", instead of
+        *      "Creating the page". (EditPage::setHeaders)
+        *    - Edit notice. The 'translateinterface' edit notice is shown when creating
+        *      or editing a an interface message override. (EditPage::showIntro)
+        *    - Opening the editor. The contents of the localisation message are used
+        *      as contents of the editor when creating a new page in the MediaWiki
+        *      namespace. This simplifies the process for editors when "changing"
+        *      an interface message by creating an override. (EditPage::getContentObject)
+        *    - Showing a diff. The left-hand side of a diff when an editor is
+        *      previewing their changes before saving the creation of a page in the
+        *      MediaWiki namespace. (EditPage::showDiff)
+        *    - Disallowing a save. When attempting to create a a MediaWiki-namespace
+        *      page with the proposed content matching the interface message default,
+        *      the save is rejected, the same way we disallow blank pages from being
+        *      created. (EditPage::internalAttemptSave)
+        *
+        * - ApiEditPage:
+        *    - Default content, when using the 'prepend' or 'append' feature.
+        *
+        * - SkinTemplate:
+        *    - Label the create action as "Edit", if the page can be an override.
         *
         * @return string|bool
         */
index 817c9fd..b5a6d3a 100644 (file)
@@ -163,47 +163,35 @@ class RawAction extends FormlessAction {
                $title = $this->getTitle();
                $request = $this->getRequest();
 
-               // If it's a MediaWiki message we can just hit the message cache
-               if ( $request->getBool( 'usemsgcache' ) && $title->getNamespace() == NS_MEDIAWIKI ) {
-                       // The first "true" is to use the database, the second is to use
-                       // the content langue and the last one is to specify the message
-                       // key already contains the language in it ("/de", etc.).
-                       $text = MessageCache::singleton()->get( $title->getDBkey(), true, true, true );
-                       // If the message doesn't exist, return a blank
-                       if ( $text === false ) {
-                               $text = '';
-                       }
-               } else {
-                       // Get it from the DB
-                       $rev = Revision::newFromTitle( $title, $this->getOldId() );
-                       if ( $rev ) {
-                               $lastmod = wfTimestamp( TS_RFC2822, $rev->getTimestamp() );
-                               $request->response()->header( "Last-modified: $lastmod" );
+               // Get it from the DB
+               $rev = Revision::newFromTitle( $title, $this->getOldId() );
+               if ( $rev ) {
+                       $lastmod = wfTimestamp( TS_RFC2822, $rev->getTimestamp() );
+                       $request->response()->header( "Last-modified: $lastmod" );
 
-                               // Public-only due to cache headers
-                               $content = $rev->getContent();
+                       // Public-only due to cache headers
+                       $content = $rev->getContent();
 
-                               if ( $content === null ) {
-                                       // revision not found (or suppressed)
+                       if ( $content === null ) {
+                               // revision not found (or suppressed)
+                               $text = false;
+                       } elseif ( !$content instanceof TextContent ) {
+                               // non-text content
+                               wfHttpError( 415, "Unsupported Media Type", "The requested page uses the content model `"
+                                       . $content->getModel() . "` which is not supported via this interface." );
+                               die();
+                       } else {
+                               // want a section?
+                               $section = $request->getIntOrNull( 'section' );
+                               if ( $section !== null ) {
+                                       $content = $content->getSection( $section );
+                               }
+
+                               if ( $content === null || $content === false ) {
+                                       // section not found (or section not supported, e.g. for JS, JSON, and CSS)
                                        $text = false;
-                               } elseif ( !$content instanceof TextContent ) {
-                                       // non-text content
-                                       wfHttpError( 415, "Unsupported Media Type", "The requested page uses the content model `"
-                                               . $content->getModel() . "` which is not supported via this interface." );
-                                       die();
                                } else {
-                                       // want a section?
-                                       $section = $request->getIntOrNull( 'section' );
-                                       if ( $section !== null ) {
-                                               $content = $content->getSection( $section );
-                                       }
-
-                                       if ( $content === null || $content === false ) {
-                                               // section not found (or section not supported, e.g. for JS, JSON, and CSS)
-                                               $text = false;
-                                       } else {
-                                               $text = $content->getNativeData();
-                                       }
+                                       $text = $content->getNativeData();
                                }
                        }
                }
index ab9ae8e..17c8040 100644 (file)
@@ -174,6 +174,7 @@ class ApiStashEdit extends ApiBase {
 
                $title = $page->getTitle();
                $key = self::getStashKey( $title, self::getContentHash( $content ), $user );
+               $fname = __METHOD__;
 
                // Use the master DB to allow for fast blocking locks on the "save path" where this
                // value might actually be used to complete a page edit. If the edit submission request
@@ -182,13 +183,13 @@ class ApiStashEdit extends ApiBase {
                // need to duplicate parsing of the same content/user/summary bundle, so try to avoid
                // blocking at all here.
                $dbw = wfGetDB( DB_MASTER );
-               if ( !$dbw->lock( $key, __METHOD__, 0 ) ) {
+               if ( !$dbw->lock( $key, $fname, 0 ) ) {
                        // De-duplicate requests on the same key
                        return self::ERROR_BUSY;
                }
                /** @noinspection PhpUnusedLocalVariableInspection */
-               $unlocker = new ScopedCallback( function () use ( $dbw, $key ) {
-                       $dbw->unlock( $key, __METHOD__ );
+               $unlocker = new ScopedCallback( function () use ( $dbw, $key, $fname ) {
+                       $dbw->unlock( $key, $fname );
                } );
 
                $cutoffTime = time() - self::PRESUME_FRESH_TTL_SEC;
index 86a6aae..c538ee7 100644 (file)
@@ -136,7 +136,8 @@ class LocalPasswordPrimaryAuthenticationProvider
                // @codeCoverageIgnoreStart
                if ( $this->getPasswordFactory()->needsUpdate( $pwhash ) ) {
                        $newHash = $this->getPasswordFactory()->newFromPlaintext( $req->password );
-                       \DeferredUpdates::addCallableUpdate( function () use ( $newHash, $oldRow ) {
+                       $fname = __METHOD__;
+                       \DeferredUpdates::addCallableUpdate( function () use ( $newHash, $oldRow, $fname ) {
                                $dbw = wfGetDB( DB_MASTER );
                                $dbw->update(
                                        'user',
@@ -145,7 +146,7 @@ class LocalPasswordPrimaryAuthenticationProvider
                                                'user_id' => $oldRow->user_id,
                                                'user_password' => $oldRow->user_password
                                        ],
-                                       __METHOD__
+                                       $fname
                                );
                        } );
                }
index 7a1b988..aa929bc 100644 (file)
@@ -582,10 +582,11 @@ class MessageCache {
                        // Ignore $wgMaxMsgCacheEntrySize so the process cache is up to date
                        $this->cache->setField( $code, $title, ' ' . $text );
                }
+               $fname = __METHOD__;
 
                // (b) Update the shared caches in a deferred update with a fresh DB snapshot
                DeferredUpdates::addCallableUpdate(
-                       function () use ( $title, $msg, $code ) {
+                       function () use ( $title, $msg, $code, $fname ) {
                                global $wgMaxMsgCacheEntrySize;
                                // Allow one caller at a time to avoid race conditions
                                $scopedLock = $this->getReentrantScopedLock(
@@ -593,7 +594,7 @@ class MessageCache {
                                );
                                if ( !$scopedLock ) {
                                        LoggerFactory::getInstance( 'MessageCache' )->error(
-                                               __METHOD__ . ': could not acquire lock to update {title} ({code})',
+                                               $fname . ': could not acquire lock to update {title} ({code})',
                                                [ 'title' => $title, 'code' => $code ] );
                                        return;
                                }
@@ -1029,14 +1030,15 @@ class MessageCache {
         * @return string Either " <MESSAGE>" or "!NONEXISTANT"
         */
        private function loadCachedMessagePageEntry( $dbKey, $code, $hash ) {
+               $fname = __METHOD__;
                return $this->srvCache->getWithSetCallback(
                        $this->srvCache->makeKey( 'messages-big', $hash, $dbKey ),
                        IExpiringStore::TTL_MINUTE,
-                       function () use ( $code, $dbKey, $hash ) {
+                       function () use ( $code, $dbKey, $hash, $fname ) {
                                return $this->wanCache->getWithSetCallback(
                                        $this->bigMessageCacheKey( $hash, $dbKey ),
                                        $this->mExpiry,
-                                       function ( $oldValue, &$ttl, &$setOpts ) use ( $dbKey, $code ) {
+                                       function ( $oldValue, &$ttl, &$setOpts ) use ( $dbKey, $code, $fname ) {
                                                // Try loading the message from the database
                                                $dbr = wfGetDB( DB_REPLICA );
                                                $setOpts += Database::getCacheSetOptions( $dbr );
@@ -1053,7 +1055,7 @@ class MessageCache {
                                                        $message = $this->getMessageTextFromContent( $content );
                                                } else {
                                                        LoggerFactory::getInstance( 'MessageCache' )->warning(
-                                                               __METHOD__ . ': failed to load page text for \'{titleKey}\'',
+                                                               $fname . ': failed to load page text for \'{titleKey}\'',
                                                                [ 'titleKey' => $dbKey, 'code' => $code ]
                                                        );
                                                        $message = null;
index a2af01c..a26f5b6 100644 (file)
@@ -60,7 +60,10 @@ class OldChangesList extends ChangesList {
                ) {
                        return false;
                }
-               $attribs = wfArrayFilterByKey( $attribs, [ Sanitizer::class, 'isReservedDataAttribute' ] );
+               $attribs = array_filter( $attribs,
+                       [ Sanitizer::class, 'isReservedDataAttribute' ],
+                       ARRAY_FILTER_USE_KEY
+               );
 
                $dateheader = ''; // $html now contains only <li>...</li>, for hooks' convenience.
                $this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
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 d4a79d9..9d0789c 100644 (file)
@@ -29,7 +29,7 @@ use Psr\Log\LogLevel;
 use UDPTransport;
 
 /**
- * PSR-3 logger that mimics the historic implementation of MediaWiki's
+ * PSR-3 logger that mimics the historic implementation of MediaWiki's former
  * wfErrorLog logging implementation.
  *
  * This logger is configured by the following global configuration variables:
@@ -145,10 +145,6 @@ class LegacyLogger extends AbstractLogger {
                        // specfied.
                        $shouldEmit = (bool)$wgDBerrorLog;
 
-               } elseif ( $channel === 'wfErrorLog' ) {
-                       // All messages on the wfErrorLog channel should be emitted.
-                       $shouldEmit = true;
-
                } elseif ( $channel === 'wfDebug' ) {
                        // wfDebug messages are emitted if a catch all logging file has
                        // been specified. Checked explicitly so that 'private' flagged
@@ -192,10 +188,9 @@ class LegacyLogger extends AbstractLogger {
        /**
         * Format a message.
         *
-        * Messages to the 'wfDebug', 'wfLogDBError' and 'wfErrorLog' channels
-        * receive special formatting to mimic the historic output of the functions
-        * of the same name. All other channel values are formatted based on the
-        * historic output of the `wfDebugLog()` global function.
+        * Messages to the 'wfDebug' and 'wfLogDBError' channels receive special formatting to mimic the
+        * historic output of the functions of the same name. All other channel values are formatted
+        * based on the historic output of the `wfDebugLog()` global function.
         *
         * @param string $channel
         * @param string $message
@@ -211,9 +206,6 @@ class LegacyLogger extends AbstractLogger {
                } elseif ( $channel === 'wfLogDBError' ) {
                        $text = self::formatAsWfLogDBError( $channel, $message, $context );
 
-               } elseif ( $channel === 'wfErrorLog' ) {
-                       $text = "{$message}\n";
-
                } elseif ( $channel === 'profileoutput' ) {
                        // Legacy wfLogProfilingData formatitng
                        $forward = '';
index 92624a0..e16dd4c 100644 (file)
@@ -24,10 +24,11 @@ use MediaWiki\Logger\LegacyLogger;
 use Monolog\Formatter\NormalizerFormatter;
 
 /**
- * Log message formatter that mimics the legacy log message formatting of
- * `wfDebug`, `wfDebugLog`, `wfLogDBError` and `wfErrorLog` global functions by
- * delegating the formatting to \MediaWiki\Logger\LegacyLogger.
+ * Log message formatter that mimics the legacy log message formatting of `wfDebug`, `wfDebugLog`,
+ * `wfLogDBError` and the former `wfErrorLog` global functions by delegating the formatting to
+ * \MediaWiki\Logger\LegacyLogger.
  *
+ * @deprecated since 1.32
  * @since 1.25
  * @copyright © 2013 Wikimedia Foundation and contributors
  * @see \MediaWiki\Logger\LegacyLogger
index dbeb136..258a9a8 100644 (file)
@@ -27,7 +27,7 @@ use Monolog\Logger;
 use UnexpectedValueException;
 
 /**
- * Log handler that replicates the behavior of MediaWiki's wfErrorLog()
+ * Log handler that replicates the behavior of MediaWiki's former wfErrorLog()
  * logging service. Log output can be directed to a local file, a PHP stream,
  * or a udp2log server.
  *
index 652a87d..af835e4 100644 (file)
@@ -76,6 +76,7 @@ class MWException extends Exception {
                global $wgSitename;
                $args = array_slice( func_get_args(), 2 );
 
+               // FIXME: Keep logic in sync with MWExceptionRenderer::msg.
                $res = false;
                if ( $this->useMessageCache() ) {
                        try {
@@ -87,7 +88,9 @@ class MWException extends Exception {
                        $res = wfMsgReplaceArgs( $fallback, $args );
                        // If an exception happens inside message rendering,
                        // {{SITENAME}} sometimes won't be replaced.
-                       $res = preg_replace( '/\{\{SITENAME\}\}/', $wgSitename, $res );
+                       $res = strtr( $res, [
+                               '{{SITENAME}}' => $wgSitename,
+                       ] );
                }
                return $res;
        }
index bd823b5..0e81a43 100644 (file)
@@ -35,12 +35,35 @@ class MWExceptionHandler {
         * @var string $reservedMemory
         */
        protected static $reservedMemory;
+
        /**
+        * Error types that, if unhandled, are fatal to the request.
+        *
+        * On PHP 7, these error types may be thrown as Error objects, which
+        * implement Throwable (but not Exception).
+        *
+        * On HHVM, these invoke the set_error_handler callback, similar to how
+        * (non-fatal) warnings and notices are reported, except that after this
+        * handler runs for fatal error tpyes, script execution stops!
+        *
+        * The user will be shown an HTTP 500 Internal Server Error.
+        * As such, these should be sent to MediaWiki's "fatal" or "exception"
+        * channel. Normally, the error handler logs them to the "error" channel.
+        *
         * @var array $fatalErrorTypes
         */
        protected static $fatalErrorTypes = [
-               E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR,
-               /* HHVM's FATAL_ERROR level */ 16777217,
+               E_ERROR,
+               E_PARSE,
+               E_CORE_ERROR,
+               E_COMPILE_ERROR,
+               E_USER_ERROR,
+
+               // E.g. "Catchable fatal error: Argument X must be Y, null given"
+               E_RECOVERABLE_ERROR,
+
+               // HHVM's FATAL_ERROR constant
+               16777217,
        ];
        /**
         * @var bool $handledFatalCallback
@@ -192,10 +215,6 @@ class MWExceptionHandler {
                // behaviour given the null was not part of the code and is likely not
                // accounted for.
                switch ( $level ) {
-                       case E_RECOVERABLE_ERROR:
-                               $levelName = 'Error';
-                               $severity = LogLevel::ERROR;
-                               break;
                        case E_WARNING:
                        case E_CORE_WARNING:
                        case E_COMPILE_WARNING:
@@ -231,9 +250,9 @@ class MWExceptionHandler {
                self::logError( $e, 'error', $severity );
 
                // If $wgPropagateErrors is true return false so PHP shows/logs the error normally.
-               // Ignore $wgPropagateErrors if the error should break execution, or track_errors is set
+               // Ignore $wgPropagateErrors if track_errors is set
                // (which means someone is counting on regular PHP error handling behavior).
-               return !( $wgPropagateErrors || $level == E_RECOVERABLE_ERROR || ini_get( 'track_errors' ) );
+               return !( $wgPropagateErrors || ini_get( 'track_errors' ) );
        }
 
        /**
index 1f1cabe..de2af6b 100644 (file)
@@ -199,13 +199,17 @@ class MWExceptionRenderer {
        private static function msg( $key, $fallback /*[, params...] */ ) {
                global $wgSitename;
                $args = array_slice( func_get_args(), 2 );
+
+               // FIXME: Keep logic in sync with MWException::msg.
                try {
                        $res = wfMessage( $key, $args )->text();
                } catch ( Exception $e ) {
                        $res = wfMsgReplaceArgs( $fallback, $args );
                        // If an exception happens inside message rendering,
                        // {{SITENAME}} sometimes won't be replaced.
-                       $res = preg_replace( '/\{\{SITENAME\}\}/', $wgSitename, $res );
+                       $res = strtr( $res, [
+                               '{{SITENAME}}' => $wgSitename,
+                       ] );
                }
                return $res;
        }
index 858e124..455d38f 100644 (file)
@@ -810,8 +810,9 @@ class FileRepo {
         */
        public function getDescriptionStylesheetUrl() {
                if ( isset( $this->scriptDirUrl ) ) {
-                       return $this->makeUrl( 'title=MediaWiki:Filepage.css&' .
-                               wfArrayToCgi( Skin::getDynamicStylesheetQuery() ) );
+                       // Must match canonical query parameter order for optimum caching
+                       // See Title::getCdnUrls
+                       return $this->makeUrl( 'title=MediaWiki:Filepage.css&action=raw&ctype=text/css' );
                }
 
                return false;
index 5a0cf6a..a30d213 100644 (file)
@@ -2071,13 +2071,14 @@ abstract class File implements IDBAccessObject {
                                $lang->getCode(),
                                md5( $this->getName() )
                        );
+                       $fname = __METHOD__;
 
                        return $cache->getWithSetCallback(
                                $key,
                                $this->repo->descriptionCacheExpiry ?: $cache::TTL_UNCACHEABLE,
-                               function ( $oldValue, &$ttl, array &$setOpts ) use ( $renderUrl ) {
+                               function ( $oldValue, &$ttl, array &$setOpts ) use ( $renderUrl, $fname ) {
                                        wfDebug( "Fetching shared description from $renderUrl\n" );
-                                       $res = Http::get( $renderUrl, [], __METHOD__ );
+                                       $res = Http::get( $renderUrl, [], $fname );
                                        if ( !$res ) {
                                                $ttl = WANObjectCache::TTL_UNCACHEABLE;
                                        }
index ee4df1d..1869967 100644 (file)
@@ -152,6 +152,7 @@ class ForeignDBFile extends LocalFile {
                }
 
                $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+               $fname = __METHOD__;
 
                return $cache->getWithSetCallback(
                        $this->repo->getLocalCacheKey(
@@ -161,9 +162,9 @@ class ForeignDBFile extends LocalFile {
                                $touched
                        ),
                        $this->repo->descriptionCacheExpiry ?: $cache::TTL_UNCACHEABLE,
-                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $renderUrl ) {
+                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $renderUrl, $fname ) {
                                wfDebug( "Fetching shared description from $renderUrl\n" );
-                               $res = Http::get( $renderUrl, [], __METHOD__ );
+                               $res = Http::get( $renderUrl, [], $fname );
                                if ( !$res ) {
                                        $ttl = WANObjectCache::TTL_UNCACHEABLE;
                                }
index b6c249b..36a44dc 100644 (file)
@@ -1696,6 +1696,7 @@ class LocalFile extends File {
                # Defer purges, page creation, and link updates in case they error out.
                # The most important thing is that files and the DB registry stay synced.
                $dbw->endAtomic( __METHOD__ );
+               $fname = __METHOD__;
 
                # Do some cache purges after final commit so that:
                # a) Changes are more likely to be seen post-purge
@@ -1706,7 +1707,7 @@ class LocalFile extends File {
                                __METHOD__,
                                function () use (
                                        $reupload, $wikiPage, $newPageContent, $comment, $user,
-                                       $logEntry, $logId, $descId, $tags
+                                       $logEntry, $logId, $descId, $tags, $fname
                                ) {
                                        # Update memcache after the commit
                                        $this->invalidateCache();
@@ -1758,7 +1759,7 @@ class LocalFile extends File {
                                                'logging',
                                                $update,
                                                [ 'log_id' => $logId ],
-                                               __METHOD__
+                                               $fname
                                        );
                                        $this->getRepo()->getMasterDB()->insert(
                                                'log_search',
@@ -1767,7 +1768,7 @@ class LocalFile extends File {
                                                        'ls_value' => $logEntry->getAssociatedRevId(),
                                                        'ls_log_id' => $logId,
                                                ],
-                                               __METHOD__
+                                               $fname
                                        );
 
                                        # Add change tags, if any
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 05cf933..cdf4cde 100644 (file)
@@ -279,10 +279,11 @@ class ClassicInterwikiLookup implements InterwikiLookup {
                        }
                }
 
+               $fname = __METHOD__;
                $iwData = $this->objectCache->getWithSetCallback(
                        $this->objectCache->makeKey( 'interwiki', $prefix ),
                        $this->objectCacheExpiry,
-                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix ) {
+                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix, $fname ) {
                                $dbr = wfGetDB( DB_REPLICA ); // TODO: inject LoadBalancer
 
                                $setOpts += Database::getCacheSetOptions( $dbr );
@@ -291,7 +292,7 @@ class ClassicInterwikiLookup implements InterwikiLookup {
                                        'interwiki',
                                        self::selectFields(),
                                        [ 'iw_prefix' => $prefix ],
-                                       __METHOD__
+                                       $fname
                                );
 
                                return $row ? (array)$row : '!NONEXISTENT';
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 5d8a6cf..ef364b5 100644 (file)
@@ -36,11 +36,12 @@ class PurgeJobUtils {
                if ( $dbkeys === [] ) {
                        return;
                }
+               $fname = __METHOD__;
 
                DeferredUpdates::addUpdate( new AutoCommitUpdate(
                        $dbw,
                        __METHOD__,
-                       function () use ( $dbw, $namespace, $dbkeys ) {
+                       function () use ( $dbw, $namespace, $dbkeys, $fname ) {
                                $services = MediaWikiServices::getInstance();
                                $lbFactory = $services->getDBLoadBalancerFactory();
                                // Determine which pages need to be updated.
@@ -55,7 +56,7 @@ class PurgeJobUtils {
                                                'page_title' => $dbkeys,
                                                'page_touched < ' . $dbw->addQuotes( $now )
                                        ],
-                                       __METHOD__
+                                       $fname
                                );
 
                                if ( !$ids ) {
@@ -63,7 +64,7 @@ class PurgeJobUtils {
                                }
 
                                $batchSize = $services->getMainConfig()->get( 'UpdateRowsPerQuery' );
-                               $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+                               $ticket = $lbFactory->getEmptyTransactionTicket( $fname );
                                foreach ( array_chunk( $ids, $batchSize ) as $idBatch ) {
                                        $dbw->update(
                                                'page',
@@ -72,9 +73,9 @@ class PurgeJobUtils {
                                                        'page_id' => $idBatch,
                                                        'page_touched < ' . $dbw->addQuotes( $now ) // handle races
                                                ],
-                                               __METHOD__
+                                               $fname
                                        );
-                                       $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
+                                       $lbFactory->commitAndWaitForReplication( $fname, $ticket );
                                }
                        }
                ) );
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 0f57551..8d82965 100644 (file)
@@ -824,11 +824,12 @@ abstract class DatabaseMysqlBase extends Database {
                        // Using one key for all cluster replica DBs is preferable
                        $this->getLBInfo( 'clusterMasterHost' ) ?: $this->getServer()
                );
+               $fname = __METHOD__;
 
                return $cache->getWithSetCallback(
                        $key,
                        $cache::TTL_INDEFINITE,
-                       function () use ( $cache, $key ) {
+                       function () use ( $cache, $key, $fname ) {
                                // Get and leave a lock key in place for a short period
                                if ( !$cache->lock( $key, 0, 10 ) ) {
                                        return false; // avoid master connection spike slams
@@ -841,7 +842,7 @@ abstract class DatabaseMysqlBase extends Database {
 
                                // Connect to and query the master; catch errors to avoid outages
                                try {
-                                       $res = $conn->query( 'SELECT @@server_id AS id', __METHOD__ );
+                                       $res = $conn->query( 'SELECT @@server_id AS id', $fname );
                                        $row = $res ? $res->fetchObject() : false;
                                        $id = $row ? (int)$row->id : 0;
                                } catch ( DBError $e ) {
@@ -1043,11 +1044,12 @@ abstract class DatabaseMysqlBase extends Database {
         * @throws DBQueryError If the variable doesn't exist for some reason
         */
        protected function getServerId() {
+               $fname = __METHOD__;
                return $this->srvCache->getWithSetCallback(
                        $this->srvCache->makeGlobalKey( 'mysql-server-id', $this->getServer() ),
                        self::SERVER_ID_CACHE_TTL,
-                       function () {
-                               $res = $this->query( "SELECT @@server_id AS id", __METHOD__ );
+                       function () use ( $fname ) {
+                               $res = $this->query( "SELECT @@server_id AS id", $fname );
                                return intval( $this->fetchObject( $res )->id );
                        }
                );
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 00b4130..f36d98e 100644 (file)
@@ -1256,10 +1256,11 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function closeAll() {
-               $this->forEachOpenConnection( function ( IDatabase $conn ) {
+               $fname = __METHOD__;
+               $this->forEachOpenConnection( function ( IDatabase $conn ) use ( $fname ) {
                        $host = $conn->getServer();
                        $this->connLogger->debug(
-                               __METHOD__ . ": closing connection to database '$host'." );
+                               $fname . ": closing connection to database '$host'." );
                        $conn->close();
                } );
 
@@ -1447,6 +1448,7 @@ class LoadBalancer implements ILoadBalancer {
                } );
 
                $e = null; // first exception
+               $fname = __METHOD__;
                // Loop until callbacks stop adding callbacks on other connections
                do {
                        // Run any pending callbacks for each connection...
@@ -1464,13 +1466,13 @@ class LoadBalancer implements ILoadBalancer {
                                }
                        );
                        // Clear out any active transactions left over from callbacks...
-                       $this->forEachOpenMasterConnection( function ( Database $conn ) use ( &$e ) {
+                       $this->forEachOpenMasterConnection( function ( Database $conn ) use ( &$e, $fname ) {
                                if ( $conn->writesPending() ) {
                                        // A callback from another handle wrote to this one and DBO_TRX is set
-                                       $this->queryLogger->warning( __METHOD__ . ": found writes pending." );
+                                       $this->queryLogger->warning( $fname . ": found writes pending." );
                                        $fnames = implode( ', ', $conn->pendingWriteAndCallbackCallers() );
                                        $this->queryLogger->warning(
-                                               __METHOD__ . ": found writes pending ($fnames).",
+                                               $fname . ": found writes pending ($fnames).",
                                                [
                                                        'db_server' => $conn->getServer(),
                                                        'db_name' => $conn->getDBname()
@@ -1479,10 +1481,10 @@ class LoadBalancer implements ILoadBalancer {
                                } elseif ( $conn->trxLevel() ) {
                                        // A callback from another handle read from this one and DBO_TRX is set,
                                        // which can easily happen if there is only one DB (no replicas)
-                                       $this->queryLogger->debug( __METHOD__ . ": found empty transaction." );
+                                       $this->queryLogger->debug( $fname . ": found empty transaction." );
                                }
                                try {
-                                       $conn->commit( __METHOD__, $conn::FLUSHING_ALL_PEERS );
+                                       $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
                                } catch ( Exception $ex ) {
                                        $e = $e ?: $ex;
                                }
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 e426f7f..ed4045d 100644 (file)
@@ -414,17 +414,13 @@ abstract class Skin extends ContextSource {
        /**
         * Get the query to generate a dynamic stylesheet
         *
+        * @deprecated since 1.32 Use action=raw&ctype=text/css directly.
         * @return array
         */
        public static function getDynamicStylesheetQuery() {
-               global $wgSquidMaxage;
-
                return [
                                'action' => 'raw',
-                               'maxage' => $wgSquidMaxage,
-                               'usemsgcache' => 'yes',
                                'ctype' => 'text/css',
-                               'smaxage' => $wgSquidMaxage,
                        ];
        }
 
index 73ca76b..4587d40 100644 (file)
@@ -115,7 +115,7 @@ class SpecialExpandTemplates extends SpecialPage {
                        }
 
                        $config = $this->getConfig();
-                       if ( $config->get( 'UseTidy' ) && $options->getTidy() ) {
+                       if ( MWTidy::isEnabled() && $options->getTidy() ) {
                                $tmp = MWTidy::tidy( $tmp );
                        }
 
index aaa7663..a29fbf8 100644 (file)
@@ -1051,7 +1051,7 @@ class User implements IDBAccessObject, UserIdentity {
                // Certain names may be reserved for batch processes.
                foreach ( $reservedUsernames as $reserved ) {
                        if ( substr( $reserved, 0, 4 ) == 'msg:' ) {
-                               $reserved = wfMessage( substr( $reserved, 4 ) )->inContentLanguage()->text();
+                               $reserved = wfMessage( substr( $reserved, 4 ) )->inContentLanguage()->plain();
                        }
                        if ( $reserved == $name ) {
                                return false;
@@ -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 ) );
 
@@ -4389,7 +4390,7 @@ class User implements IDBAccessObject, UserIdentity {
                                        'user',
                                        'user_id',
                                        [ 'user_name' => $this->mName ],
-                                       __METHOD__,
+                                       $fname,
                                        [ 'LOCK IN SHARE MODE' ]
                                );
                                $loaded = false;
@@ -4399,7 +4400,7 @@ class User implements IDBAccessObject, UserIdentity {
                                        }
                                }
                                if ( !$loaded ) {
-                                       throw new MWException( __METHOD__ . ": hit a key conflict attempting " .
+                                       throw new MWException( $fname . ": hit a key conflict attempting " .
                                                "to insert user '{$this->mName}' row, but it was not present in select!" );
                                }
                                return Status::newFatal( 'userexists' );
index 6b0c2aa..1b92f51 100644 (file)
@@ -823,7 +823,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
 
                                        $dbw = $this->getConnectionRef( DB_MASTER );
                                        $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
-                                       $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
+                                       $ticket = $factory->getEmptyTransactionTicket( $fname );
 
                                        $watchersChunks = array_chunk( $watchers, $wgUpdateRowsPerQuery );
                                        foreach ( $watchersChunks as $watchersChunk ) {
@@ -838,7 +838,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
                                                );
                                                if ( count( $watchersChunks ) > 1 ) {
                                                        $factory->commitAndWaitForReplication(
-                                                               __METHOD__, $ticket, [ 'domain' => $dbw->getDomainID() ]
+                                                               $fname, $ticket, [ 'domain' => $dbw->getDomainID() ]
                                                        );
                                                }
                                        }
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 ff06e49..a0177b1 100644 (file)
@@ -4322,7 +4322,6 @@ useemail
 uselang
 uselivepreview
 usemod
-usemsgcache
 usenewrc
 user
 useragent
index 02c8bed..747319d 100644 (file)
@@ -143,6 +143,7 @@ ERROR
                        );
                }
                $this->total += $dbw->affectedRows();
+               MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->waitForReplication();
        }
 
        /**
index 60f5e8a..6eb2d6d 100644 (file)
@@ -116,8 +116,8 @@ class PopulateArchiveRevId extends LoggedUpdateMaintenance {
                                        $toDelete[] = $id;
 
                                        $maxId = max(
-                                               (int)$dbw->selectField( 'archive', 'MAX(ar_rev_id)', [], __METHOD__ ),
-                                               (int)$dbw->selectField( 'slots', 'MAX(slot_revision_id)', [], __METHOD__ )
+                                               (int)$dbw->selectField( 'archive', 'MAX(ar_rev_id)', [], $fname ),
+                                               (int)$dbw->selectField( 'slots', 'MAX(slot_revision_id)', [], $fname )
                                        );
                                        if ( $id <= $maxId ) {
                                                $dbw->insert( 'revision', [ 'rev_id' => $maxId + 1 ] + self::$dummyRev, $fname );
@@ -220,7 +220,43 @@ class PopulateArchiveRevId extends LoggedUpdateMaintenance {
                        );
                }
                if ( !$rev ) {
-                       throw new UnexpectedValueException( 'No revisions are available to copy' );
+                       // Since no revisions are available to copy, generate a dummy
+                       // revision to a dummy page, then rollback the commit
+                       wfDebug( __METHOD__ . ": No revisions are available to copy\n" );
+
+                       $dbw->begin();
+
+                       // Make a title and revision and insert them
+                       $title = Title::newFromText( "PopulateArchiveRevId_4b05b46a81e29" );
+                       $page = WikiPage::factory( $title );
+                       $updater = $page->newPageUpdater(
+                               User::newSystemUser( 'Maintenance script', [ 'steal' => true ] )
+                       );
+                       $updater->setContent(
+                               'main',
+                               ContentHandler::makeContent( "Content for dummy rev", $title )
+                       );
+                       $updater->saveRevision(
+                               CommentStoreComment::newUnsavedComment( 'dummy rev summary' ),
+                               EDIT_NEW | EDIT_SUPPRESS_RC
+                       );
+
+                       // get the revision row just inserted
+                       $rev = $dbw->selectRow(
+                               'revision',
+                               '*',
+                               [],
+                               __METHOD__,
+                               [ 'ORDER BY' => 'rev_timestamp ASC' ]
+                       );
+
+                       $dbw->rollback();
+               }
+               if ( !$rev ) {
+                       // This should never happen.
+                       throw new UnexpectedValueException(
+                               'No revisions are available to copy, and one couldn\'t be created'
+                       );
                }
 
                unset( $rev->rev_id );
index 9d6122d..283e99f 100644 (file)
@@ -1508,6 +1508,36 @@ class OutputPageTest extends MediaWikiTestCase {
                $op->addWikiText( 'a' );
        }
 
+       /**
+        * @covers OutputPage::addWikiMsg
+        */
+       public function testAddWikiMsg() {
+               $msg = wfMessage( 'parentheses' );
+               $this->assertSame( '(a)', $msg->rawParams( 'a' )->plain() );
+
+               $op = $this->newInstance();
+               $this->assertSame( '', $op->getHTML() );
+               $op->addWikiMsg( 'parentheses', "<b>a" );
+               // This is known to be bad unbalanced HTML; this will be fixed
+               // by I743f4185a03403f8d9b9db010ff1ee4e9342e062 (T198214)
+               $this->assertSame( "<p>(<b>a)\n</p>", $op->getHTML() );
+       }
+
+       /**
+        * @covers OutputPage::wrapWikiMsg
+        */
+       public function testWrapWikiMsg() {
+               $msg = wfMessage( 'parentheses' );
+               $this->assertSame( '(a)', $msg->rawParams( 'a' )->plain() );
+
+               $op = $this->newInstance();
+               $this->assertSame( '', $op->getHTML() );
+               $op->wrapWikiMsg( '[$1]', [ 'parentheses', "<b>a" ] );
+               // This is known to be bad unbalanced HTML; this will be fixed
+               // by I743f4185a03403f8d9b9db010ff1ee4e9342e062 (T198214)
+               $this->assertSame( "<p>[(<b>a)]\n</p>", $op->getHTML() );
+       }
+
        /**
         * @covers OutputPage::addParserOutputMetadata
         */
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 5c2aa2b..6b1ed7f 100644 (file)
@@ -11,15 +11,17 @@ use Wikimedia\TestingAccessWrapper;
 class DatabasePostgresTest extends MediaWikiTestCase {
 
        private function doTestInsertIgnore() {
-               $reset = new ScopedCallback( function () {
+               $fname = __METHOD__;
+               $reset = new ScopedCallback( function () use ( $fname ) {
                        if ( $this->db->explicitTrxActive() ) {
-                               $this->db->rollback( __METHOD__ );
+                               $this->db->rollback( $fname );
                        }
-                       $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ) );
+                       $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ), $fname );
                } );
 
                $this->db->query(
-                       "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER NOT NULL PRIMARY KEY)"
+                       "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER NOT NULL PRIMARY KEY)",
+                       __METHOD__
                );
                $this->db->insert( 'foo', [ [ 'i' => 1 ], [ 'i' => 2 ] ], __METHOD__ );
 
@@ -92,19 +94,22 @@ class DatabasePostgresTest extends MediaWikiTestCase {
        }
 
        private function doTestInsertSelectIgnore() {
-               $reset = new ScopedCallback( function () {
+               $fname = __METHOD__;
+               $reset = new ScopedCallback( function () use ( $fname ) {
                        if ( $this->db->explicitTrxActive() ) {
-                               $this->db->rollback( __METHOD__ );
+                               $this->db->rollback( $fname );
                        }
-                       $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ) );
-                       $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'bar' ) );
+                       $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ), $fname );
+                       $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'bar' ), $fname );
                } );
 
                $this->db->query(
-                       "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER)"
+                       "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER)",
+                       __METHOD__
                );
                $this->db->query(
-                       "CREATE TEMPORARY TABLE {$this->db->tableName( 'bar' )} (i INTEGER NOT NULL PRIMARY KEY)"
+                       "CREATE TEMPORARY TABLE {$this->db->tableName( 'bar' )} (i INTEGER NOT NULL PRIMARY KEY)",
+                       __METHOD__
                );
                $this->db->insert( 'bar', [ [ 'i' => 1 ], [ 'i' => 2 ] ], __METHOD__ );
 
index a70c005..83554d2 100644 (file)
@@ -127,9 +127,8 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
         * @covers BitmapMetadataHandler::png
         */
        public function testPNGXMP() {
-               if ( !extension_loaded( 'xml' ) ) {
-                       $this->markTestSkipped( "This test needs the xml extension." );
-               }
+               $this->checkPHPExtension( 'xml' );
+
                $handler = new BitmapMetadataHandler();
                $result = $handler->PNG( $this->filePath . 'xmp.png' );
                $expected = [
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é' ],
                        [