Adam Roses Wight <awight@wikimedia.org>
Adam Roses Wight <awight@wikimedia.org> <spam@ludd.net>
addshore <addshorewiki@gmail.com>
+addshore <addshorewiki@gmail.com> <adamshorland@gmail.com>
Aditya Sastry <ganeshaditya1@gmail.com>
Adrian Heine <adrian.heine@wikimedia.de>
Alex Z. <mrzmanwiki@gmail.com> <mrzman@users.mediawiki.org>
Daniel Friesen <mediawiki@danielfriesen.name> <daniel@nadir-seen-fire.com>
Daniel Friesen <mediawiki@danielfriesen.name> <dantman@users.mediawiki.org>
Daniel Friesen <mediawiki@danielfriesen.name> <pub-github@nadir-seen-fire.com>
-Daniel Kinzler <daniel.kinzler@wikimedia.de>
-Daniel Kinzler <daniel.kinzler@wikimedia.de> <daniel@users.mediawiki.org>
+Daniel Kinzler <dkinzler@wikimedia.org>
+Daniel Kinzler <dkinzler@wikimedia.org> <daniel.kinzler@wikimedia.de>
+Daniel Kinzler <dkinzler@wikimedia.org> <daniel@users.mediawiki.org>
Daniel Renfro <bluecurio@gmail.com> <drenfro@vistaprint.com>
Danny B. <Wikipedia.Danny.B@email.cz>
Danny B. <Wikipedia.Danny.B@email.cz> <danny.b@email.cz>
Darkdragon09 <ubuntu@ip-172-31-39-38.us-west-2.compute.internal>
David Causse <dcausse@wikimedia.org>
David Chan <david@sheetmusic.org.uk>
+Dayllan Maza <dmaza@wikimedia.org>
+Dayllan Maza <dmaza@wikimedia.org> <dayllan.maza@gmail.com>
Dereckson <dereckson@espace-win.org>
Derk-Jan Hartman <hartman@videolan.org>
Derk-Jan Hartman <hartman@videolan.org> <hartman.wiki@gmail.com>
Katie Filbert <aude.wiki@gmail.com>
Katie Filbert <aude.wiki@gmail.com> <aude@users.mediawiki.org>
Kevin Israel <pleasestand@live.com>
+Kosta Harlan <kharlan@wikimedia.org>
+Kosta Harlan <kharlan@wikimedia.org> <kosta@fastmail.com>
Kunal Grover <kunalgrover05@gmail.com>
Kunal Mehta <legoktm@member.fsf.org>
Kunal Mehta <legoktm@member.fsf.org> <legoktm.wikipedia@gmail.com>
Patrick Reilly <preilly@wikimedia.org> <preilly@users.mediawiki.org>
Patrick Westerhoff <PatrickWesterhoff@gmail.com>
Paul Copperman <paul.copperman@gmail.com> <pcopp@users.mediawiki.org>
+Petar Petković <ppetkovic@wikimedia.org>
Peter Coombe <pcoombe@wikimedia.org>
Peter Coti <petercoti@gmail.com>
Peter Potrowl <peter017@gmail.com> <peter17@users.mediawiki.org>
removed. Use the 'ChangesListSpecialPageStructuredFilters' hook instead.
* DeferredUpdates::setImmediateMode(), deprecated since 1.29, has been removed.
* File / MediaHandler::getStreamHeaders(), deprecated since 1.30, was removed.
+* The hook 'DoEditSectionLink', deprecated since 1.25, has been removed. Use
+ the hook 'SkinEditSectionLinks' instead.
+* The hook 'UserGetImplicitGroups', deprecated since 1.25, has been removed.
+* The global function wfRunHooks, deprecated since 1.25, has now been removed.
+ Use Hooks::run().
+* The hook 'UnknownAction', deprecated since 1.19, has now been removed.
+* The hook 'ParserLimitReport', deprecated since 1.22, has been removed. Use
+ the hooks 'ParserLimitReportPrepare' and 'ParserLimitReportFormat' instead.
=== Deprecations in 1.32 ===
* HTMLForm::setSubmitProgressive() is deprecated. No need to call it. Submit
&$article: article (object) being viewed
&$oldid: oldid (int) being viewed
-'DoEditSectionLink': DEPRECATED since 1.25! Use SkinEditSectionLinks instead.
-Override the HTML generated for section edit links
-$skin: Skin object rendering the UI
-$title: Title object for the title being linked to (may not be the same as
- the page title, if the section is included from a template)
-$section: The designation of the section being pointed to, to be included in
- the link, like "§ion=$section"
-$tooltip: The default tooltip. Escape before using.
- By default, this is wrapped in the 'editsectionhint' message.
-&$result: The HTML to return, prefilled with the default plus whatever other
- changes earlier hooks have made
-$lang: The language code to use for the link in the wfMessage function
-
'EditFilter': Perform checks on an edit
$editor: EditPage instance (object). The edit form (see includes/EditPage.php)
$text: Contents of the edit box
&$parser: Parser object
&$varCache: variable cache (array)
-'ParserLimitReport': DEPRECATED since 1.22! Use ParserLimitReportPrepare and
-ParserLimitReportFormat instead.
-Called at the end of Parser:parse() when the parser will
-include comments about size of the text parsed.
-$parser: Parser object
-&$limitReport: text that will be included (without comment tags)
-
'ParserLimitReportFormat': Called for each row in the parser limit report that
needs formatting. If nothing handles this hook, the default is to use "$key" to
get the label, and "$key-value" or "$key-value-text"/"$key-value-html" to
test case files matching the suffix "Test.php".
&$paths: list of test cases and directories to search.
-'UnknownAction': DEPRECATED since 1.19! To add an action in an extension,
-create a subclass of Action, and add a new key to $wgActions.
-An unknown "action" has occurred (useful for defining your own actions).
-$action: action name
-$article: article "acted on"
-
'UnwatchArticle': Before a watch is removed from an article.
&$user: user watching
&$page: WikiPage object to be removed
&$timestamp: timestamp, change this to override local email authentication
timestamp
-'UserGetImplicitGroups': DEPRECATED since 1.25!
-Called in User::getImplicitGroups().
-&$groups: List of implicit (automatically-assigned) groups
-
'UserGetLanguageObject': Called when getting user's interface language object.
$user: User object
&$code: Language code that will be used to create the object
$this->contentFormat,
$ex->getMessage()
);
- $out->addWikiText( '<div class="error">' . $msg->text() . '</div>' );
+ $out->addWikiText( '<div class="error">' . $msg->plain() . '</div>' );
}
}
$this->contentFormat,
$ex->getMessage()
);
- $out->addWikiText( '<div class="error">' . $msg->text() . '</div>' );
+ $out->addWikiText( '<div class="error">' . $msg->plain() . '</div>' );
}
}
}
return ObjectCache::getInstance( $wgMessageCacheType );
}
-/**
- * Call hook functions defined in $wgHooks
- *
- * @param string $event Event name
- * @param array $args Parameters passed to hook functions
- * @param string|null $deprecatedVersion Optionally mark hook as deprecated with version number
- *
- * @return bool True if no handler aborted the hook
- * @deprecated since 1.25 - use Hooks::run
- */
-function wfRunHooks( $event, array $args = [], $deprecatedVersion = null ) {
- wfDeprecated( __METHOD__, '1.25' );
- return Hooks::run( $event, $args, $deprecatedVersion );
-}
-
/**
* Wrapper around php's unpack.
*
$action->show();
return;
}
- // NOTE: deprecated hook. Add to $wgActions instead
- if ( Hooks::run(
- 'UnknownAction',
- [
- $request->getVal( 'action', 'view' ),
- $page
- ],
- '1.19'
- ) ) {
- $output->setStatusCode( 404 );
- $output->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
- }
+
+ // If we've not found out which action it is by now, it's unknown
+ $output->setStatusCode( 404 );
+ $output->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
}
/**
* are distinct from Special Pages because an action must apply to exactly one page.
*
* To add an action in an extension, create a subclass of Action, and add the key to
- * $wgActions. There is also the deprecated UnknownAction hook
+ * $wgActions.
*
* Actions generally fall into two groups: the show-a-form-then-do-something-with-the-input
* format (protect, delete, move, etc), and the just-do-something format (watch, rollback,
$cache = [];
- # Common conditions
- $conds = [
- 'page_is_redirect' => 0,
- 'page_namespace' => NS_MEDIAWIKI,
- ];
-
- $mostused = [];
+ $mostused = []; // list of "<cased message key>/<code>"
if ( $wgAdaptiveMessageCache && $code !== $wgLanguageCode ) {
if ( !$this->cache->has( $wgLanguageCode ) ) {
$this->load( $wgLanguageCode );
}
}
+ // Get the list of software-defined messages in core/extensions
+ $overridable = array_flip( Language::getMessageKeysFor( $wgLanguageCode ) );
+
+ // Common conditions
+ $conds = [
+ 'page_is_redirect' => 0,
+ 'page_namespace' => NS_MEDIAWIKI,
+ ];
if ( count( $mostused ) ) {
$conds['page_title'] = $mostused;
} elseif ( $code !== $wgLanguageCode ) {
$dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() );
}
- # Conditions to fetch oversized pages to ignore them
- $bigConds = $conds;
- $bigConds[] = 'page_len > ' . intval( $wgMaxMsgCacheEntrySize );
-
- # Load titles for all oversized pages in the MediaWiki namespace
+ // Set the stubs for oversized software-defined messages in the main cache map
$res = $dbr->select(
'page',
[ 'page_title', 'page_latest' ],
- $bigConds,
+ array_merge( $conds, [ 'page_len > ' . intval( $wgMaxMsgCacheEntrySize ) ] ),
__METHOD__ . "($code)-big"
);
foreach ( $res as $row ) {
- $cache[$row->page_title] = '!TOO BIG';
+ $name = $this->contLang->lcfirst( $row->page_title );
+ // Include entries/stubs for all keys in $mostused in adaptive mode
+ if ( $wgAdaptiveMessageCache || isset( $overridable[$name] ) ) {
+ $cache[$row->page_title] = '!TOO BIG';
+ }
// At least include revision ID so page changes are reflected in the hash
$cache['EXCESSIVE'][$row->page_title] = $row->page_latest;
}
- # Conditions to load the remaining pages with their contents
- $smallConds = $conds;
- $smallConds[] = 'page_len <= ' . intval( $wgMaxMsgCacheEntrySize );
-
+ // Set the text for small software-defined messages in the main cache map
$res = $dbr->select(
[ 'page', 'revision', 'text' ],
- [ 'page_title', 'old_id', 'old_text', 'old_flags' ],
- $smallConds,
+ [ 'page_title', 'page_latest', 'old_id', 'old_text', 'old_flags' ],
+ array_merge( $conds, [ 'page_len <= ' . intval( $wgMaxMsgCacheEntrySize ) ] ),
__METHOD__ . "($code)-small",
[],
[
'text' => [ 'JOIN', 'rev_text_id=old_id' ],
]
);
-
foreach ( $res as $row ) {
- $text = Revision::getRevisionText( $row );
- if ( $text === false ) {
- // Failed to fetch data; possible ES errors?
- // Store a marker to fetch on-demand as a workaround...
- // TODO Use a differnt marker
- $entry = '!TOO BIG';
- wfDebugLog(
- 'MessageCache',
- __METHOD__
- . ": failed to load message page text for {$row->page_title} ($code)"
- );
+ $name = $this->contLang->lcfirst( $row->page_title );
+ // Include entries/stubs for all keys in $mostused in adaptive mode
+ if ( $wgAdaptiveMessageCache || isset( $overridable[$name] ) ) {
+ $text = Revision::getRevisionText( $row );
+ if ( $text === false ) {
+ // Failed to fetch data; possible ES errors?
+ // Store a marker to fetch on-demand as a workaround...
+ // TODO Use a differnt marker
+ $entry = '!TOO BIG';
+ wfDebugLog(
+ 'MessageCache',
+ __METHOD__
+ . ": failed to load message page text for {$row->page_title} ($code)"
+ );
+ } else {
+ $entry = ' ' . $text;
+ }
+ $cache[$row->page_title] = $entry;
} else {
- $entry = ' ' . $text;
+ // T193271: cache object gets too big and slow to generate.
+ // At least include revision ID so page changes are reflected in the hash.
+ $cache['EXCESSIVE'][$row->page_title] = $row->page_latest;
}
- $cache[$row->page_title] = $entry;
}
$cache['VERSION'] = MSG_CACHE_VERSION;
Hooks::run( 'MessageCache::get', [ &$lckey ] );
// Loop through each language in the fallback list until we find something useful
- $lang = wfGetLangObj( $langcode );
$message = $this->getMessageFromFallbackChain(
- $lang,
+ wfGetLangObj( $langcode ),
$lckey,
!$this->mDisable && $useDB
);
$this->getMessagePageName( $langcode, $uckey ),
$langcode
);
-
if ( $message !== false ) {
return $message;
}
$this->load( $code );
$entry = $this->cache->getField( $code, $title );
+
if ( $entry !== null ) {
+ // Message page exists as an override of a software messages
if ( substr( $entry, 0, 1 ) === ' ' ) {
// The message exists and is not '!TOO BIG'
return (string)substr( $entry, 1 );
} elseif ( $entry === '!NONEXISTENT' ) {
+ // The text might be '-' or missing due to some data loss
return false;
}
- // Fall through and try invididual message cache below
- } else {
- // Message does not have a MediaWiki page definition
- $message = false;
- Hooks::run( 'MessagesPreLoad', [ $title, &$message, $code ] );
- if ( $message !== false ) {
- $this->cache->setField( $code, $title, ' ' . $message );
- } else {
- $this->cache->setField( $code, $title, '!NONEXISTENT' );
- }
-
- return $message;
- }
-
- if ( $this->cacheVolatile[$code] ) {
- $entry = false;
- // Make sure that individual keys respect the WAN cache holdoff period too
- LoggerFactory::getInstance( 'MessageCache' )->debug(
- __METHOD__ . ': loading volatile key \'{titleKey}\'',
- [ 'titleKey' => $title, 'code' => $code ] );
+ // Load the message page, utilizing the individual message cache.
+ // If the page does not exist, there will be no hook handler fallbacks.
+ $entry = $this->loadCachedMessagePageEntry(
+ $title,
+ $code,
+ $this->cache->getField( $code, 'HASH' )
+ );
} else {
- // Try the individual message cache
+ // Message page does not exist or does not override a software message.
+ // Load the message page, utilizing the individual message cache.
$entry = $this->loadCachedMessagePageEntry(
$title,
$code,
$this->cache->getField( $code, 'HASH' )
);
+ if ( substr( $entry, 0, 1 ) !== ' ' ) {
+ // Message does not have a MediaWiki page definition; try hook handlers
+ $message = false;
+ Hooks::run( 'MessagesPreLoad', [ $title, &$message, $code ] );
+ if ( $message !== false ) {
+ $this->cache->setField( $code, $title, ' ' . $message );
+ } else {
+ $this->cache->setField( $code, $title, '!NONEXISTENT' );
+ }
+
+ return $message;
+ }
}
if ( $entry !== false && substr( $entry, 0, 1 ) === ' ' ) {
- $this->cache->setField( $code, $title, $entry );
+ if ( $this->cacheVolatile[$code] ) {
+ // Make sure that individual keys respect the WAN cache holdoff period too
+ LoggerFactory::getInstance( 'MessageCache' )->debug(
+ __METHOD__ . ': loading volatile key \'{titleKey}\'',
+ [ 'titleKey' => $title, 'code' => $code ] );
+ } else {
+ $this->cache->setField( $code, $title, $entry );
+ }
// The message exists, so make sure a string is returned
return (string)substr( $entry, 1 );
}
}
return false;
}
+
+ /**
+ * Whether to retry the job.
+ * @return bool
+ */
+ public function allowRetries() {
+ // ThumbnailRenderJob is a warmup for the thumbnails cache,
+ // so loosing it is not a problem. Most times the job fails
+ // for non-renderable or missing images which will not be fixed
+ // by a retry, but will create additional load on the renderer.
+ return false;
+ }
}
// Since we're not really outputting HTML, decode the entities and
// then re-encode the things that need hiding inside HTML comments.
$limitReport = htmlspecialchars_decode( $limitReport );
- // Run deprecated hook
- Hooks::run( 'ParserLimitReport', [ $this, &$limitReport ], '1.22' );
// Sanitize for comment. Note '‐' in the replacement is U+2010,
// which looks much like the problematic '-'.
);
}
- // TODO remove this method once old parser cache objects have expired, probably mid-October 2018
- public function __wakeup() {
- // T203716 remove wrapper that was added by logic in an older version of this class,
- // where the wrapper was included in mText. This might sometimes remove a wrapper that's
- // genuine content (manually added to a system message), but that will work out OK, see below.
- $text = $this->getRawText();
- $start = Html::openElement( 'div', [
- 'class' => 'mw-parser-output'
- ] );
- $startLen = strlen( $start );
- $end = Html::closeElement( 'div' );
- $endPos = strrpos( $text, $end );
- $endLen = strlen( $end );
- if ( substr( $text, 0, $startLen ) === $start && $endPos !== false
- // if the closing div is followed by real content, bail out of unwrapping
- && preg_match( '/^(?>\s*<!--.*?-->)*\s*$/s', substr( $text, $endPos + $endLen ) )
- ) {
- $text = substr( $text, $startLen );
- $text = substr( $text, 0, $endPos - $startLen ) .
- substr( $text, $endPos - $startLen + $endLen );
- $this->setText( $text );
- // We found a wrapper to remove, so the ParserOutput was probably created by the
- // code path that now contains an addWrapperDivClass( 'mw-parser-output' ) call,
- // but it did not contain it when this object was cached, so we need to fix the
- // wrapper class variable.
- // If this was a message with a manually added wrapper, we are technically wrong about
- // this but we were wrong about the unwrapping as well so it will work out just right,
- // except when this is a normal page view of such a message page, in which case
- // it will be single-wrapped instead of double-wrapped (harmless) or something wants
- // render the message with unwrap=true (in which case the message won't be wrapped even
- // though it should, but the few code paths using unwrap=true only do it for real pages).
- $this->clearWrapperDivClass();
- $this->addWrapperDivClass( 'mw-parser-output' );
- }
- }
-
/**
* Merges internal metadata such as flags, accessed options, and profiling info
* from $source into this ParserOutput. This should be used whenever the state of $source
);
$result .= '<span class="mw-editsection-bracket">]</span></span>';
- // Deprecated, use SkinEditSectionLinks hook instead
- Hooks::run(
- 'DoEditSectionLink',
- [ $this, $nt, $section, $tooltip, &$result, $lang ],
- '1.25'
- );
return $result;
}
/**
* Get a list of implicit groups
+ * TODO: Should we deprecate this? It's trivial, but we don't want to encourage use of globals.
+ *
* @return array Array of Strings Array of internal group names
*/
public static function getImplicitGroups() {
global $wgImplicitGroups;
-
- $groups = $wgImplicitGroups;
- # Deprecated, use $wgImplicitGroups instead
- Hooks::run( 'UserGetImplicitGroups', [ &$groups ], '1.25' );
-
- return $groups;
+ return $wgImplicitGroups;
}
/**
}
function execute() {
- global $wgVersion, $wgLang, $wgAllowSchemaUpdates;
+ global $wgVersion, $wgLang, $wgAllowSchemaUpdates, $wgMessagesDirs;
if ( !$wgAllowSchemaUpdates
&& !( $this->hasOption( 'force' )
}
}
+ // T206765: We need to load the installer i18n files as some of errors come installer/updater code
+ $wgMessagesDirs['MediawikiInstaller'] = dirname( __DIR__ ) . '/includes/installer/i18n';
+
$lang = Language::factory( 'en' );
// Set global language to ensure localised errors are in English (T22633)
RequestContext::getMain()->setLanguage( $lang );
*/
private $mwGlobalsToUnset = [];
+ /**
+ * Holds original values of ini settings to be restored
+ * in tearDown().
+ * @see setIniSettings()
+ * @var array
+ */
+ private $iniSettings = [];
+
/**
* Holds original loggers which have been replaced by setLogger()
* @var LoggerInterface[]
foreach ( $this->mwGlobalsToUnset as $value ) {
unset( $GLOBALS[$value] );
}
+ foreach ( $this->iniSettings as $name => $value ) {
+ ini_set( $name, $value );
+ }
if (
array_key_exists( 'wgExtraNamespaces', $this->mwGlobals ) ||
in_array( 'wgExtraNamespaces', $this->mwGlobalsToUnset )
}
}
+ /**
+ * Set an ini setting for the duration of the test
+ * @param string $name Name of the setting
+ * @param string $value Value to set
+ * @since 1.32
+ */
+ protected function setIniSetting( $name, $value ) {
+ $original = ini_get( $name );
+ $this->iniSettings[$name] = $original;
+ ini_set( $name, $value );
+ }
+
/**
* Must be called whenever namespaces are changed, e.g., $wgExtraNamespaces is altered.
* Otherwise old namespace data will lurk and cause bugs.
];
}
- /**
- * @dataProvider provideHooks
- * @covers ::wfRunHooks
- */
- public function testOldStyleHooks( $msg, array $hook, $expectedFoo, $expectedBar ) {
- global $wgHooks;
-
- $this->hideDeprecated( 'wfRunHooks' );
- $foo = $bar = 'original';
-
- $wgHooks['MediaWikiHooksTest001'][] = $hook;
- wfRunHooks( 'MediaWikiHooksTest001', [ &$foo, &$bar ] );
-
- $this->assertSame( $expectedFoo, $foo, $msg );
- $this->assertSame( $expectedBar, $bar, $msg );
- }
-
/**
* @dataProvider provideHooks
* @covers Hooks::register
$this->assertNotContains( 'class="foo bar"', $text );
}
- public function testT203716() {
- // simulate extra wrapping from old parser cache
- $out = new ParserOutput( '<div class="mw-parser-output">Foo</div>' );
- $out = unserialize( serialize( $out ) );
-
- $plainText = $out->getText( [ 'unwrap' => true ] );
- $wrappedText = $out->getText( [ 'unwrap' => false ] );
- $wrappedText2 = $out->getText( [ 'wrapperDivClass' => 'mw-parser-output' ] );
-
- $this->assertNotContains( '<div', $plainText );
- $this->assertContains( '<div', $wrappedText );
- $this->assertStringNotMatchesFormat( '<div%s<div%s', $wrappedText );
- $this->assertContains( '<div', $wrappedText2 );
- $this->assertStringNotMatchesFormat( '<div%s<div%s', $wrappedText2 );
-
- // simulate ParserOuput creation by new parser code
- $out = new ParserOutput( 'Foo' );
- $out->addWrapperDivClass( 'mw-parser-outout' );
- $out = unserialize( serialize( $out ) );
-
- $plainText = $out->getText( [ 'unwrap' => true ] );
- $wrappedText = $out->getText( [ 'unwrap' => false ] );
- $wrappedText2 = $out->getText( [ 'wrapperDivClass' => 'mw-parser-output' ] );
-
- $this->assertNotContains( '<div', $plainText );
- $this->assertContains( '<div', $wrappedText );
- $this->assertStringNotMatchesFormat( '<div%s<div%s', $wrappedText );
- $this->assertContains( '<div', $wrappedText2 );
- $this->assertStringNotMatchesFormat( '<div%s<div%s', $wrappedText2 );
- }
-
/**
* @covers ParserOutput::getText
* @dataProvider provideGetText
$testString .= 'xxx xxx xxx';
}
$testString .= "\n<big id='в'></big>";
- $old = ini_set( 'pcre.backtrack_limit', 200 );
+ $this->setIniSetting( 'pcre.backtrack_limit', 200 );
$result = $this->lc->autoConvert( $testString, 'tg-latn' );
- ini_set( 'pcre.backtrack_limit', $old );
// The в in the id attribute should not get converted to a v
$this->assertFalse(
strpos( $result, 'v' ),