Merge "rdbms: make MySQLMasterPos handle inactive GTIDs"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 16 Feb 2018 00:11:48 +0000 (00:11 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 16 Feb 2018 00:11:48 +0000 (00:11 +0000)
52 files changed:
.travis.yml
Gruntfile.js
RELEASE-NOTES-1.31
autoload.php
includes/CategoryFinder.php
includes/DefaultSettings.php
includes/DevelopmentSettings.php [new file with mode: 0644]
includes/EditPage.php
includes/OutputPage.php
includes/SiteStats.php
includes/SiteStatsInit.php
includes/Storage/RevisionStore.php
includes/api/ApiFeedRecentChanges.php
includes/api/ApiParse.php
includes/api/i18n/en.json
includes/api/i18n/pt.json
includes/api/i18n/qqq.json
includes/cache/MessageCache.php
includes/deferred/SiteStatsUpdate.php
includes/diff/DifferenceEngine.php
includes/installer/Installer.php
includes/installer/i18n/ast.json
includes/installer/i18n/sv.json
includes/jobqueue/jobs/RecentChangesUpdateJob.php
includes/jobqueue/jobs/UserGroupExpiryJob.php [new file with mode: 0644]
includes/libs/objectcache/WANObjectCache.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/loadbalancer/ILoadBalancer.php
includes/libs/rdbms/loadbalancer/LoadBalancer.php
includes/page/Article.php
includes/parser/Parser.php
includes/parser/ParserCache.php
includes/parser/ParserOptions.php
includes/parser/ParserOutput.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialUndelete.php
includes/user/UserGroupMembership.php
languages/i18n/ast.json
languages/i18n/be-tarask.json
languages/i18n/en.json
languages/i18n/qqq.json
languages/i18n/sh.json
languages/i18n/sq.json
languages/i18n/sr-ec.json
languages/i18n/sv.json
languages/i18n/zh-hant.json
maintenance/Maintenance.php
tests/phpunit/includes/SiteStatsTest.php
tests/phpunit/includes/parser/ParserOptionsTest.php
tests/phpunit/includes/parser/ParserOutputTest.php
tests/phpunit/phpunit.php
tests/phpunit/structure/ApiStructureTest.php

index b06d9f4..a28dac0 100644 (file)
@@ -69,6 +69,8 @@ before_script:
       --dbuser "$dbuser"
       --dbpass ""
       --scriptpath "/w"
+  - echo -en "\n\nrequire_once __DIR__ . '/includes/DevelopmentSettings.php';\n" >> ./LocalSettings.php
+  - php -l ./LocalSettings.php
 
 script:
   - php tests/phpunit/phpunit.php
index d1ef72f..cb9a20d 100644 (file)
@@ -98,8 +98,8 @@ module.exports = function ( grunt ) {
                        chromium: {
                                browsers: [ 'Chromium' ]
                        },
-                       more: {
-                               browsers: [ 'Chrome', 'Firefox' ]
+                       firefox: {
+                               browsers: [ 'Firefox' ]
                        }
                },
                copy: {
index 4497516..32c1959 100644 (file)
@@ -26,6 +26,8 @@ production.
   default mode.
 * CACHE_ACCEL now only supports APC(u) or WinCache. XCache support was removed
   as upstream is inactive and has no plans to move to PHP 7.
+* The old CategorizedRecentChanges feature, including its related configuration
+  option $wgAllowCategorizedRecentChanges, has been removed.
 
 === New features in 1.31 ===
 * Wikimedia\Rdbms\IDatabase->select() and similar methods now support
index fc77fcb..9042f7b 100644 (file)
@@ -1591,6 +1591,7 @@ $wgAutoloadLocalClasses = [
        'UserBlockedError' => __DIR__ . '/includes/exception/UserBlockedError.php',
        'UserCache' => __DIR__ . '/includes/cache/UserCache.php',
        'UserDupes' => __DIR__ . '/maintenance/userDupes.inc',
+       'UserGroupExpiryJob' => __DIR__ . '/includes/jobqueue/jobs/UserGroupExpiryJob.php',
        'UserGroupMembership' => __DIR__ . '/includes/user/UserGroupMembership.php',
        'UserMailer' => __DIR__ . '/includes/mail/UserMailer.php',
        'UserNamePrefixSearch' => __DIR__ . '/includes/user/UserNamePrefixSearch.php',
index 3561f7f..7446b59 100644 (file)
@@ -42,6 +42,8 @@ use Wikimedia\Rdbms\IDatabase;
  *     $a = $cf->run();
  *     print implode( ',' , $a );
  * @endcode
+ *
+ * @deprecated since 1.31
  */
 class CategoryFinder {
        /** @var int[] The original article IDs passed to the seed function */
index 5c3ac06..ae5cef5 100644 (file)
@@ -6953,11 +6953,6 @@ $wgShowUpdatedMarker = true;
  */
 $wgDisableAnonTalk = false;
 
-/**
- * Enable filtering of categories in Recentchanges
- */
-$wgAllowCategorizedRecentChanges = false;
-
 /**
  * Allow filtering by change tag in recentchanges, history, etc
  * Has no effect if no tags are defined in valid_tag.
@@ -7459,6 +7454,7 @@ $wgJobClasses = [
        'clearUserWatchlist' => ClearUserWatchlistJob::class,
        'cdnPurge' => CdnPurgeJob::class,
        'enqueue' => EnqueueJob::class, // local queue for multi-DC setups
+       'userGroupExpiry' => UserGroupExpiryJob::class,
        'null' => NullJob::class,
 ];
 
diff --git a/includes/DevelopmentSettings.php b/includes/DevelopmentSettings.php
new file mode 100644 (file)
index 0000000..a74353a
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Extra settings useful for MediaWiki development.
+ *
+ * To enable built-in debug and development settings, add the
+ * following to your LocalSettings.php file.
+ *
+ *     require "$IP/includes/DevelopmentSettings.php";
+ *
+ * Alternatively, if running phpunit.php (or another Maintenance script),
+ * you can use the --mwdebug option to automatically load these settings.
+ *
+ * @file
+ */
+
+/**
+ * Debugging: PHP
+ */
+
+// Enable showing of errors
+error_reporting( -1 );
+ini_set( 'display_errors', 1 );
+
+/**
+ * Debugging: MediaWiki
+ */
+global $wgDevelopmentWarnings, $wgShowDBErrorBacktrace, $wgShowExceptionDetails,
+       $wgShowSQLErrors, $wgDebugRawPage,
+       $wgDebugComments, $wgDebugDumpSql, $wgDebugTimestamps,
+       $wgCommandLineMode, $wgDebugLogFile, $wgDBerrorLog, $wgDebugLogGroups;
+
+// Use of wfWarn() should cause tests to fail
+$wgDevelopmentWarnings = true;
+
+// Enable showing of errors
+$wgShowDBErrorBacktrace = true;
+$wgShowExceptionDetails = true;
+$wgShowSQLErrors = true;
+$wgDebugRawPage = true; // T49960
+
+// Enable verbose logging
+$wgDebugComments = true;
+$wgDebugDumpSql = true;
+$wgDebugTimestamps = true;
+
+// Enable log files
+$logDir = getenv( 'MW_LOG_DIR' );
+if ( $logDir ) {
+       if ( $wgCommandLineMode ) {
+               $wgDebugLogFile = "$logDir/mw-debug-cli.log";
+       } else {
+               $wgDebugLogFile = "$logDir/mw-debug-www.log";
+       }
+       $wgDBerrorLog = "$logDir/mw-dberror.log";
+       $wgDebugLogGroups['ratelimit'] = "$logDir/mw-ratelimit.log";
+       $wgDebugLogGroups['exception'] = "$logDir/mw-exception.log";
+       $wgDebugLogGroups['error'] = "$logDir/mw-error.log";
+}
+unset( $logDir );
index 6fbeed7..f9c7fb2 100644 (file)
@@ -325,7 +325,7 @@ class EditPage {
        /** @var bool Has a summary been preset using GET parameter &summary= ? */
        public $hasPresetSummary = false;
 
-       /** @var Revision|bool */
+       /** @var Revision|bool|null */
        public $mBaseRevision = false;
 
        /** @var bool */
@@ -2369,7 +2369,7 @@ ERROR;
        /**
         * @note: this method is very poorly named. If the user opened the form with ?oldid=X,
         *        one might think of X as the "base revision", which is NOT what this returns.
-        * @return Revision Current version when the edit was started
+        * @return Revision|null Current version when the edit was started
         */
        public function getBaseRevision() {
                if ( !$this->mBaseRevision ) {
@@ -4028,7 +4028,6 @@ ERROR;
                        $this->mTitle, $pstContent, $user );
                $parserOutput = $pstContent->getParserOutput( $this->mTitle, null, $parserOptions );
                ScopedCallback::consume( $scopedCallback );
-               $parserOutput->setEditSectionTokens( false ); // no section edit links
                return [
                        'parserOutput' => $parserOutput,
                        'html' => $parserOutput->getText( [
index 01b450b..f95327a 100644 (file)
@@ -287,11 +287,6 @@ class OutputPage extends ContextSource {
         */
        private $mEnableTOC = false;
 
-       /**
-        * @var bool Whether parser output should contain section edit links
-        */
-       private $mEnableSectionEditLinks = true;
-
        /**
         * @var string|null The URL to send in a <link> element with rel=license
         */
@@ -1547,7 +1542,6 @@ class OutputPage extends ContextSource {
                        // Someone is trying to set a bogus pre-$wgUser PO. Check if it has
                        // been changed somehow, and keep it if so.
                        $anonPO = ParserOptions::newFromAnon();
-                       $anonPO->setEditSection( false );
                        $anonPO->setAllowUnsafeRawHtml( false );
                        if ( !$options->matches( $anonPO ) ) {
                                wfLogWarning( __METHOD__ . ': Setting a changed bogus ParserOptions: ' . wfGetAllCallers( 5 ) );
@@ -1561,7 +1555,6 @@ class OutputPage extends ContextSource {
                                // ParserOptions for it. And don't cache this ParserOptions
                                // either.
                                $po = ParserOptions::newFromAnon();
-                               $po->setEditSection( false );
                                $po->setAllowUnsafeRawHtml( false );
                                $po->isBogus = true;
                                if ( $options !== null ) {
@@ -1571,7 +1564,6 @@ class OutputPage extends ContextSource {
                        }
 
                        $this->mParserOptions = ParserOptions::newFromContext( $this->getContext() );
-                       $this->mParserOptions->setEditSection( false );
                        $this->mParserOptions->setAllowUnsafeRawHtml( false );
                }
 
@@ -1821,7 +1813,7 @@ class OutputPage extends ContextSource {
                // so that extensions may modify ParserOutput to toggle TOC.
                // This cannot be moved to addParserOutputText because that is not
                // called by EditPage for Preview.
-               if ( $parserOutput->getTOCEnabled() && $parserOutput->getTOCHTML() ) {
+               if ( $parserOutput->getTOCHTML() ) {
                        $this->mEnableTOC = true;
                }
        }
@@ -1867,17 +1859,6 @@ class OutputPage extends ContextSource {
         */
        function addParserOutput( $parserOutput, $poOptions = [] ) {
                $this->addParserOutputMetadata( $parserOutput );
-
-               // Touch section edit links only if not previously disabled
-               if ( $parserOutput->getEditSectionTokens() ) {
-                       $parserOutput->setEditSectionTokens( $this->mEnableSectionEditLinks );
-               }
-               if ( !$this->mEnableSectionEditLinks
-                       && !array_key_exists( 'enableSectionEditLinks', $poOptions )
-               ) {
-                       $poOptions['enableSectionEditLinks'] = false;
-               }
-
                $this->addParserOutputText( $parserOutput, $poOptions );
        }
 
@@ -3895,7 +3876,7 @@ class OutputPage extends ContextSource {
         * @deprecated since 1.31, use $poOptions to addParserOutput() instead.
         */
        public function enableSectionEditLinks( $flag = true ) {
-               $this->mEnableSectionEditLinks = $flag;
+               wfDeprecated( __METHOD__, '1.31' );
        }
 
        /**
@@ -3904,7 +3885,8 @@ class OutputPage extends ContextSource {
         * @deprecated since 1.31, use $poOptions to addParserOutput() instead.
         */
        public function sectionEditLinksEnabled() {
-               return $this->mEnableSectionEditLinks;
+               wfDeprecated( __METHOD__, '1.31' );
+               return true;
        }
 
        /**
index 7b2b8d3..745c891 100644 (file)
@@ -56,13 +56,13 @@ class SiteStats {
                wfDebug( __METHOD__ . ": reading site_stats from replica DB\n" );
                $row = self::doLoadFromDB( $dbr );
 
-               if ( !self::isSane( $row ) && $lb->hasOrMadeRecentMasterChanges() ) {
+               if ( !self::isRowSane( $row ) && $lb->hasOrMadeRecentMasterChanges() ) {
                        // Might have just been initialized during this request? Underflow?
                        wfDebug( __METHOD__ . ": site_stats damaged or missing on replica DB\n" );
                        $row = self::doLoadFromDB( $lb->getConnection( DB_MASTER ) );
                }
 
-               if ( !self::isSane( $row ) ) {
+               if ( !self::isRowSane( $row ) ) {
                        if ( $config->get( 'MiserMode' ) ) {
                                // Start off with all zeroes, assuming that this is a new wiki or any
                                // repopulations where done manually via script.
@@ -79,35 +79,22 @@ class SiteStats {
                        $row = self::doLoadFromDB( $lb->getConnection( DB_MASTER ) );
                }
 
-               if ( !self::isSane( $row ) ) {
+               if ( !self::isRowSane( $row ) ) {
                        wfDebug( __METHOD__ . ": site_stats persistently nonsensical o_O\n" );
                        // Always return a row-like object
-                       $row = (object)array_fill_keys( self::selectFields(), 0 );
+                       $row = self::salvageInsaneRow( $row );
                }
 
                return $row;
        }
 
-       /**
-        * @param IDatabase $db
-        * @return stdClass|bool
-        */
-       private static function doLoadFromDB( IDatabase $db ) {
-               return $db->selectRow(
-                       'site_stats',
-                       self::selectFields(),
-                       [ 'ss_row_id' => 1 ],
-                       __METHOD__
-               );
-       }
-
        /**
         * @return int
         */
        public static function edits() {
                self::load();
 
-               return self::$row->ss_total_edits;
+               return (int)self::$row->ss_total_edits;
        }
 
        /**
@@ -116,7 +103,7 @@ class SiteStats {
        public static function articles() {
                self::load();
 
-               return self::$row->ss_good_articles;
+               return (int)self::$row->ss_good_articles;
        }
 
        /**
@@ -125,7 +112,7 @@ class SiteStats {
        public static function pages() {
                self::load();
 
-               return self::$row->ss_total_pages;
+               return (int)self::$row->ss_total_pages;
        }
 
        /**
@@ -134,7 +121,7 @@ class SiteStats {
        public static function users() {
                self::load();
 
-               return self::$row->ss_users;
+               return (int)self::$row->ss_users;
        }
 
        /**
@@ -143,7 +130,7 @@ class SiteStats {
        public static function activeUsers() {
                self::load();
 
-               return self::$row->ss_active_users;
+               return (int)self::$row->ss_active_users;
        }
 
        /**
@@ -152,7 +139,7 @@ class SiteStats {
        public static function images() {
                self::load();
 
-               return self::$row->ss_images;
+               return (int)self::$row->ss_images;
        }
 
        /**
@@ -245,6 +232,19 @@ class SiteStats {
                ];
        }
 
+       /**
+        * @param IDatabase $db
+        * @return stdClass|bool
+        */
+       private static function doLoadFromDB( IDatabase $db ) {
+               return $db->selectRow(
+                       'site_stats',
+                       self::selectFields(),
+                       [ 'ss_row_id' => 1 ],
+                       __METHOD__
+               );
+       }
+
        /**
         * Is the provided row of site stats sane, or should it be regenerated?
         *
@@ -253,7 +253,7 @@ class SiteStats {
         * @param bool|object $row
         * @return bool
         */
-       private static function isSane( $row ) {
+       private static function isRowSane( $row ) {
                if ( $row === false
                        || $row->ss_total_pages < $row->ss_good_articles
                        || $row->ss_total_edits < $row->ss_total_pages
@@ -268,7 +268,7 @@ class SiteStats {
                        'ss_users',
                        'ss_images',
                ] as $member ) {
-                       if ( $row->$member > 2000000000 || $row->$member < 0 ) {
+                       if ( $row->$member < 0 ) {
                                return false;
                        }
                }
@@ -276,6 +276,22 @@ class SiteStats {
                return true;
        }
 
+       /**
+        * @param stdClass|bool $row
+        * @return stdClass
+        */
+       private static function salvageInsaneRow( $row ) {
+               $map = $row ? (array)$row : [];
+               // Fill in any missing values with zero
+               $map += array_fill_keys( self::selectFields(), 0 );
+               // Convert negative values to zero
+               foreach ( $map as $field => $value ) {
+                       $map[$field] = max( 0, $value );
+               }
+
+               return (object)$row;
+       }
+
        /**
         * @return LoadBalancer
         */
index f888690..f527cb2 100644 (file)
@@ -198,9 +198,12 @@ class SiteStatsInit {
 
        /**
         * @param int $index
+        * @param string[] $groups
         * @return IDatabase
         */
-       private static function getDB( $index ) {
-               return MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection( $index );
+       private static function getDB( $index, $groups = [] ) {
+               $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+
+               return $lb->getConnection( $index, $groups );
        }
 }
index d832104..e7c9060 100644 (file)
@@ -252,7 +252,7 @@ class RevisionStore
                        if ( $title ) {
                                $this->logger->info(
                                        __METHOD__ . ' fell back to READ_LATEST and got a Title.',
-                                       [ 'trace' => wfDebugBacktrace() ]
+                                       [ 'trace' => wfBacktrace() ]
                                );
                                return $title;
                        }
index 2a80dd5..e5dba8f 100644 (file)
@@ -169,16 +169,6 @@ class ApiFeedRecentChanges extends ApiBase {
                        'showlinkedto' => false,
                ];
 
-               if ( $config->get( 'AllowCategorizedRecentChanges' ) ) {
-                       $ret += [
-                               'categories' => [
-                                       ApiBase::PARAM_TYPE => 'string',
-                                       ApiBase::PARAM_ISMULTI => true,
-                               ],
-                               'categories_any' => false,
-                       ];
-               }
-
                return $ret;
        }
 
index 3326fab..cbd62a9 100644 (file)
@@ -536,7 +536,6 @@ class ApiParse extends ApiBase {
                $popts->enableLimitReport( !$params['disablepp'] && !$params['disablelimitreport'] );
                $popts->setIsPreview( $params['preview'] || $params['sectionpreview'] );
                $popts->setIsSectionPreview( $params['sectionpreview'] );
-               $popts->setEditSection( !$params['disableeditsection'] );
                if ( $params['disabletidy'] ) {
                        $popts->setTidy( false );
                }
index 1689019..8d7a61c 100644 (file)
        "apihelp-feedrecentchanges-param-tagfilter": "Filter by tag.",
        "apihelp-feedrecentchanges-param-target": "Show only changes on pages linked from this page.",
        "apihelp-feedrecentchanges-param-showlinkedto": "Show changes on pages linked to the selected page instead.",
-       "apihelp-feedrecentchanges-param-categories": "Show only changes on pages in all of these categories.",
-       "apihelp-feedrecentchanges-param-categories_any": "Show only changes on pages in any of the categories instead.",
        "apihelp-feedrecentchanges-example-simple": "Show recent changes.",
        "apihelp-feedrecentchanges-example-30days": "Show recent changes for 30 days.",
 
index aa697fb..f3e086d 100644 (file)
        "apihelp-parse-param-disablepp": "Em vez deste, usar <var>$1disablelimitreport</var>.",
        "apihelp-parse-param-disableeditsection": "Omitir as hiperligações para edição da secção no resultado da análise sintática.",
        "apihelp-parse-param-disabletidy": "Não fazer a limpeza do HTML (isto é, o ''tidy'') no resultado da análise sintática.",
+       "apihelp-parse-param-disablestylededuplication": "Não desduplica as folhas de estilo incluídas na saída do analisador sintático.",
        "apihelp-parse-param-generatexml": "Gerar a árvore de análise XML (requer o modelo de conteúdo <code>$1</code>; substituído por <kbd>$2prop=parsetree</kbd>).",
        "apihelp-parse-param-preview": "Executar a análise em modo de antevisão.",
        "apihelp-parse-param-sectionpreview": "Executar a análise em modo de antevisão (também ativa o modo de antevisão).",
index e769880..fc0de4e 100644 (file)
        "apihelp-feedrecentchanges-param-tagfilter": "{{doc-apihelp-param|feedrecentchanges|tagfilter}}",
        "apihelp-feedrecentchanges-param-target": "{{doc-apihelp-param|feedrecentchanges|target}}",
        "apihelp-feedrecentchanges-param-showlinkedto": "{{doc-apihelp-param|feedrecentchanges|showlinkedto}}",
-       "apihelp-feedrecentchanges-param-categories": "{{doc-apihelp-param|feedrecentchanges|categories}}",
-       "apihelp-feedrecentchanges-param-categories_any": "{{doc-apihelp-param|feedrecentchanges|categories_any}}",
        "apihelp-feedrecentchanges-example-simple": "{{doc-apihelp-example|feedrecentchanges}}",
        "apihelp-feedrecentchanges-example-30days": "{{doc-apihelp-example|feedrecentchanges}}",
        "apihelp-feedwatchlist-summary": "{{doc-apihelp-summary|feedwatchlist}}",
index 63c03af..d5ff6cb 100644 (file)
@@ -191,13 +191,11 @@ class MessageCache {
                                // ParserOptions for it. And don't cache this ParserOptions
                                // either.
                                $po = ParserOptions::newFromAnon();
-                               $po->setEditSection( false );
                                $po->setAllowUnsafeRawHtml( false );
                                return $po;
                        }
 
                        $this->mParserOptions = new ParserOptions;
-                       $this->mParserOptions->setEditSection( false );
                        // Messages may take parameters that could come
                        // from malicious sources. As a precaution, disable
                        // the <html> parser tag when parsing messages.
index ad1f172..2f882b8 100644 (file)
@@ -101,9 +101,7 @@ class SiteStatsUpdate implements DeferrableUpdate, MergeableUpdate {
                $pd = [];
                if ( $config->get( 'SiteStatsAsyncFactor' ) ) {
                        // Lock the table so we don't have double DB/memcached updates
-                       if ( !$dbw->lockIsFree( $lockKey, __METHOD__ )
-                               || !$dbw->lock( $lockKey, __METHOD__, 1 ) // 1 sec timeout
-                       ) {
+                       if ( !$dbw->lock( $lockKey, __METHOD__, 0 ) ) {
                                $this->doUpdatePendingDeltas();
 
                                return;
index e76bffc..fa30d68 100644 (file)
@@ -658,11 +658,6 @@ class DifferenceEngine extends ContextSource {
         */
        protected function getParserOutput( WikiPage $page, Revision $rev ) {
                $parserOptions = $page->makeParserOptions( $this->getContext() );
-
-               if ( !$rev->isCurrent() || !$rev->getTitle()->quickUserCan( 'edit', $this->getUser() ) ) {
-                       $parserOptions->setEditSection( false );
-               }
-
                $parserOutput = $page->getParserOutput( $parserOptions, $rev->getId() );
 
                return $parserOutput;
index dbd143c..f5e12d6 100644 (file)
@@ -445,7 +445,6 @@ abstract class Installer {
 
                $this->parserTitle = Title::newFromText( 'Installer' );
                $this->parserOptions = new ParserOptions( $wgUser ); // language will be wrong :(
-               $this->parserOptions->setEditSection( false );
                // Don't try to access DB before user language is initialised
                $this->setParserLanguage( Language::factory( 'en' ) );
        }
index 5b24d7c..92eef95 100644 (file)
@@ -63,7 +63,7 @@
        "config-apc": "[http://www.php.net/apc APC] ta instaláu",
        "config-apcu": "[http://www.php.net/apcu APCu] ta instaláu",
        "config-wincache": "[https://www.iis.net/download/WinCacheForPhp WinCache] ta instaláu",
-       "config-no-cache-apcu": "<strong>Warning:</strong> Non pudo atopase[http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] o [http://www.iis.net/download/WinCacheForPhp WinCache].\nEl caxé d'oxetos nun ta activáu.",
+       "config-no-cache-apcu": "<strong>Atención:</strong> Nun pudo alcontrase [http://www.php.net/apcu APCu] o [http://www.iis.net/download/WinCacheForPhp WinCache].\nLa caché d'oxetos nun ta activada.",
        "config-mod-security": "<strong>Alvertencia:</strong> El to servidor web tien activáu [https://modsecurity.org/mod_security]/mod_security2 .Munches de les sos configuraciones comunes pueden causar problemes a MediaWiki o otru software que dexe a los usuarios publicar conteníu arbitrario. De ser posible, tendríes de desactivalo. Si non, consulta la  [https://modsecurity.org/documentation/ mod_security documentation] o contacta col alministrador del to servidor si atopes erros aleatorios.",
        "config-diff3-bad": "Nun s'alcontró GNU diff3.",
        "config-git": "Alcontróse'l software de control de versiones Git: <code>$1</code>.",
index 74b8ac1..0ca73d3 100644 (file)
@@ -68,7 +68,7 @@
        "config-apc": "[http://www.php.net/apc APC] är installerat",
        "config-apcu": "[http://www.php.net/apcu APCu] är installerat",
        "config-wincache": "[https://www.iis.net/download/WinCacheForPhp WinCache] är installerat",
-       "config-no-cache-apcu": "'''Varning:''' Kunde inte hitta [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] eller [http://www.iis.net/download/WinCacheForPhp WinCache].\nCachelagring av objekt är inte aktiverat.",
+       "config-no-cache-apcu": "<strong>Varning:</strong> Kunde inte hitta [http://www.php.net/apcu APCu] eller [http://www.iis.net/download/WinCacheForPhp WinCache].\nCachelagring av objekt är inte aktiverat.",
        "config-mod-security": "'''Varning:''' Din webbserver har [https://modsecurity.org/ mod_security] aktiverat. Om felaktigt konfigurerat kan den skapa problem för MediaWiki eller annan programvara som tillåter användaren att posta godtyckligt innehåll.\nTitta på [https://modsecurity.org/documentation/ mod_security-dokumentationen] eller kontakta din värd om du påträffar slumpmässiga fel.",
        "config-diff3-bad": "GNU diff3 hittades inte.",
        "config-git": "Hittade Git-mjukvara för versionskontroll: <code>$1</code>.",
        "config-cache-options": "Inställningar för cachelagring av objekt:",
        "config-cache-help": "Cachelagring av objekt används för att förbättra hastigheten på MediaWiki genom att cachelagra data som används ofta.\nMedelstora till stora webbplatser är starkt uppmuntrade att aktivera detta, och små webbplatser kommer även att se fördelar.",
        "config-cache-none": "Ingen cachelagring (ingen funktionalitet tas bort, men hastighet kan påverkas på större wiki-webbplatser)",
-       "config-cache-accel": "Cachelagring av PHP-objekt (APC, APCu, XCache eller WinCache)",
+       "config-cache-accel": "Cachelagring av PHP-objekt (APC, APCu eller WinCache)",
        "config-cache-memcached": "Använda Memcached (kräver ytterligare inställningar och konfiguration)",
        "config-memcached-servers": "Memcached-servrar:",
        "config-memcached-help": "Lista över IP-adresser som ska användas för Memcached.\nBör ange en per rad och specificera den port som ska användas. Till exempel:\n 127.0.0.1:11211\n 192.168.1.25:1234",
index a92ae96..d97e4f9 100644 (file)
@@ -76,10 +76,9 @@ class RecentChangesUpdateJob extends Job {
                $lockKey = wfWikiID() . ':recentchanges-prune';
 
                $dbw = wfGetDB( DB_MASTER );
-               if ( !$dbw->lockIsFree( $lockKey, __METHOD__ )
-                       || !$dbw->lock( $lockKey, __METHOD__, 1 )
-               ) {
-                       return; // already in progress
+               if ( !$dbw->lock( $lockKey, __METHOD__, 0 ) ) {
+                       // already in progress
+                       return;
                }
 
                $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
@@ -138,7 +137,7 @@ class RecentChangesUpdateJob extends Job {
                                $dbw->setSessionOptions( [ 'connTimeout' => 900 ] );
 
                                $lockKey = wfWikiID() . '-activeusers';
-                               if ( !$dbw->lockIsFree( $lockKey, __METHOD__ ) || !$dbw->lock( $lockKey, __METHOD__, 1 ) ) {
+                               if ( !$dbw->lock( $lockKey, __METHOD__, 0 ) ) {
                                        // Exclusive update (avoids duplicate entries)… it's usually fine to just drop out here,
                                        // if the Job is already running.
                                        return;
diff --git a/includes/jobqueue/jobs/UserGroupExpiryJob.php b/includes/jobqueue/jobs/UserGroupExpiryJob.php
new file mode 100644 (file)
index 0000000..0945e58
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Job that purges expired user group memberships.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup JobQueue
+ */
+
+class UserGroupExpiryJob extends Job {
+       public function __construct( $params = false ) {
+               parent::__construct( 'userGroupExpiry', Title::newMainPage(), $params );
+               $this->removeDuplicates = true;
+       }
+
+       /**
+        * Run the job
+        * @return bool Success
+        */
+       public function run() {
+               UserGroupMembership::purgeExpired();
+
+               return true;
+       }
+}
index eec766b..58d359c 100644 (file)
@@ -1536,7 +1536,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        }
 
        /**
-        * Locally set a key to expire soon if it is stale based on $purgeTimestamp
+        * Set a key to soon expire in the local cluster if it pre-dates $purgeTimestamp
         *
         * This sets stale keys' time-to-live at HOLDOFF_TTL seconds, which both avoids
         * broadcasting in mcrouter setups and also avoids races with new tombstones.
@@ -1568,7 +1568,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        }
 
        /**
-        * Locally set a "check" key to expire soon if it is stale based on $purgeTimestamp
+        * Set a "check" key to soon expire in the local cluster if it pre-dates $purgeTimestamp
         *
         * @param string $key Cache key
         * @param int $purgeTimestamp UNIX timestamp of purge
index d1814e1..b24b8cb 100644 (file)
@@ -1405,13 +1405,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                $this->tableNamesWithIndexClauseOrJOIN(
                                        $table, $useIndexes, $ignoreIndexes, $join_conds );
                } elseif ( $table != '' ) {
-                       if ( $table[0] == ' ' ) {
-                               $from = ' FROM ' . $table;
-                       } else {
-                               $from = ' FROM ' .
-                                       $this->tableNamesWithIndexClauseOrJOIN(
-                                               [ $table ], $useIndexes, $ignoreIndexes, [] );
-                       }
+                       $from = ' FROM ' .
+                               $this->tableNamesWithIndexClauseOrJOIN(
+                                       [ $table ], $useIndexes, $ignoreIndexes, [] );
                } else {
                        $from = '';
                }
index e246b79..088c3f2 100644 (file)
@@ -170,15 +170,21 @@ interface ILoadBalancer {
        public function getAnyOpenConnection( $i );
 
        /**
-        * Get a connection by index
+        * Get a connection handle by server index
         *
         * Avoid using CONN_TRX_AUTO with sqlite (e.g. check getServerType() first)
         *
+        * If the caller uses $domain or sets CONN_TRX_AUTO in $flags, then it must also
+        * call ILoadBalancer::reuseConnection() on the handle when finished using it.
+        * In all other cases, this is not necessary, though not harmful either.
+        *
         * @param int $i Server index or DB_MASTER/DB_REPLICA
         * @param array|string|bool $groups Query group(s), or false for the generic reader
         * @param string|bool $domain Domain ID, or false for the current domain
         * @param int $flags Bitfield of CONN_* class constants
         *
+        * @note This method throws DBAccessError if ILoadBalancer::disable() was called
+        *
         * @throws DBError
         * @return Database
         */
@@ -254,7 +260,11 @@ interface ILoadBalancer {
         *
         * Avoid using CONN_TRX_AUTO with sqlite (e.g. check getServerType() first)
         *
-        * @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
+        * If the caller uses $domain or sets CONN_TRX_AUTO in $flags, then it must also
+        * call ILoadBalancer::reuseConnection() on the handle when finished using it.
+        * In all other cases, this is not necessary, though not harmful either.
+        *
+        * @note This method throws DBAccessError if ILoadBalancer::disable() was called
         *
         * @param int $i Server index (does not support DB_MASTER/DB_REPLICA)
         * @param string|bool $domain Domain ID, or false for the current domain
index 04b3ea3..ccdb48e 100644 (file)
@@ -39,15 +39,15 @@ use Exception;
  */
 class LoadBalancer implements ILoadBalancer {
        /** @var array[] Map of (server index => server config array) */
-       private $mServers;
+       private $servers;
        /** @var Database[][][] Map of (connection category => server index => IDatabase[]) */
-       private $mConns;
+       private $conns;
        /** @var float[] Map of (server index => weight) */
-       private $mLoads;
+       private $loads;
        /** @var array[] Map of (group => server index => weight) */
-       private $mGroupLoads;
+       private $groupLoads;
        /** @var bool Whether to disregard replica DB lag as a factor in replica DB selection */
-       private $mAllowLagged;
+       private $allowLagged;
        /** @var int Seconds to spend waiting on replica DB lag to resolve */
        private $waitTimeout;
        /** @var array The LoadMonitor configuration */
@@ -79,15 +79,15 @@ class LoadBalancer implements ILoadBalancer {
        /** @var Database DB connection object that caused a problem */
        private $errorConnection;
        /** @var int The generic (not query grouped) replica DB index (of $mServers) */
-       private $mReadIndex;
+       private $readIndex;
        /** @var bool|DBMasterPos False if not set */
-       private $mWaitForPos;
+       private $waitForPos;
        /** @var bool Whether the generic reader fell back to a lagged replica DB */
        private $laggedReplicaMode = false;
        /** @var bool Whether the generic reader fell back to a lagged replica DB */
        private $allReplicasDownMode = false;
        /** @var string The last DB selection or connection error */
-       private $mLastError = 'Unknown error';
+       private $lastError = 'Unknown error';
        /** @var string|bool Reason the LB is read-only or false if not */
        private $readOnlyReason = false;
        /** @var int Total connections opened */
@@ -139,12 +139,12 @@ class LoadBalancer implements ILoadBalancer {
                if ( !isset( $params['servers'] ) ) {
                        throw new InvalidArgumentException( __CLASS__ . ': missing servers parameter' );
                }
-               $this->mServers = $params['servers'];
-               foreach ( $this->mServers as $i => $server ) {
+               $this->servers = $params['servers'];
+               foreach ( $this->servers as $i => $server ) {
                        if ( $i == 0 ) {
-                               $this->mServers[$i]['master'] = true;
+                               $this->servers[$i]['master'] = true;
                        } else {
-                               $this->mServers[$i]['replica'] = true;
+                               $this->servers[$i]['replica'] = true;
                        }
                }
 
@@ -157,8 +157,8 @@ class LoadBalancer implements ILoadBalancer {
                        ? $params['waitTimeout']
                        : self::MAX_WAIT_DEFAULT;
 
-               $this->mReadIndex = -1;
-               $this->mConns = [
+               $this->readIndex = -1;
+               $this->conns = [
                        // Connection were transaction rounds may be applied
                        self::KEY_LOCAL => [],
                        self::KEY_FOREIGN_INUSE => [],
@@ -168,9 +168,9 @@ class LoadBalancer implements ILoadBalancer {
                        self::KEY_FOREIGN_INUSE_NOROUND => [],
                        self::KEY_FOREIGN_FREE_NOROUND => []
                ];
-               $this->mLoads = [];
-               $this->mWaitForPos = false;
-               $this->mAllowLagged = false;
+               $this->loads = [];
+               $this->waitForPos = false;
+               $this->allowLagged = false;
 
                if ( isset( $params['readOnlyReason'] ) && is_string( $params['readOnlyReason'] ) ) {
                        $this->readOnlyReason = $params['readOnlyReason'];
@@ -188,13 +188,13 @@ class LoadBalancer implements ILoadBalancer {
                $this->loadMonitorConfig += [ 'lagWarnThreshold' => $this->maxLag ];
 
                foreach ( $params['servers'] as $i => $server ) {
-                       $this->mLoads[$i] = $server['load'];
+                       $this->loads[$i] = $server['load'];
                        if ( isset( $server['groupLoads'] ) ) {
                                foreach ( $server['groupLoads'] as $group => $ratio ) {
-                                       if ( !isset( $this->mGroupLoads[$group] ) ) {
-                                               $this->mGroupLoads[$group] = [];
+                                       if ( !isset( $this->groupLoads[$group] ) ) {
+                                               $this->groupLoads[$group] = [];
                                        }
-                                       $this->mGroupLoads[$group][$i] = $ratio;
+                                       $this->groupLoads[$group][$i] = $ratio;
                                }
                        }
                }
@@ -289,8 +289,8 @@ class LoadBalancer implements ILoadBalancer {
                foreach ( $lags as $i => $lag ) {
                        if ( $i != 0 ) {
                                # How much lag this server nominally is allowed to have
-                               $maxServerLag = isset( $this->mServers[$i]['max lag'] )
-                                       ? $this->mServers[$i]['max lag']
+                               $maxServerLag = isset( $this->servers[$i]['max lag'] )
+                                       ? $this->servers[$i]['max lag']
                                        : $this->maxLag; // default
                                # Constrain that futher by $maxLag argument
                                $maxServerLag = min( $maxServerLag, $maxLag );
@@ -332,18 +332,18 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function getReaderIndex( $group = false, $domain = false ) {
-               if ( count( $this->mServers ) == 1 ) {
+               if ( count( $this->servers ) == 1 ) {
                        // Skip the load balancing if there's only one server
                        return $this->getWriterIndex();
-               } elseif ( $group === false && $this->mReadIndex >= 0 ) {
+               } elseif ( $group === false && $this->readIndex >= 0 ) {
                        // Shortcut if the generic reader index was already cached
-                       return $this->mReadIndex;
+                       return $this->readIndex;
                }
 
                if ( $group !== false ) {
                        // Use the server weight array for this load group
-                       if ( isset( $this->mGroupLoads[$group] ) ) {
-                               $loads = $this->mGroupLoads[$group];
+                       if ( isset( $this->groupLoads[$group] ) ) {
+                               $loads = $this->groupLoads[$group];
                        } else {
                                // No loads for this group, return false and the caller can use some other group
                                $this->connLogger->info( __METHOD__ . ": no loads for group $group" );
@@ -352,7 +352,7 @@ class LoadBalancer implements ILoadBalancer {
                        }
                } else {
                        // Use the generic load group
-                       $loads = $this->mLoads;
+                       $loads = $this->loads;
                }
 
                // Scale the configured load ratios according to each server's load and state
@@ -365,7 +365,7 @@ class LoadBalancer implements ILoadBalancer {
                        return false;
                }
 
-               if ( $this->mWaitForPos && $i != $this->getWriterIndex() ) {
+               if ( $this->waitForPos && $i != $this->getWriterIndex() ) {
                        // Before any data queries are run, wait for the server to catch up to the
                        // specified position. This is used to improve session consistency. Note that
                        // when LoadBalancer::waitFor() sets mWaitForPos, the waiting triggers here,
@@ -375,9 +375,9 @@ class LoadBalancer implements ILoadBalancer {
                        }
                }
 
-               if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group === false ) {
+               if ( $this->readIndex <= 0 && $this->loads[$i] > 0 && $group === false ) {
                        // Cache the generic reader index for future ungrouped DB_REPLICA handles
-                       $this->mReadIndex = $i;
+                       $this->readIndex = $i;
                        // Record if the generic reader index is in "lagged replica DB" mode
                        if ( $laggedReplicaMode ) {
                                $this->laggedReplicaMode = true;
@@ -408,15 +408,15 @@ class LoadBalancer implements ILoadBalancer {
                // Quickly look through the available servers for a server that meets criteria...
                $currentLoads = $loads;
                while ( count( $currentLoads ) ) {
-                       if ( $this->mAllowLagged || $laggedReplicaMode ) {
+                       if ( $this->allowLagged || $laggedReplicaMode ) {
                                $i = ArrayUtils::pickRandom( $currentLoads );
                        } else {
                                $i = false;
-                               if ( $this->mWaitForPos && $this->mWaitForPos->asOfTime() ) {
+                               if ( $this->waitForPos && $this->waitForPos->asOfTime() ) {
                                        // ChronologyProtecter sets mWaitForPos for session consistency.
                                        // This triggers doWait() after connect, so it's especially good to
                                        // avoid lagged servers so as to avoid excessive delay in that method.
-                                       $ago = microtime( true ) - $this->mWaitForPos->asOfTime();
+                                       $ago = microtime( true ) - $this->waitForPos->asOfTime();
                                        // Aim for <= 1 second of waiting (being too picky can backfire)
                                        $i = $this->getRandomNonLagged( $currentLoads, $domain, $ago + 1 );
                                }
@@ -471,11 +471,11 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function waitFor( $pos ) {
-               $oldPos = $this->mWaitForPos;
+               $oldPos = $this->waitForPos;
                try {
-                       $this->mWaitForPos = $pos;
+                       $this->waitForPos = $pos;
                        // If a generic reader connection was already established, then wait now
-                       $i = $this->mReadIndex;
+                       $i = $this->readIndex;
                        if ( $i > 0 ) {
                                if ( !$this->doWait( $i ) ) {
                                        $this->laggedReplicaMode = true;
@@ -488,14 +488,14 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function waitForOne( $pos, $timeout = null ) {
-               $oldPos = $this->mWaitForPos;
+               $oldPos = $this->waitForPos;
                try {
-                       $this->mWaitForPos = $pos;
+                       $this->waitForPos = $pos;
 
-                       $i = $this->mReadIndex;
+                       $i = $this->readIndex;
                        if ( $i <= 0 ) {
                                // Pick a generic replica DB if there isn't one yet
-                               $readLoads = $this->mLoads;
+                               $readLoads = $this->loads;
                                unset( $readLoads[$this->getWriterIndex()] ); // replica DBs only
                                $readLoads = array_filter( $readLoads ); // with non-zero load
                                $i = ArrayUtils::pickRandom( $readLoads );
@@ -508,7 +508,7 @@ class LoadBalancer implements ILoadBalancer {
                        }
                } finally {
                        # Restore the old position, as this is not used for lag-protection but for throttling
-                       $this->mWaitForPos = $oldPos;
+                       $this->waitForPos = $oldPos;
                }
 
                return $ok;
@@ -517,14 +517,14 @@ class LoadBalancer implements ILoadBalancer {
        public function waitForAll( $pos, $timeout = null ) {
                $timeout = $timeout ?: $this->waitTimeout;
 
-               $oldPos = $this->mWaitForPos;
+               $oldPos = $this->waitForPos;
                try {
-                       $this->mWaitForPos = $pos;
-                       $serverCount = count( $this->mServers );
+                       $this->waitForPos = $pos;
+                       $serverCount = count( $this->servers );
 
                        $ok = true;
                        for ( $i = 1; $i < $serverCount; $i++ ) {
-                               if ( $this->mLoads[$i] > 0 ) {
+                               if ( $this->loads[$i] > 0 ) {
                                        $start = microtime( true );
                                        $ok = $this->doWait( $i, true, $timeout ) && $ok;
                                        $timeout -= ( microtime( true ) - $start );
@@ -535,7 +535,7 @@ class LoadBalancer implements ILoadBalancer {
                        }
                } finally {
                        # Restore the old position, as this is not used for lag-protection but for throttling
-                       $this->mWaitForPos = $oldPos;
+                       $this->waitForPos = $oldPos;
                }
 
                return $ok;
@@ -549,8 +549,8 @@ class LoadBalancer implements ILoadBalancer {
                        return;
                }
 
-               if ( !$this->mWaitForPos || $pos->hasReached( $this->mWaitForPos ) ) {
-                       $this->mWaitForPos = $pos;
+               if ( !$this->waitForPos || $pos->hasReached( $this->waitForPos ) ) {
+                       $this->waitForPos = $pos;
                }
        }
 
@@ -559,7 +559,7 @@ class LoadBalancer implements ILoadBalancer {
         * @return IDatabase|bool
         */
        public function getAnyOpenConnection( $i ) {
-               foreach ( $this->mConns as $connsByServer ) {
+               foreach ( $this->conns as $connsByServer ) {
                        if ( !empty( $connsByServer[$i] ) ) {
                                /** @var IDatabase[] $serverConns */
                                $serverConns = $connsByServer[$i];
@@ -588,7 +588,7 @@ class LoadBalancer implements ILoadBalancer {
                $knownReachedPos = $this->srvCache->get( $key );
                if (
                        $knownReachedPos instanceof DBMasterPos &&
-                       $knownReachedPos->hasReached( $this->mWaitForPos )
+                       $knownReachedPos->hasReached( $this->waitForPos )
                ) {
                        $this->replLogger->debug(
                                __METHOD__ .
@@ -631,14 +631,14 @@ class LoadBalancer implements ILoadBalancer {
                        [ 'dbserver' => $server ]
                );
 
-               $result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
+               $result = $conn->masterPosWait( $this->waitForPos, $timeout );
 
                if ( $result === null ) {
                        $this->replLogger->warning(
                                __METHOD__ . ': Errored out waiting on {host} pos {pos}',
                                [
                                        'host' => $server,
-                                       'pos' => $this->mWaitForPos,
+                                       'pos' => $this->waitForPos,
                                        'trace' => ( new RuntimeException() )->getTraceAsString()
                                ]
                        );
@@ -648,7 +648,7 @@ class LoadBalancer implements ILoadBalancer {
                                __METHOD__ . ': Timed out waiting on {host} pos {pos}',
                                [
                                        'host' => $server,
-                                       'pos' => $this->mWaitForPos,
+                                       'pos' => $this->waitForPos,
                                        'trace' => ( new RuntimeException() )->getTraceAsString()
                                ]
                        );
@@ -657,7 +657,7 @@ class LoadBalancer implements ILoadBalancer {
                        $this->replLogger->info( __METHOD__ . ": Done" );
                        $ok = true;
                        // Remember that the DB reached this point
-                       $this->srvCache->set( $key, $this->mWaitForPos, BagOStuff::TTL_DAY );
+                       $this->srvCache->set( $key, $this->waitForPos, BagOStuff::TTL_DAY );
                }
 
                if ( $close ) {
@@ -699,14 +699,14 @@ class LoadBalancer implements ILoadBalancer {
 
                # Operation-based index
                if ( $i == self::DB_REPLICA ) {
-                       $this->mLastError = 'Unknown error'; // reset error string
+                       $this->lastError = 'Unknown error'; // reset error string
                        # Try the general server pool if $groups are unavailable.
                        $i = ( $groups === [ false ] )
                                ? false // don't bother with this if that is what was tried above
                                : $this->getReaderIndex( false, $domain );
                        # Couldn't find a working server in getReaderIndex()?
                        if ( $i === false ) {
-                               $this->mLastError = 'No working replica DB server: ' . $this->mLastError;
+                               $this->lastError = 'No working replica DB server: ' . $this->lastError;
                                // Throw an exception
                                $this->reportConnectionError();
                                return null; // not reached
@@ -773,20 +773,20 @@ class LoadBalancer implements ILoadBalancer {
                }
 
                $domain = $conn->getDomainID();
-               if ( !isset( $this->mConns[$connInUseKey][$serverIndex][$domain] ) ) {
+               if ( !isset( $this->conns[$connInUseKey][$serverIndex][$domain] ) ) {
                        throw new InvalidArgumentException( __METHOD__ .
                                ": connection $serverIndex/$domain not found; it may have already been freed." );
-               } elseif ( $this->mConns[$connInUseKey][$serverIndex][$domain] !== $conn ) {
+               } elseif ( $this->conns[$connInUseKey][$serverIndex][$domain] !== $conn ) {
                        throw new InvalidArgumentException( __METHOD__ .
                                ": connection $serverIndex/$domain mismatched; it may have already been freed." );
                }
 
                $conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
                if ( $refCount <= 0 ) {
-                       $this->mConns[$connFreeKey][$serverIndex][$domain] = $conn;
-                       unset( $this->mConns[$connInUseKey][$serverIndex][$domain] );
-                       if ( !$this->mConns[$connInUseKey][$serverIndex] ) {
-                               unset( $this->mConns[$connInUseKey][$serverIndex] ); // clean up
+                       $this->conns[$connFreeKey][$serverIndex][$domain] = $conn;
+                       unset( $this->conns[$connInUseKey][$serverIndex][$domain] );
+                       if ( !$this->conns[$connInUseKey][$serverIndex] ) {
+                               unset( $this->conns[$connInUseKey][$serverIndex] ); // clean up
                        }
                        $this->connLogger->debug( __METHOD__ . ": freed connection $serverIndex/$domain" );
                } else {
@@ -838,14 +838,14 @@ class LoadBalancer implements ILoadBalancer {
                } else {
                        // Connection is to the local domain
                        $connKey = $autoCommit ? self::KEY_LOCAL_NOROUND : self::KEY_LOCAL;
-                       if ( isset( $this->mConns[$connKey][$i][0] ) ) {
-                               $conn = $this->mConns[$connKey][$i][0];
+                       if ( isset( $this->conns[$connKey][$i][0] ) ) {
+                               $conn = $this->conns[$connKey][$i][0];
                        } else {
-                               if ( !isset( $this->mServers[$i] ) || !is_array( $this->mServers[$i] ) ) {
+                               if ( !isset( $this->servers[$i] ) || !is_array( $this->servers[$i] ) ) {
                                        throw new InvalidArgumentException( "No server with index '$i'." );
                                }
                                // Open a new connection
-                               $server = $this->mServers[$i];
+                               $server = $this->servers[$i];
                                $server['serverIndex'] = $i;
                                $server['autoCommitOnly'] = $autoCommit;
                                if ( $this->localDomain->getDatabase() !== null ) {
@@ -856,7 +856,7 @@ class LoadBalancer implements ILoadBalancer {
                                $host = $this->getServerName( $i );
                                if ( $conn->isOpen() ) {
                                        $this->connLogger->debug( "Connected to database $i at '$host'." );
-                                       $this->mConns[$connKey][$i][0] = $conn;
+                                       $this->conns[$connKey][$i][0] = $conn;
                                } else {
                                        $this->connLogger->warning( "Failed to connect to database $i at '$host'." );
                                        $this->errorConnection = $conn;
@@ -916,39 +916,39 @@ class LoadBalancer implements ILoadBalancer {
                        $connInUseKey = self::KEY_FOREIGN_INUSE;
                }
 
-               if ( isset( $this->mConns[$connInUseKey][$i][$domain] ) ) {
+               if ( isset( $this->conns[$connInUseKey][$i][$domain] ) ) {
                        // Reuse an in-use connection for the same domain
-                       $conn = $this->mConns[$connInUseKey][$i][$domain];
+                       $conn = $this->conns[$connInUseKey][$i][$domain];
                        $this->connLogger->debug( __METHOD__ . ": reusing connection $i/$domain" );
-               } elseif ( isset( $this->mConns[$connFreeKey][$i][$domain] ) ) {
+               } elseif ( isset( $this->conns[$connFreeKey][$i][$domain] ) ) {
                        // Reuse a free connection for the same domain
-                       $conn = $this->mConns[$connFreeKey][$i][$domain];
-                       unset( $this->mConns[$connFreeKey][$i][$domain] );
-                       $this->mConns[$connInUseKey][$i][$domain] = $conn;
+                       $conn = $this->conns[$connFreeKey][$i][$domain];
+                       unset( $this->conns[$connFreeKey][$i][$domain] );
+                       $this->conns[$connInUseKey][$i][$domain] = $conn;
                        $this->connLogger->debug( __METHOD__ . ": reusing free connection $i/$domain" );
-               } elseif ( !empty( $this->mConns[$connFreeKey][$i] ) ) {
+               } elseif ( !empty( $this->conns[$connFreeKey][$i] ) ) {
                        // Reuse a free connection from another domain
-                       $conn = reset( $this->mConns[$connFreeKey][$i] );
-                       $oldDomain = key( $this->mConns[$connFreeKey][$i] );
+                       $conn = reset( $this->conns[$connFreeKey][$i] );
+                       $oldDomain = key( $this->conns[$connFreeKey][$i] );
                        if ( strlen( $dbName ) && !$conn->selectDB( $dbName ) ) {
-                               $this->mLastError = "Error selecting database '$dbName' on server " .
+                               $this->lastError = "Error selecting database '$dbName' on server " .
                                        $conn->getServer() . " from client host {$this->host}";
                                $this->errorConnection = $conn;
                                $conn = false;
                        } else {
                                $conn->tablePrefix( $prefix );
-                               unset( $this->mConns[$connFreeKey][$i][$oldDomain] );
+                               unset( $this->conns[$connFreeKey][$i][$oldDomain] );
                                // Note that if $domain is an empty string, getDomainID() might not match it
-                               $this->mConns[$connInUseKey][$i][$conn->getDomainId()] = $conn;
+                               $this->conns[$connInUseKey][$i][$conn->getDomainId()] = $conn;
                                $this->connLogger->debug( __METHOD__ .
                                        ": reusing free connection from $oldDomain for $domain" );
                        }
                } else {
-                       if ( !isset( $this->mServers[$i] ) || !is_array( $this->mServers[$i] ) ) {
+                       if ( !isset( $this->servers[$i] ) || !is_array( $this->servers[$i] ) ) {
                                throw new InvalidArgumentException( "No server with index '$i'." );
                        }
                        // Open a new connection
-                       $server = $this->mServers[$i];
+                       $server = $this->servers[$i];
                        $server['serverIndex'] = $i;
                        $server['foreignPoolRefCount'] = 0;
                        $server['foreign'] = true;
@@ -961,7 +961,7 @@ class LoadBalancer implements ILoadBalancer {
                        } else {
                                $conn->tablePrefix( $prefix ); // as specified
                                // Note that if $domain is an empty string, getDomainID() might not match it
-                               $this->mConns[$connInUseKey][$i][$conn->getDomainID()] = $conn;
+                               $this->conns[$connInUseKey][$i][$conn->getDomainID()] = $conn;
                                $this->connLogger->debug( __METHOD__ . ": opened new connection for $i/$domain" );
                        }
                }
@@ -1079,7 +1079,7 @@ class LoadBalancer implements ILoadBalancer {
                $conn = $this->errorConnection; // the connection which caused the error
                $context = [
                        'method' => __METHOD__,
-                       'last_error' => $this->mLastError,
+                       'last_error' => $this->lastError,
                ];
 
                if ( $conn instanceof IDatabase ) {
@@ -1090,7 +1090,7 @@ class LoadBalancer implements ILoadBalancer {
                        );
 
                        // throws DBConnectionError
-                       $conn->reportConnectionError( "{$this->mLastError} ({$context['db_server']})" );
+                       $conn->reportConnectionError( "{$this->lastError} ({$context['db_server']})" );
                } else {
                        // No last connection, probably due to all servers being too busy
                        $this->connLogger->error(
@@ -1099,7 +1099,7 @@ class LoadBalancer implements ILoadBalancer {
                        );
 
                        // If all servers were busy, mLastError will contain something sensible
-                       throw new DBConnectionError( null, $this->mLastError );
+                       throw new DBConnectionError( null, $this->lastError );
                }
        }
 
@@ -1108,22 +1108,22 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function haveIndex( $i ) {
-               return array_key_exists( $i, $this->mServers );
+               return array_key_exists( $i, $this->servers );
        }
 
        public function isNonZeroLoad( $i ) {
-               return array_key_exists( $i, $this->mServers ) && $this->mLoads[$i] != 0;
+               return array_key_exists( $i, $this->servers ) && $this->loads[$i] != 0;
        }
 
        public function getServerCount() {
-               return count( $this->mServers );
+               return count( $this->servers );
        }
 
        public function getServerName( $i ) {
-               if ( isset( $this->mServers[$i]['hostName'] ) ) {
-                       $name = $this->mServers[$i]['hostName'];
-               } elseif ( isset( $this->mServers[$i]['host'] ) ) {
-                       $name = $this->mServers[$i]['host'];
+               if ( isset( $this->servers[$i]['hostName'] ) ) {
+                       $name = $this->servers[$i]['hostName'];
+               } elseif ( isset( $this->servers[$i]['host'] ) ) {
+                       $name = $this->servers[$i]['host'];
                } else {
                        $name = '';
                }
@@ -1132,7 +1132,7 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function getServerType( $i ) {
-               return isset( $this->mServers[$i]['type'] ) ? $this->mServers[$i]['type'] : 'unknown';
+               return isset( $this->servers[$i]['type'] ) ? $this->servers[$i]['type'] : 'unknown';
        }
 
        public function getMasterPos() {
@@ -1140,7 +1140,7 @@ class LoadBalancer implements ILoadBalancer {
                # master (however unlikely that may be), then we can fetch the position from the replica DB.
                $masterConn = $this->getAnyOpenConnection( $this->getWriterIndex() );
                if ( !$masterConn ) {
-                       $serverCount = count( $this->mServers );
+                       $serverCount = count( $this->servers );
                        for ( $i = 1; $i < $serverCount; $i++ ) {
                                $conn = $this->getAnyOpenConnection( $i );
                                if ( $conn ) {
@@ -1166,7 +1166,7 @@ class LoadBalancer implements ILoadBalancer {
                        $conn->close();
                } );
 
-               $this->mConns = [
+               $this->conns = [
                        self::KEY_LOCAL => [],
                        self::KEY_FOREIGN_INUSE => [],
                        self::KEY_FOREIGN_FREE => [],
@@ -1179,7 +1179,7 @@ class LoadBalancer implements ILoadBalancer {
 
        public function closeConnection( IDatabase $conn ) {
                $serverIndex = $conn->getLBInfo( 'serverIndex' ); // second index level of mConns
-               foreach ( $this->mConns as $type => $connsByServer ) {
+               foreach ( $this->conns as $type => $connsByServer ) {
                        if ( !isset( $connsByServer[$serverIndex] ) ) {
                                continue;
                        }
@@ -1188,7 +1188,7 @@ class LoadBalancer implements ILoadBalancer {
                                if ( $conn === $trackedConn ) {
                                        $host = $this->getServerName( $i );
                                        $this->connLogger->debug( "Closing connection to database $i at '$host'." );
-                                       unset( $this->mConns[$type][$serverIndex][$i] );
+                                       unset( $this->conns[$type][$serverIndex][$i] );
                                        --$this->connsOpened;
                                        break 2;
                                }
@@ -1552,11 +1552,11 @@ class LoadBalancer implements ILoadBalancer {
 
        public function allowLagged( $mode = null ) {
                if ( $mode === null ) {
-                       return $this->mAllowLagged;
+                       return $this->allowLagged;
                }
-               $this->mAllowLagged = $mode;
+               $this->allowLagged = $mode;
 
-               return $this->mAllowLagged;
+               return $this->allowLagged;
        }
 
        public function pingAll() {
@@ -1571,7 +1571,7 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function forEachOpenConnection( $callback, array $params = [] ) {
-               foreach ( $this->mConns as $connsByServer ) {
+               foreach ( $this->conns as $connsByServer ) {
                        foreach ( $connsByServer as $serverConns ) {
                                foreach ( $serverConns as $conn ) {
                                        $mergedParams = array_merge( [ $conn ], $params );
@@ -1583,7 +1583,7 @@ class LoadBalancer implements ILoadBalancer {
 
        public function forEachOpenMasterConnection( $callback, array $params = [] ) {
                $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $connsByServer ) {
+               foreach ( $this->conns as $connsByServer ) {
                        if ( isset( $connsByServer[$masterIndex] ) ) {
                                /** @var IDatabase $conn */
                                foreach ( $connsByServer[$masterIndex] as $conn ) {
@@ -1595,7 +1595,7 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function forEachOpenReplicaConnection( $callback, array $params = [] ) {
-               foreach ( $this->mConns as $connsByServer ) {
+               foreach ( $this->conns as $connsByServer ) {
                        foreach ( $connsByServer as $i => $serverConns ) {
                                if ( $i === $this->getWriterIndex() ) {
                                        continue; // skip master
@@ -1619,9 +1619,9 @@ class LoadBalancer implements ILoadBalancer {
 
                $lagTimes = $this->getLagTimes( $domain );
                foreach ( $lagTimes as $i => $lag ) {
-                       if ( $this->mLoads[$i] > 0 && $lag > $maxLag ) {
+                       if ( $this->loads[$i] > 0 && $lag > $maxLag ) {
                                $maxLag = $lag;
-                               $host = $this->mServers[$i]['host'];
+                               $host = $this->servers[$i]['host'];
                                $maxIndex = $i;
                        }
                }
@@ -1636,7 +1636,7 @@ class LoadBalancer implements ILoadBalancer {
 
                $knownLagTimes = []; // map of (server index => 0 seconds)
                $indexesWithLag = [];
-               foreach ( $this->mServers as $i => $server ) {
+               foreach ( $this->servers as $i => $server ) {
                        if ( empty( $server['is static'] ) ) {
                                $indexesWithLag[] = $i; // DB server might have replication lag
                        } else {
index 343c4c8..8eb3709 100644 (file)
@@ -480,12 +480,10 @@ class Article implements Page {
                # Render printable version, use printable version cache
                if ( $outputPage->isPrintable() ) {
                        $parserOptions->setIsPrintable( true );
-                       $parserOptions->setEditSection( false );
                        $poOptions['enableSectionEditLinks'] = false;
                } elseif ( $this->disableSectionEditForRender
                        || !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user )
                ) {
-                       $parserOptions->setEditSection( false );
                        $poOptions['enableSectionEditLinks'] = false;
                }
 
@@ -1526,7 +1524,6 @@ class Article implements Page {
        public function render() {
                $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
                $this->getContext()->getOutput()->setArticleBodyOnly( true );
-               $this->getContext()->getOutput()->enableSectionEditLinks( false );
                $this->disableSectionEditForRender = true;
                $this->view();
        }
index b2d8511..2adfd0a 100644 (file)
@@ -4032,13 +4032,9 @@ class Parser {
 
                # Inhibit editsection links if requested in the page
                if ( isset( $this->mDoubleUnderscores['noeditsection'] ) ) {
-                       $maybeShowEditLink = $showEditLink = false;
+                       $maybeShowEditLink = false;
                } else {
-                       $maybeShowEditLink = true; /* Actual presence will depend on ParserOptions option */
-                       $showEditLink = $this->mOptions->getEditSection();
-               }
-               if ( $showEditLink ) {
-                       $this->mOutput->setEditSectionTokens( true );
+                       $maybeShowEditLink = true; /* Actual presence will depend on post-cache transforms */
                }
 
                # Get all headlines for numbering them and adding funky stuff like [edit]
@@ -4390,9 +4386,9 @@ class Parser {
                         * $this : caller
                         * $section : the section number
                         * &$sectionContent : ref to the content of the section
-                        * $showEditLinks : boolean describing whether this section has an edit link
+                        * $maybeShowEditLinks : boolean describing whether this section has an edit link
                         */
-                       Hooks::run( 'ParserSectionCreate', [ $this, $i, &$sections[$i], $showEditLink ] );
+                       Hooks::run( 'ParserSectionCreate', [ $this, $i, &$sections[$i], $maybeShowEditLink ] );
 
                        $i++;
                }
index c680129..8a7fca6 100644 (file)
@@ -253,11 +253,6 @@ class ParserCache {
 
                wfDebug( "ParserOutput cache found.\n" );
 
-               // The edit section preference may not be the appropiate one in
-               // the ParserOutput, as we are not storing it in the parsercache
-               // key. Force it here. See T33445.
-               $value->setEditSectionTokens( $popts->getEditSection() );
-
                $wikiPage = method_exists( $article, 'getPage' )
                        ? $article->getPage()
                        : $article;
index ca8e739..bbddbe2 100644 (file)
@@ -80,13 +80,6 @@ class ParserOptions {
         */
        private $mTimestamp;
 
-       /**
-        * The edit section flag is in ParserOptions for historical reasons, but
-        * doesn't actually affect the parser output since Feb 2015.
-        * @var bool
-        */
-       private $mEditSection = true;
-
        /**
         * Stored user object
         * @var User
@@ -876,7 +869,8 @@ class ParserOptions {
         * @return bool
         */
        public function getEditSection() {
-               return $this->mEditSection;
+               wfDeprecated( __METHOD__, '1.31' );
+               return true;
        }
 
        /**
@@ -886,7 +880,8 @@ class ParserOptions {
         * @return bool Old value
         */
        public function setEditSection( $x ) {
-               return wfSetVar( $this->mEditSection, $x );
+               wfDeprecated( __METHOD__, '1.31' );
+               return true;
        }
 
        /**
@@ -1284,18 +1279,6 @@ class ParserOptions {
                $defaults = self::getCanonicalOverrides() + self::getDefaults();
                $inCacheKey = self::$inCacheKey;
 
-               // Historical hack: 'editsection' hasn't been a true parser option since
-               // Feb 2015 (instead the parser outputs a constant placeholder and post-parse
-               // processing handles the option). But Wikibase forces it in $forOptions
-               // and expects the cache key to still vary on it for T85252.
-               // @deprecated since 1.30, Wikibase should use addExtraKey() or something instead.
-               if ( in_array( 'editsection', $forOptions, true ) ) {
-                       $options['editsection'] = $this->mEditSection;
-                       $defaults['editsection'] = true;
-                       $inCacheKey['editsection'] = true;
-                       ksort( $inCacheKey );
-               }
-
                // We only include used options with non-canonical values in the key
                // so adding a new option doesn't invalidate the entire parser cache.
                // The drawback to this is that changing the default value of an option
index 19375e0..8f0a1d7 100644 (file)
@@ -151,12 +151,6 @@ class ParserOutput extends CacheTime {
         */
        public $mSections = [];
 
-       /**
-        * @deprecated since 1.31 Use getText() options.
-        * @var bool $mEditSectionTokens prefix/suffix markers if edit sections were output as tokens.
-        */
-       public $mEditSectionTokens = true;
-
        /**
         * @var array $mProperties Name/value pairs to be cached in the DB.
         */
@@ -172,12 +166,6 @@ class ParserOutput extends CacheTime {
         */
        public $mTimestamp;
 
-       /**
-        * @deprecated since 1.31 Use getText() options.
-        * @var bool $mTOCEnabled Whether TOC should be shown, can't override __NOTOC__.
-        */
-       public $mTOCEnabled = true;
-
        /**
         * @var bool $mEnableOOUI Whether OOUI should be enabled.
         */
@@ -280,22 +268,9 @@ class ParserOutput extends CacheTime {
         * @return string HTML
         */
        public function getText( $options = [] ) {
-               if ( !array_key_exists( 'allowTOC', $options ) && empty( $this->mTOCEnabled ) ) {
-                       wfDeprecated( 'ParserOutput stateful allowTOC', '1.31' );
-               }
-
-               //  Note that while $this->mEditSectionTokens formerly defaulted to false,
-               //  ParserOptions->getEditSection() defaults to true and Parser copies
-               //  that to us so true makes more sense as the stateless default.
-               if ( !array_key_exists( 'enableSectionEditLinks', $options ) && !$this->mEditSectionTokens ) {
-                       wfDeprecated( 'ParserOutput stateful enableSectionEditLinks', '1.31' );
-               }
-
                $options += [
-                       // empty() here because old cached versions might lack the field somehow.
-                       // In that situation, the historical behavior (possibly buggy) is to remove the TOC.
-                       'allowTOC' => !empty( $this->mTOCEnabled ),
-                       'enableSectionEditLinks' => $this->mEditSectionTokens,
+                       'allowTOC' => true,
+                       'enableSectionEditLinks' => true,
                        'unwrap' => false,
                        'deduplicateStyles' => true,
                ];
@@ -442,7 +417,8 @@ class ParserOutput extends CacheTime {
         * @deprecated since 1.31 Use getText() options.
         */
        public function getEditSectionTokens() {
-               return $this->mEditSectionTokens;
+               wfDeprecated( __METHOD__, '1.31' );
+               return true;
        }
 
        public function &getLinks() {
@@ -532,7 +508,8 @@ class ParserOutput extends CacheTime {
         * @deprecated since 1.31 Use getText() options.
         */
        public function getTOCEnabled() {
-               return $this->mTOCEnabled;
+               wfDeprecated( __METHOD__, '1.31' );
+               return true;
        }
 
        public function getEnableOOUI() {
@@ -563,7 +540,8 @@ class ParserOutput extends CacheTime {
         * @deprecated since 1.31 Use getText() options.
         */
        public function setEditSectionTokens( $t ) {
-               return wfSetVar( $this->mEditSectionTokens, $t );
+               wfDeprecated( __METHOD__, '1.31' );
+               return true;
        }
 
        public function setIndexPolicy( $policy ) {
@@ -582,7 +560,8 @@ class ParserOutput extends CacheTime {
         * @deprecated since 1.31 Use getText() options.
         */
        public function setTOCEnabled( $flag ) {
-               return wfSetVar( $this->mTOCEnabled, $flag );
+               wfDeprecated( __METHOD__, '1.31' );
+               return true;
        }
 
        public function addCategory( $c, $sort ) {
index 7cc0dc6..4abdebf 100644 (file)
@@ -220,20 +220,6 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                }
        }
 
-       /**
-        * Get a FormOptions object containing the default options
-        *
-        * @return FormOptions
-        */
-       public function getDefaultOptions() {
-               $opts = parent::getDefaultOptions();
-
-               $opts->add( 'categories', '' );
-               $opts->add( 'categories_any', false );
-
-               return $opts;
-       }
-
        /**
         * Get all custom filters
         *
@@ -359,11 +345,6 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        $join_conds
                );
 
-               // Build the final data
-               if ( $this->getConfig()->get( 'AllowCategorizedRecentChanges' ) ) {
-                       $this->filterByCategories( $rows, $opts );
-               }
-
                return $rows;
        }
 
@@ -667,16 +648,12 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
         */
        function getExtraOptions( $opts ) {
                $opts->consumeValues( [
-                       'namespace', 'invert', 'associated', 'tagfilter', 'categories', 'categories_any'
+                       'namespace', 'invert', 'associated', 'tagfilter'
                ] );
 
                $extraOpts = [];
                $extraOpts['namespace'] = $this->namespaceFilterForm( $opts );
 
-               if ( $this->getConfig()->get( 'AllowCategorizedRecentChanges' ) ) {
-                       $extraOpts['category'] = $this->categoryFilterForm( $opts );
-               }
-
                $tagFilter = ChangeTags::buildTagFilterSelector(
                        $opts['tagfilter'], false, $this->getContext() );
                if ( count( $tagFilter ) ) {
@@ -740,29 +717,17 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                return [ $nsLabel, "$nsSelect $invert $associated" ];
        }
 
-       /**
-        * Create an input to filter changes by categories
-        *
-        * @param FormOptions $opts
-        * @return array
-        */
-       protected function categoryFilterForm( FormOptions $opts ) {
-               list( $label, $input ) = Xml::inputLabelSep( $this->msg( 'rc_categories' )->text(),
-                       'categories', 'mw-categories', false, $opts['categories'] );
-
-               $input .= ' ' . Xml::checkLabel( $this->msg( 'rc_categories_any' )->text(),
-                       'categories_any', 'mw-categories_any', $opts['categories_any'] );
-
-               return [ $label, $input ];
-       }
-
        /**
         * Filter $rows by categories set in $opts
         *
+        * @deprecated since 1.31
+        *
         * @param ResultWrapper &$rows Database rows
         * @param FormOptions $opts
         */
        function filterByCategories( &$rows, FormOptions $opts ) {
+               wfDeprecated( __METHOD__, '1.31' );
+
                $categories = array_map( 'trim', explode( '|', $opts['categories'] ) );
 
                if ( !count( $categories ) ) {
index f821eff..127a36b 100644 (file)
@@ -450,9 +450,7 @@ class SpecialUndelete extends SpecialPage {
                if ( ( $this->mPreview || !$isText ) && $content ) {
                        // NOTE: non-text content has no source view, so always use rendered preview
 
-                       // Hide [edit]s
                        $popts = $out->parserOptions();
-                       $popts->setEditSection( false );
 
                        $pout = $content->getParserOutput( $this->mTargetObj, $rev->getId(), $popts, true );
                        $out->addParserOutput( $pout, [
index f771f42..e757e59 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use Wikimedia\Rdbms\IDatabase;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Represents a "user group membership" -- a specific instance of a user belonging
@@ -158,7 +159,7 @@ class UserGroupMembership {
                }
 
                // Purge old, expired memberships from the DB
-               self::purgeExpired( $dbw );
+               JobQueueGroup::singleton()->push( new UserGroupExpiryJob() );
 
                // Check that the values make sense
                if ( $this->group === null ) {
@@ -236,38 +237,59 @@ class UserGroupMembership {
 
        /**
         * Purge expired memberships from the user_groups table
-        *
-        * @param IDatabase|null $dbw
         */
-       public static function purgeExpired( IDatabase $dbw = null ) {
-               if ( wfReadOnly() ) {
+       public static function purgeExpired() {
+               $services = MediaWikiServices::getInstance();
+               if ( $services->getReadOnlyMode()->isReadOnly() ) {
                        return;
                }
 
-               if ( $dbw === null ) {
-                       $dbw = wfGetDB( DB_MASTER );
-               }
+               $lbFactory = $services->getDBLoadBalancerFactory();
+               $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+               $dbw = $services->getDBLoadBalancer()->getConnection( DB_MASTER );
 
-               DeferredUpdates::addUpdate( new AtomicSectionUpdate(
-                       $dbw,
-                       __METHOD__,
-                       function ( IDatabase $dbw, $fname ) {
-                               $expiryCond = [ 'ug_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ];
-                               $res = $dbw->select( 'user_groups', self::selectFields(), $expiryCond, $fname );
+               $lockKey = $dbw->getDomainID() . ':usergroups-prune'; // specific to this wiki
+               $scopedLock = $dbw->getScopedLockAndFlush( $lockKey, __METHOD__, 0 );
+               if ( !$scopedLock ) {
+                       return; // already running
+               }
 
-                               // save an array of users/groups to insert to user_former_groups
-                               $usersAndGroups = [];
+               $now = time();
+               do {
+                       $dbw->startAtomic( __METHOD__ );
+
+                       $res = $dbw->select(
+                               'user_groups',
+                               self::selectFields(),
+                               [ 'ug_expiry < ' . $dbw->addQuotes( $dbw->timestamp( $now ) ) ],
+                               __METHOD__,
+                               [ 'FOR UPDATE', 'LIMIT' => 100 ]
+                       );
+
+                       if ( $res->numRows() > 0 ) {
+                               $insertData = []; // array of users/groups to insert to user_former_groups
+                               $deleteCond = []; // array for deleting the rows that are to be moved around
                                foreach ( $res as $row ) {
-                                       $usersAndGroups[] = [ 'ufg_user' => $row->ug_user, 'ufg_group' => $row->ug_group ];
+                                       $insertData[] = [ 'ufg_user' => $row->ug_user, 'ufg_group' => $row->ug_group ];
+                                       $deleteCond[] = $dbw->makeList(
+                                               [ 'ug_user' => $row->ug_user, 'ug_group' => $row->ug_group ],
+                                               $dbw::LIST_AND
+                                       );
                                }
+                               // Delete the rows we're about to move
+                               $dbw->delete(
+                                       'user_groups',
+                                       $dbw->makeList( $deleteCond, $dbw::LIST_OR ),
+                                       __METHOD__
+                               );
+                               // Push the groups to user_former_groups
+                               $dbw->insert( 'user_former_groups', $insertData, __METHOD__, [ 'IGNORE' ] );
+                       }
 
-                               // delete 'em all
-                               $dbw->delete( 'user_groups', $expiryCond, $fname );
+                       $dbw->endAtomic( __METHOD__ );
 
-                               // and push the groups to user_former_groups
-                               $dbw->insert( 'user_former_groups', $usersAndGroups, __METHOD__, [ 'IGNORE' ] );
-                       }
-               ) );
+                       $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
+               } while ( $res->numRows() > 0 );
        }
 
        /**
index 10c3855..c2e2391 100644 (file)
        "rollback-success": "Revertíes les ediciones de {{GENDER:$3|$1}}; devueltu a la última revisión de {{GENDER:$4|$2}}.",
        "rollback-success-notify": "Revertíes les ediciones de $1 a la última revisión de $2. [$3 Ver cambeos]",
        "sessionfailure-title": "Fallu de sesión",
-       "sessionfailure": "Paez qu'hai un problema col aniciu de sesión;\natayóse esta aición por precaución escontra secuestru de sesiones.\nTorna a la páxina anterior, recarga esa páxina y vuelve a tentalo.",
+       "sessionfailure": "Paez qu'hai un problema col aniciu de sesión;\natayóse esta aición por precaución escontra secuestru de sesiones.\nUnvia'l formulariu otra vegada.",
        "changecontentmodel": "Cambiar el modelu de conteníu d'una páxina",
        "changecontentmodel-legend": "Cambiar el modelu de conteníu",
        "changecontentmodel-title-label": "Títulu de la páxina",
        "watchlistedit-clear-titles": "Títulos:",
        "watchlistedit-clear-submit": "Llimpiar la llista de siguimientu (¡Esto ye permanente!)",
        "watchlistedit-clear-done": "Llimpióse la to llista de siguimientu.",
+       "watchlistedit-clear-jobqueue": "Ta llimpiándose la llista de siguimientu. ¡Esta aición puede tardar daqué de tiempu!",
        "watchlistedit-clear-removed": "{{PLURAL:$1|Desanicióse 1 títulu|Desaniciáronse $1 títulos}}:",
        "watchlistedit-too-many": "Hai demasiaes páxines p'amosales equí.",
        "watchlisttools-clear": "Llimpiar la llista de siguimientu",
index dfff3a8..7b2a5a5 100644 (file)
        "right-deletelogentry": "выдаленьне і аднаўленьне асобных запісаў журналу",
        "right-deleterevision": "выдаленьне і аднаўленьне асобных вэрсіяў старонак",
        "right-deletedhistory": "Прагляд выдаленай гісторыі старонак бяз доступу да выдаленага тэксту",
-       "right-deletedtext": "прагляд выдаленага тэксту і зьменаў паміж выдаленымі вэрсіямі старонак",
+       "right-deletedtext": "Ð\9fрагляд выдаленага тэксту і зьменаў паміж выдаленымі вэрсіямі старонак",
        "right-browsearchive": "пошук выдаленых старонак",
        "right-undelete": "аднаўленьне старонак",
        "right-suppressrevision": "праглядаць, хаваць і аднаўляць пэўныя вэрсіі старонак, зробленыя любым удзельнікам",
index 9d06c96..b710762 100644 (file)
        "unpatrolledletter": "!",
        "number_of_watching_users_RCview": "[$1]",
        "number_of_watching_users_pageview": "[$1 watching {{PLURAL:$1|user|users}}]",
-       "rc_categories": "Limit to categories (separate with \"|\"):",
-       "rc_categories_any": "Any of the chosen",
        "rc-change-size": "$1",
        "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} after change",
        "newsectionsummary": "/* $1 */ new section",
index 1e3c25f..6a071ed 100644 (file)
        "unpatrolledletter": "{{optional}}\n\nUsed in {{msg-mw|Recentchanges-label-legend}}, meaning \"unpatrolled\".",
        "number_of_watching_users_RCview": "{{notranslate}}\nParameters:\n* $1 - number of users who are watching",
        "number_of_watching_users_pageview": "Used if <code>$wgPageShowWatchingUsers</code> is true.\n* $1 - number of watching user(s)",
-       "rc_categories": "A label of an input box. Appears on Special:RecentChanges if filtering recent changes by category is enabled (that is, $wgAllowCategorizedRecentChanges is set to true).",
-       "rc_categories_any": "Appears ''after'' the input box the label of which is {{msg-mw|rc_categories}}, which appears on [[Special:RecentChanges]], if <code>$wgAllowCategorizedRecentChanges</code> is true. \"Chosen\" refers to categories.",
        "rc-change-size": "{{optional}}\nDoes not work under $wgMiserMode ([[mwr:48986|r48986]]).\n\nParameters:\n* $1 - size of diff",
        "rc-change-size-new": "Tooltip when hovering a change list diff size. Parameters:\n* $1 - the resulting new size (in bytes)",
        "newsectionsummary": "Default summary when adding a new section to a page. Parameters:\n* $1 - section title",
index 3f0065f..21204ef 100644 (file)
        "feedback-subject": "Tema:",
        "feedback-submit": "Unesi",
        "feedback-thanks": "Hvala! Vaša povratna informacija je postavljena na stranicu „[$2 $1]“.",
-       "searchsuggest-search": "Traži",
+       "searchsuggest-search": "Traži {{GRAMMAR:akuzativ|{{SITENAME}}}}",
        "searchsuggest-containing": "sadrži...",
        "api-error-badtoken": "Unutrašnja greška: token nije ispravan.",
        "api-error-emptypage": "Stvaranje novih praznih stranica nije dozvoljeno.",
index 355a49b..9cabb1f 100644 (file)
        "thu": "Enj",
        "fri": "Pre",
        "sat": "Sht",
-       "january": "Janar",
-       "february": "Shkurt",
-       "march": "Mars",
-       "april": "Prill",
-       "may_long": "Maj",
-       "june": "Qershor",
-       "july": "Korrik",
-       "august": "Gusht",
-       "september": "Shtator",
-       "october": "Tetor",
-       "november": "Nëntor",
-       "december": "Dhjetor",
-       "january-gen": "Janar",
-       "february-gen": "Shkurt",
-       "march-gen": "Mars",
-       "april-gen": "Prill",
-       "may-gen": "Maj",
-       "june-gen": "Qershor",
-       "july-gen": "Korrik",
-       "august-gen": "Gusht",
-       "september-gen": "Shtator",
-       "october-gen": "Tetor",
-       "november-gen": "Nëntor",
-       "december-gen": "Dhjetor",
+       "january": "janar",
+       "february": "shkurt",
+       "march": "mars",
+       "april": "prill",
+       "may_long": "maj",
+       "june": "qershor",
+       "july": "korrik",
+       "august": "gusht",
+       "september": "shtator",
+       "october": "tetor",
+       "november": "nëntor",
+       "december": "dhjetor",
+       "january-gen": "janar",
+       "february-gen": "shkurt",
+       "march-gen": "mars",
+       "april-gen": "prill",
+       "may-gen": "maj",
+       "june-gen": "qershor",
+       "july-gen": "korrik",
+       "august-gen": "gusht",
+       "september-gen": "shtator",
+       "october-gen": "tetor",
+       "november-gen": "nëntor",
+       "december-gen": "dhjetor",
        "jan": "Jan",
        "feb": "Shk",
        "mar": "Mar",
index b891c43..0e8ac87 100644 (file)
@@ -73,7 +73,7 @@
        "tog-watchlisthideminor": "Сакриј мање измене са списка надгледања",
        "tog-watchlisthideliu": "Сакриј измене пријављених корисника са списка надгледања",
        "tog-watchlistreloadautomatically": "Аутоматски освежи списак надгледања кад год се филтер измени (потребан JavaScript)",
-       "tog-watchlistunwatchlinks": "Додај везе за директно додавање/уклањање ставки са списка надгледања (потребан ЈаваСкрипт)",
+       "tog-watchlistunwatchlinks": "Додај везе за директно додавање/уклањање ставки са списка надгледања (потребан JavaScript)",
        "tog-watchlisthideanons": "Сакриј измене анонимних корисника са списка надгледања",
        "tog-watchlisthidepatrolled": "Сакриј патролиране измене са списка надгледања",
        "tog-watchlisthidecategorization": "Сакриј категоризацију страница",
        "tagline": "Из {{SITENAME}}",
        "help": "Помоћ",
        "search": "Претражи",
-       "search-ignored-headings": "#<!-- ову линију оставите онакву каква јесте --> <pre>\n# Наслови који ће бити игнорисани упитом\n# Промене су видљиве одмах након што страница са насловом буде пописана\n# Можете изнудити поновно пописивање са \"нулл\" променом\n# Синтакса је следећа:\n# * Свака врста која започиње \"#\" знаком па све до краја је коментар\n# * Свака не празна врста је тачан наслов за занемарити, у тачном облику\nРеференце\nСпољашње везе\nПогледајте\n#</pre> <!-- ову линију оставите онакву каква јесте -->",
+       "search-ignored-headings": " #<!-- не мењајте ништа у овом реду --> <pre>\n# Наслови који ће бити занемарени при претрази.\n# Измене су видљиве одмах након што се страница са насловом попише.\n# Можете изнудити поновно пописивање „нултом” изменом.\n# Синтакса је следећа:\n#  * Сваки ред који започиње знаком „#” је коментар.\n#  * Сваки не празни ред је тачан наслов који ће бити занемарен, с тим да се разликују мала и велика слова и све остало\nРеференце\nСпољашње везе\nТакође погледајте\n #</pre> <!-- не мењајте ништа у овом реду -->",
        "searchbutton": "Претражи",
        "go": "Иди",
        "searcharticle": "Иди",
        "cannotloginnow-title": "Пријава тренутно није могућа",
        "cannotloginnow-text": "Пријава није могућа када се користи $1.",
        "cannotcreateaccount-title": "Отварање налога није могуће",
+       "cannotcreateaccount-text": "Директно прављење налога није омогућено на овом викију.",
        "yourdomainname": "Домен:",
        "password-change-forbidden": "Не можете да промените лозинку на овом викију.",
        "externaldberror": "Дошло је до грешке при препознавању базе података или немате овлашћења да ажурирате свој спољни налог.",
        "createacct-email-ph": "Унесите Вашу имејл адресу",
        "createacct-another-email-ph": "Унесите имејл адресу",
        "createaccountmail": "Користите привремену, случајно створену лозинку и пошаљите на наведену имејл адресу",
+       "createaccountmail-help": "Може се користити да се некоме направи налог без сазнања лозинке.",
        "createacct-realname": "Право име (необавезно)",
        "createacct-reason": "Разлог",
        "createacct-reason-ph": "Зашто правите још један налог?",
+       "createacct-reason-help": "Порука која се приказује у дневнику стварања корисничких налога",
        "createacct-submit": "Отвори налог",
        "createacct-another-submit": "Отвори налог",
        "createacct-continue-submit": "Наставите отварање налога",
        "botpasswords-label-cancel": "Откажи",
        "botpasswords-label-delete": "Обриши",
        "botpasswords-label-resetpassword": "Ресетуј лозинку",
+       "botpasswords-label-grants": "Применљиве дозволе:",
        "botpasswords-label-grants-column": "Одобрено",
        "botpasswords-bad-appid": "„$1” није исправан назив бота.",
        "botpasswords-insert-failed": "Неуспешно додавање бота \"$1\". Да ли је већ додат?",
        "recentchangesdays-max": "Највише $1 {{PLURAL:$1|дан|дана}}",
        "recentchangescount": "Број измена за приказ:",
        "prefs-help-recentchangescount": "Подразумева скорашње измене, историје страница и дневнике.",
-       "prefs-help-watchlist-token2": "Ово је тајни кључ за веб-довод Вашег списка надгледања. \nСвако ко зна овај кључ биће у могућности да види Ваша списак надгледања, зато кључ немојте одавати никоме. \nАко је потребно, кључ [[Special:ResetTokens|можете ресетовати]].",
+       "prefs-help-watchlist-token2": "Ово је тајни кључ за веб-довод Вашег списка надгледања. \nСвако ко зна овај кључ биће у могућности да види Ваш списак надгледања, зато кључ немојте одавати никоме. \nАко је потребно, кључ [[Special:ResetTokens|можете ресетовати]].",
        "savedprefs": "Ваша подешавања су сачувана.",
        "savedrights": "Корисничке групе за {{GENDER:$1|$1}} су сачуване.",
        "timezonelegend": "Временска зона:",
        "email-blacklist-label": "Онемогући следећим корисницима да ми шаљу имејлове:",
        "prefs-searchoptions": "Претрага",
        "prefs-namespaces": "Именски простори",
-       "default": "подÑ\80азÑ\83мевано",
+       "default": "подÑ\80азÑ\83мевана",
        "prefs-files": "Датотеке",
        "prefs-custom-css": "Прилагођени CSS",
        "prefs-custom-js": "Прилагођени јаваскрипт",
        "grant-group-file-interaction": "Уређивање датотека",
        "grant-group-watchlist-interaction": "Уређивање вашег списка надгледања",
        "grant-group-email": "Пошаљи имејл",
+       "grant-group-high-volume": "Извршавање великог броја радњи",
+       "grant-group-customization": "Прилагођавање и подешавања",
+       "grant-group-administration": "Извршавање административних радњи",
+       "grant-group-private-information": "Приступање Вашим личним подацима",
        "grant-group-other": "Разне активности",
        "grant-blockusers": "Блокирање и деблокирање корисника",
        "grant-createaccount": "Отварање налога",
        "grant-editpage": "Уређивање постојећих страница",
        "grant-editprotected": "Уређивање заштићених страница",
        "grant-highvolume": "Масовно уређивање",
+       "grant-oversight": "Скривање корисника и измена",
        "grant-patrol": "Патролирање измена",
        "grant-privateinfo": "Приступи приватним информацијама",
        "grant-protect": "Закључавање и откључавање страница",
        "grant-basic": "Основна права",
        "grant-viewdeleted": "Преглед обрисаних страница и датотека",
        "grant-viewmywatchlist": "Преглед вашег списак надгледања",
+       "grant-viewrestrictedlogs": "Прегледање ограничених уноса у дневнику",
        "newuserlogpage": "Дневник нових корисника",
        "newuserlogpagetext": "Ово је дневник нових корисника.",
        "rightslog": "Дневник корисничких права",
        "action-writeapi": "писање АПИ-ја",
        "action-delete": "брисање ове странице",
        "action-deleterevision": "брисање измена",
+       "action-deletelogentry": "обриши уносе у дневнику",
        "action-deletedhistory": "прегледање обрисане историје странице",
+       "action-deletedtext": "прегледај обрисани текст измене",
        "action-browsearchive": "претраживање обрисаних страница",
        "action-undelete": "враћање страница",
        "action-suppressrevision": "прегледање и враћање сакривених измена",
        "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|измена|измене}}, $2",
        "rcfilters-date-popup-title": "Временски период",
        "rcfilters-days-title": "Скорашњи дани",
-       "rcfilters-hours-title": "СкоÑ\80аÑ\88Ñ\9aи сати",
+       "rcfilters-hours-title": "СкоÑ\80аÑ\88Ñ\9aе сати",
        "rcfilters-days-show-days": "$1 {{PLURAL:$1|дан|дана}}",
        "rcfilters-days-show-hours": "$1 {{PLURAL:$1|сат|сата}}",
        "rcfilters-highlighted-filters-list": "Истакнуто: $1",
        "uploaded-href-unsafe-target-svg": "Пронађен href са несигурним подацима: URI одредиште <code>&lt;$1 $2=\"$3\"&gt;</code> у постављеној SVG датотеци.",
        "uploaded-animate-svg": "Пронађена „animate“ ознака која можда мења href користећи се „from“ атрибутом <code>&lt;$1 $2=\"$3\"&gt;</code> у постављеној SVG датотеци.",
        "uploadscriptednamespace": "Ова SVG датотека садржи погрешан именски простор „<nowiki>$1</nowiki>“",
+       "uploadinvalidxml": "Није могуће рашчланити XML отпремљене датотеке.",
        "uploadvirus": "Датотека садржи вирус!\nДетаљи: $1",
        "uploadjava": "Датотека је формата ZIP који садржи јава .class елемент.\nСлање јава датотека није дозвољено јер оне могу изазвати заобилажење сигурносних ограничења.",
        "upload-source": "Изворна датотека",
        "upload-too-many-redirects": "Адреса садржи превише преусмерења",
        "upload-http-error": "Дошло је до HTTP грешке: $1",
        "upload-copy-upload-invalid-domain": "Примерци отпремања нису доступни на овом домену.",
-       "upload-dialog-title": "Ð\9eÑ\82пÑ\80емаÑ\9aе Ð´Ð°Ñ\82оÑ\82ека",
+       "upload-dialog-title": "Ð\9eÑ\82пÑ\80еми Ð´Ð°Ñ\82оÑ\82екÑ\83",
        "upload-dialog-button-cancel": "Откажи",
        "upload-dialog-button-back": "Назад",
        "upload-dialog-button-done": "Готово",
        "upload-form-label-own-work": "Ово је моје сопствено дело",
        "upload-form-label-infoform-categories": "Категорије",
        "upload-form-label-infoform-date": "Датум",
+       "upload-form-label-not-own-work-local-generic-local": "Такође можете покушати [[Special:Upload|подразумевану страницу за отпремање]].",
        "backend-fail-stream": "Не могу да емитујем датотеку $1.",
        "backend-fail-backup": "Не могу да направим резерву датотеке $1.",
        "backend-fail-notexists": "Датотека $1 не постоји.",
        "uploadstash-badtoken": "Извршавање дате радње није успело, разлог томе може бити истек времена за уређивање. Покушајте поново.",
        "uploadstash-errclear": "Чишћење датотека није успело.",
        "uploadstash-refresh": "Освежи списак датотека",
+       "uploadstash-thumbnail": "погледај минијатуру",
        "uploadstash-bad-path": "Путања не постоји.",
        "uploadstash-bad-path-invalid": "Путања није исправна.",
        "uploadstash-bad-path-unknown-type": "Непознат тип „$1“.",
+       "uploadstash-bad-path-unrecognized-thumb-name": "Непрепознато име минијатуре.",
+       "uploadstash-bad-path-bad-format": "Кључ „$1“ није у одговарајућем облику.",
+       "uploadstash-file-not-found-no-thumb": "Не могу добити минијатуру.",
+       "uploadstash-file-not-found-no-remote-thumb": "Добављање минијатуре није успело: $1\nАдреса = $2",
+       "uploadstash-file-not-found-missing-content-type": "Недостаје заглавље за врсту садржаја.",
+       "uploadstash-no-extension": "Нема траженог додатка.",
        "invalid-chunk-offset": "Неисправна полазна тачка",
        "img-auth-accessdenied": "Приступ је одбијен",
        "img-auth-nopathinfo": "Недостаје PATH_INFO.\nВаш сервер није подешен да прослеђује овакве податке.\nМожда је заснован на CGI-ју који не подржава img_auth.\nПогледајте https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization?uselang=sr-ec.",
        "apisandbox-sending-request": "Слање API захтева...",
        "apisandbox-loading-results": "Пријем API резултата...",
        "apisandbox-results-error": "Дошло је до грешке приликом учитавања резултата API упита: $1.",
+       "apisandbox-request-selectformat-label": "Прикажи сахтеване податке као:",
        "apisandbox-request-url-label": "Адреса захтева:",
        "apisandbox-continue": "Настави",
        "apisandbox-continue-clear": "Очисти",
        "tooltip-ca-move": "Премести ову страницу",
        "tooltip-ca-watch": "Додај ову страницу на списак надгледања",
        "tooltip-ca-unwatch": "Уклони ову страницу са списка надгледања",
-       "tooltip-search": "Ð\9fÑ\80еÑ\82Ñ\80ага",
+       "tooltip-search": "Ð\9fÑ\80еÑ\82Ñ\80ажи",
        "tooltip-search-go": "Идите на страницу с овим именом, ако постоји",
        "tooltip-search-fulltext": "Претражите странице с овим текстом",
        "tooltip-p-logo": "Посетите главну страну",
        "feedback-termsofuse": "Прихватам да пошаљем повратне информације у складу са условима коришћења.",
        "feedback-thanks": "Хвала! Ваша повратна информација је постављена на страницу „[$2 $1]“.",
        "feedback-thanks-title": "Хвала вам!",
-       "searchsuggest-search": "Ð\9fÑ\80еÑ\82Ñ\80ага",
+       "searchsuggest-search": "Ð\9fÑ\80еÑ\82Ñ\80ажи",
        "searchsuggest-containing": "садржи...",
        "api-error-badtoken": "Унутрашња грешка: неисправан жетон.",
        "api-error-emptypage": "Стварање нових празних страница није дозвољено.",
index bd5e3ff..c27d023 100644 (file)
        "rollback-success": "Återställde ändringar av {{GENDER:$3|$1}};\nändrade tillbaka till senaste versionen av {{GENDER:$4|$2}}.",
        "rollback-success-notify": "Återställde ändringar av $1;\nändrade tillbaka till senaste sidversion av $2. [$3 Visa ändringar]",
        "sessionfailure-title": "Sessionsfel",
-       "sessionfailure": "Något med din session som inloggad är på tok. Din begärda åtgärd har avbrutits, för att förhindra att någon kapar din session. Klicka på \"Tillbaka\" i din webbläsare och ladda om den sida du kom ifrån. Försök sedan igen.",
+       "sessionfailure": "Någonting med din inloggningssession är på tok;\ndin begärda åtgärd har avbrutits för att förhindra att någon kapar din session.\nSkicka formuläret igen.",
        "changecontentmodel": "Ändra innehållsmodell för en sida",
        "changecontentmodel-legend": "Ändra innehållsmodell",
        "changecontentmodel-title-label": "Sidtitel",
        "watchlistedit-clear-titles": "Sidor:",
        "watchlistedit-clear-submit": "Rensa bevakningslistan (Detta är permanent!)",
        "watchlistedit-clear-done": "Din bevakningslista har rensats.",
+       "watchlistedit-clear-jobqueue": "Din bevakningslista skapas. Detta kan ta en stund!",
        "watchlistedit-clear-removed": "{{PLURAL:$1|1 sida|$1 sidor}} togs bort:",
        "watchlistedit-too-many": "Det finns för många sidor att visa här.",
        "watchlisttools-clear": "Rensa bevakningslistan",
index 0387142..1714526 100644 (file)
        "colon-separator": ":",
        "word-separator": "",
        "ellipsis": "…",
-       "parentheses": " ($1)",
+       "parentheses": "($1)",
        "quotation-marks": "\"$1\"",
        "imgmultipageprev": "← 上一頁",
        "imgmultipagenext": "下一頁 →",
        "logentry-protect-protect-cascade": "$1 {{GENDER:$2|已保護}} $3 $4 [連鎖]",
        "logentry-protect-modify": "$1 {{GENDER:$2|已更改}} $3 的保護層級 $4",
        "logentry-protect-modify-cascade": "$1 {{GENDER:$2|已更改}} $3 的保護層級 $4 [連鎖]",
-       "logentry-rights-rights": "$1 {{GENDER:$2|已更改}} {{GENDER:$6|$3}} 的群組成員資格由 $4 成為 $5",
+       "logentry-rights-rights": "$1已將{{GENDER:$6|$3}}的使用者群組從$4{{GENDER:$2|更改}}至$5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|已更改}} $3 的群組成員資格",
        "logentry-rights-autopromote": "$1 已自動{{GENDER:$2|提升}}從 $4 成為 $5",
        "logentry-upload-upload": "$1 {{GENDER:$2|已上傳}} $3",
        "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|已移除}}{{PLURAL:$9|標籤|標籤}} $8 自日誌項目 $3 的修訂 $5。",
        "logentry-tag-update-revision": "$1 {{GENDER:$2|已更新}}標籤於頁面 $3 的修訂 $4 ({{PLURAL:$7|加入}} $6; {{PLURAL:$9|移除}} $8)。",
        "logentry-tag-update-logentry": "$1 {{GENDER:$2|已更新}}標籤於頁面 $3 的日誌項目 $5 ({{PLURAL:$7|加入}} $6; {{PLURAL:$9|移除}} $8)。",
-       "rightsnone": "(無)",
+       "rightsnone": "(無)",
        "rightslogentry-temporary-group": "$1 (臨時,直到 $2)",
        "feedback-adding": "正在新增意見回饋至頁面...",
        "feedback-back": "返回",
index 617071b..7e4bf7c 100644 (file)
@@ -514,6 +514,8 @@ abstract class Maintenance {
                        "http://en.wikipedia.org. This is sometimes necessary because " .
                        "server name detection may fail in command line scripts.", false, true );
                $this->addOption( 'profiler', 'Profiler output format (usually "text")', false, true );
+               // This is named --mwdebug, because --debug would conflict in the phpunit.php CLI script.
+               $this->addOption( 'mwdebug', 'Enable built-in MediaWiki development settings', false, true );
 
                # Save generic options to display them separately in help
                $this->mGenericParameters = $this->mParams;
@@ -1149,6 +1151,11 @@ abstract class Maintenance {
                        MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
                }
 
+               # Apply debug settings
+               if ( $this->hasOption( 'mwdebug' ) ) {
+                       require __DIR__ . '/../includes/DevelopmentSettings.php';
+               }
+
                // Per-script profiling; useful for debugging
                $this->activateProfiler();
 
index cdbf9fd..56bde5d 100644 (file)
@@ -11,9 +11,10 @@ class SiteStatsTest extends MediaWikiTestCase {
                $cache = \MediaWiki\MediaWikiServices::getInstance()->getMainWANObjectCache();
                $jobq = JobQueueGroup::singleton();
 
-               // Delete EditPage jobs that might have been left behind by other tests
+               // Delete jobs that might have been left behind by other tests
                $jobq->get( 'htmlCacheUpdate' )->delete();
                $jobq->get( 'recentChangesUpdate' )->delete();
+               $jobq->get( 'userGroupExpiry' )->delete();
                $cache->delete( $cache->makeKey( 'SiteStats', 'jobscount' ) );
 
                $jobq->push( new NullJob( Title::newMainPage(), [] ) );
index d55372c..e2ed1d5 100644 (file)
@@ -141,23 +141,6 @@ class ParserOptionsTest extends MediaWikiTestCase {
                $confstr .= '!onPageRenderingHash';
        }
 
-       // Test weird historical behavior is still weird
-       public function testOptionsHashEditSection() {
-               $popt = ParserOptions::newCanonical();
-               $popt->registerWatcher( function ( $name ) {
-                       $this->assertNotEquals( 'editsection', $name );
-               } );
-
-               $this->assertTrue( $popt->getEditSection() );
-               $this->assertSame( 'canonical', $popt->optionsHash( [] ) );
-               $this->assertSame( 'canonical', $popt->optionsHash( [ 'editsection' ] ) );
-
-               $popt->setEditSection( false );
-               $this->assertFalse( $popt->getEditSection() );
-               $this->assertSame( 'canonical', $popt->optionsHash( [] ) );
-               $this->assertSame( 'editsection=0', $popt->optionsHash( [ 'editsection' ] ) );
-       }
-
        /**
         * @expectedException InvalidArgumentException
         * @expectedExceptionMessage Unknown parser option bogus
index 44c1773..b08ba6c 100644 (file)
@@ -1,7 +1,5 @@
 <?php
 
-use Wikimedia\TestingAccessWrapper;
-
 /**
  * @group Database
  *        ^--- trigger DB shadowing because we are using Title magic
@@ -95,31 +93,17 @@ class ParserOutputTest extends MediaWikiTestCase {
         * @covers ParserOutput::getText
         * @dataProvider provideGetText
         * @param array $options Options to getText()
-        * @param array $poState ParserOptions state fields to set
         * @param string $text Parser text
         * @param string $expect Expected output
         */
-       public function testGetText( $options, $poState, $text, $expect ) {
+       public function testGetText( $options, $text, $expect ) {
                $this->setMwGlobals( [
                        'wgArticlePath' => '/wiki/$1',
                        'wgScriptPath' => '/w',
                        'wgScript' => '/w/index.php',
                ] );
-               $this->hideDeprecated( 'ParserOutput stateful allowTOC' );
-               $this->hideDeprecated( 'ParserOutput stateful enableSectionEditLinks' );
 
                $po = new ParserOutput( $text );
-
-               // Emulate Parser
-               $po->setEditSectionTokens( true );
-
-               if ( $poState ) {
-                       $wrap = TestingAccessWrapper::newFromObject( $po );
-                       foreach ( $poState as $key => $value ) {
-                               $wrap->$key = $value;
-                       }
-               }
-
                $actual = $po->getText( $options );
                $this->assertSame( $expect, $actual );
        }
@@ -169,89 +153,8 @@ EOF;
 EOF;
 
                return [
-                       'No stateless options, default state' => [
-                               [], [], $text, <<<EOF
-<div class="mw-parser-output"><p>Test document.
-</p>
-<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
-<ul>
-<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
-<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
-<ul>
-<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
-</ul>
-</li>
-<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
-</ul>
-</div>
-
-<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
-<p>One
-</p>
-<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
-<p>Two
-</p>
-<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=3" title="Edit section: Section 2.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
-<p>Two point one
-</p>
-<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
-<p>Three
-</p></div>
-EOF
-                       ],
-                       'No stateless options, TOC statefully disabled' => [
-                               [], [ 'mTOCEnabled' => false ], $text, <<<EOF
-<div class="mw-parser-output"><p>Test document.
-</p>
-
-<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
-<p>One
-</p>
-<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
-<p>Two
-</p>
-<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=3" title="Edit section: Section 2.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
-<p>Two point one
-</p>
-<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
-<p>Three
-</p></div>
-EOF
-                       ],
-                       'No stateless options, section edits statefully disabled' => [
-                               [], [ 'mEditSectionTokens' => false ], $text, <<<EOF
-<div class="mw-parser-output"><p>Test document.
-</p>
-<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
-<ul>
-<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
-<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
-<ul>
-<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
-</ul>
-</li>
-<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
-</ul>
-</div>
-
-<h2><span class="mw-headline" id="Section_1">Section 1</span></h2>
-<p>One
-</p>
-<h2><span class="mw-headline" id="Section_2">Section 2</span></h2>
-<p>Two
-</p>
-<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span></h3>
-<p>Two point one
-</p>
-<h2><span class="mw-headline" id="Section_3">Section 3</span></h2>
-<p>Three
-</p></div>
-EOF
-                       ],
-                       'Stateless options override stateful settings' => [
-                               [ 'allowTOC' => true, 'enableSectionEditLinks' => true ],
-                               [ 'mTOCEnabled' => false, 'mEditSectionTokens' => false ],
-                               $text, <<<EOF
+                       'No options' => [
+                               [], $text, <<<EOF
 <div class="mw-parser-output"><p>Test document.
 </p>
 <div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
@@ -280,8 +183,8 @@ EOF
 </p></div>
 EOF
                        ],
-                       'Statelessly disable section edit links' => [
-                               [ 'enableSectionEditLinks' => false ], [], $text, <<<EOF
+                       'Disable section edit links' => [
+                               [ 'enableSectionEditLinks' => false ], $text, <<<EOF
 <div class="mw-parser-output"><p>Test document.
 </p>
 <div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
@@ -310,8 +213,8 @@ EOF
 </p></div>
 EOF
                        ],
-                       'Statelessly disable TOC' => [
-                               [ 'allowTOC' => false ], [], $text, <<<EOF
+                       'Disable TOC' => [
+                               [ 'allowTOC' => false ], $text, <<<EOF
 <div class="mw-parser-output"><p>Test document.
 </p>
 
@@ -329,8 +232,8 @@ EOF
 </p></div>
 EOF
                        ],
-                       'Statelessly unwrap text' => [
-                               [ 'unwrap' => true ], [], $text, <<<EOF
+                       'Unwrap text' => [
+                               [ 'unwrap' => true ], $text, <<<EOF
 <p>Test document.
 </p>
 <div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
@@ -360,15 +263,15 @@ EOF
 EOF
                        ],
                        'Unwrap without a mw-parser-output wrapper' => [
-                               [ 'unwrap' => true ], [], '<div class="foobar">Content</div>', '<div class="foobar">Content</div>'
+                               [ 'unwrap' => true ], '<div class="foobar">Content</div>', '<div class="foobar">Content</div>'
                        ],
                        'Unwrap with extra comment at end' => [
-                               [ 'unwrap' => true ], [], '<div class="mw-parser-output"><p>Test document.</p></div>
+                               [ 'unwrap' => true ], '<div class="mw-parser-output"><p>Test document.</p></div>
 <!-- Saved in parser cache... -->', '<p>Test document.</p>
 <!-- Saved in parser cache... -->'
                        ],
                        'Style deduplication' => [
-                               [], [], $dedupText, <<<EOF
+                               [], $dedupText, <<<EOF
 <p>This is a test document.</p>
 <style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
 <link rel="mw-deduplicated-inline-style" href="mw-data:duplicate1"/>
@@ -382,7 +285,7 @@ EOF
 EOF
                        ],
                        'Style deduplication disabled' => [
-                               [ 'deduplicateStyles' => false ], [], $dedupText, $dedupText
+                               [ 'deduplicateStyles' => false ], $dedupText, $dedupText
                        ],
                ];
                // phpcs:enable
index 7203777..a78bd82 100755 (executable)
@@ -21,6 +21,7 @@ class PHPUnitMaintClass extends Maintenance {
                'use-bagostuff' => false,
                'use-jobqueue' => false,
                'use-normal-tables' => false,
+               'mwdebug' => false,
                'reuse-db' => false,
                'wiki' => false,
                'profiler' => false,
index 6d86551..d0126f2 100644 (file)
@@ -20,11 +20,9 @@ class ApiStructureTest extends MediaWikiTestCase {
        private static $testGlobals = [
                [
                        'MiserMode' => false,
-                       'AllowCategorizedRecentChanges' => false,
                ],
                [
                        'MiserMode' => true,
-                       'AllowCategorizedRecentChanges' => true,
                ],
        ];