Merge "Update OOUI to v0.27.1"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 31 May 2018 08:48:56 +0000 (08:48 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 31 May 2018 08:48:56 +0000 (08:48 +0000)
62 files changed:
.editorconfig [new file with mode: 0644]
.phpcs.xml
RELEASE-NOTES-1.32
autoload.php
includes/Message.php
includes/OutputPage.php
includes/Storage/RevisionRecord.php
includes/api/ApiResult.php
includes/api/ApiUsageException.php
includes/api/UsageException.php [new file with mode: 0644]
includes/dao/IDBAccessObject.php
includes/diff/DiffEngine.php
includes/http/PhpHttpRequest.php
includes/installer/i18n/pt-br.json
includes/installer/i18n/yi.json
includes/json/FormatJson.php
includes/libs/IP.php
includes/libs/filebackend/FileBackendStore.php
includes/libs/rdbms/database/Database.php
includes/logging/LogPage.php
includes/media/FormatMetadata.php
includes/media/GIFMetadataExtractor.php
includes/parser/Preprocessor.php
includes/preferences/DefaultPreferencesFactory.php
includes/registration/ExtensionRegistry.php
includes/resourceloader/ResourceLoaderLessVarFileModule.php
includes/session/PHPSessionHandler.php
includes/specials/SpecialPreferences.php
includes/utils/UIDGenerator.php
includes/utils/ZipDirectoryReader.php
languages/i18n/as.json
languages/i18n/be-tarask.json
languages/i18n/btm.json
languages/i18n/diq.json
languages/i18n/he.json
languages/i18n/hu.json
languages/i18n/inh.json
languages/i18n/lez.json
languages/i18n/lfn.json
languages/i18n/oc.json
languages/i18n/tr.json
languages/i18n/uk.json
languages/i18n/yi.json
languages/i18n/zh-hant.json
maintenance/generateSitemap.php
maintenance/storage/storageTypeStats.php
resources/Resources.php
resources/src/jquery/jquery.lengthLimit.js
resources/src/mediawiki/mediawiki.base.js [new file with mode: 0644]
resources/src/mediawiki/mediawiki.js
tests/phpunit/data/registration/autoload_namespaces.json [new file with mode: 0644]
tests/phpunit/includes/api/ApiEditPageTest.php
tests/phpunit/includes/api/RandomImageGenerator.php
tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php
tests/phpunit/includes/registration/ExtensionRegistryTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
tests/phpunit/languages/LanguageTest.php
tests/phpunit/maintenance/deleteAutoPatrolLogsTest.php
tests/phpunit/suites/ParserTestTopLevelSuite.php
tests/qunit/QUnitTestResources.php
tests/qunit/suites/resources/mediawiki.widgets/MediaSearch/mediawiki.widgets.APIResultsQueue.test.js [new file with mode: 0644]

diff --git a/.editorconfig b/.editorconfig
new file mode 100644 (file)
index 0000000..9231818
--- /dev/null
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+indent_style = tab
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
index 69d788d..14bb494 100644 (file)
@@ -72,7 +72,6 @@
                        Whitelist existing violations, but enable the sniff to prevent
                        any new occurrences.
                -->
-               <exclude-pattern>*/includes/api/ApiUsageException\.php</exclude-pattern>
                <exclude-pattern>*/includes/media/XCF\.php</exclude-pattern>
                <exclude-pattern>*/includes/Feed\.php</exclude-pattern>
                <exclude-pattern>*/includes/libs/xmp/XMP\.php</exclude-pattern>
index 3d193d4..13d15c2 100644 (file)
@@ -126,6 +126,9 @@ because of Phabricator reports.
 * (T140807) The wgResourceLoaderLESSImportPaths configuration option was removed
   from ResourceLoader. Instead, use `@import` statements in LESS to import
   files directly from nearby directories within the same project.
+* The protected methods PHPSessionHandler::returnSuccess() and returnFailure(),
+  only needed for PHP5 compatibility, have been removed. It now uses the boolean
+  values `true` and `false` respectively.
 
 === Deprecations in 1.32 ===
 * Use of a StartProfiler.php file is deprecated in favour of placing
index 138a9b9..77144df 100644 (file)
@@ -1537,7 +1537,7 @@ $wgAutoloadLocalClasses = [
        'UploadStashWrongOwnerException' => __DIR__ . '/includes/upload/UploadStash.php',
        'UploadStashZeroLengthFileException' => __DIR__ . '/includes/upload/UploadStash.php',
        'UppercaseCollation' => __DIR__ . '/includes/collation/UppercaseCollation.php',
-       'UsageException' => __DIR__ . '/includes/api/ApiUsageException.php',
+       'UsageException' => __DIR__ . '/includes/api/UsageException.php',
        'User' => __DIR__ . '/includes/user/User.php',
        'UserArray' => __DIR__ . '/includes/user/UserArray.php',
        'UserArrayFromResult' => __DIR__ . '/includes/user/UserArrayFromResult.php',
index 7d05f41..fb6dcc5 100644 (file)
@@ -726,6 +726,8 @@ class Message implements MessageSpecifier, Serializable {
         * @throws MWException
         */
        public function inLanguage( $lang ) {
+               $previousLanguage = $this->language;
+
                if ( $lang instanceof Language ) {
                        $this->language = $lang;
                } elseif ( is_string( $lang ) ) {
@@ -740,7 +742,11 @@ class Message implements MessageSpecifier, Serializable {
                                . "passed a String or Language object; $type given"
                        );
                }
-               $this->message = null;
+
+               if ( $this->language !== $previousLanguage ) {
+                       // The language has changed. Clear the message cache.
+                       $this->message = null;
+               }
                $this->interface = false;
                return $this;
        }
index 7f72d36..564641a 100644 (file)
@@ -2413,10 +2413,6 @@ class OutputPage extends ContextSource {
                $response->header( 'Content-type: ' . $config->get( 'MimeType' ) . '; charset=UTF-8' );
                $response->header( 'Content-language: ' . $wgContLang->getHtmlCode() );
 
-               // Avoid Internet Explorer "compatibility view" in IE 8-10, so that
-               // jQuery etc. can work correctly.
-               $response->header( 'X-UA-Compatible: IE=Edge' );
-
                if ( !$this->mArticleBodyOnly ) {
                        $sk = $this->getSkin();
 
index 6d83e1c..ff0a70d 100644 (file)
@@ -48,8 +48,9 @@ abstract class RevisionRecord {
        const DELETED_COMMENT = 2;
        const DELETED_USER = 4;
        const DELETED_RESTRICTED = 8;
-       const SUPPRESSED_USER = 12; // convenience
-       const SUPPRESSED_ALL = 15; // convenience
+       const SUPPRESSED_USER = self::DELETED_USER | self::DELETED_RESTRICTED; // convenience
+       const SUPPRESSED_ALL = self::DELETED_TEXT | self::DELETED_COMMENT | self::DELETED_USER |
+               self::DELETED_RESTRICTED; // convenience
 
        // Audience options for accessors
        const FOR_PUBLIC = 1;
index 468d878..1afacaf 100644 (file)
@@ -61,7 +61,7 @@ class ApiResult implements ApiSerializable {
         * probably wrong.
         * @since 1.25
         */
-       const NO_VALIDATE = 12;
+       const NO_VALIDATE = self::NO_SIZE_CHECK | 8;
 
        /**
         * Key for the 'indexed tag name' metadata item. Value is string.
index c200dcb..7f8a26b 100644 (file)
  * @file
  */
 
-/**
- * This exception will be thrown when dieUsage is called to stop module execution.
- *
- * @ingroup API
- * @deprecated since 1.29, use ApiUsageException instead
- */
-class UsageException extends MWException {
-
-       private $mCodestr;
-
-       /**
-        * @var null|array
-        */
-       private $mExtraData;
-
-       /**
-        * @param string $message
-        * @param string $codestr
-        * @param int $code
-        * @param array|null $extradata
-        */
-       public function __construct( $message, $codestr, $code = 0, $extradata = null ) {
-               parent::__construct( $message, $code );
-               $this->mCodestr = $codestr;
-               $this->mExtraData = $extradata;
-
-               if ( !$this instanceof ApiUsageException ) {
-                       wfDeprecated( __METHOD__, '1.29' );
-               }
-
-               // This should never happen, so throw an exception about it that will
-               // hopefully get logged with a backtrace (T138585)
-               if ( !is_string( $codestr ) || $codestr === '' ) {
-                       throw new InvalidArgumentException( 'Invalid $codestr, was ' .
-                               ( $codestr === '' ? 'empty string' : gettype( $codestr ) )
-                       );
-               }
-       }
-
-       /**
-        * @return string
-        */
-       public function getCodeString() {
-               wfDeprecated( __METHOD__, '1.29' );
-               return $this->mCodestr;
-       }
-
-       /**
-        * @return array
-        */
-       public function getMessageArray() {
-               wfDeprecated( __METHOD__, '1.29' );
-               $result = [
-                       'code' => $this->mCodestr,
-                       'info' => $this->getMessage()
-               ];
-               if ( is_array( $this->mExtraData ) ) {
-                       $result = array_merge( $result, $this->mExtraData );
-               }
-
-               return $result;
-       }
-
-       /**
-        * @return string
-        */
-       public function __toString() {
-               return "{$this->getCodeString()}: {$this->getMessage()}";
-       }
-}
-
 /**
  * Exception used to abort API execution with an error
  *
diff --git a/includes/api/UsageException.php b/includes/api/UsageException.php
new file mode 100644 (file)
index 0000000..426ce91
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+/**
+ * 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 2 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
+ */
+
+/**
+ * This exception will be thrown when dieUsage is called to stop module execution.
+ *
+ * @ingroup API
+ * @deprecated since 1.29, use ApiUsageException instead
+ */
+class UsageException extends MWException {
+
+       private $mCodestr;
+
+       /**
+        * @var null|array
+        */
+       private $mExtraData;
+
+       /**
+        * @param string $message
+        * @param string $codestr
+        * @param int $code
+        * @param array|null $extradata
+        */
+       public function __construct( $message, $codestr, $code = 0, $extradata = null ) {
+               parent::__construct( $message, $code );
+               $this->mCodestr = $codestr;
+               $this->mExtraData = $extradata;
+
+               if ( !$this instanceof ApiUsageException ) {
+                       wfDeprecated( __METHOD__, '1.29' );
+               }
+
+               // This should never happen, so throw an exception about it that will
+               // hopefully get logged with a backtrace (T138585)
+               if ( !is_string( $codestr ) || $codestr === '' ) {
+                       throw new InvalidArgumentException( 'Invalid $codestr, was ' .
+                               ( $codestr === '' ? 'empty string' : gettype( $codestr ) )
+                       );
+               }
+       }
+
+       /**
+        * @return string
+        */
+       public function getCodeString() {
+               wfDeprecated( __METHOD__, '1.29' );
+               return $this->mCodestr;
+       }
+
+       /**
+        * @return array
+        */
+       public function getMessageArray() {
+               wfDeprecated( __METHOD__, '1.29' );
+               $result = [
+                       'code' => $this->mCodestr,
+                       'info' => $this->getMessage()
+               ];
+               if ( is_array( $this->mExtraData ) ) {
+                       $result = array_merge( $result, $this->mExtraData );
+               }
+
+               return $result;
+       }
+
+       /**
+        * @return string
+        */
+       public function __toString() {
+               return "{$this->getCodeString()}: {$this->getMessage()}";
+       }
+}
index e18a090..a555c55 100644 (file)
@@ -59,9 +59,9 @@ interface IDBAccessObject {
        /** @var int Read from the master/quorum */
        const READ_LATEST = 1;
        /* @var int Read from the master/quorum and lock out other writers */
-       const READ_LOCKING = 3; // READ_LATEST (1) and "LOCK IN SHARE MODE" (2)
+       const READ_LOCKING = self::READ_LATEST | 2; // READ_LATEST (1) and "LOCK IN SHARE MODE" (2)
        /** @var int Read from the master/quorum and lock out other writers and locking readers */
-       const READ_EXCLUSIVE = 7; // READ_LOCKING (3) and "FOR UPDATE" (4)
+       const READ_EXCLUSIVE = self::READ_LOCKING | 4; // READ_LOCKING (3) and "FOR UPDATE" (4)
 
        /** @var int Read from a replica DB or without a quorum, using the master/quorum on miss */
        const READ_LATEST_IMMUTABLE = 8;
index 273d1d6..142c51d 100644 (file)
@@ -351,7 +351,7 @@ class DiffEngine {
                        $this->maxDifferences = ceil( ( $this->m + $this->n ) / 2.0 );
                        if ( $this->m * $this->n > $this->tooLong ) {
                                // limit complexity to D^POW_LIMIT for long sequences
-                               $this->maxDifferences = floor( pow( $this->maxDifferences, $this->powLimit - 1.0 ) );
+                               $this->maxDifferences = floor( $this->maxDifferences ** ( $this->powLimit - 1.0 ) );
                                wfDebug( "Limiting max number of differences to $this->maxDifferences\n" );
                        }
 
index 0f499c2..30ab181 100644 (file)
@@ -137,13 +137,7 @@ class PhpHttpRequest extends MWHttpRequest {
                }
 
                if ( $this->sslVerifyHost ) {
-                       // PHP 5.6.0 deprecates CN_match, in favour of peer_name which
-                       // actually checks SubjectAltName properly.
-                       if ( version_compare( PHP_VERSION, '5.6.0', '>=' ) ) {
-                               $options['ssl']['peer_name'] = $this->parsedUrl['host'];
-                       } else {
-                               $options['ssl']['CN_match'] = $this->parsedUrl['host'];
-                       }
+                       $options['ssl']['peer_name'] = $this->parsedUrl['host'];
                }
 
                $options['ssl'] += $this->getCertOptions();
@@ -169,19 +163,6 @@ class PhpHttpRequest extends MWHttpRequest {
                        restore_error_handler();
 
                        if ( !$fh ) {
-                               // HACK for instant commons.
-                               // If we are contacting (commons|upload).wikimedia.org
-                               // try again with CN_match for en.wikipedia.org
-                               // as php does not handle SubjectAltName properly
-                               // prior to "peer_name" option in php 5.6
-                               if ( isset( $options['ssl']['CN_match'] )
-                                       && ( $options['ssl']['CN_match'] === 'commons.wikimedia.org'
-                                               || $options['ssl']['CN_match'] === 'upload.wikimedia.org' )
-                               ) {
-                                       $options['ssl']['CN_match'] = 'en.wikipedia.org';
-                                       $context = stream_context_create( $options );
-                                       continue;
-                               }
                                break;
                        }
 
index bc3abc8..3eeb672 100644 (file)
        "config-type-oracle": "Oracle",
        "config-type-mssql": "Microsoft SQL Server",
        "config-support-info": "O MediaWiki suporta os sistemas de banco de dados a seguir:\n\n$1\n\nSe você não vê o sistema de banco de dados que você está tentando usar listados abaixo, siga as instruções relacionadas acima, para ativar o suporte.",
-       "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] é o principal alvo para o MediaWiki e é melhor suportado. O MediaWiki também funciona com [{{int:version-db-mariadb-url}} MariaDB] e [{{int:version-db-percona-url}} Percona Server], que são compatíveis com MySQL. ([Http://www.php.net/manual/en/mysqli.installation.php Como compilar PHP com suporte a MySQL])",
+       "config-dbsupport-mysql": "* O [{{int:version-db-mysql-url}} MySQL] é a base de dados preferida para o MediaWiki e a melhor suportada. O MediaWiki também trabalha com [{{int:version-db-mariadb-url}} MariaDB] e [{{int:version-db-percona-url}} Percona Server], que são compatíveis com MySQL. ([https://secure.php.net/manual/en/mysqli.installation.php Como compilar PHP com suporte para MySQL].)",
        "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] é um popular sistema de banco de dados de código aberto como uma alternativa para o MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Como compilar o PHP com suporte PostgreSQL])",
        "config-dbsupport-sqlite": "* O [{{int:version-db-sqlite-url}} SQLite] é uma plataforma de base de dados ligeira muito bem suportada. ([https://secure.php.net/manual/en/pdo.installation.php Como compilar PHP com suporte para SQLite], usa PDO.)",
        "config-dbsupport-oracle": "* A [{{int:version-db-oracle-url}} Oracle] é uma base de dados comercial para empresas. ([https://secure.php.net/manual/en/oci8.installation.php Como compilar PHP com suporte para OCI8].)",
index f85ef6e..c4892e7 100644 (file)
@@ -39,8 +39,8 @@
        "config-env-bad": "מ'האט קאנטראלירט די סביבה.\nאיר קענט נישט אינסטאלירן מעדיעוויקי.",
        "config-env-php": "PHP $1 איז אינצטאלירט.",
        "config-env-hhvm": "HHVM $1 איז אינסטאלירט.",
-       "config-apc": "[https://secure.php.net/apc APC] איז אינסטאלירט",
-       "config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] איז אינסטאלירט",
+       "config-apc": "[https://secure.php.net/apc APC] איז אינסטאַלירט",
+       "config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] איז אינסטאַלירט",
        "config-diff3-bad": "GNU diff3 נישט געטראפן.",
        "config-using-server": "באניצן סארווער־נאמען \"<nowiki>$1</nowiki>\".",
        "config-using-uri": "באניצן סארווער־אדרעס \"<nowiki>$1$2</nowiki>\".",
index 0c77a7b..acbbf26 100644 (file)
@@ -77,17 +77,6 @@ class FormatJson {
         */
        const STRIP_COMMENTS = 0x400;
 
-       /**
-        * Regex that matches whitespace inside empty arrays and objects.
-        *
-        * This doesn't affect regular strings inside the JSON because those can't
-        * have a real line break (\n) in them, at this point they are already escaped
-        * as the string "\n" which this doesn't match.
-        *
-        * @private
-        */
-       const WS_CLEANUP_REGEX = '/(?<=[\[{])\n\s*+(?=[\]}])/';
-
        /**
         * Characters problematic in JavaScript.
         *
@@ -129,11 +118,6 @@ class FormatJson {
                        $pretty = $pretty ? '    ' : false;
                }
 
-               static $bug66021;
-               if ( $pretty !== false && $bug66021 === null ) {
-                       $bug66021 = json_encode( [], JSON_PRETTY_PRINT ) !== '[]';
-               }
-
                // PHP escapes '/' to prevent breaking out of inline script blocks using '</script>',
                // which is hardly useful when '<' and '>' are escaped (and inadequate), and such
                // escaping negatively impacts the human readability of URLs and similar strings.
@@ -147,10 +131,6 @@ class FormatJson {
                }
 
                if ( $pretty !== false ) {
-                       // Workaround for <https://bugs.php.net/bug.php?id=66021>
-                       if ( $bug66021 ) {
-                               $json = preg_replace( self::WS_CLEANUP_REGEX, '', $json );
-                       }
                        if ( $pretty !== '    ' ) {
                                // Change the four-space indent to a tab indent
                                $json = str_replace( "\n    ", "\n\t", $json );
index 06589d2..8efcd15 100644 (file)
@@ -425,7 +425,7 @@ class IP {
                        $ip = self::sanitizeIP( $ip );
                        $n = ip2long( $ip );
                        if ( $n < 0 ) {
-                               $n += pow( 2, 32 );
+                               $n += 2 ** 32;
                                # On 32-bit platforms (and on Windows), 2^32 does not fit into an int,
                                # so $n becomes a float. We convert it to string instead.
                                if ( is_float( $n ) ) {
@@ -487,7 +487,7 @@ class IP {
                        }
                        # Convert to unsigned
                        if ( $network < 0 ) {
-                               $network += pow( 2, 32 );
+                               $network += 2 ** 32;
                        }
                } else {
                        $network = false;
@@ -523,7 +523,7 @@ class IP {
                                $start = $end = false;
                        } else {
                                $start = sprintf( '%08X', $network );
-                               $end = sprintf( '%08X', $network + pow( 2, ( 32 - $bits ) ) - 1 );
+                               $end = sprintf( '%08X', $network + 2 ** ( 32 - $bits ) - 1 );
                        }
                // Explicit range
                } elseif ( strpos( $range, '-' ) !== false ) {
index 06b263a..16ea3ea 100644 (file)
@@ -1560,7 +1560,7 @@ abstract class FileBackendStore extends FileBackend {
                $shards = [];
                list( $digits, $base ) = $this->getContainerHashLevels( $container );
                if ( $digits > 0 ) {
-                       $numShards = pow( $base, $digits );
+                       $numShards = $base ** $digits;
                        for ( $index = 0; $index < $numShards; $index++ ) {
                                $shards[] = '.' . Wikimedia\base_convert( $index, 10, $base, $digits );
                        }
index d1230e0..1f92c47 100644 (file)
@@ -1169,27 +1169,21 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
                if ( $ret === false ) {
                        if ( $this->trxLevel ) {
-                               if ( !$this->wasKnownStatementRollbackError() ) {
-                                       # Either the query was aborted or all queries after BEGIN where aborted.
-                                       if ( $this->explicitTrxActive() || $priorWritesPending ) {
-                                               # In the first case, the only options going forward are (a) ROLLBACK, or
-                                               # (b) ROLLBACK TO SAVEPOINT (if one was set). If the later case, the only
-                                               # option is ROLLBACK, since the snapshots would have been released.
-                                               $this->trxStatus = self::STATUS_TRX_ERROR;
-                                               $this->trxStatusCause =
-                                                       $this->makeQueryException( $lastError, $lastErrno, $sql, $fname );
-                                               $tempIgnore = false; // cannot recover
-                                       } else {
-                                               # Nothing prior was there to lose from the transaction,
-                                               # so just roll it back.
-                                               $this->rollback( __METHOD__ . " ($fname)", self::FLUSHING_INTERNAL );
-                                       }
-                                       $this->trxStatusIgnoredCause = null;
-                               } else {
+                               if ( $this->wasKnownStatementRollbackError() ) {
                                        # We're ignoring an error that caused just the current query to be aborted.
-                                       # But log the cause so we can log a deprecation notice if a
-                                       # caller actually does ignore it.
+                                       # But log the cause so we can log a deprecation notice if a caller actually
+                                       # does ignore it.
                                        $this->trxStatusIgnoredCause = [ $lastError, $lastErrno, $fname ];
+                               } else {
+                                       # Either the query was aborted or all queries after BEGIN where aborted.
+                                       # In the first case, the only options going forward are (a) ROLLBACK, or
+                                       # (b) ROLLBACK TO SAVEPOINT (if one was set). If the later case, the only
+                                       # option is ROLLBACK, since the snapshots would have been released.
+                                       $this->trxStatus = self::STATUS_TRX_ERROR;
+                                       $this->trxStatusCause =
+                                               $this->makeQueryException( $lastError, $lastErrno, $sql, $fname );
+                                       $tempIgnore = false; // cannot recover
+                                       $this->trxStatusIgnoredCause = null;
                                }
                        }
 
@@ -3448,16 +3442,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        $this->trxIdleCallbacks = []; // consumed (and recursion guard)
                        $this->trxEndCallbacks = []; // consumed (recursion guard)
                        foreach ( $callbacks as $callback ) {
+                               ++$count;
+                               list( $phpCallback ) = $callback;
+                               $this->clearFlag( self::DBO_TRX ); // make each query its own transaction
                                try {
-                                       ++$count;
-                                       list( $phpCallback ) = $callback;
-                                       $this->clearFlag( self::DBO_TRX ); // make each query its own transaction
                                        call_user_func( $phpCallback, $trigger, $this );
-                                       if ( $autoTrx ) {
-                                               $this->setFlag( self::DBO_TRX ); // restore automatic begin()
-                                       } else {
-                                               $this->clearFlag( self::DBO_TRX ); // restore auto-commit
-                                       }
                                } catch ( Exception $ex ) {
                                        call_user_func( $this->errorLogger, $ex );
                                        $e = $e ?: $ex;
@@ -3466,6 +3455,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                        if ( $this->trxLevel() ) {
                                                $this->rollback( __METHOD__, self::FLUSHING_INTERNAL );
                                        }
+                               } finally {
+                                       if ( $autoTrx ) {
+                                               $this->setFlag( self::DBO_TRX ); // restore automatic begin()
+                                       } else {
+                                               $this->clearFlag( self::DBO_TRX ); // restore auto-commit
+                                       }
                                }
                        }
                } while ( count( $this->trxIdleCallbacks ) );
index 9f34264..265a41c 100644 (file)
@@ -37,8 +37,8 @@ class LogPage {
        const DELETED_RESTRICTED = 8;
 
        // Convenience fields
-       const SUPPRESSED_USER = 12;
-       const SUPPRESSED_ACTION = 9;
+       const SUPPRESSED_USER = self::DELETED_USER | self::DELETED_RESTRICTED;
+       const SUPPRESSED_ACTION = self::DELETED_ACTION | self::DELETED_RESTRICTED;
 
        /** @var bool */
        public $updateRecentChanges;
index 2a8b375..52d7373 100644 (file)
@@ -787,7 +787,7 @@ class FormatMetadata extends ContextSource {
                                                        }
                                                }
                                                if ( is_numeric( $val ) ) {
-                                                       $fNumber = pow( 2, $val / 2 );
+                                                       $fNumber = 2 ** ( $val / 2 );
                                                        if ( $fNumber !== false ) {
                                                                $val = $this->msg( 'exif-maxaperturevalue-value',
                                                                        $this->formatNum( $val ),
index a26539a..591ccf1 100644 (file)
@@ -264,7 +264,7 @@ class GIFMetadataExtractor {
         */
        static function readGCT( $fh, $bpp ) {
                if ( $bpp > 0 ) {
-                       $max = pow( 2, $bpp );
+                       $max = 2 ** $bpp;
                        for ( $i = 1; $i <= $max; ++$i ) {
                                fread( $fh, 3 );
                        }
index 49e961a..b6084d8 100644 (file)
@@ -171,7 +171,8 @@ interface PPFrame {
        const RECOVER_COMMENTS = 16;
        const NO_TAGS = 32;
 
-       const RECOVER_ORIG = 59; // = 1|2|8|16|32 no constant expression support in PHP yet
+       const RECOVER_ORIG = self::NO_ARGS | self::NO_TEMPLATES | self::NO_IGNORE |
+               self::RECOVER_COMMENTS | self::NO_TAGS;
 
        /** This constant exists when $indexOffset is supported in newChild() */
        const SUPPORTS_INDEX_OFFSET = 1;
index eb94ff1..62973d9 100644 (file)
@@ -1723,9 +1723,8 @@ class DefaultPreferencesFactory implements PreferencesFactory {
        protected function submitForm( array $formData, HTMLForm $form ) {
                $res = $this->saveFormData( $formData, $form );
 
-               if ( $res ) {
+               if ( $res === true ) {
                        $context = $form->getContext();
-
                        $urlOptions = [];
 
                        if ( $res === 'eauth' ) {
@@ -1749,7 +1748,7 @@ class DefaultPreferencesFactory implements PreferencesFactory {
                        $context->getOutput()->redirect( $url );
                }
 
-               return Status::newGood();
+               return ( $res === true ? Status::newGood() : $res );
        }
 
        /**
index b34a123..c91d6d6 100644 (file)
@@ -247,6 +247,7 @@ class ExtensionRegistry {
                        }
                        if ( isset( $info['AutoloadNamespaces'] ) ) {
                                $autoloadNamespaces += $this->processAutoLoader( $dir, $info['AutoloadNamespaces'] );
+                               AutoLoader::$psr4Namespaces += $autoloadNamespaces;
                        }
 
                        // get all requirements/dependencies for this extension
index 17d00e0..c4e517a 100644 (file)
@@ -4,10 +4,21 @@
  * Subclass with context specific LESS variables
  */
 class ResourceLoaderLessVarFileModule extends ResourceLoaderFileModule {
-       protected $lessVariables = [
-               'collapsible-collapse',
-               'collapsible-expand',
-       ];
+       protected $lessVariables = [];
+
+       /**
+        * @inheritDoc
+        */
+       public function __construct(
+               $options = [],
+               $localBasePath = null,
+               $remoteBasePath = null
+       ) {
+               if ( isset( $options['lessMessages'] ) ) {
+                       $this->lessVariables = $options['lessMessages'];
+               }
+               parent::__construct( $options, $localBasePath, $remoteBasePath );
+       }
 
        /**
         * @inheritDoc
@@ -19,6 +30,7 @@ class ResourceLoaderLessVarFileModule extends ResourceLoaderFileModule {
 
        /**
         * Exclude a set of messages from a JSON string representation
+        *
         * @param string $blob
         * @param array $exclusions
         * @return array $blob
@@ -29,7 +41,7 @@ class ResourceLoaderLessVarFileModule extends ResourceLoaderFileModule {
                foreach ( $exclusions as $key ) {
                        unset( $data[$key] );
                }
-               return $data;
+               return (object)$data;
        }
 
        /**
@@ -45,6 +57,7 @@ class ResourceLoaderLessVarFileModule extends ResourceLoaderFileModule {
         * (ModifyVars) method so that the variable can be loaded and made available to stylesheets.
         * Note this does not take care of CSS escaping. That will be taken care of as part
         * of CSS Janus.
+        *
         * @param string $msg
         * @return string wrapped LESS variable definition
         */
@@ -53,14 +66,16 @@ class ResourceLoaderLessVarFileModule extends ResourceLoaderFileModule {
        }
 
        /**
-        * @param \ResourceLoaderContext $context
+        * Get language-specific LESS variables for this module.
+        *
+        * @param ResourceLoaderContext $context
         * @return array LESS variables
         */
-       protected function getLessVars( \ResourceLoaderContext $context ) {
+       protected function getLessVars( ResourceLoaderContext $context ) {
                $blob = parent::getMessageBlob( $context );
                $lessMessages = $this->excludeMessagesFromBlob( $blob, $this->messages );
 
-               $vars = [];
+               $vars = parent::getLessVars( $context );
                foreach ( $lessMessages as $msgKey => $value ) {
                        $vars['msg-' . $msgKey] = self::wrapAndEscapeMessage( $value );
                }
index b76f0ff..4e1a69b 100644 (file)
@@ -162,39 +162,12 @@ class PHPSessionHandler implements \SessionHandlerInterface {
                }
        }
 
-       /**
-        * Workaround for PHP5 bug
-        *
-        * PHP5 has a bug in handling boolean return values for
-        * SessionHandlerInterface methods, it expects 0 or -1 instead of true or
-        * false. See <https://wiki.php.net/rfc/session.user.return-value>.
-        *
-        * PHP7 and HHVM are not affected.
-        *
-        * @todo When we drop support for Zend PHP 5, this can be removed.
-        * @return bool|int
-        * @codeCoverageIgnore
-        */
-       protected static function returnSuccess() {
-               return defined( 'HHVM_VERSION' ) || version_compare( PHP_VERSION, '7.0.0', '>=' ) ? true : 0;
-       }
-
-       /**
-        * Workaround for PHP5 bug
-        * @see self::returnSuccess()
-        * @return bool|int
-        * @codeCoverageIgnore
-        */
-       protected static function returnFailure() {
-               return defined( 'HHVM_VERSION' ) || version_compare( PHP_VERSION, '7.0.0', '>=' ) ? false : -1;
-       }
-
        /**
         * Initialize the session (handler)
         * @private For internal use only
         * @param string $save_path Path used to store session files (ignored)
         * @param string $session_name Session name (ignored)
-        * @return bool|int Success (see self::returnSuccess())
+        * @return true
         */
        public function open( $save_path, $session_name ) {
                if ( self::$instance !== $this ) {
@@ -203,20 +176,20 @@ class PHPSessionHandler implements \SessionHandlerInterface {
                if ( !$this->enable ) {
                        throw new \BadMethodCallException( 'Attempt to use PHP session management' );
                }
-               return self::returnSuccess();
+               return true;
        }
 
        /**
         * Close the session (handler)
         * @private For internal use only
-        * @return bool|int Success (see self::returnSuccess())
+        * @return true
         */
        public function close() {
                if ( self::$instance !== $this ) {
                        throw new \UnexpectedValueException( __METHOD__ . ': Wrong instance called!' );
                }
                $this->sessionFieldCache = [];
-               return self::returnSuccess();
+               return true;
        }
 
        /**
@@ -251,7 +224,7 @@ class PHPSessionHandler implements \SessionHandlerInterface {
         * @param string $dataStr Session data. Not that you should ever call this
         *   directly, but note that this has the same issues with code injection
         *   via user-controlled data as does PHP's unserialize function.
-        * @return bool|int Success (see self::returnSuccess())
+        * @return bool
         */
        public function write( $id, $dataStr ) {
                if ( self::$instance !== $this ) {
@@ -270,14 +243,14 @@ class PHPSessionHandler implements \SessionHandlerInterface {
                                [
                                        'session' => $id,
                        ] );
-                       return self::returnSuccess();
+                       return true;
                }
 
                // First, decode the string PHP handed us
                $data = \Wikimedia\PhpSessionSerializer::decode( $dataStr );
                if ( $data === null ) {
                        // @codeCoverageIgnoreStart
-                       return self::returnFailure();
+                       return false;
                        // @codeCoverageIgnoreEnd
                }
 
@@ -350,14 +323,14 @@ class PHPSessionHandler implements \SessionHandlerInterface {
 
                $session->persist();
 
-               return self::returnSuccess();
+               return true;
        }
 
        /**
         * Destroy a session
         * @private For internal use only
         * @param string $id Session id
-        * @return bool|int Success (see self::returnSuccess())
+        * @return true
         */
        public function destroy( $id ) {
                if ( self::$instance !== $this ) {
@@ -370,14 +343,14 @@ class PHPSessionHandler implements \SessionHandlerInterface {
                if ( $session ) {
                        $session->clear();
                }
-               return self::returnSuccess();
+               return true;
        }
 
        /**
         * Execute garbage collection.
         * @private For internal use only
         * @param int $maxlifetime Maximum session life time (ignored)
-        * @return bool|int Success (see self::returnSuccess())
+        * @return true
         * @codeCoverageIgnore See T135576
         */
        public function gc( $maxlifetime ) {
@@ -386,6 +359,6 @@ class PHPSessionHandler implements \SessionHandlerInterface {
                }
                $before = date( 'YmdHis', time() );
                $this->store->deleteObjectsExpiringBefore( $before );
-               return self::returnSuccess();
+               return true;
        }
 }
index 41ee353..0490cbb 100644 (file)
@@ -184,9 +184,7 @@ class SpecialPreferences extends SpecialPage {
 
                $context = new DerivativeContext( $this->getContext() );
                $context->setTitle( $this->getPageTitle( 'reset' ) ); // Reset subpage
-               $htmlForm = HTMLForm::factory(
-                       $this->oouiEnabled ? 'ooui' : 'vform', [], $context, 'prefs-restore'
-               );
+               $htmlForm = HTMLForm::factory( 'ooui', [], $context, 'prefs-restore' );
 
                $htmlForm->setSubmitTextMsg( 'restoreprefs' );
                $htmlForm->setSubmitDestructive();
index 4d5c3af..c23d999 100644 (file)
@@ -400,14 +400,14 @@ class UIDGenerator {
                        // Write back the new counter value
                        ftruncate( $handle, 0 );
                        rewind( $handle );
-                       fwrite( $handle, fmod( $counter, pow( 2, 48 ) ) ); // warp-around as needed
+                       fwrite( $handle, fmod( $counter, 2 ** 48 ) ); // warp-around as needed
                        fflush( $handle );
                        // Release the UID lock file
                        flock( $handle, LOCK_UN );
                }
 
                $ids = [];
-               $divisor = pow( 2, $bits );
+               $divisor = 2 ** $bits;
                $currentId = floor( $counter - $count ); // pre-increment counter value
                for ( $i = 0; $i < $count; ++$i ) {
                        $ids[] = fmod( ++$currentId, $divisor );
@@ -532,7 +532,7 @@ class UIDGenerator {
        protected function millisecondsSinceEpochBinary( array $time ) {
                list( $sec, $msec ) = $time;
                $ts = 1000 * $sec + $msec;
-               if ( $ts > pow( 2, 52 ) ) {
+               if ( $ts > 2 ** 52 ) {
                        throw new RuntimeException( __METHOD__ .
                                ': sorry, this function doesn\'t work after the year 144680' );
                }
@@ -551,7 +551,7 @@ class UIDGenerator {
                $offset = '122192928000000000';
                if ( PHP_INT_SIZE >= 8 ) { // 64 bit integers
                        $ts = ( 1000 * $sec + $msec ) * 10000 + (int)$offset + $delta;
-                       $id_bin = str_pad( decbin( $ts % pow( 2, 60 ) ), 60, '0', STR_PAD_LEFT );
+                       $id_bin = str_pad( decbin( $ts % ( 2 ** 60 ) ), 60, '0', STR_PAD_LEFT );
                } elseif ( extension_loaded( 'gmp' ) ) {
                        $ts = gmp_add( gmp_mul( (string)$sec, '1000' ), (string)$msec ); // ms
                        $ts = gmp_add( gmp_mul( $ts, '10000' ), $offset ); // 100ns intervals
index f0ace2c..20bad13 100644 (file)
@@ -656,7 +656,7 @@ class ZipDirectoryReader {
                                }
 
                                // Throw an exception if there was loss of precision
-                               if ( $value > pow( 2, 52 ) ) {
+                               if ( $value > 2 ** 52 ) {
                                        $this->error( 'zip-unsupported', 'number too large to be stored in a double. ' .
                                                'This could happen if we tried to unpack a 64-bit structure ' .
                                                'at an invalid location.' );
index fb329e0..f7772e1 100644 (file)
        "copyrightwarning2": "অনুগ্ৰহ কৰি মন কৰক যে {{SITENAME}}লৈ কৰা সকলো বৰঙণি আন সদস্যই সম্পাদনা কৰিব, সলনি কৰিব অথবা মচি পেলাব পাৰে ।\nআপুনি যদি আপোনাৰ লিখনি নিৰ্দয়ভাৱে সম্পাদনা কৰা ভাল নাপায়, তেনেহলে নিজৰ লিখনি ইয়াত নিদিব ।<br />\nইয়াত আপোনাৰ লিখনি দিয়াৰ লগে লগে আপুনি আপোনা-আপুনি প্ৰতিশ্ৰুতি দিছে যে এই লিখনিটো আপোনাৰ মৌলিক লিখনি, বা কোনো স্বত্বাধিকাৰ নথকা বা কোনো ৰাজহুৱা ৱেবছাইট বা তেনে কোনো মুকলি উৎসৰ পৰা আহৰণ কৰা । (অধিক জানিবলৈ $1 চাওক)\n\n'''স্বত্বাধিকাৰযুক্ত কোনো সমল অনুমতি অবিহনে দাখিল নকৰে যেন!'''",
        "longpageerror": "'''ভুল: আপুনি জমা দিয়া পাঠ {{PLURAL:$1|এক কিলো-বাইট|$1 কিলো-বাইট}} আকাৰৰ, যি {{PLURAL:$2|এক কিলো-বাইট|$2 কিলো-বাইট}} সীমাতকৈ বেছি।'''\nইয়াক সাঁচিব পৰা নাযাব।",
        "readonlywarning": "'''সতৰ্কবাণী: চোৱা-চিতাৰ হেতু এই তথ্যকোষ বন্ধ কৰি ৰখা হৈছে, গতিকে আপুনি এই মূহুৰ্তত আপোনাৰ সম্পাদনা সাঁচিব নোৱাৰিব।'''\nআপুনি লেখাটো টেক্সট-ফাইলত কপী-পে'ষ্ট কৰি পিছলৈ ব্যৱহাৰৰ বাবে সাঁচি ৰাখিব পাৰে।\n\nতথ্যকোষ বন্ধ কৰি ৰখা প্ৰশাসকজনে এই ব্যাখ্যা দিছে: $1",
-       "protectedpagewarning": "'''সতৰ্কবাণী: এই পৃষ্ঠা বন্ধ ৰখা হৈছে; কেৱল প্ৰশাসকৰৰ মৰ্যদাৰ সদস্যইহে সম্পাদনা কৰিব পাৰিব ।'''\nআপোনাৰ সুবিধাৰ বাবে পৃষ্ঠাৰ সাম্প্ৰতিক ল'গ সংৰক্ষণ তলত দিয়া হ'ল ।",
+       "protectedpagewarning": "<strong>সতৰ্কবাণী: এই পৃষ্ঠা সুৰক্ষিত কৰা হৈছে; কেৱল প্ৰশাসনিক মৰ্যাদাৰ সদস্যইহে ইয়াক সম্পাদনা কৰিব পাৰিব।</strong>\nআপোনাৰ সুবিধাৰ বাবে পৃষ্ঠাৰ সাম্প্ৰতিক ল'গ অন্তৰ্ভুক্তি তলত দিয়া হ'ল ।",
        "semiprotectedpagewarning": "টোকা: এই পৃষ্ঠা বন্ধ ৰখা হৈছে; কেৱল পঞ্জীভূত সদস্যই হে সম্পাদনা কৰিব পাৰিব ।\nআপোনাৰ সুবিধাৰ বাবে পৃষ্ঠাৰ সাম্প্ৰতিক ল'গ সংৰক্ষণ তলত দিয়া হ'ল ।",
        "cascadeprotectedwarning": "<strong>সতৰ্কবাণী:</strong> এই পৃষ্ঠাটো সুৰক্ষিত কৰি ৰখা হৈছে যাতে কেৱল প্ৰশাসনিক ক্ষমতা থকা সদস্যই ইয়াক সম্পাদনা কৰিব পাৰে, কাৰণ ই প্ৰপাতাকাৰ-সুৰক্ষিত  {{PLURAL:$1|পৃষ্ঠাটোৰ|পৃষ্ঠাবোৰৰ}} অন্তৰ্ভুক্ত:",
        "titleprotectedwarning": "'''সতৰ্কবাণী: এই পৃষ্ঠাটো সুৰক্ষিত কৰা হৈছে যাতে কেৱল [[Special:ListGroupRights|specific rights]] সদস্যই ইয়াক তৈয়াৰ কৰিব পাৰে ।'''\nআপোনাৰ সুবিধাৰ্থে অভিলেখৰ শেহতীয়া ভৰ্তি তলত দিয়া হ’ল।",
index eb9533f..2e353e6 100644 (file)
        "passwordpolicies-policy-minimalpasswordlength": "Пароль мусіць мець даўжыню найменей $1 {{PLURAL:$1|сымбаль|сымбалі|сымбаляў}}",
        "passwordpolicies-policy-minimumpasswordlengthtologin": "Пароль мусіць мець даўжыню найменш $1 {{PLURAL:$1|сымбаль|сымбалі|сымбаляў}}, каб уваходзіць у сыстэму",
        "passwordpolicies-policy-passwordcannotmatchusername": "Пароль ня можа супадаць зь імем ўдзельніка",
-       "passwordpolicies-policy-passwordcannotmatchblacklist": "Пароль ня можа супадаць з паролямі з чорнага сьпісу"
+       "passwordpolicies-policy-passwordcannotmatchblacklist": "Пароль ня можа супадаць з паролямі з чорнага сьпісу",
+       "passwordpolicies-policy-maximalpasswordlength": "Пароль мусіць быць даўжынёй менш за $1 {{PLURAL:$1|сымбаль|сымбалі|сымбаляў}}"
 }
index 073808f..dff32b6 100644 (file)
@@ -1,7 +1,8 @@
 {
        "@metadata": {
                "authors": [
-                       "Simartampua"
+                       "Simartampua",
+                       "Apundung"
                ]
        },
        "sunday": "Akad",
        "pagecategories": "{{PLURAL:$1|Category|Kategori}}",
        "category_header": "Alaman i kategori \"$1\"",
        "subcategories": "Subkategori",
-       "category-media-header": "Media in jategori \"$1\"",
+       "category-media-header": "Media i kategori \"$1\"",
        "category-empty": "<em>Kategori on sonnari indadong alaman dot media.</em>",
        "hidden-categories": "{{PLURAL:$1|Hidden category|Kategori monjap}}",
        "category-subcat-count": "{{PLURAL:$2|Kategori on umna puna subkategori ima.|Kategori on puna ima {{PLURAL:$1|subkategori|$1 subkategori}}, tingon bahat $2.}}",
        "category-article-count": "{{PLURAL:$2|Kategori on umna marisi alaman ima.|Onma{{PLURAL:$1|alaman ima|$1 alaman}} i kategori on, tingon bahat $2.}}",
        "category-file-count": "{{PLURAL:$2|Kategori on umna marisi alaman ima.|Onma{{PLURAL:$1|alaman ima|$1 alaman}} i kategori on, tingon bahat $2.}}",
        "listingcontinuesabbrev": "Sat",
+       "noindex-category": "Alaman naso tarindeks",
        "broken-file-category": "Alaman dot link berkas sega",
        "about": "Satontang",
        "newwindow": "(bukak i tingkap nabaru)",
@@ -71,7 +73,7 @@
        "mytalk": "Dokon",
        "navigation": "Navigasi",
        "and": "&#32;dot",
-       "namespaces": "Namespaces",
+       "namespaces": "Ruang gorar",
        "variants": "Mocoman",
        "navigation-heading": "Menu Navigasi",
        "returnto": "Keimulak tu $1",
@@ -95,7 +97,7 @@
        "personaltools": "Alat pribadi",
        "talk": "Marpokat",
        "views": "Sise",
-       "toolbox": "Alat",
+       "toolbox": "Parkuas",
        "otherlanguages": "I saro nalain",
        "redirectedfrom": "(Ialihkon tingon $1)",
        "redirectpagesub": "Alihkon alaman",
        "jumptosearch": "jalaki",
        "aboutsite": "Satontang {{SITENAME}}",
        "aboutpage": "Project:Satontang",
+       "copyright": "Konten tarsadio itoru $1 lainkon adong parnyataan nalain.",
        "copyrightpage": "{{ns:project}}:Hak cipta",
        "currentevents": "Namasa sonnari",
        "currentevents-url": "Project:Kajadian nabaru",
        "privacy": "Kabijakan privasi",
        "privacypage": "Project:Kabijakan privasi",
        "retrievedfrom": "Dapot tingon \"$1\"",
+       "youhavenewmessages": "{{PLURAL:$3|Amu puna}} $1 ($2).",
        "youhavenewmessagesfromusers": "{{PLURAL:$4|Amu puna}} $1 tingon {{PLURAL:$3|another user|$3 pamake}} ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|a new message|999=tona na baru}}",
        "newmessagesdifflinkplural": "Parpudi {{PLURAL:$1|change|999=parubaan}}",
        "nosuchspecialpage": "Inda adong alaman husus songon i",
        "nospecialpagetext": "<strong>Amu madung mangido alaman husus naso valid.</strong>\n\nDaftar alaman husus bisa iligi i[[Special:SpecialPages|{{int:specialpages}}]].",
        "badtitle": "Judul na jat",
-       "badtitletext": "Judul alaman na naipaido inda valid, kosong, sanga judul inter-sare sanga antar-wiki na sala ikaitkon. On mungkin marisi sada sanga lobi karakter na inda bisa ipake i judul.",
+       "badtitletext": "Judul alaman na naipaido inda valid, kosong, sanga judul antar-saro sanga antar-wiki na sala ikaitkon. On mungkin marisi sada sanga lobi karakter na inda bisa ipake i judul.",
        "viewsource": "Sise sumber",
        "viewsource-title": "Sise sumber ni $1",
        "viewsourcetext": "Amu bisa manyise dot mangkopi sumber ni alaman on",
        "createacct-yourpasswordagain": "Konfirmasi hata kunci",
        "createacct-yourpasswordagain-ph": "Pamasuk hata kunci mulak",
        "userlogin-remembermypassword": "Jago totop masuk log",
-       "login": "Masuk",
+       "login": "Masuk log",
        "userlogin-noaccount": "Ulang adong akun",
        "userlogin-joinproject": "Martop {{SITENAME}}",
        "createaccount": "Baen akun",
        "createacct-benefit-body2": "{{PLURAL:$1|page|alaman}}",
        "createacct-benefit-body3": "{{PLURAL:$1|contributor|kontributor}} baru",
        "loginlanguagelabel": "Saro: $1",
-       "pt-login": "Masuk",
+       "pt-login": "Masuk log",
        "pt-login-button": "Masuk log",
        "pt-createaccount": "Baen akun",
-       "pt-userlogout": "Kaluar",
+       "pt-userlogout": "Kaluar log",
        "botpasswords-label-needsreset": "(sandi porlu isitel mulak)",
        "botpasswords-needs-reset": "Hata sandi bot tu gorar bot \"$2\" ni {{GENDER:$1|user}} \"$1\" angkon na isitel mulak.",
        "passwordreset": "Gonti hata kunci",
        "watchthis": "Pamatai alamanon",
        "savearticle": "Simpan alaman",
        "preview": "Cimak",
-       "showpreview": "Pitidaon pratayang",
+       "showpreview": "Patidaon pratayang",
        "showdiff": "Patidaon parubaan",
        "anoneditwarning": "<strong> Paringatan: </ strong> Amu indape masuk. Alamat IP Muyu nangkan tarida sacara publik molo Amu mambaen pangeditan. Molo Amu <strong> [$1 masuk] </ strong> sanga<strong> [$2 mambaen akun] </ strong>, hasil pangeditan Muyu nangkan ikaitkon dohot gorar pamake Muyu, rap dohot manfaat lainna.",
        "blockedtext": "<strong>Alamat IP muyu madung iblokir sacara otomatis arani ipake ni pamake nalain,.</strong>\n\nNa iblokir ni $1.\nPamvlokiran ibaen arani <em>$2</em>.\n\n* Iblokir sian: $8\n* Blokir kadaluarsa i: $6\n* Sasarab pamblokiran: $7\n\nAmu bisa mayapai $1 sanga [[{{MediaWiki:Grouppage-sysop}}|administrator]] lainna tuna mamokatkon hal on.\n\nAmu inda bisa mamake fitur \"{{int:emailuser}}\" kacuali amu madung mamasukkon alamat surel na sah i [[Special:Preferences|preferensi akun]] dot amu inda iblokir mamakena.\n\nAlama IP muyu saonnari ima $3, dot ID pamblokiran ima #$5.\nTolong baen informasi-informasi in i satiop parsapaan muyu.",
        "autoblockedtext": "Alamat IP mu nangkan otomatis iblokir arani ipake pamake nalain, na madung iblokir ni $1.\nAlasanna ima:\n\n:<em>$2</em>\n\n* Muloi i blokir: $8\n* Kadaluarsa blokir: $6\n* Blokir na ituju: $7\n\nSapai amuma $1 sanga sada tingon nalainnai [[{{MediaWiki:Grouppage-sysop}}|administrators]] giot mamokatkon blokiran on.\n\nRoaon molo Amu inda tola mamake fitur \"{{int: emailuser}}\" kacuali Amu puna alamat surel valid na tardaftar i [[Special:Preferences|preferensi pamake]] dot Amu indape iblokir mamakena.\n\nAlamat IP mu sonnari ima $3, dot blokir ID ima #$5.\nIaropkon baen sude rincian na iginjang singkop molo mambaen parsaapaan na ibaen muyu.",
-       "loginreqlink": "Masuk",
-       "newarticletext": "Amu madung paiut tautan tu alaman na so adong dope. Giot mambaen alaman, muloima mangetik i kotak i toru on (ligin [$1 alaman bantuan] giot info lobi lanjut). Molo Amu adong i son arani kasalahan, klik tombol <strong> back </ strong> i browser Muyu.\n\nTERJEMAHKAN",
+       "loginreqlink": "Masuk log",
+       "newarticletext": "Amu madung paiut tautan tu alaman na so adong dope. Giot mambaen alaman, muloima mangetik i kotak i toru on (ligin [$1 alaman bantuan] giot info lobi lanjut). Molo Amu adong i son arani kasalahan, klik tombol <strong> mulam </ strong> i browser Muyu.",
+       "anontalkpagetext": "---- \n\n<em> on ima alaman marpokat tu pangguna anonim naso mambaen akun dope, sanga na inda mamakena. </em> Arani i ami angkon mamake alamat IP numerik tu mangidentifikasi ia. Songon alamat IP bisa ibagi ni bahat pengguna. Molo Amu ima pangguna anonim dot marasa molo komentar na inda relevan madung iarahkon tu Amu, kehemamu [[Special:CreateAccount|baen akun]] sanga [[Special:UserLogin|masuk log]] tu mangilakkon gamang i tu jolo niari dohot pengguna anonim laina.",
        "noarticletext": "Saonnari inda adong teks i alaman on. Amu bisa [[Special:Search/{{PAGENAME}}|manjalai judul alaman on]] i alaman lain, <span class = \"plainlinks\"> [{{fullurl: {{# Special: Log}} | page = { {FULLPAGENAMEE}}}} telusuri log terkait], atau [{{fullurl: {{FULLPAGENAME}} | action = edit}} baen alaman on] </ span>.",
        "noarticletext-nopermission": "Saonnari inda adong teks i alaman on. Amu bisa [[Special:Search/{{PAGENAME}}|manjalai judul alaman on]] i alaman lain, sanga <span class = \"plainlinks\"> [{{fullurl:{{#Special:Log}}|alaman={{FULLPAGENAMEE}}}} jalai log tarkait] </ span>, tai Amu inda puna izin giot mambaen alaman on.",
        "userpage-userdoesnotexist-view": "Akun pangguna ''$1'' inda tardaftar",
+       "clearyourcache": "<strong> Catatan: </strong> Sidung manyimpan, Amu mungkin angkon malewati cache browser Muyu anso mangida parubaanna. \n* <strong> Firefox/Safari:</strong> Tahan <em> Shift </em> atia mangklik <em> Reload </em>, sanga pisat <em> Ctrl-F5 </em> sanga <em> Ctrl-R </em> (<em> ⌘-R </ em> pada Mac) \n* <strong> Google Chrome: </strong> Pisat <em> Ctrl-Shift-R </em> (<em> ⌘ -Shift-R </em> i Mac) \n* <strong> Internet Explorer: </strong> Tahan <em> Ctrl </em> atia mangklik <em> Segarkon </em>, sanga pisat <em> Ctrl-F5 </em> \n* <strong> Opera: </ strong> Kei tu <em> Menu → Pangaturan </em> (<em> Opera → Preferensi </ em> i Mac) dot sidungi ke <em> Privasi & kaamanan → Apus data panjalajahan → Tembolok gambar dot file </em>.",
+       "previewnote": "<strong> Ingot molo on umna pratinjau. </strong> Parubaan Muyu indape isimpan!",
+       "continue-editing": "Kei tu ganan pangeditan",
        "editing": "Mangedit $1",
        "creating": "Baen $1",
        "editingsection": "Patureon $1 (bagian)",
        "templatesused": "{{PLURAL:$1|Template|Templat}} baen tu alaman on:",
+       "templatesusedpreview": "{{PLURAL:$1|Template|Templat}} ipake i pratinjau on:",
        "template-protected": "Larangan",
        "template-semiprotected": "(satonga-larangan)",
        "hiddencategories": "Alaman on ima anggota {{PLURAL:$1|1 kategori namonjap|$1 kategori namonjap}}:",
        "permissionserrors": "Santabi sega",
        "permissionserrorstext-withaction": "Amu inda iizinkon $2, tu naonan {{PLURAL:$1|reason|alasan}}:",
+       "recreate-moveddeleted-warn": "<strong> Paringotan: Amu mambaen mulak laman na unjung ihapus. </strong> Amu angkon mapartimbangkon sanga sasue giot malanjutkon mangedit alaman on. Log pangapusan dot pamindahan tu alaman on isadioon i son so tagi:",
        "moveddeleted-notice": "Alaman on nangkan iapus. \nCatatan pangapusan, palarangan, dot pamindahan tu alaman ilehen i toru on manjadi referensi.",
        "content-model-wikitext": "Tekswiki",
+       "undo-failure": "Pangeditan inda bisa iantai arani pangeditan dompak na konflik.",
        "viewpagelogs": "Sise log ni alaman on",
        "currentrev-asof": "Parubaan parpudi ima $1",
        "revisionasof": "Revisi par $1",
        "history-feed-description": "Sejarah revisi tu alaman i wiki",
        "history-feed-item-nocomment": "$1 i $2",
        "rev-delundel": "Uba visibilitas",
+       "mergelog": "Pasada log",
        "history-title": "Sejarah Revisi ni \"$1\"",
        "difference-title": "Parbedaan antara revisi \"$1\"",
        "lineno": "Baris $1:",
        "compareselectedversions": "Bandingkon dot revisi tarpili",
        "editundo": "Mambuka",
+       "diff-empty": "(Inda dong parbedaan)",
        "diff-multi-sameuser": "({{PLURAL:$1|Sada revisi manonga|$1 revisi manonga}} ni pangguna na sarupo inda ipatidaon)",
+       "diff-multi-otherusers": "({{PLURAL:$1|Sada revisi sedang|$1 revisi sedang}} ibaen {{PLURAL:$2|sada pangguna lainna|$2 pangguna}} inda ipatidaon)",
        "searchresults": "Jalaki sude hasil",
        "searchresults-title": "Jalaki sude hasil tu \"$1\"",
        "prevn": "Cimak {{PLURAL:$1|$1}}",
        "group-bot": "Bot",
        "group-sysop": "Administrator",
        "grouppage-bot": "{{ns:project}}:Bot",
+       "grouppage-sysop": "{{ns:project}}:Administrator",
        "right-writeapi": "Pamakean ni API tulis",
        "newuserlogpage": "Log pambaenan pamake",
+       "rightslog": "Log hak pangguna",
        "action-edit": "Pature alaman on",
        "action-createaccount": "baen akun pamake on",
        "enhancedrc-history": "sejarah",
        "recentchanges-summary": "Lacak parubaan tarbaru i wiki i alaman on.",
        "recentchanges-noresult": "Inda adong parubaan saonok periode na ilehen sasuae dohot kriteria on.",
        "recentchanges-feed-description": "Jalaki parubaan tarbaru i wiki i umpan on.",
-       "recentchanges-label-newpage": "Patureon on mammbaen alaman baru",
-       "recentchanges-label-minor": "On pature na menek",
+       "recentchanges-label-newpage": "Editan on mammbaen alaman baru",
+       "recentchanges-label-minor": "On edit na menek",
        "recentchanges-label-bot": "Naipature ni bot",
        "recentchanges-label-unpatrolled": "Suntingan on indape ipareso",
        "recentchanges-label-plusminus": "Ukuran alamanon iuba dohot bahatni byte",
-       "recentchanges-legend-heading": "<strong>tarombo:</strong>",
+       "recentchanges-legend-heading": "<strong>Tando:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ligin juo [[Special:NewPages|daftar alaman nabaru]])",
        "rcnotefrom": "Itoru {{PLURAL:$5|is the change|ima parubaan}} saonok<strong>$3, $4</strong> (sampe tu <strong>$1</strong> patidaon).",
        "rclistfrom": "Patidaon parubaan tarbaru imuloi tingon $2, $3",
        "rcshowhideanons": "$1 pamake anonim",
        "rcshowhideanons-show": "Patidaon",
        "rcshowhideanons-hide": "Bunion",
+       "rcshowhidepatr": "$1 edit nai pareso",
        "rcshowhidemine": "$1 naupature",
        "rcshowhidemine-show": "Patidaon",
        "rcshowhidemine-hide": "Bunion",
        "minoreditletter": "m",
        "newpageletter": "B",
        "boteditletter": "b",
-       "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} sidung maruba",
+       "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} sidung iuba",
+       "rc-old-title": "Aslina ibaen i ''$1''",
        "recentchangeslinked": "Parubaan tarkait",
        "recentchangeslinked-feed": "Parubaan tarkait",
        "recentchangeslinked-toolbox": "Parubaan tarkait",
        "file-anchor-link": "Berkas",
        "filehist": "Sejarah berkas",
        "filehist-help": "Klik tanggal / woktu giot maligi file songon na na i woktu i.",
+       "filehist-revert": "Mulak",
        "filehist-current": "Saonnari",
        "filehist-datetime": "Tangagal/Woktu",
-       "filehist-thumb": "Thumbnail",
-       "filehist-thumbtext": "Thumbnail tu versi par $1",
+       "filehist-thumb": "Gambar",
+       "filehist-thumbtext": "Gambar tu versi par $1",
+       "filehist-nothumb": "Inda adong gambar",
        "filehist-user": "Pamake",
        "filehist-dimensions": "Ukuran",
        "filehist-comment": "Komentar",
        "imagelinks": "Berkas ipake",
-       "linkstoimage": "Namamatai {{PLURAL:$1|page links|$1 tautan alaman}} tu berkas on:",
+       "linkstoimage": "Onma {{PLURAL:$1|page links|$1 tautan alaman}} tu berkas on:",
+       "linkstoimage-more": "Lobi tingon $1 {{PLURAL:$1|page links|tautan alaman}} tu berkas on. Daftar naonan patidaon {{PLURAL:$1|first page link|tautan alaman parjolo $1}} tu berkas on sajo. A [[Special:WhatLinksHere/$2|daftar singkop]] tarsadio.",
        "nolinkstoimage": "Inda adong alaman na martaut tu berkason",
+       "linkstoimage-redirect": "$1 (pangalihan berkas) $2",
        "sharedupload-desc-here": "File on tingon $1 dot bisa ipake ni proyek lain. Deskripsi i [alaman deskripsi file $2] i sadun ipatidaon naitoruon.",
+       "filepage-nofile": "Inda adong berkas dohot gorar on.",
        "upload-disallowed-here": "Amu inda bisa manimpo berkas on.",
        "randompage": "Gaor alaman",
        "statistics": "Statistik",
+       "double-redirect-fixer": "Papas Pangalihan",
        "nbytes": "$1 {{PLURAL:$1|byte|bytes}}",
-       "nmembers": "$1 {{PLURAL:$1|member|members}}",
+       "nmembers": "$1 {{PLURAL:$1|member|anggota}}",
        "prefixindex": "Sude alaman na prefix",
        "listusers": "Daptar pamake",
        "newpages": "Alaman baru",
        "booksources": "Sumber buku",
        "booksources-search-legend": "Jalai tu sumber buku",
        "booksources-search": "Jalai",
+       "specialloguserlabel": "Panampil:",
+       "speciallogtitlelabel": "Target (judul sanga {{ns:user}}:gorar pangguna tu pangguna):",
        "log": "Masuk",
        "all-logs-page": "Sude log",
+       "alllogstext": "Tampilan gabungan tingon sude log na tarsadio tingon {{SITENAME}}. Amu bisa mamparsompit tampilan dohot mamili jenis log, gorar pangguna (sensitif huruf), sanga alaman na tarpangaruh (juo kasus-sensitive).",
        "logempty": "Inda adong item nacocok i log",
        "allpages": "Sude alaman",
        "allarticles": "Sude alaman",
        "allpagessubmit": "Kehe",
        "allpages-hide-redirects": "Bunion pangalihan",
        "categories": "Kategori",
+       "listgrouprights-members": "(daftar anggota)",
        "emailuser": "Email ni pamake on",
        "usermessage-editor": "Tona ni sistem",
        "watchlist": "Pamataan",
        "watch": "Pamatai",
        "unwatch": "Inda ipamatai",
        "watchlist-details": "{{PLURAL:$1|$1 page is|$1 alaman}} i pamataanmu (dot alaman parkobaran).",
+       "wlheader-showupdated": "Laman na madung iubah sian parpudi Amu ligi, ipatidaon i huruf <strong> naapal </strong>.",
+       "wlnote": "Itoruon {{PLURAL:$1|is the last change|ima parpudi<strong>$1</strong> parubaan}} i {{PLURAL:$2|hour|<strong>$2</strong> jom}}, i $3, $4.",
        "wlshowlast": "Patidaon parpudi $1 jom $2 ari",
        "watchlist-options": "Opsi pamataan",
        "enotif_reset": "Tandai sude alaman namadung iligi",
        "rollbacklinkcount": "Paulak $1 {{PLURAL:$1|edit|edits}}",
        "protectlogpage": "Log nai jago",
        "protectedarticle": "larangan \"[[$1]]\"",
+       "modifiedarticleprotection": "Gonti tingkat larangan tu ''[[$1]]''",
        "protect-default": "Patola sude pangguna",
        "restriction-edit": "Pature",
        "restriction-move": "Pindah",
-       "namespace": "Namespace:",
+       "namespace": "Ruanggorar:",
        "invert": "Pilian sabalikna",
-       "tooltip-invert": "Centang kotak on giot mambunion parubaan tu alaman i ruang gorar na ipili (dot ruang girar tarkait molo icentang)",
+       "tooltip-invert": "Centang kotak on giot mambunion parubaan tu alaman i ruang gorar na ipili (dot ruang gorar tarkait molo icentang)",
        "namespace_association": "Kumpulan ruang gorar",
        "tooltip-namespace_association": "Centang kotak on giot mambunion ruang gorar pangecean sanga subjek na tarkait dohot ruang gorar na ipili",
        "blanknamespace": "(Utamo)",
        "contributions": "{{GENDER:$1|User}} kontribusi",
-       "contributions-title": "Kontribusi pannguna tu $1",
+       "contributions-title": "Kontribusi pangguna tu $1",
        "mycontris": "Kontribusi",
        "anoncontribs": "Kontribusingku",
        "contribsub2": "Tu {{GENDER:$3|$1}} ($2)",
+       "nocontribs": "Inda adong parubaan na sasue dohot kriteria on.",
        "uctop": "(sonnari)",
        "month": "Tingon bulan (dot nasolpu)",
        "year": "Tingon taon (dot nasolpu)",
        "whatlinkshere-hideimages": "$1 tautan berkas",
        "whatlinkshere-filters": "Saringan",
        "ipboptions": "2 jom:2 hours,1 ari:1 day,3 ari:3 days,1 poken:1 week,2 poken:2 weeks,1 bulan:1 month,3 bulan:3 months,6 bulan:6 months,1 taon:1 year,onok:infinite",
+       "infiniteblock": "Inda tarbatas",
        "blocklink": "blokir",
        "contribslink": "Kontributor",
        "blocklogpage": "Blokir log",
        "blocklogentry": "blokir [[$1]] dot woktu kadaluarsa $2 $3",
+       "reblock-logentry": "Gonti sitelan blokir tu [[$1]] dot maso kadaluarsa tu $2 $3",
+       "block-log-flags-nocreate": "Pambaenan akun inonaktipkon",
        "proxyblocker": "Blokir proxi",
        "movelogpage": "Pindah log",
        "export": "Expor alaman",
        "tooltip-pt-watchlist": "Daftar nialaman na parubaanna ipamataimu",
        "tooltip-pt-mycontris": "Daftar {{GENDER:|your}} kontribusi",
        "tooltip-pt-login": "Amu iaropkon masuk, tai inda wajib",
-       "tooltip-pt-logout": "Kaluar",
+       "tooltip-pt-logout": "Kaluar log",
        "tooltip-pt-createaccount": "Amu iaropkon mambaen akun dot masuk; taiba, i inda wajib",
        "tooltip-ca-talk": "Pokat satontang isi ni alaman",
        "tooltip-ca-edit": "Pature alamanon",
        "tooltip-ca-protect": "Jago alamanon",
        "tooltip-ca-delete": "Apus alaman on",
        "tooltip-ca-move": "Papindah alamanon",
-       "tooltip-ca-watch": "Baen alamanon tu pangawasanmu",
+       "tooltip-ca-watch": "Baen alamanon tu pamataanmu",
        "tooltip-ca-unwatch": "Apus alaman on tingon pamataanmu",
        "tooltip-search": "Jalaki {{SITENAME}}",
        "tooltip-search-go": "Kehe tu alaman dohot gorar naonan molo adong.",
        "tooltip-n-randompage": "Muat alaman sumbarang",
        "tooltip-n-help": "Inganan anso binoto",
        "tooltip-t-whatlinkshere": "Daftar alaman wiki na martaut tuson",
-       "tooltip-t-recentchangeslinked": "Parubaan tarbaru i sude alaman namartautan tingon alaman on",
+       "tooltip-t-recentchangeslinked": "Parubaan tarbaru i sude alaman namartautan tu alaman on",
        "tooltip-feed-atom": "Umpan atom ni alamaon",
        "tooltip-t-contributions": "Daftar kontribusi ni {{GENDER:$1|pamake on}}",
        "tooltip-t-emailuser": "Kirim email tu {{GENDER:$1|pamake on}}",
        "tooltip-t-permalink": "Tautan permanen tu revisi ni alamanon",
        "tooltip-ca-nstab-main": "Sise isi ni alaman",
        "tooltip-ca-nstab-user": "Sise alaman pamake",
-       "tooltip-ca-nstab-special": "On alaman spesial, dot on inda bisa ipature",
+       "tooltip-ca-nstab-special": "On alaman husus, dot on inda bisa ipature",
        "tooltip-ca-nstab-project": "Sise alaman proyek",
        "tooltip-ca-nstab-image": "Sise alaman berkas",
        "tooltip-ca-nstab-mediawiki": "Sise sistem tona",
        "pageinfo-default-sort": "Kunci panyortiran default",
        "pageinfo-length": "Lanjang alaman (i byte)",
        "pageinfo-article-id": "ID alaman",
-       "pageinfo-language": "Bahasa isi ni alaman",
+       "pageinfo-language": "Saro isi ni alaman",
        "pageinfo-content-model": "Model konten alaman",
        "pageinfo-robot-policy": "Pangindeksan ni robot",
+       "pageinfo-robot-index": "Iizinkon",
+       "pageinfo-robot-noindex": "Inda iizinkon",
        "pageinfo-watchers": "Bahatni alaman naipamatai",
+       "pageinfo-few-watchers": "Urang tingon $1 {{PLURAL:$1|pamatai|napamatai}}",
        "pageinfo-redirects-name": "Bahatni pangalihan tu alaman on",
+       "pageinfo-subpages-name": "Bahatni sub alaman ni alaman on",
+       "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|redirect|pangalihan}}; $3 {{PLURAL:$3|non-redirect|inda pangalihan}})",
        "pageinfo-firstuser": "Pambaen alaman",
        "pageinfo-firsttime": "Tanggal ibaen alaman",
+       "pageinfo-lastuser": "Pangedit parpudi",
+       "pageinfo-lasttime": "Tanggal edit parpudi",
        "pageinfo-edits": "Bahat ni edit",
+       "pageinfo-authors": "Bahatni sude panulis namarbeda",
+       "pageinfo-recent-edits": "Bahat pangeditan tarbaru (i $1 pardudi)",
+       "pageinfo-recent-authors": "Bahat ni panulis namarbeda i ari nalewat",
+       "pageinfo-magic-words": "Ajaib {{PLURAL:$1|word|hata}} ($1)",
        "pageinfo-hidden-categories": "Bunion {{PLURAL:$1|category|Kategori}} ($1)",
-       "pageinfo-templates": "Transclusi {{PLURAL:$1|template|templat}} ($1)",
+       "pageinfo-templates": "Transklusi {{PLURAL:$1|template|templat}} ($1)",
        "pageinfo-toolboxlink": "Informasi alaman",
        "pageinfo-contentpage": "Ietong manjadi sada alaman",
        "pageinfo-contentpage-yes": "Olo",
        "nextdiff": "Edit tarbaru →",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|page|alaman}}",
        "file-info-size": "$1 × $2 piksel, ukuran berkas: $3, Type MIME: $4",
+       "file-info-size-pages": "$1 × $2 piksel, ukuran berkas: $3, tipe MIME : $4, $5 {{PLURAL:$5|page|alaman}}",
        "file-nohires": "Indadongbe resolusi naum ginjang",
        "svg-long-desc": "Berkas SVG, nominall $1 × $2 piksel, ukuran berkas: $3",
        "show-big-image": "Berkas asli",
        "watchlisttools-clear": "Paias pamataan",
        "watchlisttools-view": "Sise parubaan na pas",
        "watchlisttools-edit": "Sise dot pature pamataan",
+       "watchlisttools-raw": "Pature daptar pamataan",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|obarkon]])",
        "redirect": "Pangalihan ni berkas, pamake, alaman, revisi, sanga log ID",
        "redirect-summary": "Alaman husus on mangalihkon tu berkas (ilehen gorar berkas), alaman (ilehen ID revisi sanga ID alaman), alaman pangguna (ilehen ID pangguna numerik), sanga entri log (ilehen ID log). Pamakean:\n[[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], sanga [[{{#Special:Redirect}}/logid/186]].",
        "redirect-page": "ID alaman",
        "redirect-revision": "Revisi alaman",
        "redirect-file": "Gorarberkas:",
-       "specialpages": "Alaman Spesial",
+       "specialpages": "Alaman husus",
        "tag-filter": "[[Special:Tags|Tag]] saring:",
-       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]: $2)",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]:$2)",
        "tags-active-yes": "Olo",
        "tags-active-no": "Inda",
        "tags-hitcount": "$1 {{PLURAL:$1|change|parubaan}}",
        "logentry-delete-delete": "$1 {{GENDER:$2|iapus}} alaman $3",
+       "logentry-delete-restore": "$1 {{GENDER:$2|restored}} alaman $3 ($4)",
        "logentry-delete-revision": "$1 {{GENDER:$2|iuba}} visibilitas {{PLURAL:$5|a revision|$5 revisi}} i alaman $3: $4",
        "revdelete-content-hid": "Isi naibunion",
        "logentry-move-move": "$1 {{GENDER:$2|pindah}} alam $3 to $4",
        "logentry-newusers-create": "Akun pamake $1 ima {{GENDER:$2|baen}}",
        "logentry-newusers-autocreate": "Akun pangguna $1 madung {{GENDER:$2|ibaen}} otomatis",
        "logentry-upload-upload": "$1 {{GENDER:$2|maiunggah}} $3",
+       "logentry-upload-overwrite": "$1 {{GENDER:$2|iunggah}} versi nabaru tingon $3",
        "searchsuggest-search": "Jalaki {{SITENAME}}",
        "duration-days": "$1 {{PLURAL:$1|day|ari}}",
-       "randomrootpage": "Alaman urat kocok"
+       "randomrootpage": "Gaor alaman urat"
 }
index f80e87f..dac9e07 100644 (file)
        "february": "Gucige",
        "march": "Adar",
        "april": "Nisane",
-       "may_long": "Gulan",
+       "may_long": "Gulane",
        "june": "Heziran",
        "july": "Temuz",
        "august": "Tebaxe",
        "february-gen": "Sıbat",
        "march-gen": "Adar",
        "april-gen": "Nisane",
-       "may-gen": "Gulan",
+       "may-gen": "Gulane",
        "june-gen": "Heziran",
        "july-gen": "Temuz",
        "august-gen": "Tebaxe",
        "jan": "Çel",
        "feb": "Sbt",
        "mar": "Adr",
-       "apr": "Lsn",
-       "may": "Gln",
+       "apr": "Nsn",
+       "may": "Gul",
        "jun": "Hez",
        "jul": "Tmz",
        "aug": "Tbx",
        "february-date": "$1 Sıbat",
        "march-date": "$1 Adar",
        "april-date": "$1 Nisane",
-       "may-date": "$1 Gulan",
+       "may-date": "$1 Gulane",
        "june-date": "$1 Heziran",
        "july-date": "$1 Temuze",
        "august-date": "$1 Tebaxe",
index 7e6cf6e..da04a90 100644 (file)
        "badipaddress": "כתובת IP שגויה",
        "blockipsuccesssub": "החסימה הושלמה בהצלחה",
        "blockipsuccesstext": "{{GENDER:$1|המשתמש|המשתמשת}} [[Special:Contributions/$1|$1]] {{GENDER:$1|נחסם|נחסמה}}.<br />\nניתן לעיין ב[[Special:BlockList|רשימת החסומים]] כדי לצפות בחסימות.",
-       "ipb-blockingself": "{{GENDER:|אתה עומד|את עומדת|אתם עומדים}} לחסום את {{GENDER:|עצמך|עצמך|עצמכם}}! האם {{GENDER:|אתה בטוח שאתה רוצה|את בטוחה שאת רוצה|אתם בטוחים שאתם רוצים}} לעשות את זה?",
-       "ipb-confirmhideuser": "{{GENDER:|אתה עומד|את עומדת|אתם עומדים}} לחסום משתמש עם האפשרות \"הסתרת משתמש\". זה יעלים את שם המשתמש בכל הרשימות ופעולות היומן. האם {{GENDER:|אתה בטוח שברצונך|את בטוחה שברצונך|אתם בטוחים שברצונכם}} לעשות את זה?",
+       "ipb-blockingself": "החשבון שלך ייחסם! האם ברצונך לעשות זאת?",
+       "ipb-confirmhideuser": "החשבון ייחסם עם האפשרות \"הסתרת משתמש\". זה יעלים את שם המשתמש בכל הרשימות ופעולות היומן. האם ברצונך לעשות זאת?",
        "ipb-confirmaction": "אם {{GENDER:|אתה בטוח שברצונך|את בטוחה שברצונך|אתם בטוחים שברצונכם}} לעשות זאת, אנא {{GENDER:|סמן|סמני|סמנו}} את השדה \"{{int:ipb-confirm}}\" שמופיע למטה.",
        "ipb-edit-dropdown": "עריכת סיבות החסימה",
        "ipb-unblock-addr": "שחרור חסימה של $1",
index fb955f6..7c4f579 100644 (file)
        "protectedtitles-submit": "Leírások mutatása",
        "listusers": "Szerkesztők",
        "listusers-editsonly": "Csak a szerkesztéssel rendelkező szerkesztők mutatása",
+       "listusers-temporarygroupsonly": "Csak az ideiglenes szerkesztői csoportokban szereplő szerkesztők mutatása",
        "listusers-creationsort": "Rendezés létrehozási dátum szerint",
        "listusers-desc": "Rendezés csökkenő sorrendben",
        "usereditcount": "{{PLURAL:$1|egy|$1}} szerkesztés",
        "unlinkaccounts-success": "A fiókok szétkapcsolva.",
        "authenticationdatachange-ignored": "A hitelesítő adatok változtatása nincs kezelve. Talán nincs beállítva szolgáltató?",
        "userjsispublic": "Figyelem: JavaScript-allapokon ne tárolj bizalmas adatokat, mivel minden felhasználó számára láthatóak.",
+       "userjsonispublic": "Figyelem: JSON-allapokon ne tárolj bizalmas adatokat, mivel minden felhasználó számára láthatóak.",
        "usercssispublic": "Figyelem: CSS-allapokon ne tárolj bizalmas adatokat, mivel minden felhasználó számára láthatóak.",
        "restrictionsfield-badip": "Érvénytelen IP-cím vagy -tartomány: $1",
        "restrictionsfield-label": "Engedélyezett IP-tartományok:",
        "pagedata-bad-title": "Érvénytelen cím: $1.",
        "unregistered-user-config": "Biztonsági okokból JavaScript, CSS és JSON szerkesztői alapok nem töltődnek be regisztrálatlan felhasználóknak.",
        "passwordpolicies": "Jelszóirányelvek",
+       "passwordpolicies-summary": "Ez egy lista a hatékony jelszóirányelvekről a wikin használt felhasználói csoportok szerint.",
        "passwordpolicies-group": "Csoport",
        "passwordpolicies-policies": "Irányelvek",
        "passwordpolicies-policy-minimalpasswordlength": "A jelszónak legalább $1 karakterből kell állnia",
        "passwordpolicies-policy-minimumpasswordlengthtologin": "A jelszónak legalább $1 karakterből kell állnia a bejelentkezéshez",
        "passwordpolicies-policy-passwordcannotmatchusername": "A jelszó nem lehet azonos a felhasználónévvel",
+       "passwordpolicies-policy-passwordcannotmatchblacklist": "A jelszó nem lehet azonos a feketelistán szereplő jelszavakkal.",
        "passwordpolicies-policy-maximalpasswordlength": "A jelszó legfeljebb $1 karakter hosszú lehet"
 }
index 71deca3..1400381 100644 (file)
        "searchprofile-images-tooltip": "Файлаш лахар",
        "searchprofile-everything-tooltip": "Массайолча оагIонаш тIа лахар (дувцара оагIонаш чу а лоацаш)",
        "searchprofile-advanced-tooltip": "Лаха хьахержача цIерий аренашка",
-       "search-result-size": "$1 ({{PLURAL:$2|$2 дош|$2 дешаш}})",
+       "search-result-size": "$1 ({{PLURAL:$2|$2 дош}})",
        "search-result-category-size": "$1 {{PLURAL:$1|юкъедахар}} ($2 {{PLURAL:$2|кIалоагIат}}, $3 {{PLURAL:$3|файл}})",
        "search-redirect": "($1 яхача оагIон тIара дIа-хьа хьожадар)",
        "search-section": "(дáкъа «$1»)",
        "prefs-watchlist-token": "Зема хьаязъяьра токен:",
        "prefs-resetpass": "ДIахувца пароль",
        "prefs-changeemail": "дIахувца е дIаяккха электронни пошт",
+       "prefs-setemail": "Электронни пошта цIай дӀаоттадар",
        "prefs-email": "Электронни поштан оттамаш",
        "prefs-rendering": "ТIера куц",
        "saveprefs": "ДIаязде",
index 2b857b1..cb38f8a 100644 (file)
        "noarticletext-nopermission": "Исятда и ччина са текстни авач.\nКвевай [[Special:Search/{{PAGENAME}}| и тӀвар алай ччин]] муькуь ччинра жугъуриз ва я\n<span class=\"plainlinks\"> [{{fullurl: {{# Special:Log}} | page = {{FULLPAGENAMEE}}}} журналрин талукь тир кхьей затӀар жугъуриз] жеда.",
        "blocked-notice-logextract": "И уртах алайчIава блокарнава.\nАгъадихъ блокарунин журналдикай эхиримжи кхьинар къалурнава:",
        "previewnote": "'''Рикlел хуьх хьи, им анжах сифтедин килигун я.'''  \nКуь масакIавилер гьеле хвенвач!",
-       "editing": "$1 Ð\94уьзар хъувун",
+       "editing": "$1 Ð´уьзар хъувун",
        "editingsection": "Дуьзар хъувун $1  (пай)",
        "editingcomment": "$1 дуьзар хъувун (цIийи пай)",
        "editconflict": "Дуьзар хъувунрин акьунар: $1",
index 9edaaa8..ec4d570 100644 (file)
        "listusers-submit": "Mostra",
        "listusers-noresult": "No usor trovada.",
        "listusers-blocked": "(impedida)",
-       "activeusers": "Lista ativa de usores",
+       "activeusers": "Lista de usores ativa",
        "activeusers-intro": "Esta es un lista de usores ci ia es ativa en alga modo en la {{PLURAL:$1|dia|dias}} la plu resente.",
        "activeusers-count": "$1 {{PLURAL:$1|ata|atas}} en la {{PLURAL:$3|dia|$3 dias}} la plu resente.",
        "activeusers-from": "Mostra usores comensante a:",
index aa2f766..1b14a70 100644 (file)
        "rcfilters-filter-categorization-description": "Registraments de paginas apondudas o suprimidas de las categorias.",
        "rcfilters-filter-logactions-label": "Accions traçadas",
        "rcfilters-filter-logactions-description": "Accions dels administrators, creacions de comptes, supressions de paginas, telecargaments...",
+       "rcfilters-hideminor-conflicts-typeofchange": "D'unes tipes de cambiament pòdon pas èsser designats coma  \"minors\", doncas aqueste filtre es en conflicte amb los filtres  de Tipe de cambiament seguent: $1",
+       "rcfilters-typeofchange-conflicts-hideminor": "Aqueste filtre «Tipe de cambiament» es en conflicte amb lo filtre «Edicions minoras». D'unes tipes de cambiaments  pòdon pas èstre considerats coma «minors».",
+       "rcfilters-filtergroup-lastRevision": "Darrièras revisions",
+       "rcfilters-filter-lastrevision-label": "Darrièra revision",
+       "rcfilters-filter-lastrevision-description": "Sonque lo cambiament de pagina lo mai recent.",
+       "rcfilters-filter-previousrevision-label": "Pas la darrièra revision",
+       "rcfilters-filter-previousrevision-description": "Totes los cambiaments que son pas «la darrièra revision».",
+       "rcfilters-filter-excluded": "Exclús",
+       "rcfilters-tag-prefix-namespace-inverted": "<strong>:Pas</strong> $1",
+       "rcfilters-exclude-button-off": "Exclure los seleccionats",
+       "rcfilters-exclude-button-on": "Seleccionats excluses",
+       "rcfilters-view-tags": "Cambiaments marcats",
+       "rcfilters-view-namespaces-tooltip": "Resultat del filtratge per espaci de noms",
+       "rcfilters-view-tags-tooltip": "Resultat del filtratge per etiqueta d'edicion",
+       "rcfilters-view-return-to-default-tooltip": "Tornar al menut de filtres principal",
+       "rcfilters-view-tags-help-icon-tooltip": "Ne saber mai suls cambiaments marcats",
+       "rcfilters-liveupdates-button": "Actualizacion en dirècte",
+       "rcfilters-liveupdates-button-title-on": "Desactivar los cambiaments en dirècte",
        "rcnotefrom": "Çaijós {{PLURAL:$5|la modificacion efectuada|las modificacions efectuadas}} dempuèi lo <strong>$3, $4</strong> (afichadas fins a <strong>$1</strong>).",
        "rclistfrom": "Afichar las modificacions novèlas dempuèi lo $3 $2",
        "rcshowhideminor": "$1 los cambiaments menors",
index fcbe9af..7e5c073 100644 (file)
        "savechanges": "Değişiklikleri kaydet",
        "publishpage": "Sayfayı yayımla",
        "publishchanges": "Değişiklikleri yayımla",
+       "savearticle-start": "Sayfayı kaydet...",
+       "savechanges-start": "Değişiklikleri kaydet...",
+       "publishpage-start": "Sayfayı yayımla...",
+       "publishchanges-start": "Değişiklikleri yayımla...",
        "preview": "Önizleme",
        "showpreview": "Önizlemeyi göster",
        "showdiff": "Değişiklikleri göster",
        "postedit-confirmation-created": "Sayfa oluşturuldu.",
        "postedit-confirmation-restored": "Sayfa geri yüklendi.",
        "postedit-confirmation-saved": "Değişikliğiniz kaydedildi.",
+       "postedit-confirmation-published": "Değişikliğiniz yayımlandı.",
        "edit-already-exists": "Yeni sayfa oluşturulamıyor.\nSayfa zaten mevcut.",
        "defaultmessagetext": "Varsayılan mesaj metni",
        "content-failed-to-parse": "$1 modeli için $2 içerik türü çözümlenemedi: $3",
        "invalid-content-data": "Geçersiz içerik verisi",
        "content-not-allowed-here": "\"$1\" içeriğine, [[$2]] sayfasında izin verilmemekte.",
        "editwarning-warning": "Bu sayfadan ayrılmak yaptığınız herhangi bir değişikliği kaybetmenize sebep olabilir.\nEğer giriş yaptıysanız, bu uyarıyı, tercihlerinizin \"{{int:prefs-editing}}\" bölümünde devre dışı bırakabilirsiniz.",
+       "editpage-invalidcontentmodel-title": "İçerik modeli desteklenmiyor",
        "editpage-invalidcontentmodel-text": "\"$1\" içerik modeli desteklenmemektedir.",
        "editpage-notsupportedcontentformat-title": "İçerik biçimi desteklenmiyor",
        "editpage-notsupportedcontentformat-text": "$1 içerik biçimi $2 içerik modeli tarafından desteklenmiyor.",
        "content-model-css": "CSS",
        "content-json-empty-object": "Boş nesne",
        "content-json-empty-array": "Boş dizi",
+       "deprecated-self-close-category": "Kendiliğinden geçersiz HTML etiketlerini kullanan sayfalar",
        "duplicate-args-warning": "<strong>Uyarı:</strong>[[:$1]] [[:$2]] şablonunu \"$3\" parametresi için birden fazla değerle çağırıyor. Sadece sağlanan son değer kullanılacak.",
        "duplicate-args-category": "Yinelenen şablon değişkenleri kullanan sayfalar",
        "duplicate-args-category-desc": "Sayfada içeren şablonları çağırmak için bu terimler kullanılır <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "post-expand-template-argument-warning": "'''Uyarı:''' Bu sayfa çok fazla genişleme boyutuna sahip en az bir şablon değişkeni içeriyor.\nBu değişkenler atlandı.",
        "post-expand-template-argument-category": "Geçersiz şablon değiştirgenleri içeren sayfalar",
        "parser-template-loop-warning": "Şablon düğümü tespit edildi: [[$1]]",
+       "template-loop-category": "Şablon döngülü sayfalar",
        "parser-template-recursion-depth-warning": "Şablon özyineleme yoğunluğu sınırı aşıldı ($1)",
        "language-converter-depth-warning": "Dil çevirici derinlik sınırı aşıldı ($1)",
        "node-count-exceeded-category": "Düğüm sayısı aşılan sayfalar",
        "right-applychangetags": "Değişiklikleriyle beraber [[Special:Tags|etiketleri]] uygula",
        "right-changetags": "Tekil sürümler ve günlük kayıtlarına rastgele [[Special:Tags|etiket]] ekleme veya çıkarma",
        "grant-group-email": "E-posta gönder",
+       "grant-group-other": "Çeşitli aktivite",
        "grant-createeditmovepage": "Sayfaları oluşturma, düzenleme ve taşıma",
        "grant-editmycssjs": "Kullanıcı CSS/JavaScript'ini düzenle",
        "grant-editmyoptions": "Kullanıcı tercihlerini Düzenle",
index c20ce59..e62ebec 100644 (file)
@@ -75,7 +75,8 @@
                        "Renamerr",
                        "Avatar6",
                        "Fitoschido",
-                       "Movses"
+                       "Movses",
+                       "Esk78"
                ]
        },
        "tog-underline": "Підкреслювання посилань:",
        "tog-watchlisthideminor": "Приховати незначні редагування у списку спостереження",
        "tog-watchlisthideliu": "Приховати редагування зареєстрованих дописувачів у списку спостереження",
        "tog-watchlistreloadautomatically": "Перезавантажувати список спостереження автоматично кожного разу, коли зміниться фільтр (вимагається JavaScript)",
-       "tog-watchlistunwatchlinks": "Ð\94одаÑ\82и Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ñ\96Ñ\81Ñ\82Ñ\8c Ð´Ð»Ñ\8f Ð²Ð¸ÐºÐ»Ñ\8eÑ\87еннÑ\8f Ñ\81Ñ\82оÑ\80Ñ\96нок Ð¿Ñ\80Ñ\8fмо Ð·Ñ\96 Ñ\81Ñ\82оÑ\80Ñ\96нки Ñ\81пиÑ\81кÑ\83 Ñ\81поÑ\81Ñ\82еÑ\80еженнÑ\8f (потрібен JavaScript для функціонування)",
+       "tog-watchlistunwatchlinks": "Ð\94одаÑ\82и Ð¿Ñ\80Ñ\8fмÑ\96 Ð¼Ð°Ñ\80кеÑ\80и Ð²ÐºÐ»Ñ\8eÑ\87еннÑ\8f/виклÑ\8eÑ\87еннÑ\8f Ð·Ñ\96 Ñ\81пиÑ\81кÑ\83 Ñ\81поÑ\81Ñ\82еÑ\80еженнÑ\8f ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}}) Ð´Ð»Ñ\8f Ñ\81Ñ\82еженнÑ\8f Ð·Ð° Ñ\81Ñ\82оÑ\80Ñ\96нками Ñ\96з Ð·Ð¼Ñ\96нами (потрібен JavaScript для функціонування)",
        "tog-watchlisthideanons": "Приховати редагування анонімних користувачів у списку спостереження",
        "tog-watchlisthidepatrolled": "Приховати відпатрульовані редагування у списку спостереження",
        "tog-watchlisthidecategorization": "Приховати категоризацію сторінок",
        "botpasswords-existing": "Існуючі паролі бота",
        "botpasswords-createnew": "Створити новий пароль бота",
        "botpasswords-editexisting": "Редагувати існуючий пароль бота",
+       "botpasswords-label-needsreset": "(необхідно скинути пароль)",
        "botpasswords-label-appid": "Назва бота:",
        "botpasswords-label-create": "Створити",
        "botpasswords-label-update": "Оновити",
        "botpasswords-restriction-failed": "Вхід не було здійснено через обмеження для паролю бота.",
        "botpasswords-invalid-name": "Вказане ім'я користувача не містить роздільник для пароля бота («$1»).",
        "botpasswords-not-exist": "У користувача «$1» нема пароля для бота «$2».",
+       "botpasswords-needs-reset": "Пароль бота з ім'ям «$2» {{GENDER:$1|користувача|користувачки}} «$1» необхідно скинути.",
        "resetpass_forbidden": "Пароль не можна змінити",
        "resetpass_forbidden-reason": "Пароль не можна змінити: $1",
        "resetpass-no-info": "Щоб звертатися безпосередньо до цієї сторінки, вам слід увійти до системи.",
        "previewerrortext": "Сталася помилка при спробі попереднього перегляду Ваших змін.",
        "blockedtitle": "Користувача заблоковано",
        "blockedtext": "<strong>Ваш обліковий запис або IP-адреса заблоковані.</strong>\n\nБлокування виконане адміністратором $1.\nПричина блокування: <em>$2</em>.\n\n* Початок блокування: $8\n* Закінчення блокування: $6\n* Діапазон блокування: $7\n\nВи можете надіслати листа користувачеві $1 або будь-якому іншому [[{{MediaWiki:Grouppage-sysop}}|адміністратору]], щоб обговорити блокування.\n\nЗверніть увагу, що ви не зможете використати функцію \"{{int:emailuser}}\", якщо ви не зареєстровані або не підтвердили свою електронну адресу в [[Special:Preferences|особистих налаштуваннях]], а також якщо вам було заборонено надсилати листи при блокуванні.\n\nВаша поточна IP-адреса — $3, ідентифікатор блокування — #$5. Будь ласка, зазначайте ці дані у своїх запитах.",
-       "autoblockedtext": "Ð\92аÑ\88а IP-адÑ\80еÑ\81а Ð°Ð²Ñ\82омаÑ\82иÑ\87но Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð° Ñ\83 Ð·Ð²'Ñ\8fзкÑ\83 Ð· Ñ\82им, Ñ\89о Ð²Ð¾Ð½Ð° Ñ\80анÑ\96Ñ\88е Ð²Ð¸ÐºÐ¾Ñ\80иÑ\81Ñ\82овÑ\83валаÑ\81Ñ\8f ÐºÐ¸Ð¼Ð¾Ñ\81Ñ\8c Ñ\96з Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¸Ñ\85 ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87Ñ\96в. Ð\90дмÑ\96нÑ\96Ñ\81Ñ\82Ñ\80аÑ\82оÑ\80 ($1), Ñ\89о Ñ\97Ñ\97 Ð·Ð°Ð±Ð»Ð¾ÐºÑ\83вав, Ð·Ð°Ð·Ð½Ð°Ñ\87ив Ð½Ð°Ñ\81Ñ\82Ñ\83пнÑ\83 Ð¿Ñ\80иÑ\87инÑ\83 Ð±Ð»Ð¾ÐºÑ\83ваннÑ\8f:\n\n:''$2''\n\n* Ð\9fоÑ\87аÑ\82ок Ð±Ð»Ð¾ÐºÑ\83ваннÑ\8f: $8\n* Ð\97акÑ\96нÑ\87еннÑ\8f Ð±Ð»Ð¾ÐºÑ\83ваннÑ\8f: $6\n* Ð\91локÑ\83ваннÑ\8f Ð²Ð¸ÐºÐ¾Ð½Ð°Ð²: $7\n\nÐ\92и Ð¼Ð¾Ð¶ÐµÑ\82е Ð½Ð°Ð´Ñ\96Ñ\81лаÑ\82и Ð»Ð¸Ñ\81Ñ\82а ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87евÑ\96 $1 Ð°Ð±Ð¾ Ð±Ñ\83дÑ\8c\8fкомÑ\83 Ñ\96нÑ\88омÑ\83 [[{{MediaWiki:Grouppage-sysop}}|адмÑ\96нÑ\96Ñ\81Ñ\82Ñ\80аÑ\82оÑ\80Ñ\83]], Ñ\89об Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ\80иÑ\82и Ð±Ð»Ð¾ÐºÑ\83ваннÑ\8f.\n\nÐ\97веÑ\80нÑ\96Ñ\82Ñ\8c Ñ\83вагÑ\83, Ñ\89о Ð²Ð¸ Ð½Ðµ Ð·Ð¼Ð¾Ð¶ÐµÑ\82е Ð½Ð°Ð´Ñ\96Ñ\81лаÑ\82и Ð»Ð¸Ñ\81Ñ\82а Ð°Ð´Ð¼Ñ\96нÑ\96Ñ\81Ñ\82Ñ\80аÑ\82оÑ\80овÑ\96, Ñ\8fкÑ\89о Ð²Ð¸ Ð½Ðµ Ð·Ð°Ñ\80еÑ\94Ñ\81Ñ\82Ñ\80ованÑ\96 Ñ\83 Ð¿Ñ\80оекÑ\82Ñ\96 Ð°Ð±Ð¾ Ð½Ðµ Ð¿Ñ\96дÑ\82веÑ\80дили Ñ\81воÑ\8e ÐµÐ»ÐµÐºÑ\82Ñ\80оннÑ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 Ð² [[Special:Preferences|оÑ\81обиÑ\81Ñ\82иÑ\85 Ð½Ð°Ð»Ð°Ñ\88Ñ\82Ñ\83ваннÑ\8fÑ\85]], Ð° Ñ\82акож Ñ\8fкÑ\89о Ð²Ð°Ð¼ Ð±Ñ\83ло Ð·Ð°Ð±Ð¾Ñ\80онено Ð½Ð°Ð´Ñ\81илаÑ\82и Ð»Ð¸Ñ\81Ñ\82и Ð¿Ñ\80и Ð±Ð»Ð¾ÐºÑ\83ваннÑ\96.\n\nÐ\92аÑ\88а Ð¿Ð¾Ñ\82оÑ\87на IP-адÑ\80еÑ\81а â\80\94 $3, Ñ\96денÑ\82иÑ\84Ñ\96каÑ\82оÑ\80 Ð±Ð»Ð¾ÐºÑ\83ваннÑ\8f â\80\94 #$5. Ð\91Ñ\83дÑ\8c Ð»Ð°Ñ\81ка, Ð·Ð°Ð·Ð½Ð°Ñ\87айÑ\82е Ð¹Ð¾Ð³Ð¾ у своїх запитах.",
+       "autoblockedtext": "Ð\92аÑ\88а IP-адÑ\80еÑ\81а Ð°Ð²Ñ\82омаÑ\82иÑ\87но Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð° Ñ\83 Ð·Ð²'Ñ\8fзкÑ\83 Ð· Ñ\82им, Ñ\89о Ð²Ð¾Ð½Ð° Ñ\80анÑ\96Ñ\88е Ð²Ð¸ÐºÐ¾Ñ\80иÑ\81Ñ\82овÑ\83валаÑ\81Ñ\8f ÐºÐ¸Ð¼Ð¾Ñ\81Ñ\8c Ñ\96з ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87Ñ\96в, Ñ\8fкого Ð·Ð°Ð±Ð»Ð¾ÐºÑ\83вав $1.\nÐ\9fÑ\80иÑ\87ина Ð±Ð»Ð¾ÐºÑ\83ваннÑ\8f Ð±Ð»Ð¾ÐºÑ\83ваннÑ\8f:\n\n:<em>$2</em>\n\n* Ð\9fоÑ\87аÑ\82ок Ð±Ð»Ð¾ÐºÑ\83ваннÑ\8f: $8\n* Ð\97акÑ\96нÑ\87еннÑ\8f Ð±Ð»Ð¾ÐºÑ\83ваннÑ\8f: $6\n* Ð\91локÑ\83ваннÑ\8f Ð²Ð¸ÐºÐ¾Ð½Ð°Ð²: $7\n\nÐ\92и Ð¼Ð¾Ð¶ÐµÑ\82е Ð½Ð°Ð´Ñ\96Ñ\81лаÑ\82и Ð»Ð¸Ñ\81Ñ\82а ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87евÑ\96 $1 Ð°Ð±Ð¾ Ð±Ñ\83дÑ\8c\8fкомÑ\83 Ñ\96нÑ\88омÑ\83 [[{{MediaWiki:Grouppage-sysop}}|адмÑ\96нÑ\96Ñ\81Ñ\82Ñ\80аÑ\82оÑ\80Ñ\83]], Ñ\89об Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ\80иÑ\82и Ð±Ð»Ð¾ÐºÑ\83ваннÑ\8f.\n\nÐ\97веÑ\80нÑ\96Ñ\82Ñ\8c Ñ\83вагÑ\83, Ñ\89о Ð²Ð¸ Ð½Ðµ Ð·Ð¼Ð¾Ð¶ÐµÑ\82е Ñ\81коÑ\80иÑ\81Ñ\82аÑ\82иÑ\81Ñ\8f Ñ\84Ñ\83нкÑ\86Ñ\96Ñ\94Ñ\8e \"{{int:emailuser}}\", Ñ\82ак Ñ\8fк Ð½Ðµ Ð¼Ð°Ñ\94Ñ\82е Ð´Ñ\96йÑ\81ноÑ\97 ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð¿Ð¾Ñ\88Ñ\82и, Ð·Ð°Ñ\80еÑ\94Ñ\81Ñ\82Ñ\80ованоÑ\97 Ð² [[Special:Preferences|оÑ\81обиÑ\81Ñ\82иÑ\85 Ð½Ð°Ð»Ð°Ñ\88Ñ\82Ñ\83ваннÑ\8fÑ\85]], Ð° Ñ\82акож Ñ\8fкÑ\89о Ð²Ð°Ð¼ Ð±Ñ\83ло Ð·Ð°Ð±Ð¾Ñ\80онено Ð½Ð°Ð´Ñ\81илаÑ\82и Ð»Ð¸Ñ\81Ñ\82и Ð¿Ñ\80и Ð±Ð»Ð¾ÐºÑ\83ваннÑ\96.\n\nÐ\92аÑ\88а Ð¿Ð¾Ñ\82оÑ\87на IP-адÑ\80еÑ\81а â\80\94 $3, Ñ\96денÑ\82иÑ\84Ñ\96каÑ\82оÑ\80 Ð±Ð»Ð¾ÐºÑ\83ваннÑ\8f â\80\94 #$5. Ð\91Ñ\83дÑ\8c Ð»Ð°Ñ\81ка, Ð·Ð°Ð·Ð½Ð°Ñ\87айÑ\82е Ñ\86Ñ\96 Ð´Ð°Ð½Ñ\96 у своїх запитах.",
        "systemblockedtext": "Ваше ім'я користувача або IP-адресу було автоматично заблоковано MediaWiki.\nВказана причина:\n\n:<em>$2</em>\n\n* Початок блокування: $8\n* Закінчення блокування: $6\n* Ціль блокування: $7\n\nВаша поточна IP-адреса — $3.\nБудь ласка, додайте всі вказані подробиці до будь-яких запитів, які Ви будете робити.",
        "blockednoreason": "не вказано причини",
        "whitelistedittext": "Ви повинні $1, щоб редагувати сторінки.",
        "longpageerror": "'''Помилка: Поданий вами текст становить $1 {{PLURAL:$1|кілобайт|кілобайти|кілобайтів}}, що більше за встановлену межу у {{PLURAL:$2|кілобайт|кілобайти|кілобайтів}}.'''\nЙого неможливо зберегти.",
        "readonlywarning": "<strong>Попередження: База даних заблокована на обслуговування, тому, на даний момент, ви не можете записати ваші зміни.\n</strong>\nМожливо, вам варто скопіювати текст у файл на вашому комп'ютері й зберегти його на пізніше.\n\nАдміністратор, що заблокував базу даних, залишив наступне пояснення: $1",
        "protectedpagewarning": "'''Попередження: Ця сторінка була захищена від змін так, що тільки користувачі з правами адміністратора можуть її редагувати.'''\nОстанній запис журналу наведений нижче для довідки:",
-       "semiprotectedpagewarning": "'''Зауваження:''' Ця сторінка захищена так, що її можуть редагувати тільки зареєстровані користувачі.\nОстанній запис журналу наведений нижче для довідки:",
+       "semiprotectedpagewarning": "<strong>Зауваження:</strong> Ця сторінка захищена так, що її можуть редагувати тільки зареєстровані користувачі.\nОстанній запис журналу наведений нижче для довідки:",
        "cascadeprotectedwarning": "<strong>Попередження:</strong> Цю сторінку можуть редагувати лише користувачі зі [[Special:ListGroupRights|специфічними правами]], оскільки вона включена на {{PLURAL:$1|1=сторінці|сторінках}}, де встановлено каскадний захист:",
        "titleprotectedwarning": "'''Попередження. Ця сторінка була захищена так, що для її створення потрібні [[Special:ListGroupRights|особливі права]].'''\nОстанній запис журналу наведений нижче для довідки:",
        "templatesused": "{{PLURAL:$1|1=Шаблон, використаний|Шаблони, використані}} на цій сторінці:",
        "prefs-watchlist-edits": "Максимальна кількість змін, яку можна виводити у списку спостереження:",
        "prefs-watchlist-edits-max": "Максимально: 1000",
        "prefs-watchlist-token": "Токен списку спостереження:",
+       "prefs-watchlist-managetokens": "Управління токенами",
        "prefs-misc": "Інші налаштування",
        "prefs-resetpass": "Змінити пароль",
        "prefs-changeemail": "Змінити або вилучити адресу електронної пошти",
        "recentchangescount": "Кількість редагувань для показу за замовчуванням на сторінках нових редагувань, історій сторінок та в журналах:",
        "prefs-help-recentchangescount": "Максимальна кількість: 1000",
        "prefs-help-watchlist-token2": "Це секретний ключ до веб-каналу вашого списку спостереження.\nБудь-хто, хто його знає, матиме можливість читати ваш список спостереження, тому не поширюйте його.\nЯкщо вам потрібно, [[Special:ResetTokens|ви можете скинути його]].",
+       "prefs-help-tokenmanagement": "Ви можете переглянути і скинути секретний ключ свого облікового запису, який дає доступ до веб-каналу вашого списку спостереження. Кожен, хто його знає, матиме можливість читати ваш список спостереження, тому не поширюйте його.",
        "savedprefs": "Ваші налаштування збережено.",
        "savedrights": "Групи {{GENDER:$1|користувача $1|користувачки $1}} було збережено.",
        "timezonelegend": "Часовий пояс:",
        "prefs-dateformat": "Формат дати",
        "prefs-timeoffset": "Зсув часу",
        "prefs-advancedediting": "Загальні параметри",
+       "prefs-developertools": "Інструменти розробника",
        "prefs-editor": "Редактор",
        "prefs-preview": "Попередній перегляд",
        "prefs-advancedrc": "Розширені налаштування",
        "rcfilters-filter-humans-label": "Людина (не бот)",
        "rcfilters-filter-humans-description": "Редагування, зроблені людиною.",
        "rcfilters-filtergroup-reviewstatus": "Статус перевірки",
+       "rcfilters-filter-reviewstatus-unpatrolled-description": "Зміни, не позначені вручну або автоматично як патрульовані.",
        "rcfilters-filter-reviewstatus-unpatrolled-label": "Неперевірені",
+       "rcfilters-filter-reviewstatus-manual-description": "Зміни, позначені вручну як патрульовані.",
+       "rcfilters-filter-reviewstatus-manual-label": "Патрульовані вручну",
+       "rcfilters-filter-reviewstatus-auto-description": "Зміни довірених користувачів, які відмічаються як автопатрульовані.",
+       "rcfilters-filter-reviewstatus-auto-label": "Автопатрульовані",
        "rcfilters-filtergroup-significance": "Важливість",
        "rcfilters-filter-minor-label": "Незначні редагування",
        "rcfilters-filter-minor-description": "Редагування, позначені авторами як незначні.",
        "deadendpages": "Сторінки без посилань",
        "deadendpagestext": "Наступні сторінки не містять посилань на інші сторінки цієї вікі.",
        "protectedpages": "Захищені сторінки",
+       "protectedpages-filters": "Фільтри:",
        "protectedpages-indef": "Тільки безстроково захищені",
        "protectedpages-summary": "На цій сторінці перераховані сторінки, які на цей момент захищені. Список назв, які захищені від створення див.  [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]].",
        "protectedpages-cascade": "Тільки каскадний захист",
        "protectedtitles-submit": "Показувати заголовки",
        "listusers": "Список користувачів",
        "listusers-editsonly": "Показати лише користувачів, які зробили принаймні одне редагування",
+       "listusers-temporarygroupsonly": "Показувати лише користувачів із тимчасових груп",
        "listusers-creationsort": "Сортувати за датою створення",
        "listusers-desc": "Сортувати за спадним порядком",
        "usereditcount": "$1 {{PLURAL:$1|редагування|редагування|редагувань}}",
        "apisandbox-dynamic-parameters-add-label": "Додати параметр:",
        "apisandbox-dynamic-parameters-add-placeholder": "Назва параметра",
        "apisandbox-dynamic-error-exists": "Параметр з назвою «$1» вже існує.",
+       "apisandbox-templated-parameter-reason": "Цей [[Special:ApiHelp/main#main/templatedparams|шаблонний параметр]] запропонований на основі  {{PLURAL:$1|значення|значень}} з $2.",
        "apisandbox-deprecated-parameters": "Застарілі параметри",
        "apisandbox-fetch-token": "Автозаповнення токена",
+       "apisandbox-add-multi": "Додати",
        "apisandbox-submit-invalid-fields-title": "Деякі поля недійсні",
        "apisandbox-submit-invalid-fields-message": "Будь ласка, виправте відмічені поля і спробуйте знову.",
        "apisandbox-results": "Результати",
        "fix-double-redirects": "Виправити всі перенаправлення на попередню назву",
        "move-leave-redirect": "Залишити перенаправлення",
        "protectedpagemovewarning": "'''Попередження:''' Ця сторінка захищена так, що її можуть перейменовувати тільки адміністратори.\nОстанній запис журналу наведений нижче для довідки:",
-       "semiprotectedpagemovewarning": "'''Зауваження:''' Ця сторінка захищена так, що перейменовувати її можуть тільки зареєстровані користувачі.\nОстанній запис журналу наведений нижче для довідки:",
+       "semiprotectedpagemovewarning": "<strong>Зауваження:</strong> Ця сторінка захищена так, що перемістити її можуть тільки зареєстровані користувачі.\nОстанній запис журналу наведений нижче для довідки:",
        "move-over-sharedrepo": "У спільному сховищі існує [[:$1]]. Перейменування файлу на цю назву призведе до перекриття файлу зі спільного сховища.",
        "file-exists-sharedrepo": "Обрана назва файлу вже використовується у спільному сховищі.\nБудь ласка, оберіть іншу назву.",
        "export": "Експорт статей",
        "version-specialpages": "Спеціальні сторінки",
        "version-parserhooks": "Перехоплювачі синтаксичного аналізатора",
        "version-variables": "Змінні",
+       "version-editors": "Редактори",
        "version-antispam": "Захист від спаму",
        "version-api": "API",
        "version-other": "Інше",
        "pagedata-title": "Дані сторінки",
        "pagedata-text": "Ця сторінка надає сторінкам інтерфейс даних. Будь ласка, вкажіть назву сторінки в URL-адресі, скориставшись синтаксисом підсторінок.\n* Переговори щодо контенту застосовуються на основі прийнятних форматів Вашого клієнта. Це означає, що дані сторінки будуть надані в тому форматі, якому віддає перевагу Ваш клієнт.",
        "pagedata-not-acceptable": "Не знайдено підхожого формату. Підтримувані MIME-типи: $1",
-       "pagedata-bad-title": "Недійсна назва: $1."
+       "pagedata-bad-title": "Недійсна назва: $1.",
+       "unregistered-user-config": "З міркувань безпеки користувальницькі підсторінки JavaScript, CSS та JSON не можуть бути завантажені для незареєстрованих учасників.",
+       "passwordpolicies": "Політика щодо паролів",
+       "passwordpolicies-summary": "Це список ефективних правил щодо паролів для груп користувачів, визначених у цій вікі.",
+       "passwordpolicies-group": "Група",
+       "passwordpolicies-policies": "Політика",
+       "passwordpolicies-policy-minimalpasswordlength": "Пароль має містити принаймні $1 {{PLURAL:$1|символ|символи|символів}}",
+       "passwordpolicies-policy-minimumpasswordlengthtologin": "Пароль має містити принаймні $1 {{PLURAL:$1|символ|символи|символів}} щоб мати можливість увійти до свого облікового запису",
+       "passwordpolicies-policy-passwordcannotmatchusername": "Пароль не може збігатися з назвою облікового запису",
+       "passwordpolicies-policy-passwordcannotmatchblacklist": "Пароль не може збігатися з паролями із чорного списку",
+       "passwordpolicies-policy-maximalpasswordlength": "Пароль повинен бути коротшим $1 {{PLURAL:$1|символа|символів}}",
+       "passwordpolicies-policy-passwordcannotbepopular": "Пароль не може бути {{PLURAL:$1|часто вживаним|будь-яким з $1 часто вживаних паролів}}"
 }
index 200fc66..bbd22ed 100644 (file)
        "whatlinkshere": "װאָס פֿאַרבינדט אַהער",
        "whatlinkshere-title": "בלעטער וואס פֿארבינדן צו $1",
        "whatlinkshere-page": "בלאַט:",
-       "linkshere-2": "די פאלגנדע בלעטער פארבינדן צום בלאט '''$1''':",
-       "nolinkshere-2": "קיין שום בלאט פארבינדט נישט צו '''$1'''.",
-       "nolinkshere-ns-2": "קיין בלעטער פֿאַרבינדן נישט צו '''$1''' אינעם אויסגעקליבענעם נאמענטייל.",
+       "linkshere-2": "די פאָלגנדיקע בלעטער פאַרבינדן צום בלאַט '''$1''':",
+       "nolinkshere-2": "קיין שום בלאַט פאַרבינדט נישט צו '''$1'''.",
+       "nolinkshere-ns-2": "קיין בלעטער פאַרבינדן נישט צו '''$1''' אינעם אויסגעקליבענעם נאָמענטייל.",
        "isredirect": "ווײַטערפירן בלאט",
        "istemplate": "אײַנשליסן",
        "isimage": "!טעקע לינק",
index 53498bf..5faa030 100644 (file)
        "ipboptions": "2 小時:2 hours,1 天:1 day,3 天:3 days,1 週:1 week,2 週:2 weeks,1 個月:1 month,3 個月:3 months,6 個月:6 months,1 年:1 year,無限期:infinite",
        "ipbhidename": "在編輯及清單中隱藏使用者名稱",
        "ipbwatchuser": "監視這位使用者的使用者頁面及其對話頁面",
-       "ipb-disableusertalk": "阻止此使用者在封鎖期間編輯自己的對話頁面",
+       "ipb-disableusertalk": "禁止使用者在封鎖期間編輯自己的對話頁面",
        "ipb-change-block": "使用現有設定重新封鎖使用者",
        "ipb-confirm": "確認封鎖",
        "badipaddress": "無效的 IP 位址",
index 5a2c6c7..e2dc9b8 100644 (file)
@@ -177,7 +177,7 @@ class GenerateSitemap extends Maintenance {
        public function execute() {
                $this->setNamespacePriorities();
                $this->url_limit = 50000;
-               $this->size_limit = pow( 2, 20 ) * 10;
+               $this->size_limit = ( 2 ** 20 ) * 10;
 
                # Create directory if needed
                $fspath = $this->getOption( 'fspath', getcwd() );
index 9ba3d1b..af9dd08 100644 (file)
@@ -31,7 +31,7 @@ class StorageTypeStats extends Maintenance {
                        exit( 1 );
                }
 
-               $binSize = intval( pow( 10, floor( log10( $endId ) ) - 3 ) );
+               $binSize = intval( 10 ** ( floor( log10( $endId ) ) - 3 ) );
                if ( $binSize < 100 ) {
                        $binSize = 100;
                }
index 4e0a452..b32bbcd 100644 (file)
@@ -115,6 +115,10 @@ return [
        'jquery.makeCollapsible.styles' => [
                'targets' => [ 'desktop', 'mobile' ],
                'class' => ResourceLoaderLessVarFileModule::class,
+               'lessMessages' => [
+                       'collapsible-collapse',
+                       'collapsible-expand',
+               ],
                'styles' => [
                        'resources/src/jquery/jquery.makeCollapsible.styles.less',
                ],
@@ -852,6 +856,7 @@ return [
                        'resources/src/mediawiki/mediawiki.js',
                        'resources/src/mediawiki/mediawiki.requestIdleCallback.js',
                        'resources/src/mediawiki/mediawiki.errorLogger.js',
+                       'resources/src/mediawiki/mediawiki.base.js',
                ],
                'debugScripts' => 'resources/src/mediawiki/mediawiki.log.js',
                'targets' => [ 'desktop', 'mobile' ],
index 2738d1a..186ad17 100644 (file)
                        // we can trim the previous one).
                        // See https://www.w3.org/TR/DOM-Level-3-Events/#events-keyboard-event-order for
                        // the order and characteristics of the key events.
-                       $el.on( eventKeys, function () {
+
+                       function enforceLimit() {
                                var res = trimFn(
                                        prevSafeVal,
                                        this.value,
                                // trimFn to compare the new value to an empty string instead of the
                                // old value, resulting in trimming always from the end (T42850).
                                prevSafeVal = res.newVal;
+                       }
+
+                       $el.on( eventKeys, function ( e ) {
+                               if ( e.type === 'cut' || e.type === 'paste' ) {
+                                       // For 'cut'/'paste', the input value is only updated after the event handlers resolve.
+                                       setTimeout( enforceLimit.bind( this ) );
+                               } else {
+                                       enforceLimit.call( this );
+                               }
                        } );
                } );
        }
diff --git a/resources/src/mediawiki/mediawiki.base.js b/resources/src/mediawiki/mediawiki.base.js
new file mode 100644 (file)
index 0000000..b68d779
--- /dev/null
@@ -0,0 +1,34 @@
+/*!
+ * This file is currently loaded as part of the 'mediawiki' module and therefore
+ * concatenated to mediawiki.js and executed at the same time. This file exists
+ * to help prepare for splitting up the 'mediawiki' module.
+ * This effort is tracked at https://phabricator.wikimedia.org/T192623
+ *
+ * In short:
+ *
+ * - mediawiki.js will be reduced to the minimum needed to define mw.loader and
+ *   mw.config, and then moved to its own private "mediawiki.loader" module that
+ *   can be embedded within the StartupModule response.
+ *
+ * - mediawiki.base.js and other files in this directory will remain part of the
+ *   "mediawiki" module, and will remain a default/implicit dependency for all
+ *   regular modules, just like jquery and wikibits already are.
+ */
+/* globals mw */
+( function () {
+       /**
+        * @class mw
+        * @singleton
+        */
+
+       /**
+        * @inheritdoc mw.inspect#runReports
+        * @method
+        */
+       mw.inspect = function () {
+               var args = arguments;
+               mw.loader.using( 'mediawiki.inspect', function () {
+                       mw.inspect.runReports.apply( mw.inspect, args );
+               } );
+       };
+}() );
index 2d3f6ad..a2af443 100644 (file)
                                }
                        }
 
+                       /**
+                        * @private
+                        * @param {Object} params Map of parameter names to values
+                        * @return {string}
+                        */
+                       function makeQueryString( params ) {
+                               return Object.keys( params ).map( function ( key ) {
+                                       return encodeURIComponent( key ) + '=' + encodeURIComponent( params[ key ] );
+                               } ).join( '&' );
+                       }
+
                        /**
                         * Create network requests for a batch of modules.
                         *
                                        // combining versions from the module query string in-order. (T188076)
                                        query.version = getCombinedVersion( packed.list );
                                        query = sortQuery( query );
-                                       addScript( sourceLoadScript + '?' + $.param( query ) );
+                                       addScript( sourceLoadScript + '?' + makeQueryString( query ) );
                                }
 
                                if ( !batch.length ) {
                                                // > '&modules='.length === 9
                                                // > '&version=1234567'.length === 16
                                                // > 9 + 16 = 25
-                                               currReqBaseLength = $.param( currReqBase ).length + 25;
+                                               currReqBaseLength = makeQueryString( currReqBase ).length + 25;
 
                                                // We may need to split up the request to honor the query string length limit,
                                                // so build it piece by piece.
                                        return registry[ moduleName ].module.exports;
                                },
 
-                               /**
-                                * @inheritdoc mw.inspect#runReports
-                                * @method
-                                */
-                               inspect: function () {
-                                       var args = slice.call( arguments );
-                                       mw.loader.using( 'mediawiki.inspect', function () {
-                                               mw.inspect.runReports.apply( mw.inspect, args );
-                                       } );
-                               },
-
                                /**
                                 * On browsers that implement the localStorage API, the module store serves as a
                                 * smart complement to the browser cache. Unlike the browser cache, the module store
diff --git a/tests/phpunit/data/registration/autoload_namespaces.json b/tests/phpunit/data/registration/autoload_namespaces.json
new file mode 100644 (file)
index 0000000..19c502c
--- /dev/null
@@ -0,0 +1,7 @@
+{
+       "manifest_version": 2,
+       "name": "WithAutoloadNamespaces",
+       "AutoloadNamespaces": {
+               "Test\\MediaWiki\\AutoLoader\\": "../autoloader/psr4/"
+       }
+}
index c196338..3909b30 100644 (file)
@@ -733,7 +733,7 @@ class ApiEditPageTest extends ApiTestCase {
                $dbw = wfGetDB( DB_MASTER );
                $dbw->update(
                        'revision',
-                       [ 'rev_timestamp' => wfTimestamp( TS_MW, time() - 86400 ) ],
+                       [ 'rev_timestamp' => $dbw->timestamp( time() - 86400 ) ],
                        [ 'rev_id' => $revId1 ],
                        __METHOD__
                );
index 50a59f9..7fd5f1d 100644 (file)
@@ -187,7 +187,7 @@ class RandomImageGenerator {
                $spec['height'] = mt_rand( $this->minHeight, $this->maxHeight );
                $spec['fill'] = $this->getRandomColor();
 
-               $diagonalLength = sqrt( pow( $spec['width'], 2 ) + pow( $spec['height'], 2 ) );
+               $diagonalLength = sqrt( $spec['width'] ** 2 + $spec['height'] ** 2 );
 
                $draws = [];
                for ( $i = 0; $i <= $this->shapesToDraw; $i++ ) {
index b434396..84c0c1c 100644 (file)
@@ -1893,15 +1893,31 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase {
 
                $this->database->setFlag( Database::DBO_TRX );
 
-               // Implicit transaction gets silently rolled back
+               // Implicit transaction does not get silently rolled back
                $this->database->begin( __METHOD__, Database::TRANSACTION_INTERNAL );
                call_user_func( $doError );
-               $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
-               $this->database->commit( __METHOD__, Database::FLUSHING_INTERNAL );
-               // phpcs:ignore
-               $this->assertLastSql( 'BEGIN; DELETE FROM error WHERE 1; ROLLBACK; BEGIN; DELETE FROM x WHERE field = \'1\'; COMMIT' );
+               try {
+                       $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( DBTransactionError $e ) {
+                       $this->assertEquals(
+                               'Cannot execute query from ' . __METHOD__ . ' while transaction status is ERROR.',
+                               $e->getMessage()
+                       );
+               }
+               try {
+                       $this->database->commit( __METHOD__, Database::FLUSHING_INTERNAL );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( DBTransactionError $e ) {
+                       $this->assertEquals(
+                               'Cannot execute query from ' . __METHOD__ . ' while transaction status is ERROR.',
+                               $e->getMessage()
+                       );
+               }
+               $this->database->rollback( __METHOD__, Database::FLUSHING_INTERNAL );
+               $this->assertLastSql( 'BEGIN; DELETE FROM error WHERE 1; ROLLBACK' );
 
-               // ... unless there were prior writes
+               // Likewise if there were prior writes
                $this->database->begin( __METHOD__, Database::TRANSACTION_INTERNAL );
                $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
                call_user_func( $doError );
index 3f0dae6..c12882b 100644 (file)
@@ -260,6 +260,16 @@ class DatabaseTest extends PHPUnit\Framework\TestCase {
 
                $lbFactory->commitMasterChanges( __METHOD__ );
                $this->assertFalse( $called, 'Not called in next round commit' );
+
+               $db->setFlag( DBO_TRX );
+               try {
+                       $db->onTransactionCommitOrIdle( function () {
+                               throw new RuntimeException( 'test' );
+                       } );
+                       $this->fail( "Exception not thrown" );
+               } catch ( RuntimeException $e ) {
+                       $this->assertTrue( $db->getFlag( DBO_TRX ) );
+               }
        }
 
        /**
index a372c8c..7120a91 100644 (file)
@@ -71,6 +71,17 @@ class ExtensionRegistryTest extends MediaWikiTestCase {
                ] );
        }
 
+       public function testReadFromQueueInitializeAutoloaderWithPsr4Namespaces() {
+               $registry = new ExtensionRegistry();
+               $registry->readFromQueue( [
+                       "{$this->dataDir}/autoload_namespaces.json" => 1
+               ] );
+               $this->assertTrue(
+                       class_exists( 'Test\\MediaWiki\\AutoLoader\\TestFooBar' ),
+                       "Registry initializes Autoloader from AutoloadNamespaces"
+               );
+       }
+
        /**
         * @dataProvider provideExportExtractedDataGlobals
         */
index e0b8c5e..c15eb2c 100644 (file)
@@ -962,7 +962,7 @@ mw.example();
        /**
         * @covers ResourceLoader::respond
         */
-       public function testRespond() {
+       public function testRespondEmpty() {
                $rl = $this->getMockBuilder( EmptyResourceLoader::class )
                        ->setMethods( [
                                'tryRespondNotModified',
@@ -978,6 +978,66 @@ mw.example();
                $rl->respond( $context );
        }
 
+       /**
+        * @covers ResourceLoader::respond
+        */
+       public function testRespondSimple() {
+               $module = new ResourceLoaderTestModule( [ 'script' => 'foo();' ] );
+               $rl = $this->getMockBuilder( EmptyResourceLoader::class )
+                       ->setMethods( [
+                               'measureResponseTime',
+                               'tryRespondNotModified',
+                               'sendResponseHeaders',
+                               'makeModuleResponse',
+                       ] )
+                       ->getMock();
+               $rl->register( 'test', $module );
+               $context = $this->getResourceLoaderContext(
+                       [ 'modules' => 'test', 'only' => null ],
+                       $rl
+               );
+
+               $rl->expects( $this->once() )->method( 'makeModuleResponse' )
+                       ->with( $context, [ 'test' => $module ] )
+                       ->willReturn( 'implement_foo;' );
+               $this->expectOutputRegex( '/^implement_foo;/' );
+
+               $rl->respond( $context );
+       }
+
+       /**
+        * @covers ResourceLoader::respond
+        */
+       public function testRespondInternalFailures() {
+               $module = new ResourceLoaderTestModule( [ 'script' => 'foo();' ] );
+               $rl = $this->getMockBuilder( EmptyResourceLoader::class )
+                       ->setMethods( [
+                               'measureResponseTime',
+                               'preloadModuleInfo',
+                               'getCombinedVersion',
+                               'tryRespondNotModified',
+                               'makeModuleResponse',
+                               'sendResponseHeaders',
+                       ] )
+                       ->getMock();
+               $rl->register( 'test', $module );
+               $context = $this->getResourceLoaderContext( [ 'modules' => 'test' ], $rl );
+               // Disable logging from outputErrorAndLog
+               $this->setLogger( 'exception', new Psr\Log\NullLogger() );
+
+               $rl->expects( $this->once() )->method( 'preloadModuleInfo' )
+                       ->willThrowException( new Exception( 'Preload error' ) );
+               $rl->expects( $this->once() )->method( 'getCombinedVersion' )
+                       ->willThrowException( new Exception( 'Version error' ) );
+               $rl->expects( $this->once() )->method( 'makeModuleResponse' )
+                       ->with( $context, [ 'test' => $module ] )
+                       ->willReturn( 'foo;' );
+               // Internal errors should be caught and logged without affecting module output
+               $this->expectOutputRegex( '/^\/\*.+Preload error.+Version error.+\*\/.*foo;/ms' );
+
+               $rl->respond( $context );
+       }
+
        /**
         * @covers ResourceLoader::measureResponseTime
         */
index 050ed83..8653bcd 100644 (file)
@@ -1110,27 +1110,27 @@ class LanguageTest extends LanguageClassesTestCase {
                                "1 gigabyte"
                        ],
                        [
-                               pow( 1024, 4 ),
+                               1024 ** 4,
                                "1 TB",
                                "1 terabyte"
                        ],
                        [
-                               pow( 1024, 5 ),
+                               1024 ** 5,
                                "1 PB",
                                "1 petabyte"
                        ],
                        [
-                               pow( 1024, 6 ),
+                               1024 ** 6,
                                "1 EB",
                                "1,024 exabyte"
                        ],
                        [
-                               pow( 1024, 7 ),
+                               1024 ** 7,
                                "1 ZB",
                                "1 zetabyte"
                        ],
                        [
-                               pow( 1024, 8 ),
+                               1024 ** 8,
                                "1 YB",
                                "1 yottabyte"
                        ],
@@ -1173,37 +1173,37 @@ class LanguageTest extends LanguageClassesTestCase {
                                "1 megabit per second"
                        ],
                        [
-                               pow( 10, 9 ),
+                               10 ** 9,
                                "1 Gbps",
                                "1 gigabit per second"
                        ],
                        [
-                               pow( 10, 12 ),
+                               10 ** 12,
                                "1 Tbps",
                                "1 terabit per second"
                        ],
                        [
-                               pow( 10, 15 ),
+                               10 ** 15,
                                "1 Pbps",
                                "1 petabit per second"
                        ],
                        [
-                               pow( 10, 18 ),
+                               10 ** 18,
                                "1 Ebps",
                                "1 exabit per second"
                        ],
                        [
-                               pow( 10, 21 ),
+                               10 ** 21,
                                "1 Zbps",
                                "1 zetabit per second"
                        ],
                        [
-                               pow( 10, 24 ),
+                               10 ** 24,
                                "1 Ybps",
                                "1 yottabit per second"
                        ],
                        [
-                               pow( 10, 27 ),
+                               10 ** 27,
                                "1,000 Ybps",
                                "1,000 yottabits per second"
                        ],
index cd68fa5..19e93de 100644 (file)
@@ -27,6 +27,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
        }
 
        private function insertLoggingData() {
+               $dbw = wfGetDB( DB_MASTER );
                $logs = [];
 
                // Manual patrolling
@@ -35,7 +36,9 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                        'log_action' => 'patrol',
                        'log_user' => 7251,
                        'log_params' => '',
-                       'log_timestamp' => 20041223210426
+                       'log_timestamp' => $dbw->timestamp( '20041223210426' ),
+                       'log_namespace' => NS_MAIN,
+                       'log_title' => 'DeleteAutoPatrolLogs',
                ];
 
                // Autopatrol #1
@@ -44,7 +47,9 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                        'log_action' => 'autopatrol',
                        'log_user' => 7252,
                        'log_params' => '',
-                       'log_timestamp' => 20051223210426
+                       'log_timestamp' => $dbw->timestamp( '20051223210426' ),
+                       'log_namespace' => NS_MAIN,
+                       'log_title' => 'DeleteAutoPatrolLogs',
                ];
 
                // Block
@@ -53,7 +58,9 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                        'log_action' => 'block',
                        'log_user' => 7253,
                        'log_params' => '',
-                       'log_timestamp' => 20061223210426
+                       'log_timestamp' => $dbw->timestamp( '20061223210426' ),
+                       'log_namespace' => NS_MAIN,
+                       'log_title' => 'DeleteAutoPatrolLogs',
                ];
 
                // Very old/ invalid patrol
@@ -62,7 +69,9 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                        'log_action' => 'patrol',
                        'log_user' => 7253,
                        'log_params' => 'nanana',
-                       'log_timestamp' => 20061223210426
+                       'log_timestamp' => $dbw->timestamp( '20061223210426' ),
+                       'log_namespace' => NS_MAIN,
+                       'log_title' => 'DeleteAutoPatrolLogs',
                ];
 
                // Autopatrol #2
@@ -71,7 +80,9 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                        'log_action' => 'autopatrol',
                        'log_user' => 7254,
                        'log_params' => '',
-                       'log_timestamp' => 20071223210426
+                       'log_timestamp' => $dbw->timestamp( '20071223210426' ),
+                       'log_namespace' => NS_MAIN,
+                       'log_title' => 'DeleteAutoPatrolLogs',
                ];
 
                // Autopatrol #3 old way
@@ -80,7 +91,9 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                        'log_action' => 'patrol',
                        'log_user' => 7255,
                        'log_params' => serialize( [ '6::auto' => true ] ),
-                       'log_timestamp' => 20081223210426
+                       'log_timestamp' => $dbw->timestamp( '20081223210426' ),
+                       'log_namespace' => NS_MAIN,
+                       'log_title' => 'DeleteAutoPatrolLogs',
                ];
 
                // Manual patrol #2 old way
@@ -89,7 +102,9 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                        'log_action' => 'patrol',
                        'log_user' => 7256,
                        'log_params' => serialize( [ '6::auto' => false ] ),
-                       'log_timestamp' => 20091223210426
+                       'log_timestamp' => $dbw->timestamp( '20091223210426' ),
+                       'log_namespace' => NS_MAIN,
+                       'log_title' => 'DeleteAutoPatrolLogs',
                ];
 
                // Autopatrol #4 very old way
@@ -98,7 +113,9 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                        'log_action' => 'patrol',
                        'log_user' => 7257,
                        'log_params' => "9227851\n0\n1",
-                       'log_timestamp' => 20081223210426
+                       'log_timestamp' => $dbw->timestamp( '20081223210426' ),
+                       'log_namespace' => NS_MAIN,
+                       'log_title' => 'DeleteAutoPatrolLogs',
                ];
 
                // Manual patrol #3 very old way
@@ -107,10 +124,12 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                        'log_action' => 'patrol',
                        'log_user' => 7258,
                        'log_params' => "9227851\n0\n0",
-                       'log_timestamp' => 20091223210426
+                       'log_timestamp' => $dbw->timestamp( '20091223210426' ),
+                       'log_namespace' => NS_MAIN,
+                       'log_title' => 'DeleteAutoPatrolLogs',
                ];
 
-               wfGetDB( DB_MASTER )->insert( 'logging', $logs );
+               $dbw->insert( 'logging', $logs );
        }
 
        public function runProvider() {
index 07b18f5..fe38a98 100644 (file)
@@ -29,7 +29,7 @@ class ParserTestTopLevelSuite extends PHPUnit_Framework_TestSuite {
        /** Include non core files as set in $wgParserTestFiles */
        const NO_CORE = 2;
        /** Include anything set via $wgParserTestFiles */
-       const WITH_ALL = 3; # CORE_ONLY | NO_CORE
+       const WITH_ALL = self::CORE_ONLY | self::NO_CORE;
 
        /** @} */
 
index 1922de5..1ec6f72 100644 (file)
@@ -95,6 +95,8 @@ return [
                        'tests/qunit/suites/resources/mediawiki.rcfilters/dm.SavedQueryItemModel.test.js',
                        'tests/qunit/suites/resources/mediawiki.rcfilters/dm.SavedQueriesModel.test.js',
                        'tests/qunit/suites/resources/mediawiki.rcfilters/UriProcessor.test.js',
+                       'tests/qunit/suites/resources/mediawiki.widgets/' .
+                               'MediaSearch/mediawiki.widgets.APIResultsQueue.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.cldr.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js',
@@ -137,6 +139,7 @@ return [
                        'mediawiki.experiments',
                        'mediawiki.inspect',
                        'mediawiki.visibleTimeout',
+                       'mediawiki.widgets.MediaSearch',
                        'test.mediawiki.qunit.testrunner',
                ],
        ]
diff --git a/tests/qunit/suites/resources/mediawiki.widgets/MediaSearch/mediawiki.widgets.APIResultsQueue.test.js b/tests/qunit/suites/resources/mediawiki.widgets/MediaSearch/mediawiki.widgets.APIResultsQueue.test.js
new file mode 100644 (file)
index 0000000..92970bb
--- /dev/null
@@ -0,0 +1,197 @@
+/*!
+ * VisualEditor DataModel ResourceQueue tests.
+ *
+ * @copyright 2011-2018 VisualEditor Team and others; see http://ve.mit-license.org
+ */
+
+QUnit.module( 'mediawiki.widgets.APIResultsQueue' );
+
+( function ( $, mw ) {
+       var itemCounter, FullResourceProvider, EmptyResourceProvider, SingleResultResourceProvider;
+
+       itemCounter = 0;
+       FullResourceProvider = function ( config ) {
+               this.timer = null;
+               this.responseDelay = 1;
+               // Inheritance
+               FullResourceProvider.super.call( this, '', config );
+       };
+       EmptyResourceProvider = function ( config ) {
+               this.timer = null;
+               this.responseDelay = 1;
+               // Inheritance
+               EmptyResourceProvider.super.call( this, '', config );
+       };
+       SingleResultResourceProvider = function ( config ) {
+               this.timer = null;
+               this.responseDelay = 1;
+               // Inheritance
+               SingleResultResourceProvider.super.call( this, '', config );
+       };
+
+       OO.inheritClass( FullResourceProvider, mw.widgets.APIResultsProvider );
+       OO.inheritClass( EmptyResourceProvider, mw.widgets.APIResultsProvider );
+       OO.inheritClass( SingleResultResourceProvider, mw.widgets.APIResultsProvider );
+
+       FullResourceProvider.prototype.getResults = function ( howMany ) {
+               var i, timer,
+                       result = [],
+                       deferred = $.Deferred();
+
+               for ( i = itemCounter; i < itemCounter + howMany; i++ ) {
+                       result.push( 'result ' + ( i + 1 ) );
+               }
+               itemCounter = i;
+
+               timer = setTimeout(
+                       function () {
+                               // Always resolve with some values
+                               deferred.resolve( result );
+                       },
+                       this.responseDelay );
+
+               return deferred.promise( { abort: function () { clearTimeout( timer ); } } );
+       };
+
+       EmptyResourceProvider.prototype.getResults = function () {
+               var provider = this,
+                       deferred = $.Deferred(),
+                       timer = setTimeout(
+                               function () {
+                                       provider.toggleDepleted( true );
+                                       // Always resolve with empty value
+                                       deferred.resolve( [] );
+                               },
+                               this.responseDelay );
+
+               return deferred.promise( { abort: function () { clearTimeout( timer ); } } );
+       };
+
+       SingleResultResourceProvider.prototype.getResults = function ( howMany ) {
+               var timer,
+                       provider = this,
+                       deferred = $.Deferred();
+
+               timer = setTimeout(
+                       function () {
+                               provider.toggleDepleted( howMany > 1 );
+                               // Always resolve with one value
+                               deferred.resolve( [ 'one result (' + ( itemCounter++ + 1 ) + ')' ] );
+                       },
+                       this.responseDelay );
+
+               return deferred.promise( { abort: function () { clearTimeout( timer ); } } );
+       };
+
+       /* Tests */
+
+       QUnit.test( 'Query providers', function ( assert ) {
+               var done = assert.async(),
+                       providers = [
+                               new FullResourceProvider(),
+                               new EmptyResourceProvider(),
+                               new SingleResultResourceProvider()
+                       ],
+                       queue = new mw.widgets.APIResultsQueue( {
+                               threshold: 2
+                       } );
+
+               assert.expect( 15 );
+
+               // Add providers to queue
+               queue.setProviders( providers );
+
+               // Set parameters and fetch
+               queue.setParams( { foo: 'bar' } );
+
+               queue.get( 10 )
+                       .then( function ( data ) {
+                               // Check that we received all requested results
+                               assert.equal( data.length, 10, 'Query 1: Results received.' );
+                               // We've asked for 10 items + 2 threshold from all providers.
+                               // Provider 1 returned 12 results
+                               // Provider 2 returned 0 results
+                               // Provider 3 returned 1 results
+                               // Overall 13 results. 10 were retrieved. 3 left in queue.
+                               assert.equal( queue.getQueueSize(), 3, 'Query 1: Remaining queue size.' );
+
+                               // Check if sources are depleted
+                               assert.ok( !providers[ 0 ].isDepleted(), 'Query 1: Full provider not depleted.' );
+                               assert.ok( providers[ 1 ].isDepleted(), 'Query 1: Empty provider is depleted.' );
+                               assert.ok( providers[ 2 ].isDepleted(), 'Query 1: Single result provider is depleted.' );
+
+                               // Ask for more results
+                               return queue.get( 10 );
+                       } )
+                       .then( function ( data1 ) {
+                               // This time, only provider 1 was queried, because the other
+                               // two were marked as depleted.
+                               // * We asked for 10 items
+                               // * There are currently 3 items in the queue
+                               // * The queue queried provider #1 for 12 items
+                               // * The queue returned 10 results as requested
+                               // * 5 results are now left in the queue.
+                               assert.equal( data1.length, 10, 'Query 1: Second set of results received.' );
+                               assert.equal( queue.getQueueSize(), 5, 'Query 1: Remaining queue size.' );
+
+                               // Change the query
+                               queue.setParams( { foo: 'baz' } );
+                               // Check if sources are depleted
+                               assert.ok( !providers[ 0 ].isDepleted(), 'Query 2: Full provider not depleted.' );
+                               assert.ok( !providers[ 1 ].isDepleted(), 'Query 2: Empty provider not depleted.' );
+                               assert.ok( !providers[ 2 ].isDepleted(), 'Query 2: Single result provider not depleted.' );
+
+                               return queue.get( 10 );
+                       } )
+                       .then( function ( data2 ) {
+                               // This should be the same as the very first result
+                               assert.equal( data2.length, 10, 'Query 2: Results received.' );
+                               assert.equal( queue.getQueueSize(), 3, 'Query 2: Remaining queue size.' );
+                               // Check if sources are depleted
+                               assert.ok( !providers[ 0 ].isDepleted(), 'Query 2: Full provider not depleted.' );
+                               assert.ok( providers[ 1 ].isDepleted(), 'Query 2: Empty provider is not depleted.' );
+                               assert.ok( providers[ 2 ].isDepleted(), 'Query 2: Single result provider is not depleted.' );
+                       } )
+                       // Finish the async test
+                       .then( done );
+       } );
+
+       QUnit.test( 'Abort providers', function ( assert ) {
+               var done = assert.async(),
+                       completed = false,
+                       biggerQueue = new mw.widgets.APIResultsQueue( {
+                               threshold: 5
+                       } ),
+                       providers = [
+                               new FullResourceProvider(),
+                               new EmptyResourceProvider(),
+                               new SingleResultResourceProvider()
+                       ];
+
+               assert.expect( 1 );
+
+               // Make the delay higher
+               providers.forEach( function ( provider ) { provider.responseDelay = 3; } );
+
+               // Add providers to queue
+               biggerQueue.setProviders( providers );
+
+               biggerQueue.setParams( { foo: 'bar' } );
+               biggerQueue.get( 100 )
+                       .always( function () {
+                               // This should only run if the promise wasn't aborted
+                               completed = true;
+                       } );
+
+               // Make the delay higher
+               providers.forEach( function ( provider ) { provider.responseDelay = 5; } );
+
+               biggerQueue.setParams( { foo: 'baz' } );
+               biggerQueue.get( 10 )
+                       .then( function () {
+                               assert.ok( !completed, 'Provider promises aborted.' );
+                       } )
+                       // Finish the async test
+                       .then( done );
+       } );
+}( jQuery, mediaWiki ) );