Merge "resourceloader: Add version to ResourceLoaderImage urls for long-cache"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sat, 28 Sep 2019 20:20:32 +0000 (20:20 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 28 Sep 2019 20:20:32 +0000 (20:20 +0000)
109 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
autoload.php
docs/Introduction.md
includes/DefaultSettings.php
includes/Revision/RevisionStore.php
includes/ServiceWiring.php
includes/api/ApiMain.php
includes/api/i18n/ja.json
includes/db/MWLBFactory.php
includes/filebackend/FileBackendGroup.php
includes/filerepo/FileRepo.php
includes/installer/MysqlUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/installer/i18n/de.json
includes/installer/i18n/ja.json
includes/installer/i18n/uk.json
includes/libs/StringUtils.php
includes/libs/objectcache/wancache/WANObjectCache.php
includes/libs/rdbms/loadbalancer/LoadBalancerSingle.php
includes/logging/ProtectLogFormatter.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/resourceloader/ResourceLoaderWikiModule.php
includes/specialpage/QueryPage.php
languages/i18n/diq.json
languages/i18n/en.json
languages/i18n/hu.json
languages/i18n/ja.json
languages/i18n/khw.json
languages/i18n/lzh.json
languages/i18n/nap.json
languages/i18n/nqo.json
languages/i18n/qqq.json
maintenance/archives/patch-archive-ar_comment_id.sql [new file with mode: 0644]
maintenance/archives/patch-comment-table.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/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
resources/Resources.php
tests/common/TestsAutoLoader.php
tests/phpunit/MediaWikiIntegrationTestCase.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/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/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/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/session/SessionUnitTest.php

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 55e5a7f..dc57ff6 100644 (file)
@@ -532,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 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 29b628c..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;
 
index a1aeccb..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;
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 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 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 ad5708a..63b320e 100644 (file)
@@ -23,7 +23,6 @@
 
 use MediaWiki\Config\ServiceOptions;
 use MediaWiki\Logger\LoggerFactory;
-use Wikimedia\Rdbms\LBFactory;
 use Wikimedia\Rdbms\DatabaseDomain;
 
 /**
@@ -359,34 +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( [
-                               'un_user_id' => 'user_id',
-                               'un_user_ip' => 'user_ip',
-                       ] );
-               }
-       }
-
        /**
         * Log a database deprecation warning
         * @param string $msg Deprecation message
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 7d41d04..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' ],
@@ -390,6 +404,7 @@ class MysqlUpdater extends DatabaseUpdater {
                                '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 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 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 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 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 0d546fa..c69d4ec 100644 (file)
@@ -824,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";
                        }
 
@@ -1098,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':
@@ -1124,6 +1131,7 @@ MESSAGE;
                                                        }
                                                }
                                                $strContent = self::makeLoaderImplementScript(
+                                                       $context,
                                                        $implementKey,
                                                        $scripts,
                                                        $content['styles'] ?? [],
@@ -1169,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 );
                                }
@@ -1178,7 +1186,7 @@ MESSAGE;
                        }
                } elseif ( $states ) {
                        $this->errors[] = 'Problematic modules: '
-                               . self::encodeJsonForScript( $states );
+                               . $context->encodeJson( $states );
                }
 
                return $out;
@@ -1217,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
@@ -1231,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 . '}' );
@@ -1249,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'] . '}' );
@@ -1260,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.' );
                }
@@ -1278,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() );
        }
 
        /**
@@ -1357,25 +1366,22 @@ 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 ):
-        *         Set the state of a single module called $name to $state
+        * Returns a JS call to mw.loader.state, which sets the state of modules
+        * to a given value:
         *
-        *    - ResourceLoader::makeLoaderStateScript( [ $name => $state, ... ] ):
+        *    - ResourceLoader::makeLoaderStateScript( $context, [ $name => $state, ... ] ):
         *         Set the state of modules with the given names to the given states
         *
-        * @param array|string $states
-        * @param string|null $state
+        * @internal
+        * @param ResourceLoaderContext $context
+        * @param array $states
         * @return string JavaScript code
         */
-       public static function makeLoaderStateScript( $states, $state = null ) {
-               if ( !is_array( $states ) ) {
-                       $states = [ $states => $state ];
-               }
+       public static function makeLoaderStateScript(
+               ResourceLoaderContext $context, array $states
+       ) {
                return 'mw.loader.state('
-                       . self::encodeJsonForScript( $states )
+                       . $context->encodeJson( $states )
                        . ');';
        }
 
@@ -1420,15 +1426,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
@@ -1438,7 +1444,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().
@@ -1461,30 +1469,29 @@ MESSAGE;
                array_walk( $modules, [ self::class, 'trimArray' ] );
 
                return 'mw.loader.register('
-                       . self::encodeJsonForScript( $modules )
+                       . $context->encodeJson( $modules )
                        . ');';
        }
 
        /**
         * Returns JS code which calls mw.loader.addSource() with the given
-        * parameters. Has two calling conventions:
-        *
-        *   - ResourceLoader::makeLoaderSourcesScript( $id, $properties ):
-        *       Register a single source
+        * parameters.
         *
-        *   - ResourceLoader::makeLoaderSourcesScript( [ $id1 => $loadUrl, $id2 => $loadUrl, ... ] );
+        *   - ResourceLoader::makeLoaderSourcesScript( $context,
+        *         [ $id1 => $loadUrl, $id2 => $loadUrl, ... ]
+        *     );
         *       Register sources with the given IDs and properties.
         *
-        * @param string|array $sources Source ID
-        * @param string|null $loadUrl load.php url
+        * @internal For use by ResourceLoaderStartUpModule only
+        * @param ResourceLoaderContext $context
+        * @param array $sources
         * @return string JavaScript code
         */
-       public static function makeLoaderSourcesScript( $sources, $loadUrl = null ) {
-               if ( !is_array( $sources ) ) {
-                       $sources = [ $sources => $loadUrl ];
-               }
+       public static function makeLoaderSourcesScript(
+               ResourceLoaderContext $context, array $sources
+       ) {
                return 'mw.loader.addSource('
-                       . self::encodeJsonForScript( $sources )
+                       . $context->encodeJson( $sources )
                        . ');';
        }
 
index d98d86b..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;
@@ -480,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 37501d4..237cea4 100644 (file)
@@ -165,11 +165,11 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
 
        /**
         * @param string $titleText
-        * @param ResourceLoaderContext|null $context (but passing null is deprecated)
+        * @param ResourceLoaderContext $context
         * @return null|string
         * @since 1.32 added the $context parameter
         */
-       protected function getContent( $titleText, ResourceLoaderContext $context = null ) {
+       protected function getContent( $titleText, ResourceLoaderContext $context ) {
                $title = Title::newFromText( $titleText );
                if ( !$title ) {
                        return null; // Bad title
@@ -194,20 +194,16 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
 
        /**
         * @param Title $title
-        * @param ResourceLoaderContext|null $context (but passing null is deprecated)
+        * @param ResourceLoaderContext $context
         * @param int|null $maxRedirects Maximum number of redirects to follow. If
         *  null, uses $wgMaxRedirects
         * @return Content|null
         * @since 1.32 added the $context and $maxRedirects parameters
         */
        protected function getContentObj(
-               Title $title, ResourceLoaderContext $context = null, $maxRedirects = null
+               Title $title, ResourceLoaderContext $context, $maxRedirects = null
        ) {
-               if ( $context === null ) {
-                       wfDeprecated( __METHOD__ . ' without a ResourceLoader context', '1.32' );
-               }
-
-               $overrideCallback = $context ? $context->getContentOverrideCallback() : null;
+               $overrideCallback = $context->getContentOverrideCallback();
                $content = $overrideCallback ? call_user_func( $overrideCallback, $title ) : null;
                if ( $content ) {
                        if ( !$content instanceof Content ) {
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 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 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 20e2bb7..ff33e2e 100644 (file)
        "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": "ボットパスワード制限によりログインできません。",
        "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 659bbec..bf2ca77 100644 (file)
        "showpreview": "示覽",
        "showdiff": "示異",
        "anoneditwarning": "'''警示:'''子未登簿,若確纂,IP將誌。茍[$1 登]或[$2 開戶口],是纂欲存以子名,及他效。",
-       "anonpreviewwarning": "''子未登簿,IP將誌。''",
+       "anonpreviewwarning": "子未登簿,IP將誌。",
        "missingsummary": "'''醒示:'''子未概之,復「$1」則文倍焉。",
        "missingcommenttext": "請贊之",
        "missingcommentheader": "'''醒示:'''子未概標之,復「$1」則文倍焉。",
index 14235e1..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_change": "càgna",
        "unprotect": "Càgna prutezzione",
        "newpage": "Paggena nova",
-       "talkpagelinktext": "Chiàcchiera",
+       "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.",
        "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",
        "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 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}}.",
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-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 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 0b0e485..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',
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;
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 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 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 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..99f4347 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,28 @@ 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' )
-               );
-               $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 ) );
        }
 
        /**
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( [