Merge "resourceloader: Remove support for $context === null in getContentObj"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sat, 28 Sep 2019 20:00:24 +0000 (20:00 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 28 Sep 2019 20:00:24 +0000 (20:00 +0000)
194 files changed:
.pipeline/blubber.yaml [new file with mode: 0644]
.pipeline/config.yaml [new file with mode: 0644]
.pipeline/dev_prereq.sh [new file with mode: 0755]
Gruntfile.js
RELEASE-NOTES-1.34
autoload.php
composer.json
docs/Introduction.md
docs/hooks.txt
includes/CommentStore.php
includes/DefaultSettings.php
includes/FauxRequest.php
includes/Html.php
includes/MediaWiki.php
includes/Rest/Router.php
includes/Revision/RevisionStore.php
includes/ServiceWiring.php
includes/Title.php
includes/api/ApiMain.php
includes/api/ApiQueryBacklinksprop.php
includes/api/ApiQueryBase.php
includes/api/i18n/ja.json
includes/db/MWLBFactory.php
includes/exception/BadRequestError.php
includes/exception/ErrorPageError.php
includes/exception/PermissionsError.php
includes/exception/ThrottledError.php
includes/exception/UserNotLoggedIn.php
includes/filebackend/FileBackendGroup.php
includes/filerepo/FileRepo.php
includes/filerepo/file/LocalFileLockError.php
includes/htmlform/CollapsibleFieldsetLayout.php [new file with mode: 0644]
includes/htmlform/OOUIHTMLForm.php
includes/http/HttpRequestFactory.php
includes/http/MWHttpRequest.php
includes/installer/DatabaseInstaller.php
includes/installer/MysqlUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/installer/WebInstaller.php
includes/installer/WebInstallerOptions.php
includes/installer/WebInstallerRestart.php
includes/installer/i18n/de.json
includes/installer/i18n/ja.json
includes/installer/i18n/nb.json
includes/installer/i18n/uk.json
includes/libs/StringUtils.php
includes/libs/objectcache/wancache/WANObjectCache.php
includes/libs/rdbms/database/DatabaseMysqlBase.php
includes/libs/rdbms/database/position/MySQLMasterPos.php
includes/libs/rdbms/loadbalancer/LoadBalancerSingle.php
includes/logging/LogPager.php
includes/logging/ProtectLogFormatter.php
includes/page/Article.php
includes/parser/Parser.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderClientHtml.php
includes/resourceloader/ResourceLoaderContext.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderLanguageDataModule.php
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/resourceloader/ResourceLoaderUserDefaultsModule.php
includes/resourceloader/ResourceLoaderUserOptionsModule.php
includes/resourceloader/ResourceLoaderUserTokensModule.php
includes/site/MediaWikiSite.php
includes/site/Site.php
includes/specialpage/QueryPage.php
includes/specials/SpecialContributions.php
includes/specials/pagers/NewFilesPager.php
languages/i18n/ban.json
languages/i18n/bg.json
languages/i18n/diq.json
languages/i18n/en.json
languages/i18n/exif/af.json
languages/i18n/exif/nap.json
languages/i18n/exif/nds-nl.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/gl.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/khw.json
languages/i18n/kiu.json
languages/i18n/ko.json
languages/i18n/lad.json
languages/i18n/lv.json
languages/i18n/lzh.json
languages/i18n/min.json
languages/i18n/ml.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/nds-nl.json
languages/i18n/nds.json
languages/i18n/nl.json
languages/i18n/nqo.json
languages/i18n/qqq.json
languages/i18n/roa-tara.json
languages/i18n/sv.json
languages/i18n/te.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
maintenance/Maintenance.php
maintenance/archives/patch-archive-ar_comment_id.sql [new file with mode: 0644]
maintenance/archives/patch-comment-table.sql
maintenance/archives/patch-drop-archive-ar_usertext_timestamp.sql [new file with mode: 0644]
maintenance/archives/patch-drop-archive-usertext_timestamp.sql [new file with mode: 0644]
maintenance/archives/patch-drop-user-fields.sql
maintenance/archives/patch-filearchive-fa_description_id.sql [new file with mode: 0644]
maintenance/archives/patch-image-img_description-default.sql [new file with mode: 0644]
maintenance/archives/patch-image_comment_temp-table.sql [new file with mode: 0644]
maintenance/archives/patch-ipblocks-ipb_reason_id.sql [new file with mode: 0644]
maintenance/archives/patch-logging-log_comment_id.sql [new file with mode: 0644]
maintenance/archives/patch-oldimage-oi_description_id.sql [new file with mode: 0644]
maintenance/archives/patch-protected_titles-pt_reason_id.sql [new file with mode: 0644]
maintenance/archives/patch-recentchanges-rc_comment_id.sql [new file with mode: 0644]
maintenance/archives/patch-rename-mysql-user_newtalk-indexes.sql [new file with mode: 0644]
maintenance/archives/patch-revision-rev_comment-default.sql [new file with mode: 0644]
maintenance/archives/patch-revision_comment_temp-table.sql [new file with mode: 0644]
maintenance/cleanupTable.inc
maintenance/migrateArchiveText.php
maintenance/postgres/archives/patch-comment-table.sql
maintenance/postgres/archives/patch-image_comment_temp-table.sql [new file with mode: 0644]
maintenance/postgres/archives/patch-revision_comment_temp-table.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-archive-ar_comment_id.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-comment-table.sql [deleted file]
maintenance/sqlite/archives/patch-image-img_description-default.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-ipblocks-ipb_reason_id.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-logging-log_comment_id.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-oldimage-oi_description_id.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-protected_titles-pt_reason_id.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-recentchanges-rc_comment_id.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-revision-rev_comment-default.sql [new file with mode: 0644]
maintenance/storage/checkStorage.php
maintenance/storage/compressOld.php
maintenance/storage/fixT22757.php [deleted file]
maintenance/storage/recompressTracked.php
maintenance/updateSpecialPages.php
maintenance/userDupes.inc
resources/Resources.php
resources/src/jquery/jquery.makeCollapsible.styles.less
resources/src/mediawiki.base/mediawiki.errorLogger.js
resources/src/mediawiki.htmlform.ooui.styles.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.WatchlistTopSectionWidget.less
resources/src/mediawiki.special.recentchanges.js
resources/src/mediawiki.special/contributions.less [new file with mode: 0644]
resources/src/mediawiki.special/special.less
resources/src/mediawiki.widgets/mw.widgets.TitleOptionWidget.js
resources/src/startup/mediawiki.js
resources/src/startup/mediawiki.requestIdleCallback.js
tests/common/TestsAutoLoader.php
tests/phpunit/MediaWikiIntegrationTestCase.php
tests/phpunit/includes/CommentStoreTest.php
tests/phpunit/includes/HtmlTest.php
tests/phpunit/includes/Revision/NoContentModelRevisionStoreDbTest.php [deleted file]
tests/phpunit/includes/Revision/PreMcrRevisionStoreDbTest.php [deleted file]
tests/phpunit/includes/Revision/PreMcrSchemaOverride.php [deleted file]
tests/phpunit/includes/Revision/RevisionQueryInfoTest.php
tests/phpunit/includes/Revision/RevisionStoreDbTestBase.php
tests/phpunit/includes/Revision/RevisionStoreFactoryTest.php
tests/phpunit/includes/Revision/RevisionStoreTest.php
tests/phpunit/includes/RevisionNoContentModelDbTest.php [deleted file]
tests/phpunit/includes/RevisionPreMcrDbTest.php [deleted file]
tests/phpunit/includes/api/ApiMainTest.php
tests/phpunit/includes/api/ApiQuerySiteinfoTest.php
tests/phpunit/includes/changes/CategoryMembershipChangeTest.php
tests/phpunit/includes/content/WikitextContentTest.php
tests/phpunit/includes/db/LBFactoryTest.php
tests/phpunit/includes/db/LoadBalancerTest.php
tests/phpunit/includes/filebackend/FileBackendTest.php
tests/phpunit/includes/libs/StringUtilsTest.php
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php
tests/phpunit/includes/logging/ProtectLogFormatterTest.php
tests/phpunit/includes/page/PageArchivePreMcrTest.php [deleted file]
tests/phpunit/includes/page/WikiPageNoContentModelDbTest.php [deleted file]
tests/phpunit/includes/page/WikiPagePreMcrDbTest.php [deleted file]
tests/phpunit/includes/pager/RangeChronologicalPagerTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderContextTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderImageTest.php [deleted file]
tests/phpunit/includes/resourceloader/ResourceLoaderStartUpModuleTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
tests/phpunit/includes/session/CookieSessionProviderTest.php
tests/phpunit/unit/includes/FauxResponseTest.php
tests/phpunit/unit/includes/changes/ChangesListFilterGroupTest.php
tests/phpunit/unit/includes/diff/DiffOpTest.php
tests/phpunit/unit/includes/language/LanguageCodeTest.php
tests/phpunit/unit/includes/resourceloader/ResourceLoaderImageTest.php [new file with mode: 0644]
tests/phpunit/unit/includes/session/SessionUnitTest.php
tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js

diff --git a/.pipeline/blubber.yaml b/.pipeline/blubber.yaml
new file mode 100644 (file)
index 0000000..13ad966
--- /dev/null
@@ -0,0 +1,14 @@
+version: v4
+base: docker-registry.wikimedia.org/dev/stretch-php72-fpm-apache2
+
+lives:
+  in: /var/www/html
+
+variants:
+  dev:
+    runs:
+      insecurely: true
+    builder:
+      command: [.pipeline/dev_prereq.sh]
+      requirements: [.pipeline, .pipeline/dev_prereq.sh, composer.json]
+    copies: [local]
diff --git a/.pipeline/config.yaml b/.pipeline/config.yaml
new file mode 100644 (file)
index 0000000..f69ba98
--- /dev/null
@@ -0,0 +1,8 @@
+pipelines:
+  publish:
+    blubberfile: blubber.yaml
+    stages:
+      - name: publish
+        build: dev
+        publish:
+          image: true
diff --git a/.pipeline/dev_prereq.sh b/.pipeline/dev_prereq.sh
new file mode 100755 (executable)
index 0000000..a1f4bd0
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+mkdir /tmp/php
+mkdir -p extensions
+
+git clone --depth 1 https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor.git /var/www/html/extensions/VisualEditor
+git clone --depth 1 https://gerrit.wikimedia.org/r/mediawiki/skins/Vector /var/www/html/skins/Vector
+cd /var/www/html/extensions/VisualEditor
+git submodule update --depth 1 --init
+
+cd /var/www/html
+composer install
+cat <<PHP > LocalSettings.php
+<?php
+require_once '/var/config/LocalSettings.php';
+PHP
index 8115ea2..9615330 100644 (file)
@@ -117,7 +117,7 @@ module.exports = function ( grunt ) {
                                        included: true,
                                        served: false
                                } ],
-                               logLevel: 'DEBUG',
+                               logLevel: ( process.env.ZUUL_PROJECT ? 'DEBUG' : 'INFO' ),
                                frameworks: [ 'qunit' ],
                                reporters: [ 'mocha' ],
                                singleRun: true,
index 508ba08..0d6f1e1 100644 (file)
@@ -126,6 +126,9 @@ $wgPasswordPolicy['policies']['default']['PasswordNotInLargeBlacklist'] = false;
 * (T222388) API modules can now be specified as an ObjectFactory spec,
   allowing the construction of modules that require services to be injected
   in their constructor.
+* (T117736) The function signature of SpecialContributions::getForm::filters
+  has changed. It now expects definitions of additional filter fields as array
+  rather than string.
 
 === External library changes in 1.34 ===
 
@@ -147,6 +150,7 @@ $wgPasswordPolicy['policies']['default']['PasswordNotInLargeBlacklist'] = false;
 * Updated wikimedia/timestamp from 2.2.0 to 3.0.0.
 * Updated wikimedia/xmp-reader from 0.6.2 to 0.6.3.
 * Updated mediawiki/mediawiki-phan-config from 0.6.0 to 0.6.1 (dev-only).
+* Updated wikimedia/avro from 1.8.0 to 1.9.0 (dev-only).
 * …
 
 ==== Removed external libraries ====
@@ -572,6 +576,8 @@ because of Phabricator reports.
 * Specifying a SpecialPage object for the list of special pages (either through
   the SpecialPage_initList hook or by adding to $wgSpecialPages) is now
   deprecated.
+* WebInstaller::getInfoBox(), getWarningBox() and getErrorBox() are deprecated.
+  Use Html::errorBox() or Html::warningBox() instead.
 * Use of ActorMigration with 'ar_user', 'img_user', 'oi_user', 'fa_user',
   'rc_user', 'log_user', and 'ipb_by' is deprecated. Queries should be adjusted
   to use the corresponding actor fields directly. Note that use with
index 7ff29ce..dc57ff6 100644 (file)
@@ -285,6 +285,7 @@ $wgAutoloadLocalClasses = [
        'CloneDatabase' => __DIR__ . '/includes/db/CloneDatabase.php',
        'CodeCleanerGlobalsPass' => __DIR__ . '/maintenance/CodeCleanerGlobalsPass.inc',
        'CodeContentHandler' => __DIR__ . '/includes/content/CodeContentHandler.php',
+       'CollapsibleFieldsetLayout' => __DIR__ . '/includes/htmlform/CollapsibleFieldsetLayout.php',
        'Collation' => __DIR__ . '/includes/collation/Collation.php',
        'CollationCkb' => __DIR__ . '/includes/collation/CollationCkb.php',
        'CommandLineInc' => __DIR__ . '/maintenance/commandLine.inc',
@@ -531,7 +532,6 @@ $wgAutoloadLocalClasses = [
        'FixDefaultJsonContentPages' => __DIR__ . '/maintenance/fixDefaultJsonContentPages.php',
        'FixDoubleRedirects' => __DIR__ . '/maintenance/fixDoubleRedirects.php',
        'FixExtLinksProtocolRelative' => __DIR__ . '/maintenance/fixExtLinksProtocolRelative.php',
-       'FixT22757' => __DIR__ . '/maintenance/storage/fixT22757.php',
        'FixTimestamps' => __DIR__ . '/maintenance/fixTimestamps.php',
        'FixUserRegistration' => __DIR__ . '/maintenance/fixUserRegistration.php',
        'ForeignAPIFile' => __DIR__ . '/includes/filerepo/file/ForeignAPIFile.php',
index 1f2776c..bd34b92 100644 (file)
@@ -74,7 +74,7 @@
                "nmred/kafka-php": "0.1.5",
                "phpunit/phpunit": "4.8.36 || ^6.5",
                "psy/psysh": "0.9.9",
-               "wikimedia/avro": "1.8.0",
+               "wikimedia/avro": "1.9.0",
                "wikimedia/testing-access-wrapper": "~1.0",
                "wmde/hamcrest-html-matchers": "^0.1.0",
                "mediawiki/mediawiki-phan-config": "0.7.1",
index 4814599..e54b5b1 100644 (file)
@@ -1,7 +1,6 @@
 Introduction {#mainpage}
 =======
 
-Welcome on MediaWiki autogenerated documentation system.
+Welcome to the MediaWiki autogenerated documentation system.
 
-If you are looking to use, install or configure your wiki, you probably
-want to look at the main site: <https://www.mediawiki.org/>.
+If you are looking to use, install or configure your wiki, see the main site: <https://www.mediawiki.org/>.
index 55ba06e..4261b0b 100644 (file)
@@ -3177,7 +3177,7 @@ $row: Revision information from the database
 'SpecialContributions::getForm::filters': Called with a list of filters to render
 on Special:Contributions.
 $sp: SpecialContributions object, for context
-&$filters: List of filters rendered as HTML
+&$filters: List of filter object definitions (compatible with OOUI form)
 
 'SpecialListusersDefaultQuery': Called right before the end of
 UsersPager::getDefaultQuery().
index 994a064..9054e7a 100644 (file)
@@ -83,7 +83,8 @@ class CommentStore {
        protected $key = null;
 
        /**
-        * @var int One of the MIGRATION_* constants
+        * @var int One of the MIGRATION_* constants, or an appropriate combination
+        *  of SCHEMA_COMPAT_* constants.
         * @todo Deprecate and remove once extensions seem unlikely to need to use
         *  it for migration anymore.
         */
@@ -98,11 +99,19 @@ class CommentStore {
        /**
         * @param Language $lang Language to use for comment truncation. Defaults
         *  to content language.
-        * @param int $migrationStage One of the MIGRATION_* constants. Always
-        *  MIGRATION_NEW for MediaWiki core since 1.33.
+        * @param int $stage One of the MIGRATION_* constants, or an appropriate
+        *  combination of SCHEMA_COMPAT_* constants. Always MIGRATION_NEW for
+        *  MediaWiki core since 1.33.
         */
-       public function __construct( Language $lang, $migrationStage ) {
-               $this->stage = $migrationStage;
+       public function __construct( Language $lang, $stage ) {
+               if ( ( $stage & SCHEMA_COMPAT_WRITE_BOTH ) === 0 ) {
+                       throw new InvalidArgumentException( '$stage must include a write mode' );
+               }
+               if ( ( $stage & SCHEMA_COMPAT_READ_BOTH ) === 0 ) {
+                       throw new InvalidArgumentException( '$stage must include a read mode' );
+               }
+
+               $this->stage = $stage;
                $this->lang = $lang;
        }
 
@@ -166,21 +175,21 @@ class CommentStore {
        public function getFields( $key = null ) {
                $key = $this->getKey( $key );
                $fields = [];
-               if ( $this->stage === MIGRATION_OLD ) {
+               if ( ( $this->stage & SCHEMA_COMPAT_READ_BOTH ) === SCHEMA_COMPAT_READ_OLD ) {
                        $fields["{$key}_text"] = $key;
                        $fields["{$key}_data"] = 'NULL';
                        $fields["{$key}_cid"] = 'NULL';
-               } else {
-                       if ( $this->stage < MIGRATION_NEW ) {
+               } else { // READ_BOTH or READ_NEW
+                       if ( $this->stage & SCHEMA_COMPAT_READ_OLD ) {
                                $fields["{$key}_old"] = $key;
                        }
 
                        $tempTableStage = isset( $this->tempTables[$key] )
                                ? $this->tempTables[$key]['stage'] : MIGRATION_NEW;
-                       if ( $tempTableStage < MIGRATION_NEW ) {
+                       if ( $tempTableStage & SCHEMA_COMPAT_READ_OLD ) {
                                $fields["{$key}_pk"] = $this->tempTables[$key]['joinPK'];
                        }
-                       if ( $tempTableStage > MIGRATION_OLD ) {
+                       if ( $tempTableStage & SCHEMA_COMPAT_READ_NEW ) {
                                $fields["{$key}_id"] = "{$key}_id";
                        }
                }
@@ -211,21 +220,21 @@ class CommentStore {
                        $fields = [];
                        $joins = [];
 
-                       if ( $this->stage === MIGRATION_OLD ) {
+                       if ( ( $this->stage & SCHEMA_COMPAT_READ_BOTH ) === SCHEMA_COMPAT_READ_OLD ) {
                                $fields["{$key}_text"] = $key;
                                $fields["{$key}_data"] = 'NULL';
                                $fields["{$key}_cid"] = 'NULL';
-                       } else {
-                               $join = $this->stage === MIGRATION_NEW ? 'JOIN' : 'LEFT JOIN';
+                       } else { // READ_BOTH or READ_NEW
+                               $join = ( $this->stage & SCHEMA_COMPAT_READ_OLD ) ? 'LEFT JOIN' : 'JOIN';
 
                                $tempTableStage = isset( $this->tempTables[$key] )
                                        ? $this->tempTables[$key]['stage'] : MIGRATION_NEW;
-                               if ( $tempTableStage < MIGRATION_NEW ) {
+                               if ( $tempTableStage & SCHEMA_COMPAT_READ_OLD ) {
                                        $t = $this->tempTables[$key];
                                        $alias = "temp_$key";
                                        $tables[$alias] = $t['table'];
                                        $joins[$alias] = [ $join, "{$alias}.{$t['pk']} = {$t['joinPK']}" ];
-                                       if ( $tempTableStage === MIGRATION_OLD ) {
+                                       if ( ( $tempTableStage & SCHEMA_COMPAT_READ_BOTH ) === SCHEMA_COMPAT_READ_OLD ) {
                                                $joinField = "{$alias}.{$t['field']}";
                                        } else {
                                                // Nothing hits this code path for now, but will in the future when we set
@@ -245,7 +254,7 @@ class CommentStore {
                                $tables[$alias] = 'comment';
                                $joins[$alias] = [ $join, "{$alias}.comment_id = {$joinField}" ];
 
-                               if ( $this->stage === MIGRATION_NEW ) {
+                               if ( ( $this->stage & SCHEMA_COMPAT_READ_BOTH ) === SCHEMA_COMPAT_READ_NEW ) {
                                        $fields["{$key}_text"] = "{$alias}.comment_text";
                                } else {
                                        $fields["{$key}_text"] = "COALESCE( {$alias}.comment_text, $key )";
@@ -282,13 +291,15 @@ class CommentStore {
                        $cid = $row["{$key}_cid"] ?? null;
                        $text = $row["{$key}_text"];
                        $data = $row["{$key}_data"];
-               } elseif ( $this->stage === MIGRATION_OLD ) {
+               } elseif ( ( $this->stage & SCHEMA_COMPAT_READ_BOTH ) === SCHEMA_COMPAT_READ_OLD ) {
                        $cid = null;
                        if ( $fallback && isset( $row[$key] ) ) {
                                wfLogWarning( "Using deprecated fallback handling for comment $key" );
                                $text = $row[$key];
                        } else {
-                               wfLogWarning( "Missing {$key}_text and {$key}_data fields in row with MIGRATION_OLD" );
+                               wfLogWarning(
+                                       "Missing {$key}_text and {$key}_data fields in row with MIGRATION_OLD / READ_OLD"
+                               );
                                $text = '';
                        }
                        $data = null;
@@ -296,7 +307,7 @@ class CommentStore {
                        $tempTableStage = isset( $this->tempTables[$key] )
                                ? $this->tempTables[$key]['stage'] : MIGRATION_NEW;
                        $row2 = null;
-                       if ( $tempTableStage > MIGRATION_OLD && array_key_exists( "{$key}_id", $row ) ) {
+                       if ( ( $tempTableStage & SCHEMA_COMPAT_READ_NEW ) && array_key_exists( "{$key}_id", $row ) ) {
                                if ( !$db ) {
                                        throw new InvalidArgumentException(
                                                "\$row does not contain fields needed for comment $key and getComment(), but "
@@ -311,7 +322,9 @@ class CommentStore {
                                        __METHOD__
                                );
                        }
-                       if ( !$row2 && $tempTableStage < MIGRATION_NEW && array_key_exists( "{$key}_pk", $row ) ) {
+                       if ( !$row2 && ( $tempTableStage & SCHEMA_COMPAT_READ_OLD ) &&
+                               array_key_exists( "{$key}_pk", $row )
+                       ) {
                                if ( !$db ) {
                                        throw new InvalidArgumentException(
                                                "\$row does not contain fields needed for comment $key and getComment(), but "
@@ -341,7 +354,9 @@ class CommentStore {
                                $cid = $row2->comment_id;
                                $text = $row2->comment_text;
                                $data = $row2->comment_data;
-                       } elseif ( $this->stage < MIGRATION_NEW && array_key_exists( "{$key}_old", $row ) ) {
+                       } elseif ( ( $this->stage & SCHEMA_COMPAT_READ_OLD ) &&
+                               array_key_exists( "{$key}_old", $row )
+                       ) {
                                $cid = null;
                                $text = $row["{$key}_old"];
                                $data = null;
@@ -474,7 +489,7 @@ class CommentStore {
                # Truncate comment in a Unicode-sensitive manner
                $comment->text = $this->lang->truncateForVisual( $comment->text, self::COMMENT_CHARACTER_LIMIT );
 
-               if ( $this->stage > MIGRATION_OLD && !$comment->id ) {
+               if ( ( $this->stage & SCHEMA_COMPAT_WRITE_NEW ) && !$comment->id ) {
                        $dbData = $comment->data;
                        if ( !$comment->message instanceof RawMessage ) {
                                if ( $dbData === null ) {
@@ -534,14 +549,14 @@ class CommentStore {
 
                $comment = $this->createComment( $dbw, $comment, $data );
 
-               if ( $this->stage <= MIGRATION_WRITE_BOTH ) {
+               if ( $this->stage & SCHEMA_COMPAT_WRITE_OLD ) {
                        $fields[$key] = $this->lang->truncateForDatabase( $comment->text, 255 );
                }
 
-               if ( $this->stage >= MIGRATION_WRITE_BOTH ) {
+               if ( $this->stage & SCHEMA_COMPAT_WRITE_NEW ) {
                        $tempTableStage = isset( $this->tempTables[$key] )
                                ? $this->tempTables[$key]['stage'] : MIGRATION_NEW;
-                       if ( $tempTableStage <= MIGRATION_WRITE_BOTH ) {
+                       if ( $tempTableStage & SCHEMA_COMPAT_WRITE_OLD ) {
                                $t = $this->tempTables[$key];
                                $func = __METHOD__;
                                $commentId = $comment->id;
@@ -556,7 +571,7 @@ class CommentStore {
                                        );
                                };
                        }
-                       if ( $tempTableStage >= MIGRATION_WRITE_BOTH ) {
+                       if ( $tempTableStage & SCHEMA_COMPAT_WRITE_NEW ) {
                                $fields["{$key}_id"] = $comment->id;
                        }
                }
@@ -594,7 +609,7 @@ class CommentStore {
 
                $tempTableStage = isset( $this->tempTables[$key] )
                        ? $this->tempTables[$key]['stage'] : MIGRATION_NEW;
-               if ( $tempTableStage < MIGRATION_WRITE_NEW ) {
+               if ( $tempTableStage & SCHEMA_COMPAT_WRITE_OLD ) {
                        throw new InvalidArgumentException( "Must use insertWithTempTable() for $key" );
                }
 
index fd1affc..31cb7ae 100644 (file)
@@ -7937,6 +7937,7 @@ $wgAllowSpecialInclusion = true;
 /**
  * Set this to an array of special page names to prevent
  * maintenance/updateSpecialPages.php from updating those pages.
+ * Mapping each special page name to an run mode like 'periodical' if a cronjob is set up.
  */
 $wgDisableQueryPageUpdate = false;
 
@@ -9094,6 +9095,16 @@ $wgSpecialSearchFormOptions = [];
  */
 $wgNativeImageLazyLoading = false;
 
+/**
+ * Option to whether serve the main page as the domain root
+ *
+ * @warning EXPERIMENTAL!
+ *
+ * @since 1.34
+ * @var bool
+ */
+$wgMainPageIsDomainRoot = false;
+
 /**
  * For really cool vim folding this needs to be at the end:
  * vim: foldmarker=@{,@} foldmethod=marker
index 78f6ca9..9337270 100644 (file)
@@ -86,14 +86,6 @@ class FauxRequest extends WebRequest {
                return (string)$this->getVal( $name, $default );
        }
 
-       /**
-        * @return array
-        * @suppress PhanParamSignatureMismatch
-        */
-       public function getValues() {
-               return $this->data;
-       }
-
        /**
         * @return array
         */
index c4b57af..ea2ce07 100644 (file)
@@ -704,7 +704,7 @@ class Html {
         * Return the HTML for a message box.
         * @since 1.31
         * @param string $html of contents of box
-        * @param string $className corresponding to box
+        * @param string|array $className corresponding to box
         * @param string $heading (optional)
         * @return string of HTML representing a box.
         */
@@ -718,32 +718,38 @@ class Html {
        /**
         * Return a warning box.
         * @since 1.31
+        * @since 1.34 $className optional parameter added
         * @param string $html of contents of box
+        * @param string $className (optional) corresponding to box
         * @return string of HTML representing a warning box.
         */
-       public static function warningBox( $html ) {
-               return self::messageBox( $html, 'warningbox' );
+       public static function warningBox( $html, $className = '' ) {
+               return self::messageBox( $html, [ 'warningbox', $className ] );
        }
 
        /**
         * Return an error box.
         * @since 1.31
+        * @since 1.34 $className optional parameter added
         * @param string $html of contents of error box
         * @param string $heading (optional)
+        * @param string $className (optional) corresponding to box
         * @return string of HTML representing an error box.
         */
-       public static function errorBox( $html, $heading = '' ) {
-               return self::messageBox( $html, 'errorbox', $heading );
+       public static function errorBox( $html, $heading = '', $className = '' ) {
+               return self::messageBox( $html, [ 'errorbox', $className ], $heading );
        }
 
        /**
         * Return a success box.
         * @since 1.31
+        * @since 1.34 $className optional parameter added
         * @param string $html of contents of box
+        * @param string $className (optional) corresponding to box
         * @return string of HTML representing a success box.
         */
-       public static function successBox( $html ) {
-               return self::messageBox( $html, 'successbox' );
+       public static function successBox( $html, $className = '' ) {
+               return self::messageBox( $html, [ 'successbox', $className ] );
        }
 
        /**
@@ -1005,7 +1011,7 @@ class Html {
        /**
         * Get HTML for an information message box with an icon.
         *
-        * @internal For use by the WebInstaller class.
+        * @internal For use by the WebInstaller class only.
         * @param string $rawHtml HTML
         * @param string $icon Path to icon file (used as 'src' attribute)
         * @param string $alt Alternate text for the icon
index f91477a..1a75714 100644 (file)
@@ -346,6 +346,10 @@ class MediaWiki {
                        return false;
                }
 
+               if ( $this->config->get( 'MainPageIsDomainRoot' ) && $request->getRequestURL() === '/' ) {
+                       return false;
+               }
+
                if ( $title->isSpecialPage() ) {
                        list( $name, $subpage ) = MediaWikiServices::getInstance()->getSpecialPageFactory()->
                                resolveAlias( $title->getDBkey() );
@@ -522,11 +526,15 @@ class MediaWiki {
                        try {
                                $this->main();
                        } catch ( ErrorPageError $e ) {
+                               $out = $this->context->getOutput();
+                               // TODO: Should ErrorPageError::report accept a OutputPage parameter?
+                               $e->report( ErrorPageError::STAGE_OUTPUT );
+
                                // T64091: while exceptions are convenient to bubble up GUI errors,
                                // they are not internal application faults. As with normal requests, this
                                // should commit, print the output, do deferred updates, jobs, and profiling.
                                $this->doPreOutputCommit();
-                               $e->report(); // display the GUI error
+                               $out->output(); // display the GUI error
                        }
                } catch ( Exception $e ) {
                        $context = $this->context;
index 6821d89..6dfcf3c 100644 (file)
@@ -276,8 +276,7 @@ class Router {
                $request->setPathParams( array_map( 'rawurldecode', $match['params'] ) );
                $spec = $match['userData'];
                $objectFactorySpec = array_intersect_key( $spec,
-                       // @todo ObjectFactory supports more keys than this.
-                       [ 'factory' => true, 'class' => true, 'args' => true ] );
+                       [ 'factory' => true, 'class' => true, 'args' => true, 'services' => true ] );
                /** @var $handler Handler (annotation for PHPStorm) */
                $handler = $this->objectFactory->createObject( $objectFactorySpec );
                $handler->init( $this, $request, $spec, $this->responseFactory );
index 3ecef76..c444cc4 100644 (file)
@@ -182,9 +182,9 @@ class RevisionStore
                        'Reading needs to be enabled for the old or the new schema.'
                );
                Assert::parameter(
-                       ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_BOTH ) !== 0,
+                       ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) !== 0,
                        '$mcrMigrationStage',
-                       'Writing needs to be enabled for the old or the new schema.'
+                       'Writing needs to be enabled for the new schema.'
                );
                Assert::parameter(
                        ( $mcrMigrationStage & SCHEMA_COMPAT_READ_OLD ) === 0
@@ -192,12 +192,6 @@ class RevisionStore
                        '$mcrMigrationStage',
                        'Cannot read the old schema when not also writing it.'
                );
-               Assert::parameter(
-                       ( $mcrMigrationStage & SCHEMA_COMPAT_READ_NEW ) === 0
-                       || ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) !== 0,
-                       '$mcrMigrationStage',
-                       'Cannot read the new schema when not also writing it.'
-               );
 
                $this->loadBalancer = $loadBalancer;
                $this->blobStore = $blobStore;
@@ -1907,7 +1901,11 @@ class RevisionStore
         *               'content'- whether the actual content of the slots should be
         *               preloaded.
         * @param int $queryFlags
-        * @param Title|null $title
+        * @param Title|null $title The title to which all the revision rows belong, if there
+        *        is such a title and the caller has it handy, so we don't have to look it up again.
+        *        If this parameter is given and any of the rows has a rev_page_id that is different
+        *        from $title->getArticleID(), an InvalidArgumentException is thrown.
+        *
         * @return StatusValue a status with a RevisionRecord[] of successfully fetched revisions
         *                                         and an array of errors for the revisions failed to fetch.
         */
@@ -1924,7 +1922,10 @@ class RevisionStore
                $titlesByPageId = [];
                foreach ( $rows as $row ) {
                        if ( isset( $rowsByRevId[$row->rev_id] ) ) {
-                               throw new InvalidArgumentException( "Duplicate rows in newRevisionsFromBatch {$row->rev_id}" );
+                               $result->warning(
+                                       'internalerror',
+                                       "Duplicate rows in newRevisionsFromBatch, rev_id {$row->rev_id}"
+                               );
                        }
                        if ( $title && $row->rev_page != $title->getArticleID() ) {
                                throw new InvalidArgumentException(
index ab51eab..83847d8 100644 (file)
@@ -207,10 +207,7 @@ return [
                );
                $class = MWLBFactory::getLBFactoryClass( $lbConf );
 
-               $instance = new $class( $lbConf );
-               MWLBFactory::setSchemaAliases( $instance, $mainConfig->get( 'DBtype' ) );
-
-               return $instance;
+               return new $class( $lbConf );
        },
 
        'EventRelayerGroup' => function ( MediaWikiServices $services ) : EventRelayerGroup {
index 9843e81..6c15a06 100644 (file)
@@ -2137,7 +2137,7 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return string String of the URL.
         */
        public function getLocalURL( $query = '', $query2 = false ) {
-               global $wgArticlePath, $wgScript, $wgServer, $wgRequest;
+               global $wgArticlePath, $wgScript, $wgServer, $wgRequest, $wgMainPageIsDomainRoot;
 
                $query = self::fixUrlQueryArgs( $query, $query2 );
 
@@ -2214,6 +2214,11 @@ class Title implements LinkTarget, IDBAccessObject {
                                $url = $wgServer . $url;
                        }
                }
+
+               if ( $wgMainPageIsDomainRoot && $this->isMainPage() && $query === '' ) {
+                       return '/';
+               }
+
                // Avoid PHP 7.1 warning from passing $this by reference
                $titleRef = $this;
                Hooks::run( 'GetLocalURL', [ &$titleRef, &$url, $query ] );
index 7bbce97..d2c957d 100644 (file)
@@ -1154,8 +1154,7 @@ class ApiMain extends ApiBase {
                }
 
                if ( $this->getParameter( 'curtimestamp' ) ) {
-                       $result->addValue( null, 'curtimestamp', wfTimestamp( TS_ISO_8601, time() ),
-                               ApiResult::NO_SIZE_CHECK );
+                       $result->addValue( null, 'curtimestamp', wfTimestamp( TS_ISO_8601 ), ApiResult::NO_SIZE_CHECK );
                }
 
                if ( $this->getParameter( 'responselanginfo' ) ) {
index b8672ee..022fd9b 100644 (file)
@@ -286,6 +286,8 @@ class ApiQueryBacklinksprop extends ApiQueryGeneratorBase {
                $res = $this->select( __METHOD__ );
 
                if ( is_null( $resultPageSet ) ) {
+                       $this->executeGenderCacheFromResultWrapper( $res, __METHOD__ );
+
                        $count = 0;
                        foreach ( $res as $row ) {
                                if ( ++$count > $params['limit'] ) {
index 8d9cb48..059c438 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
 use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\IResultWrapper;
 
@@ -569,6 +570,42 @@ abstract class ApiQueryBase extends ApiBase {
                );
        }
 
+       /**
+        * Preprocess the result set to fill the GenderCache with the necessary information
+        * before using self::addTitleInfo
+        *
+        * @param IResultWrapper $res Result set to work on.
+        *  The result set must have _namespace and _title fields with the provided field prefix
+        * @param string $fname The caller function name, always use __METHOD__
+        * @param string $fieldPrefix Prefix for fields to check gender for
+        */
+       protected function executeGenderCacheFromResultWrapper(
+               IResultWrapper $res, $fname = __METHOD__, $fieldPrefix = 'page'
+       ) {
+               if ( !$res->numRows() ) {
+                       return;
+               }
+
+               $services = MediaWikiServices::getInstance();
+               $nsInfo = $services->getNamespaceInfo();
+               $namespaceField = $fieldPrefix . '_namespace';
+               $titleField = $fieldPrefix . '_title';
+
+               $usernames = [];
+               foreach ( $res as $row ) {
+                       if ( $nsInfo->hasGenderDistinction( $row->$namespaceField ) ) {
+                               $usernames[] = $row->$titleField;
+                       }
+               }
+
+               if ( $usernames === [] ) {
+                       return;
+               }
+
+               $genderCache = $services->getGenderCache();
+               $genderCache->doQuery( $usernames, $fname );
+       }
+
        /** @} */
 
        /************************************************************************//**
index acdb05c..eb588c2 100644 (file)
        "apihelp-edit-param-summary": "編集の要約。$1section=new で $1sectiontitle が設定されていない場合は節名としても利用されます。",
        "apihelp-edit-param-tags": "この版に適用する変更タグ。",
        "apihelp-edit-param-minor": "この編集に細部の変更の印を付ける",
-       "apihelp-edit-param-notminor": "細部の編集ではない。",
+       "apihelp-edit-param-notminor": "利用者設定で「{{int:tog-minordefault}}」を指定してあっても、細部の編集とマークしないでください。",
        "apihelp-edit-param-bot": "この編集をボットの編集としてマークする。",
        "apihelp-edit-param-basetimestamp": "編集前の版のタイムスタンプ。編集競合を検出するために使用されます。\n[[Special:ApiHelp/query+revisions|action=query&prop=revisions&rvprop=timestamp]] で取得できます。",
        "apihelp-edit-param-starttimestamp": "編集作業を開始したときのタイムスタンプ。編集競合を検出するために使用されます。適切な値は <var>[[Special:ApiHelp/main|curtimestamp]]</var> を使用して編集作業を開始するとき (たとえば、編集するページの本文を読み込んだとき) に取得できます。",
        "apihelp-options-example-change": "<kbd>skin</kbd> および <kbd>hideminor</kbd> の個人設定を変更する。",
        "apihelp-options-example-complex": "すべての個人設定を初期化し、<kbd>skin</kbd> および <kbd> nickname </kbd> を設定する。",
        "apihelp-paraminfo-summary": "API モジュールに関する情報を取得します。",
-       "apihelp-paraminfo-param-modules": "モジュールの名前のリスト (<var>action</var> および <var>format</var> パラメーターの値, または <kbd>main</kbd>). <kbd>+</kbd> を使用して下位モジュールを指定できます。",
+       "apihelp-paraminfo-param-modules": "モジュール名のリスト (<var>action</var> および <var>format</var> パラメーターまたは <kbd>main</kbd>の値)。特定の下位モジュールの指定は<kbd>+</kbd> 、全下位モジュールの指定は<kbd>+*</kbd>を使い、あるいは<kbd>+**</kbd>ですべての下位モジュールを再帰的に指定します。",
        "apihelp-paraminfo-param-helpformat": "ヘルプ文字列の形式。",
        "apihelp-paraminfo-param-querymodules": "クエリモジュール名のリスト (<var>prop</var>, <var>meta</var> or <var>list</var> パラメータの値)。<kbd>$1querymodules=foo</kbd> の代わりに <kbd>$1modules=query+foo</kbd> を使用してください。",
        "apihelp-paraminfo-example-1": "<kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>, and <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> に関する情報を表示する。",
        "apihelp-parse-paramvalue-prop-revid": "構文解析されたページの版IDを追加します。",
        "apihelp-parse-paramvalue-prop-displaytitle": "構文解析されたウィキテキストのタイトルを追加します。",
        "apihelp-parse-paramvalue-prop-headitems": "ページの <code>&lt;head&gt;</code> の中に入れてアイテムを提供します。",
-       "apihelp-parse-paramvalue-prop-headhtml": "ページの解析された <code>&lt;head&gt;</code> を与える。",
+       "apihelp-parse-paramvalue-prop-headhtml": "ページの<code>&lt;html&gt;</code>と<code>&lt;head&gt;</code>の要素を開いて<code>&lt;body&gt;</code>を開示し、Doctype を解析する。",
        "apihelp-parse-paramvalue-prop-jsconfigvars": "ページに固有のJavaScriptの設定変数を提供します。適用するには、<code>mw.config.set()</code>を使用します。",
        "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "JSON文字列としてページに固有のJavaScriptの設定変数を提供します。",
        "apihelp-parse-paramvalue-prop-indicators": "ページ上で使用されるページのステータスインジケータのHTMLを提供します。",
index 1803009..63b320e 100644 (file)
@@ -23,7 +23,6 @@
 
 use MediaWiki\Config\ServiceOptions;
 use MediaWiki\Logger\LoggerFactory;
-use Wikimedia\Rdbms\LBFactory;
 use Wikimedia\Rdbms\DatabaseDomain;
 
 /**
@@ -359,35 +358,6 @@ abstract class MWLBFactory {
                return $class;
        }
 
-       /**
-        * @param LBFactory $lbFactory
-        * @param string $dbType 'mysql', 'sqlite', etc.
-        * @internal For use with service wiring
-        */
-       public static function setSchemaAliases( LBFactory $lbFactory, $dbType ) {
-               if ( $dbType === 'mysql' ) {
-                       /**
-                        * When SQLite indexes were introduced in r45764, it was noted that
-                        * SQLite requires index names to be unique within the whole database,
-                        * not just within a schema. As discussed in CR r45819, to avoid the
-                        * need for a schema change on existing installations, the indexes
-                        * were implicitly mapped from the new names to the old names.
-                        *
-                        * This mapping can be removed if DB patches are introduced to alter
-                        * the relevant tables in existing installations. Note that because
-                        * this index mapping applies to table creation, even new installations
-                        * of MySQL have the old names (except for installations created during
-                        * a period where this mapping was inappropriately removed, see
-                        * T154872).
-                        */
-                       $lbFactory->setIndexAliases( [
-                               'ar_usertext_timestamp' => 'usertext_timestamp',
-                               'un_user_id' => 'user_id',
-                               'un_user_ip' => 'user_ip',
-                       ] );
-               }
-       }
-
        /**
         * Log a database deprecation warning
         * @param string $msg Deprecation message
index 5fcf0e6..2448421 100644 (file)
@@ -26,9 +26,9 @@
  */
 class BadRequestError extends ErrorPageError {
 
-       public function report() {
+       public function report( $action = self::SEND_OUTPUT ) {
                global $wgOut;
                $wgOut->setStatusCode( 400 );
-               parent::report();
+               parent::report( $action );
        }
 }
index 4b18126..64216a4 100644 (file)
@@ -25,6 +25,8 @@
  * @ingroup Exception
  */
 class ErrorPageError extends MWException implements ILocalizedException {
+       const SEND_OUTPUT = 0;
+       const STAGE_OUTPUT = 1;
        public $title, $msg, $params;
 
        /**
@@ -60,13 +62,19 @@ class ErrorPageError extends MWException implements ILocalizedException {
                return wfMessage( $this->msg, $this->params );
        }
 
-       public function report() {
+       public function report( $action = self::SEND_OUTPUT ) {
                if ( self::isCommandLine() || defined( 'MW_API' ) ) {
                        parent::report();
                } else {
                        global $wgOut;
                        $wgOut->showErrorPage( $this->title, $this->msg, $this->params );
-                       $wgOut->output();
+                       // Allow skipping of the final output step, so that web-based page views
+                       // from MediaWiki.php, can inspect the staged OutputPage state, and perform
+                       // graceful shutdown via doPreOutputCommit first, just like for regular
+                       // output when there isn't an error page.
+                       if ( $action === self::SEND_OUTPUT ) {
+                               $wgOut->output();
+                       }
                }
        }
 }
index 87a3dc2..9fa1c7c 100644 (file)
@@ -67,10 +67,12 @@ class PermissionsError extends ErrorPageError {
                parent::__construct( 'permissionserrors', Message::newFromSpecifier( $errors[0] ) );
        }
 
-       public function report() {
+       public function report( $action = self::SEND_OUTPUT ) {
                global $wgOut;
 
                $wgOut->showPermissionsErrorPage( $this->errors, $this->permission );
-               $wgOut->output();
+               if ( $action === self::SEND_OUTPUT ) {
+                       $wgOut->output();
+               }
        }
 }
index bec0d90..cdeb402 100644 (file)
@@ -32,9 +32,9 @@ class ThrottledError extends ErrorPageError {
                );
        }
 
-       public function report() {
+       public function report( $action = ErrorPageError::SEND_OUTPUT ) {
                global $wgOut;
                $wgOut->setStatusCode( 429 );
-               parent::report();
+               parent::report( $action );
        }
 }
index 7a99765..ff992b0 100644 (file)
@@ -75,14 +75,15 @@ class UserNotLoggedIn extends ErrorPageError {
         * Redirect to Special:Userlogin if the specified message is compatible. Otherwise,
         * show an error page as usual.
         */
-       public function report() {
+       public function report( $action = self::SEND_OUTPUT ) {
                // If an unsupported message is used, don't try redirecting to Special:Userlogin,
                // since the message may not be compatible.
                if ( !in_array( $this->msg, LoginHelper::getValidErrorMessages() ) ) {
-                       parent::report();
+                       parent::report( $action );
+                       return;
                }
 
-               // Message is valid. Redirec to Special:Userlogin
+               // Message is valid. Redirect to Special:Userlogin
 
                $context = RequestContext::getMain();
 
@@ -98,6 +99,8 @@ class UserNotLoggedIn extends ErrorPageError {
                        'warning' => $this->msg,
                ] ) );
 
-               $output->output();
+               if ( $action === self::SEND_OUTPUT ) {
+                       $output->output();
+               }
        }
 }
index 9e04d09..f369f00 100644 (file)
@@ -123,8 +123,28 @@ class FileBackendGroup {
                        }
                        $class = $config['class'];
 
-                       // @FIXME: ideally this would default to the DB domain (which includes the schema)
-                       $config['domainId'] = $config['domainId'] ?? ( $config['wikiId'] ?? wfWikiID() );
+                       if ( isset( $config['domainId'] ) ) {
+                               $domainId = $config['domainId'];
+                       } elseif ( isset( $config['wikiId'] ) ) {
+                               $domainId = $config['wikiId']; // b/c
+                       } else {
+                               // Only use the raw database/prefix for backwards compatibility
+                               $ld = WikiMap::getCurrentWikiDbDomain();
+                               $domainId = strlen( $ld->getTablePrefix() )
+                                       ? "{$ld->getDatabase()}-{$ld->getTablePrefix()}"
+                                       : $ld->getDatabase();
+                               // If the local wiki ID and local domain ID do not match, probably due to a
+                               // non-default schema, issue a warning. A non-default schema indicates that
+                               // it might be used to disambiguate different wikis.
+                               $wikiId = WikiMap::getWikiIdFromDbDomain( $ld );
+                               if ( $ld->getSchema() !== null && $domainId !== $wikiId ) {
+                                       wfWarn(
+                                               "\$wgFileBackend entry '$name' should have 'domainId' set.\n" .
+                                               "Legacy default 'domainId' is '$domainId' but wiki ID is '$wikiId'."
+                                       );
+                               }
+                       }
+                       $config['domainId'] = $domainId;
                        $config['readOnly'] = $config['readOnly'] ?? $readOnlyReason;
 
                        unset( $config['class'] ); // backend won't need this
index ff8f056..f095066 100644 (file)
@@ -838,7 +838,11 @@ class FileRepo {
        /**
         * Store a file to a given destination.
         *
-        * @param string $srcPath Source file system path, storage path, or virtual URL
+        * Using FSFile/TempFSFile can improve performance via caching.
+        * Using TempFSFile can further improve performance by signalling that it is safe
+        * to touch the source file or write extended attribute metadata to it directly.
+        *
+        * @param string|FSFile $srcPath Source file system path, storage path, or virtual URL
         * @param string $dstZone Destination zone
         * @param string $dstRel Destination relative path
         * @param int $flags Bitwise combination of the following flags:
@@ -862,6 +866,8 @@ class FileRepo {
        /**
         * Store a batch of files
         *
+        * @see FileRepo::store()
+        *
         * @param array $triplets (src, dest zone, dest rel) triplets as per store()
         * @param int $flags Bitwise combination of the following flags:
         *   self::OVERWRITE         Overwrite an existing destination file instead of failing
@@ -884,11 +890,18 @@ class FileRepo {
                $operations = [];
                // Validate each triplet and get the store operation...
                foreach ( $triplets as $triplet ) {
-                       list( $srcPath, $dstZone, $dstRel ) = $triplet;
+                       list( $src, $dstZone, $dstRel ) = $triplet;
+                       $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src;
                        wfDebug( __METHOD__
                                . "( \$src='$srcPath', \$dstZone='$dstZone', \$dstRel='$dstRel' )\n"
                        );
-
+                       // Resolve source path
+                       if ( $src instanceof FSFile ) {
+                               $op = 'store';
+                       } else {
+                               $src = $this->resolveToStoragePathIfVirtual( $src );
+                               $op = FileBackend::isStoragePath( $src ) ? 'copy' : 'store';
+                       }
                        // Resolve destination path
                        $root = $this->getZonePath( $dstZone );
                        if ( !$root ) {
@@ -904,13 +917,10 @@ class FileRepo {
                                return $this->newFatal( 'directorycreateerror', $dstDir );
                        }
 
-                       // Resolve source to a storage path if virtual
-                       $srcPath = $this->resolveToStoragePathIfVirtual( $srcPath );
-
                        // Copy the source file to the destination
                        $operations[] = [
-                               'op' => FileBackend::isStoragePath( $srcPath ) ? 'copy' : 'store',
-                               'src' => $srcPath, // storage path (copy) or local file path (store)
+                               'op' => $op,
+                               'src' => $src, // storage path (copy) or local file path (store)
                                'dst' => $dstPath,
                                'overwrite' => ( $flags & self::OVERWRITE ) ? true : false,
                                'overwriteSame' => ( $flags & self::OVERWRITE_SAME ) ? true : false,
@@ -970,6 +980,10 @@ class FileRepo {
         * This function can be used to write to otherwise read-only foreign repos.
         * This is intended for copying generated thumbnails into the repo.
         *
+        * Using FSFile/TempFSFile can improve performance via caching.
+        * Using TempFSFile can further improve performance by signalling that it is safe
+        * to touch the source file or write extended attribute metadata to it directly.
+        *
         * @param string|FSFile $src Source file system path, storage path, or virtual URL
         * @param string $dst Virtual URL or storage path
         * @param array|string|null $options An array consisting of a key named headers
@@ -981,39 +995,14 @@ class FileRepo {
                return $this->quickImportBatch( [ [ $src, $dst, $options ] ] );
        }
 
-       /**
-        * Purge a file from the repo. This does no locking nor journaling.
-        * This function can be used to write to otherwise read-only foreign repos.
-        * This is intended for purging thumbnails.
-        *
-        * @param string $path Virtual URL or storage path
-        * @return Status
-        */
-       final public function quickPurge( $path ) {
-               return $this->quickPurgeBatch( [ $path ] );
-       }
-
-       /**
-        * Deletes a directory if empty.
-        * This function can be used to write to otherwise read-only foreign repos.
-        *
-        * @param string $dir Virtual URL (or storage path) of directory to clean
-        * @return Status
-        */
-       public function quickCleanDir( $dir ) {
-               $status = $this->newGood();
-               $status->merge( $this->backend->clean(
-                       [ 'dir' => $this->resolveToStoragePathIfVirtual( $dir ) ] ) );
-
-               return $status;
-       }
-
        /**
         * Import a batch of files from the local file system into the repo.
         * This does no locking nor journaling and overrides existing files.
         * This function can be used to write to otherwise read-only foreign repos.
         * This is intended for copying generated thumbnails into the repo.
         *
+        * @see FileRepo::quickImport()
+        *
         * All path parameters may be a file system path, storage path, or virtual URL.
         * When "headers" are given they are used as HTTP headers if supported.
         *
@@ -1046,7 +1035,7 @@ class FileRepo {
 
                        $operations[] = [
                                'op' => $op,
-                               'src' => $src,
+                               'src' => $src, // storage path (copy) or local path/FSFile (store)
                                'dst' => $dst,
                                'headers' => $headers
                        ];
@@ -1057,6 +1046,33 @@ class FileRepo {
                return $status;
        }
 
+       /**
+        * Purge a file from the repo. This does no locking nor journaling.
+        * This function can be used to write to otherwise read-only foreign repos.
+        * This is intended for purging thumbnails.
+        *
+        * @param string $path Virtual URL or storage path
+        * @return Status
+        */
+       final public function quickPurge( $path ) {
+               return $this->quickPurgeBatch( [ $path ] );
+       }
+
+       /**
+        * Deletes a directory if empty.
+        * This function can be used to write to otherwise read-only foreign repos.
+        *
+        * @param string $dir Virtual URL (or storage path) of directory to clean
+        * @return Status
+        */
+       public function quickCleanDir( $dir ) {
+               $status = $this->newGood();
+               $status->merge( $this->backend->clean(
+                       [ 'dir' => $this->resolveToStoragePathIfVirtual( $dir ) ] ) );
+
+               return $status;
+       }
+
        /**
         * Purge a batch of files from the repo.
         * This function can be used to write to otherwise read-only foreign repos.
@@ -1169,6 +1185,10 @@ class FileRepo {
         * Returns a Status object. On success, the value contains "new" or
         * "archived", to indicate whether the file was new with that name.
         *
+        * Using FSFile/TempFSFile can improve performance via caching.
+        * Using TempFSFile can further improve performance by signalling that it is safe
+        * to touch the source file or write extended attribute metadata to it directly.
+        *
         * Options to $options include:
         *   - headers : name/value map of HTTP headers to use in response to GET/HEAD requests
         *
@@ -1199,6 +1219,8 @@ class FileRepo {
        /**
         * Publish a batch of files
         *
+        * @see FileRepo::publish()
+        *
         * @param array $ntuples (source, dest, archive) triplets or
         *   (source, dest, archive, options) 4-tuples as per publish().
         * @param int $flags Bitfield, may be FileRepo::DELETE_SOURCE to indicate
@@ -1277,7 +1299,7 @@ class FileRepo {
                        } else {
                                $operations[] = [
                                        'op' => 'store',
-                                       'src' => $src, // FSFile (preferred) or local file path
+                                       'src' => $src, // storage path (copy) or local path/FSFile (store)
                                        'dst' => $dstPath,
                                        'overwrite' => true, // replace current
                                        'headers' => $headers
index 7cfc8c2..b76f3da 100644 (file)
@@ -29,9 +29,9 @@ class LocalFileLockError extends ErrorPageError {
                );
        }
 
-       public function report() {
+       public function report( $action = self::SEND_OUTPUT ) {
                global $wgOut;
                $wgOut->setStatusCode( 429 );
-               parent::report();
+               parent::report( $action );
        }
 }
diff --git a/includes/htmlform/CollapsibleFieldsetLayout.php b/includes/htmlform/CollapsibleFieldsetLayout.php
new file mode 100644 (file)
index 0000000..804f1b9
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+class CollapsibleFieldsetLayout extends OOUI\FieldsetLayout {
+       public function __construct( array $config = [] ) {
+               parent::__construct( $config );
+
+               $this->addClasses( [ 'mw-collapsible' ] );
+               if ( isset( $config[ 'collapsed' ] ) && $config[ 'collapsed' ] ) {
+                       $this->addClasses( [ 'mw-collapsed' ] );
+               }
+               $this->header->addClasses( [ 'mw-collapsible-toggle' ] );
+               $this->group->addClasses( [ 'mw-collapsible-content' ] );
+
+               $this->header->appendContent(
+                       new OOUI\IconWidget( [
+                               'icon' => 'expand',
+                               'label' => wfMessage( 'collapsible-expand' )->text(),
+                       ] ),
+                       new OOUI\IconWidget( [
+                               'icon' => 'collapse',
+                               'label' => wfMessage( 'collapsible-collapse' )->text(),
+                       ] )
+               );
+
+               $this->header->setAttributes( [
+                       'role' => 'button',
+               ] );
+       }
+}
index baafa5e..94ba75e 100644 (file)
@@ -281,17 +281,10 @@ class OOUIHTMLForm extends HTMLForm {
 
        public function wrapForm( $html ) {
                if ( is_string( $this->mWrapperLegend ) ) {
-                       $classes = $this->mCollapsible ? [ 'mw-collapsible' ] : [];
-                       if ( $this->mCollapsed ) {
-                               $classes[] = 'mw-collapsed';
-                       }
-                       $content = new OOUI\FieldsetLayout( [
+                       $phpClass = $this->mCollapsible ? CollapsibleFieldsetLayout::class : OOUI\FieldsetLayout::class;
+                       $content = new $phpClass( [
                                'label' => $this->mWrapperLegend,
-                               'classes' => $classes,
-                               'group' => new OOUI\StackLayout( [
-                                       'expanded' => false,
-                                       'classes' => [ 'mw-collapsible-content' ],
-                               ] ),
+                               'collapsed' => $this->mCollapsed,
                                'items' => [
                                        new OOUI\Widget( [
                                                'content' => new OOUI\HtmlSnippet( $html )
index 510d26d..84e7b73 100644 (file)
@@ -37,8 +37,8 @@ class HttpRequestFactory {
         * Generate a new MWHttpRequest object
         * @param string $url Url to use
         * @param array $options Possible keys for the array:
-        *    - timeout             Timeout length in seconds
-        *    - connectTimeout      Timeout for connection, in seconds (curl only)
+        *    - timeout             Timeout length in seconds or 'default'
+        *    - connectTimeout      Timeout for connection, in seconds (curl only) or 'default'
         *    - postData            An array of key-value pairs or a url-encoded form data
         *    - proxy               The proxy to use.
         *                          Otherwise it will use $wgHTTPProxy (if set)
@@ -59,13 +59,12 @@ class HttpRequestFactory {
         *    - originalRequest     Information about the original request (as a WebRequest object or
         *                          an associative array with 'ip' and 'userAgent').
         * @codingStandardsIgnoreStart
-        * @phan-param array{timeout?:int,connectTimeout?:int,postData?:array,proxy?:string,noProxy?:bool,sslVerifyHost?:bool,sslVerifyCert?:bool,caInfo?:string,maxRedirects?:int,followRedirects?:bool,userAgent?:string,method?:string,logger?:\Psr\Log\LoggerInterface,username?:string,password?:string,originalRequest?:WebRequest|array{ip:string,userAgent:string}} $options
+        * @phan-param array{timeout?:int|string,connectTimeout?:int|string,postData?:array,proxy?:string,noProxy?:bool,sslVerifyHost?:bool,sslVerifyCert?:bool,caInfo?:string,maxRedirects?:int,followRedirects?:bool,userAgent?:string,method?:string,logger?:\Psr\Log\LoggerInterface,username?:string,password?:string,originalRequest?:\WebRequest|array{ip:string,userAgent:string}} $options
         * @codingStandardsIgnoreEnd
         * @param string $caller The method making this request, for profiling
         * @throws RuntimeException
         * @return MWHttpRequest
         * @see MWHttpRequest::__construct
-        * @suppress PhanUndeclaredTypeParameter
         */
        public function create( $url, array $options = [], $caller = __METHOD__ ) {
                if ( !Http::$httpEngine ) {
index d1c14ae..0f3096e 100644 (file)
@@ -89,7 +89,7 @@ abstract class MWHttpRequest implements LoggerAwareInterface {
         * @param string $url Url to use. If protocol-relative, will be expanded to an http:// URL
         * @param array $options (optional) extra params to pass (see HttpRequestFactory::create())
         * @codingStandardsIgnoreStart
-        * @phan-param array{timeout?:int,connectTimeout?:int,postData?:array,proxy?:string,noProxy?:bool,sslVerifyHost?:bool,sslVerifyCert?:bool,caInfo?:string,maxRedirects?:int,followRedirects?:bool,userAgent?:string,logger?:LoggerInterface,username?:string,password?:string,originalRequest?:WebRequest|array{ip:string,userAgent:string},method?:string} $options
+        * @phan-param array{timeout?:int|string,connectTimeout?:int|string,postData?:array,proxy?:string,noProxy?:bool,sslVerifyHost?:bool,sslVerifyCert?:bool,caInfo?:string,maxRedirects?:int,followRedirects?:bool,userAgent?:string,logger?:LoggerInterface,username?:string,password?:string,originalRequest?:WebRequest|array{ip:string,userAgent:string},method?:string} $options
         * @codingStandardsIgnoreEnd
         * @param string $caller The method making this request, for profiling
         * @param Profiler|null $profiler An instance of the profiler for profiling, or null
index ac8c9e6..ce7e29d 100644 (file)
@@ -690,7 +690,7 @@ abstract class DatabaseInstaller {
                        $this->getPasswordBox( 'wgDBpassword', 'config-db-password' ) .
                        $this->parent->getHelpBox( 'config-db-web-help' );
                if ( $noCreateMsg ) {
-                       $s .= $this->parent->getWarningBox( wfMessage( $noCreateMsg )->plain() );
+                       $s .= Html::warningBox( wfMessage( $noCreateMsg )->plain(), 'config-warning-box' );
                } else {
                        $s .= $this->getCheckBox( '_CreateDBAccount', 'config-db-web-create' );
                }
index ea88411..a249ada 100644 (file)
@@ -327,6 +327,20 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'renameIndex', 'user_properties', 'user_properties_user_property', 'PRIMARY', false,
                                'patch-user_properties-fix-pk.sql' ],
                        [ 'addTable', 'comment', 'patch-comment-table.sql' ],
+                       [ 'addTable', 'revision_comment_temp', 'patch-revision_comment_temp-table.sql' ],
+                       // image_comment_temp is no longer needed when upgrading to MW 1.31 or newer,
+                       // as it is dropped later in the update process as part of 'migrateImageCommentTemp'.
+                       // File kept on disk and the updater entry here for historical purposes.
+                       // [ 'addTable', 'image_comment_temp', 'patch-image_comment_temp-table.sql' ],
+                       [ 'addField', 'archive', 'ar_comment_id', 'patch-archive-ar_comment_id.sql' ],
+                       [ 'addField', 'filearchive', 'fa_description_id', 'patch-filearchive-fa_description_id.sql' ],
+                       [ 'modifyField', 'image', 'img_description', 'patch-image-img_description-default.sql' ],
+                       [ 'addField', 'ipblocks', 'ipb_reason_id', 'patch-ipblocks-ipb_reason_id.sql' ],
+                       [ 'addField', 'logging', 'log_comment_id', 'patch-logging-log_comment_id.sql' ],
+                       [ 'addField', 'oldimage', 'oi_description_id', 'patch-oldimage-oi_description_id.sql' ],
+                       [ 'addField', 'protected_titles', 'pt_reason_id', 'patch-protected_titles-pt_reason_id.sql' ],
+                       [ 'addField', 'recentchanges', 'rc_comment_id', 'patch-recentchanges-rc_comment_id.sql' ],
+                       [ 'modifyField', 'revision', 'rev_comment', 'patch-revision-rev_comment-default.sql' ],
 
                        // This field was added in 1.31, but is put here so it can be used by 'migrateComments'
                        [ 'addField', 'image', 'img_description_id', 'patch-image-img_description_id.sql' ],
@@ -386,7 +400,11 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'modifyTable', 'job', 'patch-job-params-mediumblob.sql' ],
 
                        // 1.34
+                       [ 'dropIndex', 'archive', 'ar_usertext_timestamp',
+                               'patch-drop-archive-ar_usertext_timestamp.sql' ],
+                       [ 'dropIndex', 'archive', 'usertext_timestamp', 'patch-drop-archive-usertext_timestamp.sql' ],
                        [ 'dropField', 'logging', 'log_user', 'patch-drop-user-fields.sql' ],
+                       [ 'addIndex', 'user_newtalk', 'un_user_ip', 'patch-rename-mysql-user_newtalk-indexes.sql' ],
                ];
        }
 
index b2c7d66..d7b1457 100644 (file)
@@ -483,6 +483,11 @@ class PostgresUpdater extends DatabaseUpdater {
                        [ 'changeNullableField', 'protected_titles', 'pt_reason', 'NOT NULL', true ],
                        [ 'addPgField', 'protected_titles', 'pt_reason_id', 'INTEGER NOT NULL DEFAULT 0' ],
                        [ 'addTable', 'comment', 'patch-comment-table.sql' ],
+                       [ 'addTable', 'revision_comment_temp', 'patch-revision_comment_temp-table.sql' ],
+                       // image_comment_temp is no longer needed when upgrading to MW 1.31 or newer,
+                       // as it is dropped later in the update process as part of 'migrateImageCommentTemp'.
+                       // File kept on disk and the updater entry here for historical purposes.
+                       // [ 'addTable', 'image_comment_temp', 'patch-image_comment_temp-table.sql' ],
 
                        // This field was added in 1.31, but is put here so it can be used by 'migrateComments'
                        [ 'addPgField', 'image', 'img_description_id', 'INTEGER NOT NULL DEFAULT 0' ],
index 7c3878c..15b3a5a 100644 (file)
@@ -186,6 +186,19 @@ class SqliteUpdater extends DatabaseUpdater {
                        [ 'renameIndex', 'user_properties', 'user_properties_user_property', 'PRIMARY', false,
                                'patch-user_properties-fix-pk.sql' ],
                        [ 'addTable', 'comment', 'patch-comment-table.sql' ],
+                       [ 'addTable', 'revision_comment_temp', 'patch-revision_comment_temp-table.sql' ],
+                       // image_comment_temp is no longer needed when upgrading to MW 1.31 or newer,
+                       // as it is dropped later in the update process as part of 'migrateImageCommentTemp'.
+                       // File kept on disk and the updater entry here for historical purposes.
+                       // [ 'addTable', 'image_comment_temp', 'patch-image_comment_temp-table.sql' ],
+                       [ 'addField', 'archive', 'ar_comment_id', 'patch-archive-ar_comment_id.sql' ],
+                       [ 'modifyField', 'image', 'img_description', 'patch-image-img_description-default.sql' ],
+                       [ 'addField', 'ipblocks', 'ipb_reason_id', 'patch-ipblocks-ipb_reason_id.sql' ],
+                       [ 'addField', 'logging', 'log_comment_id', 'patch-logging-log_comment_id.sql' ],
+                       [ 'addField', 'oldimage', 'oi_description_id', 'patch-oldimage-oi_description_id.sql' ],
+                       [ 'addField', 'protected_titles', 'pt_reason_id', 'patch-protected_titles-pt_reason_id.sql' ],
+                       [ 'addField', 'recentchanges', 'rc_comment_id', 'patch-recentchanges-rc_comment_id.sql' ],
+                       [ 'modifyField', 'revision', 'rev_comment', 'patch-revision-rev_comment-default.sql' ],
 
                        // This field was added in 1.31, but is put here so it can be used by 'migrateComments'
                        [ 'addField', 'image', 'img_description_id', 'patch-image-img_description_id.sql' ],
index b6e90a9..d9cd6de 100644 (file)
@@ -390,7 +390,8 @@ class WebInstaller extends Installer {
                        );
                }
                $text = $msg->useDatabase( false )->plain();
-               $this->output->addHTML( $this->getErrorBox( $text ) );
+               $box = Html::errorBox( $text, '', 'config-error-box' );
+               $this->output->addHTML( $box );
        }
 
        /**
@@ -637,34 +638,40 @@ class WebInstaller extends Installer {
        /**
         * Get HTML for an error box with an icon.
         *
+        * @deprecated since 1.34 Use Html::errorBox() instead.
         * @param string $text Wikitext, get this with wfMessage()->plain()
         *
         * @return string
         */
        public function getErrorBox( $text ) {
+               wfDeprecated( __METHOD__, '1.34' );
                return $this->getInfoBox( $text, 'critical-32.png', 'config-error-box' );
        }
 
        /**
         * Get HTML for a warning box with an icon.
         *
+        * @deprecated since 1.34 Use Html::warningBox() instead.
         * @param string $text Wikitext, get this with wfMessage()->plain()
         *
         * @return string
         */
        public function getWarningBox( $text ) {
+               wfDeprecated( __METHOD__, '1.34' );
                return $this->getInfoBox( $text, 'warning-32.png', 'config-warning-box' );
        }
 
        /**
         * Get HTML for an information message box with an icon.
         *
+        * @deprecated since 1.34.
         * @param string|HtmlArmor $text Wikitext to be parsed (from Message::plain) or raw HTML.
         * @param string|bool $icon Icon name, file in mw-config/images. Default: false
         * @param string|bool $class Additional class name to add to the wrapper div. Default: false.
         * @return string HTML
         */
        public function getInfoBox( $text, $icon = false, $class = false ) {
+               wfDeprecated( __METHOD__, '1.34' );
                $html = ( $text instanceof HtmlArmor ) ?
                        HtmlArmor::getHtml( $text ) :
                        $this->parse( $text, true );
@@ -1046,9 +1053,9 @@ class WebInstaller extends Installer {
                        $text = $status->getWikiText();
 
                        if ( $status->isOK() ) {
-                               $box = $this->getWarningBox( $text );
+                               $box = Html::warningBox( $text, 'config-warning-box' );
                        } else {
-                               $box = $this->getErrorBox( $text );
+                               $box = Html::errorBox( $text, '', 'config-error-box' );
                        }
 
                        $this->output->addHTML( $box );
index 7bec49a..3521fa1 100644 (file)
@@ -137,7 +137,7 @@ class WebInstallerOptions extends WebInstallerPage {
                        }
                } else {
                        $skinHtml .=
-                               $this->parent->getWarningBox( wfMessage( 'config-skins-missing' )->plain() ) .
+                               Html::warningBox( wfMessage( 'config-skins-missing' )->plain(), 'config-warning-box' ) .
                                Html::hidden( 'config_wgDefaultSkin', $chosenSkinName );
                }
 
index be55c32..07e2e75 100644 (file)
@@ -36,7 +36,7 @@ class WebInstallerRestart extends WebInstallerPage {
                }
 
                $this->startForm();
-               $s = $this->parent->getWarningBox( wfMessage( 'config-help-restart' )->plain() );
+               $s = Html::warningBox( wfMessage( 'config-help-restart' )->plain(), '', 'config-warning-box' );
                $this->addHTML( $s );
                $this->endForm( 'restart' );
 
index 4039391..866c3f5 100644 (file)
@@ -58,7 +58,7 @@
        "config-restart": "Ja, erneut starten",
        "config-welcome": "=== Prüfung der Installationsumgebung ===\nDie Basisprüfungen werden jetzt durchgeführt, um festzustellen, ob die Installationsumgebung für MediaWiki geeignet ist.\nNotiere diese Informationen und gib sie an, sofern du Hilfe beim Installieren benötigst.",
        "config-welcome-section-copyright": "=== Lizenz und Nutzungsbedingungen ===\n\n$1\n\nDieses Programm ist freie Software, d. h. es kann, gemäß den Bedingungen der von der Free Software Foundation veröffentlichten ''GNU General Public License'', weiterverteilt und/oder modifiziert werden. Dabei kann die Version 2, oder nach eigenem Ermessen, jede neuere Version der Lizenz verwendet werden.\n\nDieses Programm wird in der Hoffnung verteilt, dass es nützlich sein wird, allerdings '''ohne jegliche Garantie''' und sogar ohne die implizierte Garantie einer '''Marktgängigkeit''' oder '''Eignung für einen bestimmten Zweck'''. Hierzu sind weitere Hinweise in der ''GNU General Public License'' enthalten.\n\nEine [$2 Kopie der GNU General Public License] sollte zusammen mit diesem Programm verteilt worden sein. Sofern dies nicht der Fall war, kann eine Kopie bei der Free Software Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, schriftlich angefordert oder auf deren Website [https://www.gnu.org/copyleft/gpl.html online gelesen] werden.",
-       "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/de Website von MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/de Benutzer­anleitung]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/de Administratoren­anleitung]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/de Häufig gestellte Fragen]\n----\n* <doclink href=Readme>Lies mich</doclink>\n* <doclink href=ReleaseNotes>Versions­informationen</doclink>\n* <doclink href=Copying>Lizenz­bestimmungen</doclink>\n* <doclink href=UpgradeDoc>Aktualisierung</doclink>",
+       "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/de Website von MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/de Benutzer­anleitung]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/de Administratoren­anleitung]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/de Häufig gestellte Fragen]",
        "config-sidebar-readme": "Lies mich",
        "config-sidebar-relnotes": "Veröffentlichungsinformationen",
        "config-sidebar-license": "Kopieren",
@@ -95,7 +95,7 @@
        "config-uploads-not-safe": "'''Warnung:''' Das Standardverzeichnis für hochgeladene Dateien <code>$1</code> ist für die willkürliche Ausführung von Skripten anfällig.\nObwohl MediaWiki die hochgeladenen Dateien auf Sicherheitsrisiken überprüft, wird dennoch dringend empfohlen, diese [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security Sicherheitslücke] zu schließen, bevor das Hochladen von Dateien aktiviert wird.",
        "config-no-cli-uploads-check": "'''Warnung''': Das Standardverzeichnis für hochgeladene Dateien (<code>$1</code>) wird, während der Installation über die Kommandozeile, nicht auf Sicherheitsanfälligkeiten hinsichtlich willkürlicher Skriptausführungen geprüft.",
        "config-brokenlibxml": "Das System nutzt eine Kombination aus PHP- und libxml2-Versionen, die fehleranfällig ist und versteckte Datenfehler bei MediaWiki und anderen Webanwendungen verursachen kann.\nAktualisiere auf libxml2 2.7.3 oder später, um das Problem zu lösen. Installationsabbruch ([https://bugs.php.net/bug.php?id=45996 siehe hierzu die Fehlermeldung bei PHP]).",
-       "config-suhosin-max-value-length": "Suhosin ist installiert und beschränkt die Länge des GET-Parameters auf $1 Bytes.\nDer ResouceLoader von MediaWiki wird zwar unter diesen Bedingungen funktionieren, allerdings nur mit verminderter Leistungsfähigkeit.\nSofern möglich, sollte der Parameter <code>suhosin.get.max_value_length</code> in der Datei <code>php.ini</code> auf 1024 oder höher festgelegt werden.\nGleichzeitig muss der Parameter <code>$wgResourceLoaderMaxQueryLength</code> in der Datei <code>LocalSettings.php</code> auf den selben Wert eingestellt werden.",
+       "config-suhosin-max-value-length": "Suhosin ist installiert und beschränkt die Länge des GET-Parameters auf $1 Bytes.\nMediaWiki wird nur funktionieren, wenn code>suhosin.get.max_value_length</code> $2 oder höher ist. Deaktiviere diese Einstellung oder ändere sie in <code>php.ini</code> zu $3.",
        "config-using-32bit": "<strong>Warnung:</strong> Es scheint, als ob dein System mit 32-Bit-Ganzzahlen läuft. Dies wird [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:32-bit nicht empfohlen].",
        "config-db-type": "Datenbanksystem:",
        "config-db-host": "Datenbankserver:",
index 511b0da..7a614e8 100644 (file)
        "config-extension-not-found": "拡張機能「$1」の登録ファイルは見つかりませんでした",
        "config-extension-dependency": "拡張機能「$1」のインストール中に依存関係エラーが発生しました: $2",
        "mainpagetext": "<strong>MediaWiki はインストール済みです。</strong>",
-       "mainpagedocfooter": "ウィキソフトウェアの使い方に関する情報は[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents 利用者案内]を参照してください。\n\n== はじめましょう ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings/ja 設定の一覧]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/ja MediaWiki よくある質問と回答]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki リリース情報メーリングリスト]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation/ja MediaWiki のあなたの言語へのローカライズ]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam あなたのウィキでスパムと戦う方法を学ぶ]"
+       "mainpagedocfooter": "ウィキソフトウェアの使い方に関する情報は[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents 利用者案内]を参照してください。\n\nウィキソフトウェアの使い方に関する情報は[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents 利用者案内]を参照してください。\n\n== はじめましょう ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings/ja 設定の一覧]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/ja MediaWiki よくある質問]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki リリース情報メーリングリスト]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation/ja MediaWiki をご使用の言語へ地域化]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam ご使用のウィキでスパムと戦う方法を学ぶ]"
 }
index ba5af6c..6195104 100644 (file)
        "config-restart": "Ja, start på nytt",
        "config-welcome": "=== Miljøsjekker ===\nGrunnleggende sjekker utføres for å se om dette miljøet er egnet for en MediaWiki-installasjon.\nDu bør oppgi resultatene fra disse sjekkene om du trenger hjelp under installasjonen.",
        "config-welcome-section-copyright": "=== Opphavsrett og vilkår ===\n\n$1\n\nMediaWiki er fri programvare; du kan redistribuere det og/eller modifisere det under betingelsene i GNU General Public License som publisert av Free Software Foundation; enten versjon 2 av lisensen, eller (etter eget valg) enhver senere versjon.\n\nDette programmet er distribuert i håp om at det vil være nyttig, men '''uten noen garanti'''; ikke engang implisitt garanti av '''salgbarhet''' eller '''egnethet for et bestemt formål'''.\nSe GNU General Public License for flere detaljer.\n\nDu skal ha mottatt [$2 en kopi av GNU General Public License] sammen med dette programmet; hvis ikke, skriv til Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA eller [https://www.gnu.org/copyleft/gpl.html les det på nettet].",
-       "config-sidebar": "* [https://www.mediawiki.org MediaWiki hjem]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Brukerguide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Administratorguide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ OSS]\n----\n* <doclink href=Readme>Les meg</doclink>\n* <doclink href=ReleaseNotes>Utgivelsesnotater</doclink>\n* <doclink href=Copying>Kopiering</doclink>\n* <doclink href=UpgradeDoc>Oppgradering</doclink>",
+       "config-sidebar": "* [https://www.mediawiki.org MediaWiki.org]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Brukerguide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Administratorguide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ OSS]",
+       "config-sidebar-readme": "Les meg",
+       "config-sidebar-relnotes": "Utgivelsesnotater",
+       "config-sidebar-license": "Kopiering",
+       "config-sidebar-upgrade": "Oppgradering",
        "config-env-good": "Miljøet har blitt sjekket.\nDu kan installere MediaWiki.",
        "config-env-bad": "Miljøet har blitt sjekket.\nDu kan ikke installere MediaWiki.",
        "config-env-php": "PHP $1 er installert.",
        "config-env-hhvm": "HHVM $1 er installert.",
-       "config-unicode-using-intl": "Bruker [https://pecl.php.net/intl intl PECL-utvidelsen] for Unicode-normalisering.",
-       "config-unicode-pure-php-warning": "'''Advarsel''': [https://pecl.php.net/intl intl PECL-utvidelsen] er ikke tilgjengelig for å håndtere Unicode-normaliseringen, faller tilbake til en langsommere ren-PHP-implementasjon.\nOm du kjører et nettsted med høy trafikk bør du lese litt om [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations Unicode-normalisering].",
+       "config-unicode-using-intl": "Bruker [https://php.net/manual/en/book.intl.php PHPs intl-utvidelse] for Unicode-normalisering.",
+       "config-unicode-pure-php-warning": "<strong>Advarsel:</strong> [https://php.net/manual/en/book.intl.php PHPs intl-utvidelse] er ikke tilgjengelig for å håndtere Unicode-normaliseringen, faller tilbake til en langsommere ren-PHP-implementasjon.\nOm du kjører et nettsted med høy trafikk bør du lese om [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations Unicode-normalisering].",
        "config-unicode-update-warning": "<strong>Advarsel:</strong> Den installerte versjonen av Unicode-normalisereren bruker en eldre versjon av [http://site.icu-project.org/ ICU-prosjektets] bibliotek.\nDu bør [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations oppgradere] om du er bekymret for å bruke Unicode.",
        "config-no-db": "Fant ingen passende databasedriver! Du må installere en databasedriver for PHP.\nFølgende {{PLURAL:$2|databasetype|databasetyper}} støttes: $1\n\nOm du kompilerte PHP selv, rekonfigurer den med en aktivert databaseklient, for eksempel ved å bruke <code>./configure --with-mysqli</code>.\nOm du installerte PHP fra en Debian- eller Ubuntu-pakke, må du også installere for eksempel <code>php-mysql</code>-pakken.",
-       "config-outdated-sqlite": "'''Advarsel''': Du har SQLite $1, som er en eldre versjon enn minimumskravet SQLite $2. SQLite vil ikke være tilgjengelig.",
+       "config-outdated-sqlite": "<strong>Advarsel:</strong> Du har SQLite $2, som er en eldre versjon enn minimumskravet SQLite $1. SQLite vil ikke være tilgjengelig.",
        "config-no-fts3": "'''Advarsel''': SQLite er kompilert uten [//sqlite.org/fts3.html FTS3-modulen], søkefunksjoner vil ikke være tilgjengelig på dette bakstykket.",
        "config-pcre-old": "'''Alvorlig:''' PCRE $1 eller senere kreves.\nDin PHP-kode er lenket med PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Nærmere informasjon].",
        "config-pcre-no-utf8": "'''Fatal''': PHPs PCRE modul ser ut til å være kompilert uten PCRE_UTF8-støtte.\nMediaWiki krever UTF-8-støtte for å fungere riktig.",
@@ -83,7 +87,7 @@
        "config-uploads-not-safe": "'''Advarsel:''' Din standardmappe for opplastinger <code>$1</code> er sårbar for kjøring av vilkårlige skript.\nSelv om MediaWiki sjekker alle opplastede filer for sikkerhetstrusler er det sterkt anbefalt å [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security lukke denne sikkerhetssårbarheten] før du aktiverer opplastinger.",
        "config-no-cli-uploads-check": "'''Advarsel:''' Din standard-katalog for opplastinger (<code>$1</code>) er ikke kontrollert for sårbarhet overfor vilkårlig skript-kjøring under CLI-installasjonen.",
        "config-brokenlibxml": "Ditt system bruker en kombinasjon av PHP- og libxml2-versjoner som har feil og kan forårsake skjult dataødeleggelse i MediaWiki og andre web-applikasjoner.\nOppgrader til libxml2 2.7.3 eller nyere ([https://bugs.php.net/bug.php?id=45996 Feil-liste for PHP]).\nInstalleringen ble abortert.",
-       "config-suhosin-max-value-length": "Suhosin er installert og begrenser GET-parameterlengder til $1 bytes. MediaWiki sin ResourceLoader-komponent klarer å komme rundt denne begrensningen, men med redusert ytelse. Om mulig bør du sette <code>suhosin.get.max_value_length</code> til minst 1024 i <code>php.ini</code>, og sette <code>$wgResourceLoaderMaxQueryLength</code> til samme verdi i <code>LocalSettings.php</code>.",
+       "config-suhosin-max-value-length": "Suhosin er installert og begrenser GET-parameterlengder til $1 bytes.\nMediaWiki krever at <code>suoshin.get.max_value_length</code> er minst $2. Slå av denne innstillingen eller øk verdien til $3 i <code>php.ini</code>.",
        "config-using-32bit": "<strong>Adversel:</strong> Systemet ditt ser ut til å være 32-bit-basert, mens dette er [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:32-bit not advised].",
        "config-db-type": "Databasetype:",
        "config-db-host": "Databasevert:",
index 938f199..e9d578b 100644 (file)
@@ -93,7 +93,7 @@
        "config-uploads-not-safe": "'''Увага:''' Ваша типова папка для завантажень <code>$1</code> вразлива до виконання довільних скриптів.\nХоча MediaWiki перевіряє усі завантажені файли на наявність загроз, наполегливо рекомендується [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security закрити дану вразливість] перед тим, як дозволяти завантаження файлів.",
        "config-no-cli-uploads-check": "'''Увага:''' Ваша типова папка для завантажень (<code>$1</code>) не перевірялась на вразливість до виконання довільних скриптів під час встановлення CLI.",
        "config-brokenlibxml": "У Вашій системі невдале поєднання версій PHP і libxml2, яке може спричинити пошкодження прихованих даних у MediaWiki та інших веб-застосунках.\nОновіть libxml2 до версії 2.7.3 або пізнішої  ([https://bugs.php.net/bug.php?id=45996 відомості про помилку]).\nВстановлення перервано.",
-       "config-suhosin-max-value-length": "Suhosin Ð²Ñ\81Ñ\82ановлено Ñ\96 Ð¾Ð±Ð¼ÐµÐ¶Ñ\83Ñ\94 Ð¿Ð°Ñ\80амеÑ\82Ñ\80а GET  <code>length</code> Ð´Ð¾ $1 Ð±Ð°Ð¹Ñ\82а. Ð\9aомпоненÑ\82 MediaWiki ResourceLoader Ð±Ñ\83де Ð¾Ð±Ñ\85одиÑ\82и Ñ\86е Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ\8f, Ð¾Ð´Ð½Ð°Ðº Ñ\86е Ð·Ð¼ÐµÐ½Ñ\88иÑ\82Ñ\8c Ð¿Ñ\80одÑ\83кÑ\82ивнÑ\96Ñ\81Ñ\82Ñ\8c. Ð¯ÐºÑ\89о Ñ\86е Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾, Ð\92ам Ð²Ð°Ñ\80Ñ\82о Ð²Ñ\81Ñ\82ановиÑ\82и Ð·Ð½Ð°Ñ\87еннÑ\8f <code>suhosin.get.max_value_length</code> Ñ\8fк 1024 Ñ\96 Ð±Ñ\96лÑ\8cÑ\88е Ñ\83 <code>php.ini</code> Ñ\96 Ð²Ñ\81Ñ\82ановиÑ\82и Ñ\82аке Ð¶ Ð·Ð½Ð°Ñ\87еннÑ\8f <code>$wgResourceLoaderMaxQueryLength</code> Ñ\83 LocalSettings.php .",
+       "config-suhosin-max-value-length": "Suhosin Ð²Ñ\81Ñ\82ановлено Ñ\96 Ð²Ñ\96н Ð¾Ð±Ð¼ÐµÐ¶Ñ\83Ñ\94 GET-паÑ\80амеÑ\82Ñ\80 <code>length</code> Ð´Ð¾ $1 {{PLURAL:$1|байÑ\82|байÑ\82а|байÑ\82}}.   Ð\94лÑ\8f MediaWiki Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð·Ð½Ð°Ñ\87еннÑ\8f <code>suhosin.get.max_value_length</code> Ð±Ñ\83ло Ñ\85оÑ\87а Ð± $2. Ð\92имкнÑ\96Ñ\82Ñ\8c Ñ\86е Ð½Ð°Ð»Ð°Ñ\88Ñ\82Ñ\83ваннÑ\8f, Ð°Ð±Ð¾ Ð·Ð±Ñ\96лÑ\8cÑ\88Ñ\96Ñ\82Ñ\8c Ð·Ð½Ð°Ñ\87еннÑ\8f Ñ\83 <code>php.ini</code> Ð´Ð¾ $3.",
        "config-using-32bit": "<strong>Попередження:</strong> схоже, що Ваша система працює з 32-бітними цілими числами. Таке [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:32-bit не рекомендується].",
        "config-db-type": "Тип бази даних:",
        "config-db-host": "Хост бази даних:",
index 19dd8fe..7303d9b 100644 (file)
@@ -316,6 +316,23 @@ class StringUtils {
                return $text;
        }
 
+       /**
+        * Utility function to check if the given string is a valid PCRE regex. Avoids
+        * manually calling suppressWarnings and restoreWarnings, and provides a
+        * one-line solution without the need to use @.
+        *
+        * @since 1.34
+        * @param string $string The string you want to check being a valid regex
+        * @return bool
+        */
+       public static function isValidPCRERegex( $string ) {
+               AtEase::suppressWarnings();
+               // @phan-suppress-next-line PhanParamSuspiciousOrder False positive
+               $isValid = preg_match( $string, '' );
+               AtEase::restoreWarnings();
+               return $isValid !== false;
+       }
+
        /**
         * Escape a string to make it suitable for inclusion in a preg_replace()
         * replacement parameter.
@@ -343,21 +360,4 @@ class StringUtils {
                        return new ArrayIterator( explode( $separator, $subject ) );
                }
        }
-
-       /**
-        * Utility function to check if the given string is a valid regex. Avoids
-        * manually calling suppressWarnings and restoreWarnings, and provides a
-        * one-line solution without the need to use @.
-        *
-        * @since 1.34
-        * @param string $string The string you want to check being a valid regex
-        * @return bool
-        */
-       public static function isValidRegex( $string ) {
-               AtEase::suppressWarnings();
-               // @phan-suppress-next-line PhanParamSuspiciousOrder False positive
-               $isValid = preg_match( $string, '' );
-               AtEase::restoreWarnings();
-               return $isValid !== false;
-       }
 }
index 70f3553..2f44a55 100644 (file)
@@ -1269,7 +1269,7 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                // Nested callback process cache use is not lag-safe with regard to HOLDOFF_TTL since
                // process cached values are more lagged than persistent ones as they are not purged.
                if ( $pCache && $this->callbackDepth == 0 ) {
-                       $cached = $pCache->get( $this->getProcessCacheKey( $key, $version ), INF, false );
+                       $cached = $pCache->get( $this->getProcessCacheKey( $key, $version ), $pcTTL, false );
                        if ( $cached !== false ) {
                                return $cached;
                        }
@@ -2545,6 +2545,9 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                if ( !isset( $this->processCaches[$group] ) ) {
                        list( , $size ) = explode( ':', $group );
                        $this->processCaches[$group] = new MapCacheLRU( (int)$size );
+                       if ( $this->wallClockOverride !== null ) {
+                               $this->processCaches[$group]->setMockTime( $this->wallClockOverride );
+                       }
                }
 
                return $this->processCaches[$group];
@@ -2641,5 +2644,8 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
        public function setMockTime( &$time ) {
                $this->wallClockOverride =& $time;
                $this->cache->setMockTime( $time );
+               foreach ( $this->processCaches as $pCache ) {
+                       $pCache->setMockTime( $time );
+               }
        }
 }
index 7be3b7d..464e68c 100644 (file)
@@ -850,27 +850,44 @@ abstract class DatabaseMysqlBase extends Database {
                }
 
                if ( $this->getLBInfo( 'is static' ) === true ) {
+                       $this->queryLogger->debug(
+                               "Bypassed replication wait; database has a static dataset",
+                               $this->getLogContext( [ 'method' => __METHOD__ ] )
+                       );
+
                        return 0; // this is a copy of a read-only dataset with no master DB
                } elseif ( $this->lastKnownReplicaPos && $this->lastKnownReplicaPos->hasReached( $pos ) ) {
+                       $this->queryLogger->debug(
+                               "Bypassed replication wait; replication already known to have reached $pos",
+                               $this->getLogContext( [ 'method' => __METHOD__ ] )
+                       );
+
                        return 0; // already reached this point for sure
                }
 
                // Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
                if ( $pos->getGTIDs() ) {
-                       // Ignore GTIDs from domains exclusive to the master DB (presumably inactive)
-                       $rpos = $this->getReplicaPos();
-                       $gtidsWait = $rpos ? MySQLMasterPos::getCommonDomainGTIDs( $pos, $rpos ) : [];
+                       // Get the GTIDs from this replica server too see the domains (channels)
+                       $refPos = $this->getReplicaPos();
+                       if ( !$refPos ) {
+                               $this->queryLogger->error(
+                                       "Could not get replication position",
+                                       $this->getLogContext( [ 'method' => __METHOD__ ] )
+                               );
+
+                               return -1; // this is the master itself?
+                       }
+                       // GTIDs with domains (channels) that are active and are present on the replica
+                       $gtidsWait = $pos::getRelevantActiveGTIDs( $pos, $refPos );
                        if ( !$gtidsWait ) {
                                $this->queryLogger->error(
-                                       "No GTIDs with the same domain between master ($pos) and replica ($rpos)",
-                                       $this->getLogContext( [
-                                               'method' => __METHOD__,
-                                       ] )
+                                       "No active GTIDs in $pos share a domain with those in $refPos",
+                                       $this->getLogContext( [ 'method' => __METHOD__, 'activeDomain' => $pos ] )
                                );
 
                                return -1; // $pos is from the wrong cluster?
                        }
-                       // Wait on the GTID set (MariaDB only)
+                       // Wait on the GTID set
                        $gtidArg = $this->addQuotes( implode( ',', $gtidsWait ) );
                        if ( strpos( $gtidArg, ':' ) !== false ) {
                                // MySQL GTIDs, e.g "source_id:transaction_id"
@@ -886,28 +903,28 @@ abstract class DatabaseMysqlBase extends Database {
                        $sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)";
                }
 
-               list( $res, $err ) = $this->executeQuery( $sql, __METHOD__, self::QUERY_IGNORE_DBO_TRX );
-               $row = $res ? $this->fetchRow( $res ) : false;
-               if ( !$row ) {
-                       throw new DBExpectedError( $this, "Replication wait failed: {$err}" );
-               }
+               $res = $this->query( $sql, __METHOD__, self::QUERY_IGNORE_DBO_TRX );
+               $row = $this->fetchRow( $res );
 
                // Result can be NULL (error), -1 (timeout), or 0+ per the MySQL manual
                $status = ( $row[0] !== null ) ? intval( $row[0] ) : null;
                if ( $status === null ) {
-                       if ( !$pos->getGTIDs() ) {
-                               // T126436: jobs programmed to wait on master positions might be referencing
-                               // binlogs with an old master hostname; this makes MASTER_POS_WAIT() return null.
-                               // Try to detect this case and treat the replica DB as having reached the given
-                               // position (any master switchover already requires that the new master be caught
-                               // up before the switch).
-                               $replicationPos = $this->getReplicaPos();
-                               if ( $replicationPos && !$replicationPos->channelsMatch( $pos ) ) {
-                                       $this->lastKnownReplicaPos = $replicationPos;
-                                       $status = 0;
-                               }
-                       }
+                       $this->queryLogger->error(
+                               "An error occurred while waiting for replication to reach $pos",
+                               $this->getLogContext( [ 'method' => __METHOD__, 'sql' => $sql ] )
+                       );
+               } elseif ( $status < 0 ) {
+                       $this->queryLogger->error(
+                               "Timed out waiting for replication to reach $pos",
+                               $this->getLogContext( [
+                                       'method' => __METHOD__, 'sql' => $sql, 'timeout' => $timeout
+                               ] )
+                       );
                } elseif ( $status >= 0 ) {
+                       $this->queryLogger->debug(
+                               "Replication has reached $pos",
+                               $this->getLogContext( [ 'method' => __METHOD__ ] )
+                       );
                        // Remember that this position was reached to save queries next time
                        $this->lastKnownReplicaPos = $pos;
                }
index fa2c1db..e1b8f9a 100644 (file)
@@ -190,39 +190,69 @@ class MySQLMasterPos implements DBMasterPos {
        }
 
        /**
+        * Set the GTID domain known to be used in new commits on a replication stream of interest
+        *
+        * This makes getRelevantActiveGTIDs() filter out GTIDs from other domains
+        *
+        * @see MySQLMasterPos::getRelevantActiveGTIDs()
+        * @see https://mariadb.com/kb/en/library/gtid/#gtid_domain_id
+        *
         * @param int|null $id @@gtid_domain_id of the active replication stream
+        * @return MySQLMasterPos This instance (since 1.34)
         * @since 1.31
         */
        public function setActiveDomain( $id ) {
                $this->activeDomain = (int)$id;
+
+               return $this;
        }
 
        /**
+        * Set the server ID known to be used in new commits on a replication stream of interest
+        *
+        * This makes getRelevantActiveGTIDs() filter out GTIDs from other origin servers
+        *
+        * @see MySQLMasterPos::getRelevantActiveGTIDs()
+        *
         * @param int|null $id @@server_id of the server were writes originate
+        * @return MySQLMasterPos This instance (since 1.34)
         * @since 1.31
         */
        public function setActiveOriginServerId( $id ) {
                $this->activeServerId = (int)$id;
+
+               return $this;
        }
 
        /**
+        * Set the server UUID known to be used in new commits on a replication stream of interest
+        *
+        * This makes getRelevantActiveGTIDs() filter out GTIDs from other origin servers
+        *
+        * @see MySQLMasterPos::getRelevantActiveGTIDs()
+        *
         * @param string|null $id @@server_uuid of the server were writes originate
+        * @return MySQLMasterPos This instance (since 1.34)
         * @since 1.31
         */
        public function setActiveOriginServerUUID( $id ) {
                $this->activeServerUUID = $id;
+
+               return $this;
        }
 
        /**
         * @param MySQLMasterPos $pos
         * @param MySQLMasterPos $refPos
-        * @return string[] List of GTIDs from $pos that have domains in $refPos
-        * @since 1.31
+        * @return string[] List of active GTIDs from $pos that have domains in $refPos
+        * @since 1.34
         */
-       public static function getCommonDomainGTIDs( MySQLMasterPos $pos, MySQLMasterPos $refPos ) {
-               return array_values(
-                       array_intersect_key( $pos->gtids, $refPos->getActiveGtidCoordinates() )
-               );
+       public static function getRelevantActiveGTIDs( MySQLMasterPos $pos, MySQLMasterPos $refPos ) {
+               return array_values( array_intersect_key(
+                       $pos->gtids,
+                       $pos->getActiveGtidCoordinates(),
+                       $refPos->gtids
+               ) );
        }
 
        /**
index 4c68833..a3e57ae 100644 (file)
@@ -27,11 +27,6 @@ use InvalidArgumentException;
 
 /**
  * Trivial LoadBalancer that always returns an injected connection handle.
- *
- * Note that, while this LoadBalancer does not open any connections itself,
- * it still closes the injected connection at times, including during destruction.
- * It is therefore unsuitable for use in tests unless you have a Database instance
- * separate from the main test database (which is expected to stay open).
  */
 class LoadBalancerSingle extends LoadBalancer {
        /** @var IDatabase */
index 781df06..0b78a36 100644 (file)
@@ -361,6 +361,11 @@ class LogPager extends ReverseChronologicalPager {
                if ( !$this->mTagFilter && !array_key_exists( 'ls_field', $this->mConds ) ) {
                        $options[] = 'STRAIGHT_JOIN';
                }
+               if ( $this->performer !== '' ) {
+                       // T223151: MariaDB's optimizer, at least 10.1, likes to choose a wildly bad plan for
+                       // some reason for this code path. Tell it not to use the wrong index it wants to pick.
+                       $options['IGNORE INDEX'] = [ 'logging' => [ 'times' ] ];
+               }
 
                $info = [
                        'tables' => $tables,
index 6e3b26b..81d9aa2 100644 (file)
@@ -101,7 +101,7 @@ class ProtectLogFormatter extends LogFormatter {
                ];
 
                // Show change protection link
-               if ( !MediaWikiServices::getInstance()
+               if ( MediaWikiServices::getInstance()
                                ->getPermissionManager()
                                ->userHasRight( $this->context->getUser(), 'protect' )
                ) {
index 4b37181..b6e366e 100644 (file)
@@ -1668,7 +1668,7 @@ class Article implements Page {
                }
 
                // the outer div is need for styling the revision info and nav in MobileFrontend
-               $outputPage->addSubtitle( "<div class=\"mw-revision\">" . $revisionInfo .
+               $outputPage->addSubtitle( "<div class=\"mw-revision warningbox\">" . $revisionInfo .
                        "<div id=\"mw-revision-nav\">" . $cdel .
                        $context->msg( 'revision-nav' )->rawParams(
                                $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
index ec1628f..9ec5834 100644 (file)
@@ -910,7 +910,7 @@ class Parser {
         */
        public function setTitle( $t ) {
                if ( !$t ) {
-                       $t = Title::newFromText( 'NO TITLE' );
+                       $t = Title::makeTitle( NS_SPECIAL, 'Badtitle/Parser' );
                }
 
                if ( $t->hasFragment() ) {
index d959ff6..5d104d3 100644 (file)
@@ -689,24 +689,29 @@ class ResourceLoader implements LoggerAwareInterface {
         *
         * @since 1.28
         * @param ResourceLoaderContext $context
+        * @param string[]|null $modules
         * @return string Hash
         */
-       public function makeVersionQuery( ResourceLoaderContext $context ) {
+       public function makeVersionQuery( ResourceLoaderContext $context, array $modules = null ) {
+               if ( $modules === null ) {
+                       wfDeprecated( __METHOD__ . ' without $modules', '1.34' );
+                       $modules = $context->getModules();
+               }
                // As of MediaWiki 1.28, the server and client use the same algorithm for combining
                // version hashes. There is no technical reason for this to be same, and for years the
                // implementations differed. If getCombinedVersion in PHP (used for StartupModule and
                // E-Tag headers) differs in the future from getCombinedVersion in JS (used for 'version'
                // query parameter), then this method must continue to match the JS one.
-               $moduleNames = [];
-               foreach ( $context->getModules() as $name ) {
+               $filtered = [];
+               foreach ( $modules as $name ) {
                        if ( !$this->getModule( $name ) ) {
                                // If a versioned request contains a missing module, the version is a mismatch
                                // as the client considered a module (and version) we don't have.
                                return '';
                        }
-                       $moduleNames[] = $name;
+                       $filtered[] = $name;
                }
-               return $this->getCombinedVersion( $context, $moduleNames );
+               return $this->getCombinedVersion( $context, $filtered );
        }
 
        /**
@@ -819,7 +824,7 @@ class ResourceLoader implements LoggerAwareInterface {
                        $errorResponse = self::makeComment( $errorText );
                        if ( $context->shouldIncludeScripts() ) {
                                $errorResponse .= 'if (window.console && console.error) { console.error('
-                                       . self::encodeJsonForScript( $errorText )
+                                       . $context->encodeJson( $errorText )
                                        . "); }\n";
                        }
 
@@ -863,7 +868,7 @@ class ResourceLoader implements LoggerAwareInterface {
                // - Version mismatch (T117587, T47877)
                if ( is_null( $context->getVersion() )
                        || $errors
-                       || $context->getVersion() !== $this->makeVersionQuery( $context )
+                       || $context->getVersion() !== $this->makeVersionQuery( $context, $context->getModules() )
                ) {
                        $maxage = $rlMaxage['unversioned']['client'];
                        $smaxage = $rlMaxage['unversioned']['server'];
@@ -1093,7 +1098,14 @@ MESSAGE;
                                                        $strContent = $scripts;
                                                } elseif ( is_array( $scripts ) ) {
                                                        // ...except when $scripts is an array of URLs or an associative array
-                                                       $strContent = self::makeLoaderImplementScript( $implementKey, $scripts, [], [], [] );
+                                                       $strContent = self::makeLoaderImplementScript(
+                                                               $context,
+                                                               $implementKey,
+                                                               $scripts,
+                                                               [],
+                                                               [],
+                                                               []
+                                                       );
                                                }
                                                break;
                                        case 'styles':
@@ -1119,6 +1131,7 @@ MESSAGE;
                                                        }
                                                }
                                                $strContent = self::makeLoaderImplementScript(
+                                                       $context,
                                                        $implementKey,
                                                        $scripts,
                                                        $content['styles'] ?? [],
@@ -1164,7 +1177,7 @@ MESSAGE;
 
                        // Set the state of modules we didn't respond to with mw.loader.implement
                        if ( $states ) {
-                               $stateScript = self::makeLoaderStateScript( $states );
+                               $stateScript = self::makeLoaderStateScript( $context, $states );
                                if ( !$context->getDebug() ) {
                                        $stateScript = self::filter( 'minify-js', $stateScript );
                                }
@@ -1173,7 +1186,7 @@ MESSAGE;
                        }
                } elseif ( $states ) {
                        $this->errors[] = 'Problematic modules: '
-                               . self::encodeJsonForScript( $states );
+                               . $context->encodeJson( $states );
                }
 
                return $out;
@@ -1212,6 +1225,7 @@ MESSAGE;
        /**
         * Return JS code that calls mw.loader.implement with given module properties.
         *
+        * @param ResourceLoaderContext $context
         * @param string $name Module name or implement key (format "`[name]@[version]`")
         * @param XmlJsCode|array|string $scripts Code as XmlJsCode (to be wrapped in a closure),
         *  list of URLs to JavaScript files, string of JavaScript for `$.globalEval`, or array with
@@ -1226,13 +1240,13 @@ MESSAGE;
         * @throws MWException
         * @return string JavaScript code
         */
-       protected static function makeLoaderImplementScript(
-               $name, $scripts, $styles, $messages, $templates
+       private static function makeLoaderImplementScript(
+               ResourceLoaderContext $context, $name, $scripts, $styles, $messages, $templates
        ) {
                if ( $scripts instanceof XmlJsCode ) {
                        if ( $scripts->value === '' ) {
                                $scripts = null;
-                       } elseif ( self::inDebugMode() ) {
+                       } elseif ( $context->getDebug() ) {
                                $scripts = new XmlJsCode( "function ( $, jQuery, require, module ) {\n{$scripts->value}\n}" );
                        } else {
                                $scripts = new XmlJsCode( 'function($,jQuery,require,module){' . $scripts->value . '}' );
@@ -1244,7 +1258,7 @@ MESSAGE;
                                // All of these essentially do $file = $file['content'];, some just have wrapping around it
                                if ( $file['type'] === 'script' ) {
                                        // Multi-file modules only get two parameters ($ and jQuery are being phased out)
-                                       if ( self::inDebugMode() ) {
+                                       if ( $context->getDebug() ) {
                                                $file = new XmlJsCode( "function ( require, module ) {\n{$file['content']}\n}" );
                                        } else {
                                                $file = new XmlJsCode( 'function(require,module){' . $file['content'] . '}' );
@@ -1255,8 +1269,8 @@ MESSAGE;
                        }
                        $scripts = XmlJsCode::encodeObject( [
                                'main' => $scripts['main'],
-                               'files' => XmlJsCode::encodeObject( $files, self::inDebugMode() )
-                       ], self::inDebugMode() );
+                               'files' => XmlJsCode::encodeObject( $files, $context->getDebug() )
+                       ], $context->getDebug() );
                } elseif ( !is_string( $scripts ) && !is_array( $scripts ) ) {
                        throw new MWException( 'Invalid scripts error. Array of URLs or string of code expected.' );
                }
@@ -1273,7 +1287,7 @@ MESSAGE;
                ];
                self::trimArray( $module );
 
-               return Xml::encodeJsCall( 'mw.loader.implement', $module, self::inDebugMode() );
+               return Xml::encodeJsCall( 'mw.loader.implement', $module, $context->getDebug() );
        }
 
        /**
@@ -1355,22 +1369,26 @@ MESSAGE;
         * Returns a JS call to mw.loader.state, which sets the state of one
         * ore more modules to a given value. Has two calling conventions:
         *
-        *    - ResourceLoader::makeLoaderStateScript( $name, $state ):
+        *    - ResourceLoader::makeLoaderStateScript( $context, $name, $state ):
         *         Set the state of a single module called $name to $state
         *
-        *    - ResourceLoader::makeLoaderStateScript( [ $name => $state, ... ] ):
+        *    - ResourceLoader::makeLoaderStateScript( $context, [ $name => $state, ... ] ):
         *         Set the state of modules with the given names to the given states
         *
+        * @internal
+        * @param ResourceLoaderContext $context
         * @param array|string $states
         * @param string|null $state
         * @return string JavaScript code
         */
-       public static function makeLoaderStateScript( $states, $state = null ) {
+       public static function makeLoaderStateScript(
+               ResourceLoaderContext $context, $states, $state = null
+       ) {
                if ( !is_array( $states ) ) {
                        $states = [ $states => $state ];
                }
                return 'mw.loader.state('
-                       . self::encodeJsonForScript( $states )
+                       . $context->encodeJson( $states )
                        . ');';
        }
 
@@ -1415,15 +1433,15 @@ MESSAGE;
         * @par Example
         * @code
         *
-        *     ResourceLoader::makeLoaderRegisterScript( [
+        *     ResourceLoader::makeLoaderRegisterScript( $context, [
         *        [ $name1, $version1, $dependencies1, $group1, $source1, $skip1 ],
         *        [ $name2, $version2, $dependencies1, $group2, $source2, $skip2 ],
         *        ...
         *     ] ):
         * @endcode
         *
-        * @internal
-        * @since 1.32
+        * @internal For use by ResourceLoaderStartUpModule only
+        * @param ResourceLoaderContext $context
         * @param array $modules Array of module registration arrays, each containing
         *  - string: module name
         *  - string: module version
@@ -1433,7 +1451,9 @@ MESSAGE;
         *  - string|null: Script body of a skip function (optional)
         * @return string JavaScript code
         */
-       public static function makeLoaderRegisterScript( array $modules ) {
+       public static function makeLoaderRegisterScript(
+               ResourceLoaderContext $context, array $modules
+       ) {
                // Optimisation: Transform dependency names into indexes when possible
                // to produce smaller output. They are expanded by mw.loader.register on
                // the other end using resolveIndexedDependencies().
@@ -1456,7 +1476,7 @@ MESSAGE;
                array_walk( $modules, [ self::class, 'trimArray' ] );
 
                return 'mw.loader.register('
-                       . self::encodeJsonForScript( $modules )
+                       . $context->encodeJson( $modules )
                        . ');';
        }
 
@@ -1464,22 +1484,28 @@ MESSAGE;
         * Returns JS code which calls mw.loader.addSource() with the given
         * parameters. Has two calling conventions:
         *
-        *   - ResourceLoader::makeLoaderSourcesScript( $id, $properties ):
+        *   - ResourceLoader::makeLoaderSourcesScript( $context, $id, $properties ):
         *       Register a single source
         *
-        *   - ResourceLoader::makeLoaderSourcesScript( [ $id1 => $loadUrl, $id2 => $loadUrl, ... ] );
+        *   - ResourceLoader::makeLoaderSourcesScript( $context,
+        *         [ $id1 => $loadUrl, $id2 => $loadUrl, ... ]
+        *     );
         *       Register sources with the given IDs and properties.
         *
+        * @internal For use by ResourceLoaderStartUpModule only
+        * @param ResourceLoaderContext $context
         * @param string|array $sources Source ID
         * @param string|null $loadUrl load.php url
         * @return string JavaScript code
         */
-       public static function makeLoaderSourcesScript( $sources, $loadUrl = null ) {
+       public static function makeLoaderSourcesScript(
+               ResourceLoaderContext $context, $sources, $loadUrl = null
+       ) {
                if ( !is_array( $sources ) ) {
                        $sources = [ $sources => $loadUrl ];
                }
                return 'mw.loader.addSource('
-                       . self::encodeJsonForScript( $sources )
+                       . $context->encodeJson( $sources )
                        . ');';
        }
 
index bf03e49..71961e2 100644 (file)
@@ -213,7 +213,7 @@ class ResourceLoaderClientHtml {
                                // Load from load.php?only=styles via <link rel=stylesheet>
                                $data['styles'][] = $name;
                        }
-                       $deprecation = $module->getDeprecationInformation();
+                       $deprecation = $module->getDeprecationInformation( $context );
                        if ( $deprecation ) {
                                $data['styleDeprecations'][] = $deprecation;
                        }
@@ -254,14 +254,14 @@ class ResourceLoaderClientHtml {
                // See also startup/startup.js.
                $nojsClass = $nojsClass ?? $this->getDocumentAttributes()['class'];
                $jsClass = preg_replace( '/(^|\s)client-nojs(\s|$)/', '$1client-js$2', $nojsClass );
-               $jsClassJson = ResourceLoader::encodeJsonForScript( $jsClass );
+               $jsClassJson = $this->context->encodeJson( $jsClass );
                $script = <<<JAVASCRIPT
 document.documentElement.className = {$jsClassJson};
 JAVASCRIPT;
 
                // Inline script: Declare mw.config variables for this page.
                if ( $this->config ) {
-                       $confJson = ResourceLoader::encodeJsonForScript( $this->config );
+                       $confJson = $this->context->encodeJson( $this->config );
                        $script .= <<<JAVASCRIPT
 RLCONF = {$confJson};
 JAVASCRIPT;
@@ -270,7 +270,7 @@ JAVASCRIPT;
                // Inline script: Declare initial module states for this page.
                $states = array_merge( $this->exemptStates, $data['states'] );
                if ( $states ) {
-                       $stateJson = ResourceLoader::encodeJsonForScript( $states );
+                       $stateJson = $this->context->encodeJson( $states );
                        $script .= <<<JAVASCRIPT
 RLSTATE = {$stateJson};
 JAVASCRIPT;
@@ -278,7 +278,7 @@ JAVASCRIPT;
 
                // Inline script: Declare general modules to load on this page.
                if ( $data['general'] ) {
-                       $pageModulesJson = ResourceLoader::encodeJsonForScript( $data['general'] );
+                       $pageModulesJson = $this->context->encodeJson( $data['general'] );
                        $script .= <<<JAVASCRIPT
 RLPAGEMODULES = {$pageModulesJson};
 JAVASCRIPT;
@@ -436,7 +436,8 @@ JAVASCRIPT;
 
                                // Link/embed each set
                                foreach ( $moduleSets as list( $embed, $moduleSet ) ) {
-                                       $context->setModules( array_keys( $moduleSet ) );
+                                       $moduleSetNames = array_keys( $moduleSet );
+                                       $context->setModules( $moduleSetNames );
                                        if ( $embed ) {
                                                // Decide whether to use style or script element
                                                if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
@@ -456,10 +457,10 @@ JAVASCRIPT;
                                                // This should NOT be done for the site group (T29564) because anons get that too
                                                // and we shouldn't be putting timestamps in CDN-cached HTML
                                                if ( $group === 'user' ) {
-                                                       // Must setModules() before makeVersionQuery()
-                                                       $context->setVersion( $rl->makeVersionQuery( $context ) );
+                                                       $context->setVersion( $rl->makeVersionQuery( $context, $moduleSetNames ) );
                                                }
 
+                                               // Must setModules() before createLoaderURL()
                                                $url = $rl->createLoaderURL( $source, $context, $extraQuery );
 
                                                // Decide whether to use 'style' or 'script' element
@@ -479,7 +480,7 @@ JAVASCRIPT;
                                                        ] );
                                                } else {
                                                        $chunk = ResourceLoader::makeInlineScript(
-                                                               'mw.loader.load(' . ResourceLoader::encodeJsonForScript( $url ) . ');',
+                                                               'mw.loader.load(' . $mainContext->encodeJson( $url ) . ');',
                                                                $nonce
                                                        );
                                                }
index 3db0c01..1274052 100644 (file)
@@ -406,7 +406,7 @@ class ResourceLoaderContext implements MessageLocalizer {
        /**
         * Get the request base parameters, omitting any defaults.
         *
-        * @internal For internal use by ResourceLoaderStartUpModule only
+        * @internal For use by ResourceLoaderStartUpModule only
         * @return array
         */
        public function getReqBase() {
@@ -422,4 +422,32 @@ class ResourceLoaderContext implements MessageLocalizer {
                }
                return $reqBase;
        }
+
+       /**
+        * Wrapper around json_encode that avoids needless escapes,
+        * and pretty-prints in debug mode.
+        *
+        * @internal
+        * @param mixed $data
+        * @return string|false JSON string, false on error
+        */
+       public function encodeJson( $data ) {
+               // Keep output as small as possible by disabling needless escape modes
+               // that PHP uses by default.
+               // However, while most module scripts are only served on HTTP responses
+               // for JavaScript, some modules can also be embedded in the HTML as inline
+               // scripts. This, and the fact that we sometimes need to export strings
+               // containing user-generated content and labels that may genuinely contain
+               // a sequences like "</script>", we need to encode either '/' or '<'.
+               // By default PHP escapes '/'. Let's escape '<' instead which is less common
+               // and allows URLs to mostly remain readable.
+               $jsonFlags = JSON_UNESCAPED_SLASHES |
+                       JSON_UNESCAPED_UNICODE |
+                       JSON_HEX_TAG |
+                       JSON_HEX_AMP;
+               if ( $this->getDebug() ) {
+                       $jsonFlags |= JSON_PRETTY_PRINT;
+               }
+               return json_encode( $data, $jsonFlags );
+       }
 }
index 23a9a14..3388058 100644 (file)
@@ -384,7 +384,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @return string|array JavaScript code for $context, or package files data structure
         */
        public function getScript( ResourceLoaderContext $context ) {
-               $deprecationScript = $this->getDeprecationInformation();
+               $deprecationScript = $this->getDeprecationInformation( $context );
                if ( $this->packageFiles !== null ) {
                        $packageFiles = $this->getPackageFiles( $context );
                        if ( $deprecationScript ) {
@@ -906,7 +906,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         *     keyed by media type
         * @throws MWException
         */
-       public function readStyleFiles( array $styles, $flip, $context ) {
+       public function readStyleFiles( array $styles, $flip, ResourceLoaderContext $context ) {
                if ( !$styles ) {
                        return [];
                }
@@ -933,7 +933,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @return string CSS data in script file
         * @throws MWException If the file doesn't exist
         */
-       protected function readStyleFile( $path, $flip, $context ) {
+       protected function readStyleFile( $path, $flip, ResourceLoaderContext $context ) {
                $localPath = $this->getLocalPath( $path );
                $remotePath = $this->getRemotePath( $path );
                if ( !file_exists( $localPath ) ) {
@@ -973,7 +973,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @param ResourceLoaderContext $context
         * @return bool
         */
-       public function getFlip( $context ) {
+       public function getFlip( ResourceLoaderContext $context ) {
                return $context->getDirection() === 'rtl' && !$this->noflip;
        }
 
index ffc9b3d..a6e5f15 100644 (file)
@@ -57,8 +57,8 @@ class ResourceLoaderLanguageDataModule extends ResourceLoaderFileModule {
        public function getScript( ResourceLoaderContext $context ) {
                return parent::getScript( $context )
                        . 'mw.language.setData('
-                       . ResourceLoader::encodeJsonForScript( $context->getLanguage() ) . ','
-                       . ResourceLoader::encodeJsonForScript( $this->getData( $context ) )
+                       . $context->encodeJson( $context->getLanguage() ) . ','
+                       . $context->encodeJson( $this->getData( $context ) )
                        . ');';
        }
 
index c9fd267..eca3a97 100644 (file)
@@ -128,7 +128,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
         * @param ResourceLoaderContext $context
         * @return bool
         */
-       public function getFlip( $context ) {
+       public function getFlip( ResourceLoaderContext $context ) {
                return MediaWikiServices::getInstance()->getContentLanguage()->getDir() !==
                        $context->getDirection();
        }
@@ -136,9 +136,13 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
        /**
         * Get JS representing deprecation information for the current module if available
         *
+        * @param ResourceLoaderContext|null $context Missing $context is deprecated in 1.34
         * @return string JavaScript code
         */
-       public function getDeprecationInformation() {
+       public function getDeprecationInformation( ResourceLoaderContext $context = null ) {
+               if ( $context === null ) {
+                       wfDeprecated( __METHOD__ . ' without a ResourceLoader context', '1.34' );
+               }
                $deprecationInfo = $this->deprecated;
                if ( $deprecationInfo ) {
                        $name = $this->getName();
@@ -146,7 +150,10 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                        if ( is_string( $deprecationInfo ) ) {
                                $warning .= "\n" . $deprecationInfo;
                        }
-                       return 'mw.log.warn(' . ResourceLoader::encodeJsonForScript( $warning ) . ');';
+                       if ( $context === null ) {
+                               return 'mw.log.warn(' . ResourceLoader::encodeJsonForScript( $warning ) . ');';
+                       }
+                       return 'mw.log.warn(' . $context->encodeJson( $warning ) . ');';
                } else {
                        return '';
                }
index 78775fb..df8126e 100644 (file)
@@ -58,7 +58,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
         * @param ResourceLoaderContext $context
         * @return array
         */
-       private function getConfigSettings( $context ) {
+       private function getConfigSettings( ResourceLoaderContext $context ) {
                $conf = $this->getConfig();
 
                /**
@@ -310,7 +310,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        }
 
                        $skipFunction = $module->getSkipFunction();
-                       if ( $skipFunction !== null && !ResourceLoader::inDebugMode() ) {
+                       if ( $skipFunction !== null && !$context->getDebug() ) {
                                $skipFunction = ResourceLoader::filter( 'minify-js', $skipFunction );
                        }
 
@@ -326,7 +326,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                self::compileUnresolvedDependencies( $registryData );
 
                // Register sources
-               $out .= ResourceLoader::makeLoaderSourcesScript( $resourceLoader->getSources() );
+               $out .= ResourceLoader::makeLoaderSourcesScript( $context, $resourceLoader->getSources() );
 
                // Figure out the different call signatures for mw.loader.register
                $registrations = [];
@@ -344,10 +344,10 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                }
 
                // Register modules
-               $out .= "\n" . ResourceLoader::makeLoaderRegisterScript( $registrations );
+               $out .= "\n" . ResourceLoader::makeLoaderRegisterScript( $context, $registrations );
 
                if ( $states ) {
-                       $out .= "\n" . ResourceLoader::makeLoaderStateScript( $states );
+                       $out .= "\n" . ResourceLoader::makeLoaderStateScript( $context, $states );
                }
 
                return $out;
@@ -426,23 +426,23 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
 
                // Perform replacements for mediawiki.js
                $mwLoaderPairs = [
-                       '$VARS.reqBase' => ResourceLoader::encodeJsonForScript( $context->getReqBase() ),
-                       '$VARS.baseModules' => ResourceLoader::encodeJsonForScript( $this->getBaseModules() ),
-                       '$VARS.maxQueryLength' => ResourceLoader::encodeJsonForScript(
+                       '$VARS.reqBase' => $context->encodeJson( $context->getReqBase() ),
+                       '$VARS.baseModules' => $context->encodeJson( $this->getBaseModules() ),
+                       '$VARS.maxQueryLength' => $context->encodeJson(
                                $conf->get( 'ResourceLoaderMaxQueryLength' )
                        ),
                        // The client-side module cache can be disabled by site configuration.
                        // It is also always disabled in debug mode.
-                       '$VARS.storeEnabled' => ResourceLoader::encodeJsonForScript(
+                       '$VARS.storeEnabled' => $context->encodeJson(
                                $conf->get( 'ResourceLoaderStorageEnabled' ) && !$context->getDebug()
                        ),
-                       '$VARS.wgLegacyJavaScriptGlobals' => ResourceLoader::encodeJsonForScript(
+                       '$VARS.wgLegacyJavaScriptGlobals' => $context->encodeJson(
                                $conf->get( 'LegacyJavaScriptGlobals' )
                        ),
-                       '$VARS.storeKey' => ResourceLoader::encodeJsonForScript( $this->getStoreKey() ),
-                       '$VARS.storeVary' => ResourceLoader::encodeJsonForScript( $this->getStoreVary( $context ) ),
-                       '$VARS.groupUser' => ResourceLoader::encodeJsonForScript( $this->getGroupId( 'user' ) ),
-                       '$VARS.groupPrivate' => ResourceLoader::encodeJsonForScript( $this->getGroupId( 'private' ) ),
+                       '$VARS.storeKey' => $context->encodeJson( $this->getStoreKey() ),
+                       '$VARS.storeVary' => $context->encodeJson( $this->getStoreVary( $context ) ),
+                       '$VARS.groupUser' => $context->encodeJson( $this->getGroupId( 'user' ) ),
+                       '$VARS.groupPrivate' => $context->encodeJson( $this->getGroupId( 'private' ) ),
                ];
                $profilerStubs = [
                        '$CODE.profileExecuteStart();' => 'mw.loader.profiler.onExecuteStart( module );',
@@ -461,7 +461,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
 
                // Perform string replacements for startup.js
                $pairs = [
-                       '$VARS.configuration' => ResourceLoader::encodeJsonForScript(
+                       '$VARS.configuration' => $context->encodeJson(
                                $this->getConfigSettings( $context )
                        ),
                        // Raw JavaScript code (not JSON)
index 9610cce..75a2d7a 100644 (file)
@@ -42,7 +42,7 @@ class ResourceLoaderUserDefaultsModule extends ResourceLoaderModule {
         */
        public function getScript( ResourceLoaderContext $context ) {
                return 'mw.user.options.set('
-                       . ResourceLoader::encodeJsonForScript( User::getDefaultOptions() )
+                       . $context->encodeJson( User::getDefaultOptions() )
                        . ');';
        }
 }
index 866d98b..b89324c 100644 (file)
@@ -55,7 +55,7 @@ class ResourceLoaderUserOptionsModule extends ResourceLoaderModule {
                // Use FILTER_NOMIN annotation to prevent needless minification and caching (T84960).
                return ResourceLoader::FILTER_NOMIN
                        . 'mw.user.options.set('
-                       . ResourceLoader::encodeJsonForScript(
+                       . $context->encodeJson(
                                $context->getUserObj()->getOptions( User::GETOPTIONS_EXCLUDE_DEFAULTS )
                        )
                        . ');';
index 45edd6e..21944ee 100644 (file)
@@ -55,7 +55,7 @@ class ResourceLoaderUserTokensModule extends ResourceLoaderModule {
                // Use FILTER_NOMIN annotation to prevent needless minification and caching (T84960).
                return ResourceLoader::FILTER_NOMIN
                        . 'mw.user.tokens.set('
-                       . ResourceLoader::encodeJsonForScript( $this->contextUserTokens( $context ) )
+                       . $context->encodeJson( $this->contextUserTokens( $context ) )
                        . ');';
        }
 
index 3e2a9a2..aa7a6d6 100644 (file)
@@ -176,13 +176,13 @@ class MediaWikiSite extends Site {
         *
         * @param string|bool $pageName Page name or false (default: false)
         *
-        * @return string|bool|null
+        * @return string|null
         */
        public function getPageUrl( $pageName = false ) {
                $url = $this->getLinkPath();
 
-               if ( $url === false ) {
-                       return false;
+               if ( $url === null ) {
+                       return null;
                }
 
                if ( $pageName !== false ) {
index 10711a6..401f6e4 100644 (file)
@@ -354,7 +354,7 @@ class Site implements Serializable {
 
        /**
         * Returns the full URL for the given page on the site.
-        * Or false if the needed information is not known.
+        * Or null if the needed information is not known.
         *
         * This generated URL is usually based upon the path returned by getLinkPath(),
         * but this is not a requirement.
@@ -365,13 +365,13 @@ class Site implements Serializable {
         *
         * @param bool|string $pageName
         *
-        * @return string|bool|null
+        * @return string|null
         */
        public function getPageUrl( $pageName = false ) {
                $url = $this->getLinkPath();
 
-               if ( $url === false ) {
-                       return false;
+               if ( $url === null ) {
+                       return null;
                }
 
                if ( $pageName !== false ) {
index b7eb3c0..6ed5e12 100644 (file)
@@ -118,6 +118,30 @@ abstract class QueryPage extends SpecialPage {
                return $qp;
        }
 
+       /**
+        * Get a list of query pages disabled and with it's run mode
+        * @param Config $config
+        * @return string[]
+        */
+       public static function getDisabledQueryPages( Config $config ) {
+               $disableQueryPageUpdate = $config->get( 'DisableQueryPageUpdate' );
+
+               if ( !is_array( $disableQueryPageUpdate ) ) {
+                       return [];
+               }
+
+               $pages = [];
+               foreach ( $disableQueryPageUpdate as $name => $runMode ) {
+                       if ( is_int( $name ) ) {
+                               // The run mode may be omitted
+                               $pages[$runMode] = 'disabled';
+                       } else {
+                               $pages[$name] = $runMode;
+                       }
+               }
+               return $pages;
+       }
+
        /**
         * A mutator for $this->listoutput;
         *
@@ -632,13 +656,21 @@ abstract class QueryPage extends SpecialPage {
 
                                # If updates on this page have been disabled, let the user know
                                # that the data set won't be refreshed for now
-                               if ( is_array( $this->getConfig()->get( 'DisableQueryPageUpdate' ) )
-                                       && in_array( $this->getName(), $this->getConfig()->get( 'DisableQueryPageUpdate' ) )
-                               ) {
-                                       $out->wrapWikiMsg(
-                                               "<div class=\"mw-querypage-no-updates\">\n$1\n</div>",
-                                               'querypage-no-updates'
-                                       );
+                               $disabledQueryPages = self::getDisabledQueryPages( $this->getConfig() );
+                               if ( isset( $disabledQueryPages[$this->getName()] ) ) {
+                                       $runMode = $disabledQueryPages[$this->getName()];
+                                       if ( $runMode === 'disabled' ) {
+                                               $out->wrapWikiMsg(
+                                                       "<div class=\"mw-querypage-no-updates\">\n$1\n</div>",
+                                                       'querypage-no-updates'
+                                               );
+                                       } else {
+                                               // Messages used here: querypage-updates-periodical
+                                               $out->wrapWikiMsg(
+                                                       "<div class=\"mw-querypage-updates-" . $runMode . "\">\n$1\n</div>",
+                                                       'querypage-updates-' . $runMode
+                                               );
+                                       }
                                }
                        }
                }
index e8b85fa..8f92cd5 100644 (file)
@@ -23,7 +23,6 @@
 
 use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\MediaWikiServices;
-use MediaWiki\Widget\DateInputWidget;
 
 /**
  * Special:Contributions, show user contributions in a paged list
@@ -43,11 +42,16 @@ class SpecialContributions extends IncludableSpecialPage {
                $out = $this->getOutput();
                // Modules required for viewing the list of contributions (also when included on other pages)
                $out->addModuleStyles( [
+                       'jquery.makeCollapsible.styles',
                        'mediawiki.interface.helpers.styles',
                        'mediawiki.special',
                        'mediawiki.special.changeslist',
                ] );
-               $out->addModules( 'mediawiki.special.recentchanges' );
+               $out->addModules( [
+                       'mediawiki.special.recentchanges',
+                       // Certain skins e.g. Minerva might have disabled this module.
+                       'mediawiki.page.ready'
+               ] );
                $this->addHelpLink( 'Help:User contributions' );
 
                $this->opts = [];
@@ -59,7 +63,7 @@ class SpecialContributions extends IncludableSpecialPage {
 
                if ( !strlen( $target ) ) {
                        if ( !$this->including() ) {
-                               $out->addHTML( $this->getForm() );
+                               $out->addHTML( $this->getForm( $this->opts ) );
                        }
 
                        return;
@@ -77,7 +81,7 @@ class SpecialContributions extends IncludableSpecialPage {
                if ( ExternalUserNames::isExternal( $target ) ) {
                        $userObj = User::newFromName( $target, false );
                        if ( !$userObj ) {
-                               $out->addHTML( $this->getForm() );
+                               $out->addHTML( $this->getForm( $this->opts ) );
                                return;
                        }
 
@@ -89,12 +93,12 @@ class SpecialContributions extends IncludableSpecialPage {
                } else {
                        $nt = Title::makeTitleSafe( NS_USER, $target );
                        if ( !$nt ) {
-                               $out->addHTML( $this->getForm() );
+                               $out->addHTML( $this->getForm( $this->opts ) );
                                return;
                        }
                        $userObj = User::newFromName( $nt->getText(), false );
                        if ( !$userObj ) {
-                               $out->addHTML( $this->getForm() );
+                               $out->addHTML( $this->getForm( $this->opts ) );
                                return;
                        }
                        $id = $userObj->getId();
@@ -114,14 +118,23 @@ class SpecialContributions extends IncludableSpecialPage {
                }
 
                $ns = $request->getVal( 'namespace', null );
-               if ( $ns !== null && $ns !== '' ) {
+               if ( $ns !== null && $ns !== '' && $ns !== 'all' ) {
                        $this->opts['namespace'] = intval( $ns );
                } else {
                        $this->opts['namespace'] = '';
                }
 
+               // Backwards compatibility: Before using OOUI form the old HTML form had
+               // fields for nsInvert and associated. These have now been replaced with the
+               // wpFilters query string parameters. These are retained to keep old URIs working.
                $this->opts['associated'] = $request->getBool( 'associated' );
                $this->opts['nsInvert'] = (bool)$request->getVal( 'nsInvert' );
+               $nsFilters = $request->getArray( 'wpfilters', null );
+               if ( $nsFilters !== null ) {
+                       $this->opts['associated'] = in_array( 'associated', $nsFilters );
+                       $this->opts['nsInvert'] = in_array( 'nsInvert', $nsFilters );
+               }
+
                $this->opts['tagfilter'] = (string)$request->getVal( 'tagfilter' );
 
                // Allows reverts to have the bot flag in recent changes. It is just here to
@@ -149,7 +162,7 @@ class SpecialContributions extends IncludableSpecialPage {
                                "<div class=\"mw-negative-namespace-not-supported error\">\n\$1\n</div>",
                                [ 'negative-namespace-not-supported' ]
                        );
-                       $out->addHTML( $this->getForm() );
+                       $out->addHTML( $this->getForm( $this->opts ) );
                        return;
                }
 
@@ -201,9 +214,6 @@ class SpecialContributions extends IncludableSpecialPage {
                $this->addFeedLinks( $feedParams );
 
                if ( Hooks::run( 'SpecialContributionsBeforeMainOutput', [ $id, $userObj, $this ] ) ) {
-                       if ( !$this->including() ) {
-                               $out->addHTML( $this->getForm() );
-                       }
                        $pager = new ContribsPager( $this->getContext(), [
                                'target' => $target,
                                'namespace' => $this->opts['namespace'],
@@ -217,6 +227,9 @@ class SpecialContributions extends IncludableSpecialPage {
                                'nsInvert' => $this->opts['nsInvert'],
                                'associated' => $this->opts['associated'],
                        ], $this->getLinkRenderer() );
+                       if ( !$this->including() ) {
+                               $out->addHTML( $this->getForm( $this->opts ) );
+                       }
 
                        if ( IP::isValidRange( $target ) && !$pager->isQueryableRange( $target ) ) {
                                // Valid range, but outside CIDR limit.
@@ -458,52 +471,12 @@ class SpecialContributions extends IncludableSpecialPage {
 
        /**
         * Generates the namespace selector form with hidden attributes.
+        * @param array $pagerOptions with keys contribs, user, deletedOnly, limit, target, topOnly,
+        *  newOnly, hideMinor, namespace, associated, nsInvert, tagfilter, year, start, end
         * @return string HTML fragment
         */
-       protected function getForm() {
+       protected function getForm( array $pagerOptions ) {
                $this->opts['title'] = $this->getPageTitle()->getPrefixedText();
-               if ( !isset( $this->opts['target'] ) ) {
-                       $this->opts['target'] = '';
-               } else {
-                       $this->opts['target'] = str_replace( '_', ' ', $this->opts['target'] );
-               }
-
-               if ( !isset( $this->opts['namespace'] ) ) {
-                       $this->opts['namespace'] = '';
-               }
-
-               if ( !isset( $this->opts['nsInvert'] ) ) {
-                       $this->opts['nsInvert'] = '';
-               }
-
-               if ( !isset( $this->opts['associated'] ) ) {
-                       $this->opts['associated'] = false;
-               }
-
-               if ( !isset( $this->opts['start'] ) ) {
-                       $this->opts['start'] = '';
-               }
-
-               if ( !isset( $this->opts['end'] ) ) {
-                       $this->opts['end'] = '';
-               }
-
-               if ( !isset( $this->opts['tagfilter'] ) ) {
-                       $this->opts['tagfilter'] = '';
-               }
-
-               if ( !isset( $this->opts['topOnly'] ) ) {
-                       $this->opts['topOnly'] = false;
-               }
-
-               if ( !isset( $this->opts['newOnly'] ) ) {
-                       $this->opts['newOnly'] = false;
-               }
-
-               if ( !isset( $this->opts['hideMinor'] ) ) {
-                       $this->opts['hideMinor'] = false;
-               }
-
                // Modules required only for the form
                $this->getOutput()->addModules( [
                        'mediawiki.userSuggest',
@@ -511,15 +484,7 @@ class SpecialContributions extends IncludableSpecialPage {
                ] );
                $this->getOutput()->addModuleStyles( 'mediawiki.widgets.DateInputWidget.styles' );
                $this->getOutput()->enableOOUI();
-
-               $form = Html::openElement(
-                       'form',
-                       [
-                               'method' => 'get',
-                               'action' => wfScript(),
-                               'class' => 'mw-contributions-form'
-                       ]
-               );
+               $fields = [];
 
                # Add hidden params for tracking except for parameters in $skipParameters
                $skipParameters = [
@@ -527,7 +492,6 @@ class SpecialContributions extends IncludableSpecialPage {
                        'nsInvert',
                        'deletedOnly',
                        'target',
-                       'contribs',
                        'year',
                        'month',
                        'start',
@@ -543,207 +507,171 @@ class SpecialContributions extends IncludableSpecialPage {
                        if ( in_array( $name, $skipParameters ) ) {
                                continue;
                        }
-                       $form .= "\t" . Html::hidden( $name, $value ) . "\n";
-               }
-
-               $tagFilter = ChangeTags::buildTagFilterSelector(
-                       $this->opts['tagfilter'], false, $this->getContext() );
-
-               if ( $tagFilter ) {
-                       $filterSelection = Html::rawElement(
-                               'div',
-                               [],
-                               implode( "\u{00A0}", $tagFilter )
-                       );
-               } else {
-                       $filterSelection = Html::rawElement( 'div', [], '' );
-               }
-
-               $labelUsername = Xml::label(
-                       $this->msg( 'sp-contributions-username' )->text(),
-                       'mw-target-user-or-ip'
-               );
-               $input = Html::input(
-                       'target',
-                       $this->opts['target'],
-                       'text',
-                       [
-                               'id' => 'mw-target-user-or-ip',
-                               'size' => '40',
-                               'class' => [
-                                       'mw-input',
-                                       'mw-ui-input-inline',
-                                       'mw-autocomplete-user', // used by mediawiki.userSuggest
-                               ],
-                       ] + (
-                               // Only autofocus if target hasn't been specified
-                               $this->opts['target'] ? [] : [ 'autofocus' => true ]
-                       )
-               );
-
-               $targetSelection = Html::rawElement(
-                       'div',
-                       [],
-                       $labelUsername . ' ' . $input . ' '
-               );
 
-               $hidden = $this->opts['namespace'] === '' ? ' mw-input-hidden' : '';
-               $namespaceSelection = Xml::tags(
-                       'div',
-                       [],
-                       Xml::label(
-                               $this->msg( 'namespace' )->text(),
-                               'namespace'
-                       ) . "\u{00A0}" .
-                       Html::namespaceSelector(
-                               [ 'selected' => $this->opts['namespace'], 'all' => '', 'in-user-lang' => true ],
-                               [
-                                       'name' => 'namespace',
-                                       'id' => 'namespace',
-                                       'class' => 'namespaceselector',
-                               ]
-                       ) . "\u{00A0}" .
-                               Html::rawElement(
-                                       'span',
-                                       [ 'class' => 'mw-input-with-label' . $hidden ],
-                                       Xml::checkLabel(
-                                               $this->msg( 'invert' )->text(),
-                                               'nsInvert',
-                                               'nsinvert',
-                                               $this->opts['nsInvert'],
-                                               [
-                                                       'title' => $this->msg( 'tooltip-invert' )->text(),
-                                                       'class' => 'mw-input'
-                                               ]
-                                       ) . "\u{00A0}"
-                               ) .
-                               Html::rawElement( 'span', [ 'class' => 'mw-input-with-label' . $hidden ],
-                                       Xml::checkLabel(
-                                               $this->msg( 'namespace_association' )->text(),
-                                               'associated',
-                                               'nsassociated',
-                                               $this->opts['associated'],
-                                               [
-                                                       'title' => $this->msg( 'tooltip-namespace_association' )->text(),
-                                                       'class' => 'mw-input'
-                                               ]
-                                       ) . "\u{00A0}"
-                               )
-               );
+                       $fields[$name] = [
+                               'name' => $name,
+                               'type' => 'hidden',
+                               'default' => $value,
+                       ];
+               }
+
+               $target = $this->opts['target'] ?? null;
+               $fields['target'] = [
+                       'type' => 'text',
+                       'cssclass' => 'mw-autocomplete-user mw-ui-input-inline mw-input',
+                       'default' => $target ?
+                               str_replace( '_', ' ', $target ) : '' ,
+                       'label' => $this->msg( 'sp-contributions-username' )->text(),
+                       'name' => 'target',
+                       'id' => 'mw-target-user-or-ip',
+                       'size' => 40,
+                       'autofocus' => !$target,
+                       'section' => 'contribs-top',
+               ];
 
-               $filters = [];
+               $ns = $this->opts['namespace'] ?? null;
+               $fields['namespace'] = [
+                       'type' => 'namespaceselect',
+                       'label' => $this->msg( 'namespace' )->text(),
+                       'name' => 'namespace',
+                       'cssclass' => 'namespaceselector',
+                       'default' => $ns,
+                       'id' => 'namespace',
+                       'section' => 'contribs-top',
+               ];
+               $request = $this->getRequest();
+               $nsFilters = $request->getArray( 'wpfilters' );
+               $fields['nsFilters'] = [
+                       'class' => 'HTMLMultiSelectField',
+                       'label' => '',
+                       'name' => 'wpfilters',
+                       'flatlist' => true,
+                       // Only shown when namespaces are selected.
+                       'cssclass' => $ns === '' ?
+                               'contribs-ns-filters mw-input-with-label mw-input-hidden' :
+                               'contribs-ns-filters mw-input-with-label',
+                       // `contribs-ns-filters` class allows these fields to be toggled on/off by JavaScript.
+                       // See resources/src/mediawiki.special.recentchanges.js
+                       'infusable' => true,
+                       'options' => [
+                               $this->msg( 'invert' )->text() => 'nsInvert',
+                               $this->msg( 'namespace_association' )->text() => 'associated',
+                       ],
+                       'default' => $nsFilters,
+                       'section' => 'contribs-top',
+               ];
+               $fields['tagfilter'] = [
+                       'type' => 'tagfilter',
+                       'cssclass' => 'mw-tagfilter-input',
+                       'id' => 'tagfilter',
+                       'label-message' => [ 'tag-filter', 'parse' ],
+                       'name' => 'tagfilter',
+                       'size' => 20,
+                       'section' => 'contribs-top',
+               ];
 
                if ( MediaWikiServices::getInstance()
-                               ->getPermissionManager()
-                               ->userHasRight( $this->getUser(), 'deletedhistory' )
+                       ->getPermissionManager()
+                       ->userHasRight( $this->getUser(), 'deletedhistory' )
                ) {
-                       $filters[] = Html::rawElement(
-                               'span',
-                               [ 'class' => 'mw-input-with-label' ],
-                               Xml::checkLabel(
-                                       $this->msg( 'history-show-deleted' )->text(),
-                                       'deletedOnly',
-                                       'mw-show-deleted-only',
-                                       $this->opts['deletedOnly'],
-                                       [ 'class' => 'mw-input' ]
-                               )
-                       );
-               }
-
-               $filters[] = Html::rawElement(
-                       'span',
-                       [ 'class' => 'mw-input-with-label' ],
-                       Xml::checkLabel(
-                               $this->msg( 'sp-contributions-toponly' )->text(),
-                               'topOnly',
-                               'mw-show-top-only',
-                               $this->opts['topOnly'],
-                               [ 'class' => 'mw-input' ]
-                       )
-               );
-               $filters[] = Html::rawElement(
-                       'span',
-                       [ 'class' => 'mw-input-with-label' ],
-                       Xml::checkLabel(
-                               $this->msg( 'sp-contributions-newonly' )->text(),
-                               'newOnly',
-                               'mw-show-new-only',
-                               $this->opts['newOnly'],
-                               [ 'class' => 'mw-input' ]
-                       )
-               );
-               $filters[] = Html::rawElement(
-                       'span',
-                       [ 'class' => 'mw-input-with-label' ],
-                       Xml::checkLabel(
-                               $this->msg( 'sp-contributions-hideminor' )->text(),
-                               'hideMinor',
-                               'mw-hide-minor-edits',
-                               $this->opts['hideMinor'],
-                               [ 'class' => 'mw-input' ]
-                       )
-               );
+                       $fields['deletedOnly'] = [
+                               'type' => 'check',
+                               'id' => 'mw-show-deleted-only',
+                               'label' => $this->msg( 'history-show-deleted' )->text(),
+                               'name' => 'deletedOnly',
+                               'section' => 'contribs-top',
+                       ];
+               }
+
+               $fields['topOnly'] = [
+                       'type' => 'check',
+                       'id' => 'mw-show-top-only',
+                       'label' => $this->msg( 'sp-contributions-toponly' )->text(),
+                       'name' => 'topOnly',
+                       'section' => 'contribs-top',
+               ];
+               $fields['newOnly'] = [
+                       'type' => 'check',
+                       'id' => 'mw-show-new-only',
+                       'label' => $this->msg( 'sp-contributions-newonly' )->text(),
+                       'name' => 'newOnly',
+                       'section' => 'contribs-top',
+               ];
+               $fields['hideMinor'] = [
+                       'type' => 'check',
+                       'cssclass' => 'mw-hide-minor-edits',
+                       'id' => 'mw-show-new-only',
+                       'label' => $this->msg( 'sp-contributions-hideminor' )->text(),
+                       'name' => 'hideMinor',
+                       'section' => 'contribs-top',
+               ];
 
+               // Allow additions at this point to the filters.
+               $rawFilters = [];
                Hooks::run(
                        'SpecialContributions::getForm::filters',
-                       [ $this, &$filters ]
-               );
-
-               $extraOptions = Html::rawElement(
-                       'div',
-                       [],
-                       implode( '', $filters )
+                       [ $this, &$rawFilters ]
                );
+               foreach ( $rawFilters as $filter ) {
+                       // Backwards compatibility support for previous hook function signature.
+                       if ( is_string( $filter ) ) {
+                               $fields[] = [
+                                       'type' => 'info',
+                                       'default' => $filter,
+                                       'raw' => true,
+                                       'section' => 'contribs-top',
+                               ];
+                               wfDeprecated(
+                                       __METHOD__ .
+                                       ' returning string[]',
+                                       '1.33'
+                               );
+                       } else {
+                               // Preferred append method.
+                               $fields[] = $filter;
+                       }
+               }
 
-               $dateRangeSelection = Html::rawElement(
-                       'div',
-                       [],
-                       Xml::label( $this->msg( 'date-range-from' )->text(), 'mw-date-start' ) . ' ' .
-                       new DateInputWidget( [
-                               'infusable' => true,
-                               'id' => 'mw-date-start',
-                               'name' => 'start',
-                               'value' => $this->opts['start'],
-                               'longDisplayFormat' => true,
-                       ] ) . '<br>' .
-                       Xml::label( $this->msg( 'date-range-to' )->text(), 'mw-date-end' ) . ' ' .
-                       new DateInputWidget( [
-                               'infusable' => true,
-                               'id' => 'mw-date-end',
-                               'name' => 'end',
-                               'value' => $this->opts['end'],
-                               'longDisplayFormat' => true,
-                       ] )
-               );
+               $fields['start'] = [
+                       'type' => 'date',
+                       'default' => '',
+                       'id' => 'mw-date-start',
+                       'label' => $this->msg( 'date-range-from' )->text(),
+                       'name' => 'start',
+                       'section' => 'contribs-date',
+               ];
+               $fields['end'] = [
+                       'type' => 'date',
+                       'default' => '',
+                       'id' => 'mw-date-end',
+                       'label' => $this->msg( 'date-range-to' )->text(),
+                       'name' => 'end',
+                       'section' => 'contribs-date',
+               ];
 
-               $submit = Xml::tags( 'div', [],
-                       Html::submitButton(
-                               $this->msg( 'sp-contributions-submit' )->text(),
-                               [ 'class' => 'mw-submit' ], [ 'mw-ui-progressive' ]
+               $htmlForm = HTMLForm::factory( 'ooui', $fields, $this->getContext() );
+               $htmlForm
+                       ->setMethod( 'get' )
+                       // When offset is defined, the user is paging through results
+                       // so we hide the form by default to allow users to focus on browsing
+                       // rather than defining search parameters
+                       ->setCollapsibleOptions(
+                               ( $pagerOptions['target'] ?? null ) ||
+                               ( $pagerOptions['start'] ?? null ) ||
+                               ( $pagerOptions['end'] ?? null )
                        )
-               );
-
-               $form .= Xml::fieldset(
-                       $this->msg( 'sp-contributions-search' )->text(),
-                       $targetSelection .
-                       $namespaceSelection .
-                       $filterSelection .
-                       $extraOptions .
-                       $dateRangeSelection .
-                       $submit,
-                       [ 'class' => 'mw-contributions-table' ]
-               );
+                       ->setAction( wfScript() )
+                       ->setSubmitText( $this->msg( 'sp-contributions-submit' )->text() )
+                       ->setWrapperLegend( $this->msg( 'sp-contributions-search' )->text() );
 
                $explain = $this->msg( 'sp-contributions-explain' );
                if ( !$explain->isBlank() ) {
-                       $form .= Html::rawElement(
-                               'p', [ 'id' => 'mw-sp-contributions-explain' ], $explain->parse()
-                       );
+                       $htmlForm->addFooterText( "<p id='mw-sp-contributions-explain'>{$explain->parse()}</p>" );
                }
 
-               $form .= Xml::closeElement( 'form' );
+               $htmlForm->loadData();
 
-               return $form;
+               return $htmlForm->getHTML( false );
        }
 
        /**
index be4a1bd..9a78c5d 100644 (file)
@@ -64,11 +64,11 @@ class NewFilesPager extends RangeChronologicalPager {
        function getQueryInfo() {
                $opts = $this->opts;
                $conds = [];
-               $imgQuery = LocalFile::getQueryInfo();
-               $tables = $imgQuery['tables'];
-               $fields = [ 'img_name', 'img_timestamp' ] + $imgQuery['fields'];
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'img_user' );
+               $tables = [ 'image' ] + $actorQuery['tables'];
+               $fields = [ 'img_name', 'img_timestamp' ] + $actorQuery['fields'];
                $options = [];
-               $jconds = $imgQuery['joins'];
+               $jconds = $actorQuery['joins'];
 
                $user = $opts->getValue( 'user' );
                if ( $user !== '' ) {
@@ -89,7 +89,7 @@ class NewFilesPager extends RangeChronologicalPager {
                                        'LEFT JOIN',
                                        [
                                                'ug_group' => $groupsWithBotPermission,
-                                               'ug_user = ' . $imgQuery['fields']['img_user'],
+                                               'ug_user = ' . $actorQuery['fields']['img_user'],
                                                'ug_expiry IS NULL OR ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() )
                                        ]
                                ];
@@ -107,7 +107,7 @@ class NewFilesPager extends RangeChronologicalPager {
                                'JOIN',
                                [
                                        'rc_title = img_name',
-                                       'rc_actor = ' . $imgQuery['fields']['img_actor'],
+                                       'rc_actor = ' . $actorQuery['fields']['img_actor'],
                                        'rc_timestamp = img_timestamp'
                                ]
                        ];
index 457680d..d4ab79e 100644 (file)
        "tog-numberheadings": "isinin nomor murda anggen cara otomatis",
        "tog-editondblclick": "sunting lembar nganggen klik kaping pindo",
        "tog-editsectiononrightclick": "sayagayang panyuntingan kepahan anggen ngeklik tengen ring kepahan judul",
-       "tog-watchcreations": "imbuhin lembar sane karyanin titiang ring kepahan pangiwasan",
+       "tog-watchcreations": "Tambeh kaca sané kardi titiang miwah berkas sané unggah titiang ring pangawasan titiang",
        "tog-watchdefault": "imbuhin lembar panyuntingansane sunting titiang ring kepahan pangiwasan",
-       "tog-watchmoves": "imbuhang lembar sane kakisidang titiang ring kepahan pangiwasan",
+       "tog-watchmoves": "Tambeh kaca miwan berkas sané gingsirang titiang ring pangawasan titiang",
        "tog-watchdeletion": "imbuhin lembar sane kaapus ring kepahan pangiwasan",
-       "tog-watchuploads": "Tambehin berkas anyar sané unggah tiang ka pupulan pantaun\nTambahkan berkas baru yang saya unggah ke daftar pantauan",
+       "tog-watchuploads": "Tambeh berkas anyar sané unggah titiang ring pangawasan titiang",
        "tog-watchrollback": "Tambehin kaca sané sampun uliang tiang ka tengah pupulan pantauan tiangé",
        "tog-minordefault": "pingetin samian suntingan dados suntingan alit sane ajeg",
        "tog-previewontop": "tampilang pratayang sadurung kotak sunting lan nenten sadurungnyane",
        "yourpasswordagain": "jumunin kruna sandi",
        "createacct-yourpasswordagain": "Pastiang kruna sandi",
        "createacct-yourpasswordagain-ph": "Dagingin malih kruna sandi",
-       "userlogin-remembermypassword": "Banggayang mangda tetep ngranjing",
+       "userlogin-remembermypassword": "Banggayang mangda setata manjing log",
        "cannotlogin-title": "Nénten prasida manjing log",
        "cannotlogin-text": "Nénten prasida manjing log.",
        "cannotloginnow-title": "Mangkin nénten prasida manjing log",
        "history-feed-title": "Babad uahan",
        "history-feed-description": "Babad uahan kaca puniki ring wiki",
        "history-feed-item-nocomment": "$1 ring $2",
-       "rev-delundel": "gentos pangatonan",
+       "rev-delundel": "uah pakantenan",
        "rev-showdeleted": "sinahang",
        "revdelete-hide-comment": "Uah ringkesan",
        "revdel-restore": "gentos pangatonan",
        "filehist-revert": "wangdéang",
        "filehist-current": "mangkin",
        "filehist-datetime": "Tanggal/Galah",
-       "filehist-thumb": "Miniatur",
-       "filehist-thumbtext": "miniatur anggen versi ring $1",
+       "filehist-thumb": "Gambar alit",
+       "filehist-thumbtext": "Gambar alit indik vérsi $1",
        "filehist-nothumb": "Tusing ade miniatur",
        "filehist-user": "Sang anganggé",
        "filehist-dimensions": "Diménsi",
        "linkshere": "Kaca-kaca ring sor puniki kasambung ring <strong>$2</strong>:",
        "nolinkshere": "Nénten wénten kaca sané madué pranala ring <strong>$2</strong>.",
        "isredirect": "Kaca gingsiran",
-       "istemplate": "sareng kasurat",
+       "istemplate": "transklusi",
        "isimage": "pranala berkas",
        "whatlinkshere-prev": "{{PLURAL:$1|sadurungnyané|$1 sadurungnyané}}",
        "whatlinkshere-next": "{{PLURAL:$1|salanturnyané|$1 salanturnyané}}",
        "blocklogpage": "Log pangempet",
        "blocklogentry": "mlokir [[$1]] anggen pangwates galah $2$3",
        "reblock-logentry": "nguwah blokiran aturan antuk [[$1]] sareng galah kadaluwarsa $2 $3",
-       "block-log-flags-nocreate": "ngawe akun kaicalang",
+       "block-log-flags-nocreate": "pangardi akun kapademang",
        "proxyblocker": "Sané ngablokir proxy",
        "movelogpage": "Log gingsiran",
        "revertmove": "wangdéang",
        "tooltip-t-recentchangeslinked": "Uahan sané mangkin saking kaca-kaca sané kasambung ring kaca puniki",
        "tooltip-feed-atom": "\"atom feed\" anggen lembar puniki",
        "tooltip-t-contributions": "Bacakan pituut antuk {{GENDER:$1|sang anganggé puniki}}",
-       "tooltip-t-emailuser": "Ngirim surel majeng ring {{GENDER:$1|penganggo puniki}}",
+       "tooltip-t-emailuser": "Kirim séwala éléktronik ring {{GENDER:$1|sang anganggé puniki}}",
        "tooltip-t-upload": "Unggahang depukan",
        "tooltip-t-specialpages": "Bacakan makasami kaca kusus",
        "tooltip-t-print": "Vérsi cétak kaca puniki",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|kaca}}",
        "file-info-size": "$1x$2 piksel, agengnyané depukan: $3, soroh MIME:$4",
        "file-info-size-pages": "$1 × $2 piksel, agengnyané berkas: $3, soroh MIME: $4, $5 {{PLURAL:$5|kaca}}",
-       "file-nohires": "tan kasayagaang ukuran sane lewih ageng",
+       "file-nohires": "Nénten kasayagaang résolusi sané lewih ageng.",
        "svg-long-desc": "Berkas SVG, jimbarnyané $1 × $2 piksel, agengnyané berkas: $3",
        "show-big-image": "Depukan sujati",
        "show-big-image-preview": "Agengnyané pratuduh puniki: $1.",
        "compare-page2": "Kaca 2",
        "logentry-delete-delete": "$1 {{GENDER:$2|ngusapin}} kaca $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|ngwaliang}} kaca $3 ($4)",
-       "logentry-delete-revision": "$1 {{GENDER:$2|mauah}} kaca utama {{PLURAL:$5|$5  pamecikan}} ring kaca $3: $4",
+       "logentry-delete-revision": "$1 {{GENDER:$2|nguahin}} pakantenan saking {{PLURAL:$5|$5  révisi}} ring kaca $3: $4",
        "revdelete-content-hid": "daging kaengkebang",
        "logentry-move-move": "$1 {{GENDER:$2|ngingsirang}} kaca $3 ka $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|ngingsirang}} kaca $3 ka $4 tur nenten ngawe pengalihan",
index a209419..8da3832 100644 (file)
        "listfiles-userdoesnotexist": "Няма регистрирана потребителска сметка за „$1“.",
        "imgfile": "файл",
        "listfiles": "Списък на файловете",
+       "listfiles_subpage": "Качвания от $1",
        "listfiles_thumb": "Миникартинка",
        "listfiles_date": "Дата",
        "listfiles_name": "Име на файла",
        "blocklist-userblocks": "Скриване блокирането на потребителски сметки",
        "blocklist-tempblocks": "Скриване на временни блокирания",
        "blocklist-addressblocks": "Скриване на отделни блокирания на IP адреси",
+       "blocklist-type": "Вид:",
        "blocklist-type-opt-all": "Всички",
        "blocklist-type-opt-sitewide": "За всички уикита",
        "blocklist-type-opt-partial": "Частично",
        "version-ext-colheader-description": "Описание",
        "version-ext-colheader-credits": "Автори",
        "version-license-title": "Лиценз за $1",
+       "version-credits-title": "Списък на авторите на $1",
        "version-poweredby-credits": "Това уики работи на базата на <strong>[https://www.mediawiki.org/ MediaWiki]</strong>, copyright © 2001-$1 $2.",
        "version-poweredby-others": "други",
        "version-poweredby-translators": "преводачи в translatewiki.net",
        "feedback-bugornote": "Ако сте готови подробно да опишете технически проблем, моля [$1 докладвайте го тук].\nВ противен случай, можете да използвате лесния формуляр по-долу. Коментарът ви ще бъде добавен към страницата „[$3 $2]“, наред с вашето потребителско име.",
        "feedback-cancel": "Отказ",
        "feedback-close": "Готово",
+       "feedback-external-bug-report-button": "Изпращане на техническа задача",
        "feedback-dialog-title": "Изпращане на обратна връзка",
        "feedback-error1": "Грешка: Неразпознат резултат от API",
        "feedback-error2": "Грешка: Неуспешна редакция",
        "duration-centuries": "$1 {{PLURAL:$1|век|века}}",
        "duration-millennia": "$1 {{PLURAL:$1|хилядолетие|хилядолетия}}",
        "rotate-comment": "Изображението е завъртяно на $1 {{PLURAL:$1|градус|градуса}} по часовниковата стрелка",
+       "limitreport-cputime": "Употреба на процесорното време",
        "limitreport-cputime-value": "$1 {{PLURAL:$1|секунда|секунди}}",
+       "limitreport-walltime": "Употреба в режим на реално време",
        "limitreport-walltime-value": "$1 {{PLURAL:$1|секунда|секунди}}",
+       "limitreport-ppvisitednodes": "Брой възли посетени от препроцесора",
        "limitreport-ppvisitednodes-value": "$1/$2",
+       "limitreport-ppgeneratednodes": "Брой възли генерирани от препроцесора",
        "limitreport-ppgeneratednodes-value": "$1/$2",
        "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|байт|байта}}",
        "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|байт|байта}}",
        "special-characters-group-ipa": "IPA",
        "special-characters-group-symbols": "Символи",
        "special-characters-group-greek": "Гръцки",
+       "special-characters-group-greekextended": "Гръцки (разширен)",
        "special-characters-group-cyrillic": "Кирилица",
        "special-characters-group-arabic": "Арабски",
        "special-characters-group-arabicextended": "Разширен арабски",
index 67cdbad..ecddb15 100644 (file)
        "sp-contributions-userrights": "idareyê heqanê {{GENDER:$1|karberan}}",
        "sp-contributions-blocked-notice": "Eno karber/ena karbere emanet blokekerdeyo/blokekerdiya.\nCıkewtışo tewr peyêno ke bloke biyo, cêr seba referansi belikerdeyo:",
        "sp-contributions-blocked-notice-anon": "Eno adresê IPi bloke biyo.\nCıkewtışo tewr peyêno ke bloke biyo, cêr seba referansi belikerdeyo:",
-       "sp-contributions-search": "Dekerdena cı geyrê",
+       "sp-contributions-search": "İştırakan cı geyrê",
        "sp-contributions-username": "Adresa IPy ya zi nameyê karberi:",
        "sp-contributions-toponly": "Tenya tewr çım ra viyarnayışanê peyniyan bımocne",
        "sp-contributions-newonly": "Tenya vurnayışanê pelevıraştışi bımocne",
index 7944a37..8092bf6 100644 (file)
        "perfcached": "The following data is cached and may not be up to date. A maximum of {{PLURAL:$1|one result is|$1 results are}} available in the cache.",
        "perfcachedts": "The following data is cached, and was last updated $1. A maximum of {{PLURAL:$4|one result is|$4 results are}} available in the cache.",
        "querypage-no-updates": "Updates for this page are currently disabled.\nData here will not presently be refreshed.",
+       "querypage-updates-periodical": "Updates for this page are run periodically.",
        "viewsource": "View source",
        "viewsource-title": "View source for $1",
        "actionthrottled": "Action throttled",
index d554442..fca6f19 100644 (file)
@@ -7,7 +7,8 @@
                        "Naudefj",
                        "SPQRobin",
                        "Shirayuki",
-                       "Spacebirdy"
+                       "Spacebirdy",
+                       "Servien"
                ]
        },
        "exif-imagewidth": "Breedte",
        "exif-gpsstatus-v": "Meetinteroperabiliteit",
        "exif-gpsmeasuremode-2": "2-dimensionele meting",
        "exif-gpsmeasuremode-3": "3-dimensionele meting",
-       "exif-gpsspeed-k": "Kilometer per huur",
+       "exif-gpsspeed-k": "Kilometer per uur",
        "exif-gpsspeed-m": "Myl per huur",
        "exif-gpsspeed-n": "Knope",
        "exif-gpsdestdistance-k": "Kilometers",
index 76bd5e0..3326a75 100644 (file)
@@ -3,7 +3,8 @@
                "authors": [
                        "C.R.",
                        "E. abu Filumena",
-                       "SabineCretella"
+                       "SabineCretella",
+                       "Sannita"
                ]
        },
        "exif-imagewidth": "Larghezza",
        "exif-imagedescription": "Titulo 'e l'immaggene",
        "exif-make": "Frabbeca ca muntaje 'a camera",
        "exif-model": "Mudello d' 'a camera",
-       "exif-software": "Software ausàto",
+       "exif-software": "Software ausato",
        "exif-artist": "Autore",
        "exif-copyright": "Titolare d' 'o Copyright",
-       "exif-exifversion": "Verzione d'Exif",
+       "exif-exifversion": "Verzione 'e ll'exif",
        "exif-flashpixversion": "Verziona Flashpix suppurtata",
        "exif-colorspace": "Spazio d' 'e culore",
        "exif-componentsconfiguration": "Significato d'ogne componente",
index 73b2b7b..57fac84 100644 (file)
@@ -29,7 +29,7 @@
        "exif-imagedescription": "Afbealdingname",
        "exif-make": "Kameramark",
        "exif-model": "Kameramodel",
-       "exif-software": "Programmatuur dee bruked wördt",
+       "exif-software": "Programmatuur dee gebruked wördt",
        "exif-artist": "Autöör",
        "exif-copyright": "Autöörsrechtenholder",
        "exif-exifversion": "Exif-versy",
@@ -93,7 +93,7 @@
        "exif-gpsaltituderef": "Höygdereferensy",
        "exif-gpsaltitude": "Höygde",
        "exif-gpstimestamp": "GPS-tyd (atoomklokke)",
-       "exif-gpssatellites": "Satelliten dee bruked binnet vöär de meating",
+       "exif-gpssatellites": "Satelliten dee gebruked binnet vöär de meating",
        "exif-gpsstatus": "Untvangerståtus",
        "exif-gpsmeasuremode": "Meatmodus",
        "exif-gpsdop": "Meatprecisy",
        "exif-gpstrack": "Beweagingsrichting",
        "exif-gpsimgdirectionref": "Referensy vöär afbealdingsrichting",
        "exif-gpsimgdirection": "Afbealdingsrichting",
-       "exif-gpsmapdatum": "Geodetiske undersööksgegeavens dee bruked binnet",
+       "exif-gpsmapdatum": "Geodetiske undersööksgegeavens dee gebruked binnet",
        "exif-gpsdestlatituderef": "Referensy vöär breyddegråd tot bestemming",
        "exif-gpsdestlatitude": "Breyddegråd bestemming",
        "exif-gpsdestlongituderef": "Referensy vöär längdegråd bestemming",
        "exif-iimversion": "IIM-versy",
        "exif-iimcategory": "Kategory",
        "exif-iimsupplementalcategory": "Anvüllende kategoryen",
-       "exif-datetimeexpires": "Neet te bruken nå",
+       "exif-datetimeexpires": "Neet te gebruken nå",
        "exif-datetimereleased": "Uutbröcht up",
        "exif-originaltransmissionref": "Oorsprungelike språklokatykode",
        "exif-identifier": "ID",
        "exif-rightscertificate": "Rechtenbeheyrcertifikaat",
        "exif-copyrighted": "Autöörsrechtenståtus",
        "exif-copyrightowner": "Autöörsrechtenholder",
-       "exif-usageterms": "Bruuksbeding",
+       "exif-usageterms": "Gebruuksbeding",
        "exif-webstatement": "Binnennetse autöörsrechtenverklåring",
        "exif-originaldocumentid": "Unik ID van et originele dokument",
        "exif-licenseurl": "Webadres vöär autöörsrechtenlicensy",
        "exif-morepermissionsurl": "Alternative licensygegeavens",
        "exif-attributionurl": "Bruuk de volgende verwysing as dit wark herbruked wördt",
        "exif-preferredattributionname": "Bruuk de volgende makersvermelding as dit wark herbruked wördt",
-       "exif-pngfilecomment": "Opmarking bie PNG-bestaand",
+       "exif-pngfilecomment": "Upmarking by PNG-bestand",
        "exif-disclaimer": "Vöärbehold",
-       "exif-contentwarning": "Waorschuwing over inhoud",
-       "exif-giffilecomment": "Opmarking bie GIF-bestaand",
-       "exif-intellectualgenre": "Soort onderwarp",
-       "exif-subjectnewscode": "Onderwarpkode",
-       "exif-scenecode": "IPTC-scènekode",
-       "exif-event": "Aofebeelden gebeurtenisse",
-       "exif-organisationinimage": "Aofebeelden organisasie",
-       "exif-personinimage": "Aofebeeld persoon",
-       "exif-originalimageheight": "Heugte van de aofbeelding veur biesniejen",
-       "exif-originalimagewidth": "Breedte van de aofbeelding veur biesniejen",
-       "exif-compression-1": "Niet ekomprimeerd",
-       "exif-compression-2": "CCITT-groep 3 1-dimensionale an-epasten \"Huffman run length\"-kodering",
-       "exif-compression-3": "CCITT-groep 3 faxcodering",
-       "exif-compression-4": "CCITT-groep 4 faxcodering",
-       "exif-copyrighted-true": "Auteursrechtelik bescharmp",
-       "exif-copyrighted-false": "Auteursrechtenstaotus niet vasteleegd",
-       "exif-unknowndate": "Onbekende daotum",
+       "exif-contentwarning": "Inholdswårsküwing",
+       "exif-giffilecomment": "Upmarking by GIF-bestand",
+       "exif-intellectualgenre": "Soort underwarp",
+       "exif-subjectnewscode": "Underwarpkode",
+       "exif-scenecode": "IPTC-scenekode",
+       "exif-event": "Afbealdede geböärtenisse",
+       "exif-organisationinimage": "Afbealdede organisaty",
+       "exif-personinimage": "Afbealded persoon",
+       "exif-originalimageheight": "Höygde van de afbealding vöär et bysnyden",
+       "exif-originalimagewidth": "Breydde van de afbealding vöär et bysnyden",
+       "exif-compression-1": "Neet komprimeerd",
+       "exif-compression-2": "CCITT-grup 3 1-dimensionale anpasde \"Huffman run length\"-kodering",
+       "exif-compression-3": "CCITT-grup 3 fakskodering",
+       "exif-compression-4": "CCITT-grup 4 fakskodering",
+       "exif-copyrighted-true": "Autöörsrechtelik beskarmd",
+       "exif-copyrighted-false": "Autöörsrechtenståtus neet upgeaven",
+       "exif-photometricinterpretation-0": "Swart-wit (wit is 0)",
+       "exif-photometricinterpretation-1": "Swart-wit (swart is 0)",
+       "exif-photometricinterpretation-3": "Palette",
+       "exif-photometricinterpretation-4": "Transparansymasker",
+       "exif-photometricinterpretation-5": "Skeiden (wårskynlik CMYK)",
+       "exif-photometricinterpretation-8": "CIE L*a*b*",
+       "exif-photometricinterpretation-9": "CIE L*a*b* (ICC-kodering)",
+       "exif-photometricinterpretation-10": "CIE L*a*b* (ITU-kodering)",
+       "exif-unknowndate": "Unbekende dåtum",
        "exif-orientation-1": "Normaal",
-       "exif-orientation-2": "horizontaal espegeld",
-       "exif-orientation-3": "180° edreid",
-       "exif-orientation-4": "verticaal edreid",
-       "exif-orientation-5": "espegeld um as linksboven-rechtsonder",
-       "exif-orientation-6": "90° linksummedreid",
-       "exif-orientation-7": "90° linksummedreid",
-       "exif-orientation-8": "90° rechtsummedreid",
-       "exif-planarconfiguration-1": "Grof gegevensformaot",
-       "exif-planarconfiguration-2": "planar gegevensformaot",
-       "exif-colorspace-65535": "Niet-ekalibreerd",
-       "exif-componentsconfiguration-0": "besteet niet",
-       "exif-exposureprogram-0": "Niet umschreven",
-       "exif-exposureprogram-1": "Haandmaotig",
+       "exif-orientation-2": "horizontaal spegeld",
+       "exif-orientation-3": "180° draid",
+       "exif-orientation-4": "vertikaal draid",
+       "exif-orientation-5": "90° teagen de klokke in draid en vertikaal spegeld",
+       "exif-orientation-6": "90° teagen de klokke in draid",
+       "exif-orientation-7": "90° mid de klokke middraid en vertikaal spegeld",
+       "exif-orientation-8": "90° mid de klokke middraid",
+       "exif-planarconfiguration-1": "Grov formaat",
+       "exif-planarconfiguration-2": "Planäär formaat",
+       "exif-colorspace-65535": "Neet kalibreerd",
+       "exif-componentsconfiguration-0": "besteyt neet",
+       "exif-exposureprogram-0": "Unbekend",
+       "exif-exposureprogram-1": "Handmåtig",
        "exif-exposureprogram-2": "Normaal",
        "exif-exposureprogram-3": "Diafragmaprioriteit",
        "exif-exposureprogram-4": "Sluterprioriteit",
-       "exif-exposureprogram-5": "Kreatief (veurkeur veur grote scharptediepte)",
-       "exif-exposureprogram-6": "Aksie (veurkeur veur hoge slutersnelheid)",
-       "exif-exposureprogram-7": "Portret (detailopname mit onscharpe achtergrond)",
-       "exif-exposureprogram-8": "Laandschap (scharpe achtergrond)",
-       "exif-subjectdistance-value": "$1 m",
-       "exif-meteringmode-0": "Onbekend",
+       "exif-exposureprogram-5": "Kreativ programma (vöärköär vöär groute skarpdedeepde)",
+       "exif-exposureprogram-6": "Akty (vöärköär vöär houge slutersnelheid)",
+       "exif-exposureprogram-7": "Portret (detailupnåme mid unskarpe achtergrund)",
+       "exif-exposureprogram-8": "Landskap (skarpe achtergrund)",
+       "exif-subjectdistance-value": "$1 meter",
+       "exif-meteringmode-0": "Unbekend",
        "exif-meteringmode-1": "Gemiddeld",
-       "exif-meteringmode-2": "Gemiddeld, naodrok op midden",
+       "exif-meteringmode-2": "Middencentreerd",
        "exif-meteringmode-3": "Spot",
-       "exif-meteringmode-4": "MultiSpot",
+       "exif-meteringmode-4": "Multi-spot",
        "exif-meteringmode-5": "Multi-segment (patrone)",
-       "exif-meteringmode-6": "Deelmeting",
-       "exif-meteringmode-255": "Aanders",
-       "exif-lightsource-0": "Onbekend",
-       "exif-lightsource-1": "Daglocht",
-       "exif-lightsource-2": "Tl-locht",
-       "exif-lightsource-3": "Tungsten (lamplocht)",
+       "exif-meteringmode-6": "Deylmeating",
+       "exif-meteringmode-255": "Anders",
+       "exif-lightsource-0": "Unbekend",
+       "exif-lightsource-1": "Daglicht",
+       "exif-lightsource-2": "TL-licht",
+       "exif-lightsource-3": "Tungsten (lamplicht)",
        "exif-lightsource-4": "Flitser",
-       "exif-lightsource-9": "Mooi weer",
-       "exif-lightsource-10": "Bewolk",
-       "exif-lightsource-11": "Schaoduw",
-       "exif-lightsource-12": "Fluorescerend daglocht (D 5700 – 7100K)",
-       "exif-lightsource-13": "Witfluorescerend daglocht (N 4600 – 5400K)",
-       "exif-lightsource-14": "Koel witfluorescerend (W 3900 – 4500K)",
-       "exif-lightsource-15": "Witfluorescerend (WW 3200 – 3700K)",
-       "exif-lightsource-17": "Standardlocht A",
-       "exif-lightsource-18": "Standardlocht B",
-       "exif-lightsource-19": "Standardlocht C",
-       "exif-lightsource-24": "ISO-studiokunstlocht",
-       "exif-lightsource-255": "Aanders",
-       "exif-flash-fired-0": "Flits is niet aofegaon",
-       "exif-flash-fired-1": "Mit flitser",
-       "exif-flash-return-0": "flits stuurt gien gegevens",
-       "exif-flash-return-2": "gien weerkaotsing van de flits vastesteld",
-       "exif-flash-return-3": "weerkaotsing van de flits vastesteld",
-       "exif-flash-mode-1": "verplicht mit flitser",
-       "exif-flash-mode-2": "flitser verplicht onderdrokt",
-       "exif-flash-mode-3": "automatiese modus",
-       "exif-flash-function-1": "Gien flitserfunksie",
-       "exif-flash-redeye-1": "rooie ogen-filter",
+       "exif-lightsource-9": "Mooi weader",
+       "exif-lightsource-10": "Bewolked",
+       "exif-lightsource-11": "Skadüw",
+       "exif-lightsource-12": "Daglicht fluorescerend (D 5700 – 7100K)",
+       "exif-lightsource-13": "Dagwit fluorescerend (N 4600 – 5400K)",
+       "exif-lightsource-14": "Kool wit fluorescerend (W 3900 – 4500K)",
+       "exif-lightsource-15": "Wit fluorescerend (WW 3200 – 3700K)",
+       "exif-lightsource-17": "Standardlicht A",
+       "exif-lightsource-18": "Standardlicht B",
+       "exif-lightsource-19": "Standardlicht C",
+       "exif-lightsource-24": "ISO-studiokünstlicht",
+       "exif-lightsource-255": "Anders",
+       "exif-flash-fired-0": "Flits is neet afgån",
+       "exif-flash-fired-1": "Flits is afgån",
+       "exif-flash-return-0": "flits stüürt geen gegeavens",
+       "exif-flash-return-2": "geen weaderkaatsing van de flits vaststeld",
+       "exif-flash-return-3": "wederkaatsing van de flits vaststeld",
+       "exif-flash-mode-1": "verplicht mid flits",
+       "exif-flash-mode-2": "flits verplicht underdrukked",
+       "exif-flash-mode-3": "automatiske modus",
+       "exif-flash-function-1": "Geen flitsfunkty",
+       "exif-flash-redeye-1": "roude-ougenredukty",
        "exif-focalplaneresolutionunit-2": "duum",
-       "exif-sensingmethod-1": "Niet vastesteld",
-       "exif-sensingmethod-2": "Eén-chip-kleursensor",
-       "exif-sensingmethod-3": "Twee-chips-kleursensor",
-       "exif-sensingmethod-4": "Dree-chips-kleurensensor",
-       "exif-sensingmethod-5": "Kleurvolgende gebiedssensor",
-       "exif-sensingmethod-7": "Dreeliendige sensor",
-       "exif-sensingmethod-8": "Kleurvolgende gebiedssensor",
+       "exif-sensingmethod-1": "Neet vaststeld",
+       "exif-sensingmethod-2": "Eyn-chip-klöörsensor",
+       "exif-sensingmethod-3": "Twey-chip-klöörsensor",
+       "exif-sensingmethod-4": "Dree-chip-klöörensensor",
+       "exif-sensingmethod-5": "Klöörvolgende gebeedssensor",
+       "exif-sensingmethod-7": "Dreelynige sensor",
+       "exif-sensingmethod-8": "Klöörvolgende gebeedssensor",
        "exif-filesource-3": "Digitale fotokamera",
-       "exif-scenetype-1": "n Drekt efotografeerden aofbeelding",
-       "exif-customrendered-0": "Normaal",
-       "exif-customrendered-1": "An-epas",
-       "exif-exposuremode-0": "Automaties",
-       "exif-exposuremode-1": "Haandmaotig",
-       "exif-exposuremode-2": "Belochtingsrie",
-       "exif-whitebalance-0": "Automaties",
-       "exif-whitebalance-1": "Haandmaotig",
-       "exif-scenecapturetype-0": "standard",
-       "exif-scenecapturetype-1": "laandschap",
-       "exif-scenecapturetype-2": "pertret",
-       "exif-scenecapturetype-3": "Nachtscène",
-       "exif-gaincontrol-0": "Gien",
-       "exif-gaincontrol-1": "Lege pieken umhoge",
-       "exif-gaincontrol-2": "Hoge pieken umhoge",
-       "exif-gaincontrol-3": "Lege pieken ummeneer",
-       "exif-gaincontrol-4": "Hoge pieken ummeneer",
+       "exif-scenetype-1": "Een direkt fotografeerde afbealding",
+       "exif-customrendered-0": "Normale verwarking",
+       "exif-customrendered-1": "Anpasde verwarking",
+       "exif-customrendered-2": "HDR (geen origineel seakerd)",
+       "exif-customrendered-3": "HDR (origineel seakerd)",
+       "exif-customrendered-4": "Origineel (vöär HDR)",
+       "exif-customrendered-6": "Panorama",
+       "exif-customrendered-7": "Portret-HDR",
+       "exif-customrendered-8": "Portret",
+       "exif-exposuremode-0": "Automatisk",
+       "exif-exposuremode-1": "Handmåtige belichting",
+       "exif-exposuremode-2": "Belichtingsryge",
+       "exif-whitebalance-0": "Automatiske witbalans",
+       "exif-whitebalance-1": "Handmåtige witbalans",
+       "exif-scenecapturetype-0": "Standard",
+       "exif-scenecapturetype-1": "Landskap",
+       "exif-scenecapturetype-2": "Portret",
+       "exif-scenecapturetype-3": "Nachtscene",
+       "exif-gaincontrol-0": "Geen",
+       "exif-gaincontrol-1": "Laege pyken ümhouge",
+       "exif-gaincontrol-2": "Houge pyken ümhouge",
+       "exif-gaincontrol-3": "Laege pyken ümdale",
+       "exif-gaincontrol-4": "Houge pyken ümdale",
        "exif-contrast-0": "Normaal",
-       "exif-contrast-1": "Zachte",
+       "exif-contrast-1": "Sachte",
        "exif-contrast-2": "Hard",
        "exif-saturation-0": "Normaal",
-       "exif-saturation-1": "Leeg",
-       "exif-saturation-2": "Hoge",
+       "exif-saturation-1": "Laeg",
+       "exif-saturation-2": "Houge",
        "exif-sharpness-0": "Normaal",
-       "exif-sharpness-1": "Zachte",
+       "exif-sharpness-1": "Sachte",
        "exif-sharpness-2": "Hard",
-       "exif-subjectdistancerange-0": "Onbekend",
-       "exif-subjectdistancerange-1": "Macro",
-       "exif-subjectdistancerange-2": "Kortbie",
-       "exif-subjectdistancerange-3": "Veeraof",
-       "exif-gpslatitude-n": "Noorderbreedte",
-       "exif-gpslatitude-s": "Zujerbreedte",
-       "exif-gpslongitude-e": "Oosterlengte",
-       "exif-gpslongitude-w": "Westerlengte",
-       "exif-gpsaltitude-above-sealevel": "$1 {{PLURAL:$1|meter|meter}} boven de zeespegel",
-       "exif-gpsaltitude-below-sealevel": "$1 {{PLURAL:$1|meter|meter}} onder de zeespegel",
-       "exif-gpsstatus-a": "Bezig mit meten",
-       "exif-gpsstatus-v": "Meetinteroperebiliteit",
-       "exif-gpsmeasuremode-2": "2-dimensionale meting",
-       "exif-gpsmeasuremode-3": "3-dimensionale meting",
-       "exif-gpsspeed-k": "Kilometer per uur",
-       "exif-gpsspeed-m": "Miel per ure",
-       "exif-gpsspeed-n": "Kneupen",
+       "exif-subjectdistancerange-0": "Unbekend",
+       "exif-subjectdistancerange-1": "Makro",
+       "exif-subjectdistancerange-2": "Kortby",
+       "exif-subjectdistancerange-3": "Wyd weg",
+       "exif-gpslatitude-n": "Noorderbreydde",
+       "exif-gpslatitude-s": "Süderbreydde",
+       "exif-gpslongitude-e": "Ousterlängde",
+       "exif-gpslongitude-w": "Westerlängde",
+       "exif-gpsaltitude-above-sealevel": "$1 {{PLURAL:$1|meter}} boaven de seyspegel",
+       "exif-gpsaltitude-below-sealevel": "$1 {{PLURAL:$1|meter}} under de seyspegel",
+       "exif-gpsstatus-a": "Beasig mid meaten",
+       "exif-gpsstatus-v": "Meatinteroperabiliteit",
+       "exif-gpsmeasuremode-2": "2-dimensionale meating",
+       "exif-gpsmeasuremode-3": "3-dimensionale meating",
+       "exif-gpsspeed-k": "Kilometer per üre",
+       "exif-gpsspeed-m": "Myl per üre",
+       "exif-gpsspeed-n": "Knoupen",
        "exif-gpsdestdistance-k": "Kilometer",
-       "exif-gpsdestdistance-m": "Miel",
-       "exif-gpsdestdistance-n": "Zeemielen",
-       "exif-gpsdop-excellent": "Uutstekend ($1)",
-       "exif-gpsdop-good": "Goed ($1)",
+       "exif-gpsdestdistance-m": "Myl",
+       "exif-gpsdestdistance-n": "Seymylen",
+       "exif-gpsdop-excellent": "Uutsteakend ($1)",
+       "exif-gpsdop-good": "Good ($1)",
        "exif-gpsdop-moderate": "Gemiddeld ($1)",
-       "exif-gpsdop-fair": "Redelik ($1)",
-       "exif-gpsdop-poor": "Slicht ($1)",
-       "exif-objectcycle-a": "Allinnig smarnens",
-       "exif-objectcycle-p": "Allinnig savends",
-       "exif-objectcycle-b": "'s Mannen én 's avens",
-       "exif-gpsdirection-t": "Waore richting",
-       "exif-gpsdirection-m": "Magnetiese richting",
-       "exif-ycbcrpositioning-1": "In t midden",
-       "exif-ycbcrpositioning-2": "E-kositueerd",
-       "exif-dc-contributor": "Luui die bie-edreugen hebben",
+       "exif-gpsdop-fair": "Readelik ($1)",
+       "exif-gpsdop-poor": "Slecht ($1)",
+       "exif-objectcycle-a": "Allinnig smargens",
+       "exif-objectcycle-p": "Allinnig såvends",
+       "exif-objectcycle-b": "Smargens en såvends",
+       "exif-gpsdirection-t": "Wåre richting",
+       "exif-gpsdirection-m": "Magnetiske richting",
+       "exif-ycbcrpositioning-1": "In et midden",
+       "exif-ycbcrpositioning-2": "Kositueerd",
+       "exif-dc-contributor": "Bydragers",
        "exif-dc-coverage": "Ruumtelike of temporele reikwiedte van media",
        "exif-dc-date": "Daotum(s)",
-       "exif-dc-publisher": "Uutgever",
-       "exif-dc-relation": "Verwaante media",
+       "exif-dc-publisher": "Uutgeaver",
+       "exif-dc-relation": "Verwande media",
        "exif-dc-rights": "Rechten",
        "exif-dc-source": "Bronmedia",
        "exif-dc-type": "Soort media",
index 7d8e596..20d2991 100644 (file)
        "version-specialpages": "Toimintosivut",
        "version-parserhooks": "Jäsenninkytkökset",
        "version-variables": "Muuttujat",
-       "version-editors": "Muokkaajat",
+       "version-editors": "Muokkaimet",
        "version-antispam": "Roskalinkkien estäminen",
        "version-other": "Muut",
        "version-mediahandlers": "Median käsittelijät",
index 07f248d..53d54a2 100644 (file)
        "thursday": "jeudi",
        "friday": "vendredi",
        "saturday": "samedi",
-       "sun": "Dim.",
-       "mon": "Lun.",
-       "tue": "Mar.",
-       "wed": "Mer.",
-       "thu": "Jeu.",
-       "fri": "Ven.",
-       "sat": "Sam.",
+       "sun": "dim.",
+       "mon": "lun.",
+       "tue": "mar.",
+       "wed": "mer.",
+       "thu": "jeu.",
+       "fri": "ven.",
+       "sat": "sam.",
        "january": "janvier",
        "february": "février",
        "march": "mars",
index 7d18ae6..3ad54a4 100644 (file)
        "tooltip-pt-watchlist": "A lista de páxinas cuxas modificacións está a seguir",
        "tooltip-pt-mycontris": "Lista das súas contribucións",
        "tooltip-pt-anoncontribs": "Unha lista das modificacións feitas desde este enderezo IP",
-       "tooltip-pt-login": "Recoméndaselle rexistrarse, se ben non é obrigatorio",
+       "tooltip-pt-login": "É recomendable que se rexistre, se ben non é obrigatorio",
        "tooltip-pt-login-private": "Precisa conectarse para usa esta wiki",
        "tooltip-pt-logout": "Saír ao anonimato",
-       "tooltip-pt-createaccount": "Recoméndaselle crear unha conta e acceder ao sistema, se ben non é obrigatorio",
+       "tooltip-pt-createaccount": "É recomendable que cree unha conta e acceda ao sistema, se ben non é obrigatorio",
        "tooltip-ca-talk": "Conversa acerca do contido desta páxina",
        "tooltip-ca-edit": "Edite esta páxina",
        "tooltip-ca-addsection": "Comezar unha nova sección",
        "tooltip-p-logo": "Visitar a páxina principal",
        "tooltip-n-mainpage": "Visitar a páxina principal",
        "tooltip-n-mainpage-description": "Visitar a páxina principal",
-       "tooltip-n-portal": "Información acerca do proxecto, o que pode facer e os lugares onde atopar as cousas",
+       "tooltip-n-portal": "Información acerca do proxecto, do que pode facer e dos lugares onde atopar as cousas",
        "tooltip-n-currentevents": "Información acerca de acontecementos de actualidade",
        "tooltip-n-recentchanges": "A lista de modificacións recentes no wiki",
        "tooltip-n-randompage": "Cargar unha páxina ao chou",
index 8fc0095..866205c 100644 (file)
        "rcfilters-filter-showlinkedfrom-label": "Prikaži promjene na povezanim stranicama",
        "rcfilters-filter-showlinkedfrom-option-label": "<strong>Stranice na koje se povezuje</strong> izabrana stranica",
        "rcfilters-filter-showlinkedto-option-label": "<strong>Stranice koje povezuju</strong> na izabranu stranicu",
+       "rcfilters-allcontents-label": "Sav sadržaj",
+       "rcfilters-alldiscussions-label": "Svi razgovori",
        "rcnotefrom": "Ispod {{PLURAL:$5|je izmjena|su izmjene}} od <strong>$3, $4</strong> (prikazano ih do <strong>$1</strong>).",
        "rclistfromreset": "Vrati na zadano izbor datuma",
        "rclistfrom": "Prikaži nove promjene počevši od $3 $2",
index a346ed2..0ecff4f 100644 (file)
        "oct": "okt",
        "nov": "nov",
        "dec": "dec",
-       "january-date": "Január $1",
-       "february-date": "Február $1",
-       "march-date": "Március $1",
-       "april-date": "Ã\81prilis $1",
-       "may-date": "Május $1",
-       "june-date": "Június $1",
-       "july-date": "Július $1",
-       "august-date": "Augusztus $1",
-       "september-date": "Szeptember $1",
-       "october-date": "Október $1",
-       "november-date": "November $1",
-       "december-date": "December $1",
+       "january-date": "január $1",
+       "february-date": "február $1",
+       "march-date": "március $1",
+       "april-date": "április $1",
+       "may-date": "május $1",
+       "june-date": "június $1",
+       "july-date": "július $1",
+       "august-date": "augusztus $1",
+       "september-date": "szeptember $1",
+       "october-date": "október $1",
+       "november-date": "november $1",
+       "december-date": "december $1",
        "period-am": "de.",
        "period-pm": "du.",
        "pagecategories": "{{PLURAL:$1|Kategória|Kategória}}",
        "category_header": "A(z) „$1” kategóriába tartozó lapok",
        "subcategories": "Alkategóriák",
        "category-media-header": "A(z) „$1” kategóriába tartozó médiafájlok",
-       "category-empty": "''Ebben a kategóriában pillanatnyilag egyetlen lap vagy médiafájl sem szerepel.''",
+       "category-empty": "<em>Ebben a kategóriában pillanatnyilag egyetlen lap vagy médiafájl sem szerepel.</em>",
        "hidden-categories": "{{PLURAL:$1|Rejtett kategória|Rejtett kategóriák}}",
        "hidden-category-category": "Rejtett kategóriák",
        "category-subcat-count": "{{PLURAL:$2|Ennek a kategóriának csak egyetlen alkategóriája van.|Ez a kategória az alábbi {{PLURAL:$1|alkategóriával|$1 alkategóriával}} rendelkezik (összesen $2 alkategóriája van).}}",
index 5cbecc3..50ef092 100644 (file)
        "tog-useeditwarning": "Avvisa quando si esce da una pagina di modifica con modifiche non salvate",
        "tog-prefershttps": "Usa sempre una connessione sicura quando si effettua l'accesso",
        "tog-showrollbackconfirmation": "Mostra una richiesta di conferma quando si clicca su un link di rollback",
+       "tog-requireemail": "Richiedi email per cambiare la password",
        "underline-always": "Sempre",
        "underline-never": "Mai",
        "underline-default": "Impostazioni predefinite del browser o del tema",
        "undo-norev": "La modifica non può essere annullata perché non esiste o è stata cancellata.",
        "undo-nochange": "Sembra che la modifica sia già stata annullata.",
        "undo-summary": "Annullata la modifica $1 di [[Special:Contributions/$2|$2]] ([[User talk:$2|discussione]])",
+       "undo-summary-anon": "Annullata la modifica $1 di [[Special:Contributions/$2|$2]]",
        "undo-summary-username-hidden": "Annullata la modifica $1 di un utente nascosto",
        "cantcreateaccount-text": "La registrazione è stata bloccata da [[User:$3|$3]] per questo indirizzo IP ('''$1''').\n\nLa motivazione del blocco fornita da $3 è la seguente: ''$2''",
        "cantcreateaccount-range-text": "La registrazione da indirizzi IP nell'intervallo <strong>$1</strong>, che include il tuo (<strong>$4</strong>), è stata bloccata da [[User:$3|$3]].\n\nLa motivazione fornita da $3 è <em>$2</em>",
        "prefs-help-email": "L'inserimento del proprio indirizzo email è facoltativo, ma permette di ricevere la propria password qualora venisse dimenticata.",
        "prefs-help-email-others": "Puoi anche scegliere di lasciare che gli altri ti contattino via posta elettronica con un collegamento dalla tua pagina utente o di discussione.\nIl tuo indirizzo non viene rivelato quando gli altri utenti ti contattano.",
        "prefs-help-email-required": "L'indirizzo email è obbligatorio.",
+       "prefs-help-requireemail": "Se selezionato, invieremo solo email per la reimpostazione della password se la persona che sta resettando la password ha fornito sia il nome utente che l'email per questa utenza.",
        "prefs-info": "Informazioni di base",
        "prefs-i18n": "Internazionalizzazione",
        "prefs-signature": "Firma",
        "ipblocklist-legend": "Cerca un utente bloccato",
        "blocklist-userblocks": "Nascondi i blocchi degli utenti registrati",
        "blocklist-tempblocks": "Nascondi i blocchi temporanei",
+       "blocklist-indefblocks": "Nascondi blocchi infiniti",
        "blocklist-addressblocks": "Nascondi i blocchi di un solo IP",
        "blocklist-type": "Tipo:",
        "blocklist-type-opt-all": "Tutto",
        "move-page-legend": "Spostamento di pagina",
        "movepagetext": "Questo modulo consente di rinominare una pagina, spostando tutta la sua cronologia al nuovo nome. La pagina attuale diverrà automaticamente un redirect al nuovo titolo. Puoi aggiornare automaticamente i redirect che puntano al titolo originale. Puoi decidere di non farlo, ma ricordati di verificare che lo spostamento non abbia creato [[Special:DoubleRedirects|doppi redirect]] o [[Special:BrokenRedirects|redirect errati]]. L'onere di garantire che i collegamenti alla pagina restino corretti spetta a chi la sposta.\n\nSi noti che la pagina <strong>non</strong> sarà spostata se ne esiste già una con il nuovo nome, a meno che quest'ultima non sia costituita solo da un redirect e sia priva di versioni precedenti. In caso di spostamento errato si può quindi tornare subito al vecchio titolo, e non è possibile sovrascrivere per errore una pagina già esistente.\n\n<strong>Nota:</strong>\nUn cambiamento così drastico può creare contrattempi e problemi, soprattutto per le pagine più visitate. Accertarsi di aver valutato le conseguenze dello spostamento prima di procedere.",
        "movepagetext-noredirectfixer": "Questo modulo consente di rinominare una pagina, spostando tutta la sua cronologia al nuovo nome. La pagina attuale diverrà automaticamente un redirect al nuovo titolo. Controlla che lo spostamento non abbia creato [[Special:DoubleRedirects|doppi redirect]] o [[Special:BrokenRedirects|redirect errati]]. L'onere di garantire che i collegamenti alla pagina restino corretti spetta a chi la sposta.\n\nSi noti che la pagina <strong>non</strong> sarà spostata se ne esiste già una con il nuovo nome, a meno che non sia costituita solo da un redirect e sia priva di versioni precedenti. In caso di spostamento errato si può quindi tornare subito al vecchio titolo, e non è possibile sovrascrivere per errore una pagina già esistente.\n\n<strong>Nota:</strong>\nUn cambiamento così drastico può creare contrattempi e problemi, soprattutto per le pagine più visitate. Accertarsi di aver valutato le conseguenze dello spostamento prima di procedere.",
+       "movepagetext-noredirectsupport": "Questo modulo consente di rinominare una pagina, spostando tutta la sua cronologia al nuovo nome. L'onere di garantire che i collegamenti alla pagina restino corretti spetta a chi la sposta.\n\nSi noti che la pagina <strong>non</strong> sarà spostata se ne esiste già una con il nuovo nome.\nIn caso di spostamento errato si può quindi tornare subito al vecchio titolo, e non è possibile sovrascrivere per errore una pagina già esistente.\n\n<strong>Nota:</strong>\nUn cambiamento così drastico può creare contrattempi e problemi, soprattutto per le pagine più visitate. Accertarsi di aver valutato le conseguenze dello spostamento prima di procedere.",
        "movepagetalktext": "Se selezioni questa casella, la corrispondente pagina di discussione sarà spostata automaticamente al nuovo titolo, a meno che esista già una pagina di discussione non vuota.\n\nIn questi casi, se lo ritieni opportuno, dovrai spostare o unire manualmente la pagina.",
        "moveuserpage-warning": "'''Attenzione:''' Si sta per spostare una pagina utente. Nota che verrà spostata solamente la pagina. L'utente ''non'' sarà rinominato.",
        "movecategorypage-warning": "<strong>Attenzione:</strong> si sta per spostare una categoria. Solo questa pagina verrà spostata: tutte le pagine nella vecchia categoria <em>non</em> saranno inserite nella nuova.",
        "specialpages-group-developer": "Strumenti per gli sviluppatori",
        "blankpage": "Pagina vuota",
        "intentionallyblankpage": "Questa pagina è lasciata volutamente vuota.",
+       "disabledspecialpage-disabled": "Questa pagina è stata disabilitata da un amministratore di sistema.",
        "external_image_whitelist": " #Lasciare questa riga esattamente com'è<pre>\n#Inserire i frammenti delle espressioni regolari (solo la parte che va fra //) di seguito\n#Queste verranno messe a confronto con gli indirizzi URL delle immagini esterne (hotlinked)\n#Le corrispondenze saranno mostrate come immagini, altrimenti verrà mostrato solo un collegamento\n#Le righe che iniziano con # sono considerate dei commenti\n#La differenza tra maiuscole e minuscole non è significativa\n\n#Inserire sopra questa riga tutti i frammenti di regex. Lasciare questa riga esattamente com'è</pre>",
        "tags": "Etichette di modifiche valide",
        "tag-filter": "Filtra per [[Special:Tags|etichetta]]:",
index f143c93..ff33e2e 100644 (file)
@@ -98,7 +98,8 @@
                        "Puntti ja",
                        "マツムシ",
                        "神樂坂秀吉",
-                       "Yuukin0248"
+                       "Yuukin0248",
+                       "Reiwa period"
                ]
        },
        "tog-underline": "リンクの下線:",
        "botpasswords-insert-failed": "ボット「$1」の追加に失敗しました。既に追加されていないか確認してください。",
        "botpasswords-update-failed": "ボット「$1」の更新に失敗しました。削除されていないか確認してください。",
        "botpasswords-created-title": "ボット用パスワードが作成されました",
-       "botpasswords-created-body": "利用者「$2」のボット名「$1」のためのパスワードが作成されました。",
+       "botpasswords-created-body": "{{GENDER:$2|利用者}}「$2」のボット「$1」のパスワードが作成されました。",
        "botpasswords-updated-title": "ボット用パスワードが更新されました",
-       "botpasswords-updated-body": "利用者「$2」のボット名「$1」のためのパスワードが更新されました。",
+       "botpasswords-updated-body": "{{GENDER:$2|利用者}}「$2」のボット「$1」のパスワードが更新されました。",
        "botpasswords-deleted-title": "ボット用パスワードが削除されました",
-       "botpasswords-deleted-body": "利用者「$2」のボット名「$1」のためのパスワードが削除されました。",
+       "botpasswords-deleted-body": "{{GENDER:$2|利用者}}「$2」のボット「$1」のパスワードが削除されました。",
        "botpasswords-newpassword": "<strong>$1</strong>用の新しいパスワードは<strong>$2</strong>です。<em>後で参照するために、この情報を控えておいてください。</em><br />(古いボットの制約などでログイン名と利用者名が同じでなければならない場合は、<strong>$3</strong>を利用者名とし、<strong>$4</strong>をパスワードとしてください。)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider が有効ではありません。",
        "botpasswords-restriction-failed": "ボットパスワード制限によりログインできません。",
        "rcfilters-filter-showlinkedto-label": "指定されたページのリンク元の変更を表示",
        "rcfilters-filter-showlinkedto-option-label": "指定されたページに<strong>リンクしているページ(リンク元)</strong>",
        "rcfilters-target-page-placeholder": "ページ名(またはカテゴリ名)を入力",
+       "rcfilters-alldiscussions-label": "全ての議論",
        "rcnotefrom": "以下は<strong>$3 $4</strong>以降の{{PLURAL:$5|更新です}} (最大 <strong>$1</strong> 件)。",
        "rclistfromreset": "日時指定をリセット",
        "rclistfrom": "$3の$2以降の更新を表示する",
        "backend-fail-contenttype": "「$1」に保存するコンテンツの種類が判断できませんでした。",
        "backend-fail-batchsize": "ストレージバックエンドは $1 件のファイル{{PLURAL:$1|操作}}のバッチを与えられました; 上限は $2 件の{{PLURAL:$2|操作}}です。",
        "backend-fail-usable": "権限が不足している、またはディレクトリ/コンテナーがないため、ファイル「$1」の読み取り/書き込みができません。",
+       "backend-fail-stat": "ファイル\"$1\"の状態を読み込めませんでした。",
        "filejournal-fail-dbconnect": "ストレージバックエンド「$1」のジャーナルデータベースに接続できません。",
        "filejournal-fail-dbquery": "ストレージバックエンド「$1」のジャーナルデータベースを更新できません。",
        "lockmanager-notlocked": "「$1」をロック解除できませんでした。ロックされていません。",
        "listfiles-userdoesnotexist": "利用者アカウント「$1」は登録されていません。",
        "imgfile": "ファイル",
        "listfiles": "ファイル一覧",
+       "listfiles_subpage": "$1 によるアップロード",
        "listfiles_thumb": "サムネイル",
        "listfiles_date": "日時",
        "listfiles_name": "名前",
        "tags-delete-not-allowed": "拡張機能によって定義されているタグは削除できません(ただし拡張機能が明示的に削除を許可している場合を除く)。",
        "tags-delete-not-found": "タグ「$1」は存在しません。",
        "tags-delete-too-many-uses": "タグ「$1」は少なくとも$2版に付与されており、削除できません。",
-       "tags-delete-warnings-after-delete": "ã\82¿ã\82°ã\80\8c$1ã\80\8dã\81®å\89\8aé\99¤ã\81\97ã\81¾ã\81\97ã\81\9fã\81\8c、以下の{{PLURAL:$2|警告}}が発生しました:",
+       "tags-delete-warnings-after-delete": "ã\82¿ã\82°ã\80\8c$1ã\80\8dã\82\92å\89\8aé\99¤ã\81\97ã\81\9fã\81¨ã\81\93ã\82\8d、以下の{{PLURAL:$2|警告}}が発生しました:",
        "tags-delete-no-permission": "変更タグを削除する権限がありません。",
        "tags-activate-title": "タグの有効化",
        "tags-activate-question": "タグ「$1」を有効化しようとしています。",
index 4153130..56175e9 100644 (file)
@@ -7,7 +7,8 @@
                        "Macofe",
                        "Saraiki",
                        "BukhariSaeed",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Amire80"
                ]
        },
        "tog-underline": "ربطو خط کشیدگی",
index 214cace..8ed1011 100644 (file)
        "group-all": "(pêro)",
        "group-user-member": "Karber",
        "group-bot-member": "Bot",
-       "group-sysop-member": "İdareker",
+       "group-sysop-member": "{{GENDER:$1|idareker}}",
        "group-bureaucrat-member": "Burokrat",
        "grouppage-user": "{{ns:project}}:Karberi",
        "grouppage-bot": "{{ns:project}}:Boti",
index e8081a4..f293d56 100644 (file)
        "listfiles-userdoesnotexist": "계정 \"$1\"은 등록되지 않았습니다.",
        "imgfile": "파일",
        "listfiles": "파일 목록",
+       "listfiles_subpage": "$1님이 올림",
        "listfiles_thumb": "섬네일",
        "listfiles_date": "날짜",
        "listfiles_name": "이름",
        "ipblocklist-legend": "차단된 사용자 찾기",
        "blocklist-userblocks": "계정에 대한 차단 숨기기",
        "blocklist-tempblocks": "기한이 정해진 차단을 숨기기",
+       "blocklist-indefblocks": "무기한 차단 숨기기",
        "blocklist-addressblocks": "단일 IP 차단을 숨기기",
        "blocklist-type": "유형:",
        "blocklist-type-opt-all": "모두",
        "mycustomjsredirectprotected": "넘겨주기이면서 사용자 공간 안에 참조하고 있지 않으므로 이 자바스크립트 페이지를 편집할 권한이 없습니다.",
        "easydeflate-invaliddeflate": "주어진 컨텐츠가 적절히 압축되지 않았습니다",
        "unprotected-js": "보안 상의 이유로 자바스크립트는 보호되지 않은 문서로부터 불러올 수 없습니다. 미디어위키: 이름공간이나 사용자의 하위 문서에서만 자바스크립트를 만들어 주십시오.",
-       "userlogout-continue": "로그아웃하시겠습니까?"
+       "userlogout-continue": "로그아웃하시겠습니까?",
+       "rest-no-match": "요청된 상대 경로($1)는 알려진 핸들러와 일치하지 않습니다"
 }
index bdfc178..e04bda1 100644 (file)
@@ -11,7 +11,8 @@
                        "Universal Life",
                        "לערי ריינהארט",
                        "아라",
-                       "StevenJ81"
+                       "StevenJ81",
+                       "Chabi1"
                ]
        },
        "tog-underline": "Suliñar los atamientos:",
        "mypage": "Hoja",
        "mytalk": "Diskusyón",
        "anontalk": "Diskusyón para este adresso de IP",
-       "navigation": "Navigación",
-       "and": "&#32;y",
+       "navigation": "Navigasyon",
+       "and": "&#32;i",
        "faq": "DDS",
        "actions": "Aksiones",
        "namespaces": "Espacios de nombres",
        "tagline": "De {{SITENAME}}",
        "help": "Ayudo",
        "search": "Buxcar",
-       "searchbutton": "Buxcar",
+       "searchbutton": "Bushkar",
        "go": "Ir",
        "searcharticle": "Yir",
        "history": "La istoria de la hoja",
        "tooltip-pt-logout": "Sal de tu cuento",
        "tooltip-pt-createaccount": "Te consejamos de avrir un cuento y hazer entrada allá, portanto no sos obligado",
        "tooltip-ca-talk": "Diskusyón encima del contènido desta hoja",
-       "tooltip-ca-edit": "Troca esta hoja",
+       "tooltip-ca-edit": "Troka esta oja",
        "tooltip-ca-addsection": "Ajusta un kapítolo muevo",
        "tooltip-ca-viewsource": "Esta hoja está guadrada.\nPuedes ver su manadero",
        "tooltip-ca-history": "Enderechamientos passados desta hoja",
        "feedback-cancel": "Anular",
        "feedback-message": "Messaje",
        "feedback-subject": "Sujeto",
-       "searchsuggest-search": "Buxca en {{SITENAME}}",
+       "searchsuggest-search": "Bushka en {{SITENAME}}",
        "duration-seconds": "$1{{PLURAL:$1|segundo|segundos}}",
        "duration-minutes": "$1{{PLURAL:$1|minuto|minutos}}",
        "duration-hours": "$1{{PLURAL:$1|ora|oras}}",
index f355e60..8d79167 100644 (file)
        "password-login-forbidden": "Šī lietotājvārda un paroles izmantošana ir aizliegta.",
        "mailmypassword": "Atiestatīt paroli",
        "passwordremindertitle": "Jauna pagaidu parole no {{SITENAME}}s",
-       "passwordremindertext": "Kāds (iespējams, Tu pats, no IP adreses $1)\nlūdza, lai nosūtām Tev jaunu {{SITENAME}} ($4) paroli.\nLietotajam $2 pagaidu parole tagad ir $3.\nLudzu, nomaini paroli, kad esi veiksmīgi iekļuvis iekšš.\nTavas pagaidu paroles derīguma termiņš beigsies pēc {{PLURAL:$5|$5 dienām|$5 dienas|$5 dienām}}.\n\nJa paroles pieprasījumu bija nosūtījis kāds cits, vai arī tu atcerējies savu veco paroli, šo var ignorēt. Vecā parole joprojām darbojas.",
+       "passwordremindertext": "Kāds (no IP adreses $1)\nlūdza, lai nosūtām tev jaunu {{SITENAME}} ($4) paroli.\nLietotajam $2 izveidota pagaidu parole $3. Ja tāds bija tavs mērķis, tagad tev jāpieslēdzas un jānomaina parole. Tavas pagaidu paroles derīguma termiņš beigsies pēc {{PLURAL:$5|$5 dienām|$5 dienas|$5 dienām}}.\n\nJa paroles pieprasījumu bija nosūtījis kāds cits, vai arī tu atcerējies savu veco paroli, šo var ignorēt. Vecā parole joprojām darbojas.",
        "noemail": "Lietotājs \"$1\" nav reģistrējis e-pasta adresi.",
        "noemailcreate": "Tev jānorāda derīgu e-pasta adresi",
        "passwordsent": "Esam nosūtījuši jaunu paroli uz e-pasta adresi, kuru ir norādījis lietotājs $1. Lūdzu, nāc iekšā ar jauno paroli, kad būsi to saņēmis.",
index 659bbec..bf2ca77 100644 (file)
        "showpreview": "示覽",
        "showdiff": "示異",
        "anoneditwarning": "'''警示:'''子未登簿,若確纂,IP將誌。茍[$1 登]或[$2 開戶口],是纂欲存以子名,及他效。",
-       "anonpreviewwarning": "''子未登簿,IP將誌。''",
+       "anonpreviewwarning": "子未登簿,IP將誌。",
        "missingsummary": "'''醒示:'''子未概之,復「$1」則文倍焉。",
        "missingcommenttext": "請贊之",
        "missingcommentheader": "'''醒示:'''子未概標之,復「$1」則文倍焉。",
index 3767ed8..b78c96c 100644 (file)
@@ -18,7 +18,8 @@
                        "Zakiy",
                        "Vlad5250",
                        "S Kartika",
-                       "NoiX180"
+                       "NoiX180",
+                       "Pitnia Ayu Saputri"
                ]
        },
        "tog-underline": "Garih bawahi pautan:",
        "right-unblockself": "Malapehan sakek surang",
        "right-protect": "Ubah tingkek palinduangan jo suntiang halaman yang dilinduangi baruntun",
        "right-editprotected": "Manyuntiang halaman yang dilinduangi sabagai \"{{int:protect-level-sysop}}\"",
+       "right-editcontentmodel": "manyuntiang model konten sabuah laman",
        "right-editinterface": "Manyuntiang antarmuko pangguno",
        "right-editusercss": "Manyuntiang berkas CSS pangguno lain",
        "right-edituserjson": "Manyuntiang berkas JSON pangguno lain",
        "right-edituserjs": "Manyuntiang berkas JS pangguno lain",
+       "right-editsitecss": "manyuntiang CSS untuak sadoalah situs",
+       "right-editsitejson": "suntiang JSON untuak sadoalah situs",
+       "right-editsitejs": "suntiang JavaScript untuak sadoalah situs",
+       "right-editmyusercss": "suntiang berkas CSS pangguno Sanak",
+       "right-editmyuserjson": "suntiang berkas JSON pangguno Sanak",
+       "right-editmyuserjs": "suntiang berkas JavaScript pangguno Sanak",
+       "right-editmyuserjsredirect": "suntiang berkas JavaScript Sanak nan marupokan aliahan",
+       "right-viewmywatchlist": "Caliak daftar pantauan Sanak",
+       "right-editmywatchlist": "Manyuntiang daftar Sanak surang. Beberapo kegiatan akan tetap akan manambahan halaman tanpa hak iko.",
+       "right-viewmyprivateinfo": "Maliek data pribadi Sanak surang (misalnyo: alamaik email, namo asli Sanak)",
+       "right-editmyprivateinfo": "Manyuntiang data pribadi Sanak surang (misalnyo: alamaik surel, namo asli)",
+       "right-editmyoptions": "manyuntiang preferensi Sanak",
+       "right-rollback": "mambaliakan sacaro capek suntiangan-suntiangan pangguno tarakhia nan manyuntiang laman tatantu",
+       "right-markbotedits": "tandoi pambaliakan revisi sabagai suntiangan bot",
        "right-noratelimit": "Indak dipangaruahi jo pambatehan jumlah suntiangan",
        "right-import": "Mangimpor laman dari wiki lain",
        "right-importupload": "Mangimpor laman dari berkas nan dimuek",
+       "right-patrol": "manandoi suntiangan pangguno lain sabagai terpatroli",
        "right-autopatrol": "Suntiangan surang sacaro otomatih ditandoi tapantau",
+       "right-patrolmarks": "caliak panandoan patroli parubahan tabaru",
+       "right-unwatchedpages": "mancaliak daftar laman nan indak tapantau",
+       "right-mergehistory": "manggabuangan revisi-revisi tadahulu dari laman ko",
        "right-userrights": "Manyuntiang sadoalah hak pangguno",
        "right-userrights-interwiki": "Manyuntiang hak pangguno-pangguno di wiki lain",
        "right-siteadmin": "Mangunci jo mambukak kunci basis data",
        "action-editcontentmodel": "manyuntiang model konten sabuah laman",
        "action-managechangetags": "buek jo matikan tag",
        "action-applychangetags": "terapkan tag basamoan jo parubahan Sanak",
-       "action-changetags": "Tambah jo apuih [[Special:Tags|tag]] arbitrari pado tiok-tiok  revisi jo entri log",
+       "action-changetags": "Tambah jo apuih arbitrari pado tiok-tiok  revisi jo entri log",
        "action-deletechangetags": "apuih tag dari basisdata",
        "action-purge": "apuih singgahan laman ko",
        "action-apihighlimits": "manggunoan bateh labiah tinggi dalam kueri API",
        "recentchanges-legend": "Piliahan parubahan baru",
        "recentchanges-summary": "Caliak parubahan baru di wiki pado laman ko.<br />\n;Patunjuak:(<span style=\"color:blue;\">bedo</span>) parubahan, (<span style=\"color:blue;\">sijarah</span>) riwayaik parubahan, '''B''' laman baru, '''b''' suntiangan bot, '''k''' suntiangan ketek, <span class=\"unpatrolled\">!</span> parubahan alun dipatroli,<br /><span style=\"color:green;\">'''(+ ''bita'')'''</span> isi laman batambah, <span style=\"color:red;\">(- ''bita'')</span> isi laman bakurang, (← Ikhtisar otomatih), (→ <span style=\"color:grey;\">Suntiangan bagian</span>)",
        "recentchanges-noresult": "Indak ado parubahan dalam rantang wakatu ko nan sasuai jo kriteria.",
+       "recentchanges-timeout": "wakatu mancari alah habih. Sanak mungkin ka mancubo parameter pancarian nan lain.",
+       "recentchanges-network": "Salamo tajadi kasalahan teknis, indak ado hasil nan biso dimuek. Silahkan cubo untuak manyagarkan halaman liak.",
+       "recentchanges-notargetpage": "Masuakan namo halaman nan di ateh untuak maliek parubahan nan terkait jo halaman itu.",
        "recentchanges-feed-description": "Tamuan parubahan baru dalam umpan wiki ko",
        "recentchanges-label-newpage": "Suntiangan ko mambuek laman baru",
        "recentchanges-label-minor": "Iko suntiangan ketek",
        "recentchanges-label-plusminus": "Parubahan ukuran laman dalam bita",
        "recentchanges-legend-heading": "<strong>Katarangan:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (caliak pulo [[Special:NewPages|daftar laman nan baru]])",
+       "recentchanges-submit": "Tunjuakan",
+       "rcfilters-tag-remove": "Hapuih '$1'",
+       "rcfilters-legend-heading": "<strong> daftar singkekan:</strong>",
        "rcfilters-other-review-tools": "Pakakeh paninjauan lainnyo",
        "rcfilters-group-results-by-page": "Kalompokan hasil manuruik laman",
        "rcfilters-activefilters": "Panyariang aktip",
        "rcfilters-activefilters-hide": "Suruakan",
        "rcfilters-activefilters-show": "Tunjuakan",
+       "rcfilters-activefilters-hide-tooltip": "Sambunyian wilayah filter aktif",
+       "rcfilters-activefilters-show-tooltip": "Tunjukan wilayah filter aktif",
        "rcfilters-advancedfilters": "Panyariang lanjutan",
        "rcfilters-limit-title": "Hasil untuak ditampilkan",
        "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|parubahan}}, $2",
        "rcfilters-savedqueries-unsetdefault": "Hapuih dari baku",
        "rcfilters-savedqueries-remove": "Hapuih",
        "rcfilters-savedqueries-new-name-label": "Namo",
+       "rcfilters-savedqueries-new-name-placeholder": "Jalehan tujuan panyaringan iko",
        "rcfilters-savedqueries-apply-label": "Buek panyariang",
        "rcfilters-savedqueries-apply-and-setdefault-label": "Buek panyariang baku",
        "rcfilters-savedqueries-cancel-label": "Batalan",
        "rcfilters-savedqueries-add-new-title": "Simpan pangaturan panyariang ko",
        "rcfilters-savedqueries-already-saved": "Panyariang ko alah tasimpan. Ubah pangaturan Sanak untuak manyimpan panyariang baru.",
+       "rcfilters-restore-default-filters": "Kambalian filter awal",
+       "rcfilters-clear-all-filters": "Hapuih sado panyaringan",
        "rcfilters-show-new-changes": "Tunjuakan parubahan baru dari $1",
        "rcfilters-search-placeholder": "Panyariang parubahan (gunokan menu atau cari namo panyariang)",
        "rcfilters-search-placeholder-mobile": "Panyariang",
+       "rcfilters-invalid-filter": "Panyaringan indak sah",
+       "rcfilters-empty-filter": "Indak ado filter aktif. Sado alah kontribusi ditampilan.",
        "rcfilters-filterlist-title": "Panyariang",
+       "rcfilters-filterlist-whatsthis": "Baa caro karajonyo?",
+       "rcfilters-filterlist-feedbacklink": "Agiah tau kami apo nan Sanak pikian tantang alaik-alaik filter ko",
+       "rcfilters-highlightbutton-title": "Caliak hasilnyo",
+       "rcfilters-highlightmenu-title": "Piliah warno",
+       "rcfilters-highlightmenu-help": "Piliah warno untuak manyorot atribut iko",
        "rcfilters-filterlist-noresults": "Indak ado panyariang ditamukan",
+       "rcfilters-noresults-conflict": "Hasil indak ditamuan karano kriteria pancariannyo batantangan",
+       "rcfilters-state-message-subset": "Filter iko indak akan bapangaruh karano hasilnyo disaratoan jo {{PLURAL:$2|}} (cubo caliak untuak mambedaannyo):$1",
+       "rcfilters-state-message-fullcoverage": "Mamiliah sado panyaringan dalam kalompok iko samo jo indak mamiliah apopun, sahinggo panyaringan ko indak mambarikan hasil. Kalompok itu: $1",
+       "rcfilters-filtergroup-authorship": "Kontribusi panulih",
        "rcfilters-filter-editsbyself-label": "Suntiangan Sanak",
+       "rcfilters-filter-editsbyself-description": "Kontribusi Sanak",
        "rcfilters-filter-editsbyother-label": "Suntiangan urang lain",
+       "rcfilters-filter-editsbyother-description": "Sadoalah parubahan kacuali parubahan miliak surang.",
+       "rcfilters-filtergroup-user-experience-level": "Pandaftaran jo pangalaman pangguno",
        "rcfilters-filter-user-experience-level-registered-label": "Tadaftar",
+       "rcfilters-filter-user-experience-level-registered-description": "Panyuntiang masuak log",
        "rcfilters-filter-user-experience-level-unregistered-label": "Indak tadaftar",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Panyuntiang nan indak masuak log",
        "rcfilters-filter-user-experience-level-newcomer-label": "Pandatang baru",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Panyuntiang tadaftar nan mampunyoi suntiangan kurang dari 10 suntiangan jo aktivitas salamo 4 ari.",
+       "rcfilters-filter-user-experience-level-learner-label": "Urang baru",
+       "rcfilters-filter-user-experience-level-learner-description": "Panyuntiang tadaftar nan pangalamannyo barado antaro \"pangguno baru\" jo \"pangguno bapangalaman\".",
+       "rcfilters-filter-user-experience-level-experienced-label": "Pangguno bapangalaman",
+       "rcfilters-filter-user-experience-level-experienced-description": "Panyuntiang tadaftar jo labiah dari 500 suntiangan sarato aktivitas salamo 30 ari.",
+       "rcfilters-filtergroup-automated": "Kontribusi otomatis",
        "rcfilters-filter-bots-label": "Bot",
+       "rcfilters-filter-bots-description": "Suntiangan nan dibuek jo pakakeh taotomatisasi.",
        "rcfilters-filter-humans-label": "Manusio (bukan bot)",
+       "rcfilters-filter-humans-description": "Suntiangan nan dibuek dek panyuntiang manusia.",
+       "rcfilters-filtergroup-reviewstatus": "Status paninjauan",
+       "rcfilters-filter-reviewstatus-unpatrolled-description": "Suntiangan nan indak ditandoi tapatroli, baiak sacaro manual atau otomatis.",
        "rcfilters-filter-reviewstatus-unpatrolled-label": "Alun dipatroli",
+       "rcfilters-filter-reviewstatus-manual-description": "Suntiangan nan sacaro manual ditandoi tapatroli",
+       "rcfilters-filter-reviewstatus-manual-label": "Tapatroli manual",
+       "rcfilters-filter-reviewstatus-auto-description": "Suntiangan dek pangguno bapangalaman nan kontribusinyo sacaro otomatis ditandoi sabagai tapatroli",
+       "rcfilters-filter-reviewstatus-auto-label": "Otomatis tapatroli",
+       "rcfilters-filtergroup-significance": "Signifikan",
        "rcfilters-filter-minor-label": "Suntiangan ketek",
+       "rcfilters-filter-minor-description": "Suntiangan nan ditandoi panyuntiang sabagai suntiangan ketek",
        "rcfilters-filter-major-label": "Nan indak suntiangan ketek",
+       "rcfilters-filter-major-description": "Suntiangan nan ditandoi sabagai suntiangan ketek",
+       "rcfilters-filtergroup-watchlist": "Halaman nan dipantau",
+       "rcfilters-filter-watchlist-watched-label": "Daftar pantau",
+       "rcfilters-filter-watchlist-watched-description": "Parubahan pado laman dalam pantauan Sanak.",
+       "rcfilters-filter-watchlist-watchednew-label": "Parubahan Daftar Pantauan baru.",
+       "rcfilters-filter-watchlist-watchednew-description": "Parubahan laman-laman nan dipantau nan alun disinggahi dek Sanak sajak parubahan tajadi.",
+       "rcfilters-filter-watchlist-notwatched-label": "Indak dalam daftar pantauan",
+       "rcfilters-filter-watchlist-notwatched-description": "Sadonyo salain parubahan pado laman nan dipantau.",
+       "rcfilters-filtergroup-watchlistactivity": "Aktivitas daftar pantauan",
+       "rcfilters-filter-watchlistactivity-unseen-label": "Parubahan nan alun dicaliak",
+       "rcfilters-filter-watchlistactivity-unseen-description": "Parubahan laman-laman nan dipantau nan alun disinggahi dek Sanak sajak parubahan tajadi.",
+       "rcfilters-filter-watchlistactivity-seen-label": "Parubahan nan alah tacaliak",
+       "rcfilters-filter-watchlistactivity-seen-description": "Parubahan laman-laman nan disinggahi Sanak sajak parubahan tajadi.",
+       "rcfilters-filtergroup-changetype": "Jinih parubahan",
        "rcfilters-filter-pageedits-label": "Suntiangan laman",
+       "rcfilters-filter-pageedits-description": "Parubahan pado konten wiki, rundiang, deskripsi kategori...",
        "rcfilters-filter-newpages-label": "Laman baru",
+       "rcfilters-filter-newpages-description": "Suntiangan nan mambuek laman baru",
        "rcfilters-filter-categorization-label": "Parubahan kategori",
+       "rcfilters-filter-categorization-description": "Rakam jajak laman nan alah ditambahkan atau diapuih dari kategori.",
        "rcfilters-filter-logactions-label": "Tindakan tacataik",
+       "rcfilters-filter-logactions-description": "Tindakan administratif, pambuatan akun, pangapusan halaman, pangunggahan...",
+       "rcfilters-hideminor-conflicts-typeofchange": "Jinih parubahan tatantu indak dapek ditandoi sabagai \"ketek\", jadi panyariang ko bakonflik jo panyariang Jinih Parubahan iko: $1",
+       "rcfilters-typeofchange-conflicts-hideminor": "Jenih parubahann ko berkonflik jo \"Suntiangan ketek\". Jenih parubahan tatantu indak dapek ditandoi sabagai \"ketek\".",
+       "rcfilters-filtergroup-lastrevision": "Revisi tabaru",
        "rcfilters-filter-lastrevision-label": "Revisi tabaru",
        "rcfilters-filter-lastrevision-description": "Anyo parubahan tabaru pado laman ko.",
+       "rcfilters-filter-previousrevision-label": "Bukan revisi tabaru",
+       "rcfilters-filter-previousrevision-description": "Sadoalah parubahan nan bukan marupokan \"revisi tabaru\".",
+       "rcfilters-filter-excluded": "Indak tamasuak",
+       "rcfilters-tag-prefix-namespace-inverted": "<strong>:bukan</strong> $1",
+       "rcfilters-exclude-button-off": "Salain nan tapiliah",
+       "rcfilters-exclude-button-on": "Salain nan tapiliah",
        "rcfilters-view-tags": "Suntiangan ditandoi",
+       "rcfilters-view-namespaces-tooltip": "Sariang hasil manuruik ruangnamo",
+       "rcfilters-view-tags-tooltip": "Pisahan hasil manggonoan panyuntingan",
+       "rcfilters-view-return-to-default-tooltip": "Kembali ka manu panyaringan utamo",
+       "rcfilters-view-tags-help-icon-tooltip": "Palajari labiah lanjuik tantangan suntiangan jo TAG",
        "rcfilters-liveupdates-button": "Parubahan langsuang",
        "rcfilters-liveupdates-button-title-on": "Matian parubahan langsuang",
+       "rcfilters-liveupdates-button-title-off": "Tampilan parubahan baru katiko parubahan tu alah tajadi",
+       "rcfilters-watchlist-markseen-button": "Tandoi sadonyo parubahan sabagai taliek",
        "rcnotefrom": "Di bawah iko adolah {{PLURAL:$5|parubahan|babagai parubahan}} sajak <strong>$3, $4</strong> (ditampilkan sampai <strong>$1</strong> parubahan).",
        "rclistfrom": "Tunjuakan parubahan baru mulai dari tanggal $3 $2",
        "rcshowhideminor": "$1 suntiangan ketek",
index 63b7e65..55a05eb 100644 (file)
        "listfiles-userdoesnotexist": "ഉപയോക്തൃ അംഗത്വം \"$1\" എടുക്കപ്പെട്ടിട്ടില്ല.",
        "imgfile": "പ്രമാണം",
        "listfiles": "പ്രമാണങ്ങളുടെ പട്ടിക",
+       "listfiles_subpage": "$1 ചെയ്ത അപ്‌ലോഡുകൾ",
        "listfiles_thumb": "ലഘുചിത്രം",
        "listfiles_date": "തീയതി",
        "listfiles_name": "പേര്",
index e421252..105f3d3 100644 (file)
        "errorpagetitle": "Sbaglio",
        "returnto": "Torna a $1.",
        "tagline": "'A {{SITENAME}}.",
-       "help": "Ajùto",
+       "help": "Ajuto",
        "help-mediawiki": "Ajuto ncopp' 'a MediaWiki",
-       "search": "Truova",
+       "search": "Cerca",
        "search-ignored-headings": " #<!-- lassa sta linea comme sta --> <pre>\n# Testate ca se sarranno gnurate int' 'a ricerca.\n# Cagnamiente a chesto addeventarranno affettive quanno 'a paggena sarrà innecizzata.\n# Vuje putite forzà 'a reinnecezzazzione d' 'a paggena facenno nu cagnamiento abbacante.\n# 'A sintasse è 'a seguente:\n#   * Ogneccosa 'a 'o carattere \"#\" 'nzegna 'a fine d' 'a linea è 'nu cummanno\n#   * Ogne linea chiena è 'o titolo esatto 'a gnurà, case e tutteccose\nRiferimente\nJonte 'e fore\nVide pure\n #</pre> <!-- lassa sta linea comme sta  -->",
-       "searchbutton": "Truova",
+       "searchbutton": "Cerca",
        "go": "Vàje",
-       "searcharticle": "Vàje",
+       "searcharticle": "Vaje",
        "history": "Verziune 'e primma",
        "history_short": "Cronologgia",
        "history_small": "cronologgia",
        "updatedmarker": "cagnamiénte 'e ll'urdema visita d' 'a mia",
-       "printableversion": "Verzione pe' stampa",
+       "printableversion": "Verzione p''a stampa",
        "permalink": "Jonta permanente",
        "print": "Stampà",
        "view": "Vire",
        "view-foreign": "Vide ncopp'a $1",
-       "edit": "Càgna",
+       "edit": "Cagna",
        "edit-local": "Càgna descrizione lucale",
        "create": "Crèa",
        "create-local": "Azzecca descrizione lucale",
        "protect": "Prutegge",
        "protect_change": "càgna",
        "unprotect": "Càgna prutezzione",
-       "newpage": "Paggena nòva",
-       "talkpagelinktext": "Chiàcchiera",
+       "newpage": "Paggena nova",
+       "talkpagelinktext": "chiacchiera",
        "specialpage": "Paggena speciàle",
-       "personaltools": "Strumiente perzonale",
-       "talk": "Chiàcchiera",
+       "personaltools": "Strumente perzonale",
+       "talk": "Chiacchiera",
        "views": "Visite",
-       "toolbox": "Strumiente",
+       "toolbox": "Strumente",
        "tool-link-userrights": "Càgna gruppe {{GENDER:$1|utente}}",
        "tool-link-userrights-readonly": "Vire gruppe {{GENDER:$1|utente}}",
        "tool-link-emailuser": "Manna na masciata email a st'{{GENDER:$1|utente}}",
        "viewcount": "Chesta paggena è stata liggiùta {{PLURAL:$1|una vòta|$1 vòte}}.",
        "protectedpage": "Paggena prutetta",
        "jumpto": "Vaje a:",
-       "jumptonavigation": "navigazione",
-       "jumptosearch": "truova",
+       "jumptonavigation": "navigazzione",
+       "jumptosearch": "cerca",
        "view-pool-error": "Ve cercammo scusa, 'e servers hanno troppo carico mo'.\nTroppe utente stanno cercanno 'e veré sta paggena.\nPe' piacere, aspettate nu poco primma 'e turnà a carrecà sta paggena.\n\n$1",
        "generic-pool-error": "Ve cercammo scusa, 'e servers hanno troppo carico mo'.\nTroppe utente stanno cercanno 'e veré sta risorsa.\nPe' piacere, aspettate nu poco primma 'e turnà a carrecà sta risorsa.",
        "pool-timeout": "Tiempo pe' s'aspettà ô blocco",
        "pool-errorunknown": "Errore scanusciuto",
        "pool-servererror": "'O servizio contatore d''e fatiche nun è a disposizióne ($1).",
        "poolcounter-usage-error": "Errore d'uso: $1",
-       "aboutsite": "'Nfrummazione ncòpp'a {{SITENAME}}",
-       "aboutpage": "Project:'Nfrummazione",
+       "aboutsite": "'Nfurmazzione ncòpp''a {{SITENAME}}",
+       "aboutpage": "Project:'Nfurmazzione",
        "copyright": "Cuntenute suggiette a licienza 'e auso $1 se nun fuje ritto atro.",
        "copyrightpage": "{{ns:project}}:Copyrights",
        "currentevents": "Nuvità",
        "currentevents-url": "Project:Novità",
-       "disclaimers": "Avvertimiènte",
-       "disclaimerpage": "Project:Avvertimiènte generale",
+       "disclaimers": "Avvertimiente",
+       "disclaimerpage": "Project:Avvertimiente generale",
        "edithelp": "Guida",
        "helppage-top-gethelp": "Ajùto",
        "mainpage": "Paggena prencepale",
        "mainpage-description": "Paggena prencepale",
        "policy-url": "Project:Policy",
-       "portal": "Porta d’'a commonetà",
-       "portal-url": "Project:Porta d''a commonetà",
-       "privacy": "'Nformazzione ppe a privacy",
-       "privacypage": "Project:'Nfrummazione ncopp'â privacy",
+       "portal": "Porta d''a communetà",
+       "portal-url": "Project:Porta d''a communetà",
+       "privacy": "'Nfurmazzione p''a privacy",
+       "privacypage": "Project:'Nfurmazzione p''a privacy",
        "badaccess": "Nun aie bastante licenzia",
        "badaccess-group0": "Nun tiene 'a licenzia pe ffà l'azione richiesta.",
        "badaccess-groups": "L'azione ch'ê addemmannato 'a pô ffà sulamente ll'utente ca stanno dint'a {{PLURAL:$2|'o gruppo|uno d' 'e gruppe}}: $1.",
        "newmessageslinkplural": "{{PLURAL:$1|na mmasciata nova|999=mmasciate nnove}}",
        "newmessagesdifflinkplural": "{{PLURAL:$1|urdemo cagnamiento|999=urdeme cagnamiente}}",
        "youhavenewmessagesmulti": "Tiene nuove mmasciate $1",
-       "editsection": "càgna",
+       "editsection": "cagna",
        "editold": "càgna",
        "viewsourceold": "vire sorgente",
        "editlink": "càgna",
        "site-atom-feed": "Feed Atom 'e $1",
        "page-rss-feed": "Feed RSS pe' \"$1\"",
        "page-atom-feed": "Feed Atom ppe \"$1\"",
-       "red-link-title": "$1 ('a paggena nun esiste)",
+       "red-link-title": "$1 (sta paggena nun esiste)",
        "sort-descending": "Urdinamento dicriscente",
        "sort-ascending": "Urdinamento criscente",
        "nstab-main": "Articulo",
        "loginlanguagelabel": "Lengua: $1",
        "suspicious-userlogout": "'A richiesta 'e disconnessione d' 'a toja è stata negate pecché pare ca fosse mannata 'a nu navigatóre rutto o nu proxy 'e \"caching\".",
        "createacct-another-realname-tip": "'O nomme overo vuosto è ozzionale.\nSi sciglite 'e nzertà 'o nomme overo, chesto s'ausarrà pe' dà l'utente l'attribuzione d' 'a fatica fatta.",
-       "pt-login": "Tràse",
+       "pt-login": "Trase",
        "pt-login-button": "Tràse",
        "pt-login-continue-button": "Và annanze e tràse",
-       "pt-createaccount": "Crèa nu cunto nuovo",
+       "pt-createaccount": "Crea 'nu cunto nuovo",
        "pt-userlogout": "Jèsce",
        "php-mail-error-unknown": "Errore scanusciuto dint'a funzione PHP mail()",
        "user-mail-no-addy": "Avite cercato 'e mannà na mmasciata e-mail senza indirizzo.",
        "shown-title": "Fa verè {{PLURAL:$1|nu risultato|$1 risultate}} pe paggena",
        "viewprevnext": "Vire ($1 {{int:pipe-separator}} $2) ($3).",
        "searchmenu-exists": "'''Ncopp' 'o sito esiste na paggena c' 'o nomme \"[[:$1]]\"'''\n{{PLURAL:$2|0=|Vedite pure dint'a l'ati risultate 'e cerca.}}",
-       "searchmenu-new": "<strong>'''Crèa 'a paggena \"[[:$1]]\" ncopp'a stu wiki!'''</strong> {{PLURAL:$2|0=|Vedite pure 'a paggena truvata c' 'a recerca vuosta|Vedite pure 'e risultate d\"a recerca}}",
+       "searchmenu-new": "<strong>'''Crèa 'a paggena \"[[:$1]]\" ncopp'a sta wiki!'''</strong> {{PLURAL:$2|0=|Vedite pure 'a paggena truvata c' 'a recerca vuosta|Vedite pure 'e risultate d' 'a recerca}}",
        "searchprofile-articles": "Paggene 'e contenute",
        "searchprofile-images": "Multimedia",
        "searchprofile-everything": "Tutto",
        "right-suppressionlog": "Vide 'e riggistre private",
        "right-block": "Nun fa fa' cagnamienti a ll'at'utente",
        "right-blockemail": "Nun fa mannà e-mail a n'utente",
-       "right-hideuser": "Blocca n'utente e fallo sparì 'a 'o pubbreco",
+       "right-hideuser": "Fremma n'utente e annascunnillo r'o pubbreco",
        "right-ipblock-exempt": "Ignora 'e blocche 'e l'IP, 'e blocche automatece e li blocche 'e range 'e l'IP",
        "right-unblockself": "Sblocca se stesso",
        "right-protect": "Cagna 'e livelle 'e prutezione 'e cagna paggene prutette ricurzivamente",
        "right-mergehistory": "Aunisce 'a cronologgia d' 'e paggene",
        "right-userrights": "Cagna 'e deritte 'e ll'utente",
        "right-userrights-interwiki": "Cagna 'e deritte 'e ll'utente int'a l'ati wiki",
-       "right-siteadmin": "Blocca e sblocca 'o database",
+       "right-siteadmin": "Chiure e arape 'o database",
        "right-override-export-depth": "Esporta 'e paggene azzeccanno 'e paggene cullegate nfin'a na profondità 'e 5",
        "right-sendemail": "Manna na mail a ll'at'utente",
        "right-managechangetags": "Crìa e appiccia/stuta 'e [[Special:Tags|tag]]",
        "nchanges": "$1 {{PLURAL:$1|cagnamiento|cagnamiente}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|'a ll'urdema visita}}",
        "enhancedrc-history": "cronologgia",
-       "recentchanges": "Urdeme nove",
+       "recentchanges": "Urdeme cagnamiente",
        "recentchanges-legend": "Opzione urdeme cagnamiénte",
        "recentchanges-summary": "Ncopp'a chesta paggena song' appresentate ll'urdeme cagnamiente fatte ê cuntenute d\"o sito.",
        "recentchanges-noresult": "Nisciuno cagnamiento dint'o periodo dato ca soddisfà sti criterie.",
        "minoreditletter": "m",
        "newpageletter": "N",
        "boteditletter": "b",
-       "rc-change-size-new": "$1 {{PLURAL:$1|byte|byte}} aropp'ô cagnamiento",
+       "rc-change-size-new": "$1 {{PLURAL:$1|byte}} aropp''o cagnamiento",
        "newsectionsummary": "/* $1 */ sezziona nnova",
        "rc-enhanced-expand": "Fa vede dettaglie",
        "rc-enhanced-hide": "Annascunne dettaglie",
        "recentchanges-page-removed-from-category": "[[:$1]] luvato d' 'a categurìa",
        "recentchanges-page-removed-from-category-bundled": "[[:$1]]  luvate 'e categurìa, [[Special:WhatLinksHere/$1|sta paggena è azzeccata dint'a n'ati paggene]]",
        "autochange-username": "Cagnamiento automateco MediaWiki",
-       "upload": "Carreca file",
+       "upload": "Careca 'na fiùra",
        "uploadbtn": "Carreca file",
        "reuploaddesc": "Torna a 'o modulo pe ffà 'a carreca",
        "upload-tryagain": "Manna 'a descrizione d' 'o file cagnato",
        "uploadlogpage": "Riggistro 'e carreche",
        "uploadlogpagetext": "Ccà abbascio song'alencate l'urdeme file carrecate.\nCuntrullate 'a [[Special:NewFiles|gallaria d' 'e file nuove]] pe' ve ffà na guardata cchiù visuale 'e tutto.",
        "filename": "Nomme d' 'o file",
-       "filedesc": "Énnece",
+       "filedesc": "Riepilego",
        "fileuploadsummary": "Dettaglie:",
        "filereuploadsummary": "Cagnamiente a 'o file:",
        "filestatus": "Stato d' 'o copyright:",
        "sp-contributions-newonly": "Sulamente 'e contribbute ca songo criazione 'e paggene",
        "sp-contributions-hideminor": "Annascunne cagnamiénte piccerille",
        "sp-contributions-submit": "Truova",
-       "whatlinkshere": "Paggene ca cullegano a chesta",
+       "whatlinkshere": "Paggene ca se cullegano a chesta",
        "whatlinkshere-title": "Paggene ca cullegano a $1",
        "whatlinkshere-page": "Paggena:",
        "linkshere": "'E ppaggene ccà abbascio teneno jonte a <strong>$2</strong>.",
        "tooltip-pt-watchlist": "'A lista d' 'e paggene ca state a cuntrullà",
        "tooltip-pt-mycontris": "N'elenco 'e cuntribbute {{GENDER:|vòste}}",
        "tooltip-pt-anoncontribs": "N'elenco 'e cagnamiente fatte 'a st'indirizzo IP",
-       "tooltip-pt-login": "A reggistrazione è cunsigliata",
+       "tooltip-pt-login": "Ve cunsigliamme 'e trasì dint'o cunto vostro, ma nun l'avite a ffà pe' fforza",
        "tooltip-pt-logout": "Jésce (logout)",
-       "tooltip-pt-createaccount": "Pigliateve curaggio e criate n'utente e trasìte; eziamme ca chisto nun s'avesse 'a ffà pe' fforza",
-       "tooltip-ca-talk": "Vide 'e chiacchere rilative a chesta paggena",
+       "tooltip-pt-createaccount": "Ve cunsigliamme 'e ve reggistrà 'nu cunte e trasì, ma nun l'avite a ffà pe' fforza",
+       "tooltip-ca-talk": "Vide 'e chiacchere 'e chesta paggena",
        "tooltip-ca-edit": "Cagna sta paggena",
        "tooltip-ca-addsection": "Cummincia 'na nova sezzione",
        "tooltip-ca-viewsource": "Chista paggena è prutetta, ma può verè 'o codice sorgente",
-       "tooltip-ca-history": "Vversione 'e primma 'e chesta paggena",
+       "tooltip-ca-history": "Verzione 'e primma 'e chesta paggena",
        "tooltip-ca-protect": "Prutegge chesta paggena",
        "tooltip-ca-unprotect": "Càgna 'a prutezzione 'e chesta paggena",
        "tooltip-ca-delete": "Scancèlla chesta paggena",
        "tooltip-ca-move": "Mòve sta paggena",
        "tooltip-ca-watch": "Azzecca sta paggena int' 'a lista 'e paggene cuntrullate vuosta",
        "tooltip-ca-unwatch": "Lèva sta paggena d' 'a lista 'e paggene cuntrullate vuosta",
-       "tooltip-search": "Truova dint'a {{SITENAME}}",
-       "tooltip-search-go": "Vaje â paggena cu stu nomme si esiste",
-       "tooltip-search-fulltext": "Ascià 'o testo indicato dint'e paggene",
+       "tooltip-search": "Cerca dint''a {{SITENAME}}",
+       "tooltip-search-go": "Vaje a 'na paggena cu stu nomme, si ce sta",
+       "tooltip-search-fulltext": "Cerca chistu testo dint''e paggene",
        "tooltip-p-logo": "Visita a paggena prencepale",
        "tooltip-n-mainpage": "Visita a paggena prencepale",
        "tooltip-n-mainpage-description": "Visita a paggena prencepale",
-       "tooltip-n-portal": "Descrizione d' 'o prugietto, che po' ffa, addò truvà 'e ccose",
+       "tooltip-n-portal": "Descrizione d''o prugietto, che putite fà e addò putite truvà 'e ccose",
        "tooltip-n-currentevents": "Ascìa 'e nfurmaziune ncopp' 'e fatte succiesse mo mmo",
-       "tooltip-n-recentchanges": "Ennece dde urdeme cagnamiénte d' 'o sito",
-       "tooltip-n-randompage": "Na paggena qualsiase",
-       "tooltip-n-help": "Paggena 'e ajùto",
-       "tooltip-t-whatlinkshere": "'Na lista 'e tutte e paggene ca song cullegate a chista",
+       "tooltip-n-recentchanges": "Ennece 'e ll'urdeme cagnamiente d''o sito",
+       "tooltip-n-randompage": "Careca 'na paggena qualonca",
+       "tooltip-n-help": "Paggena 'e ajuto",
+       "tooltip-t-whatlinkshere": "Ennece 'e tutte e paggene ca so' cullegate a chesta",
        "tooltip-t-recentchangeslinked": "Urdeme cagnamiénte dde paggene ca cullegano a chesta",
        "tooltip-feed-rss": "RSS feed pe sta pàggena",
        "tooltip-feed-atom": "Atom feed pe sta pàggena",
        "tooltip-t-contributions": "Lista dde contributte fatte 'a {{GENDER:$1|chist'utente}}",
        "tooltip-t-emailuser": "Manna 'nu email a {{GENDER:$1|chist'utente}}",
        "tooltip-t-info": "Cchiù nfurmaziune ncopp'a sta paggena",
-       "tooltip-t-upload": "Carreca file",
-       "tooltip-t-specialpages": "Lista 'e tutte e paggene speciale",
-       "tooltip-t-print": "Vversione pe stampà 'a chesta paggena",
+       "tooltip-t-upload": "Careca cchiù fiùre",
+       "tooltip-t-specialpages": "Lista 'e tutte 'e paggene speciale",
+       "tooltip-t-print": "Verzione p''a stampa 'e chesta paggena",
        "tooltip-t-permalink": "Jonta permanente a chesta vversione dda paggena",
        "tooltip-ca-nstab-main": "Vire 'a paggena 'e contenuto",
        "tooltip-ca-nstab-user": "Vire 'a paggena utente",
        "pageinfo-hidden-categories": "{{PLURAL:$1|Categurìa annascunnuta|Categurìe annascunnute}} ($1)",
        "pageinfo-templates": "Template {{PLURAL:$1|appennuto|appennute}} ($1)",
        "pageinfo-transclusions": "{{PLURAL:$1|Paggena appennuta|Paggene appennute}} ncopp'a ($1)",
-       "pageinfo-toolboxlink": "Nfurmaziune d' 'a paggena",
+       "pageinfo-toolboxlink": "'Nfurmazzione d''a paggena",
        "pageinfo-redirectsto": "Reindirizza a",
        "pageinfo-redirectsto-info": "nfurmaziune",
        "pageinfo-contentpage": "Cuntata comme na paggena 'e cuntenute",
        "feedback-thanks": "Grazie! 'O feedback vuosto s'è mpizzato dint' 'a paggena \"[$2 $1]\".",
        "feedback-thanks-title": "Ve ringraziammo!",
        "feedback-useragent": "Aggente utente:",
-       "searchsuggest-search": "Truova dint'a {{SITENAME}}",
+       "searchsuggest-search": "Cerca dint''a {{SITENAME}}",
        "searchsuggest-containing": "tène...",
        "api-error-badtoken": "Errore interno: 'O token nun è buono.",
        "api-error-emptypage": "'A criazione 'e paggene nuove abbacante nun è permessa.",
index c41fa88..e2ff083 100644 (file)
        "createaccountmail": "Bruk et midlertidig, tilfeldig passord, og send det til angitt e-postadresse",
        "createaccountmail-help": "Kan brukes til å opprette en konto for en annen person uten at du får vite passordet.",
        "createacct-realname": "Virkelig navn (valgfritt)",
-       "createacct-reason": "Årsak",
+       "createacct-reason": "Årsak (logges offentlig)",
        "createacct-reason-ph": "Hvorfor lager du en annen bruker",
        "createacct-reason-help": "Beskjed vist i kontoopprettelsesloggen",
        "createacct-submit": "Opprett konto",
        "listfiles-userdoesnotexist": "Brukerkontoen «$1» er ikke registrert.",
        "imgfile": "fil",
        "listfiles": "Filliste",
+       "listfiles_subpage": "Opplastinger av $1",
        "listfiles_thumb": "Miniatyrbilde",
        "listfiles_date": "Dato",
        "listfiles_name": "Navn",
        "ipblocklist-legend": "Finn en blokkert bruker",
        "blocklist-userblocks": "Skjul kontoblokkeringer",
        "blocklist-tempblocks": "Skjul midlertidige blokkeringer",
+       "blocklist-indefblocks": "Skjul blokkeringer på ubestemt tid",
        "blocklist-addressblocks": "Skjul individuelle IP-blokkeringer",
        "blocklist-type": "Type:",
        "blocklist-type-opt-all": "Alle",
        "mycustomjsredirectprotected": "Du har ikke tillatelse til å redigere denne JavaScript-siden fordi den er en omdirigering og ikke peker til en annen side i ditt brukernavnerom.",
        "easydeflate-invaliddeflate": "Det gitte innholdet er ikke riktig komprimert",
        "unprotected-js": "Av sikkerhetsårsaker kan ikke JavaScript lastes fra ubeskyttede sider. Bare skap JavaScript i MediaWiki-navnerommet eller som en brukerunderside",
-       "userlogout-continue": "Ønsker du å logge ut?"
+       "userlogout-continue": "Ønsker du å logge ut?",
+       "rest-prefix-mismatch": "Den forespurte stien ($1) var ikke i REST-API-ets rotsti ($2)",
+       "rest-wrong-method": "Den forespurte metoden ($1) var ikke {{PLURAL:$3|den tillatte metoden|blant de tillatte metodene}} for denne stien ($2)",
+       "rest-no-match": "Den forespurte relative stien ($1) passet ikke med noen kjent behandler"
 }
index d7e5a1c..91f6f1e 100644 (file)
@@ -44,7 +44,7 @@
        "tog-enotifusertalkpages": "Stüür my een bericht as myn oaverlegsyde wysigd is.",
        "tog-enotifminoredits": "Stüür my ouk een bericht by kleine bewarkingen van syden en bestanden",
        "tog-enotifrevealaddr": "Myn e-postadres låten seen in e-postberichten",
-       "tog-shownumberswatching": "Et antal brukers bekyken dee disse syde volgt",
+       "tog-shownumberswatching": "Et antal gebrukers bekyken dee disse syde volgt",
        "tog-oldsig": "Bestånde handteykening:",
        "tog-fancysig": "Underteykening seen as wikitekst (sunder automatiske verwysing)",
        "tog-uselivepreview": "Nåkyksyde låten seen sunder eyrst te herladen",
        "tog-watchlisthideown": "Verbarg myn eigen bewarkingen",
        "tog-watchlisthidebots": "Verbarg botbrukers",
        "tog-watchlisthideminor": "Verbarg kleine wysigingen in myn volglyste",
-       "tog-watchlisthideliu": "Bewarkingen van anmeldede brukers up myn volglyste verbargen",
-       "tog-watchlisthideanons": "Bewarkingen van anonyme brukers up myn volglyste verbargen",
+       "tog-watchlisthideliu": "Bewarkingen van anmeldede gebrukers up myn volglyste verbargen",
+       "tog-watchlisthideanons": "Bewarkingen van anonyme gebrukers up myn volglyste verbargen",
        "tog-watchlisthidepatrolled": "Wysigingen dee markeerd binnet up volglyste verbargen",
-       "tog-ccmeonemails": "Stüür my kopien van berichten an andere brukers",
+       "tog-ccmeonemails": "Stüür my kopyen van berichten an andere gebrukers",
        "tog-diffonly": "Under wysigingen neet de syde-inhold låten seen.",
        "tog-showhiddencats": "Låt verbörgen kategoryen seen",
        "tog-norollbackdiff": "Wysigingen vordlåten nå et weaderümmedraien",
        "tog-useeditwarning": "Wårschüw my as ik een bewarkede syde afsluten wil dee noch neet seakerd is",
-       "tog-prefershttps": "Altyd een beveiligde verbinding bruken as jy anmelded binnet",
+       "tog-prefershttps": "Altyd een beveiligde verbinding gebruken as jy anmelded binnet",
        "underline-always": "Altyd",
        "underline-never": "Nooit",
        "underline-default": "Standard in juw formgeaving of webkyker",
        "cancel": "Afbreaken",
        "moredotdotdot": "Meyr...",
        "morenotlisted": "Disse lyste is möägelik neet kompleet.",
-       "mypage": "Brukerssyde",
-       "mytalk": "Myn oaverleg",
+       "mypage": "Gebrukerssyde",
+       "mytalk": "Oaverleg",
        "anontalk": "Oaverleg",
        "navigation": "Navigaty",
        "and": "&#32;en",
        "views": "Weadergåven",
        "toolbox": "Warktügen",
        "tool-link-userrights": "{{GENDER:$1|Brukersgruppen}} wysigen",
-       "tool-link-emailuser": "Disse {{GENDER:$1|bruker}} een bericht stüren",
+       "tool-link-emailuser": "Disse {{GENDER:$1|gebruker}} een bericht stüren",
        "imagepage": "Bestandssyde bekyken",
        "mediawikipage": "Berichtsyde bekyken",
        "templatepage": "Mal bekyken",
        "privacypage": "Project:Gegeavensbeleid",
        "badaccess": "Geen tostemming",
        "badaccess-group0": "Jy hebbet geen tostemming üm disse akty uut te voren.",
-       "badaccess-groups": "Disse akty kan allinnig uutvoord wörden döär brukers uut {{PLURAL:$2|de grup|eyn van de gruppen}}: $1.",
+       "badaccess-groups": "Disse akty kan allinnig uutvoord wörden döär gebrukers uut {{PLURAL:$2|de grup|eyn van de gruppen}}: $1.",
        "versionrequired": "Versy $1 van MediaWiki is nöydig",
-       "versionrequiredtext": "Versy $1 van MediaWiki is nöydig üm disse syde te bruken. See [[Special:Version|Versy]].",
+       "versionrequiredtext": "Versy $1 van MediaWiki is nöydig üm disse syde te gebruken. See [[Special:Version|Versy]].",
        "ok": "Okee",
        "retrievedfrom": "Van \"$1\"",
        "youhavenewmessages": "{{PLURAL:$3|Jy hebbet}} $1 ($2).",
-       "youhavenewmessagesfromusers": "{{PLURAL:$4|Jy hebbet}} $1 van {{PLURAL:$3|een andere bruker|$3 brukers}} ($2).",
-       "youhavenewmessagesmanyusers": "Jy hebbet $1 van een bült brukers ($2).",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|Jy hebbet}} $1 van {{PLURAL:$3|een andere gebruker|$3 gebrukers}} ($2).",
+       "youhavenewmessagesmanyusers": "Jy hebbet $1 van een bült gebrukers ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|een ny bericht|999=nye berichten}}",
        "newmessagesdifflinkplural": "läste {{PLURAL:$1|wysiging|999=wysigingen}}",
        "youhavenewmessagesmulti": "Jy hebbet nye berichten up $1",
        "sort-descending": "Afloupend sorteren",
        "sort-ascending": "Uploupend sorteren",
        "nstab-main": "Syde",
-       "nstab-user": "Brukerssyde",
+       "nstab-user": "Gebrukerssyde",
        "nstab-media": "Mediasyde",
        "nstab-special": "Speciale syde",
        "nstab-project": "Projektsyde",
        "delete-hook-aborted": "Et vordsmyten wördt in et wyre skopped döär een topassing van MediaWiki.\nDer is wyder geen informaty beskikbår.",
        "no-null-revision": "Kun geen leadige nye versy maken vöär de syde \"$1\"",
        "badtitle": "Ungeldige name",
-       "badtitletext": "De name van de upvrågde syde is neet geldig, leadig, of der stünd een verkeyrde interspråk- of interwikiverwysing in.\nMöägelik binnet der eyn of meyr teykens bruked dee neet in titels tostån binnet.",
+       "badtitletext": "De name van de upvrågde syde is neet geldig, leadig, of der stünd een verkeyrde interspråk- of interwikiverwysing in.\nMöägelik binnet der eyn of meyr teykens gebruked dee neet in titels tostån binnet.",
        "perfcached": "Disse gegeavens kummen uut et tüskengehöägen en binnet meskeen neet aktueel. Der {{PLURAL:$1|is houguut eyn resultaat|binnet houguut $1 resultaten}} beskikbår in et tüskengehöägen.",
        "perfcachedts": "Disse gegeavens kummen uut et tüskengehöägen dee vöär et lätst bywarked is up $2 üm $3. Der {{PLURAL:$4|is houguut eyn resultaat|binnet houguut $4 resultaten}} beskikbår in et tüskengehöägen.",
        "querypage-no-updates": "Disse syde wördt neet bywarked.\nGegeavens up disse syde wördet neet vervarsd.",
        "protectedpagetext": "Disse syde is beveiligd. Bewarken of andere handelingen binnet neet möägelik.",
        "viewsourcetext": "Jy künnet de brontekst van disse syde bewarken en bekyken.",
        "viewyourtext": "Jy künnet <strong>juw bewarkingen</strong> an de brontekst van disse syde bekyken en kopieren.",
-       "protectedinterface": "Up disse syde steyt tekst dee bruked wördt vöär systeemteksten van disse wiki, en is beveiligd üm misbruuk te vöärkommen. Bruuk [https://translatewiki.net/ translatewiki.net], et lokaliseringsprojekt vöär MediaWiki, üm oaversetingen vöär alle wikis to te vogen of te wysigen.",
-       "editinginterface": "<strong>Wårsküwing:</strong> jy bewarket een syde dee teksten bruukt vöär de brukersümgeaving van de programmatuur. \nWat jy up disse syde wysigen is van invlööd up de brukersümgeaving van andere brukers van disse wiki.",
-       "translateinterface": "Üm oaversettingen vöär alle wikis to te vogen of te wysigen, kün jy [https://translatewiki.net/ translatewiki.net] bruken, et lokaliseringsprojekt vöär MediaWiki.",
+       "protectedinterface": "Up disse syde steyt tekst dee gebruked wördt vöär systeemteksten van disse wiki, en is beveiligd üm misbruuk te vöärkommen. Bruuk [https://translatewiki.net/ translatewiki.net], et lokaliseringsprojekt vöär MediaWiki, üm oaversetingen vöär alle wikis to te vogen of te wysigen.",
+       "editinginterface": "<strong>Wårsküwing:</strong> jy bewarket een syde dee teksten gebruukt vöär de gebrukersümgeaving van de programmatuur. \nWat jy up disse syde wysigen is van invlööd up de gebrukersümgeaving van andere gebrukers van disse wiki.",
+       "translateinterface": "Üm oaversettingen vöär alle wikis to te vogen of te wysigen, kün jy [https://translatewiki.net/ translatewiki.net] gebruken, et lokaliseringsprojekt vöär MediaWiki.",
        "cascadeprotected": "Disse syde is beveiligd ümdat et vöärkümt in de volgende {{PLURAL:$1|syde|syden}}, dee beveiligd {{PLURAL:$1|is|binnet}} mid de \"kaskade\"-opty:\n$2",
        "namespaceprotected": "Jy möäget geen syden in de <strong>$1</strong>-naamruumde bewarken.",
-       "customcssprotected": "Jy möäget disse CSS-syde neet bewarken, ümdat der persoonlike instellingen van een andere bruker in stån.",
-       "customjsprotected": "Jy möäget disse JavaScript-syde neet bewarken, ümdat der persoonlike instellingen van een andere bruker in stån.",
+       "customcssprotected": "Jy möäget disse CSS-syde neet bewarken, ümdat der persoonlike instellingen van een andere gebruker in stån.",
+       "customjsprotected": "Jy möäget disse JavaScript-syde neet bewarken, ümdat der persoonlike instellingen van een andere gebruker in stån.",
        "mycustomcssprotected": "Jy hebbet geen rechten üm disse CSS-syde te bewarken.",
        "mycustomjsprotected": "Jy hebbet geen rechten üm disse JavaScript-syde te bewarken.",
        "myprivateinfoprotected": "Jy hebbet geen rechten üm juw priveegegeavens te bewarken.",
-       "mypreferencesprotected": "Jy hebbet geen rechten üm juw vöärköären an te passen.",
+       "mypreferencesprotected": "Jy hebbet geen rechten üm juw instellingen an te passen.",
        "ns-specialprotected": "Speciale syden künnet neet bewarked wörden.",
        "titleprotected": "Et anmaken van disse syde is beveiligd döär [[User:$1|$1]].\nDe upgeaven readen is <em>$2</em>.",
        "filereadonlyerror": "Kun et bestand \"$1\" neet anpassen ümdat de bestandsmap \"$2\" up dit moment up allinnig-leasen steyt.\n\nDe upgeaven readen is: \"$3\".",
        "virus-unknownscanner": "onbekend antivirusprogramma:",
        "logouttext": "'''Je bin noen aofemeld.'''\n\nt Kan ween dat der wat ziejen bin die weeregeven wörden as of je an-emeld bin totda'j t tussengeheugen van joew webkieker leegmaken.",
        "welcomeuser": "Welkom, $1!",
-       "welcomecreation-msg": "Joew gebruker is an-emaakt.\nVergeet niet joew [[Special:Preferences|veurkeuren veur {{SITENAME}}]] an te passen.",
-       "yourname": "Brukersname",
-       "userlogin-yourname": "Brukersname",
-       "userlogin-yourname-ph": "Geav juw brukersname up",
-       "createacct-another-username-ph": "Geav de brukersname up",
+       "welcomecreation-msg": "Juw gebrukerskonto is anmaked.\nVergeat neet juw [[Special:Preferences|vöärköären vöär {{SITENAME}}]] in te stellen.",
+       "yourname": "Gebrukersname",
+       "userlogin-yourname": "Gebrukersname",
+       "userlogin-yourname-ph": "Geav juw gebrukersname up",
+       "createacct-another-username-ph": "Geav de gebrukersname up",
        "yourpassword": "Wachtwoord",
        "userlogin-yourpassword": "Wachtwoord",
        "userlogin-yourpassword-ph": "Geav juw wachtwoord up",
        "createacct-yourpasswordagain": "Bevästig wachtwoord",
        "createacct-yourpasswordagain-ph": "Geav et wachtwoord upny up",
        "userlogin-remembermypassword": "Vanselv anmelden",
-       "userlogin-signwithsecure": "Beveiligde verbinding bruken",
+       "userlogin-signwithsecure": "Beveiligde verbinding gebruken",
        "yourdomainname": "Juw domein",
        "password-change-forbidden": "Je kunnen joew wachtwoord niet wiezigen op disse wiki.",
        "externaldberror": "Der gung iets fout bie de externe authentisering, of je maggen je gebrukersprofiel niet bewarken.",
        "logout": "Afmelden",
        "userlogout": "Aofmelden",
        "notloggedin": "Neet an-emelded",
-       "userlogin-noaccount": "Heb jy noch geen brukersname?",
+       "userlogin-noaccount": "Heb jy noch geen gebrukersname?",
        "userlogin-joinproject": "Wörd lid van {{SITENAME}}",
        "createaccount": "Inskryven",
-       "userlogin-resetpassword-link": "Juuw wachtwoord vergeaten?",
+       "userlogin-resetpassword-link": "Wachtwoord vergeaten?",
        "userlogin-helplink2": "Hülpe by et anmelden",
        "userlogin-loggedin": "Je binnen al an-emeld as {{GENDER:$1|$1}}.\nGebruuk et formulyr hyrunder üm an te melden as een andere gebruker.",
        "userlogin-createanother": "Een andere gebrukerskonto anmaken",
        "createacct-emailrequired": "Netpostadres",
        "createacct-emailoptional": "Netpostadres (niet verplicht)",
-       "createacct-email-ph": "Geef joew netpostadres op",
+       "createacct-email-ph": "Geav juw netpostadresse up",
        "createacct-another-email-ph": "Vul joew netpostadres in",
        "createaccountmail": "Gebruuk n tiejelik wachtwoord dat joe netzelde is en stuur t naor t op-egeven netpostadres",
        "createacct-realname": "Echte naam (niet verplicht)",
        "createacct-reason-ph": "Waorumme je n aandere gebrukerskonto anmaken",
        "createacct-submit": "Gebrukerskonto anmaken",
        "createacct-another-submit": "Gebrukerskonto anmaken",
-       "createacct-benefit-heading": "{{SITENAME}} wörden emaakt deur meensen zo as jie.",
-       "createacct-benefit-body1": "bewarking{{PLURAL:$1||en}}",
-       "createacct-benefit-body2": "{{PLURAL:$1|zied|ziejen}}",
-       "createacct-benefit-body3": "aktieve {{PLURAL:$1|biedrager|biedragers}}",
+       "createacct-benefit-heading": "{{SITENAME}} is maked döär mensken so as ju.",
+       "createacct-benefit-body1": "{{PLURAL:$1|bewarking|bewarkingen}}",
+       "createacct-benefit-body2": "{{PLURAL:$1|syde|syden}}",
+       "createacct-benefit-body3": "aktive {{PLURAL:$1|bydrager|bydragers}}",
        "badretype": "De wachtwoorden die'j in-etikt hebben bin niet liek alleens.",
        "userexists": "Disse gebrukersnaam is al gebruuk.\nKies n aandere naam.",
        "loginerror": "Anmeldingsfout",
        "php-mail-error-unknown": "Der was n onbekende fout mit de mail()-funksie van PHP",
        "user-mail-no-addy": "Eprobeerd n berichjen te versturen zonder n netpostadres",
        "user-mail-no-body": "Der is eprobeerd n netbreef zonder tekste of mit n biester korte tekste te versturen.",
-       "changepassword": "Wachtwoord wiezigen",
+       "changepassword": "Wachtwoord wysigen",
        "resetpass_announce": "Um t anmelden te voltooien, mu'j n niej wachtwoord invoeren.",
        "resetpass_text": "<!-- Tekste hier invoegen -->",
        "resetpass_header": "Wachtwoord wiezigen",
        "resetpass-wrong-oldpass": "t Veurlopige wachtwoord of t wachtwoord da'j noen hebben is ongeldig.\nMisschien he'j t wachtwoord al ewiezigd of n niej veurlopig wachtwoord an-evreugen.",
        "resetpass-temp-password": "Veurlopig wachtwoord:",
        "resetpass-abort-generic": "De wachtwoordwieziging is aofebreuken deur n uutbreiding.",
-       "passwordreset": "Wachtwoord opniej instellen",
+       "passwordreset": "Wachtwoord upny instellen",
        "passwordreset-text-one": "Vul dit formulier in um joew wachtwoord opniej in te stellen.",
        "passwordreset-text-many": "{{PLURAL:$1|Vul een van de gegevensvelden in um per netpost n tiejelik wachtwoord te ontvangen.}}",
        "passwordreset-disabled": "Je kunnen op disse wiki joew wachtwoord niet opniej instellen.",
        "summary": "Samenvatting:",
        "subject": "Onderwarp:",
        "minoredit": "kleine wysiging",
-       "watchthis": "volg disse syde",
+       "watchthis": "Volg disse syde",
        "savearticle": "Syde uutgeaven",
        "savechanges": "Wysigingen uutgeaven",
        "publishpage": "Zied uutbrengen",
        "showpreview": "Bewarking nåkyken",
        "showdiff": "Verskil bekyken",
        "blankarticle": "<strong>Waorschuwing:</strong> de zied die'j anmaken willen is leeg.\nA'j noen weer op \"$1\" klikken, dan wördt de zied an-emaakt zonder enige inhoud.",
-       "anoneditwarning": "<strong>Waorschuwing:</strong> je bin niet an-emeld.\nJoew IP-adres zal op-esleugen wörden a'j wiezigingen op disse zied anbrengen. A'j je eigen <strong>[$1 anmelden]</strong> of <strong>[$2 inschrieven]</strong> dan koemen joew bewarkingen onder joew gebrukersnaam te staon, samen mit aandere veurdelen.",
+       "anoneditwarning": "<strong>Wårsküwing:</strong> jy binnet neet anmelded. Juw IP-adresse sal vöär ydereyne sichtbår weasen as jy wysigingen up disse syde anbrenget. As jy juw eigen <strong>[$1 anmeldet]</strong> of <strong>[$2 inskryvet]</strong> dan kommen juw bewarkingen under juw gebrukersname te stån, samen mid andere vöärdeylen.",
        "anonpreviewwarning": "''Je bin niet an-emeld.''\n''Deur de bewarking op te slaon wörden joew IP-adres op-esleugen in de ziedgeschiedenisse.''",
        "missingsummary": "'''Herinnering:''' je hebben gien samenvatting op-egeven veur de bewarking. A'j noen weer op ''Opslaon'' klikken wörden de bewarking zonder samenvatting op-esleugen.",
        "missingcommenttext": "Skryv een upmarking.",
        "summary-preview": "Samenvatting nåkyken:",
        "subject-preview": "Underwarp nåkyken:",
        "blockedtitle": "Gebruker is eblokkeerd",
-       "blockedtext": "<strong>Juw brukersname of IP-adres is blokkeerd.</strong>\n\nJy binnet blokkeerd döär $1.\nDe upgeaven readen is <em>$2</em>.\n\n* Blokkeerd vanaf: $8\n* Blokkeerd tot: $6\n* Bedoold üm te blokkeren: $7\n\nJy künnet kontakt upneamen mid $1 of een andere [[{{MediaWiki:Grouppage-sysop}}|beheyrder]] üm de blokkering te bepråten.\nJy künnet de funkty \"{{int:emailuser}}\" neet bruken, behalven as jy een geldig e-postadres in juw [[Special:Preferences|instellingen]] upgeaven hebbet en et gebruuk van disse funkty neet blokkeerd is.\nEt IP-adres wat jy nu bruket is $3, en et blokkeringsnummer is #$5.\nVermeld de gegeavens dee hyrboaven stån as jy argens up disse blokkering reageren.",
+       "blockedtext": "<strong>Juw gebrukersname of IP-adres is blokkeerd.</strong>\n\nJy binnet blokkeerd döär $1.\nDe upgeaven readen is <em>$2</em>.\n\n* Blokkeerd vanaf: $8\n* Blokkeerd tot: $6\n* Bedoold üm te blokkeren: $7\n\nJy künnet kontakt upneamen mid $1 of een andere [[{{MediaWiki:Grouppage-sysop}}|beheyrder]] üm de blokkering te bepråten.\nJy künnet de funkty \"{{int:emailuser}}\" neet gebruken, behalven as jy een geldig e-postadres in juw [[Special:Preferences|instellingen]] upgeaven hebbet en et gebruuk van disse funkty neet blokkeerd is.\nEt IP-adres wat jy nu gebruket is $3, en et blokkeringsnummer is #$5.\nVermeld de gegeavens dee hyrboaven stån as jy argens up disse blokkering reageren.",
        "autoblockedtext": "Joew IP-adres is automaties eblokkeerd umdat t gebruukt wördt deur n aandere gebruker, die eblokkeerd wördt deur $1.\nDe reden hierveur was:\n\n:''$2''\n\n* Begint: $8\n* Löp of nao: $6\n* Wee eblokkeerd wördt: $7\n\nJe kunnen kontakt opnemen mit $1 of n van de aandere\n[[{{MediaWiki:Grouppage-sysop}}|beheerders]] um de blokkering te bepraoten.\n\nNB: je kunnen de opsie \"n bericht sturen\" niet gebruken, behalven a'j n geldig netpostadres op-egeven hebben in de [[Special:Preferences|gebrukersveurkeuren]] en je niet eblokkeerd bin.\n\nJoew IP-adres is $3 en joew blokkeernummer is $5.\nGeef disse nummers deur a'j kontakt mit ene opnemen over de blokkering.",
        "blockednoreason": "gien reden op-egeven",
        "whitelistedittext": "Um ziejen te kunnen wiezigen, mu'j $1 ween",
        "accmailtitle": "Wachtwoord is verstuurd.",
        "accmailtext": "Der is n willekeurig wachtwoord veur [[User talk:$1|$1]] verstuurd naor $2. t Kan ewiezigd wörden op de zied ''[[Special:ChangePassword|wachtwoord wiezigen]]'' naoda'j an-emeld bin.",
        "newarticle": "(Niej)",
-       "newarticletext": "Disse zied besteet nog niet.\nIn t veld hieronder ku'j wat schrieven um disse zied an te maken (meer informasie vie'j op de [$1 hulpzied]).\nA'j hier per ongelok terechtekeumen bin gebruuk dan de knoppe '''veurige''' um weerumme te gaon.",
-       "anontalkpagetext": "----\n<em>Disse oaverlegsyde höyrt by een anonyme bruker dee noch geen brukersname hevt, of et neet bruukt.</em>\nDårümme bruken wy et IP-adresse ter identifikaty. Een IP-adresse kan döär meyrere lüde bruked wörden. As jy een anonyme bruker binnet, en et gevööl hebbet dat jy berichten kryget dee neet vöär ju bedoold binnet [[Special:CreateAccount|skryv ju eigen dan in]] of [[Special:UserLogin|meld ju eigen an]] üm verwarring mid andere anonyme brukers in de tokumst te vöärkommen.''",
+       "newarticletext": "Disse syde besteyt noch neet.\nIn et veld hyrunder kün jy wat skryven üm disse syde an te maken (meyr informaty vind jy up de [$1 hülpsyde]).\nAs jy hyr per ungelük terechtekommen binnet bruuk dan de knoppe '''vöärige''' üm terügge te gån.",
+       "anontalkpagetext": "----\n<em>Disse oaverlegsyde höyrt by een anonyme gebruker dee noch geen gebrukersname hevt, of et neet bruukt.</em>\nDårümme gebruken wy et IP-adresse ter identifikaty. Een IP-adresse kan döär meyrere lüde gebruked wörden. As jy een anonyme gebruker binnet, en et gevööl hebbet dat jy berichten kryget dee neet vöär ju bedoold binnet [[Special:CreateAccount|skryv ju eigen dan in]] of [[Special:UserLogin|meld ju eigen an]] üm verwarring mid andere anonyme gebrukers in de tokumst te vöärkommen.''",
        "noarticletext": "Der steyt nun geen tekst up disse syde.\nJy künnet [[Special:Search/{{PAGENAME}}|de titel upsöken]] in andere syden,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} söken in de logboken],\nof [{{fullurl:{{FULLPAGENAME}}|action=edit}} disse syde anmaken]</span>.",
-       "noarticletext-nopermission": "Op disse zied steet gien tekste.\nJe kunnen [[Special:Search/{{PAGENAME}}|zeuken naor disse term]] in aandere ziejen of\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} de logboeken deurzeuken]</span>, mer je hebben gien rechten um disse zied an te maken.",
+       "noarticletext-nopermission": "Up disse syde steyt geen tekst.\nJy künnet [[Special:Search/{{PAGENAME}}|söken nå disse term]] in andere syden of\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} de logboken döärsöken]</span>, mär jy hebbet geen rechten üm disse syde an te maken.",
        "missing-revision": "De versie #$1 van de zied \"{{FULLPAGENAME}} besteet niet.\n\nDit kömp meestentieds deur t volgen van n verouwerde verwiezing naor n zied die vortedaon is.\nWaorschienlik ku'j der meer gegevens over vienen in t [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} vortdologboek].",
        "userpage-userdoesnotexist": "Je bewarken n gebrukerszied van n gebruker die niet besteet (gebruker \"<nowiki>$1</nowiki>\"). Kiek effen nao o'j disse zied wel anmaken/bewarken willen.",
        "userpage-userdoesnotexist-view": "Gebruker \"$1\" steet hier niet in-eschreven",
        "edit_form_incomplete": "'''Partie delen van t bewarkingsformulier hebben de server niet bereikt. Kiek eers nao of de bewarkingen kloppen en probeer t opniej.'''",
        "editing": "Bewarken: $1",
        "creating": "Anmaken: $1",
-       "editingsection": "Bewarken: $1 (deelzied)",
+       "editingsection": "Bewarken: $1 (deylsyde)",
        "editingcomment": "Bewarken: $1 (niej onderwarp)",
        "editconflict": "Tegelieke bewörken: $1",
        "explainconflict": "'''NB:''' n aander hef disse zied ewiezigd naoda'j an disse bewarking begunnen bin.\nt Bovenste bewarkingsveld löt de zied zien zo as t noen is.\nDaoronder (bie \"Wiezigingen\") staon de verschillen tussen joew versie en de op-esleugen zied.\nHelemaole onderan (bie \"Joew tekste\") steet nog n bewarkingsveld mit joew versie.\nJe zullen je eigen wiezigingen in de nieje tekste in mutten passen.\n'''Allinnig''' de tekste in t bovenste veld wörden beweerd a'j noen kiezen veur \"$1\".",
        "semiprotectedpagewarning": "'''Let op:''' disse zied is beveiligd en ku'j allinnig bewarken a'j n eregistreerden gebruker bin.\nDe leste logboekregel steet hieronder:",
        "cascadeprotectedwarning": "'''Waorschuwing:''' disse zied is beveiligd, zodat allinnig beheerders disse zied bewarken kunnen, dit wörden edaon umdat disse zied veurkömp in de volgende {{PLURAL:$1|kaskade-beveiligden zied|kaskade-beveiligden ziejen}}:",
        "titleprotectedwarning": "'''Waorschuwing: disse zied is beveiligd. Je hebben [[Special:ListGroupRights|bepaolde rechten]] neudig um t an te kunnen maken.'''\nDe leste logboekregel steet hieronder:",
-       "templatesused": "{{PLURAL:$1|Mal|Mallen}} die op disse zied gebruukt wörden:",
+       "templatesused": "{{PLURAL:$1|Mal|Mallen}} dee up disse syde gebruked wörden:",
        "templatesusedpreview": "{{PLURAL:$1|Mal|Mallen}} die in disse bewarking gebruukt wörden:",
        "templatesusedsection": "{{PLURAL:$1|Mal|Mallen}} die in dit subkopjen gebruukt wörden:",
        "template-protected": "(beveiligd)",
        "template-semiprotected": "(halv-beveiligd)",
-       "hiddencategories": "Disse zied völt in de volgende verbörgen {{PLURAL:$1|kategorie|kategorieën}}:",
+       "hiddencategories": "Disse syde valt in de volgende verbörgen {{PLURAL:$1|kategory|kategoryen}}:",
        "edittools": "<!-- Disse tekste steet onder de bewarkings- en bestaandinlaodformulieren. -->",
        "nocreatetext": "Disse webstee hef de meugelikheid um nieje ziejen an te maken beteund. Je kunnen ziejen die al bestaon wiezigen of je kunnen je [[Special:UserLogin|anmelden of n gebrukerszied anmaken]].",
        "nocreate-loggedin": "Je hebben gien toestemming um nieje ziejen an te maken.",
        "sectioneditnotsupported-text": "Je kunnen op disse zied gien seksies bewarken.",
        "permissionserrors": "Gien toestemming",
        "permissionserrorstext": "Je maggen of kunnen dit niet doon. De {{PLURAL:$1|reden|redens}} daorveur {{PLURAL:$1|is|bin}}:",
-       "permissionserrorstext-withaction": "Je hebben gien recht um $2, mit de volgende {{PLURAL:$1|reden|redens}}:",
+       "permissionserrorstext-withaction": "Jy hebbet geen rechten üm $2, mid de volgende {{PLURAL:$1|readen|readenen}}:",
        "recreate-moveddeleted-warn": "'''Waorschuwing: je maken n zied an die eerder al vortedaon is.'''\n\nBedenk eerst of t neudig is um disse zied veerder te bewarken.\nVeur de dudelikheid steet hieronder  t vortdologboek en t herneumlogboek veur disse zied:",
-       "moveddeleted-notice": "Disse zied is vortedaon.\nHieronder steet de informasie uut t vortdologboek, t beveiligingslogboek, en t herneumlogboek.",
+       "moveddeleted-notice": "Disse syde is vorddån.\nHyrunder steyt de informaty uut et vortsmytlogbook, et beveiligingslogbook, en et hernöömlogbook.",
        "log-fulllog": "t Hele logboek bekieken",
        "edit-hook-aborted": "De bewarking is aofebreuken deur n hook.\nDer is gien reden op-egeven.",
        "edit-gone-missing": "De zied kon niet bie-ewörken wörden.\nt Lik derop as of t vortedaon is.",
        "undo-summary": "Versie $1 van [[Special:Contributions/$2|$2]] ([[User talk:$2|overleg]]) weerummedreid",
        "undo-summary-username-hidden": "Versie $1 deur n verbörgen gebruker weerummedreid",
        "cantcreateaccount-text": "t Anmaken van gebrukers van dit IP-adres (<b>$1</b>) is eblokkeerd deur [[User:$3|$3]].\n\nDe deur $3 op-egeven reden is ''$2''",
-       "viewpagelogs": "Bekiek logboeken veur disse zied",
+       "viewpagelogs": "Bekyk logboken vöär disse syde",
        "nohistory": "Der bin gien eerdere versies van disse zied.",
        "currentrev": "Leste versie",
-       "currentrev-asof": "Leste versie van $1",
+       "currentrev-asof": "Lätste versy van $1",
        "revisionasof": "Versy up $1",
-       "revision-info": "Versie op $1 van {{GENDER:$6|$2}}$7",
+       "revision-info": "Versy up $1 van {{GENDER:$6|$2}}$7",
        "previousrevision": "&larr; eyrere versy",
-       "nextrevision": "niejere versie &rarr;",
-       "currentrevisionlink": "versie zo as t noen is",
+       "nextrevision": "nyere versy &rarr;",
+       "currentrevisionlink": "Aktuele versy",
        "cur": "aktueel",
        "next": "Volgende",
        "last": "lätste",
        "rev-suppressed-unhide-diff": "Eén van de bewarkingen in disse vergeliekingen is '''vortedaon'''.\nAs der meer informasie is, ku'j t vienen in t [{{fullurl:{{#Special:Log}}/suppress|page={{PAGENAMEE}}}} logboek mit onderdrokten informasie].\nJe kunnen [$1 de verschillen bekieken] a'j willen.",
        "rev-deleted-diff-view": "Eén van de bewarkingen veur de verschillen die'j op-evreugen hebben, is '''vortedaon'''.\nJe kunnen disse verschillen bekieken. Misschien steet der meer over in t [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} vortdologboek].",
        "rev-suppressed-diff-view": "Eén van de bewarkingen veur de verschillen die'j op-evreugen hebben, is '''onderdrokt'''.\nJe kunnen disse verschillen bekieken. Misschien steet der over in t [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} logboek mit onderdrokten versies].",
-       "rev-delundel": "bekiek/verbarg",
+       "rev-delundel": "bekyken/verbargen",
        "rev-showdeleted": "bekiek",
        "revisiondelete": "Wiezigingen vortdoon/herstellen",
        "revdelete-nooldid-title": "Gien doelversie",
        "mergelog": "Samenvoegingslogboek",
        "revertmerge": "Samenvoeging weerummedreien",
        "mergelogpagetext": "Hieronder zie'j n lieste van de leste samenvoegingen van n ziedgeschiedenisse naor n aandere.",
-       "history-title": "Versiegeschiedenisse van \"$1\"",
-       "difference-title": "Verschil tussen versies van \"$1\"",
+       "history-title": "Versygeskydenisse van \"$1\"",
+       "difference-title": "Verskil tüsken versys van \"$1\"",
        "difference-title-multipage": "Verschil tussen ziejen \"$1\" en \"$2\"",
        "difference-multipage": "(Verschil tussen ziejen)",
        "lineno": "Regel $1:",
        "showhideselectedversions": "Ekeuzen versies bekieken/verbargen",
        "editundo": "weaderümmedraien",
        "diff-empty": "(Gien verschil)",
-       "diff-multi-sameuser": "({{PLURAL:$1|n Tussenliggende versie|$1 tussenliggende versies}} deur de zelfde gebruker is verbörgen)",
-       "diff-multi-otherusers": "({{PLURAL:$1|Eyn tüskenliggende versy|$1 tüskenliggende versys}} döär {{PLURAL:$2|eyn andere bruker|$2 brukers}} neet weadergeaven)",
+       "diff-multi-sameuser": "({{PLURAL:$1|Eyn tüskenliggende versy|$1 tüskenliggende versys}} döär deselvde gebruker is verbörgen)",
+       "diff-multi-otherusers": "({{PLURAL:$1|Eyn tüskenliggende versy|$1 tüskenliggende versys}} döär {{PLURAL:$2|eyn andere gebruker|$2 gebrukers}} neet weadergeaven)",
        "diff-multi-manyusers": "($1 tussenliggende {{PLURAL:$1|versie|versies}} deur meer as $2 {{PLURAL:$2|gebruker|gebrukers}} niet weeregeven)",
        "difference-missing-revision": "{{PLURAL:$2|Eén versie|$2 versies}} van disse verschillen ($1) {{PLURAL:$2|is|bin}} niet evunnen.\n\nDit kömp meestentieds deur t volgen van n verouwerde verwiezing naor n zied die vortedaon is.\nWaorschienlik ku'j der meer gegevens over vienen in t [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} vortdologboek].",
        "searchresults": "Söökresultaten",
        "prevn": "vöärige {{PLURAL:$1|$1}}",
        "nextn": "volgende {{PLURAL:$1|$1}}",
        "prevn-title": "{{PLURAL:$1|Veurig resultaot|Veurige $1 resultaoten}}",
-       "nextn-title": "{{PLURAL:$1|Volgend resultaot|Volgende $1 resultaoten}}",
+       "nextn-title": "{{PLURAL:$1|Volgend resultaat|Volgende $1 resultaten}}",
        "shown-title": "Låt $1 {{PLURAL:$1|resultaat|resultaten}} per syde seen",
        "viewprevnext": "($1 {{int:pipe-separator}} $2) ($3) bekyken.",
        "searchmenu-exists": "'''Der is n zied mit de naam \"[[:$1]]\" op disse wiki.'''",
-       "searchmenu-new": "<strong>De zied \"[[:$1]]\" op disse wiki anmaken!</strong> \n{{PLURAL:$2|0=|Zie oek de zied mit joew zeukresultaoten.|Zie oek de lieste mit evunnen zeukresultaoten.}}",
+       "searchmenu-new": "<strong>De syde \"[[:$1]]\" up disse wiki anmaken!</strong> \n{{PLURAL:$2|0=|See ouk de syde mid juw söökresultaten.|See ouk de lyste mid vünden söökresultaten.}}",
        "searchprofile-articles": "Artikels",
        "searchprofile-images": "Multimedia",
        "searchprofile-everything": "Alles",
        "search-result-size": "$1 ({{PLURAL:$2|1 woord|$2 woorden}})",
        "search-result-category-size": "{{PLURAL:$1|1 kategorielid|$1 kategorielejen}} ({{PLURAL:$2|1 onderkategorie|$2 onderkategorieën}}, {{PLURAL:$3|1 bestaand|$3 bestaanden}})",
        "search-redirect": "(döärverwysing vanaf $1)",
-       "search-section": "(onderwarp $1)",
+       "search-section": "(underwarp $1)",
        "search-file-match": "(kümt oavereyne mid de inhold van et bestand)",
-       "search-suggest": "Bedoelden je: $1",
+       "search-suggest": "Bedoolde jy: $1",
        "search-interwiki-caption": "Resultaten van süsterprojekten",
        "search-interwiki-default": "Resultaoten van $1:",
        "search-interwiki-more": "(meer)",
        "searchrelated": "verwaant",
        "searchall": "alles",
        "showingresults": "Hieronder {{PLURAL:$1|steet '''1''' resultaot|staon '''$1''' resultaoten}}  <b>$1</b> vanaof nummer <b>$2</b>.",
-       "search-showingresults": "{{PLURAL:$4|Resultaot <strong>$1</strong> van <strong>$2</strong>|Resultaoten <strong>$1 - $2</strong> van <strong>$3</strong>}}",
+       "search-showingresults": "{{PLURAL:$4|Resultaat <strong>$1</strong> van <strong>$3</strong>|Resultaten <strong>$1 – $2</strong> van <strong>$3</strong>}}",
        "search-nonefound": "Der binnet geen resultaten vöär de söökupdrachte.",
        "powersearch-legend": "Uutgebreid zeuken",
        "powersearch-ns": "Zeuken in naamruumten:",
        "search-external": "Extern zeuken",
        "searchdisabled": "Zeuken in {{SITENAME}} is niet meugelik. Je kunnen gebruukmaken van Google. De gegevens over {{SITENAME}} bin misschien niet bie-ewörken.",
        "search-error": "Der is wat mis-egaon bie t zeuken: $1",
-       "preferences": "Veurkeuren",
-       "mypreferences": "Myn vöärköären",
+       "preferences": "Instellingen",
+       "mypreferences": "Instellingen",
        "prefs-edits": "Antal bewarkingen:",
        "prefs-skin": "{{SITENAME}}-uterlik",
        "skin-preview": "bekieken",
        "datedefault": "Gien veurkeur",
        "prefs-labs": "Alphafunksies",
        "prefs-user-pages": "Gebrukersziejen",
-       "prefs-personal": "Gebrukersgegevens",
-       "prefs-rc": "Leste wiezigingen",
-       "prefs-watchlist": "Volglieste",
+       "prefs-personal": "Brukersgegeavens",
+       "prefs-rc": "Lätste wysigingen",
+       "prefs-watchlist": "Volglyste",
        "prefs-editwatchlist-label": "Onderwarpen op joew volglieste bewarken:",
        "prefs-editwatchlist-edit": "Onderwarpen op mien volgliesten bekieken en vortdoon",
        "prefs-watchlist-days": "Antal dagen in de volglieste bekieken:",
        "prefs-changeemail": "Netpostadres wiezigen of vorthaolen",
        "prefs-setemail": "Stel n netpostadres in",
        "prefs-email": "Instellingen veur netpost",
-       "prefs-rendering": "Ziedweergave",
-       "saveprefs": "Veurkeuren opslaon",
+       "prefs-rendering": "Uterlik",
+       "saveprefs": "Instellingen seakeren",
        "restoreprefs": "Alle standardinstellingen weerummezetten (veur alle seksies)",
        "prefs-editing": "Bewarkingsveld",
        "searchresultshead": "Zeukresultaoten",
        "recentchangescount": "Standard antal bewarkingen um te laoten zien:",
        "prefs-help-recentchangescount": "Dit geldt veur leste wiezigingen, ziedgeschiedenisse en logboekziejen",
        "prefs-help-watchlist-token2": "Dit is de geheime sleutel veur de webvoer van joew volglieste.\nIederene die t token kent, kan joew volglieste bekieken, dus deel dit token niet.\nJe kunnen de [[Special:ResetTokens|tokens opniej instellen]] a'j dat willen.",
-       "savedprefs": "Veurkeuren bin op-esleugen.",
+       "savedprefs": "Instellingen binnet seakerd.",
        "timezonelegend": "Tiedzone:",
        "localtime": "Plaotselike tied:",
        "timezoneuseserverdefault": "Wikistandard gebruken ($1)",
        "timezoneregion-indian": "Indiese Oseaan",
        "timezoneregion-pacific": "Stille Oseaan",
        "allowemail": "Berichten van aandere gebrukers toestaon",
-       "prefs-searchoptions": "Zeukinstellingen",
+       "prefs-searchoptions": "Söken",
        "prefs-namespaces": "Naamruumtes",
        "default": "standard",
        "prefs-files": "Bestaanden",
        "prefs-emailconfirm-label": "Netpostbevestiging:",
        "youremail": "Netpostadres (niet verplicht) *",
        "username": "{{GENDER:$1|Gebrukersnaam}}:",
-       "prefs-memberingroups": "{{GENDER:$2|Lid}} van {{PLURAL:$1|groep|groepen}}:",
-       "prefs-registration": "Registrasiedaotum:",
+       "prefs-memberingroups": "{{GENDER:$2|Lid}} van {{PLURAL:$1|grup|gruppen}}:",
+       "prefs-registration": "Registratydåtum:",
        "yourrealname": "Echte naam (niet verplicht)",
-       "yourlanguage": "Taal veur systeemteksten",
+       "yourlanguage": "Taal / språke:",
        "yourvariant": "Taalvariaant veur inhoud:",
        "prefs-help-variant": "Joew veurkeursvariaant of -spelling um de inhoudsziejen van disse wiki in weer te geven.",
        "yournick": "Alias veur ondertekeningen",
        "prefs-help-signature": "Reaksies op de overlegziejen mutten ondertekend wörden mit \"<nowiki>~~~~</nowiki>\", dit wörden dan ummezet in joew ondertekening mit daorbie de daotum en tied van de bewarking.",
        "badsig": "Ongeldige haandtekening; HTML naokieken.",
        "badsiglength": "Joew haandtekening is te lang.\nt Mut minder as {{PLURAL:$1|letter|letters}} hebben.",
-       "yourgender": "Geslacht:",
-       "gender-unknown": "De programmatuur gebruukt zoveul meugelik geslachtsneutrale woorden as t over joe geet.",
-       "gender-male": "Keerl",
-       "gender-female": "Deerne",
-       "prefs-help-gender": "Disse instelling is opsioneel.\n\nDe programmatuur gebruukt disse weerde um joe op de juuste maniere an te spreken en veur aandere gebrukers um joew geslacht an te geven.\nDisse informasie is zichtbaor veur aandere gebrukers.",
+       "yourgender": "Ho wil jy beskreaven wörden?",
+       "gender-unknown": "Geslachtsneutrale anspreaksform",
+       "gender-male": "Hee bewarkt syden",
+       "gender-female": "See bewarkt syden",
+       "prefs-help-gender": "Disse instelling is optioneel.\nDe programmatuur gebruukt disse waerde um ju up de jüüste manere an te spreaken en vöär andere gebrukers üm juw geslacht an te geaven.\nDisse informaty is sichtbår vöär andere gebrukers.",
        "email": "Privéberichten",
        "prefs-help-realname": "Echte naam is keuzevrie.\nA'j disse opsie invullen zu'w joew echte naam gebruken um erkenning te geven veur joew wark.",
        "prefs-help-email": "n Netpostadres is niet verplicht, mer zo ku'w wel joew wachtwoord toesturen veur a'j t vergeten bin.",
        "prefs-help-email-others": "Je kunnen oek aandere meensen de meugelikheid geven um kontakt mit joe op te nemen mit n verwiezing op joew gebrukers- en overlegzied zonder da'j de identiteit pries hoeven te geven.",
        "prefs-help-email-required": "Hier he'w n netpostadres veur neudig.",
-       "prefs-info": "Baosisinformasie",
-       "prefs-i18n": "Taalinstellingen",
+       "prefs-info": "Basisinformaty",
+       "prefs-i18n": "Språkinstellingen",
        "prefs-signature": "Ondertekening",
        "prefs-dateformat": "Daotumopmaak:",
        "prefs-timeoffset": "Tiedsverschil",
        "right-siteadmin": "De databanke blokkeren en weer vriegeven",
        "right-override-export-depth": "Ziejen exporteren, oek de ziejen waor naor verwezen wördt, tot n diepte van 5",
        "right-sendemail": "Bericht versturen naor aandere gebrukers",
-       "newuserlogpage": "Logbook van nye brukers",
+       "newuserlogpage": "Logbook van nye gebrukers",
        "newuserlogpagetext": "Hieronder staon de niej in-eschreven gebrukers",
        "rightslog": "Gebrukersrechtenlogboek",
        "rightslogtext": "Dit is n logboek mit veraanderingen van gebrukersrechten",
        "rcfilters-filter-user-experience-level-newcomer-label": "Anwas",
        "rcfilters-filter-user-experience-level-newcomer-description": "An-emelden bewarkers dee minder as 10 bewarkingen edån hebben of 4 dagen aktiv ewesd hebben.",
        "rcfilters-filter-user-experience-level-learner-label": "Leyrlingen",
-       "rcfilters-filter-user-experience-level-learner-description": "Anmeldede bewarkers mid meyr ervaring as \"anwas\", mär minder as \"ervaren brukers\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Anmeldede bewarkers mid meyr ervaring as \"anwas\", mär minder as \"ervaren gebrukers\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Ervåren gebrukers",
        "rcfilters-filter-user-experience-level-experienced-description": "An-emelde bewarkers mid meyr as 500 bewarkingen en 30 dagen van aktiviteit.",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-previousrevision-description": "Alle wysigingen dee neet de \"lätste versy\" binnen.",
        "rcfilters-view-tags": "Emarkeerde wysigingen",
        "rcfilters-view-namespaces-tooltip": "Filter resultaten up naamruumde",
-       "rcfilters-view-tags-tooltip": "Filter resultaten döär bewarkingsetiketten te bruken",
+       "rcfilters-view-tags-tooltip": "Filter resultaten döär bewarkingsetiketten te gebruken",
        "rcfilters-liveupdates-button": "Rechtstreakse aktualisering",
        "rcfilters-liveupdates-button-title-off": "Nye wysigingen voorddalik låten seen",
        "rcnotefrom": "Wysigingen sinds <strong>$3, $4</strong> (maximaal <strong>$1</strong> {{PLURAL:$1|wysiging|wysigingen}}).",
        "rclistfrom": "Bekyk wysigingen vanaf $3 $2",
        "rcshowhideminor": "$1 kleine wysigingen",
-       "rcshowhideminor-show": "Bekiek",
+       "rcshowhideminor-show": "bekyken",
        "rcshowhideminor-hide": "verbargen",
        "rcshowhidebots": "$1 botbrukers",
        "rcshowhidebots-show": "bekyken",
-       "rcshowhidebots-hide": "Verbarg",
-       "rcshowhideliu": "$1 registreerde brukers",
+       "rcshowhidebots-hide": "verbargen",
+       "rcshowhideliu": "$1 registreerde gebrukers",
        "rcshowhideliu-show": "Bekiek",
        "rcshowhideliu-hide": "verbargen",
-       "rcshowhideanons": "$1 anonyme brukers",
-       "rcshowhideanons-show": "Bekiek",
+       "rcshowhideanons": "$1 anonyme gebrukers",
+       "rcshowhideanons-show": "bekyken",
        "rcshowhideanons-hide": "verbargen",
        "rcshowhidepatr": "$1 nao-ekeken bewarkingen",
        "rcshowhidepatr-show": "Bekiek",
        "rcshowhidepatr-hide": "Verbarg",
        "rcshowhidemine": "$1 myn bewarkingen",
-       "rcshowhidemine-show": "Bekiek",
+       "rcshowhidemine-show": "bekyken",
        "rcshowhidemine-hide": "verbargen",
        "rcshowhidecategorization": "$1 kategorisering van ziejen",
        "rcshowhidecategorization-show": "Bekiek",
        "rclinks": "Bekyk de lätste $1 wysigingen van de vöärbye $2 dagen",
        "diff": "verskil",
        "hist": "geskydenisse",
-       "hide": "verbarg",
-       "show": "bekiek",
+       "hide": "verbargen",
+       "show": "bekyken",
        "minoreditletter": "K",
        "newpageletter": "N",
        "boteditletter": "B",
        "recentchangeslinked": "Soortgelyke wysigingen",
        "recentchangeslinked-feed": "Volg verwysingen",
        "recentchangeslinked-toolbox": "Volg verwysingen",
-       "recentchangeslinked-title": "Wiezigingen verwaant an $1",
+       "recentchangeslinked-title": "Wysigingen verwand an $1",
        "recentchangeslinked-summary": "Voor een sydname in üm bewarkingen te seen up syden wårnå verweasen wördt of dårnå verwysen. (Voor {{ns:category}}:kategoryname in üm leaden van een kategory te seen). Bewarkingen van syden up [[Special:Watchlist|juw volglyste]] binnet <strong>vetdrükked</strong>.",
-       "recentchangeslinked-page": "Ziednaam:",
-       "recentchangeslinked-to": "Bekiek wiezigingen op ziejen mit verwiezingen naor disse zied",
+       "recentchangeslinked-page": "Sydname:",
+       "recentchangeslinked-to": "Bekyk wysigingen up syden dee hyrhinne verwysen",
        "recentchanges-page-added-to-category": "[[:$1]] bie kategorie ezet",
        "recentchanges-page-added-to-category-bundled": "[[:$1]] bie kategorie ezet, [[Special:WhatLinksHere/$1|disse zied zit in aandere ziejen in-esleuten]]",
        "recentchanges-page-removed-from-category": "[[:$1]] is vortedaon uut kategorie",
        "uploadlogpage": "Logboek mit nieje bestaanden",
        "uploadlogpagetext": "Hieronder steet n lieste mit bestaanden die net niej bin.\nZie de [[Special:NewFiles|uutstalling mit media]] veur n overzicht.",
        "filename": "Bestaandsnaam",
-       "filedesc": "Beschrieving",
+       "filedesc": "Beskryving",
        "fileuploadsummary": "Beschrieving:",
        "filereuploadsummary": "Bestaandswiezigingen:",
        "filestatus": "Auteursrechtstaotus",
        "upload-curl-error6-text": "t Webadres kon niet bereikt wörden. Kiek effen nao o'j t goeie adres in-evoerd hebben en of de webstee bereikbaor is.",
        "upload-curl-error28": "Tiedsoverschriejing veur t versturen van t bestaand",
        "upload-curl-error28-text": "t Duren te lange veurdat de webstee reageren. Kiek effen nao of de webstee bereikbaor is, wacht effen en probeer t daornao weer. Probeer t aanders as t wat rustiger is.",
-       "license": "Lisensie",
-       "license-header": "Lisensie",
-       "nolicense": "Gien lisensie ekeuzen",
+       "license": "Licensy",
+       "license-header": "Licensy",
+       "nolicense": "Geen licensy köäsen",
        "license-nopreview": "(Naokieken is niet meugelik)",
        "upload_source_url": " (een geldig, publiek toegankelik webadres)",
        "upload_source_file": " (n bestaand op de hardeschieve)",
        "listfiles-summary": "Op disse spesiale zied ku'j alle bestaanden bekieken die lestens op-estuurd bin.",
        "listfiles_search_for": "Zeuk naor bestaand:",
-       "imgfile": "bestaand",
+       "imgfile": "bestand",
        "listfiles": "Bestaandslieste",
        "listfiles_thumb": "Miniatuuraofbeelding",
        "listfiles_date": "Daotum",
        "filehist-thumb": "Miniatuurafbealding",
        "filehist-thumbtext": "Miniatuurafbealding vöär versy van $1",
        "filehist-nothumb": "Gien miniatuuraofbeelding",
-       "filehist-user": "Bruker",
+       "filehist-user": "Gebruker",
        "filehist-dimensions": "Groutde",
        "filehist-filesize": "Bestaandsgrootte",
        "filehist-comment": "Kommentaar",
        "imagelinks": "Bestandsbruuk",
-       "linkstoimage": "Dit bestand wördt up de volgende {{PLURAL:$1|syde|$1 syden}} bruked:",
-       "linkstoimage-more": "Meyr as $1 {{PLURAL:$1|syde bruukt|syden bruken}} dit bestand.\nDe volgende lyste givt allinnig de {{PLURAL:$1|eyrste syde|eyrste $1 syden}} weader dee dit bestand bruukt.\nDe [[Special:WhatLinksHere/$2|heyle lyste]] is ouk beskikbår.",
-       "nolinkstoimage": "Geen enkelde syde gebrüükt disse holder.",
+       "linkstoimage": "Dit bestand wördt up de volgende {{PLURAL:$1|syde|$1 syden}} gebruked:",
+       "linkstoimage-more": "Meyr as $1 {{PLURAL:$1|syde gebruukt|syden gebruken}} dit bestand.\nDe volgende lyste givt allinnig de {{PLURAL:$1|eyrste syde|eyrste $1 syden}} weader dee dit bestand gebruukt.\nDe [[Special:WhatLinksHere/$2|heyle lyste]] is ouk beskikbår.",
+       "nolinkstoimage": "Geen enkele syde gebruukt dit bestand.",
        "morelinkstoimage": "[[Special:WhatLinksHere/$1|Meer verwiezingen]] naor dit bestaand bekieken.",
        "linkstoimage-redirect": "$1 (bestaandsdeurverwiezing) $2",
        "duplicatesoffile": "{{PLURAL:$1|t Volgende bestaand is|De volgende $1 bestaanden bin}} gelieke an dit bestaand ([[Special:FileDuplicateSearch/$2|meer informasie]]):",
        "sharedupload": "Dit is n edeeld bestaand op $1 en ku'j oek gebruken veur aandere projekten.",
        "sharedupload-desc-there": "Dit is n edeeld bestaand op $1 en ku'j oek gebruken veur aandere projekten. Bekiek de [$2 beschrieving van t bestaand] veur meer informasie.",
-       "sharedupload-desc-here": "Dit bestand kümt van $1 en kan ouk in andere projekten bruked weasen. De [$2 syde mid de beskryving van et bestand] steyt hyrunder.",
+       "sharedupload-desc-here": "Dit bestand kümt van $1 en kan ouk in andere projekten gebruked weasen. De [$2 syde mid de beskryving van et bestand] steyt hyrunder.",
        "sharedupload-desc-edit": "Dit besatand kömp van $1 en kan oek in aandere projekten gebruukt wörden.\nJe kunnen de [$2 zied mit de bestaandsbeschrieving] daor bewarken.",
        "sharedupload-desc-create": "Dit besatand kömp van $1 en kan oek in aandere projekten gebruukt wörden.\nJe kunnen de [$2 zied mit de bestaandsbeschrieving] daor bewarken.",
        "filepage-nofile": "Der besteet gien bestaand mit disse naam.",
        "uploadnewversion-linktext": "n Niejere versie van dit bestaand opsturen.",
        "shared-repo-from": "uut $1",
        "shared-repo": "n edeelden mediadatabanke",
-       "upload-disallowed-here": "Je kunnen dit bestaand niet overschrieven.",
+       "upload-disallowed-here": "Jy künnet dit bestand neet oaverskryven.",
        "filerevert": "$1 weerummedreien",
        "filerevert-legend": "Bestaand weerummezetten",
        "filerevert-intro": "Je bin '''[[Media:$1|$1]]''' an t weerummedreien tot de [$4 versie van $2, $3]",
        "ncategories": "$1 {{PLURAL:$1|kategorie|kategorieën}}",
        "ninterwikis": "$1 {{PLURAL:$1|interwikiverwiezing|interwikiverwiezingen}}",
        "nlinks": "$1 {{PLURAL:$1|verwiezing|verwiezingen}}",
-       "nmembers": "$1 {{PLURAL:$1|onderwarp|onderwarpen}}",
+       "nmembers": "$1 {{PLURAL:$1|lid|leaden}}",
        "nrevisions": "$1 {{PLURAL:$1|versie|versies}}",
        "nimagelinks": "Wörden op {{PLURAL:$1|één zied|$1 ziejen}} gebruukt",
        "ntransclusions": "wörden op {{PLURAL:$1|één zied|$1 ziejen}} gebruukt",
        "nopagetitle": "Doelzied besteet niet",
        "nopagetext": "De zied die'j herneumen willen besteet niet.",
        "pager-newer-n": "{{PLURAL:$1|1 niejere|$1 niejere}}",
-       "pager-older-n": "{{PLURAL:$1|1 ouwere|$1 ouwere}}",
+       "pager-older-n": "{{PLURAL:$1|1 oldere|$1 oldere}}",
        "suppress": "Toezicht",
        "querypage-disabled": "Disse spesiale zied is uutezet um prestasieredens.",
        "apisandbox": "API-zaandkule",
-       "booksources": "Boekinformasie",
-       "booksources-search-legend": "Zeuk informasie over n boek",
-       "booksources-search": "Zeuken",
+       "booksources": "Bookinformaty",
+       "booksources-search-legend": "Söök bronnen van een book",
+       "booksources-search": "ken",
        "booksources-text": "Hieronder steet n lieste mit verwiezingen naor aandere websteeën die nieje of wat ouwere boeken verkopen, en daor hebben ze warschienlik meer informasie over t boek da'j zeuken:",
        "booksources-invalid-isbn": "De op-egeven ISBN klop niet; kiek effen nao o'j gien fout emaakt hebben bie de invoer.",
        "magiclink-tracking-isbn": "Ziejen die magiese ISBN-verwiezingen gebruken",
        "cachedspecial-viewing-cached-ttl": "Je bekieken noen n versie uut t tussengeheugen van disse zied, die hooguut $1 oud is.",
        "cachedspecial-viewing-cached-ts": "Je bekieken noen n versie uut t tussengeheugen van disse zied, t kan ween dat t niet helemaole bie de tied is.",
        "cachedspecial-refresh-now": "Leste bekieken.",
-       "categories": "Kategorieën",
+       "categories": "Kategoryen",
        "categoriespagetext": "De de volgende {{PLURAL:$1|kategorie steet|kategorieën staon}} ziejen of mediabestaanden.\n[[Special:UnusedCategories|ongebruukten kategorieën]] zie'j hier niet.\nZie oek [[Special:WantedCategories|gewunste kategorieën]].",
        "categoriesfrom": "Laot kategorieën zien vanaof:",
        "deletedcontributions": "Vortedaone gebrukersbiedragen",
        "usermessage-summary": "Systeemteksten achter-eleuten",
        "usermessage-editor": "Systeemtekste",
        "watchlist": "Volglieste",
-       "mywatchlist": "Myn volglyste",
+       "mywatchlist": "Volglyste",
        "watchlistfor2": "Veur $1 ($2)",
        "nowatchlist": "Gien artikels in volglieste.",
        "watchlistanontext": "$1 is verplicht um joew volglieste te bekieken of te wiezigen.",
        "actioncomplete": "Uutevoerd",
        "actionfailed": "De haandeling is mislokt.",
        "deletedtext": "t Artikel \"$1\" is vortedaon. Zie de \"$2\" veur n lieste van ziejen die as lest vortedaon bin.",
-       "dellogpage": "Vortdologboek",
+       "dellogpage": "Vordsmytlogbook",
        "dellogpagetext": "Hieronder steet n lieste van ziejen en bestaanden die as lest vortedaon bin.",
        "deletionlog": "Vortdologboek",
        "reverted": "Eerdere versie hersteld",
        "deleting-backlinks-warning": "<strong>Waorschuwing:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|aandere ziejen]] gebruken of verwiezen naor de zied die'j vortdoon willen.",
        "rollback": "Wiezigingen herstellen",
        "rollbacklink": "weaderümmedraien",
-       "rollbacklinkcount": "{{PLURAL:$1|één bewarking|$1 bewarkingen}} weerummedreien",
+       "rollbacklinkcount": "{{PLURAL:$1|eyn bewarking|$1 bewarkingen}} weaderümmedraien",
        "rollbacklinkcount-morethan": "Meer as {{PLURAL:$1|één bewarking|$1 bewarkingen}} weerummedreien",
        "rollbackfailed": "Wieziging herstellen is mislokt",
        "cantrollback": "De wiezigingen konnen niet hersteld wörden; der is mer 1 auteur.",
        "rollback-success": "Wiezigingen van $1; weerummedreid naor de leste versie van $2.",
        "sessionfailure-title": "Sessiefout",
        "sessionfailure": "Der is n probleem mit joew anmeldsessie. De aksie is stop-ezet uut veurzörg tegen n beveiligingsrisico (dat besteet uut t meugelike \"kraken\" van disse sessie). Gao weerumme naor de veurige zied, laoj disse zied opniej en probeer t nog es.",
-       "protectlogpage": "Beveiligingslogboek",
+       "protectlogpage": "Beveiligingslogbook",
        "protectlogtext": "Hieronder staon de leste wiezigingen veur t blokkeren en vriegeven van artikels en ziejen.\nZie de [[Special:ProtectedPages|lieste mit ziejen die beveiligd bin]] veur t hele overzicht.",
        "protectedarticle": "[[$1]] is beveiligd",
        "modifiedarticleprotection": "beveiligingsnivo van \"[[$1]]\"  ewiezigd",
        "blanknamespace": "(Höyvdnaamruumde)",
        "contributions": "{{GENDER:$1|Brukersbydragen}}",
        "contributions-title": "Biedragen van $1",
-       "mycontris": "Myn bydragen",
+       "mycontris": "Bydragen",
        "anoncontribs": "Bydragen",
        "contribsub2": "Veur {{GENDER:$3|$1}} ($2)",
        "nocontribs": "Gien wiezigingen evunnen die an de estelde criteria voldoon.",
        "uctop": "leste wieziging",
-       "month": "Maond:",
-       "year": "Jaor:",
+       "month": "Månd:",
+       "year": "Vanaf jår (en eyrer):",
        "sp-contributions-blocklog": "blokkeerlogboek",
        "sp-contributions-deleted": "vortedaone gebrukersbiedragen",
        "sp-contributions-uploads": "nieje bestaanden",
        "sp-contributions-newonly": "Allinnig nieje ziejen laoten zien",
        "sp-contributions-submit": "Zeuk",
        "whatlinkshere": "Verwysingen hyrhinne",
-       "whatlinkshere-title": "Ziejen die verwiezen naor \"$1\"",
-       "whatlinkshere-page": "Zied:",
-       "linkshere": "Disse ziejen verwiezen naor '''$2''':",
+       "whatlinkshere-title": "Syden dee nå \"$1\" verwyset",
+       "whatlinkshere-page": "Syde:",
+       "linkshere": "De volgende syden verwysen nå <strong>$2</strong>:",
        "nolinkshere": "Gien enkele zied verwis naor '''$2'''.",
        "nolinkshere-ns": "Gien enkele zied verwis naor '''$2''' in de ekeuzen naamruumte.",
-       "isredirect": "deurverwiezing",
-       "istemplate": "in-evoegd as mal",
-       "isimage": "bestaandsverwiezing",
-       "whatlinkshere-prev": "{{PLURAL:$1|veurige|veurige $1}}",
+       "isredirect": "döärverwysing",
+       "istemplate": "invoogd as mal",
+       "isimage": "bestandsverwysing",
+       "whatlinkshere-prev": "{{PLURAL:$1|vöärige|vöärige $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|volgende|volgende $1}}",
-       "whatlinkshere-links": "← verwiezingen",
-       "whatlinkshere-hideredirs": "$1 deurverwiezingen",
-       "whatlinkshere-hidetrans": "Vörlageninbinnungen $1",
-       "whatlinkshere-hidelinks": "Lenken $1",
+       "whatlinkshere-links": "← verwysingen",
+       "whatlinkshere-hideredirs": "$1 döärverwysingen",
+       "whatlinkshere-hidetrans": "$1 invoogde mallen",
+       "whatlinkshere-hidelinks": "$1 verwysingen",
        "whatlinkshere-hideimages": "$1 holderverwysingen",
        "whatlinkshere-filters": "Filters",
        "autoblockid": "Automatiese blokkering #$1",
        "semiprotectedpagemovewarning": "'''Waorschuwing:''' disse zied kan allinnig deur eregistreerden gebrukers herneumd wörden.\nDe leste logboekregel steet hieronder:",
        "move-over-sharedrepo": "== t Bestaand besteet al ==\n[[:$1]] besteet al in de edeelden mediadatabanke. A'j n bestaand naor disse titel herneumen, dan ku'j  t edeelden bestaand niet gebruken.",
        "file-exists-sharedrepo": "Disse bestaandsnaam besteet al in de edeelden mediadatabanke.\nKies n aandere bestaandsnaam.",
-       "export": "Ziejen exporteren",
+       "export": "Syden eksporteren",
        "exporttext": "De tekste en geschiedenisse van n zied of n koppel ziejen kunnen in XML-formaot uutevoerd wörden. Dit bestaand ku'j daornao exporteren naor n aandere MediaWiki deur de [[Special:Import|invoerzied]] te gebruken.\n\nZet in t onderstaonde veld de namen van de ziejen die'j exporteren willen, één zied per regel, en geef an o'j alle versies mit de bewarkingssamenvatting exporteren willen of allinnig de leste versies mit de bewarkingssamenvatting.\n\nA'j dat leste doon willen dan ku'j oek n verwiezing gebruken, bieveurbeeld [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] veur de zied \"[[{{MediaWiki:Mainpage}}]]\".",
        "exportall": "Alle ziejen exporteren",
        "exportcuronly": "Allinnig de actuele versie, niet de veurgeschiedenisse",
        "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|versie|versies}} van $2",
        "javascripttest": "JavaScript testen",
        "javascripttest-qunit-intro": "Zie de [$1 testdokumentasie] op mediawiki.org.",
-       "tooltip-pt-userpage": "{{GENDER:|Oew}} gebroekersziede",
+       "tooltip-pt-userpage": "{{GENDER:|Juw}} gebrukerssyde",
        "tooltip-pt-anonuserpage": "Gebroekersbladziede vuur t IP-adres da'j broekt",
-       "tooltip-pt-mytalk": "{{GENDER:|Oew}} oaverlegziede",
+       "tooltip-pt-mytalk": "{{GENDER:|Juw}} oaverlegsyde",
        "tooltip-pt-anontalk": "Oaverlegbladziede van n naamlozen gebroeker van dit IP-adres",
-       "tooltip-pt-preferences": "{{GENDER:|Miene}} vuurkeuren",
-       "tooltip-pt-watchlist": "Lieste van zieden die op miene volglieste stoan",
-       "tooltip-pt-mycontris": "Oaverzicht van {{GENDER:|oew}} biejdreagen",
-       "tooltip-pt-login": "Jy wördet van harte uutnöygd üm ju an te melden as bruker, mär et is neet verplicht",
-       "tooltip-pt-logout": "Ofmaelden",
+       "tooltip-pt-preferences": "{{GENDER:|Juw}} instellingen",
+       "tooltip-pt-watchlist": "Lyste van syden dee up myn volglyste stån",
+       "tooltip-pt-mycontris": "Oaversicht van {{GENDER:|juw}} bydragen",
+       "tooltip-pt-login": "Jy wördet van harte nöygd üm ju an te melden as gebruker, mär et is neet verplichted",
+       "tooltip-pt-logout": "Afmelden",
        "tooltip-pt-createaccount": "Skryv juw eigen vöäral in en meld juw eigen an. Dit is lykewels neet verplicht.",
        "tooltip-ca-talk": "Låt een oaverlegtekst oaver disse syde seen",
        "tooltip-ca-edit": "Bewark disse syde",
        "tooltip-ca-addsection": "Ny underwarp tovogen",
-       "tooltip-ca-viewsource": "Disse ziede is beveiligd taegen veraanderen. Iej könt wal kieken noar de ziede",
+       "tooltip-ca-viewsource": "Disse syde is beveiligd. \nJy künnet wel de bronkode wel bekyken.",
        "tooltip-ca-history": "Oldere versys van disse syde",
        "tooltip-ca-protect": "Beveilig disse ziede taegen veraanderen",
        "tooltip-ca-unprotect": "De beveiliging vuur disse ziede wiezigen",
        "tooltip-ca-delete": "Smiet disse ziede vort",
        "tooltip-ca-undelete": "Haal n inhoald van disse ziede oet n emmer",
-       "tooltip-ca-move": "Gef disse ziede nen aanderen titel",
+       "tooltip-ca-move": "Disse syde hernömen",
        "tooltip-ca-watch": "Voog disse syde to an juw volglyste",
        "tooltip-ca-unwatch": "Smiet disse ziede van oewe voalglieste",
        "tooltip-search": "{{SITENAME}} döärsöken",
        "tooltip-t-recentchangeslinked": "Pas verrichte veranderingen dee nå disse syde verwyset",
        "tooltip-feed-rss": "RSS-voer vuur disse ziede",
        "tooltip-feed-atom": "Atom-voer vuur disse ziede",
-       "tooltip-t-contributions": "Lieste met biejdreagen van {{GENDER:$1|disse gebroeker}}",
+       "tooltip-t-contributions": "Een lyste mid bydragen van {{GENDER:$1|disse gebruker}}",
        "tooltip-t-emailuser": "Stüür disse {{GENDER:$1|gebruker}} een netpostbericht",
        "tooltip-t-info": "Meer informasie over disse zied",
        "tooltip-t-upload": "Laad afbealdingen en/of gelüüdsmateriaal",
        "tooltip-t-print": "De afdrükbåre versy van disse syde",
        "tooltip-t-permalink": "Permanente verwysing nå disse versy van de syde",
        "tooltip-ca-nstab-main": "Låt een tekst van et artikel seen",
-       "tooltip-ca-nstab-user": "Loat de gebroekersbladziede zeen",
+       "tooltip-ca-nstab-user": "Gebrukerssyde bekyken",
        "tooltip-ca-nstab-media": "Loat n mediatekst zeen",
        "tooltip-ca-nstab-special": "Dit is een bysündere syde dee jy neet veranderen künt",
-       "tooltip-ca-nstab-project": "Loat de projektbladziede zeen",
+       "tooltip-ca-nstab-project": "Projektsyde bekyken",
        "tooltip-ca-nstab-image": "Låt de bestandssyde seen",
        "tooltip-ca-nstab-mediawiki": "Loat de systeemtekstbladziede zeen",
-       "tooltip-ca-nstab-template": "Loat de malbladziede zeen",
+       "tooltip-ca-nstab-template": "Mal bekyken",
        "tooltip-ca-nstab-help": "Loat de hölpbladziede zeen",
        "tooltip-ca-nstab-category": "Låt de kategorysyde seen",
        "tooltip-minoredit": "Markeer as n klaene wieziging",
-       "tooltip-save": "Wiezigingen opsloan",
-       "tooltip-preview": "Bekiek oew versie vuurda'j t opsloan (anbeveulen)!",
-       "tooltip-diff": "Bekiek oew aegen wiezigingen",
+       "tooltip-save": "Wysigingen seakeren",
+       "tooltip-preview": "Bekyk juw wysigingen. Bruuk dit vöärdat jy seakeret.",
+       "tooltip-diff": "Låt seen welke wysigingen jy in de tekst maked hebbet.",
        "tooltip-compareselectedversions": "Bekiek de verschillen tussen de ekeuzen versies.",
        "tooltip-watch": "Voog disse ziede to an oew volglieste",
        "tooltip-watchlistedit-normal-submit": "Ziejen vortdoon",
        "tooltip-watchlistedit-raw-submit": "Volglieste biewarken",
        "tooltip-recreate": "Disse ziede opniej anmaken, ondanks t feit dat t vortdoan is.",
        "tooltip-upload": "Bestaanden opsturen",
-       "tooltip-rollback": "\"Weaderümmedraien\" drait mid eyn klik de bewarking(en) van de lätste bruker up disse syde terügge.",
-       "tooltip-undo": "A'j op \"weerummedreien\" klikken geet t bewaerkingsvaenster lös en kö'j ne vurige versie terugzetten.\nIej könt in de bewaerkingssamenvatting n reden opgeven.",
+       "tooltip-rollback": "\"Weaderümmedraien\" drait mid eyn klik de bewarking(en) van de lätste gebruker up disse syde terügge.",
+       "tooltip-undo": "As jy up \"weaderümmedraien\" klikket geyt et bewarkingsveld lös en kün jy een vöärige versy weaderümmesetten. Jy künnet in de bewarkingssamenvatting een readen upgeaven.",
        "tooltip-preferences-save": "Vuurkeuren opsloan",
-       "tooltip-summary": "Voer ne korte samenvatting in",
+       "tooltip-summary": "Voor een korte samenvatting in",
        "interlanguage-link-title": "$1 – $2",
        "anonymous": "Anonieme {{PLURAL:$1|gebruker|gebrukers}} van {{SITENAME}}",
        "siteuser": "{{SITENAME}}-gebruker $1",
        "spam_reverting": "Bezig mit t weerummezetten naor de leste versie die gien verwiezing hef naor $1",
        "spam_blanking": "Alle wiezigingen mit n verwiezing naor $1 wörden vortehaold",
        "spam_deleting": "In alle versies staon verwiezingen naor $1. Zied vortedaon",
-       "simpleantispam-label": "Antispamkontraole.\nHier <strong>niks</strong> invullen!",
+       "simpleantispam-label": "Antispamkontrole.\nHyr <strong>niks</strong> invüllen!",
        "pageinfo-title": "Informasie over \"$1\"",
        "pageinfo-not-current": "Disse gegevens bin allinnig beschikbaor veur disse versie.",
-       "pageinfo-header-basic": "Baosisinformasie",
+       "pageinfo-header-basic": "Basisinformaty",
        "pageinfo-header-edits": "Bewarkingsgeschiedenisse",
        "pageinfo-header-restrictions": "Ziedbeveiliging",
        "pageinfo-header-properties": "Ziedeigenschappen",
        "filedelete-old-unregistered": "De an-egeven bestaandsversie \"$1\" steet niet in de databanke.",
        "filedelete-current-unregistered": "t An-egeven bestaand \"$1\" steet niet in de databanke.",
        "filedelete-archive-read-only": "De webserver kan niet in de archiefmap \"$1\" schrieven.",
-       "previousdiff": "← veurige wieziging",
-       "nextdiff": "volgende wieziging →",
+       "previousdiff": "← vöärige wysiging",
+       "nextdiff": "volgende wysiging →",
        "mediawarning": "'''Waorschuwing:''' in dit bestaand zit misschien kodering die slicht is veur t systeem.",
        "imagemaxsize": "Maximale aofmetingen van aofbeeldingen:<br />\n''(veur op de beschrievingszied)''",
        "thumbsize": "Grootte van de miniatuuraofbeelding:",
        "file-info": "Bestaandsgrootte: $1, MIME-type: $2",
        "file-info-size": "$1 × $2 bealdpunten, bestandsgroutde: $3, MIME-type: $4",
        "file-info-size-pages": "$1 × $2 beeldpunten, bestaandsgrootte: $3, MIME-type: $4, $5 {{PLURAL:$5|zied|ziejen}}",
-       "file-nohires": "Gien hogere resolusie beschikbaor.",
-       "svg-long-desc": "SVG-bestaand, uutgangsgrootte $1 × $2 beeldpunten, bestaandsgrootte: $3",
+       "file-nohires": "Geen hougere resoluty beskikbår.",
+       "svg-long-desc": "SVG-bestand, uutgangsgroutde $1 × $2 bealdpunten, bestandsgroutde: $3",
        "svg-long-desc-animated": "Bewegend SVG-bestaand, uutgangsgrootte $1 × $2 beeldpunten, bestaandsgrootte: $3",
        "svg-long-error": "Ongeldig SVG-bestaand: $1",
        "show-big-image": "Oorsprungelik bestand",
        "version-hook-name": "Hooknaam",
        "version-hook-subscribedby": "In-eschreven deur",
        "version-version": "($1)",
-       "version-license": "MediaWiki-lisensie",
+       "version-license": "MediaWiki-licensy",
        "version-ext-colheader-version": "Versie",
        "version-ext-colheader-license": "Lisensie",
        "version-ext-colheader-description": "Beschrieving",
        "version-entrypoints-header-entrypoint": "Ingang",
        "version-entrypoints-header-url": "Webadres",
        "redirect": "Deurverwiezen op bestaandsnaam, gebrukers-, zied-, versie- of logboekregelnummer",
-       "redirect-summary": "Disse speciale syde verwist döär nå een bestand (as de bestandsname upgeaven wördt), een syde (as een syd- of versynummer upgeaven wördt), een brukerssyde (as et brukersnummer upgeaven wördt) of een logbookinskryving (as een logbooknummer upgeaven wördt). Gebruuk: [[{{#Special:Redirect}}/file/Vöärbeald.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] of [[{{#Special:Redirect}}/logid/186]].",
+       "redirect-summary": "Disse speciale syde verwist döär nå een bestand (as de bestandsname upgeaven wördt), een syde (as een syd- of versynummer upgeaven wördt), een gebrukerssyde (as et gebrukersnummer upgeaven wördt) of een logbookinskryving (as een logbooknummer upgeaven wördt). Gebruuk: [[{{#Special:Redirect}}/file/Vöärbeald.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] of [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Zeuk",
        "redirect-lookup": "Opzeuken:",
        "redirect-value": "Weerde:",
        "htmlform-no": "Nee",
        "htmlform-yes": "Ja",
        "htmlform-chosen-placeholder": "Kies n opsie",
-       "logentry-delete-delete": "$1 hevt de syde $3 {{GENDER:$2|vordedån}}",
+       "logentry-delete-delete": "$1 hevt de syde $3 {{GENDER:$2|vorddån}}",
        "logentry-delete-restore": "$1 {{GENDER:$2|hevt}} de syde $3 ($4) weaderümmesetted",
        "logentry-delete-event": "$1 hef de zichtbaorheid van {{PLURAL:$5|n logboekregel|$5 logboekregels}} van $3 {{GENDER:$2|ewiezigd}}: $4",
        "logentry-delete-revision": "$1 hef de zichtbaorheid van {{PLURAL:$5|een versie|$5 versies}} van de zied $3 {{GENDER:$2|ewiezigd}}: $4",
        "logentry-rights-rights": "$1 {{GENDER:$2|hef}} groepslidmaotschap veur $3 ewiezigd van $4 naor $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|hef}} t groepslidmaotschap ewiezigd veur $3",
        "logentry-rights-autopromote": "$1 {{GENDER:$2|is}} automaties bevorderd van $4 tot $5",
-       "logentry-upload-upload": "$1 hef $3 {{GENDER:$2|op-estuurd}}",
+       "logentry-upload-upload": "$1 hevt $3 {{GENDER:$2|upladen}}",
        "logentry-upload-overwrite": "$1 {{GENDER:$2|hef}} n nieje versie van $3 op-elaojen",
        "rightsnone": "(gien)",
        "feedback-adding": "Joew kommentaar wörden op de zied ezet...",
index 992e7e1..997db7b 100644 (file)
        "whatlinkshere-next": "{{PLURAL:$1|nächste|nächste $1}}",
        "whatlinkshere-links": "← Lenken",
        "whatlinkshere-hideredirs": "$1 Redirects",
-       "whatlinkshere-hidetrans": "$1 in-evoegden mallen",
-       "whatlinkshere-hidelinks": "$1 verwiezingen",
+       "whatlinkshere-hidetrans": "Vörlageninbinnungen $1",
+       "whatlinkshere-hidelinks": "Lenken $1",
        "whatlinkshere-hideimages": "$1 bestaandsverwiezingen",
        "whatlinkshere-filters": "Filters",
        "autoblockid": "Autoblock #$1",
index 5fb8323..c67bab3 100644 (file)
        "mycustomjsredirectprotected": "U hebt geen rechten om deze JavaScriptpagina te bewerken omdat het een doorverwijzing is en deze niet verwijst naar een pagina in uw gebruikersruimte.",
        "easydeflate-invaliddeflate": "De opgegeven inhoud is onjuist gecomprimeerd",
        "unprotected-js": "Vanwege veiligheidsredenen kan er geen JavaScript geladen worden vanaf onbeveiligde pagina's. Gelieve alleen JavaScript pagina's aan te maken in de MediaWiki: naamruimte of als een subpagina van een gebruikerspagina.",
-       "userlogout-continue": "Wilt u zich afmelden?"
+       "userlogout-continue": "Wilt u zich afmelden?",
+       "rest-wrong-method": "De requestmethode ($1) is {{PLURAL:$3|niet de toegestane methode voor dit pad|een van de toegestane methoden voor dit pad}} ($2)"
 }
index eb628a8..41bccb3 100644 (file)
        "createaccountmail": "ߓߍ߲߬ߛߋ߲߬ߡߊ߬ ߕߊߡߌ߲ߞߊ߲ ߕߎ߬ߡߊ߬ߞߎ߲߬ߡߊ ߟߊߓߊ߯ߙߊ߫ ߞߊ߬ ߓߊ߲߫ ߞߵߊ߬ ߗߋ߫ ߢߎߡߍߙߋ߲߫ ߞߏ߲ߘߏ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲ ߡߊ߬.",
        "createaccountmail-help": "ߊ߬ ߕߍ߫ ߛߐ߲߬ ߠߊߓߊ߯ߙߊ߫ ߟߊ߫߸ ߞߊ߬ ߡߐ߰ ߜߘߍ߫ ߟߊ߫ ߖߊ߬ߕߋ߬ߘߊ ߛߌ߲ߘߌ߫߸ ߣߴߌ ߡߊ߫ ߕߊ߬ߡߌ߲߬ߞߊ߲ ߞߊ߬ߙߊ߲߬.",
        "createacct-realname": "ߕߐ߮ ߓߘߍ (ߛߎߥߊ߲ߘߟߌ)",
-       "createacct-reason": "ß\8a߬ ß\9bß\8aß\93ß\8eß«",
+       "createacct-reason": "ß\8a߬ ß\9eß\8e߲߭",
        "createacct-reason-ph": "ߡߎ߲߬ߠߊ߫ ߌ ߦߋ߫ ߖߊ߬ߕߋ߬ߘߊ߰ ߜߘߍ߫ ߛߌ߲ߘߌ߫ ߟߊ߫",
        "createacct-reason-help": "ߗߋߛߓߍ ߦߌ߬ߘߊ߬ ߖߊ߬ߕߋ߬ߘߊ ߛߌ߲ߘߟߌ ߘߊ߲ߖߐ ߘߐ߫",
        "createacct-submit": "ߖߊ߬ߕߋ߬ߘߊ ߘߏ߫ ߘߊߦߟߍ߬",
        "newarticle": "(ߞߎߘߊ)",
        "newarticletext": "ߌ ߓߘߊ߫ ߛߘߌ߬ߜߋ߲ ߘߏ߫ ߟߊߓߊ߬ߕߏ߬ ߞߐߜߍ ߘߏ߫ ߘߐ߫߸ ߡߍ߲ ߕߴߦߋ߲߬ ߡߎߣߎ߲߬.\nߣߵߌ ߦߴߊ߬ ߝߍ߫ ߞߊ߬ ߞߐߜߍ ߘߏ߫ ߟߊߘߊ߲߫߸ ߛߓߍߟߌ ߘߊߡߌ߬ߣߊ߬ ߘߎ߰ߟߊ߬ߘߐ߫ ߞߏ߲ߘߏ ߘߐ߫ (ߞߊ߬ [$1 ߘߍ߬ߡߍ߲߬ߠߌ߲ ߞߐߜߍ] ߦߋ߫߸ ߖߐ߲߬ߛߊ߬ ߌ ߘߌ߫ ߞߌ߬ߓߊ߬ߙߏ߬ ߖߐ߲ߖߐ߲ ߛߐ߬ߘߐ߲߬). ߣߵߌ ߘߏ߲߬ ߞߍ߫ ߘߊ߫ ߦߊ߲߬ ߝߎ߬ߕߎ߲߬ߕߌ߬ ߓߟߏߡߊ߬߸ ߌ ߟߊ߫ ߛߏ߲߯ߓߊߟߊ߲ <strong>back</strong> ߛߐ߲߬ߞߌ߲߫.",
        "anontalkpagetext": "----\n<em>ߓߊ߬ߘߏ߬ ߞߐߜߍ ߣߌ߲߬ ߦߋ߫ ߟߊߓߊ߯ߙߟߊ߫ ߟߐ߲ߓߊߟߌ ߟߋ߬ ߓߟߏ߫ ߡߍ߲ ߡߊ߫ ߖߊ߬ߕߋ߬ߘߊ߬ ߛߌ߲ߘߌ߫ ߡߎߣߎ߲߬ ߥߟߴߊ߬ ߕߍ߫ ߖߊ߬ߕߋ߬ߘߊ ߏ߬ ߟߊߓߊ߯ߙߊ߫ ߟߊ߫;</em>\nߏ߬ ߞߏߛߐ߲߬ ߊ߲ ߞߊ߫ ߞߊ߲߫ ߞߵߊ߬ ߟߊ߫ ߓߡ (ߓߟߐߟߐ ߡߛߍ߬ߞߍ߬ߡߛߍߞߍ) ߛߊ߲߬ߓߊ߬ߕߐ߮ ߟߊߓߊ߯ߙߴߊ߬ ߡߊߟߐ߲߫ ߞߊߡߊ߬߸ ߟߊߓߊ߯ߙߟߊ߫ ߛߌߦߊߡߊ߲߫ ߓߴߛߋ߫ ߞߊ߬ ߘߍ߬ ߓߡ ߛߊ߲߬ߓߊ߬ߕߐ߮ ߣߌ߲߬ ߢߐ߲߰ ߠߊ߫.\nߣߴߌ ߞߍ߫ ߘߊ߫ ߟߊߓߊ߯ߙߟߊ߫ ߡߊߝߟߌ߬ߣߍ߲߫ ߘߌ߫ ߞߵߊ߬ ߛߏ߬ߓߌ߬ ߞߏ߫ ߌ ߟߊ߫ ߞߊ߲߬ߞߎߡߊ ߟߎ߬ ߕߴߌ ߕߊ߫ ߘߌ߫ ߊ߬ ߘߌ߫ ߟߐ߬ ߌ ߡߊ߬ ߌߞߘߐ߫߸ ߌ ߖߏ߫ ߞߊ߬ [[Special:CreateAccount|ߖߊ߬ߕߋ߬ߘߊ ߘߏ߫ ߘߊߦߟߍ߬]] ߥߟߊ߫ [[Special:UserLogin|ߞߊ߬ ߘߏ߲߬ߕߐ߰ߟߊ߬ߘߏ߲ ߞߍ߫]] ߖߐ߲߬ߛߊ߫ ߟߏ߲ߘߐ߬ ߓߊߛߌ߯ߓߊߟߌߦߊ ߘߌ߫ ߡߟߊ߫ ߟߊߓߊ߯ߙߟߊ߫ ߡߊߟߐ߲ߓߊߟߌ߫ ߜߘߍ ߟߎ߬ ߓߟߏ߫.",
-       "noarticletext": "ß\9bß\93ß\8dß\9fß\8cß« ß\9bß\8cß« ß\95ß\8dß« ß\9eß\90ß\9cß\8d ß£ß\8c߲߭ ß\9eß\8a߲߬ ß\95ß\8b߲߫. ß\8c ß\98ß\8cß« ß\9bß\8bß« ß\9eß\90ß\9cß\8d ß£ß\8c߲߬ \n [[Special:Search/{{PAGENAME}}|search for this page title]] ß\95ß\90ß® ß¢ß\8cߣß\8c߲߫ ß ß\8aß« ß\9eß\90ß\9cß\8d ß\95ß\90ß­ ß\9fß\8e߬ ß\98ß\90ß«߸  \n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs],\nor [{{fullurl:{{FULLPAGENAME}}|action=edit}} create this page]</span>.",
+       "noarticletext": "ß\9bß\93ß\8dß\9fß\8cß« ß\9bß\8cß« ß\95ß\8dß« ß\9eß\90ß\9cß\8d ß£ß\8c߲߭ ß\9eß\8a߲߬ ß\95ß\8b߲߬. ß\8c ß\98ß\8cß« ß\9bß\8bß« ß\9eß\90ß\9cß\8d ß£ß\8c߲߬.\n[[Special:Search/{{PAGENAME}}|search for this page title]] ß\95ß\90ß® ß¢ß\8cߣß\8c߲߫ ß ß\8aß« ß\9eß\90ß\9cß\8d ß\95ß\90ß­ ß\9fß\8e߬ ß¢ß\8aß\9dß\8d߬߸  \n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs],\nor [{{fullurl:{{FULLPAGENAME}}|action=edit}} create this page]</span>.",
        "noarticletext-nopermission": "ߛߓߍߟߌ߫ ߛߌ߫ ߕߍ߫ ߞߐߜߍ ߣߌ߲߭ ߞߊ߲߬ ߕߋ߲߫.\nߌ ߘߌ߫ ߛߋ߫ [[Special:Search/{{PAGENAME}}|search for this page title]] ߢߌߣߌ߲߫ ߠߊ߫ ߞߐߜߍ ߕߐ߭ ߟߎ߬ ߘߐ߫߸ ߥߟߊ߫ <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs]</span> ߞߏ߬ߣߌ߲߬ ߘߌ߬ߢߍ߬ ߞߍߣߍ߲߫ ߕߴߌ ߡߊ߬ ߞߐߜߍ߫ ߣߌ߲߬ ߠߊߞߊ߭ ߘߐ߫.",
        "userpage-userdoesnotexist": "ߖߊ߬ߕߋ߬ߘߊ߬ ߟߊߓߊ߯ߙߕߊ \"$1\" ߛߌ߲ߘߌߣߍ߲߫ ߕߍ߫. \nߝߛߍ߬ߝߛߍ߬ߟߌ ߞߍ߫߸ ߣߴߌ ߦߴߊ߬ ߝߍ߬ ߞߊ߬ ߞߐߜߍ ߣߌ߲߬ ߛߌ߲ߘߌ߫/ߡߊߦߟߍ߬ߡߊ߲߫.",
        "userpage-userdoesnotexist-view": "ߟߊ߬ߓߊ߰ߙߊ߬ ߖߊߕߋߘߊ \"$1\" ߟߊߞߎ߲߬ߘߎ߬ߣߍ߲߫ ߕߍ߫.",
        "recentchanges-label-unpatrolled": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߣߌ߲߬ ߡߊ߫ ߓߍ߬ߙߍ߲߬ߓߍ߬ߙߍ߲߬ ߡߎߣߎ߲߬",
        "recentchanges-label-plusminus": "ߞߐߜߍ ߢߊ߲ߞߊ߲ ߓߘߊ߫ ߡߊߦߟߍ߬ߡߊ߲߫ ߞߵߊ߬ ߝߌ߬ߘߊ߲ ߦߙߌߞߊ ߣߌ߲߬ ߘߌ߫",
        "recentchanges-legend-heading": "<strong>ߡߊ߬ߛߙߋ:</strong>",
-       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ߣß\8c߲߬ ß\9dߣß\8aß« ß¦ß\8bß« \n[[Special:NewPages|list of new pages]])",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ߣß\8c߲߬ ß\9dߣß\8aß« ß\93ߴߦß\8b߲߬ [[Special:NewPages|list of new pages]])",
        "recentchanges-submit": "ߊ߬ ߦߌ߬ߘߊ߬",
        "rcfilters-tag-remove": "$1 ߛߋ߲߬ߓߐ߫",
        "rcfilters-legend-heading": "<strong>ߟߊ߬ߘߛߏ߬ߟߌ ߛߙߍߘߍ</strong>",
index afbfd94..6b141f9 100644 (file)
        "perfcached": "Like {{msg-mw|perfcachedts}} but used when we do not know how long ago page was cached (unlikely to happen).\n\nParameters:\n* $1 - the max result cut off ($wgQueryCacheLimit)",
        "perfcachedts": "Used on pages that list page lists for which the displayed data is cached. Parameters:\n* $1 - a time stamp (date and time combined)\n* $2 - a date (optional)\n* $3 - a time (optional)\n* $4 - the cut off limit for cached results ($wgQueryCacheLimit). If there are more then this many results for the query, only the first $4 of those will be listed on the page. Usually $4 is about 1000.",
        "querypage-no-updates": "Text on some special pages, e.g. [[Special:FewestRevisions]].",
+       "querypage-updates-periodical": "Text on some special pages which are configurated with a periodical run of a maintenance script.\n\nSee also {{msg-mw|querypage-no-updates}}.",
        "viewsource": "The text displayed in place of the {{msg-mw|Edit}} tab when the user has no permission to edit the page.\n\nSee also:\n* {{msg-mw|Viewsource}}\n* {{msg-mw|Accesskey-ca-viewsource}}\n* {{msg-mw|Tooltip-ca-viewsource}}\n{{Identical|View source}}",
        "viewsource-title": "Page title shown when trying to edit a protected page. Parameters:\n* $1 - the name of the page",
        "actionthrottled": "This is the title of an error page. Read it in combination with {{msg-mw|actionthrottledtext}}.",
index b846a3f..59c63b7 100644 (file)
@@ -34,7 +34,7 @@
        "tog-watchrollback": "Aggiunge le pàggene addò agghie fatte 'n'annullamende jndr'à l'elenghe de le pàggene condrollate",
        "tog-minordefault": "Pe convenzione signe tutte le cangiaminde cumme stuédeche",
        "tog-previewontop": "Fa vedè l'andeprime apprime d'a caselle de le cangiaminde",
-       "tog-previewonfirst": "Fà vedè l'andeprime sus a 'u prime cangiamende",
+       "tog-previewonfirst": "Fà 'ndrucà l'andeprime sus a 'u prime cangiamende",
        "tog-enotifwatchlistpages": "Manneme 'na mail quanne 'a pàgene ca stoche a condrolle ha cangiate",
        "tog-enotifusertalkpages": "Manneme 'na mail quanne 'a pàgene de le 'ngazzaminde ha cangiate",
        "tog-enotifminoredits": "Manneme 'na mail quanne onne state fatte cangiaminde stuèdeche sus a le pàggene",
@@ -54,7 +54,7 @@
        "tog-watchlisthidepatrolled": "Scunne le cangiaminde condrollate jndr'à l'elenghe de le pàggene condrollate",
        "tog-watchlisthidecategorization": "Scunne 'a categorizzazzione d'a vôsce",
        "tog-ccmeonemails": "Manneme 'na copie de le mail ca je manne a l'ôtre utinde",
-       "tog-diffonly": "No fà vedè le pàggene cu le condenute sotte a le differenze",
+       "tog-diffonly": "No fà 'ndrucà le pàggene cu le condenute sotte a le differenze",
        "tog-showhiddencats": "Fa vedè le categorije scunnute",
        "tog-norollbackdiff": "Non sce penzanne a le differenze apprisse l'esecuzione de 'nu rollback",
        "tog-useeditwarning": "Avvisave quanne jie lasse 'na pàgene cangiate senze ca agghie sarvate le cangiaminde",
        "viewsourcelink": "vide 'u sorgende",
        "editsectionhint": "Cange 'a sezione: $1",
        "toc": "Condenute",
-       "showtoc": "fà vedè",
+       "showtoc": "fà 'ndrucà",
        "hidetoc": "scunne",
        "collapsible-collapse": "Scunne",
        "collapsible-expand": "Spanne",
        "rev-deleted-diff-view": "Une de le revisiune de sta differenze ha state '''scangellate'''.\nTu puè vedè ste differenze; pò essere ca stonne cchiù 'mbormaziune jndr'à l'[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} archivie de le scangellaminde].",
        "rev-suppressed-diff-view": "Une de le revisiune de sta differenze ha state '''soppresse'''.\nTu puè vedè ste differenze; pò essere ca stonne cchiù 'mbormaziune jndr'à l'[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} archivie de le soppressiune].",
        "rev-delundel": "fa vedè/scunne",
-       "rev-showdeleted": "fà vedè",
+       "rev-showdeleted": "fà 'ndrucà",
        "revisiondelete": "Scangille/Repristine revisiune",
        "revdelete-nooldid-title": "Revisione de destinazione invalida",
        "revdelete-nooldid-text": "Tu non g'è specificate 'na revisione de arrive pe abbilità sta funzione, o a specifica revisione non g'esiste oppure tu stè pruève a scunnè 'a revisiona corrende.",
        "mergehistory-into": "Pàgene de destinazione:",
        "mergehistory-list": "cangiamende d'a storie scuagghiabbele",
        "mergehistory-merge": "Le seguende revisiune de [[:$1]] ponne essere scuagghiate jndr'à [[:$2]].\nAuse 'a coulonne cu le radio buttone pe scacchià de scuagghià le revisiune ccrejate apprime de n'nu certe mumende.\nVide Bbuene ca ausanne le collegaminde de navigazzione sta culonne avène azzerate.",
-       "mergehistory-go": "Fà vedè le cangiaminde ca se ponne squagghià 'nzieme",
+       "mergehistory-go": "Fà 'ndrucà le cangiaminde ca se ponne squagghià 'nzieme",
        "mergehistory-submit": "Scuagghije 'nzieme le revisiune",
        "mergehistory-empty": "Nisciuna revisione pò essere scuagghiate.",
        "mergehistory-done": "$3 {{PLURAL:$3|revisione|revisiune}} de $1 {{PLURAL:$3|ha|onne}} state scuagghiate jndr'à [[:$2]].",
        "difference-multipage": "(Differenze 'mbrà le pàggene)",
        "lineno": "Linea $1:",
        "compareselectedversions": "Combronde le versiune selezionete",
-       "showhideselectedversions": "Fà vedè/scunne le revisiune selezionate",
+       "showhideselectedversions": "Fà 'ndrucà/scunne le revisiune scacchiate",
        "editundo": "annulle",
        "diff-empty": "(Nisciuna differenze)",
        "diff-multi-sameuser": "({{PLURAL:$1|'Na revisione de mmienze|$1 revisiune de mmienze}} da 'u stesse utende non g'avènene fatte vedè)",
        "next-page": "pàgena successive",
        "prevn-title": "Precedende $1 {{PLURAL:$1|resultete|resultete}}",
        "nextn-title": "Successive $1 {{PLURAL:$1|resultete|resultete}}",
-       "shown-title": "Fà vedè le $1 {{PLURAL:$1|resultete|resultete}} pe pàgene",
+       "shown-title": "Fà 'ndrucà le $1 {{PLURAL:$1|resultate}} pe pàgene",
        "viewprevnext": "Vide ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''Stè 'na pàgene nnumenete \"[[$1]]\" sus 'a sta Uicchipèdie'''",
        "searchmenu-new": "<strong>[[:$1|Ccreje]] 'a pàgene \"[[:$1|$1]]\" sus 'a sta Uicchipèdie!</strong> 'Ndruche pure {{PLURAL:$2|0=|'a pàgene acchiate cu 'a ricerca toje.|le resultate acchiate da 'a ricerche.}}",
        "prefs-editwatchlist-edit": "'Ndruche e live le titole da l'elenghe de le pàggene condrollate",
        "prefs-editwatchlist-raw": "Cange 'a liste de le pàggene condrollate grezze",
        "prefs-editwatchlist-clear": "Sdevache l'elenghe de le pàggene condrollate tune",
-       "prefs-watchlist-days": "Giurne da fà vedè jndr'à liste de le pàggene condrollete:",
+       "prefs-watchlist-days": "Sciurne da fà 'ndrucà jndr'à l'elenghe de le pàggene condrollate:",
        "prefs-watchlist-days-max": "Massime $1 {{PLURAL:$1|sciurne|sciurne}}",
        "prefs-watchlist-edits": "Numere massime de cangiaminde ca se ponne fa 'ndrucà jndr'à l'lenghe de le pàggene condrollate:",
        "prefs-watchlist-edits-max": "Numere massime: 1000",
        "stub-threshold": "Soglie pe collegamende stub de formattazione ($1):",
        "stub-threshold-sample-link": "esembie",
        "stub-threshold-disabled": "Disabbilitate",
-       "recentchangesdays": "Sciurne da fà vedè jndr'à le cangiaminde recende:",
+       "recentchangesdays": "Sciurne da fà 'ndrucà jndr'à le cangiaminde recende:",
        "recentchangesdays-max": "(massime $1 {{PLURAL:$1|sciurne|sciurne}})",
-       "recentchangescount": "Numere de cangiaminde da fà vedè pe default:",
+       "recentchangescount": "Numere de cangiaminde da fà 'ndrucà pe default:",
        "prefs-help-recentchangescount": "Numere massime: 1000",
        "prefs-help-watchlist-token2": "Queste jè 'a chiave segrete a le feed d'u web de l'elenghe de le pàggene condrollate tune.\nCengate vò ccu canosce ce pò leggere l'elenghe de le pàggene condrollate tune, accussì non g'ù pò condividere.\nCe è abbesogne, [[Special:ResetTokens|'u puè azzerà]].",
        "savedprefs": "Le preferenze tue onne state aggiornete.",
        "yourrealname": "Nome vere:",
        "yourlanguage": "Lènga:",
        "yourvariant": "Variande d'u condenute d'a lènghe:",
-       "prefs-help-variant": "Tu preferisce variande o ortografije da fà vedè le vôsce de sta uicchi.",
+       "prefs-help-variant": "Tu preferisce variande o ortografije da fà 'ndrucà le vôsce de sta uicchi.",
        "yournick": "Firma toje:",
        "prefs-help-signature": "Le commende sus a le pàggene de le 'ngazzaminde avessere a essere sgnate cu \"<nowiki>~~~~</nowiki>\" ca pò avène convertite cu 'a firma toje e l'orarie.",
        "badsig": "'A firme grezze jè sbagliete.\nCondrolle le tag HTML.",
        "prefs-advancedrendering": "Opzione avanzate",
        "prefs-advancedsearchoptions": "Opzione avanzate",
        "prefs-advancedwatchlist": "Opzione avanzate",
-       "prefs-displayrc": "Fà vedè l'opzione",
-       "prefs-displaywatchlist": "Fà vedè l'opzione",
+       "prefs-displayrc": "Fà 'ndrucà l'opzione",
+       "prefs-displaywatchlist": "Fà 'ndrucà l'opzione",
        "prefs-changesrc": "Cangiaminde fatte 'ndrucà",
        "prefs-changeswatchlist": "Cangiaminde fatte 'ndrucà",
        "prefs-pageswatchlist": "Pàggene ca ste condrolle",
        "rcfilters-allcontents-label": "Tutte le condenute",
        "rcfilters-alldiscussions-label": "Tutte le 'ngazzaminde",
        "rcnotefrom": "Sotte {{PLURAL:$5|ste 'u cangiamende|stonne le cangiaminde}} da <strong>$3, $4</strong> ('nzigne a <strong>$1</strong> fatte vedè).",
-       "rclistfrom": "Fà vedè le urteme cangiaminde partenne da $3 $2",
+       "rclistfrom": "Fà 'ndrucà le urteme cangiaminde partenne da $2, $3",
        "rcshowhideminor": "$1 cangiaminde stuèdeche",
-       "rcshowhideminor-show": "Fà vedè",
+       "rcshowhideminor-show": "Fà 'ndrucà",
        "rcshowhideminor-hide": "Scunne",
        "rcshowhidebots": "$1 bot",
-       "rcshowhidebots-show": "Fà vedè",
+       "rcshowhidebots-show": "Fà 'ndrucà",
        "rcshowhidebots-hide": "Scunne",
        "rcshowhideliu": "$1 utinde reggistrate",
-       "rcshowhideliu-show": "Fà vedè",
+       "rcshowhideliu-show": "Fà 'ndrucà",
        "rcshowhideliu-hide": "Scunne",
        "rcshowhideanons": "$1 utende scanusciute",
-       "rcshowhideanons-show": "Fà vedè",
+       "rcshowhideanons-show": "Fà 'ndrucà",
        "rcshowhideanons-hide": "Scunne",
        "rcshowhidepatr": "$1 cangiaminde condrollete",
-       "rcshowhidepatr-show": "Fà vedè",
+       "rcshowhidepatr-show": "Fà 'ndrucà",
        "rcshowhidepatr-hide": "Scunne",
        "rcshowhidemine": "$1 cangiaminde mie",
-       "rcshowhidemine-show": "Fà vedè",
+       "rcshowhidemine-show": "Fà 'ndrucà",
        "rcshowhidemine-hide": "Scunne",
        "rcshowhidecategorization": "$1 categorizzazzione d'a pàgene",
        "rcshowhidecategorization-show": "Fà 'ndrucà",
        "diff": "diff",
        "hist": "cunde",
        "hide": "Scunne",
-       "show": "Fà vedè",
+       "show": "Fà 'ndrucà",
        "minoreditletter": "m",
        "newpageletter": "N",
        "boteditletter": "b",
        "rc-change-size": "$1",
        "rc-change-size-new": "$1 {{PLURAL:$1|byte|byte}} apprisse 'u cangiamende",
        "newsectionsummary": "/* $1 */ seziona nove",
-       "rc-enhanced-expand": "Fà vedè le dettaglie",
+       "rc-enhanced-expand": "Fà 'ndrucà le dettaglie",
        "rc-enhanced-hide": "Scunne le dettaglie",
        "rc-old-title": "origgenariamende ccreajate cumme \"$1\"",
        "recentchangeslinked": "Cangiaminde culleghete",
        "pageswithprop-prophidden-long": "valore d'a probbietà d'u teste lunghe scunnute ($1)",
        "pageswithprop-prophidden-binary": "valore probbietà binarie scunnute ($1)",
        "doubleredirects": "Ridirezionaminde a doppie",
-       "doubleredirectstext": "Sta pàgene elenghe le pàggene ca se ridirezionane sus a otre pàggene de ridirezionaminde.\nOgne righe condene 'nu collegamende a 'u prime e a 'u seconde ridirezionamende pe fà vedè addò arrive 'u seconde ridirezionamende, 'u quale jè normalmende 'a pàgena de destinaziona \"rèale\", addò 'u prime ridirezionamende avesse appondà.\nLe situaziune de <del>ingrocie</del> onne state resolte.",
+       "doubleredirectstext": "Sta pàgene elenghe le pàggene ca se ridirezionane sus a otre pàggene de ridirezionaminde.\nOgne righe condene 'nu collegamende a 'u prime e a 'u seconde ridirezionamende pe fà 'ndrucà addò arrive 'u seconde ridirezionamende, 'u quale jè normalmende 'a pàgena de destinaziona \"rèale\", addò 'u prime ridirezionamende avesse appondà.\nLe situaziune de <del>ingrocie</del> onne state resolte.",
        "double-redirect-fixed-move": "[[$1]] ha state spustate.\nAvène aggiornate automaticamende e mò s'avène redirette a [[$2]].",
        "double-redirect-fixed-maintenance": "Aggiuste le doppie redirezionaminde da [[$1]] a [[$2]] jndr'à 'na fatije de manutenzione",
        "double-redirect-fixer": "Correttore de redirezionaminde",
        "withoutinterwiki": "Pàggene senza collegaminde a otre Uicchi",
        "withoutinterwiki-summary": "Le pàggene seguende non ge sonde collegate a nisciuna otra versione de lènghe diverse.",
        "withoutinterwiki-legend": "Prefisse",
-       "withoutinterwiki-submit": "Fà vedè",
+       "withoutinterwiki-submit": "Fà 'ndrucà",
        "fewestrevisions": "Pàggene cu mene cangiaminde",
        "nbytes": "$1 {{PLURAL:$1|byte|bytes}}",
        "ncategories": "$1 {{PLURAL:$1|categorije|categorije}}",
        "protectedtitlesempty": "Nisciune titele jè pe mò prutette cu ste parametre.",
        "protectedtitles-submit": "Fà 'ndrucà le titole",
        "listusers": "Liste de l'utende",
-       "listusers-editsonly": "Fà vedè sulamende l'utinde cu cangiaminde fatte",
+       "listusers-editsonly": "Fà 'ndrucà sulamende l'utinde cu cangiaminde fatte",
        "listusers-temporarygroupsonly": "Fà 'ndrucà sulamende le utinde jndr'à le gruppe de utinde temboranèe",
        "listusers-creationsort": "Arrenghete pe date de ccreazione",
        "listusers-desc": "Arranghe jndr'à 'n'ordine ca scenne",
        "alllogstext": "Visualizzazione combinate de tutte le archivije disponibbele sus a {{SITENAME}}.\nTu puè restringere 'a viste selezionanne 'u tipe de archivije, 'u nome utende (senzibbile a le maiuscole), o le pàggene coinvolte (pure chiste senzibbile a le maiuscole).",
        "logempty": "Non ge stè 'n'anema de priatorie jndr'à l'archivije.",
        "log-title-wildcard": "Cirche le titele ca accumenzene cu stu teste",
-       "showhideselectedlogentries": "Fà vedè/scunne le righe scacchiate de l'archivije",
+       "showhideselectedlogentries": "Fà 'ndrucà/scunne le righe scacchiate de l'archivije",
        "log-edit-tags": "Cange le tag de 'na vôsce de l'archivije scacchiate",
        "checkbox-select": "Scacchie: $1",
        "checkbox-all": "Tutte",
        "allpages": "Tutte le pàggene",
        "nextpage": "Pàgene apprisse ($1)",
        "prevpage": "Pàgene apprime ($1)",
-       "allpagesfrom": "Fà vedè le pàggene partenne da:",
-       "allpagesto": "Fà vedè pàggene ca spiccene 'u:",
+       "allpagesfrom": "Fà 'ndrucà le pàggene partenne da:",
+       "allpagesto": "Fà 'ndrucà pàggene ca spiccene 'u:",
        "allarticles": "Tutte le pàggene",
        "allinnamespace": "Tutte le pàggene (neimspeise $1)",
        "allpagessubmit": "Veje",
        "categories": "Le Categorije",
        "categories-submit": "Fà 'ndrucà",
        "categoriespagetext": "{{PLURAL:$1|'A seguende categorije tène|Le seguende categorije tènene}} pàggene o media.\n'Ndruche pure [[Special:WantedCategories|Categorije cercate]].",
-       "categoriesfrom": "Fà vedè le categorije partenne da:",
+       "categoriesfrom": "Fà 'ndrucà le categorije partenne da:",
        "deletedcontributions": "Condrebbute de l'utende scangellete",
        "deletedcontributions-title": "Condrebbute de l'utende scangellate",
        "sp-deletedcontributions-contribs": "condrebbute",
        "linksearch-text": "Le wildcard cumme a \"*.wikipedia.org\" ponne essere ausate.<br />\nOnne abbesogne almene de 'nu dominie de levèlle ierte, pe esembie \"*.org\". <br />\n{{PLURAL:$2|Protocolle}} supportate: $1 ('u valore de base jè http:// ce 'u protocolle non g'è specificate).",
        "linksearch-line": "$1 jè pundete da $2",
        "linksearch-error": "Le wildcard ponne essere ausate sulamende a l'inzie de l'hostname.",
-       "listusersfrom": "Fà vedè l'utinde partenne da:",
+       "listusersfrom": "Fà 'ndrucà l'utinde partenne da:",
        "listusers-submit": "Fa vedè",
        "listusers-noresult": "Nisciune utende acchiete.",
        "listusers-blocked": "(bloccate)",
        "activeusers": "Liste de l'utinde attive",
        "activeusers-intro": "Queste jè 'n'elenghe de utinde ca avene fatte certe tipe de attività fine a l'urteme $1 {{PLURAL:$1|sciurne|sciurne}}.",
        "activeusers-count": "$1 {{PLURAL:$1|cangiamende|cangiaminde}} jndr'à l'urteme {{PLURAL:$3|sciurne}}",
-       "activeusers-from": "Fà vedè l'utinde partenne da:",
+       "activeusers-from": "Fà 'ndrucà l'utinde partenne da:",
        "activeusers-noresult": "Nisciune utende acchiate.",
        "listgrouprights": "Deritte de le gruppe utinde",
        "listgrouprights-summary": "'A liste ca ste vide ète 'na liste de le gruppe utinde ccreiate sus a sta Uicchi, cu le lore deritte d'accesse associate.\nPonne stà [[{{MediaWiki:Listgrouprights-helppage}}|'mbormaziune de cchiù]] sus a le deritte individuale.",
        "wlshowhidemine": "cangiaminde mie",
        "wlshowhidecategorization": "categorizzazzione d'a pàgene",
        "watchlist-options": "Opzione d'a liste de le pàggene condrollete",
-       "watching": "Fà vedè...",
-       "unwatching": "No fà vedè...",
+       "watching": "Fà 'ndrucà...",
+       "unwatching": "No fà 'ndrucà...",
        "watcherrortext": "'N'errore s'a verificate quanne ste cangiave le 'mbostaziune de le pàggene condrollate pe \"$1\".",
        "enotif_reset": "Signe tutte le pàggene cumme visitete",
        "enotif_impersonal_salutation": "Utende de {{SITENAME}}",
        "undelete-header": "Vide [[Special:Log/delete|l'archivije de le scangellaminde]] pe l'urteme pàggene scangellete.",
        "undelete-search-title": "Cirche le pàggene scangellate",
        "undelete-search-box": "Cirche le pàggene scangellete",
-       "undelete-search-prefix": "Fà vedè le pàggene ca accumenzene cu:",
+       "undelete-search-prefix": "Fà 'ndrucà le pàggene ca accumenzene cu:",
        "undelete-search-submit": "Cirche",
        "undelete-no-results": "Non ge stonne pàggene acchiate jndr'à l'archivije de le scangellaminde.",
        "undelete-filename-mismatch": "Non ge pozze repristinà 'a revisione d'u file cu orarie $1: nome d'u file errate",
        "sp-contributions-search": "Ricerche pe condrebbute",
        "sp-contributions-username": "Indirizze IP o nome de l'utende:",
        "sp-contributions-toponly": "Sulamende facenne vedè le cangiaminde de l'urteme revisiune",
-       "sp-contributions-newonly": "Fà vedè sulamende le cangiaminde ca onne ccrejate 'a pàgene",
+       "sp-contributions-newonly": "Fà 'ndrucà sulamende le cangiaminde ca onne ccrejate 'a pàgene",
        "sp-contributions-hideminor": "Scunne le cangiaminde stuèdeche",
        "sp-contributions-submit": "Cirche",
        "whatlinkshere": "Appondene aqquà",
        "thumbnail_image-type": "Tipe de immaggine non supportate",
        "thumbnail_gd-library": "Configurazione d'a libbrerie GD ingomblete: funziona perse $1",
        "thumbnail_image-missing": "'U file pare ca non ge se iacchie: $1",
-       "thumbnail_image-failure-limit": "Onne state fatte 'nu sacche de tendative ($1 o de cchiù) pe fà vedè sta miniature. Pe piacere pruéve cchiù tarde.",
+       "thumbnail_image-failure-limit": "Onne state fatte 'nu sacche de tendative ($1 o de cchiù) pe fà 'ndrucà sta miniature. Pe piacere pruéve cchiù tarde.",
        "import": "Pàggene 'mbortete",
        "importinterwiki": "'Mborte da 'n'otra uicchi",
        "import-interwiki-text": "Schacchie 'na Uicchi e 'nu titole de pàgene da 'mbortà.\nLe date d'a revisione e 'u nome de le cangiature avènene preservate.\nTutte le aziune de 'mbortaziune 'mbrà le Uicchi sonde reggistrate jndr'à l'[[Special:Log/import|archivije de le 'mbortaziune]].",
        "tooltip-ca-nstab-project": "Vide 'a pàgene d'u proggette",
        "tooltip-ca-nstab-image": "Vide 'a pàgene d'u fail",
        "tooltip-ca-nstab-mediawiki": "Vide le messàgge d'u sisteme",
-       "tooltip-ca-nstab-template": "Fà vedè 'u template",
+       "tooltip-ca-nstab-template": "Fà 'ndrucà 'u template",
        "tooltip-ca-nstab-help": "Vide 'a pàgene d'ajute",
        "tooltip-ca-nstab-category": "Vide a pàgene de le categorije",
        "tooltip-minoredit": "Signe cumme a 'nu cangiaminde stuèdeche",
        "tooltip-save": "Reggistre le cangiaminde ca è fatte",
        "tooltip-publish": "Pubbleche le cangiaminde tune",
        "tooltip-preview": "Fà l'andeprime de le cangiaminde ca ste face. Pe piacere falle prima cu reggistre 'a vôsce!",
-       "tooltip-diff": "Fà vedè ce cangiaminde e fatte a 'u teste.",
+       "tooltip-diff": "Fà 'ndrucà ce cangiaminde e fatte a 'u teste.",
        "tooltip-compareselectedversions": "Vide le differenze 'mbrà le doje versiune selezionete de sta pàgene.",
        "tooltip-watch": "Mitte sta pàgene jndr'à liste de le pàggene condrollete",
        "tooltip-watchlistedit-normal-submit": "Live le titele",
        "pageinfo-header-edits": "Cunde de le cangiaminde",
        "pageinfo-header-restrictions": "Protezione d'a pàgene",
        "pageinfo-header-properties": "Probbietà d'a pàgene",
-       "pageinfo-display-title": "Fà vedè 'u titole",
+       "pageinfo-display-title": "Fà 'ndrucà 'u titole",
        "pageinfo-default-sort": "Chiave de ordenamende de base",
        "pageinfo-length": "Lunghezze d'a pàgene (in byte)",
        "pageinfo-namespace": "Namespace",
        "newimages-legend": "Filtre",
        "newimages-label": "Nome d'u fail (o 'nu stuezze de jidde):",
        "newimages-user": "Indirizze IP o nome de l'utende",
-       "newimages-showbots": "Fà vedè le scarecaminde da bot",
+       "newimages-showbots": "Fà 'ndrucà le scarecaminde da bot",
        "newimages-hidepatrolled": "Scunne le carecaminde condrollate",
        "newimages-mediatype": "Tipe de media:",
        "noimages": "Non ge stè ninde da vedè.",
        "watchlistedit-clear-submit": "Sdevache l'elenghe de le pàggene condrollate (Quiste jè permanende)",
        "watchlistedit-clear-done": "L'elenghe de le pàggene condrollate ha state sdevacate.",
        "watchlistedit-clear-removed": "{{PLURAL:$1|1 titole ha state|$1 titole onne state}} luate:",
-       "watchlistedit-too-many": "Stonne troppe pàggene da fà vedè aqquà.",
+       "watchlistedit-too-many": "Stonne troppe pàggene da fà 'ndrucà aqquà.",
        "watchlisttools-clear": "Sdevache l'elenghe de le pàggene condrollate",
        "watchlisttools-view": "Vide le cangiaminde 'mbortande",
        "watchlisttools-edit": "Vide e cange le pàggene condrollete",
        "expand_templates_ok": "OK",
        "expand_templates_remove_comments": "Live le commende",
        "expand_templates_remove_nowiki": "No fà vede le tag <nowiki> jndr'à 'u resultate",
-       "expand_templates_generate_xml": "Fà vedè l'arvule de l'analisi XML",
-       "expand_templates_generate_rawhtml": "Fà vedè l'HTML grezze",
+       "expand_templates_generate_xml": "Fà 'ndrucà l'arvule de l'analisi XML",
+       "expand_templates_generate_rawhtml": "Fà 'ndrucà  l'HTML grezze",
        "expand_templates_preview": "Andeprime",
        "expand_templates_preview_fail_html": "<em>Purcé {{SITENAME}} téne abbilitate l'HTML grezze e stavane 'nu sbuénne de date de sessione perdute, l'andeprime avène scunnute pe precauzione condre a attacche JavaScript.</em>\n\n<strong>Ce quiste jè 'nu tendative de andeprime leggittime, pe piacere pruéve arrete.</strong>\nCe angore non ge funzione, pruéve a [[Special:UserLogout|assè]] e trasè arrete e verifiche ca 'u browser tune face ausà le cookie da stu site.",
        "expand_templates_preview_fail_html_anon": "<em>Purcé {{SITENAME}} téne abbilitate l'HTML grezze e tu non g'è trasute, l'andeprime avène scunnute pe precauzione condre a attacche JavaScript.</em>\n\n<strong>Ce quiste jè 'nu tendative de andeprime leggittime, [[Special:UserLogin|tràse]] e pruéve arrete.</strong>",
index e1f585c..af8170c 100644 (file)
        "userinvalidconfigtitle": "'''Varning:''' Utseendet \"$1\" finns inte. Kom ihåg att .css-, .json- och .js-sidor för enskilda användare börjar på liten bokstav. Exempel: {{ns:user}}:Foo/vector.css i stället för {{ns:user}}:Foo/Vector.css.",
        "updated": "(Uppdaterad)",
        "note": "'''Obs!'''",
-       "previewnote": "'''Kom ihåg att detta bara är en förhandsvisning.'''\nDina ändringar har ännu inte sparats!",
+       "previewnote": "<strong>Kom ihåg att detta bara är en förhandsgranskning.</strong>\nDina ändringar har ännu inte sparats!",
        "continue-editing": "Fortsätt redigera",
        "previewconflict": "Den här förhandsvisningen är resultatet av den\nredigerbara texten ovanför,\nså som det kommer att se ut om du väljer att spara.",
        "session_fail_preview": "Vi kunde inte behandla din redigering eftersom sessionsdata gått förlorad.\nDu kanske har loggats ut. Var god se till att du fortfarande är inloggad och försök igen.\nOm det fortfarande inte fungerar, prova att [[Special:UserLogout|logga ut]] och logga in igen, samt kontrollera att din webbläsare tillåter kakor från denna webbplats.",
index d3e4af9..83a72df 100644 (file)
@@ -64,6 +64,7 @@
        "tog-watchlisthideminor": "చిన్న మార్పులను నా వీక్షణా జాబితాలో చూపించొద్దు",
        "tog-watchlisthideliu": "లాగిన్ ఐన వాడుకరులు చేసే మార్పులను వీక్షణా జాబితాలో చూపించకు",
        "tog-watchlistreloadautomatically": "ఫిల్టరు మారినప్పుడెల్లా వీక్షణ జాబితాను తిరిగి లోడు చెయ్యి (JavaScript అవసరం)",
+       "tog-watchlistunwatchlinks": "మార్పులు జరిగిన వీక్షణ పేజీలకు నేరుగా వీక్షించు/వద్దు సూచికలను ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}}) చేర్చు (టాగుల్ చెయాలంటే JavaScript ఆవశ్యకం)",
        "tog-watchlisthideanons": "అజ్ఞాత వాడుకరుల మార్పులను వీక్షణా జాబితాలో చూపించకు",
        "tog-watchlisthidepatrolled": "నిఘా ఉన్న మార్పులను వీక్షణజాబితా నుంచి దాచిపెట్టు",
        "tog-watchlisthidecategorization": "పేజీ వర్గీకరణను దాచు",
@@ -74,6 +75,7 @@
        "tog-useeditwarning": "ఏదైనా పేజీని నేను వదిలివెళ్తున్నప్పుడు దానిలో భద్రపరచని మార్పులు ఉంటే నన్ను హెచ్చరించు",
        "tog-prefershttps": "లాగిన్ అయి ఉన్నప్పుడెల్లా భద్ర కనెక్షనునే వాడు",
        "tog-showrollbackconfirmation": "రోల్‌బ్యాక్ లింకును నొక్కినపుడు నిర్ధారించుకునే సందేశాన్ని చూపించు",
+       "tog-requireemail": "సంకేతపదం మార్చుకోడానికి ఈమెయిలు ఆవశ్యకం",
        "underline-always": "ఎల్లప్పుడూ",
        "underline-never": "ఎప్పటికీ వద్దు",
        "underline-default": "అలంకారపు లేదా విహారిణి అప్రమేయం",
        "createaccountmail": "ఏదో ఒక తాత్కాలిక సంకేతపదాన్ని వాడి దాన్ని పేర్కొన్న ఈమెయిలు చిరునామాకు పంపించు",
        "createaccountmail-help": "సంకేతపదం తెలుసుకోనవసరం లేకుండా వేరొకరి కోసం ఖాతా సృష్టించేందుకు వాడవచ్చు.",
        "createacct-realname": "అసలు పేరు (ఐచ్చికం)",
-       "createacct-reason": "కారణం",
+       "createacct-reason": "కారణం (అందరికీ కనిపిస్తుంది)",
        "createacct-reason-ph": "మీరు మరో ఖాతాను ఎందుకు సృష్టించుకుంటున్నారు",
        "createacct-reason-help": "సృష్టించిన ఖాతాల చిట్టాలో చూపించే సందేశం",
        "createacct-submit": "మీ ఖాతాను సృష్టించుకోండి",
        "botpasswords": "బాట్ సంకేతపదాలు",
        "botpasswords-disabled": "బాట్ సంకేతపదాలను అచేతనం చేసాం.",
        "botpasswords-no-central-id": "బాఅత్ సంకేతపదాలను వాడాలంటే, మీరు ఒక కేంద్రీకృత ఖాతాలోకి లాగినవ్వాలి.",
+       "botpasswords-existing": "ప్రస్తుతం ఉన్న బాట్ సంకేతపదాలు",
        "botpasswords-createnew": "ఓ కొత్త బాట్ సంకేతపదాన్ని సృష్టించండి",
        "botpasswords-editexisting": "ఉనికిలో ఉన్న బాట్ సంకేతపదాన్ని మార్చండి",
+       "botpasswords-label-needsreset": "(సంకేతపదాన్ని మార్చాల్సిన అవసరం ఉంది)",
        "botpasswords-label-appid": "బాట్ పేరు:",
        "botpasswords-label-create": "సృష్టించు",
        "botpasswords-label-update": "తాజాకరించు",
index 9c00f4f..5d75381 100644 (file)
        "tog-useeditwarning": "当我离开编辑页面时,如果有尚未保存的更改,请提醒我",
        "tog-prefershttps": "在登录后总是使用安全连接",
        "tog-showrollbackconfirmation": "当点击回退链接时显示确认提示",
+       "tog-requireemail": "需要电子邮件用于重置密码",
        "underline-always": "始终",
        "underline-never": "从不",
        "underline-default": "皮肤或浏览器默认设置",
        "createaccountmail": "使用一个临时的随机密码并将其发送到指定的电子邮件地址中",
        "createaccountmail-help": "可被用于为另一个人创建账户而不需要得知密码。",
        "createacct-realname": "真实姓名(可选)",
-       "createacct-reason": "原因",
+       "createacct-reason": "原因(公开记录)",
        "createacct-reason-ph": "您为什么要创建另一个账户",
        "createacct-reason-help": "在账户创建日志中显示的消息",
        "createacct-submit": "创建您的账户",
        "content-model-css": "CSS",
        "content-json-empty-object": "空的对象",
        "content-json-empty-array": "空的数组",
+       "unsupported-content-model": "<strong>警告:</strong>内容模型$1在此wiki上不支持。",
        "deprecated-self-close-category": "使用无效自封闭HTML标签的页面",
        "deprecated-self-close-category-desc": "页面包含无效的自封闭HTML标签,例如<code>&lt;b/></code>或<code>&lt;span/></code>。这些标签的行为将很快被更改以与HTML5规格相一致,所以它们在wiki文本中的使用已弃用。",
        "duplicate-args-warning": "<strong>警告:</strong>[[:$1]]正在调用超过一个[[:$2]]中“$3”参数的值。只有最后提供的值会被使用。",
        "listfiles-userdoesnotexist": "用户帐户“$1”未注册。",
        "imgfile": "文件",
        "listfiles": "文件列表",
+       "listfiles_subpage": "由$1上传",
        "listfiles_thumb": "缩略图",
        "listfiles_date": "日期",
        "listfiles_name": "名称",
index 2bce867..49b362f 100644 (file)
        "nouserspecified": "您必須指定一個使用者名稱。",
        "login-userblocked": "這位使用者已被封鎖,不允許登入。",
        "wrongpassword": "您輸入的使用者名稱或密碼錯誤,請再試一次。",
-       "wrongpasswordempty": "輸入的密碼是空的。\n請再試一次。",
+       "wrongpasswordempty": "輸入的密碼是空的。請再試一次。",
        "passwordtooshort": "您的密碼至少需要 $1 個字元。",
        "passwordtoolong": "密碼不能超過 {{PLURAL:$1|1 個字元|$1 個字元}}。",
        "passwordtoopopular": "不能使用普遍選擇的密碼。請選擇更難猜出的密碼",
        "move-watch": "監視來源以及目標頁面",
        "movepagebtn": "移動頁面",
        "pagemovedsub": "已成功移動",
-       "cannotmove": "ç\84¡æ³\95移é\99¤é \81é\9d¢ï¼\8cå\87ºæ\96¼ä»¥ä¸\8b{{PLURAL:$1|å\8e\9få\9b |å\8e\9få\9b }}:",
+       "cannotmove": "ç\94±æ\96¼ä»¥ä¸\8b{{PLURAL:$1|å\8e\9få\9b }}ä¸\8dè\83½ç§»å\8b\95é \81é\9d¢:",
        "movepage-moved": "<strong>已移動 \"$1\" 至 \"$2\"</strong>",
        "movepage-moved-redirect": "已建立重新導向頁面。",
        "movepage-moved-noredirect": "已取消建立重新導向頁面。",
index f89fa62..f741cd2 100644 (file)
@@ -587,7 +587,7 @@ abstract class Maintenance {
                        "server name detection may fail in command line scripts.", false, true );
                $this->addOption( 'profiler', 'Profiler output format (usually "text")', false, true );
                // This is named --mwdebug, because --debug would conflict in the phpunit.php CLI script.
-               $this->addOption( 'mwdebug', 'Enable built-in MediaWiki development settings', false, true );
+               $this->addOption( 'mwdebug', 'Enable built-in MediaWiki development settings', false, false );
 
                # Save generic options to display them separately in help
                $this->mGenericParameters = $this->mParams;
diff --git a/maintenance/archives/patch-archive-ar_comment_id.sql b/maintenance/archives/patch-archive-ar_comment_id.sql
new file mode 100644 (file)
index 0000000..cb7b8a7
--- /dev/null
@@ -0,0 +1,3 @@
+ALTER TABLE /*_*/archive
+  ALTER COLUMN ar_comment SET DEFAULT '',
+  ADD COLUMN ar_comment_id bigint unsigned NOT NULL DEFAULT 0 AFTER ar_comment;
index c8bf958..bbe7810 100644 (file)
@@ -1,7 +1,7 @@
 --
 -- patch-comment-table.sql
 --
--- T166732. Add a `comment` table and various columns (and temporary tables) to reference it.
+-- T166732. Add a `comment` table.
 
 CREATE TABLE /*_*/comment (
   comment_id bigint unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
@@ -10,50 +10,3 @@ CREATE TABLE /*_*/comment (
   comment_data BLOB
 ) /*$wgDBTableOptions*/;
 CREATE INDEX /*i*/comment_hash ON /*_*/comment (comment_hash);
-
-CREATE TABLE /*_*/revision_comment_temp (
-  revcomment_rev int unsigned NOT NULL,
-  revcomment_comment_id bigint unsigned NOT NULL,
-  PRIMARY KEY (revcomment_rev, revcomment_comment_id)
-) /*$wgDBTableOptions*/;
-CREATE UNIQUE INDEX /*i*/revcomment_rev ON /*_*/revision_comment_temp (revcomment_rev);
-
-CREATE TABLE /*_*/image_comment_temp (
-  imgcomment_name varchar(255) binary NOT NULL,
-  imgcomment_description_id bigint unsigned NOT NULL,
-  PRIMARY KEY (imgcomment_name, imgcomment_description_id)
-) /*$wgDBTableOptions*/;
-CREATE UNIQUE INDEX /*i*/imgcomment_name ON /*_*/image_comment_temp (imgcomment_name);
-
-ALTER TABLE /*_*/revision
-  ALTER COLUMN rev_comment SET DEFAULT '';
-
-ALTER TABLE /*_*/archive
-  ALTER COLUMN ar_comment SET DEFAULT '',
-  ADD COLUMN ar_comment_id bigint unsigned NOT NULL DEFAULT 0 AFTER ar_comment;
-
-ALTER TABLE /*_*/ipblocks
-  ALTER COLUMN ipb_reason SET DEFAULT '',
-  ADD COLUMN ipb_reason_id bigint unsigned NOT NULL DEFAULT 0 AFTER ipb_reason;
-
-ALTER TABLE /*_*/image
-  ALTER COLUMN img_description SET DEFAULT '';
-
-ALTER TABLE /*_*/oldimage
-  ALTER COLUMN oi_description SET DEFAULT '',
-  ADD COLUMN oi_description_id bigint unsigned NOT NULL DEFAULT 0 AFTER oi_description;
-
-ALTER TABLE /*_*/filearchive
-  ADD COLUMN fa_deleted_reason_id bigint unsigned NOT NULL DEFAULT 0 AFTER fa_deleted_reason,
-  ALTER COLUMN fa_description SET DEFAULT '',
-  ADD COLUMN fa_description_id bigint unsigned NOT NULL DEFAULT 0 AFTER fa_description;
-
-ALTER TABLE /*_*/recentchanges
-  ADD COLUMN rc_comment_id bigint unsigned NOT NULL DEFAULT 0 AFTER rc_comment;
-
-ALTER TABLE /*_*/logging
-  ADD COLUMN log_comment_id bigint unsigned NOT NULL DEFAULT 0 AFTER log_comment;
-
-ALTER TABLE /*_*/protected_titles
-  ALTER COLUMN pt_reason SET DEFAULT '',
-  ADD COLUMN pt_reason_id bigint unsigned NOT NULL DEFAULT 0 AFTER pt_reason;
diff --git a/maintenance/archives/patch-drop-archive-ar_usertext_timestamp.sql b/maintenance/archives/patch-drop-archive-ar_usertext_timestamp.sql
new file mode 100644 (file)
index 0000000..158d9ae
--- /dev/null
@@ -0,0 +1,7 @@
+-- T233221: The index on `archive` variously known as `ar_usertext_timestamp`
+-- and `usertext_timestamp` has a long and sordid history. We're dropping the
+-- `ar_user_text` column entirely now (see patch-drop-user-fields.sql), but
+-- this index needs special care thanks to said history.
+
+-- Do not use the /*i*/ thing here!
+DROP INDEX ar_usertext_timestamp ON /*_*/archive;
diff --git a/maintenance/archives/patch-drop-archive-usertext_timestamp.sql b/maintenance/archives/patch-drop-archive-usertext_timestamp.sql
new file mode 100644 (file)
index 0000000..f409e97
--- /dev/null
@@ -0,0 +1,7 @@
+-- T233221: The index on `archive` variously known as `ar_usertext_timestamp`
+-- and `usertext_timestamp` has a long and sordid history. We're dropping the
+-- `ar_user_text` column entirely now (see patch-drop-user-fields.sql), but
+-- this index needs special care thanks to said history.
+
+-- Do not use the /*i*/ thing here!
+DROP INDEX usertext_timestamp ON /*_*/archive;
index 7faa593..1d0b057 100644 (file)
@@ -4,7 +4,6 @@
 -- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
 
 ALTER TABLE /*_*/archive
-  DROP INDEX /*i*/ar_usertext_timestamp,
   DROP COLUMN ar_user,
   DROP COLUMN ar_user_text,
   ALTER COLUMN ar_actor DROP DEFAULT;
diff --git a/maintenance/archives/patch-filearchive-fa_description_id.sql b/maintenance/archives/patch-filearchive-fa_description_id.sql
new file mode 100644 (file)
index 0000000..93ddd50
--- /dev/null
@@ -0,0 +1,4 @@
+ALTER TABLE /*_*/filearchive
+  ADD COLUMN fa_deleted_reason_id bigint unsigned NOT NULL DEFAULT 0 AFTER fa_deleted_reason,
+  ALTER COLUMN fa_description SET DEFAULT '',
+  ADD COLUMN fa_description_id bigint unsigned NOT NULL DEFAULT 0 AFTER fa_description;
diff --git a/maintenance/archives/patch-image-img_description-default.sql b/maintenance/archives/patch-image-img_description-default.sql
new file mode 100644 (file)
index 0000000..43d54dc
--- /dev/null
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/image
+  ALTER COLUMN img_description SET DEFAULT '';
diff --git a/maintenance/archives/patch-image_comment_temp-table.sql b/maintenance/archives/patch-image_comment_temp-table.sql
new file mode 100644 (file)
index 0000000..f873c91
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE TABLE /*_*/image_comment_temp (
+  imgcomment_name varchar(255) binary NOT NULL,
+  imgcomment_description_id bigint unsigned NOT NULL,
+  PRIMARY KEY (imgcomment_name, imgcomment_description_id)
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/imgcomment_name ON /*_*/image_comment_temp (imgcomment_name);
diff --git a/maintenance/archives/patch-ipblocks-ipb_reason_id.sql b/maintenance/archives/patch-ipblocks-ipb_reason_id.sql
new file mode 100644 (file)
index 0000000..8816a7f
--- /dev/null
@@ -0,0 +1,3 @@
+ALTER TABLE /*_*/ipblocks
+  ALTER COLUMN ipb_reason SET DEFAULT '',
+  ADD COLUMN ipb_reason_id bigint unsigned NOT NULL DEFAULT 0 AFTER ipb_reason;
diff --git a/maintenance/archives/patch-logging-log_comment_id.sql b/maintenance/archives/patch-logging-log_comment_id.sql
new file mode 100644 (file)
index 0000000..0032900
--- /dev/null
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/logging
+  ADD COLUMN log_comment_id bigint unsigned NOT NULL DEFAULT 0 AFTER log_comment;
diff --git a/maintenance/archives/patch-oldimage-oi_description_id.sql b/maintenance/archives/patch-oldimage-oi_description_id.sql
new file mode 100644 (file)
index 0000000..be24a80
--- /dev/null
@@ -0,0 +1,3 @@
+ALTER TABLE /*_*/oldimage
+  ALTER COLUMN oi_description SET DEFAULT '',
+  ADD COLUMN oi_description_id bigint unsigned NOT NULL DEFAULT 0 AFTER oi_description;
diff --git a/maintenance/archives/patch-protected_titles-pt_reason_id.sql b/maintenance/archives/patch-protected_titles-pt_reason_id.sql
new file mode 100644 (file)
index 0000000..02efd2f
--- /dev/null
@@ -0,0 +1,3 @@
+ALTER TABLE /*_*/protected_titles
+  ALTER COLUMN pt_reason SET DEFAULT '',
+  ADD COLUMN pt_reason_id bigint unsigned NOT NULL DEFAULT 0 AFTER pt_reason;
diff --git a/maintenance/archives/patch-recentchanges-rc_comment_id.sql b/maintenance/archives/patch-recentchanges-rc_comment_id.sql
new file mode 100644 (file)
index 0000000..38bff08
--- /dev/null
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/recentchanges
+  ADD COLUMN rc_comment_id bigint unsigned NOT NULL DEFAULT 0 AFTER rc_comment;
diff --git a/maintenance/archives/patch-rename-mysql-user_newtalk-indexes.sql b/maintenance/archives/patch-rename-mysql-user_newtalk-indexes.sql
new file mode 100644 (file)
index 0000000..3177b4d
--- /dev/null
@@ -0,0 +1,10 @@
+-- T233240: The indexes on `user_newtalk` may be named `un_user_id`/`un_user_ip`
+-- or `user_id`/`user_ip`. At least it won't be both or mixed. Rename them to
+-- the former.
+
+-- Do not use the /*i*/ hack here!
+ALTER TABLE /*_*/user_newtalk
+       DROP INDEX user_id,
+       DROP INDEX user_ip,
+       ADD INDEX un_user_id (user_id),
+       ADD INDEX un_user_ip (user_ip);
diff --git a/maintenance/archives/patch-revision-rev_comment-default.sql b/maintenance/archives/patch-revision-rev_comment-default.sql
new file mode 100644 (file)
index 0000000..2beedcc
--- /dev/null
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/revision
+  ALTER COLUMN rev_comment SET DEFAULT '';
diff --git a/maintenance/archives/patch-revision_comment_temp-table.sql b/maintenance/archives/patch-revision_comment_temp-table.sql
new file mode 100644 (file)
index 0000000..689e5d5
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE TABLE /*_*/revision_comment_temp (
+  revcomment_rev int unsigned NOT NULL,
+  revcomment_comment_id bigint unsigned NOT NULL,
+  PRIMARY KEY (revcomment_rev, revcomment_comment_id)
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/revcomment_rev ON /*_*/revision_comment_temp (revcomment_rev);
index b78e691..a7a6465 100644 (file)
@@ -87,7 +87,7 @@ class TableCleanup extends Maintenance {
 
                $this->output(
                        sprintf( "%s %s: %6.2f%% done on %s; ETA %s [%d/%d] %.2f/sec <%.2f%% updated>\n",
-                               wfWikiID(),
+                               WikiMap::getCurrentWikiDbDomain()->getId(),
                                wfTimestamp( TS_DB, intval( $now ) ),
                                $portion * 100.0,
                                $this->table,
index 2271c39..7bbf3d0 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup Maintenance
  */
 
+use MediaWiki\MediaWikiServices;
+
 require_once __DIR__ . '/Maintenance.php';
 
 /**
@@ -57,6 +59,10 @@ class MigrateArchiveText extends LoggedUpdateMaintenance {
        protected function doDBUpdates() {
                $replaceMissing = $this->hasOption( 'replace-missing' );
                $defaultExternalStore = $this->getConfig()->get( 'DefaultExternalStore' );
+               // @phan-suppress-next-line PhanAccessMethodInternal
+               $blobStore = MediaWikiServices::getInstance()
+                       ->getBlobStoreFactory()
+                       ->newSqlBlobStore();
                $batchSize = $this->getBatchSize();
 
                $dbr = $this->getDB( DB_REPLICA, [ 'vslow' ] );
@@ -90,8 +96,9 @@ class MigrateArchiveText extends LoggedUpdateMaintenance {
 
                                // Recompress the text (and store in external storage, if
                                // applicable) if it's not already in external storage.
-                               if ( !in_array( 'external', explode( ',', $row->ar_flags ), true ) ) {
-                                       $data = Revision::getRevisionText( $row, 'ar_' );
+                               $arFlags = explode( ',', $row->ar_flags );
+                               if ( !in_array( 'external', $arFlags, true ) ) {
+                                       $data = $blobStore->decompressData( $row->ar_text, $arFlags );
                                        if ( $data !== false ) {
                                                $flags = Revision::compressRevisionText( $data );
 
index 243a3b3..4b91815 100644 (file)
@@ -1,7 +1,7 @@
 --
 -- patch-comment-table.sql
 --
--- T166732. Add a `comment` table, and temporary tables to reference it.
+-- T166732. Add a `comment` table
 
 CREATE SEQUENCE comment_comment_id_seq;
 CREATE TABLE comment (
@@ -11,17 +11,3 @@ CREATE TABLE comment (
        comment_data TEXT
 );
 CREATE INDEX comment_hash ON comment (comment_hash);
-
-CREATE TABLE revision_comment_temp (
-       revcomment_rev        INTEGER NOT NULL,
-       revcomment_comment_id INTEGER NOT NULL,
-       PRIMARY KEY (revcomment_rev, revcomment_comment_id)
-);
-CREATE UNIQUE INDEX revcomment_rev ON revision_comment_temp (revcomment_rev);
-
-CREATE TABLE image_comment_temp (
-       imgcomment_name       TEXT NOT NULL,
-       imgcomment_description_id INTEGER NOT NULL,
-       PRIMARY KEY (imgcomment_name, imgcomment_description_id)
-);
-CREATE UNIQUE INDEX imgcomment_name ON image_comment_temp (imgcomment_name);
diff --git a/maintenance/postgres/archives/patch-image_comment_temp-table.sql b/maintenance/postgres/archives/patch-image_comment_temp-table.sql
new file mode 100644 (file)
index 0000000..be7bb82
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE TABLE image_comment_temp (
+       imgcomment_name       TEXT NOT NULL,
+       imgcomment_description_id INTEGER NOT NULL,
+       PRIMARY KEY (imgcomment_name, imgcomment_description_id)
+);
+CREATE UNIQUE INDEX imgcomment_name ON image_comment_temp (imgcomment_name);
diff --git a/maintenance/postgres/archives/patch-revision_comment_temp-table.sql b/maintenance/postgres/archives/patch-revision_comment_temp-table.sql
new file mode 100644 (file)
index 0000000..cbfaa06
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE TABLE revision_comment_temp (
+       revcomment_rev        INTEGER NOT NULL,
+       revcomment_comment_id INTEGER NOT NULL,
+       PRIMARY KEY (revcomment_rev, revcomment_comment_id)
+);
+CREATE UNIQUE INDEX revcomment_rev ON revision_comment_temp (revcomment_rev);
diff --git a/maintenance/sqlite/archives/patch-archive-ar_comment_id.sql b/maintenance/sqlite/archives/patch-archive-ar_comment_id.sql
new file mode 100644 (file)
index 0000000..d06b81c
--- /dev/null
@@ -0,0 +1,46 @@
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/archive_tmp;
+CREATE TABLE /*_*/archive_tmp (
+  ar_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  ar_namespace int NOT NULL default 0,
+  ar_title varchar(255) binary NOT NULL default '',
+  ar_text mediumblob NOT NULL,
+  ar_comment varbinary(767) NOT NULL default '',
+  ar_comment_id bigint unsigned NOT NULL DEFAULT 0,
+  ar_user int unsigned NOT NULL default 0,
+  ar_user_text varchar(255) binary NOT NULL,
+  ar_timestamp binary(14) NOT NULL default '',
+  ar_minor_edit tinyint NOT NULL default 0,
+  ar_flags tinyblob NOT NULL,
+  ar_rev_id int unsigned,
+  ar_text_id int unsigned,
+  ar_deleted tinyint unsigned NOT NULL default 0,
+  ar_len int unsigned,
+  ar_page_id int unsigned,
+  ar_parent_id int unsigned default NULL,
+  ar_sha1 varbinary(32) NOT NULL default '',
+  ar_content_model varbinary(32) DEFAULT NULL,
+  ar_content_format varbinary(64) DEFAULT NULL
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/archive_tmp (
+       ar_id, ar_namespace, ar_title, ar_text, ar_comment, ar_user, ar_user_text,
+       ar_timestamp, ar_minor_edit, ar_flags, ar_rev_id, ar_text_id, ar_deleted,
+       ar_len, ar_page_id, ar_parent_id, ar_sha1, ar_content_model,
+       ar_content_format)
+  SELECT
+       ar_id, ar_namespace, ar_title, ar_text, ar_comment, ar_user, ar_user_text,
+       ar_timestamp, ar_minor_edit, ar_flags, ar_rev_id, ar_text_id, ar_deleted,
+       ar_len, ar_page_id, ar_parent_id, ar_sha1, ar_content_model,
+       ar_content_format
+  FROM /*_*/archive;
+
+DROP TABLE /*_*/archive;
+ALTER TABLE /*_*/archive_tmp RENAME TO /*_*/archive;
+CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
+CREATE INDEX /*i*/ar_revid ON /*_*/archive (ar_rev_id);
+
+COMMIT;
+
diff --git a/maintenance/sqlite/archives/patch-comment-table.sql b/maintenance/sqlite/archives/patch-comment-table.sql
deleted file mode 100644 (file)
index d74c3a6..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
---
--- patch-comment-table.sql
---
--- T166732. Add a `comment` table and various columns (and temporary tables) to reference it.
--- Sigh, sqlite, such trouble just to change the default value of a column.
-
-CREATE TABLE /*_*/comment (
-  comment_id bigint unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
-  comment_hash INT NOT NULL,
-  comment_text BLOB NOT NULL,
-  comment_data BLOB
-) /*$wgDBTableOptions*/;
-CREATE INDEX /*i*/comment_hash ON /*_*/comment (comment_hash);
-
-CREATE TABLE /*_*/revision_comment_temp (
-  revcomment_rev int unsigned NOT NULL,
-  revcomment_comment_id bigint unsigned NOT NULL,
-  PRIMARY KEY (revcomment_rev, revcomment_comment_id)
-) /*$wgDBTableOptions*/;
-CREATE UNIQUE INDEX /*i*/revcomment_rev ON /*_*/revision_comment_temp (revcomment_rev);
-
-CREATE TABLE /*_*/image_comment_temp (
-  imgcomment_name varchar(255) binary NOT NULL,
-  imgcomment_description_id bigint unsigned NOT NULL,
-  PRIMARY KEY (imgcomment_name, imgcomment_description_id)
-) /*$wgDBTableOptions*/;
-CREATE UNIQUE INDEX /*i*/imgcomment_name ON /*_*/image_comment_temp (imgcomment_name);
-
-ALTER TABLE /*_*/recentchanges
-  ADD COLUMN rc_comment_id bigint unsigned NOT NULL DEFAULT 0;
-
-ALTER TABLE /*_*/logging
-  ADD COLUMN log_comment_id bigint unsigned NOT NULL DEFAULT 0;
-
-BEGIN;
-
-DROP TABLE IF EXISTS /*_*/revision_tmp;
-CREATE TABLE /*_*/revision_tmp (
-  rev_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
-  rev_page int unsigned NOT NULL,
-  rev_text_id int unsigned NOT NULL,
-  rev_comment varbinary(767) NOT NULL default '',
-  rev_user int unsigned NOT NULL default 0,
-  rev_user_text varchar(255) binary NOT NULL default '',
-  rev_timestamp binary(14) NOT NULL default '',
-  rev_minor_edit tinyint unsigned NOT NULL default 0,
-  rev_deleted tinyint unsigned NOT NULL default 0,
-  rev_len int unsigned,
-  rev_parent_id int unsigned default NULL,
-  rev_sha1 varbinary(32) NOT NULL default '',
-  rev_content_model varbinary(32) DEFAULT NULL,
-  rev_content_format varbinary(64) DEFAULT NULL
-) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=1024;
-
-INSERT OR IGNORE INTO /*_*/revision_tmp (
-       rev_id, rev_page, rev_text_id, rev_comment, rev_user, rev_user_text,
-       rev_timestamp, rev_minor_edit, rev_deleted, rev_len, rev_parent_id,
-       rev_sha1, rev_content_model, rev_content_format)
- SELECT
-       rev_id, rev_page, rev_text_id, rev_comment, rev_user, rev_user_text,
-       rev_timestamp, rev_minor_edit, rev_deleted, rev_len, rev_parent_id,
-       rev_sha1, rev_content_model, rev_content_format
-  FROM /*_*/revision;
-
-DROP TABLE /*_*/revision;
-ALTER TABLE /*_*/revision_tmp RENAME TO /*_*/revision;
-CREATE INDEX /*i*/rev_page_id ON /*_*/revision (rev_page, rev_id);
-CREATE INDEX /*i*/rev_timestamp ON /*_*/revision (rev_timestamp);
-CREATE INDEX /*i*/page_timestamp ON /*_*/revision (rev_page,rev_timestamp);
-CREATE INDEX /*i*/user_timestamp ON /*_*/revision (rev_user,rev_timestamp);
-CREATE INDEX /*i*/usertext_timestamp ON /*_*/revision (rev_user_text,rev_timestamp);
-CREATE INDEX /*i*/page_user_timestamp ON /*_*/revision (rev_page,rev_user,rev_timestamp);
-
-COMMIT;
-
-BEGIN;
-
-DROP TABLE IF EXISTS /*_*/archive_tmp;
-CREATE TABLE /*_*/archive_tmp (
-  ar_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
-  ar_namespace int NOT NULL default 0,
-  ar_title varchar(255) binary NOT NULL default '',
-  ar_text mediumblob NOT NULL,
-  ar_comment varbinary(767) NOT NULL default '',
-  ar_comment_id bigint unsigned NOT NULL DEFAULT 0,
-  ar_user int unsigned NOT NULL default 0,
-  ar_user_text varchar(255) binary NOT NULL,
-  ar_timestamp binary(14) NOT NULL default '',
-  ar_minor_edit tinyint NOT NULL default 0,
-  ar_flags tinyblob NOT NULL,
-  ar_rev_id int unsigned,
-  ar_text_id int unsigned,
-  ar_deleted tinyint unsigned NOT NULL default 0,
-  ar_len int unsigned,
-  ar_page_id int unsigned,
-  ar_parent_id int unsigned default NULL,
-  ar_sha1 varbinary(32) NOT NULL default '',
-  ar_content_model varbinary(32) DEFAULT NULL,
-  ar_content_format varbinary(64) DEFAULT NULL
-) /*$wgDBTableOptions*/;
-
-INSERT OR IGNORE INTO /*_*/archive_tmp (
-       ar_id, ar_namespace, ar_title, ar_text, ar_comment, ar_user, ar_user_text,
-       ar_timestamp, ar_minor_edit, ar_flags, ar_rev_id, ar_text_id, ar_deleted,
-       ar_len, ar_page_id, ar_parent_id, ar_sha1, ar_content_model,
-       ar_content_format)
-  SELECT
-       ar_id, ar_namespace, ar_title, ar_text, ar_comment, ar_user, ar_user_text,
-       ar_timestamp, ar_minor_edit, ar_flags, ar_rev_id, ar_text_id, ar_deleted,
-       ar_len, ar_page_id, ar_parent_id, ar_sha1, ar_content_model,
-       ar_content_format
-  FROM /*_*/archive;
-
-DROP TABLE /*_*/archive;
-ALTER TABLE /*_*/archive_tmp RENAME TO /*_*/archive;
-CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
-CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
-CREATE INDEX /*i*/ar_revid ON /*_*/archive (ar_rev_id);
-
-COMMIT;
-
-BEGIN;
-
-DROP TABLE IF EXISTS ipblocks_tmp;
-CREATE TABLE /*_*/ipblocks_tmp (
-  ipb_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
-  ipb_address tinyblob NOT NULL,
-  ipb_user int unsigned NOT NULL default 0,
-  ipb_by int unsigned NOT NULL default 0,
-  ipb_by_text varchar(255) binary NOT NULL default '',
-  ipb_reason varbinary(767) NOT NULL default '',
-  ipb_reason_id bigint unsigned NOT NULL DEFAULT 0,
-  ipb_timestamp binary(14) NOT NULL default '',
-  ipb_auto bool NOT NULL default 0,
-  ipb_anon_only bool NOT NULL default 0,
-  ipb_create_account bool NOT NULL default 1,
-  ipb_enable_autoblock bool NOT NULL default '1',
-  ipb_expiry varbinary(14) NOT NULL default '',
-  ipb_range_start tinyblob NOT NULL,
-  ipb_range_end tinyblob NOT NULL,
-  ipb_deleted bool NOT NULL default 0,
-  ipb_block_email bool NOT NULL default 0,
-  ipb_allow_usertalk bool NOT NULL default 0,
-  ipb_parent_block_id int default NULL
-) /*$wgDBTableOptions*/;
-
-INSERT OR IGNORE INTO /*_*/ipblocks_tmp (
-       ipb_id, ipb_address, ipb_user, ipb_by, ipb_by_text, ipb_reason,
-       ipb_timestamp, ipb_auto, ipb_anon_only, ipb_create_account,
-       ipb_enable_autoblock, ipb_expiry, ipb_range_start, ipb_range_end,
-       ipb_deleted, ipb_block_email, ipb_allow_usertalk, ipb_parent_block_id)
-  SELECT
-       ipb_id, ipb_address, ipb_user, ipb_by, ipb_by_text, ipb_reason,
-       ipb_timestamp, ipb_auto, ipb_anon_only, ipb_create_account,
-       ipb_enable_autoblock, ipb_expiry, ipb_range_start, ipb_range_end,
-       ipb_deleted, ipb_block_email, ipb_allow_usertalk, ipb_parent_block_id
-  FROM /*_*/ipblocks;
-
-DROP TABLE /*_*/ipblocks;
-ALTER TABLE /*_*/ipblocks_tmp RENAME TO /*_*/ipblocks;
-CREATE UNIQUE INDEX /*i*/ipb_address ON /*_*/ipblocks (ipb_address(255), ipb_user, ipb_auto, ipb_anon_only);
-CREATE INDEX /*i*/ipb_user ON /*_*/ipblocks (ipb_user);
-CREATE INDEX /*i*/ipb_range ON /*_*/ipblocks (ipb_range_start(8), ipb_range_end(8));
-CREATE INDEX /*i*/ipb_timestamp ON /*_*/ipblocks (ipb_timestamp);
-CREATE INDEX /*i*/ipb_expiry ON /*_*/ipblocks (ipb_expiry);
-CREATE INDEX /*i*/ipb_parent_block_id ON /*_*/ipblocks (ipb_parent_block_id);
-
-COMMIT;
-
-BEGIN;
-
-DROP TABLE IF EXISTS /*_*/image_tmp;
-CREATE TABLE /*_*/image_tmp (
-  img_name varchar(255) binary NOT NULL default '' PRIMARY KEY,
-  img_size int unsigned NOT NULL default 0,
-  img_width int NOT NULL default 0,
-  img_height int NOT NULL default 0,
-  img_metadata mediumblob NOT NULL,
-  img_bits int NOT NULL default 0,
-  img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE", "3D") default NULL,
-  img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") NOT NULL default "unknown",
-  img_minor_mime varbinary(100) NOT NULL default "unknown",
-  img_description varbinary(767) NOT NULL default '',
-  img_user int unsigned NOT NULL default 0,
-  img_user_text varchar(255) binary NOT NULL,
-  img_timestamp varbinary(14) NOT NULL default '',
-  img_sha1 varbinary(32) NOT NULL default ''
-) /*$wgDBTableOptions*/;
-
-INSERT OR IGNORE INTO /*_*/image_tmp (
-       img_name, img_size, img_width, img_height, img_metadata, img_bits,
-       img_media_type, img_major_mime, img_minor_mime, img_description, img_user,
-       img_user_text, img_timestamp, img_sha1)
-  SELECT
-       img_name, img_size, img_width, img_height, img_metadata, img_bits,
-       img_media_type, img_major_mime, img_minor_mime, img_description, img_user,
-       img_user_text, img_timestamp, img_sha1
-  FROM /*_*/image;
-
-DROP TABLE /*_*/image;
-ALTER TABLE /*_*/image_tmp RENAME TO /*_*/image;
-CREATE INDEX /*i*/img_user_timestamp ON /*_*/image (img_user,img_timestamp);
-CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);
-CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
-CREATE INDEX /*i*/img_timestamp ON /*_*/image (img_timestamp);
-CREATE INDEX /*i*/img_sha1 ON /*_*/image (img_sha1(10));
-CREATE INDEX /*i*/img_media_mime ON /*_*/image (img_media_type,img_major_mime,img_minor_mime);
-
-COMMIT;
-
-BEGIN;
-
-DROP TABLE IF EXISTS /*_*/oldimage_tmp;
-CREATE TABLE /*_*/oldimage_tmp (
-  oi_name varchar(255) binary NOT NULL default '',
-  oi_archive_name varchar(255) binary NOT NULL default '',
-  oi_size int unsigned NOT NULL default 0,
-  oi_width int NOT NULL default 0,
-  oi_height int NOT NULL default 0,
-  oi_bits int NOT NULL default 0,
-  oi_description varbinary(767) NOT NULL default '',
-  oi_description_id bigint unsigned NOT NULL DEFAULT 0,
-  oi_user int unsigned NOT NULL default 0,
-  oi_user_text varchar(255) binary NOT NULL,
-  oi_timestamp binary(14) NOT NULL default '',
-  oi_metadata mediumblob NOT NULL,
-  oi_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE", "3D") default NULL,
-  oi_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") NOT NULL default "unknown",
-  oi_minor_mime varbinary(100) NOT NULL default "unknown",
-  oi_deleted tinyint unsigned NOT NULL default 0,
-  oi_sha1 varbinary(32) NOT NULL default ''
-) /*$wgDBTableOptions*/;
-
-INSERT OR IGNORE INTO /*_*/oldimage_tmp (
-       oi_name, oi_archive_name, oi_size, oi_width, oi_height, oi_bits,
-       oi_description, oi_user, oi_user_text, oi_timestamp, oi_metadata,
-       oi_media_type, oi_major_mime, oi_minor_mime, oi_deleted, oi_sha1)
-  SELECT
-       oi_name, oi_archive_name, oi_size, oi_width, oi_height, oi_bits,
-       oi_description, oi_user, oi_user_text, oi_timestamp, oi_metadata,
-       oi_media_type, oi_major_mime, oi_minor_mime, oi_deleted, oi_sha1
-  FROM /*_*/oldimage;
-
-DROP TABLE /*_*/oldimage;
-ALTER TABLE /*_*/oldimage_tmp RENAME TO /*_*/oldimage;
-CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
-CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
-CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name(14));
-CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1(10));
-
-COMMIT;
-
--- filearchive is done in patch-filearchive-fa_description_id.sql
-
-BEGIN;
-
-DROP TABLE IF EXISTS /*_*/protected_titles_tmp;
-CREATE TABLE /*_*/protected_titles_tmp (
-  pt_namespace int NOT NULL,
-  pt_title varchar(255) binary NOT NULL,
-  pt_user int unsigned NOT NULL,
-  pt_reason varbinary(767) default '',
-  pt_reason_id bigint unsigned NOT NULL DEFAULT 0,
-  pt_timestamp binary(14) NOT NULL,
-  pt_expiry varbinary(14) NOT NULL default '',
-  pt_create_perm varbinary(60) NOT NULL
-) /*$wgDBTableOptions*/;
-
-INSERT OR IGNORE INTO /*_*/protected_titles_tmp (
-       pt_namespace, pt_title, pt_user, pt_reason, pt_timestamp, pt_expiry, pt_create_perm)
-  SELECT
-       pt_namespace, pt_title, pt_user, pt_reason, pt_timestamp, pt_expiry, pt_create_perm
-  FROM /*_*/protected_titles;
-
-DROP TABLE /*_*/protected_titles;
-ALTER TABLE /*_*/protected_titles_tmp RENAME TO /*_*/protected_titles;
-CREATE UNIQUE INDEX /*i*/pt_namespace_title ON /*_*/protected_titles (pt_namespace,pt_title);
-CREATE INDEX /*i*/pt_timestamp ON /*_*/protected_titles (pt_timestamp);
-
-COMMIT;
diff --git a/maintenance/sqlite/archives/patch-image-img_description-default.sql b/maintenance/sqlite/archives/patch-image-img_description-default.sql
new file mode 100644 (file)
index 0000000..b810ed5
--- /dev/null
@@ -0,0 +1,40 @@
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/image_tmp;
+CREATE TABLE /*_*/image_tmp (
+  img_name varchar(255) binary NOT NULL default '' PRIMARY KEY,
+  img_size int unsigned NOT NULL default 0,
+  img_width int NOT NULL default 0,
+  img_height int NOT NULL default 0,
+  img_metadata mediumblob NOT NULL,
+  img_bits int NOT NULL default 0,
+  img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE", "3D") default NULL,
+  img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") NOT NULL default "unknown",
+  img_minor_mime varbinary(100) NOT NULL default "unknown",
+  img_description varbinary(767) NOT NULL default '',
+  img_user int unsigned NOT NULL default 0,
+  img_user_text varchar(255) binary NOT NULL,
+  img_timestamp varbinary(14) NOT NULL default '',
+  img_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/image_tmp (
+       img_name, img_size, img_width, img_height, img_metadata, img_bits,
+       img_media_type, img_major_mime, img_minor_mime, img_description, img_user,
+       img_user_text, img_timestamp, img_sha1)
+  SELECT
+       img_name, img_size, img_width, img_height, img_metadata, img_bits,
+       img_media_type, img_major_mime, img_minor_mime, img_description, img_user,
+       img_user_text, img_timestamp, img_sha1
+  FROM /*_*/image;
+
+DROP TABLE /*_*/image;
+ALTER TABLE /*_*/image_tmp RENAME TO /*_*/image;
+CREATE INDEX /*i*/img_user_timestamp ON /*_*/image (img_user,img_timestamp);
+CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);
+CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
+CREATE INDEX /*i*/img_timestamp ON /*_*/image (img_timestamp);
+CREATE INDEX /*i*/img_sha1 ON /*_*/image (img_sha1(10));
+CREATE INDEX /*i*/img_media_mime ON /*_*/image (img_media_type,img_major_mime,img_minor_mime);
+
+COMMIT;
diff --git a/maintenance/sqlite/archives/patch-ipblocks-ipb_reason_id.sql b/maintenance/sqlite/archives/patch-ipblocks-ipb_reason_id.sql
new file mode 100644 (file)
index 0000000..b1079fc
--- /dev/null
@@ -0,0 +1,47 @@
+BEGIN;
+
+DROP TABLE IF EXISTS ipblocks_tmp;
+CREATE TABLE /*_*/ipblocks_tmp (
+  ipb_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  ipb_address tinyblob NOT NULL,
+  ipb_user int unsigned NOT NULL default 0,
+  ipb_by int unsigned NOT NULL default 0,
+  ipb_by_text varchar(255) binary NOT NULL default '',
+  ipb_reason varbinary(767) NOT NULL default '',
+  ipb_reason_id bigint unsigned NOT NULL DEFAULT 0,
+  ipb_timestamp binary(14) NOT NULL default '',
+  ipb_auto bool NOT NULL default 0,
+  ipb_anon_only bool NOT NULL default 0,
+  ipb_create_account bool NOT NULL default 1,
+  ipb_enable_autoblock bool NOT NULL default '1',
+  ipb_expiry varbinary(14) NOT NULL default '',
+  ipb_range_start tinyblob NOT NULL,
+  ipb_range_end tinyblob NOT NULL,
+  ipb_deleted bool NOT NULL default 0,
+  ipb_block_email bool NOT NULL default 0,
+  ipb_allow_usertalk bool NOT NULL default 0,
+  ipb_parent_block_id int default NULL
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/ipblocks_tmp (
+       ipb_id, ipb_address, ipb_user, ipb_by, ipb_by_text, ipb_reason,
+       ipb_timestamp, ipb_auto, ipb_anon_only, ipb_create_account,
+       ipb_enable_autoblock, ipb_expiry, ipb_range_start, ipb_range_end,
+       ipb_deleted, ipb_block_email, ipb_allow_usertalk, ipb_parent_block_id)
+  SELECT
+       ipb_id, ipb_address, ipb_user, ipb_by, ipb_by_text, ipb_reason,
+       ipb_timestamp, ipb_auto, ipb_anon_only, ipb_create_account,
+       ipb_enable_autoblock, ipb_expiry, ipb_range_start, ipb_range_end,
+       ipb_deleted, ipb_block_email, ipb_allow_usertalk, ipb_parent_block_id
+  FROM /*_*/ipblocks;
+
+DROP TABLE /*_*/ipblocks;
+ALTER TABLE /*_*/ipblocks_tmp RENAME TO /*_*/ipblocks;
+CREATE UNIQUE INDEX /*i*/ipb_address ON /*_*/ipblocks (ipb_address(255), ipb_user, ipb_auto, ipb_anon_only);
+CREATE INDEX /*i*/ipb_user ON /*_*/ipblocks (ipb_user);
+CREATE INDEX /*i*/ipb_range ON /*_*/ipblocks (ipb_range_start(8), ipb_range_end(8));
+CREATE INDEX /*i*/ipb_timestamp ON /*_*/ipblocks (ipb_timestamp);
+CREATE INDEX /*i*/ipb_expiry ON /*_*/ipblocks (ipb_expiry);
+CREATE INDEX /*i*/ipb_parent_block_id ON /*_*/ipblocks (ipb_parent_block_id);
+
+COMMIT;
diff --git a/maintenance/sqlite/archives/patch-logging-log_comment_id.sql b/maintenance/sqlite/archives/patch-logging-log_comment_id.sql
new file mode 100644 (file)
index 0000000..784a88d
--- /dev/null
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/logging
+  ADD COLUMN log_comment_id bigint unsigned NOT NULL DEFAULT 0;
diff --git a/maintenance/sqlite/archives/patch-oldimage-oi_description_id.sql b/maintenance/sqlite/archives/patch-oldimage-oi_description_id.sql
new file mode 100644 (file)
index 0000000..c0191a7
--- /dev/null
@@ -0,0 +1,41 @@
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/oldimage_tmp;
+CREATE TABLE /*_*/oldimage_tmp (
+  oi_name varchar(255) binary NOT NULL default '',
+  oi_archive_name varchar(255) binary NOT NULL default '',
+  oi_size int unsigned NOT NULL default 0,
+  oi_width int NOT NULL default 0,
+  oi_height int NOT NULL default 0,
+  oi_bits int NOT NULL default 0,
+  oi_description varbinary(767) NOT NULL default '',
+  oi_description_id bigint unsigned NOT NULL DEFAULT 0,
+  oi_user int unsigned NOT NULL default 0,
+  oi_user_text varchar(255) binary NOT NULL,
+  oi_timestamp binary(14) NOT NULL default '',
+  oi_metadata mediumblob NOT NULL,
+  oi_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE", "3D") default NULL,
+  oi_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") NOT NULL default "unknown",
+  oi_minor_mime varbinary(100) NOT NULL default "unknown",
+  oi_deleted tinyint unsigned NOT NULL default 0,
+  oi_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/oldimage_tmp (
+       oi_name, oi_archive_name, oi_size, oi_width, oi_height, oi_bits,
+       oi_description, oi_user, oi_user_text, oi_timestamp, oi_metadata,
+       oi_media_type, oi_major_mime, oi_minor_mime, oi_deleted, oi_sha1)
+  SELECT
+       oi_name, oi_archive_name, oi_size, oi_width, oi_height, oi_bits,
+       oi_description, oi_user, oi_user_text, oi_timestamp, oi_metadata,
+       oi_media_type, oi_major_mime, oi_minor_mime, oi_deleted, oi_sha1
+  FROM /*_*/oldimage;
+
+DROP TABLE /*_*/oldimage;
+ALTER TABLE /*_*/oldimage_tmp RENAME TO /*_*/oldimage;
+CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
+CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
+CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name(14));
+CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1(10));
+
+COMMIT;
diff --git a/maintenance/sqlite/archives/patch-protected_titles-pt_reason_id.sql b/maintenance/sqlite/archives/patch-protected_titles-pt_reason_id.sql
new file mode 100644 (file)
index 0000000..793144c
--- /dev/null
@@ -0,0 +1,26 @@
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/protected_titles_tmp;
+CREATE TABLE /*_*/protected_titles_tmp (
+  pt_namespace int NOT NULL,
+  pt_title varchar(255) binary NOT NULL,
+  pt_user int unsigned NOT NULL,
+  pt_reason varbinary(767) default '',
+  pt_reason_id bigint unsigned NOT NULL DEFAULT 0,
+  pt_timestamp binary(14) NOT NULL,
+  pt_expiry varbinary(14) NOT NULL default '',
+  pt_create_perm varbinary(60) NOT NULL
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/protected_titles_tmp (
+       pt_namespace, pt_title, pt_user, pt_reason, pt_timestamp, pt_expiry, pt_create_perm)
+  SELECT
+       pt_namespace, pt_title, pt_user, pt_reason, pt_timestamp, pt_expiry, pt_create_perm
+  FROM /*_*/protected_titles;
+
+DROP TABLE /*_*/protected_titles;
+ALTER TABLE /*_*/protected_titles_tmp RENAME TO /*_*/protected_titles;
+CREATE UNIQUE INDEX /*i*/pt_namespace_title ON /*_*/protected_titles (pt_namespace,pt_title);
+CREATE INDEX /*i*/pt_timestamp ON /*_*/protected_titles (pt_timestamp);
+
+COMMIT;
diff --git a/maintenance/sqlite/archives/patch-recentchanges-rc_comment_id.sql b/maintenance/sqlite/archives/patch-recentchanges-rc_comment_id.sql
new file mode 100644 (file)
index 0000000..55bf9b5
--- /dev/null
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/recentchanges
+  ADD COLUMN rc_comment_id bigint unsigned NOT NULL DEFAULT 0;
diff --git a/maintenance/sqlite/archives/patch-revision-rev_comment-default.sql b/maintenance/sqlite/archives/patch-revision-rev_comment-default.sql
new file mode 100644 (file)
index 0000000..0194079
--- /dev/null
@@ -0,0 +1,40 @@
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/revision_tmp;
+CREATE TABLE /*_*/revision_tmp (
+  rev_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  rev_page int unsigned NOT NULL,
+  rev_text_id int unsigned NOT NULL,
+  rev_comment varbinary(767) NOT NULL default '',
+  rev_user int unsigned NOT NULL default 0,
+  rev_user_text varchar(255) binary NOT NULL default '',
+  rev_timestamp binary(14) NOT NULL default '',
+  rev_minor_edit tinyint unsigned NOT NULL default 0,
+  rev_deleted tinyint unsigned NOT NULL default 0,
+  rev_len int unsigned,
+  rev_parent_id int unsigned default NULL,
+  rev_sha1 varbinary(32) NOT NULL default '',
+  rev_content_model varbinary(32) DEFAULT NULL,
+  rev_content_format varbinary(64) DEFAULT NULL
+) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=1024;
+
+INSERT OR IGNORE INTO /*_*/revision_tmp (
+       rev_id, rev_page, rev_text_id, rev_comment, rev_user, rev_user_text,
+       rev_timestamp, rev_minor_edit, rev_deleted, rev_len, rev_parent_id,
+       rev_sha1, rev_content_model, rev_content_format)
+ SELECT
+       rev_id, rev_page, rev_text_id, rev_comment, rev_user, rev_user_text,
+       rev_timestamp, rev_minor_edit, rev_deleted, rev_len, rev_parent_id,
+       rev_sha1, rev_content_model, rev_content_format
+  FROM /*_*/revision;
+
+DROP TABLE /*_*/revision;
+ALTER TABLE /*_*/revision_tmp RENAME TO /*_*/revision;
+CREATE INDEX /*i*/rev_page_id ON /*_*/revision (rev_page, rev_id);
+CREATE INDEX /*i*/rev_timestamp ON /*_*/revision (rev_timestamp);
+CREATE INDEX /*i*/page_timestamp ON /*_*/revision (rev_page,rev_timestamp);
+CREATE INDEX /*i*/user_timestamp ON /*_*/revision (rev_user,rev_timestamp);
+CREATE INDEX /*i*/usertext_timestamp ON /*_*/revision (rev_user_text,rev_timestamp);
+CREATE INDEX /*i*/page_user_timestamp ON /*_*/revision (rev_page,rev_user,rev_timestamp);
+
+COMMIT;
index 37625cf..3741959 100644 (file)
@@ -556,10 +556,27 @@ class CheckStorage {
 
                // Find text row again
                $dbr = wfGetDB( DB_REPLICA );
-               $oldId = $dbr->selectField( 'revision', 'rev_text_id', [ 'rev_id' => $id ], __METHOD__ );
+               global $wgMultiContentRevisionSchemaMigrationStage;
+               if ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_READ_OLD ) {
+                       $oldId = $dbr->selectField( 'revision', 'rev_text_id', [ 'rev_id' => $id ], __METHOD__ );
+               } else {
+                       $res = $dbr->selectRow(
+                               [ 'slots', 'content' ],
+                               [ 'content_address' ],
+                               [ 'slot_revision_id' => $id ],
+                               __METHOD__,
+                               [],
+                               [ 'content' => [ 'INNER JOIN', [ 'content_id = slot_content_id' ] ] ]
+                       );
+                       // @phan-suppress-next-line PhanAccessMethodInternal
+                       $blobStore = MediaWikiServices::getInstance()
+                               ->getBlobStoreFactory()
+                               ->newSqlBlobStore();
+                       $oldId = $blobStore->getTextIdFromAddress( $res->content_address );
+               }
+
                if ( !$oldId ) {
                        echo "Missing revision row for rev_id $id\n";
-
                        return;
                }
 
index b6aa626..c4779b9 100644 (file)
@@ -239,6 +239,10 @@ class CompressOld extends Maintenance {
                        /** @var ExternalStoreDB $storeObj */
                        $storeObj = $esFactory->getStore( 'DB' );
                }
+               // @phan-suppress-next-line PhanAccessMethodInternal
+               $blobStore = MediaWikiServices::getInstance()
+                       ->getBlobStoreFactory()
+                       ->newSqlBlobStore();
 
                # Get all articles by page_id
                if ( !$maxPageId ) {
@@ -370,8 +374,12 @@ class CompressOld extends Maintenance {
                                for ( $j = 0; $j < $thisChunkSize && $chunk->isHappy(); $j++ ) {
                                        $oldid = $revs[$i + $j]->old_id;
 
-                                       # Get text
-                                       $text = Revision::getRevisionText( $revs[$i + $j] );
+                                       # Get text. We do not need the full `extractBlob` since the query is built
+                                       # to fetch non-externalstore blobs.
+                                       $text = $blobStore->decompressData(
+                                               $revs[$i + $j]->old_text,
+                                               explode( ',', $revs[$i + $j]->old_flags )
+                                       );
 
                                        if ( $text === false ) {
                                                $this->error( "\nError, unable to get text in old_id $oldid" );
diff --git a/maintenance/storage/fixT22757.php b/maintenance/storage/fixT22757.php
deleted file mode 100644 (file)
index 61f1177..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-<?php
-/**
- * Script to fix T22757.
- *
- * 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
- * @ingroup Maintenance ExternalStorage
- */
-
-require_once __DIR__ . '/../Maintenance.php';
-
-/**
- * Maintenance script to fix T22757.
- *
- * @ingroup Maintenance ExternalStorage
- */
-class FixT22757 extends Maintenance {
-       public $batchSize = 10000;
-       public $mapCache = [];
-       public $mapCacheSize = 0;
-       public $maxMapCacheSize = 1000000;
-
-       function __construct() {
-               parent::__construct();
-               $this->addDescription( 'Script to fix T22757 assuming that blob_tracking is intact' );
-               $this->addOption( 'dry-run', 'Report only' );
-               $this->addOption( 'start', 'old_id to start at', false, true );
-       }
-
-       function execute() {
-               $dbr = $this->getDB( DB_REPLICA );
-               $dbw = $this->getDB( DB_MASTER );
-
-               $dryRun = $this->getOption( 'dry-run' );
-               if ( $dryRun ) {
-                       print "Dry run only.\n";
-               }
-
-               $startId = $this->getOption( 'start', 0 );
-               $numGood = 0;
-               $numFixed = 0;
-               $numBad = 0;
-
-               $totalRevs = $dbr->selectField( 'text', 'MAX(old_id)', '', __METHOD__ );
-
-               // In MySQL 4.1+, the binary field old_text has a non-working LOWER() function
-               $lowerLeft = 'LOWER(CONVERT(LEFT(old_text,22) USING latin1))';
-
-               while ( true ) {
-                       print "ID: $startId / $totalRevs\r";
-
-                       $res = $dbr->select(
-                               'text',
-                               [ 'old_id', 'old_flags', 'old_text' ],
-                               [
-                                       'old_id > ' . intval( $startId ),
-                                       'old_flags LIKE \'%object%\' AND old_flags NOT LIKE \'%external%\'',
-                                       "$lowerLeft = 'o:15:\"historyblobstub\"'",
-                               ],
-                               __METHOD__,
-                               [
-                                       'ORDER BY' => 'old_id',
-                                       'LIMIT' => $this->batchSize,
-                               ]
-                       );
-
-                       if ( !$res->numRows() ) {
-                               break;
-                       }
-
-                       $secondaryIds = [];
-                       $stubs = [];
-
-                       foreach ( $res as $row ) {
-                               $startId = $row->old_id;
-
-                               // Basic sanity checks
-                               $obj = unserialize( $row->old_text );
-                               if ( $obj === false ) {
-                                       print "{$row->old_id}: unrecoverable: cannot unserialize\n";
-                                       ++$numBad;
-                                       continue;
-                               }
-
-                               if ( !is_object( $obj ) ) {
-                                       print "{$row->old_id}: unrecoverable: unserialized to type " .
-                                               gettype( $obj ) . ", possible double-serialization\n";
-                                       ++$numBad;
-                                       continue;
-                               }
-
-                               if ( strtolower( get_class( $obj ) ) !== 'historyblobstub' ) {
-                                       print "{$row->old_id}: unrecoverable: unexpected object class " .
-                                               get_class( $obj ) . "\n";
-                                       ++$numBad;
-                                       continue;
-                               }
-
-                               // Process flags
-                               $flags = explode( ',', $row->old_flags );
-                               if ( in_array( 'utf-8', $flags ) || in_array( 'utf8', $flags ) ) {
-                                       $legacyEncoding = false;
-                               } else {
-                                       $legacyEncoding = true;
-                               }
-
-                               // Queue the stub for future batch processing
-                               $id = intval( $obj->mOldId );
-                               $secondaryIds[] = $id;
-                               $stubs[$row->old_id] = [
-                                       'legacyEncoding' => $legacyEncoding,
-                                       'secondaryId' => $id,
-                                       'hash' => $obj->mHash,
-                               ];
-                       }
-
-                       $secondaryIds = array_unique( $secondaryIds );
-
-                       if ( !count( $secondaryIds ) ) {
-                               continue;
-                       }
-
-                       // Run the batch query on blob_tracking
-                       $res = $dbr->select(
-                               'blob_tracking',
-                               '*',
-                               [
-                                       'bt_text_id' => $secondaryIds,
-                               ],
-                               __METHOD__
-                       );
-                       $trackedBlobs = [];
-                       foreach ( $res as $row ) {
-                               $trackedBlobs[$row->bt_text_id] = $row;
-                       }
-
-                       // Process the stubs
-                       foreach ( $stubs as $primaryId => $stub ) {
-                               $secondaryId = $stub['secondaryId'];
-                               if ( !isset( $trackedBlobs[$secondaryId] ) ) {
-                                       // No tracked blob. Work out what went wrong
-                                       $secondaryRow = $dbr->selectRow(
-                                               'text',
-                                               [ 'old_flags', 'old_text' ],
-                                               [ 'old_id' => $secondaryId ],
-                                               __METHOD__
-                                       );
-                                       if ( !$secondaryRow ) {
-                                               print "$primaryId: unrecoverable: secondary row is missing\n";
-                                               ++$numBad;
-                                       } elseif ( $this->isUnbrokenStub( $stub, $secondaryRow ) ) {
-                                               // Not broken yet, and not in the tracked clusters so it won't get
-                                               // broken by the current RCT run.
-                                               ++$numGood;
-                                       } elseif ( strpos( $secondaryRow->old_flags, 'external' ) !== false ) {
-                                               print "$primaryId: unrecoverable: secondary gone to {$secondaryRow->old_text}\n";
-                                               ++$numBad;
-                                       } else {
-                                               print "$primaryId: unrecoverable: miscellaneous corruption of secondary row\n";
-                                               ++$numBad;
-                                       }
-                                       unset( $stubs[$primaryId] );
-                                       continue;
-                               }
-                               $trackRow = $trackedBlobs[$secondaryId];
-
-                               // Check that the specified text really is available in the tracked source row
-                               $url = "DB://{$trackRow->bt_cluster}/{$trackRow->bt_blob_id}/{$stub['hash']}";
-                               $text = ExternalStore::fetchFromURL( $url );
-                               if ( $text === false ) {
-                                       print "$primaryId: unrecoverable: source text missing\n";
-                                       ++$numBad;
-                                       unset( $stubs[$primaryId] );
-                                       continue;
-                               }
-                               if ( md5( $text ) !== $stub['hash'] ) {
-                                       print "$primaryId: unrecoverable: content hashes do not match\n";
-                                       ++$numBad;
-                                       unset( $stubs[$primaryId] );
-                                       continue;
-                               }
-
-                               // Find the page_id and rev_id
-                               // The page is probably the same as the page of the secondary row
-                               $pageId = intval( $trackRow->bt_page );
-                               if ( !$pageId ) {
-                                       $revId = $pageId = 0;
-                               } else {
-                                       $revId = $this->findTextIdInPage( $pageId, $primaryId );
-                                       if ( !$revId ) {
-                                               // Actually an orphan
-                                               $pageId = $revId = 0;
-                                       }
-                               }
-
-                               $newFlags = $stub['legacyEncoding'] ? 'external' : 'external,utf-8';
-
-                               if ( !$dryRun ) {
-                                       // Reset the text row to point to the original copy
-                                       $this->beginTransaction( $dbw, __METHOD__ );
-                                       $dbw->update(
-                                               'text',
-                                               // SET
-                                               [
-                                                       'old_flags' => $newFlags,
-                                                       'old_text' => $url
-                                               ],
-                                               // WHERE
-                                               [ 'old_id' => $primaryId ],
-                                               __METHOD__
-                                       );
-
-                                       // Add a blob_tracking row so that the new reference can be recompressed
-                                       // without needing to run trackBlobs.php again
-                                       $dbw->insert( 'blob_tracking',
-                                               [
-                                                       'bt_page' => $pageId,
-                                                       'bt_rev_id' => $revId,
-                                                       'bt_text_id' => $primaryId,
-                                                       'bt_cluster' => $trackRow->bt_cluster,
-                                                       'bt_blob_id' => $trackRow->bt_blob_id,
-                                                       'bt_cgz_hash' => $stub['hash'],
-                                                       'bt_new_url' => null,
-                                                       'bt_moved' => 0,
-                                               ],
-                                               __METHOD__
-                                       );
-                                       $this->commitTransaction( $dbw, __METHOD__ );
-                               }
-
-                               print "$primaryId: resolved to $url\n";
-                               ++$numFixed;
-                       }
-               }
-
-               print "\n";
-               print "Fixed: $numFixed\n";
-               print "Unrecoverable: $numBad\n";
-               print "Good stubs: $numGood\n";
-       }
-
-       function findTextIdInPage( $pageId, $textId ) {
-               $ids = $this->getRevTextMap( $pageId );
-               return $ids[$textId] ?? null;
-       }
-
-       function getRevTextMap( $pageId ) {
-               if ( !isset( $this->mapCache[$pageId] ) ) {
-                       // Limit cache size
-                       while ( $this->mapCacheSize > $this->maxMapCacheSize ) {
-                               $key = key( $this->mapCache );
-                               $this->mapCacheSize -= count( $this->mapCache[$key] );
-                               unset( $this->mapCache[$key] );
-                       }
-
-                       $dbr = $this->getDB( DB_REPLICA );
-                       $map = [];
-                       $res = $dbr->select( 'revision',
-                               [ 'rev_id', 'rev_text_id' ],
-                               [ 'rev_page' => $pageId ],
-                               __METHOD__
-                       );
-                       foreach ( $res as $row ) {
-                               $map[$row->rev_text_id] = $row->rev_id;
-                       }
-                       $this->mapCache[$pageId] = $map;
-                       $this->mapCacheSize += count( $map );
-               }
-
-               return $this->mapCache[$pageId];
-       }
-
-       /**
-        * This is based on part of HistoryBlobStub::getText().
-        * Determine if the text can be retrieved from the row in the normal way.
-        * @param array $stub
-        * @param stdClass $secondaryRow
-        * @return bool
-        */
-       function isUnbrokenStub( $stub, $secondaryRow ) {
-               $flags = explode( ',', $secondaryRow->old_flags );
-               $text = $secondaryRow->old_text;
-               if ( in_array( 'external', $flags ) ) {
-                       $url = $text;
-                       Wikimedia\suppressWarnings();
-                       list( /* $proto */, $path ) = explode( '://', $url, 2 );
-                       Wikimedia\restoreWarnings();
-
-                       if ( $path == "" ) {
-                               return false;
-                       }
-                       $text = ExternalStore::fetchFromURL( $url );
-               }
-               if ( !in_array( 'object', $flags ) ) {
-                       return false;
-               }
-
-               if ( in_array( 'gzip', $flags ) ) {
-                       $obj = unserialize( gzinflate( $text ) );
-               } else {
-                       $obj = unserialize( $text );
-               }
-
-               if ( !is_object( $obj ) ) {
-                       // Correct for old double-serialization bug.
-                       $obj = unserialize( $obj );
-               }
-
-               if ( !is_object( $obj ) ) {
-                       return false;
-               }
-
-               $obj->uncompress();
-               $text = $obj->getItem( $stub['hash'] );
-
-               return $text !== false;
-       }
-}
-
-$maintClass = FixT22757::class;
-require_once RUN_MAINTENANCE_IF_MAIN;
index 9f20e67..0068046 100644 (file)
@@ -22,6 +22,7 @@
  * @ingroup Maintenance ExternalStorage
  */
 
+use MediaWiki\Storage\SqlBlobStore;
 use Wikimedia\Rdbms\IMaintainableDatabase;
 use MediaWiki\Logger\LegacyLogger;
 use MediaWiki\MediaWikiServices;
@@ -63,18 +64,20 @@ class RecompressTracked {
        public $numProcs = 1;
        public $numBatches = 0;
        public $pageBlobClass, $orphanBlobClass;
-       public $replicaPipes, $replicaProcs, $prevReplicaId;
+       public $childPipes, $childProcs, $prevChildId;
        public $copyOnly = false;
        public $isChild = false;
-       public $replicaId = false;
+       public $childId = false;
        public $noCount = false;
        public $debugLog, $infoLog, $criticalLog;
        /** @var ExternalStoreDB */
        public $store;
+       /** @var SqlBlobStore */
+       private $blobStore;
 
        private static $optionsWithArgs = [
                'procs',
-               'replica-id',
+               'child-id',
                'debug-log',
                'info-log',
                'critical-log'
@@ -85,7 +88,7 @@ class RecompressTracked {
                'procs' => 'numProcs',
                'copy-only' => 'copyOnly',
                'child' => 'isChild',
-               'replica-id' => 'replicaId',
+               'child-id' => 'childId',
                'debug-log' => 'debugLog',
                'info-log' => 'infoLog',
                'critical-log' => 'criticalLog',
@@ -114,12 +117,16 @@ class RecompressTracked {
                $this->store = $esFactory->getStore( 'DB' );
                if ( !$this->isChild ) {
                        $GLOBALS['wgDebugLogPrefix'] = "RCT M: ";
-               } elseif ( $this->replicaId !== false ) {
-                       $GLOBALS['wgDebugLogPrefix'] = "RCT {$this->replicaId}: ";
+               } elseif ( $this->childId !== false ) {
+                       $GLOBALS['wgDebugLogPrefix'] = "RCT {$this->childId}: ";
                }
                $this->pageBlobClass = function_exists( 'xdiff_string_bdiff' ) ?
                        DiffHistoryBlob::class : ConcatenatedGzipHistoryBlob::class;
                $this->orphanBlobClass = ConcatenatedGzipHistoryBlob::class;
+               // @phan-suppress-next-line PhanAccessMethodInternal
+               $this->blobStore = MediaWikiServices::getInstance()
+                       ->getBlobStoreFactory()
+                       ->newSqlBlobStore();
        }
 
        function debug( $msg ) {
@@ -145,8 +152,8 @@ class RecompressTracked {
 
        function logToFile( $msg, $file ) {
                $header = '[' . date( 'd\TH:i:s' ) . '] ' . wfHostname() . ' ' . posix_getpid();
-               if ( $this->replicaId !== false ) {
-                       $header .= "({$this->replicaId})";
+               if ( $this->childId !== false ) {
+                       $header .= "({$this->childId})";
                }
                $header .= ' ' . WikiMap::getCurrentWikiDbDomain()->getId();
                LegacyLogger::emit( sprintf( "%-50s %s\n", $header, $msg ), $file );
@@ -184,10 +191,10 @@ class RecompressTracked {
                }
 
                $this->syncDBs();
-               $this->startReplicaProcs();
+               $this->startChildProcs();
                $this->doAllPages();
                $this->doAllOrphans();
-               $this->killReplicaProcs();
+               $this->killChildProcs();
        }
 
        /**
@@ -217,12 +224,12 @@ class RecompressTracked {
         * This necessary because text recompression is slow: loading, compressing and
         * writing are all slow.
         */
-       function startReplicaProcs() {
+       function startChildProcs() {
                $wiki = WikiMap::getWikiIdFromDbDomain( WikiMap::getCurrentWikiDbDomain() );
 
                $cmd = 'php ' . Shell::escape( __FILE__ );
                foreach ( self::$cmdLineOptionMap as $cmdOption => $classOption ) {
-                       if ( $cmdOption == 'replica-id' ) {
+                       if ( $cmdOption == 'child-id' ) {
                                continue;
                        } elseif ( in_array( $cmdOption, self::$optionsWithArgs ) && isset( $this->$classOption ) ) {
                                $cmd .= " --$cmdOption " . Shell::escape( $this->$classOption );
@@ -234,7 +241,7 @@ class RecompressTracked {
                        ' --wiki ' . Shell::escape( $wiki ) .
                        ' ' . Shell::escape( ...$this->destClusters );
 
-               $this->replicaPipes = $this->replicaProcs = [];
+               $this->childPipes = $this->childProcs = [];
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
                        $pipes = [];
                        $spec = [
@@ -243,28 +250,28 @@ class RecompressTracked {
                                [ 'file', 'php://stderr', 'w' ]
                        ];
                        Wikimedia\suppressWarnings();
-                       $proc = proc_open( "$cmd --replica-id $i", $spec, $pipes );
+                       $proc = proc_open( "$cmd --child-id $i", $spec, $pipes );
                        Wikimedia\restoreWarnings();
                        if ( !$proc ) {
-                               $this->critical( "Error opening replica DB process: $cmd" );
+                               $this->critical( "Error opening child process: $cmd" );
                                exit( 1 );
                        }
-                       $this->replicaProcs[$i] = $proc;
-                       $this->replicaPipes[$i] = $pipes[0];
+                       $this->childProcs[$i] = $proc;
+                       $this->childPipes[$i] = $pipes[0];
                }
-               $this->prevReplicaId = -1;
+               $this->prevChildId = -1;
        }
 
        /**
         * Gracefully terminate the child processes
         */
-       function killReplicaProcs() {
-               $this->info( "Waiting for replica DB processes to finish..." );
+       function killChildProcs() {
+               $this->info( "Waiting for child processes to finish..." );
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
-                       $this->dispatchToReplica( $i, 'quit' );
+                       $this->dispatchToChild( $i, 'quit' );
                }
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
-                       $status = proc_close( $this->replicaProcs[$i] );
+                       $status = proc_close( $this->childProcs[$i] );
                        if ( $status ) {
                                $this->critical( "Warning: child #$i exited with status $status" );
                        }
@@ -273,24 +280,24 @@ class RecompressTracked {
        }
 
        /**
-        * Dispatch a command to the next available replica DB.
-        * This may block until a replica DB finishes its work and becomes available.
+        * Dispatch a command to the next available child process.
+        * This may block until a child process finishes its work and becomes available.
         * @param array|string ...$args
         */
        function dispatch( ...$args ) {
-               $pipes = $this->replicaPipes;
+               $pipes = $this->childPipes;
                $x = [];
                $y = [];
                $numPipes = stream_select( $x, $pipes, $y, 3600 );
                if ( !$numPipes ) {
-                       $this->critical( "Error waiting to write to replica DBs. Aborting" );
+                       $this->critical( "Error waiting to write to child process. Aborting" );
                        exit( 1 );
                }
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
-                       $replicaId = ( $i + $this->prevReplicaId + 1 ) % $this->numProcs;
-                       if ( isset( $pipes[$replicaId] ) ) {
-                               $this->prevReplicaId = $replicaId;
-                               $this->dispatchToReplica( $replicaId, $args );
+                       $childId = ( $i + $this->prevChildId + 1 ) % $this->numProcs;
+                       if ( isset( $pipes[$childId] ) ) {
+                               $this->prevChildId = $childId;
+                               $this->dispatchToChild( $childId, $args );
 
                                return;
                        }
@@ -300,14 +307,14 @@ class RecompressTracked {
        }
 
        /**
-        * Dispatch a command to a specified replica DB
-        * @param int $replicaId
+        * Dispatch a command to a specified child process
+        * @param int $childId
         * @param array|string $args
         */
-       function dispatchToReplica( $replicaId, $args ) {
+       function dispatchToChild( $childId, $args ) {
                $args = (array)$args;
                $cmd = implode( ' ', $args );
-               fwrite( $this->replicaPipes[$replicaId], "$cmd\n" );
+               fwrite( $this->childPipes[$childId], "$cmd\n" );
        }
 
        /**
@@ -531,7 +538,7 @@ class RecompressTracked {
                                }
                                $lastTextId = $row->bt_text_id;
                                // Load the text
-                               $text = Revision::getRevisionText( $row );
+                               $text = $this->blobStore->expandBlob( $row->old_text, $row->old_flags );
                                if ( $text === false ) {
                                        $this->critical( "Error loading {$row->bt_rev_id}/{$row->bt_text_id}" );
                                        continue;
@@ -685,7 +692,7 @@ class RecompressTracked {
                );
 
                foreach ( $res as $row ) {
-                       $text = Revision::getRevisionText( $row );
+                       $text = $this->blobStore->expandBlob( $row->old_text, $row->old_flags );
                        if ( $text === false ) {
                                $this->critical( "Error: cannot load revision text for old_id={$row->old_id}" );
                                continue;
index 5d756e8..7e57f67 100644 (file)
@@ -42,12 +42,13 @@ class UpdateSpecialPages extends Maintenance {
        }
 
        public function execute() {
-               global $wgQueryCacheLimit, $wgDisableQueryPageUpdate;
+               global $wgQueryCacheLimit;
 
                $dbw = $this->getDB( DB_MASTER );
 
                $this->doSpecialPageCacheUpdates( $dbw );
 
+               $disabledQueryPages = QueryPage::getDisabledQueryPages( $this->getConfig() );
                foreach ( QueryPage::getPages() as $page ) {
                        list( , $special ) = $page;
                        $limit = $page[2] ?? null;
@@ -59,7 +60,7 @@ class UpdateSpecialPages extends Maintenance {
                        }
 
                        if ( !$this->hasOption( 'override' )
-                               && $wgDisableQueryPageUpdate && in_array( $special, $wgDisableQueryPageUpdate )
+                               && isset( $disabledQueryPages[$special] )
                        ) {
                                $this->output( sprintf( "%-30s [QueryPage] disabled\n", $special ) );
                                continue;
index 5f7f9d5..cbe5d27 100644 (file)
@@ -110,8 +110,9 @@ class UserDupes {
         * @return bool
         */
        private function checkDupes( $doDelete = false ) {
+               $dbDomain = WikiMap::getCurrentWikiDbDomain()->getId();
                if ( $this->hasUniqueIndex() ) {
-                       echo wfWikiID() . " already has a unique index on its user table.\n";
+                       echo "$dbDomain already has a unique index on its user table.\n";
 
                        return true;
                }
@@ -122,7 +123,7 @@ class UserDupes {
                $dupes = $this->getDupes();
                $count = count( $dupes );
 
-               $this->out( "Found $count accounts with duplicate records on " . wfWikiID() . ".\n" );
+               $this->out( "Found $count accounts with duplicate records on $dbDomain.\n" );
                $this->trimmed = 0;
                $this->reassigned = 0;
                $this->failed = 0;
@@ -145,11 +146,13 @@ class UserDupes {
 
                if ( $this->trimmed > 0 ) {
                        if ( $doDelete ) {
-                               $this->out( "$this->trimmed duplicate user records were deleted from "
-                                       . wfWikiID() . ".\n" );
+                               $this->out(
+                                       "$this->trimmed duplicate user records were deleted from $dbDomain.\n" );
                        } else {
-                               $this->out( "$this->trimmed duplicate user accounts were found on "
-                                       . wfWikiID() . " which can be removed safely.\n" );
+                               $this->out(
+                                       "$this->trimmed duplicate user accounts were found on $dbDomain " .
+                                       "which can be removed safely.\n"
+                               );
                        }
                }
 
index c8eae03..9354435 100644 (file)
@@ -1690,6 +1690,7 @@ return [
                ],
        ],
        'mediawiki.page.watch.ajax' => [
+               'targets' => [ 'desktop', 'mobile' ],
                'scripts' => 'resources/src/mediawiki.page.watch.ajax.js',
                'dependencies' => [
                        'mediawiki.api',
@@ -1983,6 +1984,7 @@ return [
                        'resources/src/mediawiki.special/special.less',
                        'resources/src/mediawiki.special/apisandbox.css',
                        'resources/src/mediawiki.special/comparepages.less',
+                       'resources/src/mediawiki.special/contributions.less',
                        'resources/src/mediawiki.special/edittags.css',
                        'resources/src/mediawiki.special/movePage.css',
                        'resources/src/mediawiki.special/newpages.less',
@@ -2146,9 +2148,12 @@ return [
        'mediawiki.special.contributions' => [
                'scripts' => 'resources/src/mediawiki.special.contributions.js',
                'dependencies' => [
+                       'jquery.makeCollapsible',
+                       'oojs-ui',
                        'mediawiki.widgets.DateInputWidget',
                        'mediawiki.jqueryMsg',
-               ]
+               ],
+               'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.special.edittags' => [
                'scripts' => 'resources/src/mediawiki.special.edittags.js',
@@ -2195,6 +2200,9 @@ return [
                'styles' => 'resources/src/mediawiki.special.preferences.styles.ooui.less',
        ],
        'mediawiki.special.recentchanges' => [
+               'dependencies' => [
+                       'mediawiki.widgets'
+               ],
                'scripts' => 'resources/src/mediawiki.special.recentchanges.js',
                'targets' => [ 'desktop', 'mobile' ],
        ],
index ac89616..23e86f7 100644 (file)
@@ -125,12 +125,6 @@ li,
        }
 }
 
-fieldset.mw-collapsible .mw-collapsible-toggle {
-       position: absolute;
-       right: 0;
-       z-index: 1;
-}
-
 // special treatment for list items to match above
 // !important necessary to override overly-specific float left and right above.
 ol.mw-collapsible:not( @{exclude} ):before,
index b6aa5fd..2bf685e 100644 (file)
@@ -4,57 +4,58 @@
  * @class mw.errorLogger
  * @singleton
  */
-( function () {
-       'use strict';
+mw.errorLogger = {
+       /**
+        * Fired via mw.track when an error is not handled by local code and is caught by the
+        * window.onerror handler.
+        *
+        * @event global_error
+        * @param {string} errorMessage Error errorMessage.
+        * @param {string} url URL where error was raised.
+        * @param {number} lineNumber Line number where error was raised.
+        * @param {number} [columnNumber] Line number where error was raised. Not all browsers
+        *   support this.
+        * @param {Error|Mixed} [errorObject] The error object. Typically an instance of Error, but anything
+        *   (even a primitive value) passed to a throw clause will end up here.
+        */
 
-       mw.errorLogger = {
-               /**
-                * Fired via mw.track when an error is not handled by local code and is caught by the
-                * window.onerror handler.
-                *
-                * @event global_error
-                * @param {string} errorMessage Error errorMessage.
-                * @param {string} url URL where error was raised.
-                * @param {number} lineNumber Line number where error was raised.
-                * @param {number} [columnNumber] Line number where error was raised. Not all browsers
-                *   support this.
-                * @param {Error|Mixed} [errorObject] The error object. Typically an instance of Error, but anything
-                *   (even a primitive value) passed to a throw clause will end up here.
-                */
+       /**
+        * Install a window.onerror handler that will report via mw.track, while preserving
+        * any previous handler.
+        *
+        * @param {Object} window
+        */
+       installGlobalHandler: function ( window ) {
+               // We will preserve the return value of the previous handler. window.onerror works the
+               // opposite way than normal event handlers (returning true will prevent the default
+               // action, returning false will let the browser handle the error normally, by e.g.
+               // logging to the console), so our fallback old handler needs to return false.
+               var oldHandler = window.onerror || function () {
+                       return false;
+               };
 
                /**
-                * Install a window.onerror handler that will report via mw.track, while preserving
-                * any previous handler.
+                * Dumb window.onerror handler which forwards the errors via mw.track.
                 *
-                * @param {Object} window
+                * @param {string} errorMessage
+                * @param {string} url
+                * @param {number} lineNumber
+                * @param {number} [columnNumber]
+                * @param {Error|Mixed} [errorObject]
+                * @return {boolean} True to prevent the default action
+                * @fires global_error
                 */
-               installGlobalHandler: function ( window ) {
-                       // We will preserve the return value of the previous handler. window.onerror works the
-                       // opposite way than normal event handlers (returning true will prevent the default
-                       // action, returning false will let the browser handle the error normally, by e.g.
-                       // logging to the console), so our fallback old handler needs to return false.
-                       var oldHandler = window.onerror || function () {
-                               return false;
-                       };
-
-                       /**
-                        * Dumb window.onerror handler which forwards the errors via mw.track.
-                        *
-                        * @param {string} errorMessage
-                        * @param {string} url
-                        * @param {number} lineNumber
-                        * @param {number} [columnNumber]
-                        * @param {Error|Mixed} [errorObject]
-                        * @return {boolean} True to prevent the default action
-                        * @fires global_error
-                        */
-                       window.onerror = function ( errorMessage, url, lineNumber, columnNumber, errorObject ) {
-                               mw.track( 'global.error', { errorMessage: errorMessage, url: url,
-                                       lineNumber: lineNumber, columnNumber: columnNumber, errorObject: errorObject } );
-                               return oldHandler.apply( this, arguments );
-                       };
-               }
-       };
+               window.onerror = function ( errorMessage, url, lineNumber, columnNumber, errorObject ) {
+                       mw.track( 'global.error', {
+                               errorMessage: errorMessage,
+                               url: url,
+                               lineNumber: lineNumber,
+                               columnNumber: columnNumber,
+                               errorObject: errorObject
+                       } );
+                       return oldHandler.apply( this, arguments );
+               };
+       }
+};
 
-       mw.errorLogger.installGlobalHandler( window );
-}() );
+mw.errorLogger.installGlobalHandler( window );
index 470d826..22dc924 100644 (file)
@@ -6,6 +6,7 @@
 @ooui-font-size-browser: 16; // assumed browser default of `16px`
 @ooui-font-size-base: 0.875em; // equals `14px` at browser default of `16px`
 
+@ooui-spacing-small: 8 / @ooui-font-size-browser / @ooui-font-size-base; // equals `0.57142857em`≈`8px`
 @ooui-spacing-medium: 12 / @ooui-font-size-browser / @ooui-font-size-base; // equals `0.8571429em`≈`12px`
 @ooui-spacing-large: 16 / @ooui-font-size-browser / @ooui-font-size-base; // equals `1.1428571em`≈`16px`
 @ooui-padding-horizontal: 12 / @ooui-font-size-browser / @ooui-font-size-base;
        // Reducing `padding-top`, as the heading's `line-height` provides similar distance.
        padding: @ooui-spacing-medium @ooui-spacing-large @ooui-spacing-large;
 
-       // Trigger only when collapsible & JS is available via `.mw-collapsed`.
-       .client-js & .oo-ui-fieldsetLayout.mw-collapsed .oo-ui-fieldsetLayout-header {
-               // Negative margin to match the reduced distance on the top caused by the previous rule.
-               margin-bottom: -( @ooui-spacing-large - @ooui-spacing-medium );
+       .client-js & .oo-ui-fieldsetLayout.mw-collapsible .oo-ui-fieldsetLayout-header {
+               // Push legend up when JS is on, to increase clickable area.
+               margin-top: -@ooui-spacing-small;
+               margin-bottom: @ooui-spacing-small;
+               // Add `padding-top` to make up for negative `margin` above.
+               padding: @ooui-spacing-small;
+               // Make space for toggle icon defined below.
+               padding-left: 24 / @ooui-font-size-browser / @ooui-font-size-base;
 
                .oo-ui-labelElement-label {
                        margin-bottom: 0;
                }
        }
+
+       // Trigger only when collapsible & JS is available via `.mw-collapsed`.
+       .client-js & .oo-ui-fieldsetLayout.mw-collapsed .oo-ui-fieldsetLayout-header {
+               min-height: 30px;
+               // Negative margin to match the reduced distance on the top caused by the previous rule.
+               margin-bottom: -@ooui-spacing-medium;
+       }
 }
 
 .mw-htmlform-ooui {
 .mw-htmlform-ooui .mw-htmlform-submit-buttons {
        margin-top: @ooui-spacing-medium;
 }
+
+.oo-ui-fieldsetLayout.mw-collapsible {
+       .oo-ui-fieldsetLayout-header {
+               max-width: none;
+       }
+
+       .mw-collapsible-toggle .oo-ui-iconElement-icon {
+               position: absolute;
+               top: 0;
+               left: 0;
+               // Special case: Reduce to `16px` icon size here.
+               min-width: 16px;
+               width: 16 / @ooui-font-size-browser / @ooui-font-size-base;
+               margin-right: 0.5em;
+       }
+
+       // When expanded: only 'collapse' icon visible
+       .mw-collapsible-toggle .oo-ui-icon-expand {
+               display: none;
+       }
+
+       // When collapsed: only 'expand' icon visible
+       &.mw-collapsed {
+               .mw-collapsible-toggle .oo-ui-icon-expand {
+                       display: inline-block;
+               }
+
+               .mw-collapsible-toggle .oo-ui-icon-collapse {
+                       display: none;
+               }
+       }
+}
index dff7881..9559d3a 100644 (file)
        font-weight: bold;
 }
 
+// on smaller screen, set .watchlistDetail to full width
+// so that the spinner doesn't appear beside it. T225127#5518870
+@media screen and ( max-width: @width-breakpoint-tablet ) {
+       .client-js {
+               /* stylelint-disable-next-line selector-class-pattern */
+               .watchlistDetails {
+                       float: none;
+                       width: auto;
+               }
+       }
+}
+
 @-webkit-keyframes rcfiltersBouncedelay {
        // 50% equals 800ms
        0%,
index 52f7ff2..75da5dd 100644 (file)
                border-top: 2px solid @colorGray14;
        }
 }
+
+// On small screens, remove the table properties from the
+// top section. T225127#5518870
+@media screen and ( max-width: @width-breakpoint-tablet ) {
+       .mw-rcfilters-ui-watchlistTopSectionWidget {
+               .mw-rcfilters-ui-table,
+               .mw-rcfilters-ui-row,
+               .mw-rcfilters-ui-cell {
+                       display: block;
+               }
+
+               &-editWatchlistButton {
+                       margin-top: 1em;
+               }
+       }
+}
index 310832d..c62acd9 100644 (file)
@@ -2,7 +2,7 @@
  * JavaScript for Special:RecentChanges
  */
 ( function () {
-       var rc, $checkboxes, $select;
+       var rc, $checkboxes, $select, namespaceDropdown;
 
        /**
         * @class mw.special.recentchanges
                 */
                updateCheckboxes: function () {
                        // The option element for the 'all' namespace has an empty value
-                       var isAllNS = $select.val() === '';
+                       var value = $select.val(),
+                               isAllNS = value === 'all' || value === '';
 
                        // Iterates over checkboxes and propagate the selected option
                        $checkboxes.toggleClass( 'mw-input-hidden', isAllNS );
                },
 
                init: function () {
-                       $select = $( '#namespace' );
-                       $checkboxes = $( '#nsassociated, #nsinvert' ).closest( '.mw-input-with-label' );
+                       $select = $( 'select#namespace' );
+                       $checkboxes = $( '#nsassociated, #nsinvert, .contribs-ns-filters' )
+                               .closest( '.mw-input-with-label' );
 
-                       // Bind to change event of the checkboxes.
-                       // The initial state is already set in HTML.
-                       $select.on( 'change', rc.updateCheckboxes );
+                       if ( $select.length === 0 ) {
+                               $select = $( '#namespace select' );
+                               if ( $select.length > 0 ) {
+                                       namespaceDropdown = OO.ui.infuse( $( '#namespace' ).closest( '[data-ooui]' ) );
+                                       namespaceDropdown.on( 'change', rc.updateCheckboxes );
+                               }
+                       } else {
+                               // Bind to change event of the checkboxes.
+                               // The initial state is already set in HTML.
+                               $select.on( 'change', rc.updateCheckboxes );
+                       }
                }
        };
 
diff --git a/resources/src/mediawiki.special/contributions.less b/resources/src/mediawiki.special/contributions.less
new file mode 100644 (file)
index 0000000..cc0e538
--- /dev/null
@@ -0,0 +1,65 @@
+/*!
+ * Styling for Special:Contributions
+ */
+@import 'mediawiki.ui/variables.less';
+
+// OOUIHTMLForm styles.
+@ooui-font-size-browser: 16; // Assumed browser default of `16px`.
+@ooui-font-size-base: 0.875em; // Equals `14px` at browser default of `16px`.
+
+@ooui-spacing-small: 8 / @ooui-font-size-browser / @ooui-font-size-base; // Equals `0.57142857em`≈`8px`.
+@ooui-spacing-medium: 12 / @ooui-font-size-browser / @ooui-font-size-base; // Equals `0.8571429em`≈`12px`.
+@ooui-spacing-large: 16 / @ooui-font-size-browser / @ooui-font-size-base; // Equals `1.1428571em`≈`16px`.
+
+.oo-ui-fieldsetLayout-group {
+       max-width: 50em;
+
+       .oo-ui-panelLayout-padded.oo-ui-panelLayout-framed {
+               margin: 0;
+               border: 0;
+               padding: 0;
+       }
+
+       // Hide extra `legend`s when grouping form in sections.
+       .oo-ui-fieldsetLayout.oo-ui-labelElement > .oo-ui-fieldsetLayout-header {
+               display: none;
+       }
+}
+
+.mw-autocomplete-user.oo-ui-fieldLayout {
+       margin-top: @ooui-spacing-small;
+}
+
+// Higher specificity needed to override OOUIHTMLForm styles.
+.mw-htmlform-field-HTMLMultiSelectField.mw-htmlform-flatlist.oo-ui-fieldLayout {
+       margin-top: @ooui-spacing-small;
+}
+
+.mw-htmlform-field-HTMLTagFilter ~ .mw-htmlform-field-HTMLCheckField.oo-ui-fieldLayout {
+       display: inline-block;
+       padding-right: @ooui-spacing-large;
+}
+
+// Clearfix for floated `.mw-htmlform-field-HTMLDateTimeField` below.
+#mw-htmlform-contribs-date:after {
+       content: '';
+       clear: both;
+       display: block;
+}
+
+.mw-htmlform-field-HTMLDateTimeField {
+       margin-right: @ooui-spacing-large;
+       margin-bottom: @ooui-spacing-small;
+
+       .oo-ui-fieldLayout.oo-ui-labelElement&:first-child {
+               margin-top: @ooui-spacing-medium;
+       }
+}
+
+@media all and ( min-width: @width-breakpoint-tablet ) {
+       .mw-htmlform-field-HTMLDateTimeField {
+               float: left;
+               // Same `width` as DateInputWidget.
+               width: 21em;
+       }
+}
index 3f76cf0..40a2ec2 100644 (file)
        font-weight: bold;
 }
 
-.mw-contributions-form select {
-       vertical-align: middle;
-}
-
 /* Special:EditWatchlist */
 .watchlistredir {
        font-style: italic;
index 0eb1134..89de155 100644 (file)
@@ -73,7 +73,7 @@
 
                // Highlight matching parts of link suggestion
                if ( config.query ) {
-                       this.setHighlightedQuery( config.data, config.query, config.compare );
+                       this.setHighlightedQuery( config.data, config.query, config.compare, true );
                }
                this.$label.attr( 'title', config.data );
 
index 21d95df..f814d89 100644 (file)
@@ -13,7 +13,7 @@
        'use strict';
 
        var mw, StringSet, log,
-               hasOwn = Object.prototype.hasOwnProperty;
+               hasOwn = Object.hasOwnProperty;
 
        /**
         * FNV132 hash function
                                        try {
                                                sortDependencies( modules[ i ], resolved );
                                        } catch ( err ) {
-                                               // This module is unknown or has unknown dependencies.
-                                               // Undo any incomplete resolutions made and keep going.
+                                               // This module is not currently known, or has invalid dependencies.
+                                               // Most likely due to a cached reference after the module was
+                                               // removed, otherwise made redundant, or omitted from the registry
+                                               // by the ResourceLoader "target" system.
                                                resolved = saved;
-                                               mw.trackError( 'resourceloader.exception', {
-                                                       exception: err,
-                                                       source: 'resolve'
-                                               } );
+                                               mw.log.warn( 'Skipped unresolvable module ' + modules[ i ] );
+                                               if ( modules[ i ] in registry ) {
+                                                       // If the module was known but had unknown or circular dependencies,
+                                                       // also track it as an error.
+                                                       mw.trackError( 'resourceloader.exception', {
+                                                               exception: err,
+                                                               source: 'resolve'
+                                                       } );
+                                               }
                                        }
                                }
                                return resolved;
index afb4737..2787f58 100644 (file)
@@ -1,54 +1,51 @@
 /* global mw */
-( function () {
-       var maxBusy = 50;
+mw.requestIdleCallbackInternal = function ( callback ) {
+       setTimeout( function () {
+               var start = mw.now();
+               callback( {
+                       didTimeout: false,
+                       timeRemaining: function () {
+                               // Hard a target maximum busy time of 50 milliseconds
+                               return Math.max( 0, 50 - ( mw.now() - start ) );
+                       }
+               } );
+       }, 1 );
+};
 
-       mw.requestIdleCallbackInternal = function ( callback ) {
-               setTimeout( function () {
-                       var start = mw.now();
-                       callback( {
-                               didTimeout: false,
-                               timeRemaining: function () {
-                                       return Math.max( 0, maxBusy - ( mw.now() - start ) );
-                               }
-                       } );
-               }, 1 );
-       };
-
-       /**
-        * Schedule a deferred task to run in the background.
-        *
-        * This allows code to perform tasks in the main thread without impacting
-        * time-critical operations such as animations and response to input events.
-        *
-        * Basic logic is as follows:
-        *
-        * - User input event should be acknowledged within 100ms per [RAIL].
-        * - Idle work should be grouped in blocks of upto 50ms so that enough time
-        *   remains for the event handler to execute and any rendering to take place.
-        * - Whenever a native event happens (e.g. user input), the deadline for any
-        *   running idle callback drops to 0.
-        * - As long as the deadline is non-zero, other callbacks pending may be
-        *   executed in the same idle period.
-        *
-        * See also:
-        *
-        * - <https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback>
-        * - <https://w3c.github.io/requestidlecallback/>
-        * - <https://developers.google.com/web/updates/2015/08/using-requestidlecallback>
-        * [RAIL]: https://developers.google.com/web/fundamentals/performance/rail
-        *
-        * @member mw
-        * @param {Function} callback
-        * @param {Object} [options]
-        * @param {number} [options.timeout] If set, the callback will be scheduled for
-        *  immediate execution after this amount of time (in milliseconds) if it didn't run
-        *  by that time.
-        */
-       mw.requestIdleCallback = window.requestIdleCallback ?
-               // Bind because it throws TypeError if context is not window
-               window.requestIdleCallback.bind( window ) :
-               mw.requestIdleCallbackInternal;
-       // Note: Polyfill was previously disabled due to
-       // https://bugs.chromium.org/p/chromium/issues/detail?id=647870
-       // See also <http://codepen.io/Krinkle/full/XNGEvv>
-}() );
+/**
+ * Schedule a deferred task to run in the background.
+ *
+ * This allows code to perform tasks in the main thread without impacting
+ * time-critical operations such as animations and response to input events.
+ *
+ * Basic logic is as follows:
+ *
+ * - User input event should be acknowledged within 100ms per [RAIL].
+ * - Idle work should be grouped in blocks of upto 50ms so that enough time
+ *   remains for the event handler to execute and any rendering to take place.
+ * - Whenever a native event happens (e.g. user input), the deadline for any
+ *   running idle callback drops to 0.
+ * - As long as the deadline is non-zero, other callbacks pending may be
+ *   executed in the same idle period.
+ *
+ * See also:
+ *
+ * - <https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback>
+ * - <https://w3c.github.io/requestidlecallback/>
+ * - <https://developers.google.com/web/updates/2015/08/using-requestidlecallback>
+ * [RAIL]: https://developers.google.com/web/fundamentals/performance/rail
+ *
+ * @member mw
+ * @param {Function} callback
+ * @param {Object} [options]
+ * @param {number} [options.timeout] If set, the callback will be scheduled for
+ *  immediate execution after this amount of time (in milliseconds) if it didn't run
+ *  by that time.
+ */
+mw.requestIdleCallback = window.requestIdleCallback ?
+       // Bind because it throws TypeError if context is not window
+       window.requestIdleCallback.bind( window ) :
+       mw.requestIdleCallbackInternal;
+// Note: Polyfill was previously disabled due to
+// https://bugs.chromium.org/p/chromium/issues/detail?id=647870
+// See also <http://codepen.io/Krinkle/full/XNGEvv>
index 00ff2c9..c479c2d 100644 (file)
@@ -178,7 +178,6 @@ $wgAutoloadClasses += [
        'MediaWiki\Tests\Revision\RevisionSlotsTest' => "$testDir/phpunit/includes/Revision/RevisionSlotsTest.php",
        'MediaWiki\Tests\Revision\RevisionRecordTests' => "$testDir/phpunit/includes/Revision/RevisionRecordTests.php",
        'MediaWiki\Tests\Revision\RevisionStoreDbTestBase' => "$testDir/phpunit/includes/Revision/RevisionStoreDbTestBase.php",
-       'MediaWiki\Tests\Revision\PreMcrSchemaOverride' => "$testDir/phpunit/includes/Revision/PreMcrSchemaOverride.php",
        'MediaWiki\Tests\Revision\RevisionStoreRecordTest' => "$testDir/phpunit/includes/Revision/RevisionStoreRecordTest.php",
 
        # tests/phpunit/languages
index 27cbed5..a82c064 100644 (file)
@@ -1465,7 +1465,7 @@ abstract class MediaWikiIntegrationTestCase extends PHPUnit\Framework\TestCase {
                        $db->_originalTablePrefix = $oldPrefix;
 
                        $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
-                       $lb->setTempTablesOnlyMode( self::$useTemporaryTables, $lb->getLocalDomainID() );
+                       $lb->setTempTablesOnlyMode( self::$useTemporaryTables, $db->getDomainID() );
                }
 
                return true;
index 9c08b9f..57cc073 100644 (file)
@@ -73,6 +73,47 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                return $store;
        }
 
+       /**
+        * @dataProvider provideConstructor
+        * @param int $stage
+        * @param string|null $exceptionMsg
+        */
+       public function testConstructor( $stage, $exceptionMsg ) {
+               try {
+                       $m = new CommentStore( Language::factory( 'qqx' ), $stage );
+                       if ( $exceptionMsg !== null ) {
+                               $this->fail( 'Expected exception not thrown' );
+                       }
+                       $this->assertInstanceOf( CommentStore::class, $m );
+               } catch ( InvalidArgumentException $ex ) {
+                       $this->assertSame( $exceptionMsg, $ex->getMessage() );
+               }
+       }
+
+       public static function provideConstructor() {
+               return [
+                       [ 0, '$stage must include a write mode' ],
+                       [ SCHEMA_COMPAT_READ_OLD, '$stage must include a write mode' ],
+                       [ SCHEMA_COMPAT_READ_NEW, '$stage must include a write mode' ],
+                       [ SCHEMA_COMPAT_READ_BOTH, '$stage must include a write mode' ],
+
+                       [ SCHEMA_COMPAT_WRITE_OLD, '$stage must include a read mode' ],
+                       [ SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_OLD, null ],
+                       [ SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_NEW, null ],
+                       [ SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_BOTH, null ],
+
+                       [ SCHEMA_COMPAT_WRITE_NEW, '$stage must include a read mode' ],
+                       [ SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_OLD, null ],
+                       [ SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_NEW, null ],
+                       [ SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_BOTH, null ],
+
+                       [ SCHEMA_COMPAT_WRITE_BOTH, '$stage must include a read mode' ],
+                       [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, null ],
+                       [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, null ],
+                       [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_BOTH, null ],
+               ];
+       }
+
        /**
         * @dataProvider provideGetFields
         * @param int $stage
@@ -115,6 +156,14 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                MIGRATION_NEW, 'ipb_reason',
                                [ 'ipb_reason_id' => 'ipb_reason_id' ],
                        ],
+                       'Simple table, write-both/read-old' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'ipb_reason',
+                               [ 'ipb_reason_text' => 'ipb_reason', 'ipb_reason_data' => 'NULL', 'ipb_reason_cid' => 'NULL' ],
+                       ],
+                       'Simple table, write-both/read-new' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'ipb_reason',
+                               [ 'ipb_reason_id' => 'ipb_reason_id' ],
+                       ],
 
                        'Revision, old' => [
                                MIGRATION_OLD, 'rev_comment',
@@ -136,6 +185,18 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                MIGRATION_NEW, 'rev_comment',
                                [ 'rev_comment_pk' => 'rev_id' ],
                        ],
+                       'Revision, write-both/read-old' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rev_comment',
+                               [
+                                       'rev_comment_text' => 'rev_comment',
+                                       'rev_comment_data' => 'NULL',
+                                       'rev_comment_cid' => 'NULL',
+                               ],
+                       ],
+                       'Revision, write-both/read-new' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rev_comment',
+                               [ 'rev_comment_pk' => 'rev_id' ],
+                       ],
 
                        'Image, old' => [
                                MIGRATION_OLD, 'img_description',
@@ -165,6 +226,20 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                        'img_description_id' => 'img_description_id'
                                ],
                        ],
+                       'Image, write-both/read-old' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'img_description',
+                               [
+                                       'img_description_text' => 'img_description',
+                                       'img_description_data' => 'NULL',
+                                       'img_description_cid' => 'NULL',
+                               ],
+                       ],
+                       'Image, write-both/read-new' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'img_description',
+                               [
+                                       'img_description_id' => 'img_description_id'
+                               ],
+                       ],
                ];
        }
 
@@ -244,6 +319,30 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                        ],
                                ],
                        ],
+                       'Simple table, write-both/read-old' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'ipb_reason', [
+                                       'tables' => [],
+                                       'fields' => [
+                                               'ipb_reason_text' => 'ipb_reason',
+                                               'ipb_reason_data' => 'NULL',
+                                               'ipb_reason_cid' => 'NULL',
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Simple table, write-both/read-new' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'ipb_reason', [
+                                       'tables' => [ 'comment_ipb_reason' => 'comment' ],
+                                       'fields' => [
+                                               'ipb_reason_text' => 'comment_ipb_reason.comment_text',
+                                               'ipb_reason_data' => 'comment_ipb_reason.comment_data',
+                                               'ipb_reason_cid' => 'comment_ipb_reason.comment_id',
+                                       ],
+                                       'joins' => [
+                                               'comment_ipb_reason' => [ 'JOIN', 'comment_ipb_reason.comment_id = ipb_reason_id' ],
+                                       ],
+                               ],
+                       ],
 
                        'Revision, old' => [
                                MIGRATION_OLD, 'rev_comment', [
@@ -310,6 +409,35 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                        ],
                                ],
                        ],
+                       'Revision, write-both/read-old' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rev_comment', [
+                                       'tables' => [],
+                                       'fields' => [
+                                               'rev_comment_text' => 'rev_comment',
+                                               'rev_comment_data' => 'NULL',
+                                               'rev_comment_cid' => 'NULL',
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Revision, write-both/read-new' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rev_comment', [
+                                       'tables' => [
+                                               'temp_rev_comment' => 'revision_comment_temp',
+                                               'comment_rev_comment' => 'comment',
+                                       ],
+                                       'fields' => [
+                                               'rev_comment_text' => 'comment_rev_comment.comment_text',
+                                               'rev_comment_data' => 'comment_rev_comment.comment_data',
+                                               'rev_comment_cid' => 'comment_rev_comment.comment_id',
+                                       ],
+                                       'joins' => [
+                                               'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
+                                               'comment_rev_comment' => [ 'JOIN',
+                                                       'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       ],
+                               ],
+                       ],
 
                        'Image, old' => [
                                MIGRATION_OLD, 'img_description', [
@@ -373,6 +501,34 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                        ],
                                ],
                        ],
+                       'Image, write-both/read-old' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'img_description', [
+                                       'tables' => [],
+                                       'fields' => [
+                                               'img_description_text' => 'img_description',
+                                               'img_description_data' => 'NULL',
+                                               'img_description_cid' => 'NULL',
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Image, write-both/read-new' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'img_description', [
+                                       'tables' => [
+                                               'comment_img_description' => 'comment',
+                                       ],
+                                       'fields' => [
+                                               'img_description_text' => 'comment_img_description.comment_text',
+                                               'img_description_data' => 'comment_img_description.comment_data',
+                                               'img_description_cid' => 'comment_img_description.comment_id',
+                                       ],
+                                       'joins' => [
+                                               'comment_img_description' => [ 'JOIN',
+                                                       'comment_img_description.comment_id = img_description_id',
+                                               ],
+                                       ],
+                               ],
+                       ],
                ];
        }
 
@@ -413,6 +569,15 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                MIGRATION_NEW ],
                        MIGRATION_WRITE_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
                        MIGRATION_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
+
+                       SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD => [
+                               MIGRATION_OLD, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, MIGRATION_NEW
+                       ],
+                       SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW => [
+                               MIGRATION_OLD, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, MIGRATION_NEW
+                       ],
                ];
 
                foreach ( $stages as $writeStage => $possibleReadStages ) {
@@ -427,12 +592,12 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                $fields = $wstore->insert( $this->db, $key, $comment, $data );
                        }
 
-                       if ( $writeStage <= MIGRATION_WRITE_BOTH ) {
+                       if ( $writeStage & SCHEMA_COMPAT_WRITE_OLD ) {
                                $this->assertSame( $expect['text'], $fields[$key], "old field, stage=$writeStage" );
                        } else {
                                $this->assertArrayNotHasKey( $key, $fields, "old field, stage=$writeStage" );
                        }
-                       if ( $writeStage >= MIGRATION_WRITE_BOTH && !$usesTemp ) {
+                       if ( ( $writeStage & SCHEMA_COMPAT_WRITE_NEW ) && !$usesTemp ) {
                                $this->assertArrayHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
                        } else {
                                $this->assertArrayNotHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
@@ -463,13 +628,17 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                        $queryInfo['joins']
                                );
 
+                               $expectForCombination = (
+                                       ( $writeStage & SCHEMA_COMPAT_WRITE_BOTH ) === SCHEMA_COMPAT_WRITE_OLD ||
+                                       ( $readStage & SCHEMA_COMPAT_READ_BOTH ) === SCHEMA_COMPAT_READ_OLD
+                               ) ? $expectOld : $expect;
                                $this->assertComment(
-                                       $writeStage === MIGRATION_OLD || $readStage === MIGRATION_OLD ? $expectOld : $expect,
+                                       $expectForCombination,
                                        $rstore->getCommentLegacy( $this->db, $key, $fieldRow ),
                                        "w=$writeStage, r=$readStage, from getFields()"
                                );
                                $this->assertComment(
-                                       $writeStage === MIGRATION_OLD || $readStage === MIGRATION_OLD ? $expectOld : $expect,
+                                       $expectForCombination,
                                        $rstore->getComment( $key, $joinRow ),
                                        "w=$writeStage, r=$readStage, from getJoin()"
                                );
@@ -503,6 +672,15 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                MIGRATION_NEW ],
                        MIGRATION_WRITE_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
                        MIGRATION_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
+
+                       SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD => [
+                               MIGRATION_OLD, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, MIGRATION_NEW
+                       ],
+                       SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW => [
+                               MIGRATION_OLD, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, MIGRATION_NEW
+                       ],
                ];
 
                foreach ( $stages as $writeStage => $possibleReadStages ) {
@@ -517,12 +695,12 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                $fields = $wstore->insert( $this->db, $comment, $data );
                        }
 
-                       if ( $writeStage <= MIGRATION_WRITE_BOTH ) {
+                       if ( $writeStage & SCHEMA_COMPAT_WRITE_OLD ) {
                                $this->assertSame( $expect['text'], $fields[$key], "old field, stage=$writeStage" );
                        } else {
                                $this->assertArrayNotHasKey( $key, $fields, "old field, stage=$writeStage" );
                        }
-                       if ( $writeStage >= MIGRATION_WRITE_BOTH && !$usesTemp ) {
+                       if ( ( $writeStage & SCHEMA_COMPAT_WRITE_NEW ) && !$usesTemp ) {
                                $this->assertArrayHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
                        } else {
                                $this->assertArrayNotHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
@@ -553,13 +731,17 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                        $queryInfo['joins']
                                );
 
+                               $expectForCombination = (
+                                       ( $writeStage & SCHEMA_COMPAT_WRITE_BOTH ) === SCHEMA_COMPAT_WRITE_OLD ||
+                                       ( $readStage & SCHEMA_COMPAT_READ_BOTH ) === SCHEMA_COMPAT_READ_OLD
+                               ) ? $expectOld : $expect;
                                $this->assertComment(
-                                       $writeStage === MIGRATION_OLD || $readStage === MIGRATION_OLD ? $expectOld : $expect,
+                                       $expectForCombination,
                                        $rstore->getCommentLegacy( $this->db, $fieldRow ),
                                        "w=$writeStage, r=$readStage, from getFields()"
                                );
                                $this->assertComment(
-                                       $writeStage === MIGRATION_OLD || $readStage === MIGRATION_OLD ? $expectOld : $expect,
+                                       $expectForCombination,
                                        $rstore->getComment( $joinRow ),
                                        "w=$writeStage, r=$readStage, from getJoin()"
                                );
@@ -725,6 +907,9 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                        'MIGRATION_WRITE_BOTH' => [ MIGRATION_WRITE_BOTH ],
                        'MIGRATION_WRITE_NEW' => [ MIGRATION_WRITE_NEW ],
                        'MIGRATION_NEW' => [ MIGRATION_NEW ],
+
+                       'SCHEMA_COMPAT write-both/read-old' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD ],
+                       'SCHEMA_COMPAT write-both/read-new' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW ],
                ];
        }
 
index 4401410..35d06dd 100644 (file)
@@ -566,11 +566,11 @@ class HtmlTest extends MediaWikiTestCase {
                        '<div class="errorbox">err</div>'
                );
                $this->assertEquals(
-                       Html::errorBox( 'err', 'heading' ),
-                       '<div class="errorbox"><h2>heading</h2>err</div>'
+                       Html::errorBox( 'err', 'heading', 'errorbox-custom-class' ),
+                       '<div class="errorbox errorbox-custom-class"><h2>heading</h2>err</div>'
                );
                $this->assertEquals(
-                       Html::errorBox( 'err', '0' ),
+                       Html::errorBox( 'err', '0', '' ),
                        '<div class="errorbox"><h2>0</h2>err</div>'
                );
        }
diff --git a/tests/phpunit/includes/Revision/NoContentModelRevisionStoreDbTest.php b/tests/phpunit/includes/Revision/NoContentModelRevisionStoreDbTest.php
deleted file mode 100644 (file)
index 51cfc63..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-<?php
-
-namespace MediaWiki\Tests\Revision;
-
-use Revision;
-
-/**
- * Tests RevisionStore against the pre-MCR, pre-ContentHandler DB schema.
- *
- * @covers \MediaWiki\Revision\RevisionStore
- *
- * @group RevisionStore
- * @group Storage
- * @group Database
- * @group medium
- */
-class NoContentModelRevisionStoreDbTest extends RevisionStoreDbTestBase {
-
-       use PreMcrSchemaOverride;
-
-       protected function getContentHandlerUseDB() {
-               return false;
-       }
-
-       protected function revisionToRow( Revision $rev, $options = [ 'page', 'user', 'comment' ] ) {
-               $row = parent::revisionToRow( $rev, $options );
-
-               $row->rev_text_id = (string)$rev->getTextId();
-
-               return $row;
-       }
-
-       public function provideGetArchiveQueryInfo() {
-               yield [
-                       [
-                               'tables' => [ 'archive' ],
-                               'fields' => array_merge(
-                                       $this->getDefaultArchiveFields(),
-                                       [
-                                               'ar_comment_text' => 'ar_comment',
-                                               'ar_comment_data' => 'NULL',
-                                               'ar_comment_cid' => 'NULL',
-                                               'ar_user_text' => 'ar_user_text',
-                                               'ar_user' => 'ar_user',
-                                               'ar_actor' => 'NULL',
-                                       ]
-                               ),
-                               'joins' => [],
-                       ]
-               ];
-       }
-
-       public function provideGetQueryInfo() {
-               yield [
-                       [],
-                       [
-                               'tables' => [ 'revision' ],
-                               'fields' => array_merge(
-                                       $this->getDefaultQueryFields(),
-                                       $this->getCommentQueryFields(),
-                                       $this->getActorQueryFields()
-                               ),
-                               'joins' => [],
-                       ]
-               ];
-               yield [
-                       [ 'page' ],
-                       [
-                               'tables' => [ 'revision', 'page' ],
-                               'fields' => array_merge(
-                                       $this->getDefaultQueryFields(),
-                                       $this->getCommentQueryFields(),
-                                       $this->getActorQueryFields(),
-                                       [
-                                               'page_namespace',
-                                               'page_title',
-                                               'page_id',
-                                               'page_latest',
-                                               'page_is_redirect',
-                                               'page_len',
-                                       ]
-                               ),
-                               'joins' => [
-                                       'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
-                               ],
-                       ]
-               ];
-               yield [
-                       [ 'user' ],
-                       [
-                               'tables' => [ 'revision', 'user' ],
-                               'fields' => array_merge(
-                                       $this->getDefaultQueryFields(),
-                                       $this->getCommentQueryFields(),
-                                       $this->getActorQueryFields(),
-                                       [
-                                               'user_name',
-                                       ]
-                               ),
-                               'joins' => [
-                                       'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
-                               ],
-                       ]
-               ];
-               yield [
-                       [ 'text' ],
-                       [
-                               'tables' => [ 'revision', 'text' ],
-                               'fields' => array_merge(
-                                       $this->getDefaultQueryFields(),
-                                       $this->getCommentQueryFields(),
-                                       $this->getActorQueryFields(),
-                                       [
-                                               'old_text',
-                                               'old_flags',
-                                       ]
-                               ),
-                               'joins' => [
-                                       'text' => [ 'JOIN', [ 'rev_text_id=old_id' ] ],
-                               ],
-                       ]
-               ];
-       }
-
-       public function provideGetSlotsQueryInfo() {
-               $db = wfGetDB( DB_REPLICA );
-
-               yield [
-                       [],
-                       [
-                               'tables' => [
-                                       'slots' => 'revision',
-                               ],
-                               'fields' => array_merge(
-                                       [
-                                               'slot_revision_id' => 'slots.rev_id',
-                                               'slot_content_id' => 'NULL',
-                                               'slot_origin' => 'slots.rev_id',
-                                               'role_name' => $db->addQuotes( SlotRecord::MAIN ),
-                                       ]
-                               ),
-                               'joins' => [],
-                       ]
-               ];
-               yield [
-                       [ 'content' ],
-                       [
-                               'tables' => [
-                                       'slots' => 'revision',
-                               ],
-                               'fields' => array_merge(
-                                       [
-                                               'slot_revision_id' => 'slots.rev_id',
-                                               'slot_content_id' => 'NULL',
-                                               'slot_origin' => 'slots.rev_id',
-                                               'role_name' => $db->addQuotes( SlotRecord::MAIN ),
-                                               'content_size' => 'slots.rev_len',
-                                               'content_sha1' => 'slots.rev_sha1',
-                                               'content_address' =>
-                                                       $db->buildConcat( [ $db->addQuotes( 'tt:' ), 'slots.rev_text_id' ] ),
-                                               'model_name' => 'NULL',
-                                       ]
-                               ),
-                               'joins' => [],
-                       ]
-               ];
-       }
-
-       public function provideNewMutableRevisionFromArray() {
-               foreach ( parent::provideNewMutableRevisionFromArray() as $case ) {
-                       yield $case;
-               }
-
-               yield 'Basic array, with page & id' => [
-                       [
-                               'id' => 2,
-                               'page' => 1,
-                               'text_id' => 2,
-                               'timestamp' => '20171017114835',
-                               'user_text' => '111.0.1.2',
-                               'user' => 0,
-                               'minor_edit' => false,
-                               'deleted' => 0,
-                               'len' => 46,
-                               'parent_id' => 1,
-                               'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
-                               'comment' => 'Goat Comment!',
-                       ]
-               ];
-       }
-
-       /**
-        * Conditions to use together with getSlotsQueryInfo() when selecting slot rows for a given
-        * revision.
-        *
-        * @return array
-        */
-       protected function getSlotRevisionConditions( $revId ) {
-               return [ 'rev_id' => $revId ];
-       }
-
-}
diff --git a/tests/phpunit/includes/Revision/PreMcrRevisionStoreDbTest.php b/tests/phpunit/includes/Revision/PreMcrRevisionStoreDbTest.php
deleted file mode 100644 (file)
index 468ab60..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-
-namespace MediaWiki\Tests\Revision;
-
-use InvalidArgumentException;
-use MediaWiki\Revision\RevisionRecord;
-use Revision;
-use WikitextContent;
-
-/**
- * Tests RevisionStore against the pre-MCR DB schema.
- *
- * @covers \MediaWiki\Revision\RevisionStore
- *
- * @group RevisionStore
- * @group Storage
- * @group Database
- * @group medium
- */
-class PreMcrRevisionStoreDbTest extends RevisionStoreDbTestBase {
-
-       use PreMcrSchemaOverride;
-
-       protected function revisionToRow( Revision $rev, $options = [ 'page', 'user', 'comment' ] ) {
-               $row = parent::revisionToRow( $rev, $options );
-
-               $row->rev_text_id = (string)$rev->getTextId();
-               $row->rev_content_format = (string)$rev->getContentFormat();
-               $row->rev_content_model = (string)$rev->getContentModel();
-
-               return $row;
-       }
-
-       protected function assertRevisionExistsInDatabase( RevisionRecord $rev ) {
-               // Legacy schema is still being written
-               $this->assertSelect(
-                       [ 'revision', 'text' ],
-                       [ 'count(*)' ],
-                       [ 'rev_id' => $rev->getId(), 'rev_text_id > 0' ],
-                       [ [ 1 ] ],
-                       [],
-                       [ 'text' => [ 'JOIN', [ 'rev_text_id = old_id' ] ] ]
-               );
-
-               parent::assertRevisionExistsInDatabase( $rev );
-       }
-
-       public function provideInsertRevisionOn_failures() {
-               foreach ( parent::provideInsertRevisionOn_failures() as $case ) {
-                       yield $case;
-               }
-
-               yield 'slot that is not main slot' => [
-                       [
-                               'content' => [
-                                       'main' => new WikitextContent( 'Chicken' ),
-                                       'lalala' => new WikitextContent( 'Duck' ),
-                               ],
-                               'comment' => $this->getRandomCommentStoreComment(),
-                               'timestamp' => '20171117010101',
-                               'user' => true,
-                       ],
-                       new InvalidArgumentException( 'Only the main slot is supported' )
-               ];
-       }
-
-       public function provideNewMutableRevisionFromArray() {
-               foreach ( parent::provideNewMutableRevisionFromArray() as $case ) {
-                       yield $case;
-               }
-
-               yield 'Basic array, with page & id' => [
-                       [
-                               'id' => 2,
-                               'page' => 1,
-                               'text_id' => 2,
-                               'timestamp' => '20171017114835',
-                               'user_text' => '111.0.1.2',
-                               'user' => 0,
-                               'minor_edit' => false,
-                               'deleted' => 0,
-                               'len' => 46,
-                               'parent_id' => 1,
-                               'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
-                               'comment' => 'Goat Comment!',
-                               'content_format' => 'text/x-wiki',
-                               'content_model' => 'wikitext',
-                       ]
-               ];
-       }
-
-       /**
-        * Conditions to use together with getSlotsQueryInfo() when selecting slot rows for a given
-        * revision.
-        *
-        * @return array
-        */
-       protected function getSlotRevisionConditions( $revId ) {
-               return [ 'rev_id' => $revId ];
-       }
-
-}
diff --git a/tests/phpunit/includes/Revision/PreMcrSchemaOverride.php b/tests/phpunit/includes/Revision/PreMcrSchemaOverride.php
deleted file mode 100644 (file)
index 1fe2d6f..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-namespace MediaWiki\Tests\Revision;
-
-use Wikimedia\Rdbms\IMaintainableDatabase;
-use MediaWiki\DB\PatchFileLocation;
-
-/**
- * Trait providing schema overrides that allow tests to run against the pre-MCR database schema.
- */
-trait PreMcrSchemaOverride {
-
-       use PatchFileLocation;
-       use McrSchemaDetection;
-
-       /**
-        * @return int
-        */
-       protected function getMcrMigrationStage() {
-               return MIGRATION_OLD;
-       }
-
-       /**
-        * @return string[]
-        */
-       protected function getMcrTablesToReset() {
-               return [];
-       }
-
-       /**
-        * @return array[]
-        */
-       protected function getSchemaOverrides( IMaintainableDatabase $db ) {
-               $overrides = [
-                       'scripts' => [],
-                       'drop' => [],
-                       'create' => [],
-                       'alter' => [],
-               ];
-
-               if ( $this->hasMcrTables( $db ) ) {
-                       $overrides['drop'] = [ 'slots', 'content', 'slot_roles', 'content_models', ];
-                       $overrides['scripts'][] = $this->getSqlPatchPath( $db, '/drop-mcr-tables', __DIR__ );
-               }
-
-               if ( !$this->hasPreMcrFields( $db ) ) {
-                       $overrides['alter'][] = 'revision';
-                       $overrides['scripts'][] = $this->getSqlPatchPath( $db, '/create-pre-mcr-fields', __DIR__ );
-               }
-
-               return $overrides;
-       }
-
-}
index 949b664..0196c5d 100644 (file)
@@ -172,29 +172,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                ],
                        ]
                ];
-               yield 'pre-MCR, no model' => [
-                       [
-                               'wgContentHandlerUseDB' => false,
-                               'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                       ],
-                       [
-                               'tables' => [
-                                       'archive',
-                                       'actor_ar_user' => 'actor',
-                                       'comment_ar_comment' => 'comment',
-                               ],
-                               'fields' => array_merge(
-                                       $this->getArchiveQueryFields( true ),
-                                       $this->getNewActorQueryFields( 'ar' ),
-                                       $this->getNewCommentQueryFields( 'ar' )
-                               ),
-                               'joins' => [
-                                       'comment_ar_comment'
-                                               => [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
-                                       'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
-                               ],
-                       ]
-               ];
        }
 
        public function provideQueryInfo() {
@@ -397,238 +374,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                ],
                        ]
                ];
-               yield 'pre-MCR' => [
-                       [
-                               'wgContentHandlerUseDB' => true,
-                               'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                       ],
-                       [],
-                       [
-                               'tables' => [
-                                       'revision',
-                                       'temp_rev_comment' => 'revision_comment_temp',
-                                       'comment_rev_comment' => 'comment',
-                                       'temp_rev_user' => 'revision_actor_temp',
-                                       'actor_rev_user' => 'actor',
-                               ],
-                               'fields' => array_merge(
-                                       $this->getRevisionQueryFields( true ),
-                                       $this->getContentHandlerQueryFields( 'rev' ),
-                                       $this->getNewActorQueryFields( 'rev', true ),
-                                       $this->getNewCommentQueryFields( 'rev' )
-                               ),
-                               'joins' => [
-                                       'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
-                                       'comment_rev_comment'
-                                               => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
-                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
-                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
-                               ],
-                       ]
-               ];
-               yield 'pre-MCR, page, user' => [
-                       [
-                               'wgContentHandlerUseDB' => true,
-                               'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                       ],
-                       [ 'page', 'user' ],
-                       [
-                               'tables' => [
-                                       'revision', 'page', 'user',
-                                       'temp_rev_comment' => 'revision_comment_temp',
-                                       'comment_rev_comment' => 'comment',
-                                       'temp_rev_user' => 'revision_actor_temp',
-                                       'actor_rev_user' => 'actor',
-                               ],
-                               'fields' => array_merge(
-                                       $this->getRevisionQueryFields( true ),
-                                       $this->getContentHandlerQueryFields( 'rev' ),
-                                       $this->getPageQueryFields(),
-                                       $this->getUserQueryFields(),
-                                       $this->getNewActorQueryFields( 'rev', true ),
-                                       $this->getNewCommentQueryFields( 'rev' )
-                               ),
-                               'joins' => [
-                                       'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
-                                       'user' => [ 'LEFT JOIN', [
-                                               'actor_rev_user.actor_user != 0',
-                                               'user_id = actor_rev_user.actor_user',
-                                       ] ],
-                                       'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
-                                       'comment_rev_comment'
-                                               => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
-                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
-                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
-                               ],
-                       ]
-               ];
-               yield 'pre-MCR, no model' => [
-                       [
-                               'wgContentHandlerUseDB' => false,
-                               'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                       ],
-                       [],
-                       [
-                               'tables' => [
-                                       'revision',
-                                       'temp_rev_comment' => 'revision_comment_temp',
-                                       'comment_rev_comment' => 'comment',
-                                       'temp_rev_user' => 'revision_actor_temp',
-                                       'actor_rev_user' => 'actor',
-                               ],
-                               'fields' => array_merge(
-                                       $this->getRevisionQueryFields( true ),
-                                       $this->getNewActorQueryFields( 'rev', true ),
-                                       $this->getNewCommentQueryFields( 'rev' )
-                               ),
-                               'joins' => [
-                                       'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
-                                       'comment_rev_comment'
-                                               => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
-                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
-                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
-                               ],
-                       ],
-               ];
-               yield 'pre-MCR, no model, page' => [
-                       [
-                               'wgContentHandlerUseDB' => false,
-                               'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                       ],
-                       [ 'page' ],
-                       [
-                               'tables' => [
-                                       'revision', 'page',
-                                       'temp_rev_comment' => 'revision_comment_temp',
-                                       'comment_rev_comment' => 'comment',
-                                       'temp_rev_user' => 'revision_actor_temp',
-                                       'actor_rev_user' => 'actor',
-                               ],
-                               'fields' => array_merge(
-                                       $this->getRevisionQueryFields( true ),
-                                       $this->getPageQueryFields(),
-                                       $this->getNewActorQueryFields( 'rev', true ),
-                                       $this->getNewCommentQueryFields( 'rev' )
-                               ),
-                               'joins' => [
-                                       'page' => [ 'JOIN', [ 'page_id = rev_page' ], ],
-                                       'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
-                                       'comment_rev_comment'
-                                               => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
-                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
-                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
-                               ],
-                       ],
-               ];
-               yield 'pre-MCR, no model, user' => [
-                       [
-                               'wgContentHandlerUseDB' => false,
-                               'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                       ],
-                       [ 'user' ],
-                       [
-                               'tables' => [
-                                       'revision', 'user',
-                                       'temp_rev_comment' => 'revision_comment_temp',
-                                       'comment_rev_comment' => 'comment',
-                                       'temp_rev_user' => 'revision_actor_temp',
-                                       'actor_rev_user' => 'actor',
-                               ],
-                               'fields' => array_merge(
-                                       $this->getRevisionQueryFields( true ),
-                                       $this->getUserQueryFields(),
-                                       $this->getNewActorQueryFields( 'rev', true ),
-                                       $this->getNewCommentQueryFields( 'rev' )
-                               ),
-                               'joins' => [
-                                       'user' => [ 'LEFT JOIN', [
-                                               'actor_rev_user.actor_user != 0',
-                                               'user_id = actor_rev_user.actor_user',
-                                       ] ],
-                                       'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
-                                       'comment_rev_comment'
-                                               => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
-                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
-                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
-                               ],
-                       ],
-               ];
-               yield 'pre-MCR, no model, text' => [
-                       [
-                               'wgContentHandlerUseDB' => false,
-                               'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                       ],
-                       [ 'text' ],
-                       [
-                               'tables' => [
-                                       'revision', 'text',
-                                       'temp_rev_comment' => 'revision_comment_temp',
-                                       'comment_rev_comment' => 'comment',
-                                       'temp_rev_user' => 'revision_actor_temp',
-                                       'actor_rev_user' => 'actor',
-                               ],
-                               'fields' => array_merge(
-                                       $this->getRevisionQueryFields( true ),
-                                       $this->getTextQueryFields(),
-                                       $this->getNewActorQueryFields( 'rev', true ),
-                                       $this->getNewCommentQueryFields( 'rev' )
-                               ),
-                               'joins' => [
-                                       'text' => [ 'JOIN', [ 'rev_text_id=old_id' ] ],
-                                       'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
-                                       'comment_rev_comment'
-                                               => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
-                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
-                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
-                               ],
-                       ],
-               ];
-               yield 'pre-MCR, no model, text, page, user' => [
-                       [
-                               'wgContentHandlerUseDB' => false,
-                               'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                       ],
-                       [ 'text', 'page', 'user' ],
-                       [
-                               'tables' => [
-                                       'revision', 'page', 'user', 'text',
-                                       'temp_rev_comment' => 'revision_comment_temp',
-                                       'comment_rev_comment' => 'comment',
-                                       'temp_rev_user' => 'revision_actor_temp',
-                                       'actor_rev_user' => 'actor',
-                               ],
-                               'fields' => array_merge(
-                                       $this->getRevisionQueryFields( true ),
-                                       $this->getPageQueryFields(),
-                                       $this->getUserQueryFields(),
-                                       $this->getTextQueryFields(),
-                                       $this->getNewActorQueryFields( 'rev', true ),
-                                       $this->getNewCommentQueryFields( 'rev' )
-                               ),
-                               'joins' => [
-                                       'page' => [
-                                               'JOIN',
-                                               [ 'page_id = rev_page' ],
-                                       ],
-                                       'user' => [
-                                               'LEFT JOIN',
-                                               [
-                                                       'actor_rev_user.actor_user != 0',
-                                                       'user_id = actor_rev_user.actor_user',
-                                               ],
-                                       ],
-                                       'text' => [
-                                               'JOIN',
-                                               [ 'rev_text_id=old_id' ],
-                                       ],
-                                       'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
-                                       'comment_rev_comment'
-                                               => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
-                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
-                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
-                               ],
-                       ],
-               ];
        }
 
        public function provideSlotsQueryInfo() {
@@ -805,54 +550,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                'joins' => [],
                        ]
                ];
-               yield 'pre-MCR' => [
-                       [
-                               'wgMultiContentRevisionSchemaMigrationStage'
-                                       => SCHEMA_COMPAT_OLD,
-                       ],
-                       [],
-                       [
-                               'tables' => [
-                                       'revision',
-                               ],
-                               'fields' => array_merge(
-                                       [
-                                               'slot_revision_id' => 'rev_id',
-                                               'slot_content_id' => 'NULL',
-                                               'slot_origin' => 'rev_id',
-                                               'role_name' => $db->addQuotes( SlotRecord::MAIN ),
-                                       ]
-                               ),
-                               'joins' => [],
-                       ]
-               ];
-               yield 'pre-MCR, content' => [
-                       [
-                               'wgMultiContentRevisionSchemaMigrationStage'
-                                       => SCHEMA_COMPAT_OLD,
-                       ],
-                       [ 'content' ],
-                       [
-                               'tables' => [
-                                       'revision',
-                               ],
-                               'fields' => array_merge(
-                                       [
-                                               'slot_revision_id' => 'rev_id',
-                                               'slot_content_id' => 'NULL',
-                                               'slot_origin' => 'rev_id',
-                                               'role_name' => $db->addQuotes( SlotRecord::MAIN ),
-                                               'content_size' => 'rev_len',
-                                               'content_sha1' => 'rev_sha1',
-                                               'content_address' =>
-                                                       $db->buildConcat( [ $db->addQuotes( 'tt:' ), 'rev_text_id' ] ),
-                                               'rev_text_id' => 'rev_text_id',
-                                               'model_name' => 'rev_content_model',
-                                       ]
-                               ),
-                               'joins' => [],
-                       ]
-               ];
        }
 
        /**
index 4040ffc..6bf219d 100644 (file)
@@ -2076,8 +2076,10 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                /** @var Revision $rev1 */
                $rev1 = $editStatus->getValue()['revision'];
 
-               $this->setExpectedException( InvalidArgumentException::class );
-               MediaWikiServices::getInstance()->getRevisionStore()
+               $status = MediaWikiServices::getInstance()->getRevisionStore()
                        ->newRevisionsFromBatch( [ $this->revisionToRow( $rev1 ), $this->revisionToRow( $rev1 ) ] );
+
+               $this->assertFalse( $status->isGood() );
+               $this->assertTrue( $status->hasMessage( 'internalerror' ) );
        }
 }
index 99332d2..d42ab16 100644 (file)
@@ -33,7 +33,7 @@ class RevisionStoreFactoryTest extends \MediaWikiIntegrationTestCase {
                        $this->getHashWANObjectCache(),
                        $this->getMockCommentStore(),
                        ActorMigration::newMigration(),
-                       MIGRATION_OLD,
+                       MIGRATION_NEW,
                        new NullLogger(),
                        true
                );
@@ -44,8 +44,6 @@ class RevisionStoreFactoryTest extends \MediaWikiIntegrationTestCase {
                yield [ true ];
                yield [ false ];
                yield [ 'somewiki' ];
-               yield [ 'somewiki', MIGRATION_OLD , false ];
-               yield [ 'somewiki', MIGRATION_NEW , true ];
        }
 
        /**
@@ -54,7 +52,7 @@ class RevisionStoreFactoryTest extends \MediaWikiIntegrationTestCase {
         */
        public function testGetRevisionStore(
                $dbDomain,
-               $mcrMigrationStage = MIGRATION_OLD,
+               $mcrMigrationStage = MIGRATION_NEW,
                $contentHandlerUseDb = true
        ) {
                $lbFactory = $this->getMockLoadBalancerFactory();
index 83872e3..e74eabb 100644 (file)
@@ -3,37 +3,26 @@
 namespace MediaWiki\Tests\Revision;
 
 use CommentStore;
-use HashBagOStuff;
 use InvalidArgumentException;
-use Language;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Revision\RevisionAccessException;
 use MediaWiki\Revision\RevisionStore;
 use MediaWiki\Revision\SlotRoleRegistry;
-use MediaWiki\Revision\SlotRecord;
 use MediaWiki\Storage\SqlBlobStore;
 use Wikimedia\Rdbms\ILoadBalancer;
 use Wikimedia\Rdbms\MaintainableDBConnRef;
 use MediaWikiTestCase;
 use MWException;
-use Title;
 use WANObjectCache;
 use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\LoadBalancer;
 use Wikimedia\TestingAccessWrapper;
-use WikitextContent;
 
 /**
  * Tests RevisionStore
  */
 class RevisionStoreTest extends MediaWikiTestCase {
 
-       private function useTextId() {
-               global $wgMultiContentRevisionSchemaMigrationStage;
-
-               return (bool)( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_READ_OLD );
-       }
-
        /**
         * @param LoadBalancer $loadBalancer
         * @param SqlBlobStore $blobStore
@@ -120,9 +109,7 @@ class RevisionStoreTest extends MediaWikiTestCase {
 
        public function provideSetContentHandlerUseDB() {
                return [
-                       // ContentHandlerUseDB can be true of false pre migration.
-                       [ false, SCHEMA_COMPAT_OLD, false ],
-                       [ true, SCHEMA_COMPAT_OLD, false ],
+                       // ContentHandlerUseDB can be true or false pre migration.
                        // During and after migration it can not be false...
                        [ false, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, true ],
                        [ false, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, true ],
@@ -436,134 +423,8 @@ class RevisionStoreTest extends MediaWikiTestCase {
                $store->getTitle( 1, 2, RevisionStore::READ_NORMAL );
        }
 
-       public function provideNewRevisionFromRow_legacyEncoding_applied() {
-               yield 'windows-1252, old_flags is empty' => [
-                       'windows-1252',
-                       'en',
-                       [
-                               'old_flags' => '',
-                               'old_text' => "S\xF6me Content",
-                       ],
-                       'Söme Content'
-               ];
-
-               yield 'windows-1252, old_flags is null' => [
-                       'windows-1252',
-                       'en',
-                       [
-                               'old_flags' => null,
-                               'old_text' => "S\xF6me Content",
-                       ],
-                       'Söme Content'
-               ];
-       }
-
-       /**
-        * @dataProvider provideNewRevisionFromRow_legacyEncoding_applied
-        *
-        * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRow
-        */
-       public function testNewRevisionFromRow_legacyEncoding_applied( $encoding, $locale, $row, $text ) {
-               if ( !$this->useTextId() ) {
-                       $this->markTestSkipped( 'No longer applicable with MCR schema' );
-               }
-
-               $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
-               $services = MediaWikiServices::getInstance();
-               $lb = $services->getDBLoadBalancer();
-               $access = $services->getExternalStoreAccess();
-
-               $blobStore = new SqlBlobStore( $lb, $access, $cache );
-
-               $blobStore->setLegacyEncoding( $encoding, Language::factory( $locale ) );
-
-               $store = $this->getRevisionStore( $lb, $blobStore, $cache );
-
-               $record = $store->newRevisionFromRow(
-                       $this->makeRow( $row ),
-                       0,
-                       Title::newFromText( __METHOD__ . '-UTPage' )
-               );
-
-               $this->assertSame( $text, $record->getContent( SlotRecord::MAIN )->serialize() );
-       }
-
-       /**
-        * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRow
-        */
-       public function testNewRevisionFromRow_legacyEncoding_ignored() {
-               if ( !$this->useTextId() ) {
-                       $this->markTestSkipped( 'No longer applicable with MCR schema' );
-               }
-
-               $row = [
-                       'old_flags' => 'utf-8',
-                       'old_text' => 'Söme Content',
-               ];
-
-               $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
-               $services = MediaWikiServices::getInstance();
-               $lb = $services->getDBLoadBalancer();
-               $access = $services->getExternalStoreAccess();
-
-               $blobStore = new SqlBlobStore( $lb, $access, $cache );
-               $blobStore->setLegacyEncoding( 'windows-1252', Language::factory( 'en' ) );
-
-               $store = $this->getRevisionStore( $lb, $blobStore, $cache );
-
-               $record = $store->newRevisionFromRow(
-                       $this->makeRow( $row ),
-                       0,
-                       Title::newFromText( __METHOD__ . '-UTPage' )
-               );
-               $this->assertSame( 'Söme Content', $record->getContent( SlotRecord::MAIN )->serialize() );
-       }
-
-       private function makeRow( array $array ) {
-               $row = $array + [
-                               'rev_id' => 7,
-                               'rev_page' => 5,
-                               'rev_timestamp' => '20110101000000',
-                               'rev_user_text' => 'Tester',
-                               'rev_user' => 17,
-                               'rev_minor_edit' => 0,
-                               'rev_deleted' => 0,
-                               'rev_len' => 100,
-                               'rev_parent_id' => 0,
-                               'rev_sha1' => 'deadbeef',
-                               'rev_comment_text' => 'Testing',
-                               'rev_comment_data' => '{}',
-                               'rev_comment_cid' => 111,
-                               'page_namespace' => 0,
-                               'page_title' => 'TEST',
-                               'page_id' => 5,
-                               'page_latest' => 7,
-                               'page_is_redirect' => 0,
-                               'page_len' => 100,
-                               'user_name' => 'Tester',
-                       ];
-
-               if ( $this->useTextId() ) {
-                       $row += [
-                               'rev_content_format' => CONTENT_FORMAT_TEXT,
-                               'rev_content_model' => CONTENT_MODEL_TEXT,
-                               'rev_text_id' => 11,
-                               'old_id' => 11,
-                               'old_text' => 'Hello World',
-                               'old_flags' => 'utf-8',
-                       ];
-               } elseif ( !isset( $row['content'] ) && isset( $array['old_text'] ) ) {
-                       $row['content'] = [
-                               'main' => new WikitextContent( $array['old_text'] ),
-                       ];
-               }
-
-               return (object)$row;
-       }
-
        public function provideMigrationConstruction() {
                return [
-                       [ SCHEMA_COMPAT_OLD, false ],
                        [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, false ],
                        [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, false ],
                        [ SCHEMA_COMPAT_NEW, false ],
diff --git a/tests/phpunit/includes/RevisionNoContentModelDbTest.php b/tests/phpunit/includes/RevisionNoContentModelDbTest.php
deleted file mode 100644 (file)
index f19bc52..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-use MediaWiki\Tests\Revision\PreMcrSchemaOverride;
-
-/**
- * Tests Revision against the pre-MCR, pre ContentHandler DB schema.
- *
- * @covers Revision
- *
- * @group Revision
- * @group Storage
- * @group ContentHandler
- * @group Database
- * @group medium
- */
-class RevisionNoContentModelDbTest extends RevisionDbTestBase {
-
-       use PreMcrSchemaOverride;
-
-       protected function getContentHandlerUseDB() {
-               return false;
-       }
-
-       public function provideGetTextId() {
-               yield [ [], null ];
-
-               $row = (object)[
-                       'rev_id' => 7,
-                       'rev_page' => 1, // should match actual page id
-                       'rev_text_id' => 789,
-                       'rev_timestamp' => '20180101000000',
-                       'rev_len' => 7,
-                       'rev_minor_edit' => 0,
-                       'rev_deleted' => 0,
-                       'rev_parent_id' => 0,
-                       'rev_sha1' => 'deadbeef',
-                       'rev_comment' => 'some comment',
-                       'rev_comment_text' => 'some comment',
-                       'rev_comment_data' => '{}',
-                       'rev_user' => 17,
-                       'rev_user_text' => 'some user',
-               ];
-
-               yield [ $row, 789 ];
-       }
-
-       public function provideGetRevisionText() {
-               yield [
-                       [ 'text' ]
-               ];
-       }
-
-}
diff --git a/tests/phpunit/includes/RevisionPreMcrDbTest.php b/tests/phpunit/includes/RevisionPreMcrDbTest.php
deleted file mode 100644 (file)
index 444c150..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-use MediaWiki\Tests\Revision\PreMcrSchemaOverride;
-
-/**
- * Tests Revision against the pre-MCR DB schema.
- *
- * @covers Revision
- *
- * @group Revision
- * @group Storage
- * @group ContentHandler
- * @group Database
- * @group medium
- */
-class RevisionPreMcrDbTest extends RevisionDbTestBase {
-
-       use PreMcrSchemaOverride;
-
-       protected function getContentHandlerUseDB() {
-               return true;
-       }
-
-       public function provideGetTextId() {
-               yield [ [], null ];
-
-               $row = (object)[
-                       'rev_id' => 7,
-                       'rev_page' => 1, // should match actual page id
-                       'rev_text_id' => 789,
-                       'rev_timestamp' => '20180101000000',
-                       'rev_len' => 7,
-                       'rev_minor_edit' => 0,
-                       'rev_deleted' => 0,
-                       'rev_parent_id' => 0,
-                       'rev_sha1' => 'deadbeef',
-                       'rev_comment' => 'some comment',
-                       'rev_comment_text' => 'some comment',
-                       'rev_comment_data' => '{}',
-                       'rev_user' => 17,
-                       'rev_user_text' => 'some user',
-               ];
-
-               yield [ $row, 789 ];
-       }
-
-       public function provideGetRevisionText() {
-               yield [
-                       [ 'text' ]
-               ];
-       }
-}
index 3a3f5f1..1606275 100644 (file)
@@ -2,6 +2,7 @@
 
 use Wikimedia\Rdbms\DBQueryError;
 use Wikimedia\TestingAccessWrapper;
+use Wikimedia\Timestamp\ConvertibleTimestamp;
 
 /**
  * @group API
@@ -169,6 +170,10 @@ class ApiMainTest extends ApiTestCase {
        }
 
        public function testAddRequestedFieldsCurTimestamp() {
+               // Fake timestamp for better testability, CI can sometimes take
+               // unreasonably long to run the simple test request here.
+               $reset = ConvertibleTimestamp::setFakeTime( '20190102030405' );
+
                $req = new FauxRequest( [
                        'action' => 'query',
                        'meta' => 'siteinfo',
@@ -177,7 +182,7 @@ class ApiMainTest extends ApiTestCase {
                $api = new ApiMain( $req );
                $api->execute();
                $timestamp = $api->getResult()->getResultData()['curtimestamp'];
-               $this->assertLessThanOrEqual( 1, abs( strtotime( $timestamp ) - time() ) );
+               $this->assertSame( '2019-01-02T03:04:05Z', $timestamp );
        }
 
        public function testAddRequestedFieldsResponseLangInfo() {
index dce1a5f..7f5ee0c 100644 (file)
@@ -665,17 +665,13 @@ class ApiQuerySiteinfoTest extends ApiTestCase {
        }
 
        public function testContinuation() {
-               // We make lots and lots of URL protocols that are each 100 bytes
+               // Use $wgUrlProtocols to forge the size of the API query
                global $wgAPIMaxResultSize, $wgUrlProtocols;
 
-               $this->setMwGlobals( 'wgUrlProtocols', [] );
+               $protocol = 'foo://';
 
-               // Just under the limit
-               $chunks = $wgAPIMaxResultSize / 100 - 1;
-
-               for ( $i = 0; $i < $chunks; $i++ ) {
-                       $wgUrlProtocols[] = substr( str_repeat( "$i ", 50 ), 0, 100 );
-               }
+               $this->setMwGlobals( 'wgUrlProtocols', [ $protocol ] );
+               $this->setMwGlobals( 'wgAPIMaxResultSize', strlen( $protocol ) );
 
                $res = $this->doApiRequest( [
                        'action' => 'query',
index 31929d3..4e062ed 100644 (file)
@@ -85,9 +85,9 @@ class CategoryMembershipChangeTest extends MediaWikiLangTestCase {
                $this->assertEquals( self::$pageName, self::$lastNotifyArgs[4]->getPrefixedText() );
                $this->assertSame( 0, self::$lastNotifyArgs[5] );
                $this->assertSame( 0, self::$lastNotifyArgs[6] );
-               $this->assertEquals( null, self::$lastNotifyArgs[7] );
+               $this->assertNull( self::$lastNotifyArgs[7] );
                $this->assertEquals( 1, self::$lastNotifyArgs[8] );
-               $this->assertEquals( null, self::$lastNotifyArgs[9] );
+               $this->assertSame( '', self::$lastNotifyArgs[9] );
                $this->assertSame( 0, self::$lastNotifyArgs[10] );
        }
 
@@ -105,9 +105,9 @@ class CategoryMembershipChangeTest extends MediaWikiLangTestCase {
                $this->assertEquals( self::$pageName, self::$lastNotifyArgs[4]->getPrefixedText() );
                $this->assertSame( 0, self::$lastNotifyArgs[5] );
                $this->assertSame( 0, self::$lastNotifyArgs[6] );
-               $this->assertEquals( null, self::$lastNotifyArgs[7] );
+               $this->assertNull( self::$lastNotifyArgs[7] );
                $this->assertEquals( 1, self::$lastNotifyArgs[8] );
-               $this->assertEquals( null, self::$lastNotifyArgs[9] );
+               $this->assertSame( '', self::$lastNotifyArgs[9] );
                $this->assertSame( 0, self::$lastNotifyArgs[10] );
        }
 
@@ -126,7 +126,7 @@ class CategoryMembershipChangeTest extends MediaWikiLangTestCase {
                $this->assertEquals( self::$pageName, self::$lastNotifyArgs[4]->getPrefixedText() );
                $this->assertEquals( self::$pageRev->getParentId(), self::$lastNotifyArgs[5] );
                $this->assertEquals( $revision->getId(), self::$lastNotifyArgs[6] );
-               $this->assertEquals( null, self::$lastNotifyArgs[7] );
+               $this->assertNull( self::$lastNotifyArgs[7] );
                $this->assertSame( 0, self::$lastNotifyArgs[8] );
                $this->assertEquals( '127.0.0.1', self::$lastNotifyArgs[9] );
                $this->assertSame( 0, self::$lastNotifyArgs[10] );
@@ -147,7 +147,7 @@ class CategoryMembershipChangeTest extends MediaWikiLangTestCase {
                $this->assertEquals( self::$pageName, self::$lastNotifyArgs[4]->getPrefixedText() );
                $this->assertEquals( self::$pageRev->getParentId(), self::$lastNotifyArgs[5] );
                $this->assertEquals( $revision->getId(), self::$lastNotifyArgs[6] );
-               $this->assertEquals( null, self::$lastNotifyArgs[7] );
+               $this->assertNull( self::$lastNotifyArgs[7] );
                $this->assertSame( 0, self::$lastNotifyArgs[8] );
                $this->assertEquals( '127.0.0.1', self::$lastNotifyArgs[9] );
                $this->assertSame( 0, self::$lastNotifyArgs[10] );
index cd7cc10..fc1c42d 100644 (file)
@@ -390,7 +390,7 @@ just a test"
                $this->assertEquals( 'hello world.', $wikitext,
                        'Wikitext passed to hook was not as expected'
                );
-               $this->assertEquals( null, $redirectTarget, 'Redirect seen in hook was not null' );
+               $this->assertNull( $redirectTarget, 'Redirect seen in hook was not null' );
                $this->assertEquals( $title, $options->getRedirectTarget(),
                        'ParserOptions\' redirectTarget was changed'
                );
@@ -417,8 +417,7 @@ just a test"
                        $redirectTarget->getFullText(),
                        'Redirect seen in hook was not the expected title'
                );
-               $this->assertEquals(
-                       null,
+               $this->assertNull(
                        $options->getRedirectTarget(),
                        'ParserOptions\' redirectTarget was changed'
                );
index f00499f..c018744 100644 (file)
@@ -412,7 +412,7 @@ class LBFactoryTest extends MediaWikiTestCase {
                $cpIndex = null;
                $cp->shutdown( null, 'sync', $cpIndex );
 
-               $this->assertEquals( null, $cpIndex, "CP write index retained" );
+               $this->assertNull( $cpIndex, "CP write index retained" );
 
                $this->assertEquals( '45e93a9c215c031d38b7c42d8e4700ca', $cp->getClientId() );
        }
index 981b4ad..a542936 100644 (file)
@@ -434,7 +434,7 @@ class LoadBalancerTest extends MediaWikiTestCase {
                $lb = $this->newSingleServerLocalLoadBalancer();
 
                $i = $lb->getWriterIndex();
-               $this->assertEquals( null, $lb->getAnyOpenConnection( $i ) );
+               $this->assertFalse( $lb->getAnyOpenConnection( $i ) );
 
                $conn1 = $lb->getConnection( $i );
                $this->assertNotEquals( null, $conn1 );
@@ -446,7 +446,7 @@ class LoadBalancerTest extends MediaWikiTestCase {
                $this->assertFalse( $conn2->getFlag( DBO_TRX ) );
 
                if ( $lb->getServerAttributes( $i )[Database::ATTR_DB_LEVEL_LOCKING] ) {
-                       $this->assertEquals( null,
+                       $this->assertFalse(
                                $lb->getAnyOpenConnection( $i, $lb::CONN_TRX_AUTOCOMMIT ) );
                        $this->assertEquals( $conn1,
                                $lb->getConnection(
index 7bc7918..afa8283 100644 (file)
@@ -1567,11 +1567,11 @@ class FileBackendTest extends MediaWikiTestCase {
 
                $tmpFile = $this->backend->getLocalCopy( [
                        'src' => "$base/unittest-cont1/not-there" ] );
-               $this->assertEquals( null, $tmpFile, "Local copy of not existing file is null ($backendName)." );
+               $this->assertNull( $tmpFile, "Local copy of not existing file is null ($backendName)." );
 
                $tmpFile = $this->backend->getLocalReference( [
                        'src' => "$base/unittest-cont1/not-there" ] );
-               $this->assertEquals( null, $tmpFile, "Local ref of not existing file is null ($backendName)." );
+               $this->assertNull( $tmpFile, "Local ref of not existing file is null ($backendName)." );
        }
 
        /**
@@ -2484,7 +2484,7 @@ class FileBackendTest extends MediaWikiTestCase {
                        "Scoped locking of files succeeded with OK status ($backendName)." );
 
                ScopedLock::release( $sl );
-               $this->assertEquals( null, $sl,
+               $this->assertNull( $sl,
                        "Scoped unlocking of files succeeded ($backendName)." );
                $this->assertEquals( [], $status->getErrors(),
                        "Scoped unlocking of files succeeded ($backendName)." );
index 79d5788..66b04ad 100644 (file)
@@ -130,14 +130,15 @@ class StringUtilsTest extends PHPUnit\Framework\TestCase {
         * @param strin $input
         * @param bool $expected
         * @dataProvider provideRegexps
-        * @covers StringUtils::isValidRegex
+        * @covers StringUtils::isValidPCRERegex
         */
-       public function testIsValidRegex( $input, $expected ) {
-               $this->assertSame( $expected, StringUtils::isValidRegex( $input ) );
+       public function testIsValidPCRERegex( $input, $expected ) {
+               $this->assertSame( $expected, StringUtils::isValidPCRERegex( $input ) );
        }
 
        /**
-        * Data provider for testValidRegex
+        * Data provider for testIsValidPCRERegex
+        * @return array
         */
        public static function provideRegexps() {
                return [
index 7c4c9bf..0c084e0 100644 (file)
@@ -128,6 +128,32 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $this->assertFalse( $this->cache->get( $key ), "Stale set() value ignored" );
        }
 
+       /**
+        * @covers WANObjectCache::getWithSetCallback
+        */
+       public function testProcessCacheTTL() {
+               $cache = $this->cache;
+               $mockWallClock = 1549343530.2053;
+               $cache->setMockTime( $mockWallClock );
+
+               $key = "mykey-" . wfRandomString();
+
+               $hits = 0;
+               $callback = function ( $oldValue, &$ttl, &$setOpts ) use ( &$hits ) {
+                       ++$hits;
+                       return 42;
+               };
+
+               $cache->getWithSetCallback( $key, 100, $callback, [ 'pcTTL' => 5 ] );
+               $cache->delete( $key, $cache::HOLDOFF_NONE ); // clear persistent cache
+               $cache->getWithSetCallback( $key, 100, $callback, [ 'pcTTL' => 5 ] );
+               $this->assertEquals( 1, $hits, "Value process cached" );
+
+               $mockWallClock += 6;
+               $cache->getWithSetCallback( $key, 100, $callback, [ 'pcTTL' => 5 ] );
+               $this->assertEquals( 2, $hits, "Value expired in process cache" );
+       }
+
        /**
         * @covers WANObjectCache::getWithSetCallback
         */
@@ -381,8 +407,8 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $v = $cache->getWithSetCallback(
                        $key, 30, $checkFunc, [ 'staleTTL' => 50 ] + $extOpts );
                $this->assertEquals( 'xxx1', $v, "Value returned" );
-               $this->assertEquals( false, $oldValReceived, "Callback got no stale value" );
-               $this->assertEquals( null, $oldAsOfReceived, "Callback got no stale value" );
+               $this->assertFalse( $oldValReceived, "Callback got no stale value" );
+               $this->assertNull( $oldAsOfReceived, "Callback got no stale value" );
 
                $mockWallClock += 40;
                $v = $cache->getWithSetCallback(
@@ -397,8 +423,8 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                        $key, 30, $checkFunc, [ 'staleTTL' => 50 ] + $extOpts );
                $this->assertEquals( 'xxx3', $v, "Value still returned after expired" );
                $this->assertEquals( 3, $wasSet, "Value recalculated while expired" );
-               $this->assertEquals( false, $oldValReceived, "Callback got no stale value" );
-               $this->assertEquals( null, $oldAsOfReceived, "Callback got no stale value" );
+               $this->assertFalse( $oldValReceived, "Callback got no stale value" );
+               $this->assertNull( $oldAsOfReceived, "Callback got no stale value" );
 
                $mockWallClock = ( $priorTime - $cache::HOLDOFF_TTL - 1 );
                $wasSet = 0;
@@ -414,8 +440,8 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                );
                $this->assertEquals( 'xxx1', $v, "Value returned" );
                $this->assertEquals( 1, $wasSet, "Value computed" );
-               $this->assertEquals( false, $oldValReceived, "Callback got no stale value" );
-               $this->assertEquals( null, $oldAsOfReceived, "Callback got no stale value" );
+               $this->assertFalse( $oldValReceived, "Callback got no stale value" );
+               $this->assertNull( $oldAsOfReceived, "Callback got no stale value" );
 
                $mockWallClock += $cache::TTL_HOUR; // some time passes
                $v = $cache->getWithSetCallback(
@@ -1473,7 +1499,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $this->assertEquals( $valueV2, $v, "Value returned" );
                $this->assertEquals( 1, $wasSet, "Value regenerated" );
                $this->assertEquals( false, $priorValue, "Old value not given due to old format" );
-               $this->assertEquals( null, $priorAsOf, "Old value not given due to old format" );
+               $this->assertNull( $priorAsOf, "Old value not given due to old format" );
 
                $wasSet = 0;
                $v = $cache->getWithSetCallback( $key, 30, $funcV2, $verOpts + $extOpts );
index 4c92545..6fff68f 100644 (file)
@@ -316,8 +316,8 @@ class DatabaseMysqlBaseTest extends PHPUnit\Framework\TestCase {
         * @dataProvider provideCommonDomainGTIDs
         * @covers Wikimedia\Rdbms\MySQLMasterPos
         */
-       public function testCommonGtidDomains( MySQLMasterPos $pos, MySQLMasterPos $ref, $gtids ) {
-               $this->assertEquals( $gtids, MySQLMasterPos::getCommonDomainGTIDs( $pos, $ref ) );
+       public function testGetRelevantActiveGTIDs( MySQLMasterPos $pos, MySQLMasterPos $ref, $gtids ) {
+               $this->assertEquals( $gtids, MySQLMasterPos::getRelevantActiveGTIDs( $pos, $ref ) );
        }
 
        public static function provideCommonDomainGTIDs() {
@@ -327,6 +327,12 @@ class DatabaseMysqlBaseTest extends PHPUnit\Framework\TestCase {
                                new MySQLMasterPos( '255-11-1000', 1 ),
                                [ '255-13-99' ]
                        ],
+                       [
+                               ( new MySQLMasterPos( '255-13-99,256-12-50,257-14-50', 1 ) )
+                                       ->setActiveDomain( 257 ),
+                               new MySQLMasterPos( '255-11-1000,257-14-30', 1 ),
+                               [ '257-14-50' ]
+                       ],
                        [
                                new MySQLMasterPos(
                                        '2E11FA47-71CA-11E1-9E33-C80AA9429562:1-5,' .
index 1c076ca..7feaa93 100644 (file)
@@ -428,4 +428,46 @@ class ProtectLogFormatterTest extends LogFormatterTestCase {
        public function testMoveProtLogDatabaseRows( $row, $extra ) {
                $this->doTestLogFormatter( $row, $extra );
        }
+
+       public function provideGetActionLinks() {
+               yield [
+                       [ 'protect' ],
+                       true
+               ];
+               yield [
+                       [],
+                       false
+               ];
+       }
+
+       /**
+        * @param string[] $permissions
+        * @param bool $shouldMatch
+        * @dataProvider provideGetActionLinks
+        * @covers ProtectLogFormatter::getActionLinks
+        */
+       public function testGetActionLinks( array $permissions, $shouldMatch ) {
+               RequestContext::resetMain();
+               $user = $this->getTestUser()->getUser();
+               $this->overrideUserPermissions( $user, $permissions );
+               $row = $this->expandDatabaseRow( [
+                       'type' => 'protect',
+                       'action' => 'unprotect',
+                       'comment' => 'unprotect comment',
+                       'namespace' => NS_MAIN,
+                       'title' => 'ProtectPage',
+                       'params' => [],
+               ], false );
+               $context = new RequestContext();
+               $context->setUser( $user );
+               $formatter = LogFormatter::newFromRow( $row );
+               $formatter->setContext( $context );
+               if ( $shouldMatch ) {
+                       $this->assertStringMatchesFormat(
+                               '%Aaction=protect%A', $formatter->getActionLinks() );
+               } else {
+                       $this->assertStringNotMatchesFormat(
+                               '%Aaction=protect%A', $formatter->getActionLinks() );
+               }
+       }
 }
diff --git a/tests/phpunit/includes/page/PageArchivePreMcrTest.php b/tests/phpunit/includes/page/PageArchivePreMcrTest.php
deleted file mode 100644 (file)
index 4c95579..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-use MediaWiki\MediaWikiServices;
-use MediaWiki\Revision\SlotRecord;
-use MediaWiki\Storage\SqlBlobStore;
-use MediaWiki\Tests\Revision\PreMcrSchemaOverride;
-
-/**
- * Test class for page archiving, using the pre-MCR schema.
- *
- * @group ContentHandler
- * @group Database
- * ^--- important, causes temporary tables to be used instead of the real database
- *
- * @group medium
- * ^--- important, causes tests not to fail with timeout
- */
-class PageArchivePreMcrTest extends PageArchiveTestBase {
-
-       use PreMcrSchemaOverride;
-
-       protected function getExpectedArchiveRows() {
-               /** @var SqlBlobStore $blobStore */
-               $blobStore = MediaWikiServices::getInstance()->getBlobStore();
-
-               return [
-                       [
-                               'ar_minor_edit' => '0',
-                               'ar_user' => null,
-                               'ar_user_text' => $this->ipEditor,
-                               'ar_actor' => (string)User::newFromName( $this->ipEditor, false )->getActorId( $this->db ),
-                               'ar_len' => '11',
-                               'ar_deleted' => '0',
-                               'ar_rev_id' => strval( $this->ipRev->getId() ),
-                               'ar_timestamp' => $this->db->timestamp( $this->ipRev->getTimestamp() ),
-                               'ar_sha1' => '0qdrpxl537ivfnx4gcpnzz0285yxryy',
-                               'ar_page_id' => strval( $this->ipRev->getPageId() ),
-                               'ar_comment_text' => 'just a test',
-                               'ar_comment_data' => null,
-                               'ar_comment_cid' => '2',
-                               'ar_content_format' => null,
-                               'ar_content_model' => null,
-                               'ts_tags' => null,
-                               'ar_id' => '2',
-                               'ar_namespace' => '0',
-                               'ar_title' => 'PageArchiveTest_thePage',
-                               'ar_text_id' => (string)$blobStore->getTextIdFromAddress(
-                                       $this->ipRev->getSlot( SlotRecord::MAIN )->getAddress()
-                               ),
-                               'ar_parent_id' => strval( $this->ipRev->getParentId() ),
-                       ],
-                       [
-                               'ar_minor_edit' => '0',
-                               'ar_user' => (string)$this->getTestUser()->getUser()->getId(),
-                               'ar_user_text' => $this->getTestUser()->getUser()->getName(),
-                               'ar_actor' => (string)$this->getTestUser()->getUser()->getActorId(),
-                               'ar_len' => '7',
-                               'ar_deleted' => '0',
-                               'ar_rev_id' => strval( $this->firstRev->getId() ),
-                               'ar_timestamp' => $this->db->timestamp( $this->firstRev->getTimestamp() ),
-                               'ar_sha1' => 'pr0s8e18148pxhgjfa0gjrvpy8fiyxc',
-                               'ar_page_id' => strval( $this->firstRev->getPageId() ),
-                               'ar_comment_text' => 'testing',
-                               'ar_comment_data' => null,
-                               'ar_comment_cid' => '1',
-                               'ar_content_format' => null,
-                               'ar_content_model' => null,
-                               'ts_tags' => null,
-                               'ar_id' => '1',
-                               'ar_namespace' => '0',
-                               'ar_title' => 'PageArchiveTest_thePage',
-                               'ar_text_id' => (string)$blobStore->getTextIdFromAddress(
-                                       $this->firstRev->getSlot( SlotRecord::MAIN )->getAddress()
-                               ),
-                               'ar_parent_id' => '0',
-                       ],
-               ];
-       }
-
-}
diff --git a/tests/phpunit/includes/page/WikiPageNoContentModelDbTest.php b/tests/phpunit/includes/page/WikiPageNoContentModelDbTest.php
deleted file mode 100644 (file)
index 6f8d363..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-use MediaWiki\Tests\Revision\PreMcrSchemaOverride;
-
-/**
- * Tests WikiPage against the pre-MCR, pre ContentHandler DB schema.
- *
- * @covers WikiPage
- *
- * @group WikiPage
- * @group Storage
- * @group ContentHandler
- * @group Database
- * @group medium
- */
-class WikiPageNoContentModelDbTest extends WikiPageDbTestBase {
-
-       use PreMcrSchemaOverride;
-
-       protected function getContentHandlerUseDB() {
-               return false;
-       }
-
-       public function testGetDeletionUpdates() {
-               $mainContent1 = new WikitextContent( '' );
-
-               $title = Title::makeTitle( $this->getDefaultWikitextNS(), __METHOD__ );
-               $page = new WikiPage( $title );
-               $page = $this->createPage(
-                       $page,
-                       [ 'main' => $mainContent1 ]
-               );
-
-               $dataUpdates = $page->getDeletionUpdates( $page->getRevisionRecord() );
-               $this->assertNotEmpty( $dataUpdates );
-
-               $updateNames = array_map( function ( $du ) {
-                       return isset( $du->_name ) ? $du->_name : get_class( $du );
-               }, $dataUpdates );
-
-               $this->assertContains( LinksDeletionUpdate::class, $updateNames );
-       }
-
-}
diff --git a/tests/phpunit/includes/page/WikiPagePreMcrDbTest.php b/tests/phpunit/includes/page/WikiPagePreMcrDbTest.php
deleted file mode 100644 (file)
index 028beca..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-use MediaWiki\Tests\Revision\PreMcrSchemaOverride;
-
-/**
- * Tests WikiPage against the pre-MCR DB schema.
- *
- * @covers WikiPage
- *
- * @group WikiPage
- * @group Storage
- * @group ContentHandler
- * @group Database
- * @group medium
- */
-class WikiPagePreMcrDbTest extends WikiPageDbTestBase {
-
-       use PreMcrSchemaOverride;
-
-       protected function getContentHandlerUseDB() {
-               return true;
-       }
-
-       /**
-        * @covers WikiPage::getContentModel
-        */
-       public function testGetContentModel() {
-               $page = $this->createPage(
-                       __METHOD__,
-                       "some text",
-                       CONTENT_MODEL_JAVASCRIPT
-               );
-
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $page->getContentModel() );
-       }
-
-       /**
-        * @covers WikiPage::getContentHandler
-        */
-       public function testGetContentHandler() {
-               $page = $this->createPage(
-                       __METHOD__,
-                       "some text",
-                       CONTENT_MODEL_JAVASCRIPT
-               );
-
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertEquals( JavaScriptContentHandler::class, get_class( $page->getContentHandler() ) );
-       }
-
-}
index 72390ac..f11a2a2 100644 (file)
@@ -85,7 +85,7 @@ class RangeChronologicalPagerTest extends MediaWikiLangTestCase {
         */
        public function testGetDateRangeCondInvalid( $start, $end ) {
                $pager = $this->getMockForAbstractClass( RangeChronologicalPager::class );
-               $this->assertEquals( null, $pager->getDateRangeCond( $start, $end ) );
+               $this->assertNull( $pager->getDateRangeCond( $start, $end ) );
        }
 
        public function getDateRangeCondInvalidProvider() {
index c748e2c..6039bd2 100644 (file)
@@ -26,10 +26,10 @@ class ResourceLoaderContextTest extends PHPUnit\Framework\TestCase {
                // Request parameters
                $this->assertEquals( [], $ctx->getModules() );
                $this->assertEquals( 'qqx', $ctx->getLanguage() );
-               $this->assertEquals( false, $ctx->getDebug() );
-               $this->assertEquals( null, $ctx->getOnly() );
+               $this->assertFalse( $ctx->getDebug() );
+               $this->assertNull( $ctx->getOnly() );
                $this->assertEquals( 'fallback', $ctx->getSkin() );
-               $this->assertEquals( null, $ctx->getUser() );
+               $this->assertNull( $ctx->getUser() );
                $this->assertNull( $ctx->getContentOverrideCallback() );
 
                // Misc
@@ -67,11 +67,11 @@ class ResourceLoaderContextTest extends PHPUnit\Framework\TestCase {
                        $ctx->getModules(),
                        [ 'foo', 'foo.quux', 'foo.baz', 'foo.bar', 'baz.quux' ]
                );
-               $this->assertEquals( false, $ctx->getDebug() );
+               $this->assertFalse( $ctx->getDebug() );
                $this->assertEquals( 'zh', $ctx->getLanguage() );
                $this->assertEquals( 'styles', $ctx->getOnly() );
                $this->assertEquals( 'fallback', $ctx->getSkin() );
-               $this->assertEquals( null, $ctx->getUser() );
+               $this->assertNull( $ctx->getUser() );
 
                // Misc
                $this->assertEquals( 'ltr', $ctx->getDirection() );
index dad9f1e..6ebbc3a 100644 (file)
@@ -292,9 +292,8 @@ TEXT
                        ->disableOriginalConstructor()
                        ->getMock();
                $image->method( 'getDataUri' )
-                       ->will( $this->returnValue( $dataUriReturnValue ) );
-               $image->expects( $this->any() )
-                       ->method( 'getUrl' )
+                       ->willReturn( $dataUriReturnValue );
+               $image->method( 'getUrl' )
                        ->will( $this->returnValueMap( [
                                [ $context, 'load.php', null, 'original', 'original.svg' ],
                                [ $context, 'load.php', null, 'rasterized', 'rasterized.png' ],
diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderImageTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderImageTest.php
deleted file mode 100644 (file)
index c3fc55a..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/**
- * @group ResourceLoader
- */
-class ResourceLoaderImageTest extends ResourceLoaderTestCase {
-
-       protected $imagesPath;
-
-       protected function setUp() {
-               parent::setUp();
-               $this->imagesPath = __DIR__ . '/../../data/resourceloader';
-       }
-
-       protected function getTestImage( $name ) {
-               $options = ResourceLoaderImageModuleTest::$commonImageData[$name];
-               $fileDescriptor = is_string( $options ) ? $options : $options['file'];
-               $allowedVariants = ( is_array( $options ) && isset( $options['variants'] ) ) ?
-                       $options['variants'] : [];
-               $variants = array_fill_keys( $allowedVariants, [ 'color' => 'red' ] );
-               return new ResourceLoaderImageTestable(
-                       $name,
-                       'test',
-                       $fileDescriptor,
-                       $this->imagesPath,
-                       $variants
-               );
-       }
-
-       public static function provideGetPath() {
-               return [
-                       [ 'abc', 'en', 'abc.gif' ],
-                       [ 'abc', 'he', 'abc.gif' ],
-                       [ 'def', 'en', 'def.svg' ],
-                       [ 'def', 'he', 'def.svg' ],
-                       [ 'ghi', 'en', 'ghi.svg' ],
-                       [ 'ghi', 'he', 'jkl.svg' ],
-                       [ 'mno', 'en', 'mno-ltr.svg' ],
-                       [ 'mno', 'ar', 'mno-rtl.svg' ],
-                       [ 'mno', 'he', 'mno-ltr.svg' ],
-                       [ 'pqr', 'en', 'pqr-b.svg' ],
-                       [ 'pqr', 'en-gb', 'pqr-b.svg' ],
-                       [ 'pqr', 'de', 'pqr-f.svg' ],
-                       [ 'pqr', 'de-formal', 'pqr-f.svg' ],
-                       [ 'pqr', 'ar', 'pqr-f.svg' ],
-                       [ 'pqr', 'fr', 'pqr-a.svg' ],
-                       [ 'pqr', 'he', 'pqr-a.svg' ],
-               ];
-       }
-
-       /**
-        * @covers ResourceLoaderImage::getPath
-        * @dataProvider provideGetPath
-        */
-       public function testGetPath( $imageName, $languageCode, $path ) {
-               static $dirMap = [
-                       'en' => 'ltr',
-                       'en-gb' => 'ltr',
-                       'de' => 'ltr',
-                       'de-formal' => 'ltr',
-                       'fr' => 'ltr',
-                       'he' => 'rtl',
-                       'ar' => 'rtl',
-               ];
-
-               $image = $this->getTestImage( $imageName );
-               $context = $this->getResourceLoaderContext( [
-                       'lang' => $languageCode,
-                       'dir' => $dirMap[$languageCode],
-               ] );
-
-               $this->assertEquals( $image->getPath( $context ), $this->imagesPath . '/' . $path );
-       }
-
-       /**
-        * @covers ResourceLoaderImage::getExtension
-        * @covers ResourceLoaderImage::getMimeType
-        */
-       public function testGetExtension() {
-               $image = $this->getTestImage( 'def' );
-               $this->assertEquals( $image->getExtension(), 'svg' );
-               $this->assertEquals( $image->getExtension( 'original' ), 'svg' );
-               $this->assertEquals( $image->getExtension( 'rasterized' ), 'png' );
-               $image = $this->getTestImage( 'abc' );
-               $this->assertEquals( $image->getExtension(), 'gif' );
-               $this->assertEquals( $image->getExtension( 'original' ), 'gif' );
-               $this->assertEquals( $image->getExtension( 'rasterized' ), 'gif' );
-       }
-
-       /**
-        * @covers ResourceLoaderImage::getImageData
-        * @covers ResourceLoaderImage::variantize
-        * @covers ResourceLoaderImage::massageSvgPathdata
-        */
-       public function testGetImageData() {
-               $context = $this->getResourceLoaderContext();
-
-               $image = $this->getTestImage( 'def' );
-               $data = file_get_contents( $this->imagesPath . '/def.svg' );
-               $dataConstructive = file_get_contents( $this->imagesPath . '/def_variantize.svg' );
-               $this->assertEquals( $image->getImageData( $context, null, 'original' ), $data );
-               $this->assertEquals(
-                       $image->getImageData( $context, 'destructive', 'original' ),
-                       $dataConstructive
-               );
-               // Stub, since we don't know if we even have a SVG handler, much less what exactly it'll output
-               $this->assertEquals( $image->getImageData( $context, null, 'rasterized' ), 'RASTERIZESTUB' );
-
-               $image = $this->getTestImage( 'abc' );
-               $data = file_get_contents( $this->imagesPath . '/abc.gif' );
-               $this->assertEquals( $image->getImageData( $context, null, 'original' ), $data );
-               $this->assertEquals( $image->getImageData( $context, null, 'rasterized' ), $data );
-       }
-
-       /**
-        * @covers ResourceLoaderImage::massageSvgPathdata
-        */
-       public function testMassageSvgPathdata() {
-               $image = $this->getTestImage( 'ghi' );
-               $data = file_get_contents( $this->imagesPath . '/ghi.svg' );
-               $dataMassaged = file_get_contents( $this->imagesPath . '/ghi_massage.svg' );
-               $this->assertEquals( $image->massageSvgPathdata( $data ), $dataMassaged );
-       }
-}
-
-class ResourceLoaderImageTestable extends ResourceLoaderImage {
-       // Make some protected methods public
-       public function massageSvgPathdata( $svg ) {
-               return parent::massageSvgPathdata( $svg );
-       }
-
-       // Stub, since we don't know if we even have a SVG handler, much less what exactly it'll output
-       public function rasterize( $svg ) {
-               return 'RASTERIZESTUB';
-       }
-}
index 9bbf14d..0c3512a 100644 (file)
@@ -718,9 +718,9 @@ mw.loader.register([
         * @dataProvider provideRegistrations
         */
        public function testRegistrationsMinified( $modules ) {
-               $this->setMwGlobals( 'wgResourceLoaderDebug', false );
-
-               $context = $this->getResourceLoaderContext();
+               $context = $this->getResourceLoaderContext( [
+                       'debug' => 'false',
+               );
                $rl = $context->getResourceLoader();
                $rl->register( $modules );
                $module = new ResourceLoaderStartUpModule();
@@ -743,7 +743,9 @@ mw.loader.register([
         * @dataProvider provideRegistrations
         */
        public function testRegistrationsUnminified( $modules ) {
-               $context = $this->getResourceLoaderContext();
+               $context = $this->getResourceLoaderContext( [
+                       'debug' => 'true',
+               ] );
                $rl = $context->getResourceLoader();
                $rl->register( $modules );
                $module = new ResourceLoaderStartUpModule();
index be11d53..f0c97f4 100644 (file)
@@ -518,13 +518,14 @@ END
                        'wrap' => true,
                        'styles' => [], 'templates' => [], 'messages' => new XmlJsCode( '{}' ), 'packageFiles' => [],
                ];
-               ResourceLoader::clearCache();
-               $this->setMwGlobals( 'wgResourceLoaderDebug', true );
-
                $rl = TestingAccessWrapper::newFromClass( ResourceLoader::class );
+               $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest( [
+                       'debug' => 'true',
+               ] ) );
                $this->assertEquals(
                        $case['expected'],
                        $rl->makeLoaderImplementScript(
+                               $context,
                                $case['name'],
                                ( $case['wrap'] && is_string( $case['scripts'] ) )
                                        ? new XmlJsCode( $case['scripts'] )
@@ -543,7 +544,9 @@ END
        public function testMakeLoaderImplementScriptInvalid() {
                $this->setExpectedException( MWException::class, 'Invalid scripts error' );
                $rl = TestingAccessWrapper::newFromClass( ResourceLoader::class );
+               $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest() );
                $rl->makeLoaderImplementScript(
+                       $context,
                        'test', // name
                        123, // scripts
                        null, // styles
@@ -557,6 +560,9 @@ END
         * @covers ResourceLoader::makeLoaderRegisterScript
         */
        public function testMakeLoaderRegisterScript() {
+               $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest( [
+                       'debug' => 'true',
+               ] ) );
                $this->assertEquals(
                        'mw.loader.register([
     [
@@ -564,7 +570,7 @@ END
         "1234567"
     ]
 ]);',
-                       ResourceLoader::makeLoaderRegisterScript( [
+                       ResourceLoader::makeLoaderRegisterScript( $context, [
                                [ 'test.name', '1234567' ],
                        ] ),
                        'Nested array parameter'
@@ -600,7 +606,7 @@ END
         "return true;"
     ]
 ]);',
-                       ResourceLoader::makeLoaderRegisterScript( [
+                       ResourceLoader::makeLoaderRegisterScript( $context, [
                                [ 'test.foo', '100' , [], null, null ],
                                [ 'test.bar', '200', [ 'test.unknown' ], null ],
                                [ 'test.baz', '300', [ 'test.quux', 'test.foo' ], null ],
@@ -614,31 +620,34 @@ END
         * @covers ResourceLoader::makeLoaderSourcesScript
         */
        public function testMakeLoaderSourcesScript() {
+               $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest( [
+                       'debug' => 'true',
+               ] ) );
                $this->assertEquals(
                        'mw.loader.addSource({
     "local": "/w/load.php"
 });',
-                       ResourceLoader::makeLoaderSourcesScript( 'local', '/w/load.php' )
+                       ResourceLoader::makeLoaderSourcesScript( $context, 'local', '/w/load.php' )
                );
                $this->assertEquals(
                        'mw.loader.addSource({
     "local": "/w/load.php"
 });',
-                       ResourceLoader::makeLoaderSourcesScript( [ 'local' => '/w/load.php' ] )
+                       ResourceLoader::makeLoaderSourcesScript( $context, [ 'local' => '/w/load.php' ] )
                );
                $this->assertEquals(
                        'mw.loader.addSource({
     "local": "/w/load.php",
     "example": "https://example.org/w/load.php"
 });',
-                       ResourceLoader::makeLoaderSourcesScript( [
+                       ResourceLoader::makeLoaderSourcesScript( $context, [
                                'local' => '/w/load.php',
                                'example' => 'https://example.org/w/load.php'
                        ] )
                );
                $this->assertEquals(
                        'mw.loader.addSource([]);',
-                       ResourceLoader::makeLoaderSourcesScript( [] )
+                       ResourceLoader::makeLoaderSourcesScript( $context, [] )
                );
        }
 
index 33bc29f..3eb7498 100644 (file)
@@ -376,7 +376,7 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                ] );
 
                $request = new \FauxRequest();
-               $this->assertEquals( null, $provider->suggestLoginUsername( $request ) );
+               $this->assertNull( $provider->suggestLoginUsername( $request ) );
 
                $request->setCookies( [
                        'xUserName' => 'Example',
index 5e208ac..0cca53b 100644 (file)
@@ -47,7 +47,7 @@ class FauxResponseTest extends \MediaWikiUnitTestCase {
                        'expire' => $expire,
                ];
 
-               $this->assertEquals( null, $this->response->getCookie( 'xkey' ), 'Non-existing cookie' );
+               $this->assertNull( $this->response->getCookie( 'xkey' ), 'Non-existing cookie' );
                $this->response->setCookie( 'key', 'val', $expire, [
                        'prefix' => 'x',
                        'path' => '/path',
@@ -67,7 +67,7 @@ class FauxResponseTest extends \MediaWikiUnitTestCase {
         * @covers FauxResponse::header
         */
        public function testHeader() {
-               $this->assertEquals( null, $this->response->getHeader( 'Location' ), 'Non-existing header' );
+               $this->assertNull( $this->response->getHeader( 'Location' ), 'Non-existing header' );
 
                $this->response->header( 'Location: http://localhost/' );
                $this->assertEquals(
index bd54d50..efa0564 100644 (file)
@@ -71,8 +71,7 @@ class ChangesListFilterGroupTest extends \MediaWikiUnitTestCase {
                        $group->getFilter( 'foo' )->getName()
                );
 
-               $this->assertEquals(
-                       null,
+               $this->assertNull(
                        $group->getFilter( 'bar' )
                );
        }
index 17487ac..6566e14 100644 (file)
@@ -42,7 +42,7 @@ class DiffOpTest extends \MediaWikiUnitTestCase {
                $this->assertEquals( 'foo', $obj->getClosing( 0 ) );
                $this->assertEquals( 'bar', $obj->getClosing( 1 ) );
                $this->assertEquals( 'baz', $obj->getClosing( 2 ) );
-               $this->assertEquals( null, $obj->getClosing( 3 ) );
+               $this->assertNull( $obj->getClosing( 3 ) );
        }
 
        /**
index f3a7ae4..e8562cf 100644 (file)
@@ -38,7 +38,7 @@ class LanguageCodeTest extends MediaWikiUnitTestCase {
        public function testReplaceDeprecatedCodes() {
                $this->assertEquals( 'gsw', LanguageCode::replaceDeprecatedCodes( 'als' ) );
                $this->assertEquals( 'gsw', LanguageCode::replaceDeprecatedCodes( 'gsw' ) );
-               $this->assertEquals( null, LanguageCode::replaceDeprecatedCodes( null ) );
+               $this->assertNull( LanguageCode::replaceDeprecatedCodes( null ) );
        }
 
        /**
diff --git a/tests/phpunit/unit/includes/resourceloader/ResourceLoaderImageTest.php b/tests/phpunit/unit/includes/resourceloader/ResourceLoaderImageTest.php
new file mode 100644 (file)
index 0000000..5265b3e
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+
+/**
+ * @group ResourceLoader
+ */
+class ResourceLoaderImageTest extends MediaWikiUnitTestCase {
+
+       private $imagesPath;
+
+       protected function setUp() {
+               parent::setUp();
+               $this->imagesPath = __DIR__ . '/../../../data/resourceloader';
+       }
+
+       protected function tearDown() {
+               Language::$dataCache = null;
+       }
+
+       protected function getTestImage( $name ) {
+               $options = ResourceLoaderImageModuleTest::$commonImageData[$name];
+               $fileDescriptor = is_string( $options ) ? $options : $options['file'];
+               $allowedVariants = ( is_array( $options ) && isset( $options['variants'] ) ) ?
+                       $options['variants'] : [];
+               $variants = array_fill_keys( $allowedVariants, [ 'color' => 'red' ] );
+               return new ResourceLoaderImageTestable(
+                       $name,
+                       'test',
+                       $fileDescriptor,
+                       $this->imagesPath,
+                       $variants
+               );
+       }
+
+       public static function provideGetPath() {
+               return [
+                       [ 'abc', 'en', 'abc.gif' ],
+                       [ 'abc', 'he', 'abc.gif' ],
+                       [ 'def', 'en', 'def.svg' ],
+                       [ 'def', 'he', 'def.svg' ],
+                       [ 'ghi', 'en', 'ghi.svg' ],
+                       [ 'ghi', 'he', 'jkl.svg' ],
+                       [ 'mno', 'en', 'mno-ltr.svg' ],
+                       [ 'mno', 'ar', 'mno-rtl.svg' ],
+                       [ 'mno', 'he', 'mno-ltr.svg' ],
+                       [ 'pqr', 'en', 'pqr-b.svg' ],
+                       [ 'pqr', 'en-gb', 'pqr-b.svg' ],
+                       [ 'pqr', 'de', 'pqr-f.svg' ],
+                       [ 'pqr', 'de-formal', 'pqr-f.svg' ],
+                       [ 'pqr', 'ar', 'pqr-f.svg' ],
+                       [ 'pqr', 'fr', 'pqr-a.svg' ],
+                       [ 'pqr', 'he', 'pqr-a.svg' ],
+               ];
+       }
+
+       /**
+        * @covers ResourceLoaderImage::getPath
+        * @dataProvider provideGetPath
+        */
+       public function testGetPath( $imageName, $languageCode, $path ) {
+               static $dirMap = [
+                       'en' => 'ltr',
+                       'en-gb' => 'ltr',
+                       'de' => 'ltr',
+                       'de-formal' => 'ltr',
+                       'fr' => 'ltr',
+                       'he' => 'rtl',
+                       'ar' => 'rtl',
+               ];
+
+               $image = $this->getTestImage( $imageName );
+               $context = new DerivativeResourceLoaderContext(
+                       $this->createMock( ResourceLoaderContext::class )
+               );
+               $context->setLanguage( $languageCode );
+               $context->setDirection( $dirMap[$languageCode] );
+               Language::$dataCache = $this->createMock( LocalisationCache::class );
+               Language::$dataCache->method( 'getItem' )->will( $this->returnCallback( function ( $code ) {
+                       return ( [
+                               'en-gb' => [ 'en' ],
+                               'de-formal' => [ 'de' ],
+                       ] )[ $code ] ?? [];
+               } ) );
+
+               $this->assertEquals( $image->getPath( $context ), $this->imagesPath . '/' . $path );
+       }
+
+       /**
+        * @covers ResourceLoaderImage::getExtension
+        * @covers ResourceLoaderImage::getMimeType
+        */
+       public function testGetExtension() {
+               $image = $this->getTestImage( 'def' );
+               $this->assertEquals( $image->getExtension(), 'svg' );
+               $this->assertEquals( $image->getExtension( 'original' ), 'svg' );
+               $this->assertEquals( $image->getExtension( 'rasterized' ), 'png' );
+               $image = $this->getTestImage( 'abc' );
+               $this->assertEquals( $image->getExtension(), 'gif' );
+               $this->assertEquals( $image->getExtension( 'original' ), 'gif' );
+               $this->assertEquals( $image->getExtension( 'rasterized' ), 'gif' );
+       }
+
+       /**
+        * @covers ResourceLoaderImage::getImageData
+        * @covers ResourceLoaderImage::variantize
+        * @covers ResourceLoaderImage::massageSvgPathdata
+        */
+       public function testGetImageData() {
+               $context = $this->createMock( ResourceLoaderContext::class );
+
+               $image = $this->getTestImage( 'def' );
+               $data = file_get_contents( $this->imagesPath . '/def.svg' );
+               $dataConstructive = file_get_contents( $this->imagesPath . '/def_variantize.svg' );
+               $this->assertEquals( $image->getImageData( $context, null, 'original' ), $data );
+               $this->assertEquals(
+                       $image->getImageData( $context, 'destructive', 'original' ),
+                       $dataConstructive
+               );
+               // Stub, since we don't know if we even have a SVG handler, much less what exactly it'll output
+               $this->assertEquals( $image->getImageData( $context, null, 'rasterized' ), 'RASTERIZESTUB' );
+
+               $image = $this->getTestImage( 'abc' );
+               $data = file_get_contents( $this->imagesPath . '/abc.gif' );
+               $this->assertEquals( $image->getImageData( $context, null, 'original' ), $data );
+               $this->assertEquals( $image->getImageData( $context, null, 'rasterized' ), $data );
+       }
+
+       /**
+        * @covers ResourceLoaderImage::massageSvgPathdata
+        */
+       public function testMassageSvgPathdata() {
+               $image = $this->getTestImage( 'ghi' );
+               $data = file_get_contents( $this->imagesPath . '/ghi.svg' );
+               $dataMassaged = file_get_contents( $this->imagesPath . '/ghi_massage.svg' );
+               $this->assertEquals( $image->massageSvgPathdata( $data ), $dataMassaged );
+       }
+}
+
+class ResourceLoaderImageTestable extends ResourceLoaderImage {
+       // Make some protected methods public
+       public function massageSvgPathdata( $svg ) {
+               return parent::massageSvgPathdata( $svg );
+       }
+
+       // Stub, since we don't know if we even have a SVG handler, much less what exactly it'll output
+       public function rasterize( $svg ) {
+               return 'RASTERIZESTUB';
+       }
+}
index b6e1d3a..7b2b299 100644 (file)
@@ -104,7 +104,7 @@ class SessionUnitTest extends MediaWikiUnitTestCase {
                $this->assertEquals( 'zero', $session->get( 0 ) );
                $this->assertFalse( $backend->dirty );
 
-               $this->assertEquals( null, $session->get( 'null' ) );
+               $this->assertNull( $session->get( 'null' ) );
                $this->assertEquals( 'default', $session->get( 'null', 'default' ) );
                $this->assertFalse( $backend->dirty );
 
@@ -165,7 +165,7 @@ class SessionUnitTest extends MediaWikiUnitTestCase {
                $this->assertFalse( $backend->dirty );
 
                $logger->setCollect( true );
-               $this->assertEquals( null, $session['null'] );
+               $this->assertNull( $session['null'] );
                $logger->setCollect( false );
                $this->assertFalse( $backend->dirty );
                $this->assertSame( [
index 4c6e7c9..16c3183 100644 (file)
                        [ 'test.load.circleB', '0', [ 'test.load.circleC' ] ],
                        [ 'test.load.circleC', '0', [ 'test.load.circleA' ] ]
                ] );
-               this.sandbox.stub( mw, 'track', function ( topic, data ) {
+               this.sandbox.stub( mw, 'trackError', function ( topic, data ) {
                        capture.push( {
                                topic: topic,
                                error: data.exception && data.exception.message,
                mw.loader.register( [
                        [ 'test.load.circleDirect', '0', [ 'test.load.circleDirect' ] ]
                ] );
-               this.sandbox.stub( mw, 'track', function ( topic, data ) {
+               this.sandbox.stub( mw, 'trackError', function ( topic, data ) {
                        capture.push( {
                                topic: topic,
                                error: data.exception && data.exception.message,
 
        QUnit.test( '.load() - Error: Unregistered', function ( assert ) {
                var capture = [];
-               this.sandbox.stub( mw, 'track', function ( topic, data ) {
-                       capture.push( {
-                               topic: topic,
-                               error: data.exception && data.exception.message,
-                               source: data.source
-                       } );
+               this.sandbox.stub( mw.log, 'warn', function ( str ) {
+                       capture.push( str );
                } );
 
                mw.loader.load( 'test.load.unreg' );
-               assert.deepEqual(
-                       [ {
-                               topic: 'resourceloader.exception',
-                               error: 'Unknown module: test.load.unreg',
-                               source: 'resolve'
-                       } ],
-                       capture
-               );
+               assert.deepEqual( capture, [ 'Skipped unresolvable module test.load.unreg' ] );
        } );
 
        // Regression test for T36853
        QUnit.test( '.load() - Error: Missing dependency', function ( assert ) {
                var capture = [];
-               this.sandbox.stub( mw, 'track', function ( topic, data ) {
+               this.sandbox.stub( mw, 'trackError', function ( topic, data ) {
                        capture.push( {
                                topic: topic,
                                error: data.exception && data.exception.message,
                this.useStubClock();
 
                // Don't actually emit an error event
-               this.sandbox.stub( mw, 'track' );
+               this.sandbox.stub( mw, 'trackError' );
 
                mw.loader.register( [
                        [ 'test.module1', '0' ],
                }, {}, {} );
                this.tick();
 
-               assert.strictEqual( mw.loader.getState( 'test.module1' ), 'error', 'Expected "error" state for test.module1' );
-               assert.strictEqual( mw.loader.getState( 'test.module2' ), 'error', 'Expected "error" state for test.module2' );
-               assert.strictEqual( mw.loader.getState( 'test.module3' ), 'error', 'Expected "error" state for test.module3' );
+               assert.strictEqual( mw.loader.getState( 'test.module1' ), 'error', 'State of test.module1' );
+               assert.strictEqual( mw.loader.getState( 'test.module2' ), 'error', 'State of test.module2' );
+               assert.strictEqual( mw.loader.getState( 'test.module3' ), 'error', 'State of test.module3' );
 
-               assert.strictEqual( mw.track.callCount, 1 );
+               assert.strictEqual( mw.trackError.callCount, 1 );
        } );
 
        QUnit.test( 'Out-of-order implementation', function ( assert ) {
 
                mw.loader.implement( 'test.module4', function () {} );
                this.tick();
-               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
-               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
-               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'registered', 'Expected "registered" state for test.module6' );
+               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'State of test.module4' );
+               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'State of test.module5' );
+               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'registered', 'State of test.module6' );
 
                mw.loader.implement( 'test.module6', function () {} );
                this.tick();
-               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
-               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
-               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'loaded', 'Expected "loaded" state for test.module6' );
+               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'State of test.module4' );
+               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'State of test.module5' );
+               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'loaded', 'State of test.module6' );
 
                mw.loader.implement( 'test.module5', function () {} );
                this.tick();
-               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
-               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'ready', 'Expected "ready" state for test.module5' );
-               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'ready', 'Expected "ready" state for test.module6' );
+               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'State of test.module4' );
+               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'ready', 'State of test.module5' );
+               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'ready', 'State of test.module6' );
        } );
 
        QUnit.test( 'Missing dependency', function ( assert ) {