-The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Code_of_Conduct).
+The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Special:MyLanguage/Code_of_Conduct).
This action should be considered deprecated and should not be used directly.
* Extensions overriding ContentHandler::getUndoContent() will need to be
updated for the changed method signature.
+* Added a new hook, 'UserGetRightsRemove', which can be used to remove rights
+ from user. Unlike the 'UserGetRights' it will ensure that removed rights
+ will not be reinserted.
=== External library changes in 1.32 ===
* The '--tidy' option to maintenance/parse.php has been removed. Tidying
the output is now the default. Use '--no-tidy' to bypass the tidy
phase.
+* The global function wfErrorLog, deprecated since 1.25, has now been removed.
+ Use MWLoggerLegacyLogger::emit or UDPTransport.
=== Deprecations in 1.32 ===
* HTMLForm::setSubmitProgressive() is deprecated. No need to call it. Submit
* QuickTemplate::msgHtml() and BaseTemplate::msgHtml() have been deprecated
as they promote bad practises. I18n messages should always be properly
escaped.
+* Skin::getDynamicStylesheetQuery() has been deprecated. It always
+ returns action=raw&ctype=text/css which callers should use directly.
+* Class LegacyFormatter is deprecated.
=== Other changes in 1.32 ===
* (T198811) The following tables have had their UNIQUE indexes turned into
"wmde/hamcrest-html-matchers": "^0.1.0"
},
"suggest": {
- "ext-apc": "Local data and opcode cache",
+ "ext-apcu": "Local data cache for greatly improved performance",
"ext-curl": "Improved http communication abilities",
"ext-fileinfo": "Improved mime magic detection",
"ext-intl": "ICU integration",
$user: User to get rights for
&$rights: Current rights
+'UserGetRightsRemove': Called in User::getRights(). This hook override
+the UserGetRights hook. It can be used to remove rights from user
+and ensure that will not be reinserted by the other hook callbacks
+therefore this hook should not be used to add any rights, use UserGetRights instead.
+$user: User to get rights for
+&$rights: Current rights
+
'UserGroupsChanged': Called after user groups are changed.
$user: User whose groups changed
$added: Groups added
*/
$wgUseRCPatrol = true;
-/**
- * Whether a preference is displayed for structured change filters.
- * If false, no preference is displayed and structured change filters are disabled.
- * If true, structured change filters are *enabled* by default, and a preference is displayed
- * that lets users disable them.
- *
- * Temporary variable during development and will be removed.
- *
- * @since 1.30
- */
-$wgStructuredChangeFiltersShowPreference = false;
-
-/**
- * Whether a preference is displayed for structured change filters on watchlist.
- * Works just like $wgStructuredChangeFiltersShowPreference.
- *
- * Temporary variable during development and will be removed
- * @since 1.32
- */
-$wgStructuredChangeFiltersShowWatchlistPreference = false;
-
-/**
- * Whether to enable RCFilters app on Special:Watchlist
- *
- * Temporary variable during development and will be removed.
- */
-$wgStructuredChangeFiltersOnWatchlist = false;
-
/**
* Polling rate, in seconds, used by the 'live update' and 'view newest' features
* of the RCFilters app on SpecialRecentChanges and Special:Watchlist.
*
* @param string|null|bool $text Text to unserialize
* @return Content|bool|null The content object created from $text. If $text was false
- * or null, false resp. null will be returned instead.
+ * or null, then false or null will be returned instead.
*
* @throws MWException If unserializing the text results in a Content
* object that is not an instance of TextContent and
MWDebug::warning( $msg, $callerOffset + 1, $level, 'production' );
}
-/**
- * Log to a file without getting "file size exceeded" signals.
- *
- * Can also log to TCP or UDP with the syntax udp://host:port/prefix. This will
- * send lines to the specified port, prefixed by the specified prefix and a space.
- * @since 1.25 support for additional context data
- *
- * @param string $text
- * @param string $file Filename
- * @param array $context Additional logging context data
- * @throws MWException
- * @deprecated since 1.25 Use \MediaWiki\Logger\LegacyLogger::emit or UDPTransport
- */
-function wfErrorLog( $text, $file, array $context = [] ) {
- wfDeprecated( __METHOD__, '1.25' );
- $logger = LoggerFactory::getInstance( 'wfErrorLog' );
- $context['destination'] = $file;
- $logger->info( trim( $text ), $context );
-}
-
/**
* @todo document
* @todo Move logic to MediaWiki.php
*/
protected function consumeNoFork() {
while ( !feof( $this->input ) ) {
- $line = trim( fgets( $this->input ) );
- if ( $line ) {
- $result = call_user_func( $this->workCallback, $line );
+ $data = fgets( $this->input );
+ if ( $data[ strlen( $data ) - 1 ] == "\n" ) {
+ $data = substr( $data, 0, -1 );
+ }
+ if ( strlen( $data ) !== 0 ) {
+ $result = call_user_func( $this->workCallback, $data );
fwrite( $this->output, "$result\n" );
}
}
$this->updateAvailableSockets( $sockets, $used, $sockets ? 0 : 5 );
} while ( !$sockets );
}
- $data = trim( $data );
- if ( !$data ) {
+ // Strip the trailing \n. The last line of a file might not have a trailing
+ // \n though
+ if ( $data[ strlen( $data ) - 1 ] == "\n" ) {
+ $data = substr( $data, 0, -1 );
+ }
+ if ( strlen( $data ) === 0 ) {
continue;
}
$socket = array_pop( $sockets );
// Also test DjVu
$deja = new DjVuImage( $file );
if ( $deja->isValid() ) {
- $logger->info( __METHOD__ . ": detected $file as image/vnd.djvu\n" );
+ $logger->info( "Detected $file as image/vnd.djvu\n" );
$mime = 'image/vnd.djvu';
return;
*/
public static function numberingroup( $group ) {
$cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+ $fname = __METHOD__;
return $cache->getWithSetCallback(
$cache->makeKey( 'SiteStats', 'groupcounts', $group ),
$cache::TTL_HOUR,
- function ( $oldValue, &$ttl, array &$setOpts ) use ( $group ) {
+ function ( $oldValue, &$ttl, array &$setOpts ) use ( $group, $fname ) {
$dbr = self::getLB()->getConnection( DB_REPLICA );
$setOpts += Database::getCacheSetOptions( $dbr );
'ug_group' => $group,
'ug_expiry IS NULL OR ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() )
],
- __METHOD__
+ $fname
);
},
[ 'pcTTL' => $cache::TTL_PROC_LONG ]
*/
public static function pagesInNs( $ns ) {
$cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+ $fname = __METHOD__;
return $cache->getWithSetCallback(
$cache->makeKey( 'SiteStats', 'page-in-namespace', $ns ),
$cache::TTL_HOUR,
- function ( $oldValue, &$ttl, array &$setOpts ) use ( $ns ) {
+ function ( $oldValue, &$ttl, array &$setOpts ) use ( $ns, $fname ) {
$dbr = self::getLB()->getConnection( DB_REPLICA );
$setOpts += Database::getCacheSetOptions( $dbr );
'page',
'COUNT(*)',
[ 'page_namespace' => $ns ],
- __METHOD__
+ $fname
);
},
[ 'pcTTL' => $cache::TTL_PROC_LONG ]
if ( array_key_exists( $id, $table ) ) {
return $table[$id];
}
+ $fname = __METHOD__;
$table = $this->cache->getWithSetCallback(
$this->getCacheKey(),
$this->cacheTTL,
- function ( $oldValue, &$ttl, &$setOpts ) use ( $id ) {
+ function ( $oldValue, &$ttl, &$setOpts ) use ( $id, $fname ) {
// Check if cached value is up-to-date enough to have $id
if ( is_array( $oldValue ) && array_key_exists( $id, $oldValue ) ) {
// Completely leave the cache key alone
// Log a fallback to master
if ( $source === DB_MASTER ) {
$this->logger->info(
- __METHOD__ . 'falling back to master select from ' .
+ $fname . ' falling back to master select from ' .
$this->table . ' with id ' . $id
);
}
}
/**
- * Get the default message text or false if the message doesn't exist
+ * Get the default (plain) message contents for an page that overrides an
+ * interface message key.
+ *
+ * Primary use cases:
+ *
+ * - Article:
+ * - Show default when viewing the page. The Article::getSubstituteContent
+ * method displays the default message content, instead of the
+ * 'noarticletext' placeholder message normally used.
+ *
+ * - EditPage:
+ * - Title of edit page. When creating an interface message override,
+ * the editor is told they are "Editing the page", instead of
+ * "Creating the page". (EditPage::setHeaders)
+ * - Edit notice. The 'translateinterface' edit notice is shown when creating
+ * or editing a an interface message override. (EditPage::showIntro)
+ * - Opening the editor. The contents of the localisation message are used
+ * as contents of the editor when creating a new page in the MediaWiki
+ * namespace. This simplifies the process for editors when "changing"
+ * an interface message by creating an override. (EditPage::getContentObject)
+ * - Showing a diff. The left-hand side of a diff when an editor is
+ * previewing their changes before saving the creation of a page in the
+ * MediaWiki namespace. (EditPage::showDiff)
+ * - Disallowing a save. When attempting to create a a MediaWiki-namespace
+ * page with the proposed content matching the interface message default,
+ * the save is rejected, the same way we disallow blank pages from being
+ * created. (EditPage::internalAttemptSave)
+ *
+ * - ApiEditPage:
+ * - Default content, when using the 'prepend' or 'append' feature.
+ *
+ * - SkinTemplate:
+ * - Label the create action as "Edit", if the page can be an override.
*
* @return string|bool
*/
$title = $this->getTitle();
$request = $this->getRequest();
- // If it's a MediaWiki message we can just hit the message cache
- if ( $request->getBool( 'usemsgcache' ) && $title->getNamespace() == NS_MEDIAWIKI ) {
- // The first "true" is to use the database, the second is to use
- // the content langue and the last one is to specify the message
- // key already contains the language in it ("/de", etc.).
- $text = MessageCache::singleton()->get( $title->getDBkey(), true, true, true );
- // If the message doesn't exist, return a blank
- if ( $text === false ) {
- $text = '';
- }
- } else {
- // Get it from the DB
- $rev = Revision::newFromTitle( $title, $this->getOldId() );
- if ( $rev ) {
- $lastmod = wfTimestamp( TS_RFC2822, $rev->getTimestamp() );
- $request->response()->header( "Last-modified: $lastmod" );
+ // Get it from the DB
+ $rev = Revision::newFromTitle( $title, $this->getOldId() );
+ if ( $rev ) {
+ $lastmod = wfTimestamp( TS_RFC2822, $rev->getTimestamp() );
+ $request->response()->header( "Last-modified: $lastmod" );
- // Public-only due to cache headers
- $content = $rev->getContent();
+ // Public-only due to cache headers
+ $content = $rev->getContent();
- if ( $content === null ) {
- // revision not found (or suppressed)
+ if ( $content === null ) {
+ // revision not found (or suppressed)
+ $text = false;
+ } elseif ( !$content instanceof TextContent ) {
+ // non-text content
+ wfHttpError( 415, "Unsupported Media Type", "The requested page uses the content model `"
+ . $content->getModel() . "` which is not supported via this interface." );
+ die();
+ } else {
+ // want a section?
+ $section = $request->getIntOrNull( 'section' );
+ if ( $section !== null ) {
+ $content = $content->getSection( $section );
+ }
+
+ if ( $content === null || $content === false ) {
+ // section not found (or section not supported, e.g. for JS, JSON, and CSS)
$text = false;
- } elseif ( !$content instanceof TextContent ) {
- // non-text content
- wfHttpError( 415, "Unsupported Media Type", "The requested page uses the content model `"
- . $content->getModel() . "` which is not supported via this interface." );
- die();
} else {
- // want a section?
- $section = $request->getIntOrNull( 'section' );
- if ( $section !== null ) {
- $content = $content->getSection( $section );
- }
-
- if ( $content === null || $content === false ) {
- // section not found (or section not supported, e.g. for JS, JSON, and CSS)
- $text = false;
- } else {
- $text = $content->getNativeData();
- }
+ $text = $content->getNativeData();
}
}
}
$title = $page->getTitle();
$key = self::getStashKey( $title, self::getContentHash( $content ), $user );
+ $fname = __METHOD__;
// Use the master DB to allow for fast blocking locks on the "save path" where this
// value might actually be used to complete a page edit. If the edit submission request
// need to duplicate parsing of the same content/user/summary bundle, so try to avoid
// blocking at all here.
$dbw = wfGetDB( DB_MASTER );
- if ( !$dbw->lock( $key, __METHOD__, 0 ) ) {
+ if ( !$dbw->lock( $key, $fname, 0 ) ) {
// De-duplicate requests on the same key
return self::ERROR_BUSY;
}
/** @noinspection PhpUnusedLocalVariableInspection */
- $unlocker = new ScopedCallback( function () use ( $dbw, $key ) {
- $dbw->unlock( $key, __METHOD__ );
+ $unlocker = new ScopedCallback( function () use ( $dbw, $key, $fname ) {
+ $dbw->unlock( $key, $fname );
} );
$cutoffTime = time() - self::PRESUME_FRESH_TTL_SEC;
// @codeCoverageIgnoreStart
if ( $this->getPasswordFactory()->needsUpdate( $pwhash ) ) {
$newHash = $this->getPasswordFactory()->newFromPlaintext( $req->password );
- \DeferredUpdates::addCallableUpdate( function () use ( $newHash, $oldRow ) {
+ $fname = __METHOD__;
+ \DeferredUpdates::addCallableUpdate( function () use ( $newHash, $oldRow, $fname ) {
$dbw = wfGetDB( DB_MASTER );
$dbw->update(
'user',
'user_id' => $oldRow->user_id,
'user_password' => $oldRow->user_password
],
- __METHOD__
+ $fname
);
} );
}
// Ignore $wgMaxMsgCacheEntrySize so the process cache is up to date
$this->cache->setField( $code, $title, ' ' . $text );
}
+ $fname = __METHOD__;
// (b) Update the shared caches in a deferred update with a fresh DB snapshot
DeferredUpdates::addCallableUpdate(
- function () use ( $title, $msg, $code ) {
+ function () use ( $title, $msg, $code, $fname ) {
global $wgMaxMsgCacheEntrySize;
// Allow one caller at a time to avoid race conditions
$scopedLock = $this->getReentrantScopedLock(
);
if ( !$scopedLock ) {
LoggerFactory::getInstance( 'MessageCache' )->error(
- __METHOD__ . ': could not acquire lock to update {title} ({code})',
+ $fname . ': could not acquire lock to update {title} ({code})',
[ 'title' => $title, 'code' => $code ] );
return;
}
* @return string Either " <MESSAGE>" or "!NONEXISTANT"
*/
private function loadCachedMessagePageEntry( $dbKey, $code, $hash ) {
+ $fname = __METHOD__;
return $this->srvCache->getWithSetCallback(
$this->srvCache->makeKey( 'messages-big', $hash, $dbKey ),
IExpiringStore::TTL_MINUTE,
- function () use ( $code, $dbKey, $hash ) {
+ function () use ( $code, $dbKey, $hash, $fname ) {
return $this->wanCache->getWithSetCallback(
$this->bigMessageCacheKey( $hash, $dbKey ),
$this->mExpiry,
- function ( $oldValue, &$ttl, &$setOpts ) use ( $dbKey, $code ) {
+ function ( $oldValue, &$ttl, &$setOpts ) use ( $dbKey, $code, $fname ) {
// Try loading the message from the database
$dbr = wfGetDB( DB_REPLICA );
$setOpts += Database::getCacheSetOptions( $dbr );
$message = $this->getMessageTextFromContent( $content );
} else {
LoggerFactory::getInstance( 'MessageCache' )->warning(
- __METHOD__ . ': failed to load page text for \'{titleKey}\'',
+ $fname . ': failed to load page text for \'{titleKey}\'',
[ 'titleKey' => $dbKey, 'code' => $code ]
);
$message = null;
) {
return false;
}
- $attribs = wfArrayFilterByKey( $attribs, [ Sanitizer::class, 'isReservedDataAttribute' ] );
+ $attribs = array_filter( $attribs,
+ [ Sanitizer::class, 'isReservedDataAttribute' ],
+ ARRAY_FILTER_USE_KEY
+ );
$dateheader = ''; // $html now contains only <li>...</li>, for hooks' convenience.
$this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
$this->openingText = $this->extractHeadingBeforeFirstHeading( $text );
- // Add extra spacing around break tags so text crammed together like<br>this
- // doesn't make one word.
- $text = str_replace( '<br', "\n<br", $text );
-
$formatter = new HtmlFormatter( $text );
// Strip elements from the page that we never want in the search text.
use UDPTransport;
/**
- * PSR-3 logger that mimics the historic implementation of MediaWiki's
+ * PSR-3 logger that mimics the historic implementation of MediaWiki's former
* wfErrorLog logging implementation.
*
* This logger is configured by the following global configuration variables:
// specfied.
$shouldEmit = (bool)$wgDBerrorLog;
- } elseif ( $channel === 'wfErrorLog' ) {
- // All messages on the wfErrorLog channel should be emitted.
- $shouldEmit = true;
-
} elseif ( $channel === 'wfDebug' ) {
// wfDebug messages are emitted if a catch all logging file has
// been specified. Checked explicitly so that 'private' flagged
/**
* Format a message.
*
- * Messages to the 'wfDebug', 'wfLogDBError' and 'wfErrorLog' channels
- * receive special formatting to mimic the historic output of the functions
- * of the same name. All other channel values are formatted based on the
- * historic output of the `wfDebugLog()` global function.
+ * Messages to the 'wfDebug' and 'wfLogDBError' channels receive special formatting to mimic the
+ * historic output of the functions of the same name. All other channel values are formatted
+ * based on the historic output of the `wfDebugLog()` global function.
*
* @param string $channel
* @param string $message
} elseif ( $channel === 'wfLogDBError' ) {
$text = self::formatAsWfLogDBError( $channel, $message, $context );
- } elseif ( $channel === 'wfErrorLog' ) {
- $text = "{$message}\n";
-
} elseif ( $channel === 'profileoutput' ) {
// Legacy wfLogProfilingData formatitng
$forward = '';
use Monolog\Formatter\NormalizerFormatter;
/**
- * Log message formatter that mimics the legacy log message formatting of
- * `wfDebug`, `wfDebugLog`, `wfLogDBError` and `wfErrorLog` global functions by
- * delegating the formatting to \MediaWiki\Logger\LegacyLogger.
+ * Log message formatter that mimics the legacy log message formatting of `wfDebug`, `wfDebugLog`,
+ * `wfLogDBError` and the former `wfErrorLog` global functions by delegating the formatting to
+ * \MediaWiki\Logger\LegacyLogger.
*
+ * @deprecated since 1.32
* @since 1.25
* @copyright © 2013 Wikimedia Foundation and contributors
* @see \MediaWiki\Logger\LegacyLogger
use UnexpectedValueException;
/**
- * Log handler that replicates the behavior of MediaWiki's wfErrorLog()
+ * Log handler that replicates the behavior of MediaWiki's former wfErrorLog()
* logging service. Log output can be directed to a local file, a PHP stream,
* or a udp2log server.
*
global $wgSitename;
$args = array_slice( func_get_args(), 2 );
+ // FIXME: Keep logic in sync with MWExceptionRenderer::msg.
$res = false;
if ( $this->useMessageCache() ) {
try {
* @var string $reservedMemory
*/
protected static $reservedMemory;
+
/**
+ * Error types that, if unhandled, are fatal to the request.
+ *
+ * On PHP 7, these error types may be thrown as Error objects, which
+ * implement Throwable (but not Exception).
+ *
+ * On HHVM, these invoke the set_error_handler callback, similar to how
+ * (non-fatal) warnings and notices are reported, except that after this
+ * handler runs for fatal error tpyes, script execution stops!
+ *
+ * The user will be shown an HTTP 500 Internal Server Error.
+ * As such, these should be sent to MediaWiki's "fatal" or "exception"
+ * channel. Normally, the error handler logs them to the "error" channel.
+ *
* @var array $fatalErrorTypes
*/
protected static $fatalErrorTypes = [
- E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR,
- /* HHVM's FATAL_ERROR level */ 16777217,
+ E_ERROR,
+ E_PARSE,
+ E_CORE_ERROR,
+ E_COMPILE_ERROR,
+ E_USER_ERROR,
+
+ // E.g. "Catchable fatal error: Argument X must be Y, null given"
+ E_RECOVERABLE_ERROR,
+
+ // HHVM's FATAL_ERROR constant
+ 16777217,
];
/**
* @var bool $handledFatalCallback
// behaviour given the null was not part of the code and is likely not
// accounted for.
switch ( $level ) {
- case E_RECOVERABLE_ERROR:
- $levelName = 'Error';
- $severity = LogLevel::ERROR;
- break;
case E_WARNING:
case E_CORE_WARNING:
case E_COMPILE_WARNING:
self::logError( $e, 'error', $severity );
// If $wgPropagateErrors is true return false so PHP shows/logs the error normally.
- // Ignore $wgPropagateErrors if the error should break execution, or track_errors is set
+ // Ignore $wgPropagateErrors if track_errors is set
// (which means someone is counting on regular PHP error handling behavior).
- return !( $wgPropagateErrors || $level == E_RECOVERABLE_ERROR || ini_get( 'track_errors' ) );
+ return !( $wgPropagateErrors || ini_get( 'track_errors' ) );
}
/**
private static function msg( $key, $fallback /*[, params...] */ ) {
global $wgSitename;
$args = array_slice( func_get_args(), 2 );
+
+ // FIXME: Keep logic in sync with MWException::msg.
try {
$res = wfMessage( $key, $args )->text();
} catch ( Exception $e ) {
*/
public function getDescriptionStylesheetUrl() {
if ( isset( $this->scriptDirUrl ) ) {
- return $this->makeUrl( 'title=MediaWiki:Filepage.css&' .
- wfArrayToCgi( Skin::getDynamicStylesheetQuery() ) );
+ // Must match canonical query parameter order for optimum caching
+ // See Title::getCdnUrls
+ return $this->makeUrl( 'title=MediaWiki:Filepage.css&action=raw&ctype=text/css' );
}
return false;
$lang->getCode(),
md5( $this->getName() )
);
+ $fname = __METHOD__;
return $cache->getWithSetCallback(
$key,
$this->repo->descriptionCacheExpiry ?: $cache::TTL_UNCACHEABLE,
- function ( $oldValue, &$ttl, array &$setOpts ) use ( $renderUrl ) {
+ function ( $oldValue, &$ttl, array &$setOpts ) use ( $renderUrl, $fname ) {
wfDebug( "Fetching shared description from $renderUrl\n" );
- $res = Http::get( $renderUrl, [], __METHOD__ );
+ $res = Http::get( $renderUrl, [], $fname );
if ( !$res ) {
$ttl = WANObjectCache::TTL_UNCACHEABLE;
}
}
$cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+ $fname = __METHOD__;
return $cache->getWithSetCallback(
$this->repo->getLocalCacheKey(
$touched
),
$this->repo->descriptionCacheExpiry ?: $cache::TTL_UNCACHEABLE,
- function ( $oldValue, &$ttl, array &$setOpts ) use ( $renderUrl ) {
+ function ( $oldValue, &$ttl, array &$setOpts ) use ( $renderUrl, $fname ) {
wfDebug( "Fetching shared description from $renderUrl\n" );
- $res = Http::get( $renderUrl, [], __METHOD__ );
+ $res = Http::get( $renderUrl, [], $fname );
if ( !$res ) {
$ttl = WANObjectCache::TTL_UNCACHEABLE;
}
# Defer purges, page creation, and link updates in case they error out.
# The most important thing is that files and the DB registry stay synced.
$dbw->endAtomic( __METHOD__ );
+ $fname = __METHOD__;
# Do some cache purges after final commit so that:
# a) Changes are more likely to be seen post-purge
__METHOD__,
function () use (
$reupload, $wikiPage, $newPageContent, $comment, $user,
- $logEntry, $logId, $descId, $tags
+ $logEntry, $logId, $descId, $tags, $fname
) {
# Update memcache after the commit
$this->invalidateCache();
'logging',
$update,
[ 'log_id' => $logId ],
- __METHOD__
+ $fname
);
$this->getRepo()->getMasterDB()->insert(
'log_search',
'ls_value' => $logEntry->getAssociatedRevId(),
'ls_log_id' => $logId,
],
- __METHOD__
+ $fname
);
# Add change tags, if any
return parent::getRaw( $value );
}
+ /**
+ * @param mixed $value
+ * @return OOUI\FieldLayout
+ * @since 1.32
+ */
+ public function getOOUI( $value ) {
+ if ( !empty( $this->mParams['rawrow'] ) ) {
+ if ( !( $value instanceof OOUI\FieldLayout ) ) {
+ throw new Exception( "'default' must be a FieldLayout or subclass when using 'rawrow'" );
+ }
+ return $value;
+ }
+
+ return parent::getOOUI( $value );
+ }
+
protected function needsLabel() {
return false;
}
"config-install-mainpage-failed": "Could not insert main page: $1",
"config-install-done": "<strong>Congratulations!</strong>\nYou have installed MediaWiki.\n\nThe installer has generated a <code>LocalSettings.php</code> file.\nIt contains all your configuration.\n\nYou will need to download it and put it in the base of your wiki installation (the same directory as index.php). The download should have started automatically.\n\nIf the download was not offered, or if you cancelled it, you can restart the download by clicking the link below:\n\n$3\n\n<strong>Note:</strong> If you do not do this now, this generated configuration file will not be available to you later if you exit the installation without downloading it.\n\nWhen that has been done, you can <strong>[$2 enter your wiki]</strong>.",
"config-install-done-path": "<strong>Congratulations!</strong>\nYou have installed MediaWiki.\n\nThe installer has generated a <code>LocalSettings.php</code> file.\nIt contains all your configuration.\n\nYou will need to download it and put it at <code>$4</code>. The download should have started automatically.\n\nIf the download was not offered, or if you cancelled it, you can restart the download by clicking the link below:\n\n$3\n\n<strong>Note:</strong> If you do not do this now, this generated configuration file will not be available to you later if you exit the installation without downloading it.\n\nWhen that has been done, you can <strong>[$2 enter your wiki]</strong>.",
- "config-install-success": "MediaWiki has been successfully installed. You can now\nvisit <$1$2> to view your wiki.\nIf you have questions, check out our frequently asked questions list:\n<https://www.mediawiki.org/wiki/Manual:FAQ> or use one of the\nsupport forums linked on that page.",
+ "config-install-success": "MediaWiki has been successfully installed. You can now visit <$1$2> to view your wiki.\nIf you have questions, check out our frequently asked questions list:\n<https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ> or use one of the\nsupport forums linked on that page.",
"config-download-localsettings": "Download <code>LocalSettings.php</code>",
"config-help": "help",
"config-help-tooltip": "click to expand",
"Jdforrester",
"Liuxinyu970226",
"Metalhead64",
- "Tacsipacsi"
+ "Tacsipacsi",
+ "Zoranzoki21"
]
},
"config-desc": "Short description of the installer.",
"config-unknown-collation": "Warning messages in the MediaWiki installer for the database type MySQL when an unrecognised collation is used.",
"config-db-web-account": "Fieldset legend in MediaWiki installer",
"config-db-web-help": "Help text in MediaWiki installer.",
- "config-db-web-account-same": "checkbox label",
- "config-db-web-create": "checkbox label",
+ "config-db-web-account-same": "Checkbox label about setting of database account for web access.",
+ "config-db-web-create": "Checkbox label about creating of new database account.",
"config-db-web-no-create-privs": "Error message in the MediaWiki installer.",
"config-mysql-engine": "Field label for MySQL storage engine in the MediaWiki installer.",
"config-mysql-innodb": "Option for the MySQL storage engine in the MediaWiki installer.",
}
}
+ $fname = __METHOD__;
$iwData = $this->objectCache->getWithSetCallback(
$this->objectCache->makeKey( 'interwiki', $prefix ),
$this->objectCacheExpiry,
- function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix ) {
+ function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix, $fname ) {
$dbr = wfGetDB( DB_REPLICA ); // TODO: inject LoadBalancer
$setOpts += Database::getCacheSetOptions( $dbr );
'interwiki',
self::selectFields(),
[ 'iw_prefix' => $prefix ],
- __METHOD__
+ $fname
);
return $row ? (array)$row : '!NONEXISTENT';
$this->debugCallback( $msg );
// Wait for an exclusive lock to commit
- if ( !$dbwSerial->lock( 'jobrunner-serial-commit', __METHOD__, 30 ) ) {
+ if ( !$dbwSerial->lock( 'jobrunner-serial-commit', $fnameTrxOwner, 30 ) ) {
// This will trigger a rollback in the main loop
throw new DBError( $dbwSerial, "Timed out waiting on commit queue." );
}
- $unlocker = new ScopedCallback( function () use ( $dbwSerial ) {
- $dbwSerial->unlock( 'jobrunner-serial-commit', __METHOD__ );
+ $unlocker = new ScopedCallback( function () use ( $dbwSerial, $fnameTrxOwner ) {
+ $dbwSerial->unlock( 'jobrunner-serial-commit', $fnameTrxOwner );
} );
// Wait for the replica DBs to catch up
if ( $dbkeys === [] ) {
return;
}
+ $fname = __METHOD__;
DeferredUpdates::addUpdate( new AutoCommitUpdate(
$dbw,
__METHOD__,
- function () use ( $dbw, $namespace, $dbkeys ) {
+ function () use ( $dbw, $namespace, $dbkeys, $fname ) {
$services = MediaWikiServices::getInstance();
$lbFactory = $services->getDBLoadBalancerFactory();
// Determine which pages need to be updated.
'page_title' => $dbkeys,
'page_touched < ' . $dbw->addQuotes( $now )
],
- __METHOD__
+ $fname
);
if ( !$ids ) {
}
$batchSize = $services->getMainConfig()->get( 'UpdateRowsPerQuery' );
- $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+ $ticket = $lbFactory->getEmptyTransactionTicket( $fname );
foreach ( array_chunk( $ids, $batchSize ) as $idBatch ) {
$dbw->update(
'page',
'page_id' => $idBatch,
'page_touched < ' . $dbw->addQuotes( $now ) // handle races
],
- __METHOD__
+ $fname
);
- $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
+ $lbFactory->commitAndWaitForReplication( $fname, $ticket );
}
}
) );
abstract protected function doQuery( $sql );
/**
- * Determine whether a query writes to the DB.
- * Should return true if unsure.
+ * Determine whether a query writes to the DB. When in doubt, this returns true.
+ *
+ * Main use cases:
+ *
+ * - Subsequent web requests should not need to wait for replication from
+ * the master position seen by this web request, unless this request made
+ * changes to the master. This is handled by ChronologyProtector by checking
+ * doneWrites() at the end of the request. doneWrites() returns true if any
+ * query set lastWriteTime; which query() does based on isWriteQuery().
+ *
+ * - Reject write queries to replica DBs, in query().
*
* @param string $sql
* @return bool
*/
protected function isWriteQuery( $sql ) {
+ // BEGIN and COMMIT queries are considered read queries here.
+ // Database backends and drivers (MySQL, MariaDB, php-mysqli) generally
+ // treat these as write queries, in that their results have "affected rows"
+ // as meta data as from writes, instead of "num rows" as from reads.
+ // But, we treat them as read queries because when reading data (from
+ // either replica or master) we use transactions to enable repeatable-read
+ // snapshots, which ensures we get consistent results from the same snapshot
+ // for all queries within a request. Use cases:
+ // - Treating these as writes would trigger ChronologyProtector (see method doc).
+ // - We use this method to reject writes to replicas, but we need to allow
+ // use of transactions on replicas for read snapshots. This fine given
+ // that transactions by themselves don't make changes, only actual writes
+ // within the transaction matter, which we still detect.
return !preg_match(
'/^(?:SELECT|BEGIN|ROLLBACK|COMMIT|SET|SHOW|EXPLAIN|\(SELECT)\b/i', $sql );
}
/**
* Determine whether a SQL statement is sensitive to isolation level.
+ *
* A SQL statement is considered transactable if its result could vary
* depending on the transaction isolation level. Operational commands
* such as 'SET' and 'SHOW' are not considered to be transactable.
*
+ * Main purpose: Used by query() to decide whether to begin a transaction
+ * before the current query (in DBO_TRX mode, on by default).
+ *
* @param string $sql
* @return bool
*/
protected function isTransactableQuery( $sql ) {
return !in_array(
$this->getQueryVerb( $sql ),
- [ 'BEGIN', 'COMMIT', 'ROLLBACK', 'SHOW', 'SET', 'CREATE', 'ALTER' ],
+ [ 'BEGIN', 'ROLLBACK', 'COMMIT', 'SET', 'SHOW', 'CREATE', 'ALTER' ],
true
);
}
// Using one key for all cluster replica DBs is preferable
$this->getLBInfo( 'clusterMasterHost' ) ?: $this->getServer()
);
+ $fname = __METHOD__;
return $cache->getWithSetCallback(
$key,
$cache::TTL_INDEFINITE,
- function () use ( $cache, $key ) {
+ function () use ( $cache, $key, $fname ) {
// Get and leave a lock key in place for a short period
if ( !$cache->lock( $key, 0, 10 ) ) {
return false; // avoid master connection spike slams
// Connect to and query the master; catch errors to avoid outages
try {
- $res = $conn->query( 'SELECT @@server_id AS id', __METHOD__ );
+ $res = $conn->query( 'SELECT @@server_id AS id', $fname );
$row = $res ? $res->fetchObject() : false;
$id = $row ? (int)$row->id : 0;
} catch ( DBError $e ) {
* @throws DBQueryError If the variable doesn't exist for some reason
*/
protected function getServerId() {
+ $fname = __METHOD__;
return $this->srvCache->getWithSetCallback(
$this->srvCache->makeGlobalKey( 'mysql-server-id', $this->getServer() ),
self::SERVER_ID_CACHE_TTL,
- function () {
- $res = $this->query( "SELECT @@server_id AS id", __METHOD__ );
+ function () use ( $fname ) {
+ $res = $this->query( "SELECT @@server_id AS id", $fname );
return intval( $this->fetchObject( $res )->id );
}
);
return $this->query( "ATTACH DATABASE $file AS $name", $fname );
}
- function isWriteQuery( $sql ) {
+ protected function isWriteQuery( $sql ) {
return parent::isWriteQuery( $sql ) && !preg_match( '/^(ATTACH|PRAGMA)\b/i', $sql );
}
}
public function closeAll() {
- $this->forEachOpenConnection( function ( IDatabase $conn ) {
+ $fname = __METHOD__;
+ $this->forEachOpenConnection( function ( IDatabase $conn ) use ( $fname ) {
$host = $conn->getServer();
$this->connLogger->debug(
- __METHOD__ . ": closing connection to database '$host'." );
+ $fname . ": closing connection to database '$host'." );
$conn->close();
} );
} );
$e = null; // first exception
+ $fname = __METHOD__;
// Loop until callbacks stop adding callbacks on other connections
do {
// Run any pending callbacks for each connection...
}
);
// Clear out any active transactions left over from callbacks...
- $this->forEachOpenMasterConnection( function ( Database $conn ) use ( &$e ) {
+ $this->forEachOpenMasterConnection( function ( Database $conn ) use ( &$e, $fname ) {
if ( $conn->writesPending() ) {
// A callback from another handle wrote to this one and DBO_TRX is set
- $this->queryLogger->warning( __METHOD__ . ": found writes pending." );
+ $this->queryLogger->warning( $fname . ": found writes pending." );
$fnames = implode( ', ', $conn->pendingWriteAndCallbackCallers() );
$this->queryLogger->warning(
- __METHOD__ . ": found writes pending ($fnames).",
+ $fname . ": found writes pending ($fnames).",
[
'db_server' => $conn->getServer(),
'db_name' => $conn->getDBname()
} elseif ( $conn->trxLevel() ) {
// A callback from another handle read from this one and DBO_TRX is set,
// which can easily happen if there is only one DB (no replicas)
- $this->queryLogger->debug( __METHOD__ . ": found empty transaction." );
+ $this->queryLogger->debug( $fname . ": found empty transaction." );
}
try {
- $conn->commit( __METHOD__, $conn::FLUSHING_ALL_PEERS );
+ $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
} catch ( Exception $ex ) {
$e = $e ?: $ex;
}
$this->text .= substr( $text, $start, $length );
}
function startTag( $name, Attributes $attrs, $selfClose, $sourceStart, $sourceLength ) {
- // Do nothing.
+ // Inject whitespace for typical block-level tags to
+ // prevent merging unrelated<br>words.
+ if ( $this->isBlockLevelTag( $name ) ) {
+ $this->text .= ' ';
+ }
}
function endTag( $name, $sourceStart, $sourceLength ) {
- // Do nothing.
+ // Inject whitespace for typical block-level tags to
+ // prevent merging unrelated<br>words.
+ if ( $this->isBlockLevelTag( $name ) ) {
+ $this->text .= ' ';
+ }
}
function doctype( $name, $public, $system, $quirks, $sourceStart, $sourceLength ) {
// Do nothing.
function comment( $text, $sourceStart, $sourceLength ) {
// Do nothing.
}
+
+ // Per https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements
+ // retrieved on sept 12, 2018. <br> is not block level but was added anyways.
+ // The following is a complete list of all HTML block level elements
+ // (although "block-level" is not technically defined for elements that are
+ // new in HTML5).
+ // Structured as tag => true to allow O(1) membership test.
+ static private $BLOCK_LEVEL_TAGS = [
+ 'address' => true,
+ 'article' => true,
+ 'aside' => true,
+ 'blockquote' => true,
+ 'br' => true,
+ 'canvas' => true,
+ 'dd' => true,
+ 'div' => true,
+ 'dl' => true,
+ 'dt' => true,
+ 'fieldset' => true,
+ 'figcaption' => true,
+ 'figure' => true,
+ 'figcaption' => true,
+ 'footer' => true,
+ 'form' => true,
+ 'h1' => true,
+ 'h2' => true,
+ 'h3' => true,
+ 'h4' => true,
+ 'h5' => true,
+ 'h6' => true,
+ 'header' => true,
+ 'hgroup' => true,
+ 'hr' => true,
+ 'li' => true,
+ 'main' => true,
+ 'nav' => true,
+ 'noscript' => true,
+ 'ol' => true,
+ 'output' => true,
+ 'p' => true,
+ 'pre' => true,
+ 'section' => true,
+ 'table' => true,
+ 'tfoot' => true,
+ 'ul' => true,
+ 'video' => true,
+ ];
+
+ /**
+ * Detect block level tags. Of course css can make anything a block
+ * level tag, but this is still better than nothing.
+ *
+ * @param string $tagName HTML tag name
+ * @return bool True when tag is an html block level element
+ */
+ private function isBlockLevelTag( $tagName ) {
+ $key = strtolower( trim( $tagName ) );
+ return isset( self::$BLOCK_LEVEL_TAGS[$key] );
+ }
}
* @return string
*/
private static function normalizeWhitespace( $text ) {
- return preg_replace(
- '/\r\n|[\x20\x0d\x0a\x09]/',
+ return trim( preg_replace(
+ '/(?:\r\n|[\x20\x0d\x0a\x09])+/',
' ',
- $text );
+ $text ) );
}
/**
];
}
- if ( $this->config->get( 'StructuredChangeFiltersShowPreference' ) ) {
- $defaultPreferences['rcenhancedfilters-disable'] = [
- 'type' => 'toggle',
- 'section' => 'rc/optoutrc',
- 'label-message' => 'rcfilters-preference-label',
- 'help-message' => 'rcfilters-preference-help',
- ];
- }
+ $defaultPreferences['rcenhancedfilters-disable'] = [
+ 'type' => 'toggle',
+ 'section' => 'rc/optoutrc',
+ 'label-message' => 'rcfilters-preference-label',
+ 'help-message' => 'rcfilters-preference-help',
+ ];
}
/**
];
}
- if ( $this->config->get( 'StructuredChangeFiltersShowWatchlistPreference' ) ) {
- $defaultPreferences['wlenhancedfilters-disable'] = [
- 'type' => 'toggle',
- 'section' => 'watchlist/optoutwatchlist',
- 'label-message' => 'rcfilters-watchlist-preference-label',
- 'help-message' => 'rcfilters-watchlist-preference-help',
- ];
- }
+ $defaultPreferences['wlenhancedfilters-disable'] = [
+ 'type' => 'toggle',
+ 'section' => 'watchlist/optoutwatchlist',
+ 'label-message' => 'rcfilters-watchlist-preference-label',
+ 'help-message' => 'rcfilters-watchlist-preference-help',
+ ];
}
/**
/**
* Get the query to generate a dynamic stylesheet
*
+ * @deprecated since 1.32 Use action=raw&ctype=text/css directly.
* @return array
*/
public static function getDynamicStylesheetQuery() {
- global $wgSquidMaxage;
-
return [
'action' => 'raw',
- 'maxage' => $wgSquidMaxage,
- 'usemsgcache' => 'yes',
'ctype' => 'text/css',
- 'smaxage' => $wgSquidMaxage,
];
}
* @return bool
*/
public function isStructuredFilterUiEnabledByDefault() {
- if ( $this->getConfig()->get( 'StructuredChangeFiltersShowPreference' ) ) {
- return !$this->getUser()->getDefaultOption( 'rcenhancedfilters-disable' );
- } else {
- return $this->getUser()->getDefaultOption( 'rcenhancedfilters' );
- }
+ return true;
}
/**
* @return bool
*/
public static function checkStructuredFilterUiEnabled( Config $config, User $user ) {
- if ( $config->get( 'StructuredChangeFiltersShowPreference' ) ) {
- return !$user->getOption( 'rcenhancedfilters-disable' );
- } else {
- return $user->getOption( 'rcenhancedfilters' );
- }
+ return !$user->getOption( 'rcenhancedfilters-disable' );
}
/**
}
$config = $this->getConfig();
- if ( $config->get( 'UseTidy' ) && $options->getTidy() ) {
+ if ( MWTidy::isEnabled() && $options->getTidy() ) {
$tmp = MWTidy::tidy( $tmp );
}
}
public static function checkStructuredFilterUiEnabled( Config $config, User $user ) {
- if ( !$config->get( 'StructuredChangeFiltersOnWatchlist' ) ) {
- return false;
- }
-
- if ( $config->get( 'StructuredChangeFiltersShowWatchlistPreference' ) ) {
- return !$user->getOption( 'wlenhancedfilters-disable' );
- } else {
- return $user->getOption( 'rcenhancedfilters' );
- }
+ return !$user->getOption( 'wlenhancedfilters-disable' );
}
/**
// Certain names may be reserved for batch processes.
foreach ( $reservedUsernames as $reserved ) {
if ( substr( $reserved, 0, 4 ) == 'msg:' ) {
- $reserved = wfMessage( substr( $reserved, 4 ) )->inContentLanguage()->text();
+ $reserved = wfMessage( substr( $reserved, 4 ) )->inContentLanguage()->plain();
}
if ( $reserved == $name ) {
return false;
* @return bool
*/
public function setPassword( $str ) {
+ wfDeprecated( __METHOD__, '1.27' );
return $this->setPasswordInternal( $str );
}
* through the web interface.
*/
public function setInternalPassword( $str ) {
+ wfDeprecated( __METHOD__, '1.27' );
$this->setPasswordInternal( $str );
}
}
}
+ Hooks::run( 'UserGetRightsRemove', [ $this, &$this->mRights ] );
// Force reindexation of rights when a hook has unset one of them
$this->mRights = array_values( array_unique( $this->mRights ) );
'user',
'user_id',
[ 'user_name' => $this->mName ],
- __METHOD__,
+ $fname,
[ 'LOCK IN SHARE MODE' ]
);
$loaded = false;
}
}
if ( !$loaded ) {
- throw new MWException( __METHOD__ . ": hit a key conflict attempting " .
+ throw new MWException( $fname . ": hit a key conflict attempting " .
"to insert user '{$this->mName}' row, but it was not present in select!" );
}
return Status::newFatal( 'userexists' );
* @return bool True if the given password is correct, otherwise False
*/
public function checkPassword( $password ) {
+ wfDeprecated( __METHOD__, '1.27' );
+
$manager = AuthManager::singleton();
$reqs = AuthenticationRequest::loadRequestsFromSubmission(
$manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN ),
* @return bool True if matches, false otherwise
*/
public function checkTemporaryPassword( $plaintext ) {
+ wfDeprecated( __METHOD__, '1.27' );
// Can't check the temporary password individually.
return $this->checkPassword( $plaintext );
}
$dbw = $this->getConnectionRef( DB_MASTER );
$factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
- $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
+ $ticket = $factory->getEmptyTransactionTicket( $fname );
$watchersChunks = array_chunk( $watchers, $wgUpdateRowsPerQuery );
foreach ( $watchersChunks as $watchersChunk ) {
);
if ( count( $watchersChunks ) > 1 ) {
$factory->commitAndWaitForReplication(
- __METHOD__, $ticket, [ 'domain' => $dbw->getDomainID() ]
+ $fname, $ticket, [ 'domain' => $dbw->getDomainID() ]
);
}
}
$this->mCyLa2Arab = [
# # Punctuation -> Arabic
- '/#|№|No\./u' => '', # ؀
- '/\,/' => '،', # ،
- '/;/' => '؛', # ؛
- '/\?/' => '؟', # ؟
- '/%/' => '٪', # ٪
- '/\*/' => '٭', # ٭
+ '/#|№|No\./u' => '', # U+0600
+ '/\,/' => '،', # U+060C
+ '/;/' => '؛', # U+061B
+ '/\?/' => '؟', # U+061F
+ '/%/' => '٪', # U+066A
+ '/\*/' => '٭', # U+066D
# # Digits -> Arabic
- '/0/' => '۰', # ۰
- '/1/' => '۱', # ۱
- '/2/' => '۲', # ۲
- '/3/' => '۳', # ۳
- '/4/' => '۴', # ۴
- '/5/' => '۵', # ۵
- '/6/' => '۶', # ۶
- '/7/' => '۷', # ۷
- '/8/' => '۸', # ۸
- '/9/' => '۹', # ۹
+ '/0/' => '۰', # U+06F0
+ '/1/' => '۱', # U+06F1
+ '/2/' => '۲', # U+06F2
+ '/3/' => '۳', # U+06F3
+ '/4/' => '۴', # U+06F4
+ '/5/' => '۵', # U+06F5
+ '/6/' => '۶', # U+06F6
+ '/7/' => '۷', # U+06F7
+ '/8/' => '۸', # U+06F8
+ '/9/' => '۹', # U+06F9
# # Cyrillic -> Arabic
'/Аллаһ/ui' => 'ﷲ',
'/([АӘЕЁИОӨҰҮЭЮЯЪЬ])е/ui' => '$1يە',
'؟' => '?',
# digits
- '٠' => '0', # ٠
- '١' => '1', # ١
- '٢' => '2', # ٢
- '٣' => '3', # ٣
- '٤' => '4', # ٤
- '٥' => '5', # ٥
- '٦' => '6', # ٦
- '٧' => '7', # ٧
- '٨' => '8', # ٨
- '٩' => '9', # ٩
+ '٠' => '0', # U+0660
+ '١' => '1', # U+0661
+ '٢' => '2', # U+0662
+ '٣' => '3', # U+0663
+ '٤' => '4', # U+0664
+ '٥' => '5', # U+0665
+ '٦' => '6', # U+0666
+ '٧' => '7', # U+0667
+ '٨' => '8', # U+0668
+ '٩' => '9', # U+0669
];
public $mLatinToArabic = [
/* # deactivated for now, breaks links i.e. in header of Special:Recentchanges :-(
# digits
- '0' => '٠', # ٠
- '1' => '١', # ١
- '2' => '٢', # ٢
- '3' => '٣', # ٣
- '4' => '٤', # ٤
- '5' => '٥', # ٥
- '6' => '٦', # ٦
- '7' => '٧', # ٧
- '8' => '٨', # ٨
- '9' => '٩', # ٩
+ '0' => '٠', # U+0660
+ '1' => '١', # U+0661
+ '2' => '٢', # U+0662
+ '3' => '٣', # U+0663
+ '4' => '٤', # U+0664
+ '5' => '٥', # U+0665
+ '6' => '٦', # U+0666
+ '7' => '٧', # U+0667
+ '8' => '٨', # U+0668
+ '9' => '٩', # U+0669
*/
];
];
$digitTransformTable = [
- '0' => '٠', # ٠
- '1' => '١', # ١
- '2' => '٢', # ٢
- '3' => '٣', # ٣
- '4' => '٤', # ٤
- '5' => '٥', # ٥
- '6' => '٦', # ٦
- '7' => '٧', # ٧
- '8' => '٨', # ٨
- '9' => '٩', # ٩
+ '0' => '٠', # U+0660
+ '1' => '١', # U+0661
+ '2' => '٢', # U+0662
+ '3' => '٣', # U+0663
+ '4' => '٤', # U+0664
+ '5' => '٥', # U+0665
+ '6' => '٦', # U+0666
+ '7' => '٧', # U+0667
+ '8' => '٨', # U+0668
+ '9' => '٩', # U+0669
];
$separatorTransformTable = [
- '.' => '٫', # ٫
- ',' => '٬', # ٬
+ '.' => '٫', # U+066B
+ ',' => '٬', # U+066C
];
$namespaceNames = [
];
$digitTransformTable = [
- '0' => '০', # ০
- '1' => '১', # ১
- '2' => '২', # ২
- '3' => '৩', # ৩
- '4' => '৪', # ৪
- '5' => '৫', # ৫
- '6' => '৬', # ৬
- '7' => '৭', # ৭
- '8' => '৮', # ৮
- '9' => '৯', # ৯
+ '0' => '০', # U+09E6
+ '1' => '১', # U+09E7
+ '2' => '২', # U+09E8
+ '3' => '৩', # U+09E9
+ '4' => '৪', # U+09EA
+ '5' => '৫', # U+09EB
+ '6' => '৬', # U+09EC
+ '7' => '৭', # U+09ED
+ '8' => '৮', # U+09EE
+ '9' => '৯', # U+09EF
];
$digitGroupingPattern = "##,##,###";
];
$digitTransformTable = [
- '0' => '०', # ०
- '1' => '१', # १
- '2' => '२', # २
- '3' => '३', # ३
- '4' => '४', # ४
- '5' => '५', # ५
- '6' => '६', # ६
- '7' => '७', # ७
- '8' => '८', # ८
- '9' => '९', # ९
+ '0' => '०', # U+0966
+ '1' => '१', # U+0967
+ '2' => '२', # U+0968
+ '3' => '३', # U+0969
+ '4' => '४', # U+096A
+ '5' => '५', # U+096B
+ '6' => '६', # U+096C
+ '7' => '७', # U+096D
+ '8' => '८', # U+096E
+ '9' => '९', # U+096F
];
*/
$digitTransformTable = [
- '0' => '༠', # ༠
- '1' => '༡', # ༡
- '2' => '༢', # ༢
- '3' => '༣', # ༣
- '4' => '༤', # ༤
- '5' => '༥', # ༥
- '6' => '༦', # ༦
- '7' => '༧', # ༧
- '8' => '༨', # ༨
- '9' => '༩', # ༩
+ '0' => '༠', # U+0F20
+ '1' => '༡', # U+0F21
+ '2' => '༢', # U+0F22
+ '3' => '༣', # U+0F23
+ '4' => '༤', # U+0F24
+ '5' => '༥', # U+0F25
+ '6' => '༦', # U+0F26
+ '7' => '༧', # U+0F27
+ '8' => '༨', # U+0F28
+ '9' => '༩', # U+0F29
];
];
$digitTransformTable = [
- '0' => '٠', # ٠
- '1' => '١', # ١
- '2' => '٢', # ٢
- '3' => '٣', # ٣
- '4' => '٤', # ٤
- '5' => '٥', # ٥
- '6' => '٦', # ٦
- '7' => '٧', # ٧
- '8' => '٨', # ٨
- '9' => '٩', # ٩
+ '0' => '٠', # U+0660
+ '1' => '١', # U+0661
+ '2' => '٢', # U+0662
+ '3' => '٣', # U+0663
+ '4' => '٤', # U+0664
+ '5' => '٥', # U+0665
+ '6' => '٦', # U+0666
+ '7' => '٧', # U+0667
+ '8' => '٨', # U+0668
+ '9' => '٩', # U+0669
];
$separatorTransformTable = [
- '.' => '٫', # ٫
- ',' => '٬', # ٬
+ '.' => '٫', # U+066B
+ ',' => '٬', # U+066C
];
$datePreferences = [
*/
$digitTransformTable = [
- '0' => '༠', # ༠
- '1' => '༡', # ༡
- '2' => '༢', # ༢
- '3' => '༣', # ༣
- '4' => '༤', # ༤
- '5' => '༥', # ༥
- '6' => '༦', # ༦
- '7' => '༧', # ༧
- '8' => '༨', # ༨
- '9' => '༩', # ༩
+ '0' => '༠', # U+0F20
+ '1' => '༡', # U+0F21
+ '2' => '༢', # U+0F22
+ '3' => '༣', # U+0F23
+ '4' => '༤', # U+0F24
+ '5' => '༥', # U+0F25
+ '6' => '༦', # U+0F26
+ '7' => '༧', # U+0F27
+ '8' => '༨', # U+0F28
+ '9' => '༩', # U+0F29
];
];
$digitTransformTable = [
- '0' => '۰', # ۰
- '1' => '۱', # ۱
- '2' => '۲', # ۲
- '3' => '۳', # ۳
- '4' => '۴', # ۴
- '5' => '۵', # ۵
- '6' => '۶', # ۶
- '7' => '۷', # ۷
- '8' => '۸', # ۸
- '9' => '۹', # ۹
- '%' => '٪', # ٪
+ '0' => '۰', # U+06F0
+ '1' => '۱', # U+06F1
+ '2' => '۲', # U+06F2
+ '3' => '۳', # U+06F3
+ '4' => '۴', # U+06F4
+ '5' => '۵', # U+06F5
+ '6' => '۶', # U+06F6
+ '7' => '۷', # U+06F7
+ '8' => '۸', # U+06F8
+ '9' => '۹', # U+06F9
+ '%' => '٪', # U+066A
];
$separatorTransformTable = [
- '.' => '٫', # ٫
- ',' => '٬', # ٬
+ '.' => '٫', # U+066B
+ ',' => '٬', # U+066C
];
/**
];
$digitTransformTable = [
- '0' => '૦', # ૦
- '1' => '૧', # ૧
- '2' => '૨', # ૨
- '3' => '૩', # ૩
- '4' => '૪', # ૪
- '5' => '૫', # ૫
- '6' => '૬', # ૬
- '7' => '૭', # ૭
- '8' => '૮', # ૮
- '9' => '૯', # ૯
+ '0' => '૦', # U+0AE6
+ '1' => '૧', # U+0AE7
+ '2' => '૨', # U+0AE8
+ '3' => '૩', # U+0AE9
+ '4' => '૪', # U+0AEA
+ '5' => '૫', # U+0AEB
+ '6' => '૬', # U+0AEC
+ '7' => '૭', # U+0AED
+ '8' => '૮', # U+0AEE
+ '9' => '૯', # U+0AEF
];
$digitGroupingPattern = "##,##,###";
];
$digitTransformTable = [
- '0' => '०', # ०
- '1' => '१', # १
- '2' => '२', # २
- '3' => '३', # ३
- '4' => '४', # ४
- '5' => '५', # ५
- '6' => '६', # ६
- '7' => '७', # ७
- '8' => '८', # ८
- '9' => '९', # ९
+ '0' => '०', # U+0966
+ '1' => '१', # U+0967
+ '2' => '२', # U+0968
+ '3' => '३', # U+0969
+ '4' => '४', # U+096A
+ '5' => '५', # U+096B
+ '6' => '६', # U+096C
+ '7' => '७', # U+096D
+ '8' => '८', # U+096E
+ '9' => '९', # U+096F
];
$linkTrail = "/^([a-z\x{0900}-\x{0963}\x{0966}-\x{A8E0}-\x{A8FF}]+)(.*)$/sDu";
$rtl = true;
$digitTransformTable = [
- '0' => '۰', # ۰
- '1' => '۱', # ۱
- '2' => '۲', # ۲
- '3' => '۳', # ۳
- '4' => '۴', # ۴
- '5' => '۵', # ۵
- '6' => '۶', # ۶
- '7' => '۷', # ۷
- '8' => '۸', # ۸
- '9' => '۹', # ۹
+ '0' => '۰', # U+06F0
+ '1' => '۱', # U+06F1
+ '2' => '۲', # U+06F2
+ '3' => '۳', # U+06F3
+ '4' => '۴', # U+06F4
+ '5' => '۵', # U+06F5
+ '6' => '۶', # U+06F6
+ '7' => '۷', # U+06F7
+ '8' => '۸', # U+06F8
+ '9' => '۹', # U+06F9
];
$separatorTransformTable = [
- '.' => '٫', # ٫
- ',' => '٬', # ٬
+ '.' => '٫', # U+066B
+ ',' => '٬', # U+066C
];
$fallback8bitEncoding = 'windows-1256';
];
$digitTransformTable = [
- '0' => '០', # ០
- '1' => '១', # ១
- '2' => '២', # ២
- '3' => '៣', # ៣
- '4' => '៤', # ៤
- '5' => '៥', # ៥
- '6' => '៦', # ៦
- '7' => '៧', # ៧
- '8' => '៨', # ៨
- '9' => '៩', # ៩
+ '0' => '០', # U+17E0
+ '1' => '១', # U+17E1
+ '2' => '២', # U+17E2
+ '3' => '៣', # U+17E3
+ '4' => '៤', # U+17E4
+ '5' => '៥', # U+17E5
+ '6' => '៦', # U+17E6
+ '7' => '៧', # U+17E7
+ '8' => '៨', # U+17E8
+ '9' => '៩', # U+17E9
];
$separatorTransformTable = [
];
$digitTransformTable = [
- '0' => '೦', # ೦
- '1' => '೧', # ೧
- '2' => '೨', # ೨
- '3' => '೩', # ೩
- '4' => '೪', # ೪
- '5' => '೫', # ೫
- '6' => '೬', # ೬
- '7' => '೭', # ೭
- '8' => '೮', # ೮
- '9' => '೯', # ೯
+ '0' => '೦', # U+0CE6
+ '1' => '೧', # U+0CE7
+ '2' => '೨', # U+0CE8
+ '3' => '೩', # U+0CE9
+ '4' => '೪', # U+0CEA
+ '5' => '೫', # U+0CEB
+ '6' => '೬', # U+0CEC
+ '7' => '೭', # U+0CED
+ '8' => '೮', # U+0CEE
+ '9' => '೯', # U+0CEF
];
$digitGroupingPattern = "##,##,###";
];
$digitTransformTable = [
- '0' => '٠', # ٠
- '1' => '١', # ١
- '2' => '٢', # ٢
- '3' => '٣', # ٣
- '4' => '٤', # ٤
- '5' => '٥', # ٥
- '6' => '٦', # ٦
- '7' => '٧', # ٧
- '8' => '٨', # ٨
- '9' => '٩', # ٩
+ '0' => '٠', # U+0660
+ '1' => '١', # U+0661
+ '2' => '٢', # U+0662
+ '3' => '٣', # U+0663
+ '4' => '٤', # U+0664
+ '5' => '٥', # U+0665
+ '6' => '٦', # U+0666
+ '7' => '٧', # U+0667
+ '8' => '٨', # U+0668
+ '9' => '٩', # U+0669
];
$separatorTransformTable = [
- '.' => '٫', # ٫
- ',' => '٬', # ٬
+ '.' => '٫', # U+066B
+ ',' => '٬', # U+066C
];
];
$digitTransformTable = [
- '0' => '०', # ०
- '1' => '१', # १
- '2' => '२', # २
- '3' => '३', # ३
- '4' => '४', # ४
- '5' => '५', # ५
- '6' => '६', # ६
- '7' => '७', # ७
- '8' => '८', # ८
- '9' => '९', # ९
+ '0' => '०', # U+0966
+ '1' => '१', # U+0967
+ '2' => '२', # U+0968
+ '3' => '३', # U+0969
+ '4' => '४', # U+096A
+ '5' => '५', # U+096B
+ '6' => '६', # U+096C
+ '7' => '७', # U+096D
+ '8' => '८', # U+096E
+ '9' => '९', # U+096F
];
$rtl = true;
$digitTransformTable = [
- '0' => '٠', # ٠
- '1' => '١', # ١
- '2' => '٢', # ٢
- '3' => '٣', # ٣
- '4' => '٤', # ٤
- '5' => '٥', # ٥
- '6' => '٦', # ٦
- '7' => '٧', # ٧
- '8' => '٨', # ٨
- '9' => '٩', # ٩
+ '0' => '٠', # U+0660
+ '1' => '١', # U+0661
+ '2' => '٢', # U+0662
+ '3' => '٣', # U+0663
+ '4' => '٤', # U+0664
+ '5' => '٥', # U+0665
+ '6' => '٦', # U+0666
+ '7' => '٧', # U+0667
+ '8' => '٨', # U+0668
+ '9' => '٩', # U+0669
];
$separatorTransformTable = [
- '.' => '٫', # ٫
- ',' => '٬', # ٬
+ '.' => '٫', # U+066B
+ ',' => '٬', # U+066C
];
];
$digitTransformTable = [
- '0' => '໐', # ໐
- '1' => '໑', # ໑
- '2' => '໒', # ໒
- '3' => '໓', # ໓
- '4' => '໔', # ໔
- '5' => '໕', # ໕
- '6' => '໖', # ໖
- '7' => '໗', # ໗
- '8' => '໘', # ໘
- '9' => '໙', # ໙
+ '0' => '໐', # U+0ED0
+ '1' => '໑', # U+0ED1
+ '2' => '໒', # U+0ED2
+ '3' => '໓', # U+0ED3
+ '4' => '໔', # U+0ED4
+ '5' => '໕', # U+0ED5
+ '6' => '໖', # U+0ED6
+ '7' => '໗', # U+0ED7
+ '8' => '໘', # U+0ED8
+ '9' => '໙', # U+0ED9
];
];
$digitTransformTable = [
- '0' => '०', # ०
- '1' => '१', # १
- '2' => '२', # २
- '3' => '३', # ३
- '4' => '४', # ४
- '5' => '५', # ५
- '6' => '६', # ६
- '7' => '७', # ७
- '8' => '८', # ८
- '9' => '९', # ९
+ '0' => '०', # U+0966
+ '1' => '१', # U+0967
+ '2' => '२', # U+0968
+ '3' => '३', # U+0969
+ '4' => '४', # U+096A
+ '5' => '५', # U+096B
+ '6' => '६', # U+096C
+ '7' => '७', # U+096D
+ '8' => '८', # U+096E
+ '9' => '९', # U+096F
];
$linkTrail = "/^([\u{0900}-\u{0963}\u{0971}-\u{097F}\u{FEFF}\u{200D}]+)(.*)$/sDu";
];
$digitTransformTable = [
- '0' => '०', # ०
- '1' => '१', # १
- '2' => '२', # २
- '3' => '३', # ३
- '4' => '४', # ४
- '5' => '५', # ५
- '6' => '६', # ६
- '7' => '७', # ७
- '8' => '८', # ८
- '9' => '९', # ९
+ '0' => '०', # U+0966
+ '1' => '१', # U+0967
+ '2' => '२', # U+0968
+ '3' => '३', # U+0969
+ '4' => '४', # U+096A
+ '5' => '५', # U+096B
+ '6' => '६', # U+096C
+ '7' => '७', # U+096D
+ '8' => '८', # U+096E
+ '9' => '९', # U+096F
];
];
$digitTransformTable = [
- '0' => '०', # ०
- '1' => '१', # १
- '2' => '२', # २
- '3' => '३', # ३
- '4' => '४', # ४
- '5' => '५', # ५
- '6' => '६', # ६
- '7' => '७', # ७
- '8' => '८', # ८
- '9' => '९', # ९
+ '0' => '०', # U+0966
+ '1' => '१', # U+0967
+ '2' => '२', # U+0968
+ '3' => '३', # U+0969
+ '4' => '४', # U+096A
+ '5' => '५', # U+096B
+ '6' => '६', # U+096C
+ '7' => '७', # U+096D
+ '8' => '८', # U+096E
+ '9' => '९', # U+096F
];
*/
$digitTransformTable = [
- '0' => '୦', # ୦
- '1' => '୧', # ୧
- '2' => '୨', # ୨
- '3' => '୩', # ୩
- '4' => '୪', # ୪
- '5' => '୫', # ୫
- '6' => '୬', # ୬
- '7' => '୭', # ୭
- '8' => '୮', # ୮
- '9' => '୯', # ୯
+ '0' => '୦', # U+0B66
+ '1' => '୧', # U+0B67
+ '2' => '୨', # U+0B68
+ '3' => '୩', # U+0B69
+ '4' => '୪', # U+0B6A
+ '5' => '୫', # U+0B6B
+ '6' => '୬', # U+0B6C
+ '7' => '୭', # U+0B6D
+ '8' => '୮', # U+0B6E
+ '9' => '୯', # U+0B6F
];
$linkTrail = "/^([a-z\x{0B00}-\x{0B7F}]+)(.*)$/sDu";
];
$digitTransformTable = [
- '0' => '०', # ०
- '1' => '१', # १
- '2' => '२', # २
- '3' => '३', # ३
- '4' => '४', # ४
- '5' => '५', # ५
- '6' => '६', # ६
- '7' => '७', # ७
- '8' => '८', # ८
- '9' => '९', # ९
+ '0' => '०', # U+0966
+ '1' => '१', # U+0967
+ '2' => '२', # U+0968
+ '3' => '३', # U+0969
+ '4' => '४', # U+096A
+ '5' => '५', # U+096B
+ '6' => '६', # U+096C
+ '7' => '७', # U+096D
+ '8' => '८', # U+096E
+ '9' => '९', # U+096F
];
$rtl = true;
$digitTransformTable = [
- '0' => '۰', # ᝶
- '1' => '۱', # ᝷
- '2' => '۲', # ᝸
- '3' => '۳', # ᝹
- '4' => '۴', # ក
- '5' => '۵', # ខ
- '6' => '۶', # គ
- '7' => '۷', # ឃ
- '8' => '۸', # ង
- '9' => '۹', # ច
+ '0' => '۰', # U+06F0
+ '1' => '۱', # U+06F1
+ '2' => '۲', # U+06F2
+ '3' => '۳', # U+06F3
+ '4' => '۴', # U+06F4
+ '5' => '۵', # U+06F5
+ '6' => '۶', # U+06F6
+ '7' => '۷', # U+06F7
+ '8' => '۸', # U+06F8
+ '9' => '۹', # U+06F9
];
$separatorTransformTable = [
- '.' => '٫', # ٫
- ',' => '٬', # ٬
+ '.' => '٫', # U+066B
+ ',' => '٬', # U+066C
];
$namespaceNames = [
$fallback = 'hi';
$digitTransformTable = [
- '0' => '०', # ०
- '1' => '१', # १
- '2' => '२', # २
- '3' => '३', # ३
- '4' => '४', # ४
- '5' => '५', # ५
- '6' => '६', # ६
- '7' => '७', # ७
- '8' => '८', # ८
- '9' => '९', # ९
+ '0' => '०', # U+0966
+ '1' => '१', # U+0967
+ '2' => '२', # U+0968
+ '3' => '३', # U+0969
+ '4' => '४', # U+096A
+ '5' => '५', # U+096B
+ '6' => '६', # U+096C
+ '7' => '७', # U+096D
+ '8' => '८', # U+096E
+ '9' => '९', # U+096F
];
$linkPrefixExtension = false;
$rtl = true;
$digitTransformTable = [
- '0' => '۰', # ۰
- '1' => '۱', # ۱
- '2' => '۲', # ۲
- '3' => '۳', # ۳
- '4' => '۴', # ۴
- '5' => '۵', # ۵
- '6' => '۶', # ۶
- '7' => '۷', # ۷
- '8' => '۸', # ۸
- '9' => '۹', # ۹
- '%' => '٪', # ٪
+ '0' => '۰', # U+06F0
+ '1' => '۱', # U+06F1
+ '2' => '۲', # U+06F2
+ '3' => '۳', # U+06F3
+ '4' => '۴', # U+06F4
+ '5' => '۵', # U+06F5
+ '6' => '۶', # U+06F6
+ '7' => '۷', # U+06F7
+ '8' => '۸', # U+06F8
+ '9' => '۹', # U+06F9
+ '%' => '٪', # U+066A
];
uselang
uselivepreview
usemod
-usemsgcache
usenewrc
user
useragent
);
}
$this->total += $dbw->affectedRows();
+ MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->waitForReplication();
}
/**
$toDelete[] = $id;
$maxId = max(
- (int)$dbw->selectField( 'archive', 'MAX(ar_rev_id)', [], __METHOD__ ),
- (int)$dbw->selectField( 'slots', 'MAX(slot_revision_id)', [], __METHOD__ )
+ (int)$dbw->selectField( 'archive', 'MAX(ar_rev_id)', [], $fname ),
+ (int)$dbw->selectField( 'slots', 'MAX(slot_revision_id)', [], $fname )
);
if ( $id <= $maxId ) {
$dbw->insert( 'revision', [ 'rev_id' => $maxId + 1 ] + self::$dummyRev, $fname );
);
}
if ( !$rev ) {
- throw new UnexpectedValueException( 'No revisions are available to copy' );
+ // Since no revisions are available to copy, generate a dummy
+ // revision to a dummy page, then rollback the commit
+ wfDebug( __METHOD__ . ": No revisions are available to copy\n" );
+
+ $dbw->begin();
+
+ // Make a title and revision and insert them
+ $title = Title::newFromText( "PopulateArchiveRevId_4b05b46a81e29" );
+ $page = WikiPage::factory( $title );
+ $updater = $page->newPageUpdater(
+ User::newSystemUser( 'Maintenance script', [ 'steal' => true ] )
+ );
+ $updater->setContent(
+ 'main',
+ ContentHandler::makeContent( "Content for dummy rev", $title )
+ );
+ $updater->saveRevision(
+ CommentStoreComment::newUnsavedComment( 'dummy rev summary' ),
+ EDIT_NEW | EDIT_SUPPRESS_RC
+ );
+
+ // get the revision row just inserted
+ $rev = $dbw->selectRow(
+ 'revision',
+ '*',
+ [],
+ __METHOD__,
+ [ 'ORDER BY' => 'rev_timestamp ASC' ]
+ );
+
+ $dbw->rollback();
+ }
+ if ( !$rev ) {
+ // This should never happen.
+ throw new UnexpectedValueException(
+ 'No revisions are available to copy, and one couldn\'t be created'
+ );
}
unset( $rev->rev_id );
};
/**
- * Category selector capsule item widget. Extends OO.ui.CapsuleItemWidget with the ability to link
+ * Category selector tag item widget. Extends OO.ui.TagItemWidget with the ability to link
* to the given page, and to show its existence status (i.e., whether it is a redlink).
*
* @class mw.widgets.CategoryTagItemWidget
.addClass( 'new' );
}
};
-
- // For backwards compatibility. See T183299.
- mw.widgets.CategoryCapsuleItemWidget = mw.widgets.CategoryTagItemWidget;
}() );
END;
$struct = $this->getStructure( $text );
$this->assertEquals( "Opening text is opening.", $struct->getOpeningText() );
- $this->assertEquals( "Opening text is opening. Then we got more text",
+ $this->assertEquals( "Opening text is opening. Then we got more text",
$struct->getMainText() );
$this->assertEquals( [ "Header table row in table another row in table" ],
$struct->getAuxiliaryText() );
}
+
+ public function testPreservesWordSpacing() {
+ $text = "<dd><dl>foo</dl><dl>bar</dl></dd><p>baz</p>";
+ $struct = $this->getStructure( $text );
+ $this->assertEquals( "foo bar baz", $struct->getMainText() );
+ }
}
class DatabasePostgresTest extends MediaWikiTestCase {
private function doTestInsertIgnore() {
- $reset = new ScopedCallback( function () {
+ $fname = __METHOD__;
+ $reset = new ScopedCallback( function () use ( $fname ) {
if ( $this->db->explicitTrxActive() ) {
- $this->db->rollback( __METHOD__ );
+ $this->db->rollback( $fname );
}
- $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ) );
+ $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ), $fname );
} );
$this->db->query(
- "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER NOT NULL PRIMARY KEY)"
+ "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER NOT NULL PRIMARY KEY)",
+ __METHOD__
);
$this->db->insert( 'foo', [ [ 'i' => 1 ], [ 'i' => 2 ] ], __METHOD__ );
}
private function doTestInsertSelectIgnore() {
- $reset = new ScopedCallback( function () {
+ $fname = __METHOD__;
+ $reset = new ScopedCallback( function () use ( $fname ) {
if ( $this->db->explicitTrxActive() ) {
- $this->db->rollback( __METHOD__ );
+ $this->db->rollback( $fname );
}
- $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ) );
- $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'bar' ) );
+ $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ), $fname );
+ $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'bar' ), $fname );
} );
$this->db->query(
- "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER)"
+ "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER)",
+ __METHOD__
);
$this->db->query(
- "CREATE TEMPORARY TABLE {$this->db->tableName( 'bar' )} (i INTEGER NOT NULL PRIMARY KEY)"
+ "CREATE TEMPORARY TABLE {$this->db->tableName( 'bar' )} (i INTEGER NOT NULL PRIMARY KEY)",
+ __METHOD__
);
$this->db->insert( 'bar', [ [ 'i' => 1 ], [ 'i' => 2 ] ], __METHOD__ );
* @covers BitmapMetadataHandler::png
*/
public function testPNGXMP() {
- if ( !extension_loaded( 'xml' ) ) {
- $this->markTestSkipped( "This test needs the xml extension." );
- }
+ $this->checkPHPExtension( 'xml' );
+
$handler = new BitmapMetadataHandler();
$result = $handler->PNG( $this->filePath . 'xmp.png' );
$expected = [
public function provideStripAllTags() {
return [
[ '<p>Foo</p>', 'Foo' ],
- [ '<p id="one">Foo</p><p id="two">Bar</p>', 'FooBar' ],
+ [ '<p id="one">Foo</p><p id="two">Bar</p>', 'Foo Bar' ],
[ "<p>Foo</p>\n<p>Bar</p>", 'Foo Bar' ],
[ '<p>Hello <strong> world café</p>', 'Hello <strong> world café' ],
[
/**
* @dataProvider validateOptionsProvider
*/
- public function testValidateOptions( $optionsToSet, $expectedRedirect, $expectedRedirectOptions ) {
+ public function testValidateOptions(
+ $optionsToSet,
+ $expectedRedirect,
+ $expectedRedirectOptions,
+ $rcfilters
+ ) {
$redirectQuery = [];
$redirected = false;
$output = $this->getMockBuilder( OutputPage::class )
// Give users patrol permissions so we can test that.
$user = $this->getTestSysop()->getUser();
+ $user->setOption( 'rcenhancedfilters-disable', $rcfilters ? 0 : 1 );
$ctx->setUser( $user );
// Disable this hook or it could break changeType
* @covers ChangesListSpecialPage
*/
class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase {
- public function setUp() {
- parent::setUp();
- $this->setMwGlobals( [
- 'wgStructuredChangeFiltersShowPreference' => true,
- ] );
- }
-
protected function getPage() {
$mock = $this->getMockBuilder( ChangesListSpecialPage::class )
->setConstructorArgs(
[ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 1 ],
true,
[ 'userExpLevel' => 'unregistered', 'hidebots' => 1, ],
+ true,
],
[
[ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 0 ],
true,
[ 'hidebots' => 0, 'hidehumans' => 1 ],
+ true,
],
[
[ 'hideanons' => 1 ],
true,
- [ 'userExpLevel' => 'registered' ]
+ [ 'userExpLevel' => 'registered' ],
+ true,
],
[
[ 'hideliu' => 1 ],
true,
- [ 'userExpLevel' => 'unregistered' ]
+ [ 'userExpLevel' => 'unregistered' ],
+ true,
],
[
[ 'hideanons' => 1, 'hidebots' => 1 ],
true,
- [ 'userExpLevel' => 'registered', 'hidebots' => 1 ]
+ [ 'userExpLevel' => 'registered', 'hidebots' => 1 ],
+ true,
],
[
[ 'hideliu' => 1, 'hidebots' => 0 ],
true,
- [ 'userExpLevel' => 'unregistered', 'hidebots' => 0 ]
+ [ 'userExpLevel' => 'unregistered', 'hidebots' => 0 ],
+ true,
],
[
[ 'hidemyself' => 1, 'hidebyothers' => 1 ],
true,
[],
+ true,
],
[
[ 'hidebots' => 1, 'hidehumans' => 1 ],
true,
[],
+ true,
],
[
[ 'hidepatrolled' => 1, 'hideunpatrolled' => 1 ],
true,
[],
+ true,
],
[
[ 'hideminor' => 1, 'hidemajor' => 1 ],
true,
[],
+ true,
],
[
// changeType
[ 'hidepageedits' => 1, 'hidenewpages' => 1, 'hidecategorization' => 1, 'hidelog' => 1, ],
true,
[],
+ true,
],
];
}
[ 'hideanons' => 1, 'hideliu' => 1 ],
true,
[ 'hideliu' => 1 ],
+ false,
],
];
}