publish:
blubberfile: blubber.yaml
stages:
- - name: dev
+ - name: publish
build: dev
publish:
image: true
- tags: [dev]
included: true,
served: false
} ],
- logLevel: 'DEBUG',
+ logLevel: ( process.env.ZUUL_PROJECT ? 'DEBUG' : 'INFO' ),
frameworks: [ 'qunit' ],
reporters: [ 'mocha' ],
singleRun: true,
'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',
'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
'$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;
[ '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' ],
[ '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' ],
[ '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' ],
/**
* 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 */
];
// Show change protection link
- if ( !MediaWikiServices::getInstance()
+ if ( MediaWikiServices::getInstance()
->getPermissionManager()
->userHasRight( $this->context->getUser(), 'protect' )
) {
*
* @since 1.28
* @param ResourceLoaderContext $context
+ * @param string[]|null $modules
* @return string Hash
*/
- public function makeVersionQuery( ResourceLoaderContext $context ) {
+ public function makeVersionQuery( ResourceLoaderContext $context, array $modules = null ) {
+ if ( $modules === null ) {
+ wfDeprecated( __METHOD__ . ' without $modules', '1.34' );
+ $modules = $context->getModules();
+ }
// As of MediaWiki 1.28, the server and client use the same algorithm for combining
// version hashes. There is no technical reason for this to be same, and for years the
// implementations differed. If getCombinedVersion in PHP (used for StartupModule and
// E-Tag headers) differs in the future from getCombinedVersion in JS (used for 'version'
// query parameter), then this method must continue to match the JS one.
- $moduleNames = [];
- foreach ( $context->getModules() as $name ) {
+ $filtered = [];
+ foreach ( $modules as $name ) {
if ( !$this->getModule( $name ) ) {
// If a versioned request contains a missing module, the version is a mismatch
// as the client considered a module (and version) we don't have.
return '';
}
- $moduleNames[] = $name;
+ $filtered[] = $name;
}
- return $this->getCombinedVersion( $context, $moduleNames );
+ return $this->getCombinedVersion( $context, $filtered );
}
/**
$errorResponse = self::makeComment( $errorText );
if ( $context->shouldIncludeScripts() ) {
$errorResponse .= 'if (window.console && console.error) { console.error('
- . self::encodeJsonForScript( $errorText )
+ . $context->encodeJson( $errorText )
. "); }\n";
}
// - Version mismatch (T117587, T47877)
if ( is_null( $context->getVersion() )
|| $errors
- || $context->getVersion() !== $this->makeVersionQuery( $context )
+ || $context->getVersion() !== $this->makeVersionQuery( $context, $context->getModules() )
) {
$maxage = $rlMaxage['unversioned']['client'];
$smaxage = $rlMaxage['unversioned']['server'];
$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':
}
}
$strContent = self::makeLoaderImplementScript(
+ $context,
$implementKey,
$scripts,
$content['styles'] ?? [],
// 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 );
}
}
} elseif ( $states ) {
$this->errors[] = 'Problematic modules: '
- . self::encodeJsonForScript( $states );
+ . $context->encodeJson( $states );
}
return $out;
/**
* 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
* @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 . '}' );
// 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'] . '}' );
}
$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.' );
}
];
self::trimArray( $module );
- return Xml::encodeJsCall( 'mw.loader.implement', $module, self::inDebugMode() );
+ return Xml::encodeJsCall( 'mw.loader.implement', $module, $context->getDebug() );
}
/**
* Returns a JS call to mw.loader.state, which sets the state of one
* ore more modules to a given value. Has two calling conventions:
*
- * - ResourceLoader::makeLoaderStateScript( $name, $state ):
+ * - ResourceLoader::makeLoaderStateScript( $context, $name, $state ):
* Set the state of a single module called $name to $state
*
- * - ResourceLoader::makeLoaderStateScript( [ $name => $state, ... ] ):
+ * - ResourceLoader::makeLoaderStateScript( $context, [ $name => $state, ... ] ):
* Set the state of modules with the given names to the given states
*
+ * @internal
+ * @param ResourceLoaderContext $context
* @param array|string $states
* @param string|null $state
* @return string JavaScript code
*/
- public static function makeLoaderStateScript( $states, $state = null ) {
+ public static function makeLoaderStateScript(
+ ResourceLoaderContext $context, $states, $state = null
+ ) {
if ( !is_array( $states ) ) {
$states = [ $states => $state ];
}
return 'mw.loader.state('
- . self::encodeJsonForScript( $states )
+ . $context->encodeJson( $states )
. ');';
}
* @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
* - 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().
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 ):
+ * - ResourceLoader::makeLoaderSourcesScript( $context, $id, $properties ):
* Register a single source
*
- * - ResourceLoader::makeLoaderSourcesScript( [ $id1 => $loadUrl, $id2 => $loadUrl, ... ] );
+ * - ResourceLoader::makeLoaderSourcesScript( $context,
+ * [ $id1 => $loadUrl, $id2 => $loadUrl, ... ]
+ * );
* Register sources with the given IDs and properties.
*
+ * @internal For use by ResourceLoaderStartUpModule only
+ * @param ResourceLoaderContext $context
* @param string|array $sources Source ID
* @param string|null $loadUrl load.php url
* @return string JavaScript code
*/
- public static function makeLoaderSourcesScript( $sources, $loadUrl = null ) {
+ public static function makeLoaderSourcesScript(
+ ResourceLoaderContext $context, $sources, $loadUrl = null
+ ) {
if ( !is_array( $sources ) ) {
$sources = [ $sources => $loadUrl ];
}
return 'mw.loader.addSource('
- . self::encodeJsonForScript( $sources )
+ . $context->encodeJson( $sources )
. ');';
}
// 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;
}
// 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;
// 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;
// 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;
// Link/embed each set
foreach ( $moduleSets as list( $embed, $moduleSet ) ) {
- $context->setModules( array_keys( $moduleSet ) );
+ $moduleSetNames = array_keys( $moduleSet );
+ $context->setModules( $moduleSetNames );
if ( $embed ) {
// Decide whether to use style or script element
if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
// This should NOT be done for the site group (T29564) because anons get that too
// and we shouldn't be putting timestamps in CDN-cached HTML
if ( $group === 'user' ) {
- // Must setModules() before makeVersionQuery()
- $context->setVersion( $rl->makeVersionQuery( $context ) );
+ $context->setVersion( $rl->makeVersionQuery( $context, $moduleSetNames ) );
}
+ // Must setModules() before createLoaderURL()
$url = $rl->createLoaderURL( $source, $context, $extraQuery );
// Decide whether to use 'style' or 'script' element
] );
} else {
$chunk = ResourceLoader::makeInlineScript(
- 'mw.loader.load(' . ResourceLoader::encodeJsonForScript( $url ) . ');',
+ 'mw.loader.load(' . $mainContext->encodeJson( $url ) . ');',
$nonce
);
}
/**
* 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() {
}
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 );
+ }
}
* @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 ) {
* keyed by media type
* @throws MWException
*/
- public function readStyleFiles( array $styles, $flip, $context ) {
+ public function readStyleFiles( array $styles, $flip, ResourceLoaderContext $context ) {
if ( !$styles ) {
return [];
}
* @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 ) ) {
* @param ResourceLoaderContext $context
* @return bool
*/
- public function getFlip( $context ) {
+ public function getFlip( ResourceLoaderContext $context ) {
return $context->getDirection() === 'rtl' && !$this->noflip;
}
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 ) )
. ');';
}
* @param ResourceLoaderContext $context
* @return bool
*/
- public function getFlip( $context ) {
+ public function getFlip( ResourceLoaderContext $context ) {
return MediaWikiServices::getInstance()->getContentLanguage()->getDir() !==
$context->getDirection();
}
/**
* 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();
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 '';
}
* @param ResourceLoaderContext $context
* @return array
*/
- private function getConfigSettings( $context ) {
+ private function getConfigSettings( ResourceLoaderContext $context ) {
$conf = $this->getConfig();
/**
}
$skipFunction = $module->getSkipFunction();
- if ( $skipFunction !== null && !ResourceLoader::inDebugMode() ) {
+ if ( $skipFunction !== null && !$context->getDebug() ) {
$skipFunction = ResourceLoader::filter( 'minify-js', $skipFunction );
}
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 = [];
}
// 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;
// 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 );',
// Perform string replacements for startup.js
$pairs = [
- '$VARS.configuration' => ResourceLoader::encodeJsonForScript(
+ '$VARS.configuration' => $context->encodeJson(
$this->getConfigSettings( $context )
),
// Raw JavaScript code (not JSON)
*/
public function getScript( ResourceLoaderContext $context ) {
return 'mw.user.options.set('
- . ResourceLoader::encodeJsonForScript( User::getDefaultOptions() )
+ . $context->encodeJson( User::getDefaultOptions() )
. ');';
}
}
// 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 )
)
. ');';
// 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 ) )
. ');';
}
--- /dev/null
+ALTER TABLE /*_*/archive
+ ALTER COLUMN ar_comment SET DEFAULT '',
+ ADD COLUMN ar_comment_id bigint unsigned NOT NULL DEFAULT 0 AFTER ar_comment;
--
-- 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,
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;
--- /dev/null
+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;
--- /dev/null
+ALTER TABLE /*_*/image
+ ALTER COLUMN img_description SET DEFAULT '';
--- /dev/null
+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);
--- /dev/null
+ALTER TABLE /*_*/ipblocks
+ ALTER COLUMN ipb_reason SET DEFAULT '',
+ ADD COLUMN ipb_reason_id bigint unsigned NOT NULL DEFAULT 0 AFTER ipb_reason;
--- /dev/null
+ALTER TABLE /*_*/logging
+ ADD COLUMN log_comment_id bigint unsigned NOT NULL DEFAULT 0 AFTER log_comment;
--- /dev/null
+ALTER TABLE /*_*/oldimage
+ ALTER COLUMN oi_description SET DEFAULT '',
+ ADD COLUMN oi_description_id bigint unsigned NOT NULL DEFAULT 0 AFTER oi_description;
--- /dev/null
+ALTER TABLE /*_*/protected_titles
+ ALTER COLUMN pt_reason SET DEFAULT '',
+ ADD COLUMN pt_reason_id bigint unsigned NOT NULL DEFAULT 0 AFTER pt_reason;
--- /dev/null
+ALTER TABLE /*_*/recentchanges
+ ADD COLUMN rc_comment_id bigint unsigned NOT NULL DEFAULT 0 AFTER rc_comment;
--- /dev/null
+ALTER TABLE /*_*/revision
+ ALTER COLUMN rev_comment SET DEFAULT '';
--- /dev/null
+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);
--
-- 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 (
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);
--- /dev/null
+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);
--- /dev/null
+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);
--- /dev/null
+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;
+
+++ /dev/null
---
--- 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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+ALTER TABLE /*_*/logging
+ ADD COLUMN log_comment_id bigint unsigned NOT NULL DEFAULT 0;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+ALTER TABLE /*_*/recentchanges
+ ADD COLUMN rc_comment_id bigint unsigned NOT NULL DEFAULT 0;
--- /dev/null
+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;
// 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;
}
/** @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 ) {
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" );
+++ /dev/null
-<?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;
* @ingroup Maintenance ExternalStorage
*/
+use MediaWiki\Storage\SqlBlobStore;
use Wikimedia\Rdbms\IMaintainableDatabase;
use MediaWiki\Logger\LegacyLogger;
use MediaWiki\MediaWikiServices;
public $debugLog, $infoLog, $criticalLog;
/** @var ExternalStoreDB */
public $store;
+ /** @var SqlBlobStore */
+ private $blobStore;
private static $optionsWithArgs = [
'procs',
$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 ) {
}
$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;
);
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;
],
],
'mediawiki.page.watch.ajax' => [
+ 'targets' => [ 'desktop', 'mobile' ],
'scripts' => 'resources/src/mediawiki.page.watch.ajax.js',
'dependencies' => [
'mediawiki.api',
'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
$db->_originalTablePrefix = $oldPrefix;
$lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
- $lb->setTempTablesOnlyMode( self::$useTemporaryTables, $lb->getLocalDomainID() );
+ $lb->setTempTablesOnlyMode( self::$useTemporaryTables, $db->getDomainID() );
}
return true;
+++ /dev/null
-<?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 ];
- }
-
-}
+++ /dev/null
-<?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 ];
- }
-
-}
+++ /dev/null
-<?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;
- }
-
-}
],
]
];
- 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() {
],
]
];
- 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() {
'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' => [],
- ]
- ];
}
/**
$this->getHashWANObjectCache(),
$this->getMockCommentStore(),
ActorMigration::newMigration(),
- MIGRATION_OLD,
+ MIGRATION_NEW,
new NullLogger(),
true
);
yield [ true ];
yield [ false ];
yield [ 'somewiki' ];
- yield [ 'somewiki', MIGRATION_OLD , false ];
- yield [ 'somewiki', MIGRATION_NEW , true ];
}
/**
*/
public function testGetRevisionStore(
$dbDomain,
- $mcrMigrationStage = MIGRATION_OLD,
+ $mcrMigrationStage = MIGRATION_NEW,
$contentHandlerUseDb = true
) {
$lbFactory = $this->getMockLoadBalancerFactory();
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
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 ],
$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 ],
+++ /dev/null
-<?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' ]
- ];
- }
-
-}
+++ /dev/null
-<?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' ]
- ];
- }
-}
$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] );
}
$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] );
}
$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] );
$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] );
$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'
);
$redirectTarget->getFullText(),
'Redirect seen in hook was not the expected title'
);
- $this->assertEquals(
- null,
+ $this->assertNull(
$options->getRedirectTarget(),
'ParserOptions\' redirectTarget was changed'
);
$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() );
}
$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 );
$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(
$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)." );
}
/**
"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)." );
$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(
$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;
);
$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(
$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 );
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() );
+ }
+ }
}
+++ /dev/null
-<?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',
- ],
- ];
- }
-
-}
+++ /dev/null
-<?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 );
- }
-
-}
+++ /dev/null
-<?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() ) );
- }
-
-}
*/
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() {
// 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
$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() );
* @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();
* @dataProvider provideRegistrations
*/
public function testRegistrationsUnminified( $modules ) {
- $context = $this->getResourceLoaderContext();
+ $context = $this->getResourceLoaderContext( [
+ 'debug' => 'true',
+ ] );
$rl = $context->getResourceLoader();
$rl->register( $modules );
$module = new ResourceLoaderStartUpModule();
'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'] )
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
* @covers ResourceLoader::makeLoaderRegisterScript
*/
public function testMakeLoaderRegisterScript() {
+ $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest( [
+ 'debug' => 'true',
+ ] ) );
$this->assertEquals(
'mw.loader.register([
[
"1234567"
]
]);',
- ResourceLoader::makeLoaderRegisterScript( [
+ ResourceLoader::makeLoaderRegisterScript( $context, [
[ 'test.name', '1234567' ],
] ),
'Nested array parameter'
"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 ],
* @covers ResourceLoader::makeLoaderSourcesScript
*/
public function testMakeLoaderSourcesScript() {
+ $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest( [
+ 'debug' => 'true',
+ ] ) );
$this->assertEquals(
'mw.loader.addSource({
"local": "/w/load.php"
});',
- ResourceLoader::makeLoaderSourcesScript( 'local', '/w/load.php' )
+ ResourceLoader::makeLoaderSourcesScript( $context, 'local', '/w/load.php' )
);
$this->assertEquals(
'mw.loader.addSource({
"local": "/w/load.php"
});',
- ResourceLoader::makeLoaderSourcesScript( [ 'local' => '/w/load.php' ] )
+ ResourceLoader::makeLoaderSourcesScript( $context, [ 'local' => '/w/load.php' ] )
);
$this->assertEquals(
'mw.loader.addSource({
"local": "/w/load.php",
"example": "https://example.org/w/load.php"
});',
- ResourceLoader::makeLoaderSourcesScript( [
+ ResourceLoader::makeLoaderSourcesScript( $context, [
'local' => '/w/load.php',
'example' => 'https://example.org/w/load.php'
] )
);
$this->assertEquals(
'mw.loader.addSource([]);',
- ResourceLoader::makeLoaderSourcesScript( [] )
+ ResourceLoader::makeLoaderSourcesScript( $context, [] )
);
}
] );
$request = new \FauxRequest();
- $this->assertEquals( null, $provider->suggestLoginUsername( $request ) );
+ $this->assertNull( $provider->suggestLoginUsername( $request ) );
$request->setCookies( [
'xUserName' => 'Example',
'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',
* @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(
$group->getFilter( 'foo' )->getName()
);
- $this->assertEquals(
- null,
+ $this->assertNull(
$group->getFilter( 'bar' )
);
}
$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 ) );
}
/**
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 ) );
}
/**
$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 );
$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( [