Merge "Follow-up I0bb4ed7f7: Use correct 'this'"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 16 Feb 2018 12:26:17 +0000 (12:26 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 16 Feb 2018 12:26:17 +0000 (12:26 +0000)
87 files changed:
.travis.yml
Gruntfile.js
RELEASE-NOTES-1.31
autoload.php
composer.json
docs/globals.txt
includes/CategoryFinder.php
includes/DefaultSettings.php
includes/DevelopmentSettings.php [new file with mode: 0644]
includes/EditPage.php
includes/OutputPage.php
includes/Revision.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/changes/EnhancedChangesList.php
includes/db/DatabaseOracle.php
includes/deferred/SiteStatsUpdate.php
includes/diff/DifferenceEngine.php
includes/exception/MWException.php
includes/installer/Installer.php
includes/installer/i18n/ast.json
includes/installer/i18n/es-formal.json
includes/installer/i18n/ia.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/database/DatabaseMssql.php
includes/libs/rdbms/database/DatabaseMysqlBase.php
includes/libs/rdbms/database/DatabaseMysqli.php
includes/libs/rdbms/database/DatabasePostgres.php
includes/libs/rdbms/database/DatabaseSqlite.php
includes/libs/rdbms/database/IDatabase.php
includes/libs/rdbms/database/position/MySQLMasterPos.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/resourceloader/ResourceLoaderModule.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialUndelete.php
includes/user/UserGroupMembership.php
languages/i18n/ast.json
languages/i18n/be-tarask.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/es-formal.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/gan-hans.json
languages/i18n/ie.json
languages/i18n/lt.json
languages/i18n/ms.json
languages/i18n/qqq.json
languages/i18n/ru.json
languages/i18n/sh.json
languages/i18n/sq.json
languages/i18n/sr-ec.json
languages/i18n/sv.json
languages/i18n/wo.json
languages/i18n/zh-hant.json
maintenance/Maintenance.php
resources/Resources.php
resources/src/startup.js
tests/phpunit/includes/SiteStatsTest.php
tests/phpunit/includes/db/DatabaseSqliteTest.php
tests/phpunit/includes/db/DatabaseTestHelper.php
tests/phpunit/includes/db/LBFactoryTest.php
tests/phpunit/includes/exception/BadTitleErrorTest.php
tests/phpunit/includes/exception/ThrottledErrorTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseTest.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 cc08b33..24239fd 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
@@ -63,6 +65,7 @@ production.
 * Updated wikimedia/running-stat from 1.1.0 to 1.2.0.
 * Updated wikimedia/wrappedstring from 2.2.0 to 2.3.0.
 * Updated mediawiki/at-ease from 1.1.0 to 1.2.0.
+* Updated wikimedia/php-session-serializer from 1.0.4 to 1.0.5.
 * …
 
 ==== New external libraries ====
@@ -233,6 +236,7 @@ changes to languages because of Phabricator reports.
   * CommentStore::getCommentLegacy
   * CommentStore::insert
   * CommentStore::insertWithTemplate
+* The method ResourceLoaderModule::getPosition(), deprecated in 1.29, has been removed.
 
 == Compatibility ==
 MediaWiki 1.31 requires PHP 5.5.9 or later. Although HHVM 3.18.5 or later is supported,
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 c3ff8d6..56d1633 100644 (file)
@@ -37,7 +37,7 @@
                "wikimedia/html-formatter": "1.0.1",
                "wikimedia/ip-set": "1.2.0",
                "wikimedia/object-factory": "1.0.0",
-               "wikimedia/php-session-serializer": "1.0.4",
+               "wikimedia/php-session-serializer": "1.0.5",
                "wikimedia/purtle": "1.0.6",
                "wikimedia/relpath": "2.1.1",
                "wikimedia/remex-html": "1.0.2",
index 8b4c755..5bdb31c 100644 (file)
@@ -9,7 +9,7 @@ in a much more flexible way. Consider the elegance of:
     # Generate the article HTML as if viewed by a web request
     $article = new Article( Title::newFromText( $t ) );
     $article->view();
+
 versus
 
     # Save current globals
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 a6a3686..ae5cef5 100644 (file)
@@ -6673,9 +6673,9 @@ $wgGitBin = '/usr/bin/git';
  */
 $wgGitRepositoryViewers = [
        'https://(?:[a-z0-9_]+@)?gerrit.wikimedia.org/r/(?:p/)?(.*)' =>
-               'https://phabricator.wikimedia.org/r/revision/%R;%H',
+               'https://gerrit.wikimedia.org/g/%R/+/%H',
        'ssh://(?:[a-z0-9_]+@)?gerrit.wikimedia.org:29418/(.*)' =>
-               'https://phabricator.wikimedia.org/r/revision/%R;%H',
+               'https://gerrit.wikimedia.org/g/%R/+/%H',
 ];
 
 /** @} */ # End of maintenance }
@@ -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 6b44a55..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
         */
@@ -493,7 +488,7 @@ class OutputPage extends ContextSource {
         * Filter an array of modules to remove insufficiently trustworthy members, and modules
         * which are no longer registered (eg a page is cached before an extension is disabled)
         * @param array $modules
-        * @param string|null $position If not null, only return modules with this position
+        * @param string|null $position Unused
         * @param string $type
         * @return array
         */
@@ -506,7 +501,6 @@ class OutputPage extends ContextSource {
                        $module = $resourceLoader->getModule( $val );
                        if ( $module instanceof ResourceLoaderModule
                                && $module->getOrigin() <= $this->getAllowedModules( $type )
-                               && ( is_null( $position ) || $module->getPosition() == $position )
                        ) {
                                if ( $this->mTarget && !in_array( $this->mTarget, $module->getTargets() ) ) {
                                        $this->warnModuleTargetFilter( $module->getName() );
@@ -537,7 +531,7 @@ class OutputPage extends ContextSource {
         * Get the list of modules to include on this page
         *
         * @param bool $filter Whether to filter out insufficiently trustworthy modules
-        * @param string|null $position If not null, only return modules with this position
+        * @param string|null $position Unused
         * @param string $param
         * @param string $type
         * @return array Array of module names
@@ -547,7 +541,7 @@ class OutputPage extends ContextSource {
        ) {
                $modules = array_values( array_unique( $this->$param ) );
                return $filter
-                       ? $this->filterModules( $modules, $position, $type )
+                       ? $this->filterModules( $modules, null, $type )
                        : $modules;
        }
 
@@ -566,11 +560,11 @@ class OutputPage extends ContextSource {
         * Get the list of module JS to include on this page
         *
         * @param bool $filter
-        * @param string|null $position
+        * @param string|null $position Unused
         * @return array Array of module names
         */
        public function getModuleScripts( $filter = false, $position = null ) {
-               return $this->getModules( $filter, $position, 'mModuleScripts',
+               return $this->getModules( $filter, null, 'mModuleScripts',
                        ResourceLoaderModule::TYPE_SCRIPTS
                );
        }
@@ -590,11 +584,11 @@ class OutputPage extends ContextSource {
         * Get the list of module CSS to include on this page
         *
         * @param bool $filter
-        * @param string|null $position
+        * @param string|null $position Unused
         * @return array Array of module names
         */
        public function getModuleStyles( $filter = false, $position = null ) {
-               return $this->getModules( $filter, $position, 'mModuleStyles',
+               return $this->getModules( $filter, null, 'mModuleStyles',
                        ResourceLoaderModule::TYPE_STYLES
                );
        }
@@ -1548,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 ) );
@@ -1562,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 ) {
@@ -1572,7 +1564,6 @@ class OutputPage extends ContextSource {
                        }
 
                        $this->mParserOptions = ParserOptions::newFromContext( $this->getContext() );
-                       $this->mParserOptions->setEditSection( false );
                        $this->mParserOptions->setAllowUnsafeRawHtml( false );
                }
 
@@ -1822,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;
                }
        }
@@ -1868,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 );
        }
 
@@ -2977,8 +2957,8 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * JS stuff to put at the bottom of the `<body>`. These are modules with position 'bottom',
-        * legacy scripts ($this->mScripts), and user JS.
+        * JS stuff to put at the bottom of the `<body>`.
+        * These are legacy scripts ($this->mScripts), and user JS.
         *
         * @return string|WrappedStringList HTML
         */
@@ -3896,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' );
        }
 
        /**
@@ -3905,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 eba563c..d9d3149 100644 (file)
@@ -906,6 +906,11 @@ class Revision implements IDBAccessObject {
                        return $this->mRecord->getContent( 'main', $audience, $user );
                }
                catch ( RevisionAccessException $e ) {
+                       wfDebugLog(
+                               'T184670',
+                               __METHOD__ . ": Cannot get content: " . $e->getMessage() .
+                               "\n" . $e->getTraceAsString()
+                       );
                        return null;
                }
        }
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 0df6828..6c7666f 100644 (file)
@@ -324,7 +324,7 @@ class EnhancedChangesList extends ChangesList {
                                $first--;
                        }
                        # Get net change
-                       $charDifference = $this->formatCharacterDifference( $block[$first], $block[$last] );
+                       $charDifference = $this->formatCharacterDifference( $block[$first], $block[$last] ) ?: false;
                }
 
                $numberofWatchingusers = $this->numberofWatchingusers( $block[0]->numberofWatchingusers );
index eb28b30..156e315 100644 (file)
@@ -61,7 +61,7 @@ class DatabaseOracle extends Database {
        }
 
        function __destruct() {
-               if ( $this->mOpened ) {
+               if ( $this->opened ) {
                        Wikimedia\suppressWarnings();
                        $this->close();
                        Wikimedia\restoreWarnings();
@@ -100,21 +100,21 @@ class DatabaseOracle extends Database {
                }
 
                $this->close();
-               $this->mUser = $user;
-               $this->mPassword = $password;
+               $this->user = $user;
+               $this->password = $password;
                // changed internal variables functions
                // mServer now holds the TNS endpoint
                // mDBname is schema name if different from username
                if ( !$server ) {
                        // backward compatibillity (server used to be null and TNS was supplied in dbname)
-                       $this->mServer = $dbName;
-                       $this->mDBname = $user;
+                       $this->server = $dbName;
+                       $this->dbName = $user;
                } else {
-                       $this->mServer = $server;
+                       $this->server = $server;
                        if ( !$dbName ) {
-                               $this->mDBname = $user;
+                               $this->dbName = $user;
                        } else {
-                               $this->mDBname = $dbName;
+                               $this->dbName = $dbName;
                        }
                }
 
@@ -126,53 +126,53 @@ class DatabaseOracle extends Database {
                        $this->setFlag( DBO_PERSISTENT );
                }
 
-               $session_mode = $this->mFlags & DBO_SYSDBA ? OCI_SYSDBA : OCI_DEFAULT;
+               $session_mode = $this->flags & DBO_SYSDBA ? OCI_SYSDBA : OCI_DEFAULT;
 
                Wikimedia\suppressWarnings();
-               if ( $this->mFlags & DBO_PERSISTENT ) {
-                       $this->mConn = oci_pconnect(
-                               $this->mUser,
-                               $this->mPassword,
-                               $this->mServer,
+               if ( $this->flags & DBO_PERSISTENT ) {
+                       $this->conn = oci_pconnect(
+                               $this->user,
+                               $this->password,
+                               $this->server,
                                $this->defaultCharset,
                                $session_mode
                        );
-               } elseif ( $this->mFlags & DBO_DEFAULT ) {
-                       $this->mConn = oci_new_connect(
-                               $this->mUser,
-                               $this->mPassword,
-                               $this->mServer,
+               } elseif ( $this->flags & DBO_DEFAULT ) {
+                       $this->conn = oci_new_connect(
+                               $this->user,
+                               $this->password,
+                               $this->server,
                                $this->defaultCharset,
                                $session_mode
                        );
                } else {
-                       $this->mConn = oci_connect(
-                               $this->mUser,
-                               $this->mPassword,
-                               $this->mServer,
+                       $this->conn = oci_connect(
+                               $this->user,
+                               $this->password,
+                               $this->server,
                                $this->defaultCharset,
                                $session_mode
                        );
                }
                Wikimedia\restoreWarnings();
 
-               if ( $this->mUser != $this->mDBname ) {
+               if ( $this->user != $this->dbName ) {
                        // change current schema in session
-                       $this->selectDB( $this->mDBname );
+                       $this->selectDB( $this->dbName );
                }
 
-               if ( !$this->mConn ) {
+               if ( !$this->conn ) {
                        throw new DBConnectionError( $this, $this->lastError() );
                }
 
-               $this->mOpened = true;
+               $this->opened = true;
 
                # removed putenv calls because they interfere with the system globaly
                $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
                $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' );
                $this->doQuery( 'ALTER SESSION SET NLS_NUMERIC_CHARACTERS=\'.,\'' );
 
-               return $this->mConn;
+               return $this->conn;
        }
 
        /**
@@ -181,11 +181,11 @@ class DatabaseOracle extends Database {
         * @return bool
         */
        protected function closeConnection() {
-               return oci_close( $this->mConn );
+               return oci_close( $this->conn );
        }
 
        function execFlags() {
-               return $this->mTrxLevel ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS;
+               return $this->trxLevel ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS;
        }
 
        protected function doQuery( $sql ) {
@@ -217,9 +217,9 @@ class DatabaseOracle extends Database {
 
                Wikimedia\suppressWarnings();
 
-               $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql );
+               $this->mLastResult = $stmt = oci_parse( $this->conn, $sql );
                if ( $stmt === false ) {
-                       $e = oci_error( $this->mConn );
+                       $e = oci_error( $this->conn );
                        $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
 
                        return false;
@@ -335,20 +335,20 @@ class DatabaseOracle extends Database {
        }
 
        function lastError() {
-               if ( $this->mConn === false ) {
+               if ( $this->conn === false ) {
                        $e = oci_error();
                } else {
-                       $e = oci_error( $this->mConn );
+                       $e = oci_error( $this->conn );
                }
 
                return $e['message'];
        }
 
        function lastErrno() {
-               if ( $this->mConn === false ) {
+               if ( $this->conn === false ) {
                        $e = oci_error();
                } else {
-                       $e = oci_error( $this->mConn );
+                       $e = oci_error( $this->conn );
                }
 
                return $e['code'];
@@ -470,9 +470,9 @@ class DatabaseOracle extends Database {
                }
                $sql .= ')';
 
-               $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql );
+               $this->mLastResult = $stmt = oci_parse( $this->conn, $sql );
                if ( $stmt === false ) {
-                       $e = oci_error( $this->mConn );
+                       $e = oci_error( $this->conn );
                        $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
 
                        return false;
@@ -502,7 +502,7 @@ class DatabaseOracle extends Database {
                                }
                        } else {
                                /** @var OCI_Lob[] $lob */
-                               $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB );
+                               $lob[$col] = oci_new_descriptor( $this->conn, OCI_D_LOB );
                                if ( $lob[$col] === false ) {
                                        $e = oci_error( $stmt );
                                        throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] );
@@ -545,8 +545,8 @@ class DatabaseOracle extends Database {
                        }
                }
 
-               if ( !$this->mTrxLevel ) {
-                       oci_commit( $this->mConn );
+               if ( !$this->trxLevel ) {
+                       oci_commit( $this->conn );
                }
 
                return oci_free_statement( $stmt );
@@ -658,13 +658,13 @@ class DatabaseOracle extends Database {
                        FROM all_sequences asq, all_tab_columns atc
                        WHERE decode(
                                        atc.table_name,
-                                       '{$this->mTablePrefix}MWUSER',
-                                       '{$this->mTablePrefix}USER',
+                                       '{$this->tablePrefix}MWUSER',
+                                       '{$this->tablePrefix}USER',
                                        atc.table_name
                                ) || '_' ||
-                               atc.column_name || '_SEQ' = '{$this->mTablePrefix}' || asq.sequence_name
-                               AND asq.sequence_owner = upper('{$this->mDBname}')
-                               AND atc.owner = upper('{$this->mDBname}')" );
+                               atc.column_name || '_SEQ' = '{$this->tablePrefix}' || asq.sequence_name
+                               AND asq.sequence_owner = upper('{$this->dbName}')
+                               AND atc.owner = upper('{$this->dbName}')" );
 
                        while ( ( $row = $result->fetchRow() ) !== false ) {
                                $this->sequenceData[$row[1]] = [
@@ -730,9 +730,9 @@ class DatabaseOracle extends Database {
                $newName = strtoupper( $newName );
                $oldName = strtoupper( $oldName );
 
-               $tabName = substr( $newName, strlen( $this->mTablePrefix ) );
+               $tabName = substr( $newName, strlen( $this->tablePrefix ) );
                $oldPrefix = substr( $oldName, 0, strlen( $oldName ) - strlen( $tabName ) );
-               $newPrefix = strtoupper( $this->mTablePrefix );
+               $newPrefix = strtoupper( $this->tablePrefix );
 
                return $this->doQuery( "BEGIN DUPLICATE_TABLE( '$tabName', " .
                        "'$oldPrefix', '$newPrefix', $temporary ); END;" );
@@ -744,7 +744,7 @@ class DatabaseOracle extends Database {
                        $listWhere = ' AND table_name LIKE \'' . strtoupper( $prefix ) . '%\'';
                }
 
-               $owner = strtoupper( $this->mDBname );
+               $owner = strtoupper( $this->dbName );
                $result = $this->doQuery( "SELECT table_name FROM all_tables " .
                        "WHERE owner='$owner' AND table_name NOT LIKE '%!_IDX\$_' ESCAPE '!' $listWhere" );
 
@@ -805,7 +805,7 @@ class DatabaseOracle extends Database {
                );
                $row = $rset->fetchRow();
                if ( !$row ) {
-                       return oci_server_version( $this->mConn );
+                       return oci_server_version( $this->conn );
                }
 
                return $row['version'];
@@ -822,7 +822,7 @@ class DatabaseOracle extends Database {
                $table = $this->tableName( $table );
                $table = strtoupper( $this->removeIdentifierQuotes( $table ) );
                $index = strtoupper( $index );
-               $owner = strtoupper( $this->mDBname );
+               $owner = strtoupper( $this->dbName );
                $sql = "SELECT 1 FROM all_indexes WHERE owner='$owner' AND index_name='{$table}_{$index}'";
                $res = $this->doQuery( $sql );
                if ( $res ) {
@@ -844,7 +844,7 @@ class DatabaseOracle extends Database {
        function tableExists( $table, $fname = __METHOD__ ) {
                $table = $this->tableName( $table );
                $table = $this->addQuotes( strtoupper( $this->removeIdentifierQuotes( $table ) ) );
-               $owner = $this->addQuotes( strtoupper( $this->mDBname ) );
+               $owner = $this->addQuotes( strtoupper( $this->dbName ) );
                $sql = "SELECT 1 FROM all_tables WHERE owner=$owner AND table_name=$table";
                $res = $this->doQuery( $sql );
                if ( $res && $res->numRows() > 0 ) {
@@ -890,7 +890,7 @@ class DatabaseOracle extends Database {
                }
 
                $fieldInfoStmt = oci_parse(
-                       $this->mConn,
+                       $this->conn,
                        'SELECT * FROM wiki_field_info_full WHERE table_name ' .
                                $tableWhere . ' and column_name = \'' . $field . '\''
                );
@@ -935,25 +935,25 @@ class DatabaseOracle extends Database {
        }
 
        protected function doBegin( $fname = __METHOD__ ) {
-               $this->mTrxLevel = 1;
+               $this->trxLevel = 1;
                $this->doQuery( 'SET CONSTRAINTS ALL DEFERRED' );
        }
 
        protected function doCommit( $fname = __METHOD__ ) {
-               if ( $this->mTrxLevel ) {
-                       $ret = oci_commit( $this->mConn );
+               if ( $this->trxLevel ) {
+                       $ret = oci_commit( $this->conn );
                        if ( !$ret ) {
                                throw new DBUnexpectedError( $this, $this->lastError() );
                        }
-                       $this->mTrxLevel = 0;
+                       $this->trxLevel = 0;
                        $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
                }
        }
 
        protected function doRollback( $fname = __METHOD__ ) {
-               if ( $this->mTrxLevel ) {
-                       oci_rollback( $this->mConn );
-                       $this->mTrxLevel = 0;
+               if ( $this->trxLevel ) {
+                       oci_rollback( $this->conn );
+                       $this->trxLevel = 0;
                        $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' );
                }
        }
@@ -1041,12 +1041,12 @@ class DatabaseOracle extends Database {
        }
 
        function selectDB( $db ) {
-               $this->mDBname = $db;
-               if ( $db == null || $db == $this->mUser ) {
+               $this->dbName = $db;
+               if ( $db == null || $db == $this->user ) {
                        return true;
                }
                $sql = 'ALTER SESSION SET CURRENT_SCHEMA=' . strtoupper( $db );
-               $stmt = oci_parse( $this->mConn, $sql );
+               $stmt = oci_parse( $this->conn, $sql );
                Wikimedia\suppressWarnings();
                $success = oci_execute( $stmt );
                Wikimedia\restoreWarnings();
@@ -1245,9 +1245,9 @@ class DatabaseOracle extends Database {
                        $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
                }
 
-               $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql );
+               $this->mLastResult = $stmt = oci_parse( $this->conn, $sql );
                if ( $stmt === false ) {
-                       $e = oci_error( $this->mConn );
+                       $e = oci_error( $this->conn );
                        $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ );
 
                        return false;
@@ -1276,7 +1276,7 @@ class DatabaseOracle extends Database {
                                }
                        } else {
                                /** @var OCI_Lob[] $lob */
-                               $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB );
+                               $lob[$col] = oci_new_descriptor( $this->conn, OCI_D_LOB );
                                if ( $lob[$col] === false ) {
                                        $e = oci_error( $stmt );
                                        throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] );
@@ -1319,8 +1319,8 @@ class DatabaseOracle extends Database {
                        }
                }
 
-               if ( !$this->mTrxLevel ) {
-                       oci_commit( $this->mConn );
+               if ( !$this->trxLevel ) {
+                       oci_commit( $this->conn );
                }
 
                return oci_free_statement( $stmt );
@@ -1340,11 +1340,11 @@ class DatabaseOracle extends Database {
        }
 
        function getDBname() {
-               return $this->mDBname;
+               return $this->dbName;
        }
 
        function getServer() {
-               return $this->mServer;
+               return $this->server;
        }
 
        public function buildGroupConcatField(
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 16f226c..b3e9422 100644 (file)
@@ -189,7 +189,7 @@ class MWException extends Exception {
                } elseif ( self::isCommandLine() ) {
                        $message = $this->getText();
                        // T17602: STDERR may not be available
-                       if ( defined( 'STDERR' ) ) {
+                       if ( !defined( 'MW_PHPUNIT_TEST' ) && defined( 'STDERR' ) ) {
                                fwrite( STDERR, $message );
                        } else {
                                echo $message;
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 b15c7f2..da63fdc 100644 (file)
@@ -2,8 +2,18 @@
        "@metadata": {
                "authors": [
                        "Dferg",
-                       "Seb35"
+                       "Seb35",
+                       "MarcoAurelio"
                ]
        },
-       "mainpagedocfooter": "Consulte usted la [https://meta.wikimedia.org/wiki/Help:Contents/es Guía de usuario] para obtener información sobre el uso del software wiki.\n\n== Empezando ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista de ajustes de configuración]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/es FAQ de MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Lista de correo de anuncios de distribución de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Regionalizar MediaWiki para su idioma]"
+       "config-localsettings-upgrade": "Se ha encontrado un archivo <code>LocalSettings.php</code>.\nPara actualizar esta instalación, escriba el valor de <code>$wgUpgradeKey</code> en el cuadro de abajo.\nLo encontrará en <code>LocalSettings.php</code>.",
+       "config-localsettings-cli-upgrade": "Se ha detectado un archivo <code>LocalSettings.php</code>.\nPara actualizar la instalación, en su lugar ejecute <code>update.php</code>",
+       "config-localsettings-connection-error": "Se ha producido un error al conectar con la base de datos a través de la configuración especificada en <code>LocalSettings.php</code>. Corrija estos ajustes e inténtelo de nuevo.\n\n$1",
+       "config-your-language-help": "Seleccione un idioma para usar durante el proceso de instalación.",
+       "config-page-welcome": "Le damos la bienvenida a MediaWiki.",
+       "config-page-readme": "Léame",
+       "config-help-restart": "¿Desea borrar todos los datos guardados que ha escrito y reiniciar el proceso de instalación?",
+       "config-env-good": "El entorno ha sido comprobado.\nPuede instalar MediaWiki.",
+       "config-env-bad": "El entorno ha sido comprobado.\nNo puede instalar MediaWiki.",
+       "mainpagedocfooter": "Consulte la [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents guía] para obtener información sobre el uso del software wiki.\n\n== Primeros pasos ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista de ajustes de configuración]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Preguntas frecuentes sobre MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Lista de correo de anuncios de publicación de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Traducir MediaWiki a su idioma]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Aprenda a combatir el spam en su wiki]"
 }
index 6f3d84c..a7ede0a 100644 (file)
@@ -3,7 +3,8 @@
                "authors": [
                        "McDutchie",
                        "아라",
-                       "Macofe"
+                       "Macofe",
+                       "Fanjiayi"
                ]
        },
        "config-desc": "Le installator de MediaWiki",
@@ -62,7 +63,7 @@
        "config-apc": "[http://www.php.net/apc APC] es installate",
        "config-apcu": "[http://www.php.net/apcu APCu] es installate",
        "config-wincache": "[https://www.iis.net/download/WinCacheForPhp WinCache] es installate",
-       "config-no-cache-apcu": "<strong>Attention:</strong> Impossibile trovar [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] o [http://www.iis.net/download/WinCacheForPhp WinCache].\nLe cache de objectos non es activate.",
+       "config-no-cache-apcu": "<strong>Attention:</strong> Impossibile trovar [http://www.php.net/apcu APCu] o [http://www.iis.net/download/WinCacheForPhp WinCache].\nLe cache de objectos non es activate.",
        "config-mod-security": "<strong>Attention</strong>: [https://modsecurity.org/ mod_security]/mod_security2 es active in tu servitor web. Multe configurationes commun de isto causa problemas pro MediaWiki o altere software que permitte al usatores de publicar contento arbitrari. Si possibile, isto deberea esser disactivate.\nAlteremente, consulta le [https://modsecurity.org/documentation/ documentation de mod_security] o contacta le servicio de adjuta de tu servitor si tu incontra estranie errores.",
        "config-diff3-bad": "GNU diff3 non trovate.",
        "config-git": "Systema de controlo de version Git trovate: <code>$1</code>",
        "config-cache-options": "Configuration del cache de objectos:",
        "config-cache-help": "Le cache de objectos es usate pro meliorar le rapiditate de MediaWiki per immagazinar le datos frequentemente usate.\nLe sitos medie o grande es multo incoragiate de activar isto, ma anque le sitos parve percipera le beneficios.",
        "config-cache-none": "Nulle cache (nulle functionalitate es removite, ma le rapiditate pote diminuer in grande sitos wiki)",
-       "config-cache-accel": "Cache de objectos PHP (APC, APCu, XCache o WinCache)",
+       "config-cache-accel": "Cache de objectos PHP (APC, APCu o WinCache)",
        "config-cache-memcached": "Usar Memcached (require additional installation e configuration)",
        "config-memcached-servers": "Servitores Memcached:",
        "config-memcached-help": "Lista de adresses IP a usar pro Memcached.\nDebe specificar un per linea e specificar le porto a usar. Per exemplo:\n 127.0.0.1:11211\n 192.168.1.25:1234",
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..9a8996c 100644 (file)
@@ -59,19 +59,19 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        const SMALL_WRITE_ROWS = 100;
 
        /** @var string SQL query */
-       protected $mLastQuery = '';
+       protected $lastQuery = '';
        /** @var float|bool UNIX timestamp of last write query */
-       protected $mLastWriteTime = false;
+       protected $lastWriteTime = false;
        /** @var string|bool */
-       protected $mPHPError = false;
+       protected $phpError = false;
        /** @var string */
-       protected $mServer;
+       protected $server;
        /** @var string */
-       protected $mUser;
+       protected $user;
        /** @var string */
-       protected $mPassword;
+       protected $password;
        /** @var string */
-       protected $mDBname;
+       protected $dbName;
        /** @var array[] $aliases Map of (table => (dbname, schema, prefix) map) */
        protected $tableAliases = [];
        /** @var bool Whether this PHP instance is for a CLI script */
@@ -89,35 +89,33 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        protected $errorLogger;
 
        /** @var resource|null Database connection */
-       protected $mConn = null;
+       protected $conn = null;
        /** @var bool */
-       protected $mOpened = false;
+       protected $opened = false;
 
        /** @var array[] List of (callable, method name) */
-       protected $mTrxIdleCallbacks = [];
+       protected $trxIdleCallbacks = [];
        /** @var array[] List of (callable, method name) */
-       protected $mTrxPreCommitCallbacks = [];
+       protected $trxPreCommitCallbacks = [];
        /** @var array[] List of (callable, method name) */
-       protected $mTrxEndCallbacks = [];
+       protected $trxEndCallbacks = [];
        /** @var callable[] Map of (name => callable) */
-       protected $mTrxRecurringCallbacks = [];
+       protected $trxRecurringCallbacks = [];
        /** @var bool Whether to suppress triggering of transaction end callbacks */
-       protected $mTrxEndCallbacksSuppressed = false;
+       protected $trxEndCallbacksSuppressed = false;
 
        /** @var string */
-       protected $mTablePrefix = '';
+       protected $tablePrefix = '';
        /** @var string */
-       protected $mSchema = '';
+       protected $schema = '';
        /** @var int */
-       protected $mFlags;
+       protected $flags;
        /** @var array */
-       protected $mLBInfo = [];
-       /** @var bool|null */
-       protected $mDefaultBigSelects = null;
+       protected $lbInfo = [];
        /** @var array|bool */
-       protected $mSchemaVars = false;
+       protected $schemaVars = false;
        /** @var array */
-       protected $mSessionVars = [];
+       protected $sessionVars = [];
        /** @var array|null */
        protected $preparedArgs;
        /** @var string|bool|null Stashed value of html_errors INI setting */
@@ -135,94 +133,94 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         *
         * @var int
         */
-       protected $mTrxLevel = 0;
+       protected $trxLevel = 0;
        /**
         * Either a short hexidecimal string if a transaction is active or ""
         *
         * @var string
-        * @see Database::mTrxLevel
+        * @see Database::trxLevel
         */
-       protected $mTrxShortId = '';
+       protected $trxShortId = '';
        /**
         * The UNIX time that the transaction started. Callers can assume that if
         * snapshot isolation is used, then the data is *at least* up to date to that
         * point (possibly more up-to-date since the first SELECT defines the snapshot).
         *
         * @var float|null
-        * @see Database::mTrxLevel
+        * @see Database::trxLevel
         */
-       private $mTrxTimestamp = null;
+       private $trxTimestamp = null;
        /** @var float Lag estimate at the time of BEGIN */
-       private $mTrxReplicaLag = null;
+       private $trxReplicaLag = null;
        /**
         * Remembers the function name given for starting the most recent transaction via begin().
         * Used to provide additional context for error reporting.
         *
         * @var string
-        * @see Database::mTrxLevel
+        * @see Database::trxLevel
         */
-       private $mTrxFname = null;
+       private $trxFname = null;
        /**
         * Record if possible write queries were done in the last transaction started
         *
         * @var bool
-        * @see Database::mTrxLevel
+        * @see Database::trxLevel
         */
-       private $mTrxDoneWrites = false;
+       private $trxDoneWrites = false;
        /**
         * Record if the current transaction was started implicitly due to DBO_TRX being set.
         *
         * @var bool
-        * @see Database::mTrxLevel
+        * @see Database::trxLevel
         */
-       private $mTrxAutomatic = false;
+       private $trxAutomatic = false;
        /**
         * Array of levels of atomicity within transactions
         *
         * @var array
         */
-       private $mTrxAtomicLevels = [];
+       private $trxAtomicLevels = [];
        /**
         * Record if the current transaction was started implicitly by Database::startAtomic
         *
         * @var bool
         */
-       private $mTrxAutomaticAtomic = false;
+       private $trxAutomaticAtomic = false;
        /**
         * Track the write query callers of the current transaction
         *
         * @var string[]
         */
-       private $mTrxWriteCallers = [];
+       private $trxWriteCallers = [];
        /**
         * @var float Seconds spent in write queries for the current transaction
         */
-       private $mTrxWriteDuration = 0.0;
+       private $trxWriteDuration = 0.0;
        /**
         * @var int Number of write queries for the current transaction
         */
-       private $mTrxWriteQueryCount = 0;
+       private $trxWriteQueryCount = 0;
        /**
         * @var int Number of rows affected by write queries for the current transaction
         */
-       private $mTrxWriteAffectedRows = 0;
+       private $trxWriteAffectedRows = 0;
        /**
-        * @var float Like mTrxWriteQueryCount but excludes lock-bound, easy to replicate, queries
+        * @var float Like trxWriteQueryCount but excludes lock-bound, easy to replicate, queries
         */
-       private $mTrxWriteAdjDuration = 0.0;
+       private $trxWriteAdjDuration = 0.0;
        /**
-        * @var int Number of write queries counted in mTrxWriteAdjDuration
+        * @var int Number of write queries counted in trxWriteAdjDuration
         */
-       private $mTrxWriteAdjQueryCount = 0;
+       private $trxWriteAdjQueryCount = 0;
        /**
         * @var float RTT time estimate
         */
-       private $mRTTEstimate = 0.0;
+       private $rttEstimate = 0.0;
 
        /** @var array Map of (name => 1) for locks obtained via lock() */
-       private $mNamedLocksHeld = [];
+       private $namedLocksHeld = [];
        /** @var array Map of (table name => 1) for TEMPORARY tables */
-       protected $mSessionTempTables = [];
+       protected $sessionTempTables = [];
 
        /** @var IDatabase|null Lazy handle to the master DB this server replicates from */
        private $lazyMasterHandle;
@@ -230,7 +228,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        /** @var float UNIX timestamp */
        protected $lastPing = 0.0;
 
-       /** @var int[] Prior mFlags values */
+       /** @var int[] Prior flags member variable values */
        private $priorFlags = [];
 
        /** @var object|string Class name or object With profileIn/profileOut methods */
@@ -252,23 +250,23 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $password = $params['password'];
                $dbName = $params['dbname'];
 
-               $this->mSchema = $params['schema'];
-               $this->mTablePrefix = $params['tablePrefix'];
+               $this->schema = $params['schema'];
+               $this->tablePrefix = $params['tablePrefix'];
 
                $this->cliMode = $params['cliMode'];
                // Agent name is added to SQL queries in a comment, so make sure it can't break out
                $this->agent = str_replace( '/', '-', $params['agent'] );
 
-               $this->mFlags = $params['flags'];
-               if ( $this->mFlags & self::DBO_DEFAULT ) {
+               $this->flags = $params['flags'];
+               if ( $this->flags & self::DBO_DEFAULT ) {
                        if ( $this->cliMode ) {
-                               $this->mFlags &= ~self::DBO_TRX;
+                               $this->flags &= ~self::DBO_TRX;
                        } else {
-                               $this->mFlags |= self::DBO_TRX;
+                               $this->flags |= self::DBO_TRX;
                        }
                }
 
-               $this->mSessionVars = $params['variables'];
+               $this->sessionVars = $params['variables'];
 
                $this->srvCache = isset( $params['srvCache'] )
                        ? $params['srvCache']
@@ -290,9 +288,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
 
                // Set the domain object after open() sets the relevant fields
-               if ( $this->mDBname != '' ) {
+               if ( $this->dbName != '' ) {
                        // Domains with server scope but a table prefix are not used by IDatabase classes
-                       $this->currentDomain = new DatabaseDomain( $this->mDBname, null, $this->mTablePrefix );
+                       $this->currentDomain = new DatabaseDomain( $this->dbName, null, $this->tablePrefix );
                }
        }
 
@@ -464,9 +462,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                if ( $ignoreErrors !== null ) {
                        // setFlag()/clearFlag() do not allow DBO_IGNORE changes for sanity
                        if ( $ignoreErrors ) {
-                               $this->mFlags |= self::DBO_IGNORE;
+                               $this->flags |= self::DBO_IGNORE;
                        } else {
-                               $this->mFlags &= ~self::DBO_IGNORE;
+                               $this->flags &= ~self::DBO_IGNORE;
                        }
                }
 
@@ -474,19 +472,19 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        public function trxLevel() {
-               return $this->mTrxLevel;
+               return $this->trxLevel;
        }
 
        public function trxTimestamp() {
-               return $this->mTrxLevel ? $this->mTrxTimestamp : null;
+               return $this->trxLevel ? $this->trxTimestamp : null;
        }
 
        public function tablePrefix( $prefix = null ) {
-               $old = $this->mTablePrefix;
+               $old = $this->tablePrefix;
                if ( $prefix !== null ) {
-                       $this->mTablePrefix = $prefix;
-                       $this->currentDomain = ( $this->mDBname != '' )
-                               ? new DatabaseDomain( $this->mDBname, null, $this->mTablePrefix )
+                       $this->tablePrefix = $prefix;
+                       $this->currentDomain = ( $this->dbName != '' )
+                               ? new DatabaseDomain( $this->dbName, null, $this->tablePrefix )
                                : DatabaseDomain::newUnspecified();
                }
 
@@ -494,9 +492,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        public function dbSchema( $schema = null ) {
-               $old = $this->mSchema;
+               $old = $this->schema;
                if ( $schema !== null ) {
-                       $this->mSchema = $schema;
+                       $this->schema = $schema;
                }
 
                return $old;
@@ -504,10 +502,10 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
        public function getLBInfo( $name = null ) {
                if ( is_null( $name ) ) {
-                       return $this->mLBInfo;
+                       return $this->lbInfo;
                } else {
-                       if ( array_key_exists( $name, $this->mLBInfo ) ) {
-                               return $this->mLBInfo[$name];
+                       if ( array_key_exists( $name, $this->lbInfo ) ) {
+                               return $this->lbInfo[$name];
                        } else {
                                return null;
                        }
@@ -516,9 +514,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
        public function setLBInfo( $name, $value = null ) {
                if ( is_null( $value ) ) {
-                       $this->mLBInfo = $name;
+                       $this->lbInfo = $name;
                } else {
-                       $this->mLBInfo[$name] = $value;
+                       $this->lbInfo[$name] = $value;
                }
        }
 
@@ -544,55 +542,55 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        public function lastQuery() {
-               return $this->mLastQuery;
+               return $this->lastQuery;
        }
 
        public function doneWrites() {
-               return (bool)$this->mLastWriteTime;
+               return (bool)$this->lastWriteTime;
        }
 
        public function lastDoneWrites() {
-               return $this->mLastWriteTime ?: false;
+               return $this->lastWriteTime ?: false;
        }
 
        public function writesPending() {
-               return $this->mTrxLevel && $this->mTrxDoneWrites;
+               return $this->trxLevel && $this->trxDoneWrites;
        }
 
        public function writesOrCallbacksPending() {
-               return $this->mTrxLevel && (
-                       $this->mTrxDoneWrites || $this->mTrxIdleCallbacks || $this->mTrxPreCommitCallbacks
+               return $this->trxLevel && (
+                       $this->trxDoneWrites || $this->trxIdleCallbacks || $this->trxPreCommitCallbacks
                );
        }
 
        public function pendingWriteQueryDuration( $type = self::ESTIMATE_TOTAL ) {
-               if ( !$this->mTrxLevel ) {
+               if ( !$this->trxLevel ) {
                        return false;
-               } elseif ( !$this->mTrxDoneWrites ) {
+               } elseif ( !$this->trxDoneWrites ) {
                        return 0.0;
                }
 
                switch ( $type ) {
                        case self::ESTIMATE_DB_APPLY:
                                $this->ping( $rtt );
-                               $rttAdjTotal = $this->mTrxWriteAdjQueryCount * $rtt;
-                               $applyTime = max( $this->mTrxWriteAdjDuration - $rttAdjTotal, 0 );
+                               $rttAdjTotal = $this->trxWriteAdjQueryCount * $rtt;
+                               $applyTime = max( $this->trxWriteAdjDuration - $rttAdjTotal, 0 );
                                // For omitted queries, make them count as something at least
-                               $omitted = $this->mTrxWriteQueryCount - $this->mTrxWriteAdjQueryCount;
+                               $omitted = $this->trxWriteQueryCount - $this->trxWriteAdjQueryCount;
                                $applyTime += self::TINY_WRITE_SEC * $omitted;
 
                                return $applyTime;
                        default: // everything
-                               return $this->mTrxWriteDuration;
+                               return $this->trxWriteDuration;
                }
        }
 
        public function pendingWriteCallers() {
-               return $this->mTrxLevel ? $this->mTrxWriteCallers : [];
+               return $this->trxLevel ? $this->trxWriteCallers : [];
        }
 
        public function pendingWriteRowsAffected() {
-               return $this->mTrxWriteAffectedRows;
+               return $this->trxWriteAffectedRows;
        }
 
        /**
@@ -602,15 +600,15 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @return array
         */
        protected function pendingWriteAndCallbackCallers() {
-               if ( !$this->mTrxLevel ) {
+               if ( !$this->trxLevel ) {
                        return [];
                }
 
-               $fnames = $this->mTrxWriteCallers;
+               $fnames = $this->trxWriteCallers;
                foreach ( [
-                       $this->mTrxIdleCallbacks,
-                       $this->mTrxPreCommitCallbacks,
-                       $this->mTrxEndCallbacks
+                       $this->trxIdleCallbacks,
+                       $this->trxPreCommitCallbacks,
+                       $this->trxEndCallbacks
                ] as $callbacks ) {
                        foreach ( $callbacks as $callback ) {
                                $fnames[] = $callback[1];
@@ -621,7 +619,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        public function isOpen() {
-               return $this->mOpened;
+               return $this->opened;
        }
 
        public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
@@ -630,9 +628,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
 
                if ( $remember === self::REMEMBER_PRIOR ) {
-                       array_push( $this->priorFlags, $this->mFlags );
+                       array_push( $this->priorFlags, $this->flags );
                }
-               $this->mFlags |= $flag;
+               $this->flags |= $flag;
        }
 
        public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
@@ -641,9 +639,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
 
                if ( $remember === self::REMEMBER_PRIOR ) {
-                       array_push( $this->priorFlags, $this->mFlags );
+                       array_push( $this->priorFlags, $this->flags );
                }
-               $this->mFlags &= ~$flag;
+               $this->flags &= ~$flag;
        }
 
        public function restoreFlags( $state = self::RESTORE_PRIOR ) {
@@ -652,15 +650,15 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
 
                if ( $state === self::RESTORE_INITIAL ) {
-                       $this->mFlags = reset( $this->priorFlags );
+                       $this->flags = reset( $this->priorFlags );
                        $this->priorFlags = [];
                } else {
-                       $this->mFlags = array_pop( $this->priorFlags );
+                       $this->flags = array_pop( $this->priorFlags );
                }
        }
 
        public function getFlag( $flag ) {
-               return !!( $this->mFlags & $flag );
+               return !!( $this->flags & $flag );
        }
 
        /**
@@ -701,7 +699,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * Set a custom error handler for logging errors during database connection
         */
        protected function installErrorHandler() {
-               $this->mPHPError = false;
+               $this->phpError = false;
                $this->htmlErrors = ini_set( 'html_errors', '0' );
                set_error_handler( [ $this, 'connectionErrorLogger' ] );
        }
@@ -724,8 +722,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @return string|bool Last PHP error for this DB (typically connection errors)
         */
        protected function getLastPHPError() {
-               if ( $this->mPHPError ) {
-                       $error = preg_replace( '!\[<a.*</a>\]!', '', $this->mPHPError );
+               if ( $this->phpError ) {
+                       $error = preg_replace( '!\[<a.*</a>\]!', '', $this->phpError );
                        $error = preg_replace( '!^.*?:\s?(.*)$!', '$1', $error );
 
                        return $error;
@@ -742,7 +740,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @param string $errstr
         */
        public function connectionErrorLogger( $errno, $errstr ) {
-               $this->mPHPError = $errstr;
+               $this->phpError = $errstr;
        }
 
        /**
@@ -754,32 +752,32 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        protected function getLogContext( array $extras = [] ) {
                return array_merge(
                        [
-                               'db_server' => $this->mServer,
-                               'db_name' => $this->mDBname,
-                               'db_user' => $this->mUser,
+                               'db_server' => $this->server,
+                               'db_name' => $this->dbName,
+                               'db_user' => $this->user,
                        ],
                        $extras
                );
        }
 
        public function close() {
-               if ( $this->mConn ) {
+               if ( $this->conn ) {
                        if ( $this->trxLevel() ) {
                                $this->commit( __METHOD__, self::FLUSHING_INTERNAL );
                        }
 
                        $closed = $this->closeConnection();
-                       $this->mConn = false;
+                       $this->conn = false;
                } elseif (
-                       $this->mTrxIdleCallbacks ||
-                       $this->mTrxPreCommitCallbacks ||
-                       $this->mTrxEndCallbacks
+                       $this->trxIdleCallbacks ||
+                       $this->trxPreCommitCallbacks ||
+                       $this->trxEndCallbacks
                ) { // sanity
                        throw new RuntimeException( "Transaction callbacks still pending." );
                } else {
                        $closed = true;
                }
-               $this->mOpened = false;
+               $this->opened = false;
 
                return $closed;
        }
@@ -868,7 +866,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        $sql,
                        $matches
                ) ) {
-                       $this->mSessionTempTables[$matches[1]] = 1;
+                       $this->sessionTempTables[$matches[1]] = 1;
 
                        return true;
                } elseif ( preg_match(
@@ -876,8 +874,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        $sql,
                        $matches
                ) ) {
-                       $isTemp = isset( $this->mSessionTempTables[$matches[1]] );
-                       unset( $this->mSessionTempTables[$matches[1]] );
+                       $isTemp = isset( $this->sessionTempTables[$matches[1]] );
+                       unset( $this->sessionTempTables[$matches[1]] );
 
                        return $isTemp;
                } elseif ( preg_match(
@@ -885,13 +883,13 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        $sql,
                        $matches
                ) ) {
-                       return isset( $this->mSessionTempTables[$matches[1]] );
+                       return isset( $this->sessionTempTables[$matches[1]] );
                } elseif ( preg_match(
                        '/^(?:INSERT\s+(?:\w+\s+)?INTO|UPDATE|DELETE\s+FROM)\s+[`"\']?(\w+)[`"\']?/i',
                        $sql,
                        $matches
                ) ) {
-                       return isset( $this->mSessionTempTables[$matches[1]] );
+                       return isset( $this->sessionTempTables[$matches[1]] );
                }
 
                return false;
@@ -899,7 +897,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
        public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) {
                $priorWritesPending = $this->writesOrCallbacksPending();
-               $this->mLastQuery = $sql;
+               $this->lastQuery = $sql;
 
                $isWrite = $this->isWriteQuery( $sql );
                if ( $isWrite ) {
@@ -922,7 +920,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                throw new DBReadOnlyError( $this, "Database is read-only: $reason" );
                        }
                        # Set a flag indicating that writes have been done
-                       $this->mLastWriteTime = microtime( true );
+                       $this->lastWriteTime = microtime( true );
                }
 
                # Add trace comment to the begin of the sql string, right after the operator.
@@ -930,22 +928,22 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $commentedSql = preg_replace( '/\s|$/', " /* $fname {$this->agent} */ ", $sql, 1 );
 
                # Start implicit transactions that wrap the request if DBO_TRX is enabled
-               if ( !$this->mTrxLevel && $this->getFlag( self::DBO_TRX )
+               if ( !$this->trxLevel && $this->getFlag( self::DBO_TRX )
                        && $this->isTransactableQuery( $sql )
                ) {
                        $this->begin( __METHOD__ . " ($fname)", self::TRANSACTION_INTERNAL );
-                       $this->mTrxAutomatic = true;
+                       $this->trxAutomatic = true;
                }
 
                # Keep track of whether the transaction has write queries pending
-               if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $isWrite ) {
-                       $this->mTrxDoneWrites = true;
+               if ( $this->trxLevel && !$this->trxDoneWrites && $isWrite ) {
+                       $this->trxDoneWrites = true;
                        $this->trxProfiler->transactionWritingIn(
-                               $this->mServer, $this->mDBname, $this->mTrxShortId );
+                               $this->server, $this->dbName, $this->trxShortId );
                }
 
                if ( $this->getFlag( self::DBO_DEBUG ) ) {
-                       $this->queryLogger->debug( "{$this->mDBname} {$commentedSql}" );
+                       $this->queryLogger->debug( "{$this->dbName} {$commentedSql}" );
                }
 
                # Avoid fatals if close() was called
@@ -1024,7 +1022,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
 
                # Include query transaction state
-               $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : "";
+               $queryProf .= $this->trxShortId ? " [TRX#{$this->trxShortId}]" : "";
 
                $startTime = microtime( true );
                if ( $this->profiler ) {
@@ -1042,14 +1040,14 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
                if ( $ret !== false ) {
                        $this->lastPing = $startTime;
-                       if ( $isWrite && $this->mTrxLevel ) {
+                       if ( $isWrite && $this->trxLevel ) {
                                $this->updateTrxWriteQueryTime( $sql, $queryRuntime, $this->affectedRows() );
-                               $this->mTrxWriteCallers[] = $fname;
+                               $this->trxWriteCallers[] = $fname;
                        }
                }
 
                if ( $sql === self::PING_QUERY ) {
-                       $this->mRTTEstimate = $queryRuntime;
+                       $this->rttEstimate = $queryRuntime;
                }
 
                $this->trxProfiler->recordQueryCompletion(
@@ -1089,12 +1087,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        }
                }
 
-               $this->mTrxWriteDuration += $runtime;
-               $this->mTrxWriteQueryCount += 1;
-               $this->mTrxWriteAffectedRows += $affected;
+               $this->trxWriteDuration += $runtime;
+               $this->trxWriteQueryCount += 1;
+               $this->trxWriteAffectedRows += $affected;
                if ( $indicativeOfReplicaRuntime ) {
-                       $this->mTrxWriteAdjDuration += $runtime;
-                       $this->mTrxWriteAdjQueryCount += 1;
+                       $this->trxWriteAdjDuration += $runtime;
+                       $this->trxWriteAdjQueryCount += 1;
                }
        }
 
@@ -1113,7 +1111,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                # Dropped connections also mean that named locks are automatically released.
                # Only allow error suppression in autocommit mode or when the lost transaction
                # didn't matter anyway (aside from DBO_TRX snapshot loss).
-               if ( $this->mNamedLocksHeld ) {
+               if ( $this->namedLocksHeld ) {
                        return false; // possible critical section violation
                } elseif ( $sql === 'COMMIT' ) {
                        return !$priorWritesPending; // nothing written anyway? (T127428)
@@ -1134,13 +1132,13 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @return null|Exception
         */
        private function handleSessionLoss() {
-               $this->mTrxLevel = 0;
-               $this->mTrxIdleCallbacks = []; // T67263
-               $this->mTrxPreCommitCallbacks = []; // T67263
-               $this->mSessionTempTables = [];
-               $this->mNamedLocksHeld = [];
+               $this->trxLevel = 0;
+               $this->trxIdleCallbacks = []; // T67263
+               $this->trxPreCommitCallbacks = []; // T67263
+               $this->sessionTempTables = [];
+               $this->namedLocksHeld = [];
                try {
-                       // Handle callbacks in mTrxEndCallbacks
+                       // Handle callbacks in trxEndCallbacks
                        $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
                        $this->runTransactionListenerCallbacks( self::TRIGGER_ROLLBACK );
                        return null;
@@ -1405,13 +1403,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 = '';
                }
@@ -1545,7 +1539,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
        public function tableExists( $table, $fname = __METHOD__ ) {
                $tableRaw = $this->tableName( $table, 'raw' );
-               if ( isset( $this->mSessionTempTables[$tableRaw] ) ) {
+               if ( isset( $this->sessionTempTables[$tableRaw] ) ) {
                        return true; // already known to exist
                }
 
@@ -1811,17 +1805,17 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                # Stub. Shouldn't cause serious problems if it's not overridden, but
                # if your database engine supports a concept similar to MySQL's
                # databases you may as well.
-               $this->mDBname = $db;
+               $this->dbName = $db;
 
                return true;
        }
 
        public function getDBname() {
-               return $this->mDBname;
+               return $this->dbName;
        }
 
        public function getServer() {
-               return $this->mServer;
+               return $this->server;
        }
 
        public function tableName( $name, $format = 'quoted' ) {
@@ -1890,14 +1884,14 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                $database = $this->tableAliases[$table]['dbname'];
                                $schema = is_string( $this->tableAliases[$table]['schema'] )
                                        ? $this->tableAliases[$table]['schema']
-                                       : $this->mSchema;
+                                       : $this->schema;
                                $prefix = is_string( $this->tableAliases[$table]['prefix'] )
                                        ? $this->tableAliases[$table]['prefix']
-                                       : $this->mTablePrefix;
+                                       : $this->tablePrefix;
                        } else {
                                $database = '';
-                               $schema = $this->mSchema; # Default schema
-                               $prefix = $this->mTablePrefix; # Default prefix
+                               $schema = $this->schema; # Default schema
+                               $prefix = $this->tablePrefix; # Default prefix
                        }
                }
 
@@ -2349,7 +2343,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
 
                $affectedRowCount = 0;
-               $useTrx = !$this->mTrxLevel;
+               $useTrx = !$this->trxLevel;
                if ( $useTrx ) {
                        $this->begin( $fname, self::TRANSACTION_INTERNAL );
                }
@@ -2756,25 +2750,25 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        final public function onTransactionResolution( callable $callback, $fname = __METHOD__ ) {
-               if ( !$this->mTrxLevel ) {
+               if ( !$this->trxLevel ) {
                        throw new DBUnexpectedError( $this, "No transaction is active." );
                }
-               $this->mTrxEndCallbacks[] = [ $callback, $fname ];
+               $this->trxEndCallbacks[] = [ $callback, $fname ];
        }
 
        final public function onTransactionIdle( callable $callback, $fname = __METHOD__ ) {
-               $this->mTrxIdleCallbacks[] = [ $callback, $fname ];
-               if ( !$this->mTrxLevel ) {
+               $this->trxIdleCallbacks[] = [ $callback, $fname ];
+               if ( !$this->trxLevel ) {
                        $this->runOnTransactionIdleCallbacks( self::TRIGGER_IDLE );
                }
        }
 
        final public function onTransactionPreCommitOrIdle( callable $callback, $fname = __METHOD__ ) {
-               if ( $this->mTrxLevel || $this->getFlag( self::DBO_TRX ) ) {
+               if ( $this->trxLevel || $this->getFlag( self::DBO_TRX ) ) {
                        // As long as DBO_TRX is set, writes will accumulate until the load balancer issues
                        // an implicit commit of all peer databases. This is true even if a transaction has
                        // not yet been triggered by writes; make sure $callback runs *after* any such writes.
-                       $this->mTrxPreCommitCallbacks[] = [ $callback, $fname ];
+                       $this->trxPreCommitCallbacks[] = [ $callback, $fname ];
                } else {
                        // No transaction is active nor will start implicitly, so make one for this callback
                        $this->startAtomic( __METHOD__ );
@@ -2790,9 +2784,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
        final public function setTransactionListener( $name, callable $callback = null ) {
                if ( $callback ) {
-                       $this->mTrxRecurringCallbacks[$name] = $callback;
+                       $this->trxRecurringCallbacks[$name] = $callback;
                } else {
-                       unset( $this->mTrxRecurringCallbacks[$name] );
+                       unset( $this->trxRecurringCallbacks[$name] );
                }
        }
 
@@ -2805,7 +2799,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @since 1.28
         */
        final public function setTrxEndCallbackSuppression( $suppress ) {
-               $this->mTrxEndCallbacksSuppressed = $suppress;
+               $this->trxEndCallbacksSuppressed = $suppress;
        }
 
        /**
@@ -2818,7 +2812,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @throws Exception
         */
        public function runOnTransactionIdleCallbacks( $trigger ) {
-               if ( $this->mTrxEndCallbacksSuppressed ) {
+               if ( $this->trxEndCallbacksSuppressed ) {
                        return;
                }
 
@@ -2827,11 +2821,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $e = null; // first exception
                do { // callbacks may add callbacks :)
                        $callbacks = array_merge(
-                               $this->mTrxIdleCallbacks,
-                               $this->mTrxEndCallbacks // include "transaction resolution" callbacks
+                               $this->trxIdleCallbacks,
+                               $this->trxEndCallbacks // include "transaction resolution" callbacks
                        );
-                       $this->mTrxIdleCallbacks = []; // consumed (and recursion guard)
-                       $this->mTrxEndCallbacks = []; // consumed (recursion guard)
+                       $this->trxIdleCallbacks = []; // consumed (and recursion guard)
+                       $this->trxEndCallbacks = []; // consumed (recursion guard)
                        foreach ( $callbacks as $callback ) {
                                try {
                                        list( $phpCallback ) = $callback;
@@ -2852,7 +2846,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                        }
                                }
                        }
-               } while ( count( $this->mTrxIdleCallbacks ) );
+               } while ( count( $this->trxIdleCallbacks ) );
 
                if ( $e instanceof Exception ) {
                        throw $e; // re-throw any first exception
@@ -2870,8 +2864,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        public function runOnTransactionPreCommitCallbacks() {
                $e = null; // first exception
                do { // callbacks may add callbacks :)
-                       $callbacks = $this->mTrxPreCommitCallbacks;
-                       $this->mTrxPreCommitCallbacks = []; // consumed (and recursion guard)
+                       $callbacks = $this->trxPreCommitCallbacks;
+                       $this->trxPreCommitCallbacks = []; // consumed (and recursion guard)
                        foreach ( $callbacks as $callback ) {
                                try {
                                        list( $phpCallback ) = $callback;
@@ -2881,7 +2875,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                        $e = $e ?: $ex;
                                }
                        }
-               } while ( count( $this->mTrxPreCommitCallbacks ) );
+               } while ( count( $this->trxPreCommitCallbacks ) );
 
                if ( $e instanceof Exception ) {
                        throw $e; // re-throw any first exception
@@ -2898,14 +2892,14 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @since 1.20
         */
        public function runTransactionListenerCallbacks( $trigger ) {
-               if ( $this->mTrxEndCallbacksSuppressed ) {
+               if ( $this->trxEndCallbacksSuppressed ) {
                        return;
                }
 
                /** @var Exception $e */
                $e = null; // first exception
 
-               foreach ( $this->mTrxRecurringCallbacks as $phpCallback ) {
+               foreach ( $this->trxRecurringCallbacks as $phpCallback ) {
                        try {
                                $phpCallback( $trigger, $this );
                        } catch ( Exception $ex ) {
@@ -2920,29 +2914,29 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        final public function startAtomic( $fname = __METHOD__ ) {
-               if ( !$this->mTrxLevel ) {
+               if ( !$this->trxLevel ) {
                        $this->begin( $fname, self::TRANSACTION_INTERNAL );
                        // If DBO_TRX is set, a series of startAtomic/endAtomic pairs will result
                        // in all changes being in one transaction to keep requests transactional.
                        if ( !$this->getFlag( self::DBO_TRX ) ) {
-                               $this->mTrxAutomaticAtomic = true;
+                               $this->trxAutomaticAtomic = true;
                        }
                }
 
-               $this->mTrxAtomicLevels[] = $fname;
+               $this->trxAtomicLevels[] = $fname;
        }
 
        final public function endAtomic( $fname = __METHOD__ ) {
-               if ( !$this->mTrxLevel ) {
+               if ( !$this->trxLevel ) {
                        throw new DBUnexpectedError( $this, "No atomic transaction is open (got $fname)." );
                }
-               if ( !$this->mTrxAtomicLevels ||
-                       array_pop( $this->mTrxAtomicLevels ) !== $fname
+               if ( !$this->trxAtomicLevels ||
+                       array_pop( $this->trxAtomicLevels ) !== $fname
                ) {
                        throw new DBUnexpectedError( $this, "Invalid atomic section ended (got $fname)." );
                }
 
-               if ( !$this->mTrxAtomicLevels && $this->mTrxAutomaticAtomic ) {
+               if ( !$this->trxAtomicLevels && $this->trxAutomaticAtomic ) {
                        $this->commit( $fname, self::FLUSHING_INTERNAL );
                }
        }
@@ -2962,17 +2956,17 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
        final public function begin( $fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT ) {
                // Protect against mismatched atomic section, transaction nesting, and snapshot loss
-               if ( $this->mTrxLevel ) {
-                       if ( $this->mTrxAtomicLevels ) {
-                               $levels = implode( ', ', $this->mTrxAtomicLevels );
+               if ( $this->trxLevel ) {
+                       if ( $this->trxAtomicLevels ) {
+                               $levels = implode( ', ', $this->trxAtomicLevels );
                                $msg = "$fname: Got explicit BEGIN while atomic section(s) $levels are open.";
                                throw new DBUnexpectedError( $this, $msg );
-                       } elseif ( !$this->mTrxAutomatic ) {
-                               $msg = "$fname: Explicit transaction already active (from {$this->mTrxFname}).";
+                       } elseif ( !$this->trxAutomatic ) {
+                               $msg = "$fname: Explicit transaction already active (from {$this->trxFname}).";
                                throw new DBUnexpectedError( $this, $msg );
                        } else {
                                // @TODO: make this an exception at some point
-                               $msg = "$fname: Implicit transaction already active (from {$this->mTrxFname}).";
+                               $msg = "$fname: Implicit transaction already active (from {$this->trxFname}).";
                                $this->queryLogger->error( $msg );
                                return; // join the main transaction set
                        }
@@ -2987,27 +2981,27 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $this->assertOpen();
 
                $this->doBegin( $fname );
-               $this->mTrxTimestamp = microtime( true );
-               $this->mTrxFname = $fname;
-               $this->mTrxDoneWrites = false;
-               $this->mTrxAutomaticAtomic = false;
-               $this->mTrxAtomicLevels = [];
-               $this->mTrxShortId = sprintf( '%06x', mt_rand( 0, 0xffffff ) );
-               $this->mTrxWriteDuration = 0.0;
-               $this->mTrxWriteQueryCount = 0;
-               $this->mTrxWriteAffectedRows = 0;
-               $this->mTrxWriteAdjDuration = 0.0;
-               $this->mTrxWriteAdjQueryCount = 0;
-               $this->mTrxWriteCallers = [];
+               $this->trxTimestamp = microtime( true );
+               $this->trxFname = $fname;
+               $this->trxDoneWrites = false;
+               $this->trxAutomaticAtomic = false;
+               $this->trxAtomicLevels = [];
+               $this->trxShortId = sprintf( '%06x', mt_rand( 0, 0xffffff ) );
+               $this->trxWriteDuration = 0.0;
+               $this->trxWriteQueryCount = 0;
+               $this->trxWriteAffectedRows = 0;
+               $this->trxWriteAdjDuration = 0.0;
+               $this->trxWriteAdjQueryCount = 0;
+               $this->trxWriteCallers = [];
                // First SELECT after BEGIN will establish the snapshot in REPEATABLE-READ.
                // Get an estimate of the replica DB lag before then, treating estimate staleness
                // as lag itself just to be safe
                $status = $this->getApproximateLagStatus();
-               $this->mTrxReplicaLag = $status['lag'] + ( microtime( true ) - $status['since'] );
+               $this->trxReplicaLag = $status['lag'] + ( microtime( true ) - $status['since'] );
                // T147697: make explicitTrxActive() return true until begin() finishes. This way, no
                // caller will think its OK to muck around with the transaction just because startAtomic()
-               // has not yet completed (e.g. setting mTrxAtomicLevels).
-               $this->mTrxAutomatic = ( $mode === self::TRANSACTION_INTERNAL );
+               // has not yet completed (e.g. setting trxAtomicLevels).
+               $this->trxAutomatic = ( $mode === self::TRANSACTION_INTERNAL );
        }
 
        /**
@@ -3018,13 +3012,13 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         */
        protected function doBegin( $fname ) {
                $this->query( 'BEGIN', $fname );
-               $this->mTrxLevel = 1;
+               $this->trxLevel = 1;
        }
 
        final public function commit( $fname = __METHOD__, $flush = '' ) {
-               if ( $this->mTrxLevel && $this->mTrxAtomicLevels ) {
+               if ( $this->trxLevel && $this->trxAtomicLevels ) {
                        // There are still atomic sections open. This cannot be ignored
-                       $levels = implode( ', ', $this->mTrxAtomicLevels );
+                       $levels = implode( ', ', $this->trxAtomicLevels );
                        throw new DBUnexpectedError(
                                $this,
                                "$fname: Got COMMIT while atomic sections $levels are still open."
@@ -3032,20 +3026,20 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
 
                if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) {
-                       if ( !$this->mTrxLevel ) {
+                       if ( !$this->trxLevel ) {
                                return; // nothing to do
-                       } elseif ( !$this->mTrxAutomatic ) {
+                       } elseif ( !$this->trxAutomatic ) {
                                throw new DBUnexpectedError(
                                        $this,
                                        "$fname: Flushing an explicit transaction, getting out of sync."
                                );
                        }
                } else {
-                       if ( !$this->mTrxLevel ) {
+                       if ( !$this->trxLevel ) {
                                $this->queryLogger->error(
                                        "$fname: No transaction to commit, something got out of sync." );
                                return; // nothing to do
-                       } elseif ( $this->mTrxAutomatic ) {
+                       } elseif ( $this->trxAutomatic ) {
                                // @TODO: make this an exception at some point
                                $msg = "$fname: Explicit commit of implicit transaction.";
                                $this->queryLogger->error( $msg );
@@ -3059,14 +3053,14 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $this->runOnTransactionPreCommitCallbacks();
                $writeTime = $this->pendingWriteQueryDuration( self::ESTIMATE_DB_APPLY );
                $this->doCommit( $fname );
-               if ( $this->mTrxDoneWrites ) {
-                       $this->mLastWriteTime = microtime( true );
+               if ( $this->trxDoneWrites ) {
+                       $this->lastWriteTime = microtime( true );
                        $this->trxProfiler->transactionWritingOut(
-                               $this->mServer,
-                               $this->mDBname,
-                               $this->mTrxShortId,
+                               $this->server,
+                               $this->dbName,
+                               $this->trxShortId,
                                $writeTime,
-                               $this->mTrxWriteAffectedRows
+                               $this->trxWriteAffectedRows
                        );
                }
 
@@ -3081,19 +3075,19 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @param string $fname
         */
        protected function doCommit( $fname ) {
-               if ( $this->mTrxLevel ) {
+               if ( $this->trxLevel ) {
                        $this->query( 'COMMIT', $fname );
-                       $this->mTrxLevel = 0;
+                       $this->trxLevel = 0;
                }
        }
 
        final public function rollback( $fname = __METHOD__, $flush = '' ) {
                if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) {
-                       if ( !$this->mTrxLevel ) {
+                       if ( !$this->trxLevel ) {
                                return; // nothing to do
                        }
                } else {
-                       if ( !$this->mTrxLevel ) {
+                       if ( !$this->trxLevel ) {
                                $this->queryLogger->error(
                                        "$fname: No transaction to rollback, something got out of sync." );
                                return; // nothing to do
@@ -3109,17 +3103,17 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $this->assertOpen();
 
                $this->doRollback( $fname );
-               $this->mTrxAtomicLevels = [];
-               if ( $this->mTrxDoneWrites ) {
+               $this->trxAtomicLevels = [];
+               if ( $this->trxDoneWrites ) {
                        $this->trxProfiler->transactionWritingOut(
-                               $this->mServer,
-                               $this->mDBname,
-                               $this->mTrxShortId
+                               $this->server,
+                               $this->dbName,
+                               $this->trxShortId
                        );
                }
 
-               $this->mTrxIdleCallbacks = []; // clear
-               $this->mTrxPreCommitCallbacks = []; // clear
+               $this->trxIdleCallbacks = []; // clear
+               $this->trxPreCommitCallbacks = []; // clear
                try {
                        $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
                } catch ( Exception $e ) {
@@ -3139,11 +3133,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @param string $fname
         */
        protected function doRollback( $fname ) {
-               if ( $this->mTrxLevel ) {
+               if ( $this->trxLevel ) {
                        # Disconnects cause rollback anyway, so ignore those errors
                        $ignoreErrors = true;
                        $this->query( 'ROLLBACK', $fname, $ignoreErrors );
-                       $this->mTrxLevel = 0;
+                       $this->trxLevel = 0;
                }
        }
 
@@ -3161,7 +3155,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        public function explicitTrxActive() {
-               return $this->mTrxLevel && ( $this->mTrxAtomicLevels || !$this->mTrxAutomatic );
+               return $this->trxLevel && ( $this->trxAtomicLevels || !$this->trxAutomatic );
        }
 
        public function duplicateTableStructure(
@@ -3232,8 +3226,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        public function ping( &$rtt = null ) {
                // Avoid hitting the server if it was hit recently
                if ( $this->isOpen() && ( microtime( true ) - $this->lastPing ) < self::PING_TTL ) {
-                       if ( !func_num_args() || $this->mRTTEstimate > 0 ) {
-                               $rtt = $this->mRTTEstimate;
+                       if ( !func_num_args() || $this->rttEstimate > 0 ) {
+                               $rtt = $this->rttEstimate;
                                return true; // don't care about $rtt
                        }
                }
@@ -3244,7 +3238,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $this->restoreFlags( self::RESTORE_PRIOR );
 
                if ( $ok ) {
-                       $rtt = $this->mRTTEstimate;
+                       $rtt = $this->rttEstimate;
                }
 
                return $ok;
@@ -3257,10 +3251,10 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         */
        protected function reconnect() {
                $this->closeConnection();
-               $this->mOpened = false;
-               $this->mConn = false;
+               $this->opened = false;
+               $this->conn = false;
                try {
-                       $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
+                       $this->open( $this->server, $this->user, $this->password, $this->dbName );
                        $this->lastPing = microtime( true );
                        $ok = true;
                } catch ( DBConnectionError $e ) {
@@ -3286,8 +3280,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @since 1.27
         */
        protected function getTransactionLagStatus() {
-               return $this->mTrxLevel
-                       ? [ 'lag' => $this->mTrxReplicaLag, 'since' => $this->trxTimestamp() ]
+               return $this->trxLevel
+                       ? [ 'lag' => $this->trxReplicaLag, 'since' => $this->trxTimestamp() ]
                        : null;
        }
 
@@ -3395,7 +3389,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        public function setSchemaVars( $vars ) {
-               $this->mSchemaVars = $vars;
+               $this->schemaVars = $vars;
        }
 
        public function sourceStream(
@@ -3547,8 +3541,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @return array
         */
        protected function getSchemaVars() {
-               if ( $this->mSchemaVars ) {
-                       return $this->mSchemaVars;
+               if ( $this->schemaVars ) {
+                       return $this->schemaVars;
                } else {
                        return $this->getDefaultSchemaVars();
                }
@@ -3567,17 +3561,20 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        public function lockIsFree( $lockName, $method ) {
-               return true;
+               // RDBMs methods for checking named locks may or may not count this thread itself.
+               // In MySQL, IS_FREE_LOCK() returns 0 if the thread already has the lock. This is
+               // the behavior choosen by the interface for this method.
+               return !isset( $this->namedLocksHeld[$lockName] );
        }
 
        public function lock( $lockName, $method, $timeout = 5 ) {
-               $this->mNamedLocksHeld[$lockName] = 1;
+               $this->namedLocksHeld[$lockName] = 1;
 
                return true;
        }
 
        public function unlock( $lockName, $method ) {
-               unset( $this->mNamedLocksHeld[$lockName] );
+               unset( $this->namedLocksHeld[$lockName] );
 
                return true;
        }
@@ -3733,9 +3730,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        /**
-        * Get the underlying binding handle, mConn
+        * Get the underlying binding connection handle
         *
-        * Makes sure that mConn is set (disconnects and ping() failure can unset it).
+        * Makes sure the connection resource is set (disconnects and ping() failure can unset it).
         * This catches broken callers than catch and ignore disconnection exceptions.
         * Unlike checking isOpen(), this is safe to call inside of open().
         *
@@ -3744,14 +3741,14 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @since 1.26
         */
        protected function getBindingHandle() {
-               if ( !$this->mConn ) {
+               if ( !$this->conn ) {
                        throw new DBUnexpectedError(
                                $this,
                                'DB connection was already closed or the connection dropped.'
                        );
                }
 
-               return $this->mConn;
+               return $this->conn;
        }
 
        /**
@@ -3759,7 +3756,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @return string
         */
        public function __toString() {
-               return (string)$this->mConn;
+               return (string)$this->conn;
        }
 
        /**
@@ -3774,11 +3771,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
                if ( $this->isOpen() ) {
                        // Open a new connection resource without messing with the old one
-                       $this->mOpened = false;
-                       $this->mConn = false;
-                       $this->mTrxEndCallbacks = []; // don't copy
+                       $this->opened = false;
+                       $this->conn = false;
+                       $this->trxEndCallbacks = []; // don't copy
                        $this->handleSessionLoss(); // no trx or locks anymore
-                       $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
+                       $this->open( $this->server, $this->user, $this->password, $this->dbName );
                        $this->lastPing = microtime( true );
                }
        }
@@ -3797,8 +3794,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * Run a few simple sanity checks and close dangling connections
         */
        public function __destruct() {
-               if ( $this->mTrxLevel && $this->mTrxDoneWrites ) {
-                       trigger_error( "Uncommitted DB writes (transaction from {$this->mTrxFname})." );
+               if ( $this->trxLevel && $this->trxDoneWrites ) {
+                       trigger_error( "Uncommitted DB writes (transaction from {$this->trxFname})." );
                }
 
                $danglingWriters = $this->pendingWriteAndCallbackCallers();
@@ -3807,14 +3804,14 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        trigger_error( "DB transaction writes or callbacks still pending ($fnames)." );
                }
 
-               if ( $this->mConn ) {
+               if ( $this->conn ) {
                        // Avoid connection leaks for sanity. Normally, resources close at script completion.
                        // The connection might already be closed in zend/hhvm by now, so suppress warnings.
                        Wikimedia\suppressWarnings();
                        $this->closeConnection();
                        Wikimedia\restoreWarnings();
-                       $this->mConn = false;
-                       $this->mOpened = false;
+                       $this->conn = false;
+                       $this->opened = false;
                }
        }
 }
index 9a0e8c9..c7e1289 100644 (file)
@@ -93,10 +93,10 @@ class DatabaseMssql extends Database {
                }
 
                $this->close();
-               $this->mServer = $server;
-               $this->mUser = $user;
-               $this->mPassword = $password;
-               $this->mDBname = $dbName;
+               $this->server = $server;
+               $this->user = $user;
+               $this->password = $password;
+               $this->dbName = $dbName;
 
                $connectionInfo = [];
 
@@ -112,16 +112,16 @@ class DatabaseMssql extends Database {
                }
 
                Wikimedia\suppressWarnings();
-               $this->mConn = sqlsrv_connect( $server, $connectionInfo );
+               $this->conn = sqlsrv_connect( $server, $connectionInfo );
                Wikimedia\restoreWarnings();
 
-               if ( $this->mConn === false ) {
+               if ( $this->conn === false ) {
                        throw new DBConnectionError( $this, $this->lastError() );
                }
 
-               $this->mOpened = true;
+               $this->opened = true;
 
-               return $this->mConn;
+               return $this->conn;
        }
 
        /**
@@ -130,7 +130,7 @@ class DatabaseMssql extends Database {
         * @return bool
         */
        protected function closeConnection() {
-               return sqlsrv_close( $this->mConn );
+               return sqlsrv_close( $this->conn );
        }
 
        /**
@@ -186,10 +186,10 @@ class DatabaseMssql extends Database {
 
                if ( $this->mPrepareStatements ) {
                        // we do prepare + execute so we can get its field metadata for later usage if desired
-                       $stmt = sqlsrv_prepare( $this->mConn, $sql, [], $scrollArr );
+                       $stmt = sqlsrv_prepare( $this->conn, $sql, [], $scrollArr );
                        $success = sqlsrv_execute( $stmt );
                } else {
-                       $stmt = sqlsrv_query( $this->mConn, $sql, [], $scrollArr );
+                       $stmt = sqlsrv_query( $this->conn, $sql, [], $scrollArr );
                        $success = (bool)$stmt;
                }
 
@@ -953,7 +953,7 @@ class DatabaseMssql extends Database {
         * @return string Version information from the database
         */
        public function getServerVersion() {
-               $server_info = sqlsrv_server_info( $this->mConn );
+               $server_info = sqlsrv_server_info( $this->conn );
                $version = 'Error';
                if ( isset( $server_info['SQLServerVersion'] ) ) {
                        $version = $server_info['SQLServerVersion'];
@@ -977,7 +977,7 @@ class DatabaseMssql extends Database {
                }
 
                if ( $schema === false ) {
-                       $schema = $this->mSchema;
+                       $schema = $this->schema;
                }
 
                $res = $this->query( "SELECT 1 FROM INFORMATION_SCHEMA.TABLES
@@ -1042,8 +1042,8 @@ class DatabaseMssql extends Database {
         * @param string $fname
         */
        protected function doBegin( $fname = __METHOD__ ) {
-               sqlsrv_begin_transaction( $this->mConn );
-               $this->mTrxLevel = 1;
+               sqlsrv_begin_transaction( $this->conn );
+               $this->trxLevel = 1;
        }
 
        /**
@@ -1051,8 +1051,8 @@ class DatabaseMssql extends Database {
         * @param string $fname
         */
        protected function doCommit( $fname = __METHOD__ ) {
-               sqlsrv_commit( $this->mConn );
-               $this->mTrxLevel = 0;
+               sqlsrv_commit( $this->conn );
+               $this->trxLevel = 0;
        }
 
        /**
@@ -1061,8 +1061,8 @@ class DatabaseMssql extends Database {
         * @param string $fname
         */
        protected function doRollback( $fname = __METHOD__ ) {
-               sqlsrv_rollback( $this->mConn );
-               $this->mTrxLevel = 0;
+               sqlsrv_rollback( $this->conn );
+               $this->trxLevel = 0;
        }
 
        /**
@@ -1131,7 +1131,7 @@ class DatabaseMssql extends Database {
         */
        public function selectDB( $db ) {
                try {
-                       $this->mDBname = $db;
+                       $this->dbName = $db;
                        $this->query( "USE $db" );
                        return true;
                } catch ( Exception $e ) {
@@ -1255,8 +1255,8 @@ class DatabaseMssql extends Database {
        private function populateColumnCaches() {
                $res = $this->select( 'INFORMATION_SCHEMA.COLUMNS', '*',
                        [
-                               'TABLE_CATALOG' => $this->mDBname,
-                               'TABLE_SCHEMA' => $this->mSchema,
+                               'TABLE_CATALOG' => $this->dbName,
+                               'TABLE_SCHEMA' => $this->schema,
                                'DATA_TYPE' => [ 'varbinary', 'binary', 'image', 'bit' ]
                        ] );
 
index bd2d274..3085276 100644 (file)
@@ -60,6 +60,8 @@ abstract class DatabaseMysqlBase extends Database {
        protected $sqlMode;
        /** @var bool Use experimental UTF-8 transmission encoding */
        protected $utf8Mode;
+       /** @var bool|null */
+       protected $defaultBigSelects = null;
 
        /** @var string|null */
        private $serverVersion = null;
@@ -126,14 +128,14 @@ abstract class DatabaseMysqlBase extends Database {
                # Close/unset connection handle
                $this->close();
 
-               $this->mServer = $server;
-               $this->mUser = $user;
-               $this->mPassword = $password;
-               $this->mDBname = $dbName;
+               $this->server = $server;
+               $this->user = $user;
+               $this->password = $password;
+               $this->dbName = $dbName;
 
                $this->installErrorHandler();
                try {
-                       $this->mConn = $this->mysqlConnect( $this->mServer );
+                       $this->conn = $this->mysqlConnect( $this->server );
                } catch ( Exception $ex ) {
                        $this->restoreErrorHandler();
                        throw $ex;
@@ -141,7 +143,7 @@ abstract class DatabaseMysqlBase extends Database {
                $error = $this->restoreErrorHandler();
 
                # Always log connection errors
-               if ( !$this->mConn ) {
+               if ( !$this->conn ) {
                        if ( !$error ) {
                                $error = $this->lastError();
                        }
@@ -171,7 +173,7 @@ abstract class DatabaseMysqlBase extends Database {
                                        ] )
                                );
                                $this->queryLogger->debug(
-                                       "Error selecting database $dbName on server {$this->mServer}" );
+                                       "Error selecting database $dbName on server {$this->server}" );
 
                                $this->reportConnectionError( "Error selecting database $dbName" );
                        }
@@ -190,7 +192,7 @@ abstract class DatabaseMysqlBase extends Database {
                }
                // Set any custom settings defined by site config
                // (e.g. https://dev.mysql.com/doc/refman/4.1/en/innodb-parameters.html)
-               foreach ( $this->mSessionVars as $var => $val ) {
+               foreach ( $this->sessionVars as $var => $val ) {
                        // Escape strings but not numbers to avoid MySQL complaining
                        if ( !is_int( $val ) && !is_float( $val ) ) {
                                $val = $this->addQuotes( $val );
@@ -213,7 +215,7 @@ abstract class DatabaseMysqlBase extends Database {
                        }
                }
 
-               $this->mOpened = true;
+               $this->opened = true;
 
                return true;
        }
@@ -465,10 +467,10 @@ abstract class DatabaseMysqlBase extends Database {
         * @return string
         */
        public function lastError() {
-               if ( $this->mConn ) {
+               if ( $this->conn ) {
                        # Even if it's non-zero, it can still be invalid
                        Wikimedia\suppressWarnings();
-                       $error = $this->mysqlError( $this->mConn );
+                       $error = $this->mysqlError( $this->conn );
                        if ( !$error ) {
                                $error = $this->mysqlError();
                        }
@@ -477,7 +479,7 @@ abstract class DatabaseMysqlBase extends Database {
                        $error = $this->mysqlError();
                }
                if ( $error ) {
-                       $error .= ' (' . $this->mServer . ')';
+                       $error .= ' (' . $this->server . ')';
                }
 
                return $error;
@@ -594,7 +596,7 @@ abstract class DatabaseMysqlBase extends Database {
                list( $database, , $prefix, $table ) = $this->qualifiedTableComponents( $table );
                $tableName = "{$prefix}{$table}";
 
-               if ( isset( $this->mSessionTempTables[$tableName] ) ) {
+               if ( isset( $this->sessionTempTables[$tableName] ) ) {
                        return true; // already known to exist and won't show in SHOW TABLES anyway
                }
 
@@ -889,17 +891,15 @@ abstract class DatabaseMysqlBase extends Database {
                        return 0; // already reached this point for sure
                }
 
-               $useGTID = ( $this->useGTIDs && $pos->gtids );
-
                // Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
-               if ( $useGTID ) {
+               if ( $pos->gtids ) {
                        // Wait on the GTID set (MariaDB only)
                        $gtidArg = $this->addQuotes( implode( ',', $pos->gtids ) );
                        $res = $this->doQuery( "SELECT MASTER_GTID_WAIT($gtidArg, $timeout)" );
                } else {
                        // Wait on the binlog coordinates
-                       $encFile = $this->addQuotes( $pos->file );
-                       $encPos = intval( $pos->pos );
+                       $encFile = $this->addQuotes( $pos->getLogFile() );
+                       $encPos = intval( $pos->pos[1] );
                        $res = $this->doQuery( "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)" );
                }
 
@@ -912,7 +912,7 @@ abstract class DatabaseMysqlBase extends Database {
                // Result can be NULL (error), -1 (timeout), or 0+ per the MySQL manual
                $status = ( $row[0] !== null ) ? intval( $row[0] ) : null;
                if ( $status === null ) {
-                       if ( !$useGTID ) {
+                       if ( !$pos->gtids ) {
                                // T126436: jobs programmed to wait on master positions might be referencing
                                // binlogs with an old master hostname; this makes MASTER_POS_WAIT() return null.
                                // Try to detect this case and treat the replica DB as having reached the given
@@ -938,24 +938,26 @@ abstract class DatabaseMysqlBase extends Database {
         * @return MySQLMasterPos|bool
         */
        public function getReplicaPos() {
-               $res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ );
-               $row = $this->fetchObject( $res );
+               $now = microtime( true );
 
-               if ( $row ) {
-                       $pos = $row->Exec_Master_Log_Pos;
-                       // Also fetch the last-applied GTID set (MariaDB)
-                       if ( $this->useGTIDs ) {
-                               $res = $this->query( "SHOW GLOBAL VARIABLES LIKE 'gtid_slave_pos'", __METHOD__ );
-                               $gtidRow = $this->fetchObject( $res );
-                               $gtidSet = $gtidRow ? $gtidRow->Value : '';
-                       } else {
-                               $gtidSet = '';
+               if ( $this->useGTIDs ) {
+                       $res = $this->query( "SELECT @@global.gtid_slave_pos AS Value", __METHOD__ );
+                       $gtidRow = $this->fetchObject( $res );
+                       if ( $gtidRow && strlen( $gtidRow->Value ) ) {
+                               return new MySQLMasterPos( $gtidRow->Value, $now );
                        }
+               }
 
-                       return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos, $gtidSet );
-               } else {
-                       return false;
+               $res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ );
+               $row = $this->fetchObject( $res );
+               if ( $row && strlen( $row->Relay_Master_Log_File ) ) {
+                       return new MySQLMasterPos(
+                               "{$row->Relay_Master_Log_File}/{$row->Exec_Master_Log_Pos}",
+                               $now
+                       );
                }
+
+               return false;
        }
 
        /**
@@ -964,23 +966,23 @@ abstract class DatabaseMysqlBase extends Database {
         * @return MySQLMasterPos|bool
         */
        public function getMasterPos() {
-               $res = $this->query( 'SHOW MASTER STATUS', __METHOD__ );
-               $row = $this->fetchObject( $res );
+               $now = microtime( true );
 
-               if ( $row ) {
-                       // Also fetch the last-written GTID set (MariaDB)
-                       if ( $this->useGTIDs ) {
-                               $res = $this->query( "SHOW GLOBAL VARIABLES LIKE 'gtid_binlog_pos'", __METHOD__ );
-                               $gtidRow = $this->fetchObject( $res );
-                               $gtidSet = $gtidRow ? $gtidRow->Value : '';
-                       } else {
-                               $gtidSet = '';
+               if ( $this->useGTIDs ) {
+                       $res = $this->query( "SELECT @@global.gtid_binlog_pos AS Value", __METHOD__ );
+                       $gtidRow = $this->fetchObject( $res );
+                       if ( $gtidRow && strlen( $gtidRow->Value ) ) {
+                               return new MySQLMasterPos( $gtidRow->Value, $now );
                        }
+               }
 
-                       return new MySQLMasterPos( $row->File, $row->Position, $gtidSet );
-               } else {
-                       return false;
+               $res = $this->query( 'SHOW MASTER STATUS', __METHOD__ );
+               $row = $this->fetchObject( $res );
+               if ( $row && strlen( $row->File ) ) {
+                       return new MySQLMasterPos( "{$row->File}/{$row->Position}", $now );
                }
+
+               return false;
        }
 
        public function serverIsReadOnly() {
@@ -1079,6 +1081,10 @@ abstract class DatabaseMysqlBase extends Database {
         * @since 1.20
         */
        public function lockIsFree( $lockName, $method ) {
+               if ( !parent::lockIsFree( $lockName, $method ) ) {
+                       return false; // already held
+               }
+
                $encName = $this->addQuotes( $this->makeLockName( $lockName ) );
                $result = $this->query( "SELECT IS_FREE_LOCK($encName) AS lockstatus", $method );
                $row = $this->fetchObject( $result );
@@ -1170,14 +1176,14 @@ abstract class DatabaseMysqlBase extends Database {
         */
        public function setBigSelects( $value = true ) {
                if ( $value === 'default' ) {
-                       if ( $this->mDefaultBigSelects === null ) {
+                       if ( $this->defaultBigSelects === null ) {
                                # Function hasn't been called before so it must already be set to the default
                                return;
                        } else {
-                               $value = $this->mDefaultBigSelects;
+                               $value = $this->defaultBigSelects;
                        }
-               } elseif ( $this->mDefaultBigSelects === null ) {
-                       $this->mDefaultBigSelects =
+               } elseif ( $this->defaultBigSelects === null ) {
+                       $this->defaultBigSelects =
                                (bool)$this->selectField( false, '@@sql_big_selects', '', __METHOD__ );
                }
                $encValue = $value ? '1' : '0';
@@ -1376,7 +1382,7 @@ abstract class DatabaseMysqlBase extends Database {
         */
        public function listViews( $prefix = null, $fname = __METHOD__ ) {
                // The name of the column containing the name of the VIEW
-               $propertyName = 'Tables_in_' . $this->mDBname;
+               $propertyName = 'Tables_in_' . $this->dbName;
 
                // Query for the VIEWS
                $res = $this->query( 'SHOW FULL TABLES WHERE TABLE_TYPE = "VIEW"' );
@@ -1445,6 +1451,11 @@ abstract class DatabaseMysqlBase extends Database {
                        return $index;
                }
        }
+
+       protected function isTransactableQuery( $sql ) {
+               return parent::isTransactableQuery( $sql ) &&
+                       !preg_match( '/^SELECT\s+(GET|RELEASE|IS_FREE)_LOCK\(/', $sql );
+       }
 }
 
 class_alias( DatabaseMysqlBase::class, 'DatabaseMysqlBase' );
index 09ea66c..9152d1e 100644 (file)
@@ -86,7 +86,7 @@ class DatabaseMysqli extends DatabaseMysqlBase {
                $mysqli = mysqli_init();
 
                $connFlags = 0;
-               if ( $this->mFlags & self::DBO_SSL ) {
+               if ( $this->flags & self::DBO_SSL ) {
                        $connFlags |= MYSQLI_CLIENT_SSL;
                        $mysqli->ssl_set(
                                $this->sslKeyPath,
@@ -96,10 +96,10 @@ class DatabaseMysqli extends DatabaseMysqlBase {
                                $this->sslCiphers
                        );
                }
-               if ( $this->mFlags & self::DBO_COMPRESS ) {
+               if ( $this->flags & self::DBO_COMPRESS ) {
                        $connFlags |= MYSQLI_CLIENT_COMPRESS;
                }
-               if ( $this->mFlags & self::DBO_PERSISTENT ) {
+               if ( $this->flags & self::DBO_PERSISTENT ) {
                        $realServer = 'p:' . $realServer;
                }
 
@@ -112,8 +112,8 @@ class DatabaseMysqli extends DatabaseMysqlBase {
                }
                $mysqli->options( MYSQLI_OPT_CONNECT_TIMEOUT, 3 );
 
-               if ( $mysqli->real_connect( $realServer, $this->mUser,
-                       $this->mPassword, $this->mDBname, $port, $socket, $connFlags )
+               if ( $mysqli->real_connect( $realServer, $this->user,
+                       $this->password, $this->dbName, $port, $socket, $connFlags )
                ) {
                        return $mysqli;
                }
@@ -162,8 +162,8 @@ class DatabaseMysqli extends DatabaseMysqlBase {
         * @return int
         */
        function lastErrno() {
-               if ( $this->mConn ) {
-                       return $this->mConn->errno;
+               if ( $this->conn ) {
+                       return $this->conn->errno;
                } else {
                        return mysqli_connect_errno();
                }
@@ -185,7 +185,7 @@ class DatabaseMysqli extends DatabaseMysqlBase {
        function selectDB( $db ) {
                $conn = $this->getBindingHandle();
 
-               $this->mDBname = $db;
+               $this->dbName = $db;
 
                return $conn->select_db( $db );
        }
@@ -326,11 +326,11 @@ class DatabaseMysqli extends DatabaseMysqlBase {
         * @return string
         */
        public function __toString() {
-               if ( $this->mConn instanceof mysqli ) {
-                       return (string)$this->mConn->thread_id;
+               if ( $this->conn instanceof mysqli ) {
+                       return (string)$this->conn->thread_id;
                } else {
                        // mConn might be false or something.
-                       return (string)$this->mConn;
+                       return (string)$this->conn;
                }
        }
 }
index 8fbb7de..c6d5a14 100644 (file)
@@ -97,10 +97,10 @@ class DatabasePostgres extends Database {
                        );
                }
 
-               $this->mServer = $server;
-               $this->mUser = $user;
-               $this->mPassword = $password;
-               $this->mDBname = $dbName;
+               $this->server = $server;
+               $this->user = $user;
+               $this->password = $password;
+               $this->dbName = $dbName;
 
                $connectVars = [
                        // pg_connect() user $user as the default database. Since a database is *required*,
@@ -116,7 +116,7 @@ class DatabasePostgres extends Database {
                if ( (int)$this->port > 0 ) {
                        $connectVars['port'] = (int)$this->port;
                }
-               if ( $this->mFlags & self::DBO_SSL ) {
+               if ( $this->flags & self::DBO_SSL ) {
                        $connectVars['sslmode'] = 1;
                }
 
@@ -126,7 +126,7 @@ class DatabasePostgres extends Database {
 
                try {
                        // Use new connections to let LoadBalancer/LBFactory handle reuse
-                       $this->mConn = pg_connect( $this->connectString, PGSQL_CONNECT_FORCE_NEW );
+                       $this->conn = pg_connect( $this->connectString, PGSQL_CONNECT_FORCE_NEW );
                } catch ( Exception $ex ) {
                        $this->restoreErrorHandler();
                        throw $ex;
@@ -134,7 +134,7 @@ class DatabasePostgres extends Database {
 
                $phpError = $this->restoreErrorHandler();
 
-               if ( !$this->mConn ) {
+               if ( !$this->conn ) {
                        $this->queryLogger->debug(
                                "DB connection error\n" .
                                "Server: $server, Database: $dbName, User: $user, Password: " .
@@ -144,7 +144,7 @@ class DatabasePostgres extends Database {
                        throw new DBConnectionError( $this, str_replace( "\n", ' ', $phpError ) );
                }
 
-               $this->mOpened = true;
+               $this->opened = true;
 
                # If called from the command-line (e.g. importDump), only show errors
                if ( $this->cliMode ) {
@@ -159,11 +159,11 @@ class DatabasePostgres extends Database {
                        $this->query( "SET bytea_output = 'escape'", __METHOD__ ); // PHP bug 53127
                }
 
-               $this->determineCoreSchema( $this->mSchema );
+               $this->determineCoreSchema( $this->schema );
                // The schema to be used is now in the search path; no need for explicit qualification
-               $this->mSchema = '';
+               $this->schema = '';
 
-               return $this->mConn;
+               return $this->conn;
        }
 
        public function databasesAreIndependent() {
@@ -178,8 +178,8 @@ class DatabasePostgres extends Database {
         * @throws DBUnexpectedError
         */
        public function selectDB( $db ) {
-               if ( $this->mDBname !== $db ) {
-                       return (bool)$this->open( $this->mServer, $this->mUser, $this->mPassword, $db );
+               if ( $this->dbName !== $db ) {
+                       return (bool)$this->open( $this->server, $this->user, $this->password, $db );
                } else {
                        return true;
                }
@@ -199,7 +199,12 @@ class DatabasePostgres extends Database {
        }
 
        protected function closeConnection() {
-               return $this->mConn ? pg_close( $this->mConn ) : true;
+               return $this->conn ? pg_close( $this->conn ) : true;
+       }
+
+       protected function isTransactableQuery( $sql ) {
+               return parent::isTransactableQuery( $sql ) &&
+                       !preg_match( '/^SELECT\s+pg_(try_|)advisory_\w+\(/', $sql );
        }
 
        public function doQuery( $sql ) {
@@ -253,7 +258,7 @@ class DatabasePostgres extends Database {
                        }
                }
                /* Transaction stays in the ERROR state until rolled back */
-               if ( $this->mTrxLevel ) {
+               if ( $this->trxLevel ) {
                        // Throw away the transaction state, then raise the error as normal.
                        // Note that if this connection is managed by LBFactory, it's already expected
                        // that the other transactions LBFactory manages will be rolled back.
@@ -365,7 +370,7 @@ class DatabasePostgres extends Database {
        }
 
        public function lastError() {
-               if ( $this->mConn ) {
+               if ( $this->conn ) {
                        if ( $this->mLastResult ) {
                                return pg_result_error( $this->mLastResult );
                        } else {
@@ -1255,11 +1260,11 @@ SQL;
        }
 
        public function getDBname() {
-               return $this->mDBname;
+               return $this->dbName;
        }
 
        public function getServer() {
-               return $this->mServer;
+               return $this->server;
        }
 
        public function buildConcat( $stringList ) {
@@ -1319,6 +1324,9 @@ SQL;
        }
 
        public function lockIsFree( $lockName, $method ) {
+               if ( !parent::lockIsFree( $lockName, $method ) ) {
+                       return false; // already held
+               }
                // http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
                $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
                $result = $this->query( "SELECT (CASE(pg_try_advisory_lock($key))
index 01772cf..458d308 100644 (file)
@@ -51,7 +51,7 @@ class DatabaseSqlite extends Database {
        protected $mLastResult;
 
        /** @var PDO */
-       protected $mConn;
+       protected $conn;
 
        /** @var FSLockManager (hopefully on the same server as the DB) */
        protected $lockMgr;
@@ -79,8 +79,8 @@ class DatabaseSqlite extends Database {
                        throw new InvalidArgumentException( "Need 'dbDirectory' or 'dbFilePath' parameter." );
                } else {
                        $this->dbDir = $p['dbDirectory'];
-                       $this->mDBname = $p['dbname'];
-                       $lockDomain = $this->mDBname;
+                       $this->dbName = $p['dbname'];
+                       $lockDomain = $this->dbName;
                        // Stock wiki mode using standard file names per DB.
                        parent::__construct( $p );
                        // Super doesn't open when $user is false, but we can work with $dbName
@@ -153,12 +153,12 @@ class DatabaseSqlite extends Database {
                $this->close();
                $fileName = self::generateFileName( $this->dbDir, $dbName );
                if ( !is_readable( $fileName ) ) {
-                       $this->mConn = false;
+                       $this->conn = false;
                        throw new DBConnectionError( $this, "SQLite database not accessible" );
                }
                $this->openFile( $fileName );
 
-               return (bool)$this->mConn;
+               return (bool)$this->conn;
        }
 
        /**
@@ -173,29 +173,29 @@ class DatabaseSqlite extends Database {
 
                $this->dbPath = $fileName;
                try {
-                       if ( $this->mFlags & self::DBO_PERSISTENT ) {
-                               $this->mConn = new PDO( "sqlite:$fileName", '', '',
+                       if ( $this->flags & self::DBO_PERSISTENT ) {
+                               $this->conn = new PDO( "sqlite:$fileName", '', '',
                                        [ PDO::ATTR_PERSISTENT => true ] );
                        } else {
-                               $this->mConn = new PDO( "sqlite:$fileName", '', '' );
+                               $this->conn = new PDO( "sqlite:$fileName", '', '' );
                        }
                } catch ( PDOException $e ) {
                        $err = $e->getMessage();
                }
 
-               if ( !$this->mConn ) {
+               if ( !$this->conn ) {
                        $this->queryLogger->debug( "DB connection error: $err\n" );
                        throw new DBConnectionError( $this, $err );
                }
 
-               $this->mOpened = !!$this->mConn;
-               if ( $this->mOpened ) {
+               $this->opened = !!$this->conn;
+               if ( $this->opened ) {
                        # Set error codes only, don't raise exceptions
-                       $this->mConn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
+                       $this->conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
                        # Enforce LIKE to be case sensitive, just like MySQL
                        $this->query( 'PRAGMA case_sensitive_like = 1' );
 
-                       return $this->mConn;
+                       return $this->conn;
                }
 
                return false;
@@ -218,7 +218,7 @@ class DatabaseSqlite extends Database {
         * @return bool
         */
        protected function closeConnection() {
-               $this->mConn = null;
+               $this->conn = null;
 
                return true;
        }
@@ -311,7 +311,7 @@ class DatabaseSqlite extends Database {
         * @return bool|ResultWrapper
         */
        protected function doQuery( $sql ) {
-               $res = $this->mConn->query( $sql );
+               $res = $this->conn->query( $sql );
                if ( $res === false ) {
                        return false;
                }
@@ -447,7 +447,7 @@ class DatabaseSqlite extends Database {
         */
        function insertId() {
                // PDO::lastInsertId yields a string :(
-               return intval( $this->mConn->lastInsertId() );
+               return intval( $this->conn->lastInsertId() );
        }
 
        /**
@@ -472,10 +472,10 @@ class DatabaseSqlite extends Database {
         * @return string
         */
        function lastError() {
-               if ( !is_object( $this->mConn ) ) {
+               if ( !is_object( $this->conn ) ) {
                        return "Cannot return last error, no db connection";
                }
-               $e = $this->mConn->errorInfo();
+               $e = $this->conn->errorInfo();
 
                return isset( $e[2] ) ? $e[2] : '';
        }
@@ -484,10 +484,10 @@ class DatabaseSqlite extends Database {
         * @return string
         */
        function lastErrno() {
-               if ( !is_object( $this->mConn ) ) {
+               if ( !is_object( $this->conn ) ) {
                        return "Cannot return last error, no db connection";
                } else {
-                       $info = $this->mConn->errorInfo();
+                       $info = $this->conn->errorInfo();
 
                        return $info[1];
                }
@@ -720,7 +720,7 @@ class DatabaseSqlite extends Database {
         * @return string Version information from the database
         */
        function getServerVersion() {
-               $ver = $this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
+               $ver = $this->conn->getAttribute( PDO::ATTR_SERVER_VERSION );
 
                return $ver;
        }
@@ -752,7 +752,7 @@ class DatabaseSqlite extends Database {
                } else {
                        $this->query( 'BEGIN', $fname );
                }
-               $this->mTrxLevel = 1;
+               $this->trxLevel = 1;
        }
 
        /**
@@ -810,7 +810,7 @@ class DatabaseSqlite extends Database {
                        );
                        return "x'" . bin2hex( (string)$s ) . "'";
                } else {
-                       return $this->mConn->quote( (string)$s );
+                       return $this->conn->quote( (string)$s );
                }
        }
 
@@ -1054,7 +1054,7 @@ class DatabaseSqlite extends Database {
         * @return string
         */
        public function __toString() {
-               return 'SQLite ' . (string)$this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
+               return 'SQLite ' . (string)$this->conn->getAttribute( PDO::ATTR_SERVER_VERSION );
        }
 }
 
index 0964dd5..d59bee3 100644 (file)
@@ -1776,7 +1776,7 @@ interface IDatabase {
        public function setSchemaVars( $vars );
 
        /**
-        * Check to see if a named lock is available (non-blocking)
+        * Check to see if a named lock is not locked by any thread (non-blocking)
         *
         * @param string $lockName Name of lock to poll
         * @param string $method Name of method calling us
index 9ca6b11..62809cd 100644 (file)
@@ -13,9 +13,9 @@ use InvalidArgumentException;
  *    that GTID sets are complete (e.g. include all domains on the server).
  */
 class MySQLMasterPos implements DBMasterPos {
-       /** @var string Binlog file */
-       public $file;
-       /** @var int Binglog file position */
+       /** @var string|null Binlog file base name */
+       public $binlog;
+       /** @var int[]|null Binglog file position tuple */
        public $pos;
        /** @var string[] GTID list */
        public $gtids = [];
@@ -23,29 +23,29 @@ class MySQLMasterPos implements DBMasterPos {
        public $asOfTime = 0.0;
 
        /**
-        * @param string $file Binlog file name
-        * @param int $pos Binlog position
-        * @param string $gtid Comma separated GTID set [optional]
+        * @param string $position One of (comma separated GTID list, <binlog file>/<integer>)
+        * @param float $asOfTime UNIX timestamp
         */
-       function __construct( $file, $pos, $gtid = '' ) {
-               $this->file = $file;
-               $this->pos = $pos;
-               $this->gtids = array_map( 'trim', explode( ',', $gtid ) );
-               $this->asOfTime = microtime( true );
-       }
+       public function __construct( $position, $asOfTime ) {
+               $m = [];
+               if ( preg_match( '!^(.+)\.(\d+)/(\d+)$!', $position, $m ) ) {
+                       $this->binlog = $m[1]; // ideally something like host name
+                       $this->pos = [ (int)$m[2], (int)$m[3] ];
+               } else {
+                       $this->gtids = array_map( 'trim', explode( ',', $position ) );
+                       if ( !$this->gtids ) {
+                               throw new InvalidArgumentException( "GTID set should not be empty." );
+                       }
+               }
 
-       /**
-        * @return string <binlog file>/<position>, e.g db1034-bin.000976/843431247
-        */
-       function __toString() {
-               return "{$this->file}/{$this->pos}";
+               $this->asOfTime = $asOfTime;
        }
 
-       function asOfTime() {
+       public function asOfTime() {
                return $this->asOfTime;
        }
 
-       function hasReached( DBMasterPos $pos ) {
+       public function hasReached( DBMasterPos $pos ) {
                if ( !( $pos instanceof self ) ) {
                        throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ );
                }
@@ -54,14 +54,18 @@ class MySQLMasterPos implements DBMasterPos {
                $thisPosByDomain = $this->getGtidCoordinates();
                $thatPosByDomain = $pos->getGtidCoordinates();
                if ( $thisPosByDomain && $thatPosByDomain ) {
-                       $reached = true;
-                       // Check that this has positions GTE all of those in $pos for all domains in $pos
+                       $comparisons = [];
+                       // Check that this has positions reaching those in $pos for all domains in common
                        foreach ( $thatPosByDomain as $domain => $thatPos ) {
-                               $thisPos = isset( $thisPosByDomain[$domain] ) ? $thisPosByDomain[$domain] : -1;
-                               $reached = $reached && ( $thatPos <= $thisPos );
+                               if ( isset( $thisPosByDomain[$domain] ) ) {
+                                       $comparisons[] = ( $thatPos <= $thisPosByDomain[$domain] );
+                               }
                        }
-
-                       return $reached;
+                       // Check that $this has a GTID for at least one domain also in $pos; due to MariaDB
+                       // quirks, prior master switch-overs may result in inactive garbage GTIDs that cannot
+                       // be cleaned up. Assume that the domains in both this and $pos cover the relevant
+                       // active channels.
+                       return ( $comparisons && !in_array( false, $comparisons, true ) );
                }
 
                // Fallback to the binlog file comparisons
@@ -75,7 +79,7 @@ class MySQLMasterPos implements DBMasterPos {
                return false;
        }
 
-       function channelsMatch( DBMasterPos $pos ) {
+       public function channelsMatch( DBMasterPos $pos ) {
                if ( !( $pos instanceof self ) ) {
                        throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ );
                }
@@ -84,8 +88,11 @@ class MySQLMasterPos implements DBMasterPos {
                $thisPosDomains = array_keys( $this->getGtidCoordinates() );
                $thatPosDomains = array_keys( $pos->getGtidCoordinates() );
                if ( $thisPosDomains && $thatPosDomains ) {
-                       // Check that this has GTIDs for all domains in $pos
-                       return !array_diff( $thatPosDomains, $thisPosDomains );
+                       // Check that $this has a GTID for at least one domain also in $pos; due to MariaDB
+                       // quirks, prior master switch-overs may result in inactive garbage GTIDs that cannot
+                       // easily be cleaned up. Assume that the domains in both this and $pos cover the
+                       // relevant active channels.
+                       return array_intersect( $thatPosDomains, $thisPosDomains ) ? true : false;
                }
 
                // Fallback to the binlog file comparisons
@@ -95,6 +102,22 @@ class MySQLMasterPos implements DBMasterPos {
                return ( $thisBinPos && $thatBinPos && $thisBinPos['binlog'] === $thatBinPos['binlog'] );
        }
 
+       /**
+        * @return string|null
+        */
+       public function getLogFile() {
+               return $this->gtids ? null : "{$this->binlog}.{$this->pos[0]}";
+       }
+
+       /**
+        * @return string GTID set or <binlog file>/<position> (e.g db1034-bin.000976/843431247)
+        */
+       public function __toString() {
+               return $this->gtids
+                       ? implode( ',', $this->gtids )
+                       : $this->getLogFile() . "/{$this->pos[1]}";
+       }
+
        /**
         * @note: this returns false for multi-source replication GTID sets
         * @see https://mariadb.com/kb/en/mariadb/gtid
@@ -127,11 +150,8 @@ class MySQLMasterPos implements DBMasterPos {
         * @return array|bool (binlog, (integer file number, integer position)) or false
         */
        protected function getBinlogCoordinates() {
-               $m = [];
-               if ( preg_match( '!^(.+)\.(\d+)/(\d+)$!', (string)$this, $m ) ) {
-                       return [ 'binlog' => $m[1], 'pos' => [ (int)$m[2], (int)$m[3] ] ];
-               }
-
-               return false;
+               return ( $this->binlog !== null && $this->pos !== null )
+                       ? [ 'binlog' => $this->binlog, 'pos' => $this->pos ]
+                       : false;
        }
 }
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 def0eed..3be687b 100644 (file)
@@ -327,16 +327,6 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                return 'local';
        }
 
-       /**
-        * From where in the page HTML should this module be loaded?
-        *
-        * @deprecated since 1.29 Obsolete. All modules load async from `<head>`.
-        * @return string
-        */
-       public function getPosition() {
-               return 'top';
-       }
-
        /**
         * Whether this module's JS expects to work without the client-side ResourceLoader module.
         * Returning true from this function will prevent mw.loader.state() call from being
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 43f6d0e..7b2a5a5 100644 (file)
        "right-bigdelete": "Выдаленьне старонак зь вялікімі гісторыямі",
        "right-deletelogentry": "выдаленьне і аднаўленьне асобных запісаў журналу",
        "right-deleterevision": "выдаленьне і аднаўленьне асобных вэрсіяў старонак",
-       "right-deletedhistory": "пÑ\80аглÑ\8fд Ð²Ñ\8bдаленай Ð³Ñ\96Ñ\81Ñ\82оÑ\80Ñ\8bÑ\96 Ñ\81Ñ\82аÑ\80онак Ð±Ðµз доступу да выдаленага тэксту",
-       "right-deletedtext": "прагляд выдаленага тэксту і зьменаў паміж выдаленымі вэрсіямі старонак",
+       "right-deletedhistory": "Ð\9fÑ\80аглÑ\8fд Ð²Ñ\8bдаленай Ð³Ñ\96Ñ\81Ñ\82оÑ\80Ñ\8bÑ\96 Ñ\81Ñ\82аÑ\80онак Ð±Ñ\8fз доступу да выдаленага тэксту",
+       "right-deletedtext": "Ð\9fрагляд выдаленага тэксту і зьменаў паміж выдаленымі вэрсіямі старонак",
        "right-browsearchive": "пошук выдаленых старонак",
        "right-undelete": "аднаўленьне старонак",
        "right-suppressrevision": "праглядаць, хаваць і аднаўляць пэўныя вэрсіі старонак, зробленыя любым удзельнікам",
index 7d9c891..096d891 100644 (file)
        "log-title-wildcard": "Αναζήτησε τίτλους που αρχίζουν με αυτό το κείμενο",
        "showhideselectedlogentries": "Αλλαγή ορατότητας των επιλεγμένων καταχωρήσεων στο αρχείο καταγραφής συμβάντων",
        "log-edit-tags": "Επεξεργασία ετικετών των επιλεγμένων καταχωρήσεων του αρχείου καταγραφής",
+       "checkbox-select": "Επιλογή: $1",
        "checkbox-all": "Όλα",
        "checkbox-none": "Κανένα",
        "checkbox-invert": "Αντιστροφή",
        "newimages-user": "Διεύθυνση IP ή όνομα χρήστη",
        "newimages-showbots": "Εμφάνιση αρχείων ανεβασμένων από ρομπότ",
        "newimages-hidepatrolled": "Απόκρυψη ελεγμένων αρχείων.",
+       "newimages-mediatype": "Τύπος μέσου:",
        "noimages": "Δεν υπάρχουν εικόνες.",
        "ilsubmit": "Αναζήτηση",
        "bydate": "ημερομηνίας",
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 e62887f..a98ddae 100644 (file)
@@ -15,6 +15,7 @@
        "tog-hidepatrolled": "Ocultar ediciones patrulladas de los cambios recientes",
        "tog-newpageshidepatrolled": "Ocultar páginas patrulladas de la lista de páginas nuevas",
        "tog-showtoolbar": "Mostrar barra de edición",
+       "tog-oldsig": "Su firma actual:",
        "tog-ccmeonemails": "Enviarme una copia de los correos electrónicos que yo envíe a otros usuarios",
        "sunday": "domingo",
        "monday": "lunes",
        "views": "Vistas",
        "toolbox": "Herramientas",
        "otherlanguages": "Otros idiomas",
-       "lastmodifiedat": "Esta página fue modificada por última vez el $1, a las $2.",
+       "lastmodifiedat": "Esta página fue editada por última vez el $1, a las $2.",
        "protectedpage": "Página protegida",
        "jumpto": "Saltar a:",
        "jumptonavigation": "navegación",
        "jumptosearch": "buscar",
        "view-pool-error": "Lo sentimos, los servidores están sobrecargados en este momento.\nDemasiados usuarios están intentando ver esta página.\nPor favor espere unos instantes antes de reintentar acceder nuevamente a esta página.\n\n$1",
+       "generic-pool-error": "Lo sentimos, los servidores están sobrecargados en este momento.\nHay demasiados usuarios tratando de ver este recurso.\nPor favor espere un momento antes de intentar acceder de nuevo.",
        "aboutsite": "Acerca de {{SITENAME}}",
        "aboutpage": "Project:Acerca de",
        "currentevents": "Actualidad",
        "ok": "Aceptar",
        "retrievedfrom": "Obtenido de «$1»",
        "youhavenewmessages": "{{PLURAL:$3|Tiene}} $1 ($2).",
-       "youhavenewmessagesmulti": "Tienes mensajes nuevos en $1",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|Tiene}} $1 de {{PLURAL:$3|otro usuario|$3 usuarios}} ($2).",
+       "youhavenewmessagesmanyusers": "Tiene $1 de muchos usuarios ($2).",
+       "youhavenewmessagesmulti": "Tiene mensajes nuevos en $1",
        "editsection": "editar",
        "editold": "editar",
        "viewsourceold": "ver código",
        "databaseerror-query": "Consulta: $1",
        "databaseerror-function": "Función: $1",
        "databaseerror-error": "Error: $1",
+       "transaction-duration-limit-exceeded": "Con el fin de evitar un aumento excesivo del retardo de replicación, se anuló esta transacción porque la duración de escritura ($1) excedió el límite de $2 {{PLURAL:$2|segundo|segundos}}.\nSi está cambiando muchos elementos a la vez, trate de hacer operaciones similares más pequeñas.",
        "laggedslavemode": "<strong>Aviso:</strong> la página puede no contener las actualizaciones más recientes.",
+       "readonly": "Base de datos bloqueada",
        "enterlockreason": "Proporcione el motivo del bloqueo, así como una estimación de cuándo se producirá el desbloqueo",
-       "readonlytext": "La base de datos se encuentra actualmente bloqueada y no permite la creación de páginas nuevas y otras modificaciones, probablemente de forma temporal debido al mantenimiento rutinario de la base de datos. Después de esas operaciones el sitio se encontrará nuevamente disponible.\n\nLa razón dada por el administrador que bloqueó la base de datos es la que sigue: $1",
+       "readonlytext": "Actualmente la base de datos no permite nuevas entradas u otras modificaciones, probablemente por mantenimiento rutinario, tras lo cual volverá a la normalidad.\n\nLa explicación dada por el administrador que la bloqueó fue: $1",
        "missing-article": "La base de datos no encuentra el texto de una página que debería hallarse, llamada \"$1\" $2.\n\nLa causa de esto suele deberse a un ''diff'' anacrónico o un enlace al historial de una página que ha sido borrada.\n\nSi no fuera el caso, usted puede haber encontrado un fallo en el sistema.\n\nPor favor, avise a un [[Special:ListUsers/sysop|administrador]], tomando nota de la URL.",
        "internalerror": "Error interno",
        "internalerror_info": "Error interno: $1",
        "viewsource": "Ver código",
-       "actionthrottledtext": "Como medida de protección contra el ''spam'', la acción que está realizando está limitada a un número determinado de veces en un periodo corto de tiempo. Usted ha excedido ese límite. Por favor pruebe de nuevo en unos minutos.",
-       "viewsourcetext": "Puede ver y copiar el código fuente de esta página:",
-       "editinginterface": "'''Aviso:''' Está usted editando una página usada para proporcionar texto de interfaz para el software. Los cambios en esta página afectarán a la apariencia de la interfaz para los demás usuarios. Para traducciones, por favor considere usar [//translatewiki.net/wiki/Main_Page?setlang=en translatewiki.net], el proyecto de traducción de MediaWiki.",
+       "actionthrottledtext": "Como medida contra los abusos, la acción que está realizando está limitada a un número determinado de veces en un periodo corto de tiempo, y ha excedido ese límite.\nPor favor inténtelo de nuevo en unos minutos.",
+       "viewsourcetext": "Puede ver y copiar el código fuente de esta página.",
+       "viewyourtext": "Puede ver y copiar el código de <strong>sus ediciones</strong> en esta página.",
+       "protectedinterface": "Esta página proporciona el texto de la interfaz del software en este wiki, y está protegida para prevenir el abuso.\nPara agregar o cambiar las traducciones para todos los wikis, use [https://translatewiki.net/ translatewiki.net], el proyecto de localización de MediaWiki.",
+       "editinginterface": "<strong>Advertencia:</strong> está editando una página usada para proporcionar texto de la interfaz al software. \nLos cambios en esta página afectarán la apariencia de la interfaz de los demás usuarios de este wiki.",
+       "translateinterface": "Para añadir o cambiar traducciones para todos los wikis, use [https://translatewiki.net/ translatewiki.net], el proyecto de traducción de MediaWiki.",
+       "namespaceprotected": "No tiene permiso para editar las páginas del espacio de nombres <strong>$1</strong>.",
+       "customcssprotected": "No tiene permiso para editar esta página CSS, porque contiene configuraciones personales de otro usuario.",
+       "customjsprotected": "No tiene permiso para editar esta página JavaScript, porque contiene configuraciones personales de otro usuario.",
+       "mycustomcssprotected": "No tiene permiso para editar esta página CSS.",
+       "mycustomjsprotected": "No tiene permiso para editar esta página JavaScript.",
+       "myprivateinfoprotected": "No tiene permiso para editar su información privada.",
+       "mypreferencesprotected": "No tiene permiso para editar sus preferencias.",
+       "exception-nologin-text": "Por favor inicie sesión para acceder a esta página o llevar a cabo esta acción.",
+       "exception-nologin-text-manual": "Necesita $1 para poder ver esta página o llevar a cabo esta acción.",
+       "logouttext": "<strong>Su sesión ha finalizado.</strong>\n\nPuede que algunas páginas continúen mostrándose como si la sesión estuviera iniciada hasta que actualice la caché de su navegador.",
        "welcomeuser": "Le damos la bienvenida, $1.",
        "welcomecreation-msg": "Se ha creado su cuenta.\nPuede cambiar las [[Special:Preferences|preferencias]] de {{SITENAME}} si lo desea.",
        "yourname": "Nombre de usuario:",
        "yourpasswordagain": "Escriba la contraseña otra vez:",
        "createacct-yourpasswordagain": "Confirme la contraseña",
        "createacct-yourpasswordagain-ph": "Escriba la contraseña otra vez",
+       "yourdomainname": "Su dominio:",
+       "password-change-forbidden": "No puede cambiar las contraseñas en este wiki.",
+       "externaldberror": "Hubo un error de autenticación en la base de datos, o bien no tiene autorización para actualizar su cuenta externa.",
        "login": "Acceder",
+       "login-security": "Verifique su identidad",
        "nav-login-createaccount": "Iniciar sesión / crear cuenta",
        "logout": "Desconectar",
        "userlogout": "Salir",
+       "notloggedin": "No ha accedido",
        "userlogin-noaccount": "¿No tiene una cuenta?",
        "userlogin-joinproject": "Únase a {{SITENAME}}",
        "userlogin-resetpassword-link": "¿Olvidó su contraseña?",
+       "userlogin-loggedin": "Ya está {{GENDER:$1|conectado|conectada}} como $1.\nUse el formulario de abajo para iniciar sesión como otro usuario.",
+       "userlogin-reauth": "Debe iniciar sesión de nuevo para verificar que usted es {{GENDER:$1|$1}}.",
        "createacct-emailrequired": "Dirección de correo electrónico",
        "createacct-emailoptional": "Dirección de correo electrónico (opcional)",
-       "anoneditwarning": "'''Aviso:''' No ha iniciado sesión con una cuenta de usuario.\nSu dirección IP se almacenará en el historial de ediciones de la página.",
+       "createacct-email-ph": "Escriba su dirección de correo electrónico",
+       "createacct-another-email-ph": "Escriba la dirección de correo electrónico",
+       "createacct-reason-ph": "Por qué está creando otra cuenta",
+       "createacct-submit": "Cree su cuenta",
+       "createacct-benefit-heading": "Personas como usted son las que construyen {{SITENAME}}.",
+       "badretype": "Las contraseñas que usted ha introducido no coinciden.",
+       "usernameinprogress": "Ya está en marcha la creación de una cuenta para este nombre de usuario.\nPor favor, espere.",
+       "userexists": "El nombre de usuario indicado ya está en uso.\nPor favor elija un nombre diferente.",
+       "nocookiesnew": "Se ha creado la cuenta de usuario, pero aún no ha iniciado sesión.\n{{SITENAME}} usa <em>cookies</em> para identificar a los usuarios registrados.\nSu navegador tiene desactivadas las <em>cookies</em>.\nPor favor, actívelas e inicie sesión con su nuevo nombre de usuario y contraseña.",
+       "nocookieslogin": "{{SITENAME}} utiliza <em>cookies</em> para la autenticación de usuarios. Las <em>cookies</em> están desactivadas en su navegador. Por favor, actívelas e inténtelo de nuevo.",
+       "nocookiesfornew": "No se pudo crear la cuenta de usuario, porque no pudimos confirmar su origen.\nAsegúrese de que tiene las <em>cookies</em> activadas, luego recargue esta página e inténtelo de nuevo.",
+       "createacct-loginerror": "La cuenta se ha creado correctamente, pero no se pudo ingresar automáticamente. Proceda al [[Special:UserLogin|acceso manual]].",
+       "loginsuccess": "<strong>Ha accedido a {{SITENAME}} como «$1».</strong>",
+       "nosuchuser": "No existe ninguna cuenta llamada «$1».\nLos nombres de usuario distinguen mayúsculas y minúsculas.\nCompruebe su escritura o [[Special:CreateAccount|cree una cuenta nueva]].",
+       "nosuchusershort": "No existe ningún usuario llamado «$1».\nCompruebe que lo ha escrito correctamente.",
+       "nouserspecified": "Debe especificar un nombre de usuario.",
+       "login-userblocked": "No puede iniciar sesión porque su cuenta está bloqueada.",
+       "wrongpassword": "El nombre de usuario o la contraseña que ha proporcionado son incorrectos.\nPor favor inténtelo de nuevo.",
+       "wrongpasswordempty": "No ha introducido una contraseña.\nPor favor inténtelo de nuevo.",
+       "password-name-match": "Su contraseña debe ser diferente de su nombre de usuario.",
+       "passwordsent": "Se ha enviado una nueva contraseña al correo electrónico de «$1».\nPor favor, identifíquese de nuevo tras recibirla.",
+       "anoneditwarning": "<strong>Advertencia:</strong> no ha iniciado sesión. Su dirección IP se hará pública si hace cualquier edición en estas condiciones. Si <strong>[$1 inicia sesión]</strong> o <strong>[$2 crea una cuenta]</strong>, sus ediciones se atribuirán a su nombre de usuario, además de otros beneficios.",
        "newarticletext": "Ha seguido usted un enlace a una página que aún no existe.\nPara crear esta página, escriba en el campo a continuación. Para más información, consulte la [$1 página de ayuda].\nSi ha llegado aquí por error, vuelva a la página anterior.",
-       "noarticletext": "En este momento no hay texto en esta página.\nPuede [[Special:Search/{{PAGENAME}}|buscar el título de esta página]] en otras páginas,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar en los registros],\no [{{fullurl:{{FULLPAGENAME}}|action=edit}} editar esta página]</span>.",
+       "noarticletext": "En este momento no hay texto en esta página.\nPuede [[Special:Search/{{PAGENAME}}|buscar el título de esta página]] en otras páginas,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar en los registros relacionados],\no [{{fullurl:{{FULLPAGENAME}}|action=edit}} crear esta página]</span>.",
        "copyrightwarning": "Por favor observe que todas las contribuciones realizadas en {{SITENAME}} serán consideradas como liberadas bajo $2 (véase $1 para más detalles).\nSi no desea que sus escritos sean editados o redistribuídos a voluntad, entonces no contribuya aquí.<br />\nAl mismo tiempo está usted prometiendo que lo que usted va a enviar lo ha escrito usted, o copiado de una fuente de dominio público.\n'''¡No envíe textos con derechos de autor sin el debido permiso!'''",
        "permissionserrorstext-withaction": "No tiene permiso para $2 por {{PLURAL:$1|la|las}} {{PLURAL:$1|siguiente|siguientes}} {{PLURAL:$1|razón|razones}}:",
        "rev-deleted-text-permission": "Esta revisión de la página ha sido '''borrada'''.\nPuede encontrar detalles en el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de borrados].",
-       "rev-deleted-text-unhide": "Esta revisión de página ha sido '''borrada'''.\nPuede haber detalles en el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de borrados].\nComo administrador todavía puede [$1 ver esta revisión] si así lo desea.",
-       "rev-suppressed-text-unhide": "Esta revisión de la página ha sido '''suprimida'''.\nPuede haber detalles en el [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registro de supresiones].\nComo administrador podrá seguir [$1 viendo esta revisión] si desea continuar.",
-       "rev-deleted-text-view": "Esta revisión de la página ha sido '''borrada'''.\nComo administrador puede verla; puede haber detalles en el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de borrados].",
-       "rev-suppressed-text-view": "Esta revisión de la página ha sido '''suprimida'''.\nComo administrador puede verla; puede haber detalles en el [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registro de supresiones].",
+       "rev-deleted-text-unhide": "Esta revisión ha sido <strong>eliminada</strong>.\nPara más información, consulte el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de borrados].\nComo administrador, aún puede [$1 ver esta revisión] si lo desea.",
+       "rev-suppressed-text-unhide": "Esta revisión ha sido <strong>suprimida</strong>.\nPara más información, consulte el [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registro de supresiones].\nComo supresor, aún puede [$1 ver esta revisión] si lo desea.",
+       "rev-deleted-text-view": "Esta revisión ha sido <strong>eliminada</strong>.\nAún tiene la posibilidad de verla. Para más información, consulte el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de borrados].",
+       "rev-suppressed-text-view": "Esta revisión ha sido <strong>suprimida</strong>.\nAún tiene la posibilidad de verla. Para más información, consulte el [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registro de supresiones].",
        "rev-deleted-no-diff": "No puede visualizarse este cambio debido a que las revisiones han sido '''borradas'''.\nPuede haber detalles en el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de borrados].",
-       "revdelete-nooldid-text": "No se ha especificado una revisión o revisiones destino sobre las que realizar esta función.",
+       "revdelete-nooldid-text": "O bien no se ha especificado una revisión destino sobre la que realizar esta función, o bien la revisión especificada no existe, o bien está intentando ocultar la revisión actual.",
        "revdelete-show-file-confirm": "¿Está seguro de que desea ver la revisión borrada del archivo \"<nowiki>$1</nowiki>\" del $2 a las $3?",
        "revdelete-confirm": "Confirme que quiere realizar la operación, que entiende las consecuencias y que está ejecutando dicha acción acorde con [[{{MediaWiki:Policy-url}}|las políticas]].",
        "lineno": "Línea $1:",
        "rightslog": "Registro de cambios de permisos de usuarios",
        "recentchanges": "Cambios recientes",
        "recentchangeslinked-toolbox": "Cambios relacionados",
-       "recentchangeslinked-summary": "Esta es una lista de cambios efectuados recientemente a páginas enlazadas desde una página dada (o de miembros de una categoría dada).\nLas páginas que se encuentran en tu [[Special:Watchlist|lista de seguimiento]] están en <strong>negritas</strong>.",
+       "recentchangeslinked-summary": "Escriba el nombre de una página para ver cambios realizados en páginas con enlaces entrantes o salientes a esa página. (Para ver lo que pertenece a una categoría, escriba «Categoría:Nombre de la categoría»). Los cambios en páginas de su [[Special:Watchlist|lista de seguimiento]] aparecen en <strong>negrita</strong>.",
        "upload": "Subir archivo",
        "filehist-help": "Haga clic sobre una fecha/hora para ver el archivo a esa fecha.",
        "randompage": "Página aleatoria",
        "nbytes": "$1 {{PLURAL:$1|byte|bytes}}",
        "emailuser": "Enviar un correo electrónico a este usuario",
-       "addedwatchtext": "La página «[[:$1]]» ha sido añadida a su [[Special:Watchlist|lista de seguimiento]]. Los cambios futuros en esta página y en su página de discusión asociada se indicarán ahí, y la página aparecerá '''en negritas''' en la [[Special:RecentChanges|lista de cambios recientes]] para hacerla más fácil de detectar.\n\nCuando quiera eliminar la página de su lista de seguimiento, presione «Dejar de vigilar» en el menú.",
+       "addedwatchtext": "Se han añadido «[[:$1]]» y su página de discusión a su [[Special:Watchlist|lista de seguimiento]].",
        "removedwatchtext": "Se han eliminado «[[:$1]]» y su página de discusión de tu [[Special:Watchlist|lista de seguimiento]].",
        "confirmdeletetext": "Está a punto de borrar una página junto con su historial.\nPor favor confirme que desea realizar esto, que entiende las consecuencias y que está realizando esta acción de acuerdo con las [[{{MediaWiki:Policy-url}}|políticas]]",
        "deletedtext": "\"$1\" ha sido borrado.\nVea $2 para un registro de los borrados recientes.",
        "rollbacklink": "revertir",
        "protect-text": "Puede ver y modificar el nivel de protección de la página '''$1'''.",
        "protect-locked-access": "Su cuenta no tiene permiso para cambiar los niveles de protección de una página.\nA continuación se muestran las opciones actuales de la página '''$1''':",
-       "protect-cascadeon": "Actualmente esta página está protegida porque está incluida en {{PLURAL:$1|la siguiente página|las siguientes páginas}}, que tienen activada la opción de protección en cascada. Puede cambiar el nivel de protección de esta página, pero no afectará a la protección en cascada.",
+       "protect-cascadeon": "Actualmente esta página está protegida porque está transcluida en {{PLURAL:$1|la siguiente página, que tiene|las siguientes páginas, que tienen}} activada la opción de protección de cascada.\nPuede cambiar el nivel de protección de esta página, pero no afectará a la protección de cascada.",
        "protect-cantedit": "No puede cambiar el nivel de protección porque no tiene permiso para editarla.",
        "blanknamespace": "(Principal)",
        "whatlinkshere": "Lo que enlaza aquí",
        "confirmemail_body": "Alguien, probablemente usted mismo, ha registrado desde la dirección IP $1 la cuenta \"$2\" en {{SITENAME}}, utilizando esta dirección de correo.\n\nPara confirmar que esta cuenta realmente le pertenece y activar el correo en {{SITENAME}}, siga este enlace:\n\n$3\n\nSi la cuenta *no* es suya, siga este otro enlace para cancelar la confirmación de la dirección de correo:\n\n$5\n\nEl código de confirmación expirará en $4.",
        "confirmemail_body_changed": "Alguien, probablemente usted,\nha modificado la dirección de correo electrónico asociado a la cuenta \"$2\" hacia esta en {{SITENAME}}, desde la dirección IP $1.\n\nPara confirmar que esta cuenta realmente le pertenece y reactivar las funciones de correo electrónico en {{SITENAME}}, abra este enlace en su navegador:\n\n$3\n\nSi la cuenta *no* le pertenece, sigua el siguiente enlace para cancelar la confirmación:\n\n$5\n\nEste código de confirmación expirará el $4.",
        "deletedwhileediting": "'''Aviso''': ¡Esta página fue borrada después de que usted empezara a editar!",
-       "confirmrecreate": "El usuario [[User:$1|$1]] ([[User talk:$1|discusión]]) borró este artículo después de que usted empezara a editarlo y dio esta razón:\n: ''$2'' \nPor favor, confirme que realmente desea crear de nuevo esta página.",
+       "confirmrecreate": "{{GENDER:$1|El usuario|La usuaria}} [[User:$1|$1]] ([[User talk:$1|discusión]]) borró esta página después de que usted comenzara a editarla, por este motivo:\n: <em>$2</em>\nPor favor confirme que realmente quiere volver a crearla.",
        "watchlistedit-normal-explain": "Los títulos de su lista de seguimiento se muestran debajo.\nPara eliminar un título, marque la casilla junto a él, y haga clic en ''{{int:Watchlistedit-normal-submit}}''.\nTambién puede [[Special:EditWatchlist/raw|editar la lista de en crudo]].",
        "watchlistedit-raw-explain": "Los títulos de su lista de seguimiento se muestran debajo. Esta lista puede ser editada añadiendo o eliminando líneas de la lista;\nun título por línea.\nCuando acabe, haga clic en \"{{int:Watchlistedit-raw-submit}}\".\nTambién puede [[Special:EditWatchlist|usar el editor estándar]].",
        "watchlistedit-raw-done": "Su lista de seguimiento ha sido actualizada.",
index 2505c92..ab94885 100644 (file)
        "delete_and_move_text": "Kohdesivu [[:$1]] on jo olemassa. \nHaluatko poistaa sen, jotta nykyinen sivu voitaisiin siirtää sen tilalle?",
        "delete_and_move_confirm": "Kyllä, poista kohdesivu",
        "delete_and_move_reason": "Sivu on sivun [[$1]] siirron tiellä.",
-       "selfmove": " Nimi on sama;\nSivua ei voi siirtää itsensä päälle.",
+       "selfmove": "Nimi on sama;\nSivua ei voi siirtää itsensä päälle.",
        "immobile-source-namespace": "Sivuja ei voi siirtää nimiavaruudessa ”$1”",
        "immobile-target-namespace": "Sivuja ei voi siirtää nimiavaruuteen ”$1”",
        "immobile-target-namespace-iw": "Kielilinkki ei ole kelvollinen kohde sivun siirrolle.",
index 4ca7a18..550bce9 100644 (file)
        "protectedpagetext": "Cette page a été protégée pour empêcher sa modification ou d’autres actions.",
        "viewsourcetext": "Vous pouvez voir et copier le contenu de cette page.",
        "viewyourtext": "Vous pouvez voir et copier le contenu de <strong>vos modifications</strong> à cette page.",
-       "protectedinterface": "Cette page fournit du texte d’interface pour le logiciel sur ce wiki et est protégée pour éviter les abus.\nPour ajouter ou modifier des traductions sur tous les wikis, veuillez utiliser [https://translatewiki.net/ translatewiki.net], le projet de localisation de MediaWiki.",
+       "protectedinterface": "Cette page fournit du texte d’interface pour le logiciel sur ce wiki et est protégée pour éviter les abus.\nPour ajouter ou modifier des traductions sur tous les wikis, veuillez utiliser [https://translatewiki.net/ translatewiki.net], le projet de régionalisation de MediaWiki.",
        "editinginterface": "<strong>Attention :</strong> vous êtes en train de modifier une page utilisée pour créer le texte de l’interface du logiciel.\nLes changements sur cette page se répercuteront sur l’apparence de l’interface utilisateur pour les autres utilisateurs de ce wiki.",
        "translateinterface": "Pour ajouter ou modifier des traductions pour tous les wikis, veuillez utiliser [https://translatewiki.net/ translatewiki.net], le projet de localisation linguistique de MediaWiki.",
        "cascadeprotected": "Cette page est protégée contre les modifications car elle est transcluse par {{PLURAL:$1|la page suivante, qui a été protégée|les pages suivantes, qui ont été protégées}} avec l’option « protection en cascade » activée :\n$2",
index c57b95f..4e01b8e 100644 (file)
@@ -16,7 +16,8 @@
                        "Duolaimi",
                        "Impersonator 1",
                        "Babanwalia",
-                       "Macofe"
+                       "Macofe",
+                       "Fanjiayi"
                ]
        },
        "tog-underline": "下划链接",
        "nstab-template": "模版",
        "nstab-help": "帮助页",
        "nstab-category": "分类",
+       "mainpage-nstab": "封面",
        "nosuchaction": "冇有个只命令",
        "nosuchactiontext": "Wiki识别伓到个只URL命令",
        "nosuchspecialpage": "冇有个只特殊页",
        "createaccount-title": "到{{SITENAME}}创建𠮶帐户",
        "createaccount-text": "有人到{{SITENAME}}用倷𠮶电子邮件地址开设喽只名字系 \"$2\" 𠮶新帐户($4),密码系 \"$3\" 。请倷仰上登录同到修改密码。\n\n要系帐户创建不对𠮶话,倷就莫搭个只消息。",
        "loginlanguagelabel": "语言: $1",
+       "pt-login": "登入",
+       "pt-createaccount": "新开只帐户",
        "changepassword": "改过密码",
        "resetpass_announce": "倷系用到临时email𠮶代码登入𠮶。要登正入,倷要到个首设定只新密码:",
        "resetpass_header": "设过密码",
        "rclistfrom": "显示自$3 $2后𠮶新改动",
        "rcshowhideminor": "$1细编辑",
        "rcshowhidebots": "$1机器人𠮶编辑",
-       "rcshowhideliu": "$1登入用户𠮶编辑",
+       "rcshowhideliu": "$1注册用户",
        "rcshowhideanons": "$1匿名用户𠮶编辑",
        "rcshowhidepatr": "$1检查过𠮶编辑",
        "rcshowhidemine": "$1偶𠮶编辑",
-       "rclinks": "显示最晏$2日之内最新𠮶$1改动。",
+       "rclinks": "显示最晏$2日之内最新𠮶$1改动。",
        "diff": "差异",
        "hist": "历史",
        "hide": "弆到",
        "namespace": "空间名:",
        "invert": "反选",
        "blanknamespace": "(主要)",
-       "contributions": "用户贡献",
+       "contributions": "{{GENDER:$1|用户}}贡献",
        "contributions-title": "$1𠮶用户贡献",
        "mycontris": "偶𠮶贡献",
        "contribsub2": "$1𠮶贡献 ($2)",
        "tooltip-t-recentchangeslinked": "从个页连出𠮶全部页面𠮶改动",
        "tooltip-feed-rss": "个页𠮶RSS订阅",
        "tooltip-feed-atom": "个页𠮶Atom订阅",
-       "tooltip-t-contributions": "望吖个只用户𠮶贡献",
+       "tooltip-t-contributions": "由{{GENDER:$1|此用户}}做出的贡献列表",
        "tooltip-t-emailuser": "发封邮件到个只用户",
        "tooltip-t-upload": "上传图像或多媒体文件",
        "tooltip-t-specialpages": "全部特殊页列表",
        "file-info-size": "$1 × $2 像素,档案大细:$3 ,MIME类型:$4",
        "file-nohires": "冇更高分辨率𠮶图像。",
        "svg-long-desc": "SVG档案,表面大细: $1 × $2 像素,档案大细:$3",
-       "show-big-image": "å®\8cæ\95´å\88\86辨ç\8e\87",
+       "show-big-image": "å\8e\9få§\8bæ\96\87件",
        "newimages": "新建图像画廊",
        "imagelisttext": "底下系按$2排列𠮶$1只档案列表。",
        "noimages": "冇什哩可望。",
        "external_image_whitelist": "#留住个行字<pre>\n#到下首(//𠮶中间)输入正规表达式\n#佢俚会同得外部(已超连结𠮶)图片配合\n#许滴配合到出来𠮶会显示做图片,否则就光会显示做连结\n#有 # 开头𠮶行会当做注解\n#大小写冇有差别\n\n#到个行上首输入所有𠮶regex。留住个行字</pre>",
        "tag-filter": "[[Special:Tags|标签]]过滤器:",
        "rightsnone": "(冇)",
-       "searchsuggest-search": "寻吖"
+       "searchsuggest-search": "寻吖{{SITENAME}}"
 }
index 6cb2c4f..4748929 100644 (file)
@@ -14,7 +14,8 @@
                        "아라",
                        "Srdjan m",
                        "Macofe",
-                       "Stavanger7"
+                       "Stavanger7",
+                       "Fanjiayi"
                ]
        },
        "tog-underline": "Ultracatenun:",
        "october-date": "$1 octobre",
        "november-date": "$1 novembre",
        "december-date": "$1 decembre",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categorie|Categories}}",
        "category_header": "Articules in categorie \"$1\"",
        "subcategories": "Subcategories",
        "newwindow": "(es apertet in un nov fenestre)",
        "cancel": "Anullar",
        "moredotdotdot": "Plu...",
-       "morenotlisted": "Ti liste ne es complet.",
+       "morenotlisted": "Forsan ti liste es íncomplet.",
        "mypage": "Págine",
        "mytalk": "Conversation",
        "anontalk": "Discussion",
        "badaccess-groups": "Ti action es limitat a usatores in {{PLURAL:$2|li gruppe|un del secuent gruppes:}} $1",
        "versionrequired": "Version $1 de MediaWiki exiget",
        "versionrequiredtext": "Version $1 de MediaWiki es exiget por usar ti págine.\nVider [[Special:Version|págine de version]].",
+       "ok": "OK",
        "retrievedfrom": "Cargat de «$1»",
        "youhavenewmessages": "Vu have $1 ($2).",
        "youhavenewmessagesfromusers": "Tu have $1 de {{PLURAL:$3|un altri usator|$3 usatores}} ($2).",
        "nstab-template": "Avise",
        "nstab-help": "Auxilie",
        "nstab-category": "Categorie",
+       "mainpage-nstab": "Principal págine",
        "nosuchaction": "Null tal action existe",
        "nosuchactiontext": "Li action indicat in li URL es ínvalid.\nForsan tu ha mistypat li URL o secuet un íncorrect ligament.\nForsan it indica un erra in li programma usat de {{SITENAME}}.",
        "nosuchspecialpage": "Null tal special págine",
        "createaccount-title": "Creation de conto por {{SITENAME}}",
        "loginlanguagelabel": "Lingue: $1",
        "suspicious-userlogout": "Tui petition por surtir esset desaprobat pro que probabilmen esset inviat per un navigator ruptet o servitor de autorisation che caching.",
+       "pt-login": "Aperter session",
        "pt-login-button": "Aperter session",
        "pt-createaccount": "Crear un conto",
+       "pt-userlogout": "Surtir",
        "changepassword": "Modificar passa-parol",
        "oldpassword": "Anteyan passa-parol:",
        "newpassword": "Nov passa-parol:",
        "template-protected": "(protectet)",
        "template-semiprotected": "(medie-gardat)",
        "hiddencategories": "Ti págine es un membre de {{PLURAL:$1|1 categorie ocultat|$1 categories ocultat}}:",
+       "permissionserrors": "Tu ne have sufficent jures",
        "permissionserrorstext-withaction": "Vu ne have permission por $2, por li sequent {{PLURAL:$1|motive|motives}}:",
        "recreate-moveddeleted-warn": "'''Advertiment: Vu es recreant un págine que esset anteriorimen deletet.'''\n\nVu deve considerar ca it es convenent por continuar redactant ti págine.\nLi deletion e diarium de movement por li págine es sub li condition ci por convenience:",
        "moveddeleted-notice": "Ti págine ha esset deletet.\nLi deletion e diarium de movement por li págine es sub li condition in infra por referentie.",
        "rclistfrom": "Monstrar li nov modificationes desde $3 $2",
        "rcshowhideminor": "$1 redactiones minori",
        "rcshowhideminor-show": "Monstrar",
+       "rcshowhideminor-hide": "Ocultar",
        "rcshowhidebots": "$1 machines",
+       "rcshowhidebots-show": "Monstrar",
        "rcshowhidebots-hide": "Ocultar",
        "rcshowhideliu": "$1 usatores registrat",
+       "rcshowhideliu-show": "Monstrar",
        "rcshowhideliu-hide": "Ocultar",
        "rcshowhideanons": "$1 usatores anonim",
        "rcshowhideanons-show": "Monstrar",
+       "rcshowhideanons-hide": "Ocultar",
        "rcshowhidepatr": "$1 redactiones vigilat",
        "rcshowhidepatr-hide": "Ocultar",
        "rcshowhidemine": "$1 mi redactiones",
        "rcshowhidemine-show": "Monstrar",
+       "rcshowhidemine-hide": "Ocultar",
        "rclinks": "Monstrar li $1 ultim modificationes fat durante li $2 ultim dies",
        "diff": "dif",
        "hist": "hist",
        "tooltip-pt-login": "Tu es incorrageat crear un conto, ma to ne es un deventie.",
        "tooltip-pt-logout": "Surtir",
        "tooltip-ca-talk": "Discussion pri li articul.",
-       "tooltip-ca-edit": "Redacter ti págine. Ples usar li buton de prevision antequam conservar.",
+       "tooltip-ca-edit": "Redacter ti-ci págine",
        "tooltip-ca-addsection": "Comensar un nov section",
        "tooltip-ca-viewsource": "Ti págine es protectet. Ma tu posse vider e copiar su fonte.",
        "tooltip-ca-history": "Passat versiones de ti págine",
        "tooltip-preferences-save": "Conservar preferenties",
        "tooltip-summary": "Ples intrar un curt resummation.",
        "simpleantispam-label": "Control anti-spam.\n<strong>Ne</strong> plena to ci!",
+       "pageinfo-article-id": "Págine ID",
        "pageinfo-toolboxlink": "Information pri li págine",
        "previousdiff": "← Redaction anteriori",
        "nextdiff": "Proxim redaction →",
        "version-software": "Software installat",
        "version-software-product": "Producte",
        "version-software-version": "Version",
+       "redirect-submit": "Ear",
+       "redirect-user": "Usator ID",
        "specialpages": "Special págines",
        "specialpages-group-maintenance": "Raportes de conservation",
        "specialpages-group-other": "Altri págines special",
        "tags-display-header": "Aspecte in listes de change",
        "tags-description-header": "Descrition complet de signification",
        "tags-hitcount-header": "Changes nómiat",
+       "tags-active-yes": "Yes",
        "tags-edit": "redacter",
        "tags-hitcount": "$1 {{PLURAL:$1|change|changes}}",
        "logentry-delete-delete": "$1 ha removet li págine $3",
index b90472d..d4b1f93 100644 (file)
        "backend-fail-read": "Negalima nuskaityti failo $1.",
        "backend-fail-create": "Negalima sukurti failo $1.",
        "backend-fail-maxsize": "Failo $1 sukurti nepavyko nes jis didesnis nei {{PLURAL:$2|vienas baitas|$2 baitai|$2 baitų}}.",
-       "backend-fail-readonly": "Galutinė saugykla \"$1\" dabar yra skirta tik skaitymui. Buvo nurodyta priežastis: \"$2\"",
+       "backend-fail-readonly": "Galutinė saugykla \"$1\" dabar yra skirta tik skaitymui. Nurodyta priežastis: <em>$2</em>",
        "backend-fail-synced": "Failas \"$1\", esantis vidinėje galutinėje saugykloje, yra pažymėtas kaip nepilnas.",
        "backend-fail-connect": "Negalima prisijungti prie galutinės saugyklos \"$1\".",
        "backend-fail-internal": "Nežinoma klaida įvyko galutinėje saugykloje \"$1\".",
        "editcomment": "Pateiktas toks keitimo paaiškinimas: <em>$1</em>.",
        "revertpage": "Atmestas [[Special:Contributions/$2|$2]] ([[User talk:$2|Aptarimas]]) pakeitimas; sugrąžinta [[User:$1|$1]] versija",
        "revertpage-nouser": "Atversti pakeitimai paslėpto vartotojo, grąžino prieš tai buvusią versiją {{GENDER:$1|[[User:$1|$1]]}}",
-       "rollback-success": "Atmesti $1 pakeitimai;\ngrąžinta prieš tai buvusi $2 versija.",
+       "rollback-success": "Atmesti {{GENDER:$3|$1}} pakeitimai;\ngrąžinta prieš tai buvusi {{GENDER:$4|$2}} versija.",
        "rollback-success-notify": "Atmesti $1 pakeitimai;\ngrąžinta prieš tai buvusi $2 versija. [$3 Rodyti skirtumus]",
        "sessionfailure-title": "Sesijos klaida",
        "sessionfailure": "Atrodo yra problemų su jūsų prisijungimo sesija; šis veiksmas buvo atšauktas kaip atsargumo priemonė prieš sesijos vogimą.\nPrašome paspausti „atgal“ ir perkraukite puslapį iš kurio atėjote, ir pamėginkite vėl.",
        "import-nonewrevisions": "Nebuvo importuotos jokios versijos (visos jau buvo įkeltos arba praleistos dėl klaidų).",
        "xml-error-string": "$1 $2 eilutėje, $3 stulpelyje ($4 baitas): $5",
        "import-upload": "Įkelti XML duomenis",
-       "import-token-mismatch": "Sesijos duomenys prarasti.\n\nGali būti, kad esate atsijungęs. <strong>Prašome patikrinti, ar vis dar esate prisijungęs, ir pabandyti iš naujo</strong>.\nJei ir toliau nepavyksta, pamėginkite [[Special:UserLogout|atsijungti]] ir vėl prisijungti, taip pat patikrinkite, ar jūsų naršyklė priima šios svetainės slapukus.",
+       "import-token-mismatch": "Sesijos duomenys prarasti.\n\nGali būti, kad esate atsijungęs. '''Prašome patikrinti, ar vis dar esate prisijungęs, ir pabandyti iš naujo'''.\nJei ir toliau nepavyksta, pamėginkite [[Special:UserLogout|atsijungti]] ir vėl prisijungti, taip pat patikrinkite, ar jūsų naršyklė priima šios svetainės slapukus.",
        "import-invalid-interwiki": "Nepavyko importuoti iš nurodyto wiki projekto.",
        "import-error-edit": "Puslapis \"$1\" nebuvo įkeltas, nes jūs neturite teisės jį redaguoti.",
        "import-error-create": "Puslapis „$1“ nebuvo importuotas, nes jūs neturite teisės jį sukurti.",
index 058eb5b..660dc87 100644 (file)
        "toc": "Isi kandungan",
        "showtoc": "tunjukkan",
        "hidetoc": "sorokkan",
-       "collapsible-collapse": "Lipat",
-       "collapsible-expand": "Kembangkan",
+       "collapsible-collapse": "Tutup",
+       "collapsible-expand": "Buka",
        "confirmable-confirm": "Pastikah {{GENDER:$1|anda}}?",
        "confirmable-yes": "Ya",
        "confirmable-no": "Tidak",
        "botpasswords-created-title": "Kata laluan bot telah dicipta",
        "botpasswords-created-body": "Kata laluan bot untuk nama bot \"$1\" bagi {{GENDER:$2|pengguna}} \"$2\" telah dicipta.",
        "botpasswords-updated-title": "Kata laluan bot telah dikemaskinikan",
-       "botpasswords-updated-body": "Kata laluan bot untuk nama bot \"$1\" bagi {GENDER:$2|pengguna}} \"$2\" telah dikemaskini.",
+       "botpasswords-updated-body": "Kata laluan bot untuk nama bot \"$1\" bagi {{GENDER:$2|pengguna}} \"$2\" telah dikemaskini.",
        "botpasswords-deleted-title": "Kata laluan bot telah dipadam",
        "botpasswords-deleted-body": "Kata laluan bot untuk nama bot \"$1\" bagi {{GENDER:$2|pengguna}} \"$2\" telah dipadam.",
        "botpasswords-newpassword": "Kata laluan baru untuk log masuk dengan <strong>$1</strong> adalah <strong>$2</strong>. <em>Sila catatkan ini untuk rujukan masa depan.</em> <br> (Untuk bot-bot lama yang memerlukan nama log masuk agar sama dengan nama pengguna akhirnya, anda juga boleh menggunakan <strong>$3</strong> sebagai nama pengguna dan <strong>$4</strong> sebagai kata laluan.)",
        "newarticle": "(Baru)",
        "newarticletext": "Anda telah mengikuti pautan ke laman yang belum wujud.\nUntuk mencipta laman ini, sila taip dalam kotak di bawah\n(lihat [$1 laman bantuan] untuk maklumat lanjut).\nJika anda tiba di sini secara tak sengaja, hanya klik butang '''back''' pada pelayar anda.",
        "anontalkpagetext": "<em>Ini ialah laman perbincangan bagi pengguna tanpa nama yang belum membuka akaun atau tidak menggunakannya.</em>\nOleh itu, kami terpaksa menggunakan alamat IP angka untuk mengenal pasti pengguna tersebut. Alamat IP ini boleh dikongsi oleh ramai pengguna.\nSekiranya anda ialah seorang pengguna tanpa nama dan berasa bahawa komen yang tidak relevan telah ditujukan kepada anda, sila [[Special:CreateAccount|buka akaun baru]] atau [[Special:UserLogin|log masuk]] untuk mengelakkan sebarang kekeliruan dengan pengguna tanpa nama yang lain.",
-       "noarticletext": "Laman ini buat masa sekarang tidak berteks. Anda boleh [[Special:Search/{{PAGENAME}}|cari tajuk bagi laman ini]] dalam laman-laman lain, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} cari log-log yang berkaitan], atau [{{fullurl:{{FULLPAGENAME}}|action=edit}} sunting laman ini]</span>.",
+       "noarticletext": "Laman ini tiada teks buat masa sekarang.\nAnda boleh [[Special:Search/{{PAGENAME}}|cari tajuk bagi laman ini]] di laman-laman lain, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} cari log-log yang berkaitan], atau [{{fullurl:{{FULLPAGENAME}}|action=edit}} sunting laman ini]</span>.",
        "noarticletext-nopermission": "Tiada teks dalam laman ini ketika ini.\nAnda boleh [[Special:Search/{{PAGENAME}}|mencari tajuk laman ini]] dalam laman lain,\natau <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} mencari log yang berkaitan]</span>.",
        "missing-revision": "Semakan #$1 pada halaman \"{{FULLPAGENAME}}\" tidak wujud.\n\nHal ini biasanya disebabkan oleh pautan sejarah yang lapuk ke halaman yang sudah dihapuskan.\nButirannya boleh didapati di [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log penghapusan].",
        "userpage-userdoesnotexist": "Akaun pengguna \"<nowiki>$1</nowiki>\" tidak berdaftar. Sila pastikan sama ada anda mahu mencipta/menyunting laman ini.",
        "postedit-confirmation-created": "Halaman telah diwujudkan.",
        "postedit-confirmation-restored": "Halaman telah dipulihkan.",
        "postedit-confirmation-saved": "Suntingan anda telah disimpan.",
+       "postedit-confirmation-published": "Suntingan anda telah disiarkan.",
        "edit-already-exists": "Tidak dapat mencipta laman baru kerana ia telah wujud.",
        "defaultmessagetext": "Teks mesej asal",
        "content-failed-to-parse": "Kandungan $2 tidak dapat dihuraikan untuk model $1: $3",
        "invalid-content-data": "Data kandungan tidak sah",
        "content-not-allowed-here": "Kandungan \"$1\" tidak dibenarkan di halaman [[$2]]",
        "editwarning-warning": "Meninggalkan laman ini mungkin akan menyebabkan sebarang perubahan yang telah anda lakukan hilang.\nJika anda sudah log masuk, anda boleh melumpuhkan amaran ini di bahagian \"{{int:prefs-editing}}\" dalam keutamaan anda.",
+       "editpage-invalidcontentmodel-title": "Model kandungan tidak disokong",
+       "editpage-invalidcontentmodel-text": "Model kandungan \"$1\" tidak disokong.",
        "editpage-notsupportedcontentformat-title": "Format kandungan tidak disokong",
        "editpage-notsupportedcontentformat-text": "Format kandungan $1 tidak disokong oleh model kandungan $2.",
        "content-model-wikitext": "wikiteks",
        "mergehistory-empty": "Tiada semakan yang boleh digabungkan",
        "mergehistory-done": "$3 semakan bagi $1 telah digabungkan ke dalam [[:$2]].",
        "mergehistory-fail": "Gagal melaksanakan penggabungan sejarah, sila semak semula laman tersebut dan parameter waktu.",
+       "mergehistory-fail-bad-timestamp": "Cap masa tidak sah.",
+       "mergehistory-fail-invalid-source": "Halaman asal tidak sah.",
+       "mergehistory-fail-invalid-dest": "Halaman tujuan tidak sah.",
        "mergehistory-fail-toobig": "Tidak dapat melakukan gabungan sejarah sebab lebih daripada had $1 semakan perlu dipindahkan.",
        "mergehistory-no-source": "Laman sumber $1 tidak wujud.",
        "mergehistory-no-destination": "Laman destinasi $1 tidak wujud.",
        "searchprofile-advanced-tooltip": "Cari dalam ruang nama yang tersuai",
        "search-result-size": "$1 ({{PLURAL:$2|$2 patah perkataan}})",
        "search-result-category-size": "$1 {{PLURAL:$1|ahli|ahli}} ($2 {{PLURAL:$2|subkategori|subkategori}}, $3 {{PLURAL:$3|fail|fail}})",
-       "search-redirect": "(pelencongan $1)",
+       "search-redirect": "(lencongan dari $1)",
        "search-section": "(bahagian $1)",
        "search-category": "(kategori $1)",
        "search-file-match": "(sepadan dengan kandungan fail)",
        "search-interwiki-caption": "Hasil dari projek lain",
        "search-interwiki-default": "Hasil dari $1:",
        "search-interwiki-more": "(lagi)",
+       "search-interwiki-more-results": "hasil-hasil selanjutnya",
        "search-relatedarticle": "Berkaitan",
        "searchrelated": "berkaitan",
        "searchall": "semua",
        "username": "{{GENDER:$1|Nama pengguna}}:",
        "prefs-memberingroups": "{{GENDER:$2|Ahli}} {{PLURAL:$1|kumpulan|kumpulan-kumpulan}}:",
        "prefs-memberingroups-type": "$1",
+       "group-membership-link-with-expiry": "$1 (hingga $2)",
        "prefs-registration": "Waktu pendaftaran:",
        "prefs-registration-date-time": "$1",
        "yourrealname": "Nama sebenar:",
        "grant-editmywatchlist": "Sunting senarai pantau anda",
        "grant-editpage": "Sunting laman sedia ada",
        "grant-editprotected": "Sunting laman yang dilindungi",
+       "grant-uploadfile": "Muat naik fail baru",
+       "grant-basic": "Hak-hak asas",
        "newuserlogpage": "Log akaun baru",
        "newuserlogpagetext": "Yang berikut ialah log penciptaan pengguna.",
        "rightslog": "Log hak pengguna",
        "rcfilters-other-review-tools": "Alat semakan lain",
        "rcfilters-activefilters": "Penapis yang aktif",
        "rcfilters-savedqueries-defaultlabel": "Penapis yang disimpan",
+       "rcfilters-savedqueries-setdefault": "Tetapkan sebagai asali",
+       "rcfilters-savedqueries-unsetdefault": "Gugurkan sebagai asali",
+       "rcfilters-savedqueries-remove": "Gugurkan",
+       "rcfilters-savedqueries-new-name-label": "Nama",
        "rcfilters-savedqueries-add-new-title": "Simpan tetapan penapis terkini",
        "rcfilters-filter-humans-label": "Manusia (bukan bot)",
        "rcfilters-filter-pageedits-label": "Suntingan laman",
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 f019346..201ba88 100644 (file)
        "rollback-success": "Откачены правки {{GENDER:$3|$1}}; возврат к версии {{GENDER:$4|$2}}.",
        "rollback-success-notify": "Откачены правки $1; возврат к последней версии $2. [$3 Показать изменения]",
        "sessionfailure-title": "Ошибка сеанса",
-       "sessionfailure": "Ð\9fоÑ\85оже, Ð²Ð¾Ð·Ð½Ð¸ÐºÐ»Ð¸ Ð¿Ñ\80облемÑ\8b Ñ\81 Ñ\82екÑ\83Ñ\89им Ñ\81еанÑ\81ом Ñ\80абоÑ\82Ñ\8b;\nÑ\8dÑ\82о Ð´ÐµÐ¹Ñ\81Ñ\82вие Ð±Ñ\8bло Ð¾Ñ\82менено Ð² Ñ\86елÑ\8fÑ\85 Ð¿Ñ\80едоÑ\82вÑ\80аÑ\89ениÑ\8f Â«Ð·Ð°Ñ\85ваÑ\82а Ñ\81еанÑ\81а».\nÐ\9fожалÑ\83йÑ\81Ñ\82а, Ð½Ð°Ð¶Ð¼Ð¸Ñ\82е ÐºÐ½Ð¾Ð¿ÐºÑ\83 Â«Ð\9dазад» Ð¸ Ð¿ÐµÑ\80езагÑ\80Ñ\83зиÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83, Ñ\81 ÐºÐ¾Ñ\82оÑ\80ой Ð²Ñ\8b Ð¿Ñ\80иÑ\88ли.",
+       "sessionfailure": "Ð\9fоÑ\85оже, Ð²Ð¾Ð·Ð½Ð¸ÐºÐ»Ð¸ Ð¿Ñ\80облемÑ\8b Ñ\81 Ñ\82екÑ\83Ñ\89им Ñ\81еанÑ\81ом Ñ\80абоÑ\82Ñ\8b;\nÑ\8dÑ\82о Ð´ÐµÐ¹Ñ\81Ñ\82вие Ð±Ñ\8bло Ð¾Ñ\82менено Ð² Ñ\86елÑ\8fÑ\85 Ð¿Ñ\80едоÑ\82вÑ\80аÑ\89ениÑ\8f Â«Ð·Ð°Ñ\85ваÑ\82а Ñ\81еанÑ\81а».\nÐ\9fожалÑ\83йÑ\81Ñ\82а, Ð¿ÐµÑ\80еоÑ\82пÑ\80авÑ\8cÑ\82е Ñ\84оÑ\80мÑ\83.",
        "changecontentmodel": "Редактирование контентной модели страницы",
        "changecontentmodel-legend": "Изменить модель содержимого",
        "changecontentmodel-title-label": "Заголовок страницы",
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 fc400c0..9cabb1f 100644 (file)
@@ -33,7 +33,8 @@
                        "Kosovastar",
                        "Matma Rex",
                        "Arianit",
-                       "Denisa"
+                       "Denisa",
+                       "Fanjiayi"
                ]
        },
        "tog-underline": "Nënvizimi i lidhjes:",
        "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",
        "permissionserrorstext": "Nuk keni leje për të bërë këtë veprim për {{PLURAL:$1|këtë arsye|këto arsye}}:",
        "permissionserrorstext-withaction": "Ju nuk keni leje për $2, për {{PLURAL:$1|këtë arsye|këto arsye}}:",
        "recreate-moveddeleted-warn": "'''Kujdes: Po rikrijoni një faqe që është grisur më parë.'''\n\nMendohuni nëse dëshironi të vazhdoni me veprimin tuaj në këtë faqe.\nRegjistri i grisjes për këtë faqe jepet më poshtë:",
-       "moveddeleted-notice": "Kjo faqe është grisur. Të dhënat e grisjes për këtë faqe gjenden më poshtë, për referencë.",
+       "moveddeleted-notice": "Kjo faqe është grisur. Të dhënat, protection e grisjes për këtë faqe gjenden më poshtë, për referencë.",
        "log-fulllog": "Shihe ditaret të plota",
        "edit-hook-aborted": "Redaktimi u ndërpre nga një goditje.\nNuk dha asnjë shpjegim.",
        "edit-gone-missing": "Faqja nuk mund t freskohet.\nDuket se është grisur.",
        "recentchangeslinked-feed": "Ndryshime të ndërvarura",
        "recentchangeslinked-toolbox": "Ndryshime të ndërvarura",
        "recentchangeslinked-title": "Ndryshime që kanë lidhje me \"$1\"",
-       "recentchangeslinked-summary": "Kjo është një listë e ndryshimeve së fundmi të faqeve të lidhura nga faqja e dhënë (ose bëjnë pjesë tek kategoria e dhënë).\nFaqet [[Special:Watchlist|nën mbikqyrjen tuaj]] duken të '''theksuara'''.",
+       "recentchangeslinked-summary": "Kjo është një listë e ndryshimeve së fundmi të faqeve të lidhura nga faqja e dhënë (ose bëjnë pjesë tek kategoria e dhënë).\nFaqet [[Special:Watchlist|nën mbikqyrjen tuaj]] duken të <strong>theksuara</strong>.",
        "recentchangeslinked-page": "Emri i faqes:",
        "recentchangeslinked-to": "Trego ndryshimet e faqeve që lidhen tek faqja e dhënë",
        "recentchanges-page-added-to-category": "[[:$1]] shtuar në kategori",
index 49399a3..0e8ac87 100644 (file)
        "tog-minordefault": "Означавај све измене као мање",
        "tog-previewontop": "Прикажи претпреглед пре оквира за уређивање",
        "tog-previewonfirst": "Прикажи преглед на првој измени",
-       "tog-enotifwatchlistpages": "Пошаљи ми имејл када се измени страница или датотека коју надгледам",
+       "tog-enotifwatchlistpages": "Пошаљи ми имејл када се страница или датотека коју надгледам измени",
        "tog-enotifusertalkpages": "Пошаљи ми имејл када се моја страница за разговор измени",
        "tog-enotifminoredits": "Пошаљи ми имејл и за мање измене страница и датотека",
        "tog-enotifrevealaddr": "Прикажи моју имејл адресу у порукама обавештења",
        "tog-shownumberswatching": "Прикажи број корисника који надгледају",
-       "tog-oldsig": "Ð\92аÑ\88 Ñ\82ренутни потпис:",
+       "tog-oldsig": "Тренутни потпис:",
        "tog-fancysig": "Сматрај потпис као викитекст (без самоповезивања)",
-       "tog-uselivepreview": "Прикажи претпреглед без освежавања стране",
+       "tog-uselivepreview": "Ð\9fÑ\80икажи Ð¿Ñ\80еÑ\82пÑ\80еглед Ð±ÐµÐ· Ð¾Ñ\81вежаваÑ\9aа Ñ\81Ñ\82Ñ\80аниÑ\86е",
        "tog-forceeditsummary": "Упозори ме када не унесем опис измене",
        "tog-watchlisthideown": "Сакриј моје измене са списка надгледања",
        "tog-watchlisthidebots": "Сакриј измене ботова са списка надгледања",
        "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 5f96e7e..6c191b3 100644 (file)
@@ -8,7 +8,8 @@
                        "SF-Language",
                        "Urhixidur",
                        "아라",
-                       "Macofe"
+                       "Macofe",
+                       "Fanjiayi"
                ]
        },
        "tog-underline": "Rëddaatu lëkkalekaay yi :",
        "contributions": "Cëruy bii {{GENDER:$1|jëfandikukat}}",
        "contributions-title": "Cëru yu jëfandikukat bii di $1",
        "mycontris": "Cëru",
+       "anoncontribs": "Cëru",
        "contribsub2": "Ngir $1 ($2)",
        "nocontribs": "Amul benn coppite bu melokaanoo nii bu ñu gis.",
        "uctop": "(bi mujj)",
        "spam_blanking": "Setal nañ wecc sumb yi amoon lëkkalekaay buy jëme $1",
        "simpleantispam-label": "Caytu lànk-spam.\n<strong>Bu</strong> fi yokk lenn!",
        "pageinfo-toolboxlink": "Xibaar ci xët wi",
+       "pageinfo-contentpage-yes": "Waaw",
        "previousdiff": "← Coppite yi gën a yàgg",
        "nextdiff": "Coppite yi mujj →",
        "file-info": "Réyaayu file bi : $1, type MIME : $2",
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 35cd405..e139c3a 100644 (file)
@@ -1765,9 +1765,14 @@ return [
                        'oojs',
                        'mediawiki.api',
                        'mediawiki.api.options',
+                       'mediawiki.jqueryMsg',
                        'mediawiki.Uri',
                        'mediawiki.user',
                ],
+               'messages' => [
+                       'quotation-marks',
+                       'rcfilters-filterlist-title',
+               ],
        ],
        'mediawiki.rcfilters.filters.ui' => [
                'scripts' => [
@@ -1923,6 +1928,7 @@ return [
                'dependencies' => [
                        'oojs-ui-widgets',
                        'jquery.makeCollapsible',
+                       'mediawiki.jqueryMsg',
                        'mediawiki.language',
                        'mediawiki.user',
                        'mediawiki.util',
index 596c118..fe0b029 100644 (file)
@@ -34,7 +34,7 @@ window.mwNow = ( function () {
  * - Firefox 4+
  * - Safari 5+
  * - Opera 15+
- * - Mobile Safari 5.1+ (iOS 5+)
+ * - Mobile Safari 6.0+ (iOS 6+)
  * - Android 4.1+
  *
  * Browsers we support in our no-javascript run-time (Grade C):
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 369aa83..2de35a7 100644 (file)
@@ -6,8 +6,6 @@ use Wikimedia\Rdbms\DatabaseSqlite;
 use Wikimedia\Rdbms\ResultWrapper;
 
 class DatabaseSqliteMock extends DatabaseSqlite {
-       private $lastQuery;
-
        public static function newInstance( array $p = [] ) {
                $p['dbFilePath'] = ':memory:';
                $p['schema'] = false;
@@ -16,8 +14,6 @@ class DatabaseSqliteMock extends DatabaseSqlite {
        }
 
        function query( $sql, $fname = '', $tempIgnore = false ) {
-               $this->lastQuery = $sql;
-
                return true;
        }
 
index fa9898d..606c12d 100644 (file)
@@ -108,7 +108,7 @@ class DatabaseTestHelper extends Database {
 
        public function tableExists( $table, $fname = __METHOD__ ) {
                $tableRaw = $this->tableName( $table, 'raw' );
-               if ( isset( $this->mSessionTempTables[$tableRaw] ) ) {
+               if ( isset( $this->sessionTempTables[$tableRaw] ) ) {
                        return true; // already known to exist
                }
 
index 4097760..2892283 100644 (file)
@@ -194,12 +194,12 @@ class LBFactoryTest extends MediaWikiTestCase {
         * @covers \Wikimedia\Rdbms\ChronologyProtector
         */
        public function testChronologyProtector() {
-               // (a) First HTTP request
-               $m1Pos = new MySQLMasterPos( 'db1034-bin.000976', '843431247' );
-               $m2Pos = new MySQLMasterPos( 'db1064-bin.002400', '794074907' );
-
                $now = microtime( true );
 
+               // (a) First HTTP request
+               $m1Pos = new MySQLMasterPos( 'db1034-bin.000976/843431247', $now );
+               $m2Pos = new MySQLMasterPos( 'db1064-bin.002400/794074907', $now );
+
                // Master DB 1
                $mockDB1 = $this->getMockBuilder( DatabaseMysqli::class )
                        ->disableOriginalConstructor()
index dcdb1cd..b706fac 100644 (file)
@@ -10,8 +10,10 @@ class BadTitleErrorTest extends MediaWikiTestCase {
                try {
                        throw new BadTitleError();
                } catch ( BadTitleError $e ) {
+                       ob_start();
                        $e->report();
-                       $this->assertTrue( true );
+                       $text = ob_get_clean();
+                       $this->assertContains( $e->getText(), $text );
                }
        }
 
index 15f0896..5214b6d 100644 (file)
@@ -11,8 +11,10 @@ class ThrottledErrorTest extends MediaWikiTestCase {
                try {
                        throw new ThrottledError();
                } catch ( ThrottledError $e ) {
+                       ob_start();
                        $e->report();
-                       $this->assertTrue( true );
+                       $text = ob_get_clean();
+                       $this->assertContains( $e->getText(), $text );
                }
        }
 
index caf1281..ce067aa 100644 (file)
@@ -216,17 +216,32 @@ class DatabaseMysqlBaseTest extends PHPUnit_Framework_TestCase {
                        $db->listViews( '' ) );
        }
 
+       public function testBinLogName() {
+               $pos = new MySQLMasterPos( "db1052.2424/4643", 1 );
+
+               $this->assertEquals( "db1052", $pos->binlog );
+               $this->assertEquals( "db1052.2424", $pos->getLogFile() );
+               $this->assertEquals( [ 2424, 4643 ], $pos->pos );
+       }
+
        /**
         * @dataProvider provideComparePositions
         * @covers Wikimedia\Rdbms\MySQLMasterPos
         */
-       public function testHasReached( MySQLMasterPos $lowerPos, MySQLMasterPos $higherPos, $match ) {
+       public function testHasReached(
+               MySQLMasterPos $lowerPos, MySQLMasterPos $higherPos, $match, $hetero
+       ) {
                if ( $match ) {
                        $this->assertTrue( $lowerPos->channelsMatch( $higherPos ) );
 
-                       $this->assertTrue( $higherPos->hasReached( $lowerPos ) );
-                       $this->assertTrue( $higherPos->hasReached( $higherPos ) );
+                       if ( $hetero ) {
+                               // Each position is has one channel higher than the other
+                               $this->assertFalse( $higherPos->hasReached( $lowerPos ) );
+                       } else {
+                               $this->assertTrue( $higherPos->hasReached( $lowerPos ) );
+                       }
                        $this->assertTrue( $lowerPos->hasReached( $lowerPos ) );
+                       $this->assertTrue( $higherPos->hasReached( $higherPos ) );
                        $this->assertFalse( $lowerPos->hasReached( $higherPos ) );
                } else { // channels don't match
                        $this->assertFalse( $lowerPos->channelsMatch( $higherPos ) );
@@ -237,53 +252,100 @@ class DatabaseMysqlBaseTest extends PHPUnit_Framework_TestCase {
        }
 
        public static function provideComparePositions() {
+               $now = microtime( true );
+
                return [
                        // Binlog style
                        [
-                               new MySQLMasterPos( 'db1034-bin.000976', '843431247' ),
-                               new MySQLMasterPos( 'db1034-bin.000976', '843431248' ),
-                               true
+                               new MySQLMasterPos( 'db1034-bin.000976/843431247', $now ),
+                               new MySQLMasterPos( 'db1034-bin.000976/843431248', $now ),
+                               true,
+                               false
                        ],
                        [
-                               new MySQLMasterPos( 'db1034-bin.000976', '999' ),
-                               new MySQLMasterPos( 'db1034-bin.000976', '1000' ),
-                               true
+                               new MySQLMasterPos( 'db1034-bin.000976/999', $now ),
+                               new MySQLMasterPos( 'db1034-bin.000976/1000', $now ),
+                               true,
+                               false
                        ],
                        [
-                               new MySQLMasterPos( 'db1034-bin.000976', '999' ),
-                               new MySQLMasterPos( 'db1035-bin.000976', '1000' ),
+                               new MySQLMasterPos( 'db1034-bin.000976/999', $now ),
+                               new MySQLMasterPos( 'db1035-bin.000976/1000', $now ),
+                               false,
                                false
                        ],
                        // MySQL GTID style
                        [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '3E11FA47-71CA-11E1-9E33-C80AA9429562:23' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '3E11FA47-71CA-11E1-9E33-C80AA9429562:24' ),
-                               true
+                               new MySQLMasterPos( '3E11FA47-71CA-11E1-9E33-C80AA9429562:23', $now ),
+                               new MySQLMasterPos( '3E11FA47-71CA-11E1-9E33-C80AA9429562:24', $now ),
+                               true,
+                               false
                        ],
                        [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '3E11FA47-71CA-11E1-9E33-C80AA9429562:99' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '3E11FA47-71CA-11E1-9E33-C80AA9429562:100' ),
-                               true
+                               new MySQLMasterPos( '3E11FA47-71CA-11E1-9E33-C80AA9429562:99', $now ),
+                               new MySQLMasterPos( '3E11FA47-71CA-11E1-9E33-C80AA9429562:100', $now ),
+                               true,
+                               false
                        ],
                        [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '3E11FA47-71CA-11E1-9E33-C80AA9429562:99' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '1E11FA47-71CA-11E1-9E33-C80AA9429562:100' ),
+                               new MySQLMasterPos( '3E11FA47-71CA-11E1-9E33-C80AA9429562:99', $now ),
+                               new MySQLMasterPos( '1E11FA47-71CA-11E1-9E33-C80AA9429562:100', $now ),
+                               false,
                                false
                        ],
                        // MariaDB GTID style
                        [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '255-11-23' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '255-11-24' ),
-                               true
+                               new MySQLMasterPos( '255-11-23', $now ),
+                               new MySQLMasterPos( '255-11-24', $now ),
+                               true,
+                               false
+                       ],
+                       [
+                               new MySQLMasterPos( '255-11-99', $now ),
+                               new MySQLMasterPos( '255-11-100', $now ),
+                               true,
+                               false
+                       ],
+                       [
+                               new MySQLMasterPos( '255-11-999', $now ),
+                               new MySQLMasterPos( '254-11-1000', $now ),
+                               false,
+                               false
+                       ],
+                       [
+                               new MySQLMasterPos( '255-11-23,256-12-50', $now ),
+                               new MySQLMasterPos( '255-11-24', $now ),
+                               true,
+                               false
+                       ],
+                       [
+                               new MySQLMasterPos( '255-11-99,256-12-50,257-12-50', $now ),
+                               new MySQLMasterPos( '255-11-1000', $now ),
+                               true,
+                               false
                        ],
                        [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '255-11-99' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '255-11-100' ),
+                               new MySQLMasterPos( '255-11-23,256-12-50', $now ),
+                               new MySQLMasterPos( '255-11-24,155-52-63', $now ),
+                               true,
+                               false
+                       ],
+                       [
+                               new MySQLMasterPos( '255-11-99,256-12-50,257-12-50', $now ),
+                               new MySQLMasterPos( '255-11-1000,256-12-51', $now ),
+                               true,
+                               false
+                       ],
+                       [
+                               new MySQLMasterPos( '255-11-99,256-12-50', $now ),
+                               new MySQLMasterPos( '255-13-1000,256-14-49', $now ),
+                               true,
                                true
                        ],
                        [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '255-11-999' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '254-11-1000' ),
+                               new MySQLMasterPos( '253-11-999,255-11-999', $now ),
+                               new MySQLMasterPos( '254-11-1000', $now ),
+                               false,
                                false
                        ],
                ];
@@ -296,28 +358,33 @@ class DatabaseMysqlBaseTest extends PHPUnit_Framework_TestCase {
        public function testChannelsMatch( MySQLMasterPos $pos1, MySQLMasterPos $pos2, $matches ) {
                $this->assertEquals( $matches, $pos1->channelsMatch( $pos2 ) );
                $this->assertEquals( $matches, $pos2->channelsMatch( $pos1 ) );
+
+               $roundtripPos = new MySQLMasterPos( (string)$pos1, 1 );
+               $this->assertEquals( (string)$pos1, (string)$roundtripPos );
        }
 
        public static function provideChannelPositions() {
+               $now = microtime( true );
+
                return [
                        [
-                               new MySQLMasterPos( 'db1034-bin.000876', '44' ),
-                               new MySQLMasterPos( 'db1034-bin.000976', '74' ),
+                               new MySQLMasterPos( 'db1034-bin.000876/44', $now ),
+                               new MySQLMasterPos( 'db1034-bin.000976/74', $now ),
                                true
                        ],
                        [
-                               new MySQLMasterPos( 'db1052-bin.000976', '999' ),
-                               new MySQLMasterPos( 'db1052-bin.000976', '1000' ),
+                               new MySQLMasterPos( 'db1052-bin.000976/999', $now ),
+                               new MySQLMasterPos( 'db1052-bin.000976/1000', $now ),
                                true
                        ],
                        [
-                               new MySQLMasterPos( 'db1066-bin.000976', '9999' ),
-                               new MySQLMasterPos( 'db1035-bin.000976', '10000' ),
+                               new MySQLMasterPos( 'db1066-bin.000976/9999', $now ),
+                               new MySQLMasterPos( 'db1035-bin.000976/10000', $now ),
                                false
                        ],
                        [
-                               new MySQLMasterPos( 'db1066-bin.000976', '9999' ),
-                               new MySQLMasterPos( 'trump2016.000976', '10000' ),
+                               new MySQLMasterPos( 'db1066-bin.000976/9999', $now ),
+                               new MySQLMasterPos( 'trump2016.000976/10000', $now ),
                                false
                        ],
                ];
index e131506..b42a367 100644 (file)
@@ -370,10 +370,34 @@ class DatabaseTest extends PHPUnit_Framework_TestCase {
                $this->assertFalse( (bool)$db->trxLevel(), "Transaction cleared." );
        }
 
+       /**
+        * @covers Wikimedia\Rdbms\Database::getScopedLockAndFlush
+        * @covers Wikimedia\Rdbms\Database::lock
+        * @covers Wikimedia\Rdbms\Database::unlock
+        * @covers Wikimedia\Rdbms\Database::lockIsFree
+        */
        public function testGetScopedLock() {
                $db = $this->getMockDB( [ 'isOpen' ] );
                $db->method( 'isOpen' )->willReturn( true );
 
+               $this->assertEquals( 0, $db->trxLevel() );
+               $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
+               $this->assertEquals( true, $db->lock( 'x', __METHOD__ ) );
+               $this->assertEquals( false, $db->lockIsFree( 'x', __METHOD__ ) );
+               $this->assertEquals( true, $db->unlock( 'x', __METHOD__ ) );
+               $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
+               $this->assertEquals( 0, $db->trxLevel() );
+
+               $db->setFlag( DBO_TRX );
+               $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
+               $this->assertEquals( true, $db->lock( 'x', __METHOD__ ) );
+               $this->assertEquals( false, $db->lockIsFree( 'x', __METHOD__ ) );
+               $this->assertEquals( true, $db->unlock( 'x', __METHOD__ ) );
+               $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
+               $db->clearFlag( DBO_TRX );
+
+               $this->assertEquals( 0, $db->trxLevel() );
+
                $db->setFlag( DBO_TRX );
                try {
                        $this->badLockingMethodImplicit( $db );
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,
                ],
        ];