* Updated composer/spdx-licenses from 1.4.0 to 1.5.1 (dev-only).
* Updated mediawiki/codesniffer from 25.0.0 to 26.0.0 (dev-only).
* Updated cssjanus/cssjanus from 1.2.1 to 1.3.0.
+* Updated wikimedia/at-ease from 1.2.0 to 2.0.0.
* …
==== Removed external libraries ====
* (T152908) Added language support for N'Ko (nqo).
=== Breaking changes in 1.34 ===
+* The global functions wfSuppressWarnings and wfRestoreWarnings, deprecated in
+ 1.26, have been removed. Use Wikimedia\AtEase\AtEase::suppressWarnings() and
+ Wikimedia\AtEase\AtEase::restoreWarnings() directly.
* Preferences class, deprecated in 1.31, has been removed.
* The following parts of code, deprecated in 1.32, were removed in favor of
built-in PHP functions:
"php": ">=5.6.99",
"psr/log": "1.0.2",
"wikimedia/assert": "0.2.2",
- "wikimedia/at-ease": "1.2.0",
+ "wikimedia/at-ease": "2.0.0",
"wikimedia/base-convert": "2.0.0",
"wikimedia/cdb": "1.4.1",
"wikimedia/cldr-plural-rule-parser": "1.0.0",
being uploaded, use UploadVerifyFile or UploadVerifyUpload.
$upload: (object) An instance of UploadBase, with all info about the upload
$user: (object) An instance of User, the user uploading this file
-$props: (array) File properties, as returned by FSFile::getPropsFromPath()
+$props: (array|null) File properties, as returned by
+ MWFileProps::getPropsFromPath(). Note this is not always guaranteed to be set,
+ e.g. in test scenarios. Call MWFileProps::getPropsFromPath() yourself in case
+ you need the information.
&$error: output: If the file stashing should be prevented, set this to the
reason in the form of [ messagename, param1, param2, ... ] or a
MessageSpecifier instance (you might want to use ApiMessage to provide machine
(upload comment, file page contents etc.).
$upload: (object) An instance of UploadBase, with all info about the upload
$user: (object) An instance of User, the user uploading this file
-$props: (array) File properties, as returned by FSFile::getPropsFromPath()
+$props: (array|null) File properties, as returned by
+ MWFileProps::getPropsFromPath(). Note this is not always guaranteed to be set,
+ e.g. in test scenarios. Call MWFileProps::getPropsFromPath() yourself in case
+ you need the information.
$comment: (string) Upload log comment (also used as edit summary)
$pageText: (string) File description page text (only used for new uploads)
&$error: output: If the file upload should be prevented, set this to the reason
use MediaWiki\Shell\Shell;
use Wikimedia\ScopedCallback;
use Wikimedia\WrappedString;
+use Wikimedia\AtEase\AtEase;
/**
* Load an extension
if ( $wasRelative ) {
$url = "http:$url";
}
- Wikimedia\suppressWarnings();
+ AtEase::suppressWarnings();
$bits = parse_url( $url );
- Wikimedia\restoreWarnings();
+ AtEase::restoreWarnings();
// parse_url() returns an array without scheme for some invalid URLs, e.g.
// parse_url("%0Ahttp://example.com") == [ 'host' => '%0Ahttp', 'path' => 'example.com' ]
if ( !$bits || !isset( $bits['scheme'] ) ) {
return $besttype;
}
-/**
- * Reference-counted warning suppression
- *
- * @deprecated since 1.26, use Wikimedia\suppressWarnings() directly
- * @param bool $end
- */
-function wfSuppressWarnings( $end = false ) {
- Wikimedia\suppressWarnings( $end );
-}
-
-/**
- * @deprecated since 1.26, use Wikimedia\restoreWarnings() directly
- * Restore error level to previous value
- */
-function wfRestoreWarnings() {
- Wikimedia\restoreWarnings();
-}
-
/**
* Get a timestamp string in one of various formats
*
}
// Turn off the normal warning, we're doing our own below
- Wikimedia\suppressWarnings();
+ AtEase::suppressWarnings();
$ok = mkdir( $dir, $mode, true ); // PHP5 <3
- Wikimedia\restoreWarnings();
+ AtEase::restoreWarnings();
if ( !$ok ) {
// directory may have been created on another request since we last checked
# This check may also protect against code injection in
# case of broken installations.
- Wikimedia\suppressWarnings();
+ AtEase::suppressWarnings();
$haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
- Wikimedia\restoreWarnings();
+ AtEase::restoreWarnings();
if ( !$haveDiff3 ) {
wfDebug( "diff3 not found\n" );
}
global $wgDiff;
- Wikimedia\suppressWarnings();
+ AtEase::suppressWarnings();
$haveDiff = $wgDiff && file_exists( $wgDiff );
- Wikimedia\restoreWarnings();
+ AtEase::restoreWarnings();
# This check may also protect against code injection in
# case of broken installations.
if ( session_id() !== $session->getId() ) {
session_id( $session->getId() );
}
- Wikimedia\quietCall( 'session_start' );
+ AtEase::quietCall( 'session_start' );
}
/**
/// @todo FIXME: this sort of dupes some code in WebRequest::getRequestUrl()
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
// Strip the query string...
- list( $path ) = explode( '?', $_SERVER['REQUEST_URI'], 2 );
+ $path = explode( '?', $_SERVER['REQUEST_URI'], 2 )[0];
} elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
// Probably IIS. QUERY_STRING appears separately.
$path = $_SERVER['SCRIPT_NAME'];
*/
protected function printResult( $httpCode = 0 ) {
if ( $this->getConfig()->get( 'DebugAPI' ) !== false ) {
- $this->addWarning( 'apiwarn-wgDebugAPI' );
+ $this->addWarning( 'apiwarn-wgdebugapi' );
}
$printer = $this->mPrinter;
protected function getExamplesMessages() {
return [
'action=query&list=allimages&aifrom=B'
- => 'apihelp-query+allimages-example-B',
+ => 'apihelp-query+allimages-example-b',
'action=query&list=allimages&aiprop=user|timestamp|url&' .
'aisort=timestamp&aidir=older'
=> 'apihelp-query+allimages-example-recent',
return [
"action=query&list={$name}&{$p}from=B&{$p}prop=ids|title"
- => "apihelp-$path-example-B",
+ => "apihelp-$path-example-b",
"action=query&list={$name}&{$p}unique=&{$p}from=B"
=> "apihelp-$path-example-unique",
"action=query&generator={$name}&g{$p}unique=&g{$p}from=B"
protected function getExamplesMessages() {
return [
'action=query&list=allpages&apfrom=B'
- => 'apihelp-query+allpages-example-B',
+ => 'apihelp-query+allpages-example-b',
'action=query&generator=allpages&gaplimit=4&gapfrom=T&prop=info'
=> 'apihelp-query+allpages-example-generator',
'action=query&generator=allpages&gaplimit=2&' .
protected function getExamplesMessages() {
return [
'action=query&list=allusers&aufrom=Y'
- => 'apihelp-query+allusers-example-Y',
+ => 'apihelp-query+allusers-example-y',
];
}
"apihelp-query+allfileusages-paramvalue-prop-title": "Adds the title of the file.",
"apihelp-query+allfileusages-param-limit": "How many total items to return.",
"apihelp-query+allfileusages-param-dir": "The direction in which to list.",
- "apihelp-query+allfileusages-example-B": "List file titles, including missing ones, with page IDs they are from, starting at <kbd>B</kbd>.",
+ "apihelp-query+allfileusages-example-b": "List file titles, including missing ones, with page IDs they are from, starting at <kbd>B</kbd>.",
"apihelp-query+allfileusages-example-unique": "List unique file titles.",
"apihelp-query+allfileusages-example-unique-generator": "Gets all file titles, marking the missing ones.",
"apihelp-query+allfileusages-example-generator": "Gets pages containing the files.",
"apihelp-query+allimages-param-filterbots": "How to filter files uploaded by bots. Can only be used with $1sort=timestamp. Cannot be used together with $1user.",
"apihelp-query+allimages-param-mime": "What MIME types to search for, e.g. <kbd>image/jpeg</kbd>.",
"apihelp-query+allimages-param-limit": "How many images in total to return.",
- "apihelp-query+allimages-example-B": "Show a list of files starting at the letter <kbd>B</kbd>.",
+ "apihelp-query+allimages-example-b": "Show a list of files starting at the letter <kbd>B</kbd>.",
"apihelp-query+allimages-example-recent": "Show a list of recently uploaded files, similar to [[Special:NewFiles]].",
"apihelp-query+allimages-example-mimetypes": "Show a list of files with MIME type <kbd>image/png</kbd> or <kbd>image/gif</kbd>",
"apihelp-query+allimages-example-generator": "Show info about 4 files starting at the letter <kbd>T</kbd>.",
"apihelp-query+alllinks-param-namespace": "The namespace to enumerate.",
"apihelp-query+alllinks-param-limit": "How many total items to return.",
"apihelp-query+alllinks-param-dir": "The direction in which to list.",
- "apihelp-query+alllinks-example-B": "List linked titles, including missing ones, with page IDs they are from, starting at <kbd>B</kbd>.",
+ "apihelp-query+alllinks-example-b": "List linked titles, including missing ones, with page IDs they are from, starting at <kbd>B</kbd>.",
"apihelp-query+alllinks-example-unique": "List unique linked titles.",
"apihelp-query+alllinks-example-unique-generator": "Gets all linked titles, marking the missing ones.",
"apihelp-query+alllinks-example-generator": "Gets pages containing the links.",
"apihelp-query+allpages-param-dir": "The direction in which to list.",
"apihelp-query+allpages-param-filterlanglinks": "Filter based on whether a page has langlinks. Note that this may not consider langlinks added by extensions.",
"apihelp-query+allpages-param-prexpiry": "Which protection expiry to filter the page on:\n;indefinite:Get only pages with indefinite protection expiry.\n;definite:Get only pages with a definite (specific) protection expiry.\n;all:Get pages with any protections expiry.",
- "apihelp-query+allpages-example-B": "Show a list of pages starting at the letter <kbd>B</kbd>.",
+ "apihelp-query+allpages-example-b": "Show a list of pages starting at the letter <kbd>B</kbd>.",
"apihelp-query+allpages-example-generator": "Show info about 4 pages starting at the letter <kbd>T</kbd>.",
"apihelp-query+allpages-example-generator-revisions": "Show content of first 2 non-redirect pages beginning at <kbd>Re</kbd>.",
"apihelp-query+allredirects-param-namespace": "The namespace to enumerate.",
"apihelp-query+allredirects-param-limit": "How many total items to return.",
"apihelp-query+allredirects-param-dir": "The direction in which to list.",
- "apihelp-query+allredirects-example-B": "List target pages, including missing ones, with page IDs they are from, starting at <kbd>B</kbd>.",
+ "apihelp-query+allredirects-example-b": "List target pages, including missing ones, with page IDs they are from, starting at <kbd>B</kbd>.",
"apihelp-query+allredirects-example-unique": "List unique target pages.",
"apihelp-query+allredirects-example-unique-generator": "Gets all target pages, marking the missing ones.",
"apihelp-query+allredirects-example-generator": "Gets pages containing the redirects.",
"apihelp-query+alltransclusions-param-namespace": "The namespace to enumerate.",
"apihelp-query+alltransclusions-param-limit": "How many total items to return.",
"apihelp-query+alltransclusions-param-dir": "The direction in which to list.",
- "apihelp-query+alltransclusions-example-B": "List transcluded titles, including missing ones, with page IDs they are from, starting at <kbd>B</kbd>.",
+ "apihelp-query+alltransclusions-example-b": "List transcluded titles, including missing ones, with page IDs they are from, starting at <kbd>B</kbd>.",
"apihelp-query+alltransclusions-example-unique": "List unique transcluded titles.",
"apihelp-query+alltransclusions-example-unique-generator": "Gets all transcluded titles, marking the missing ones.",
"apihelp-query+alltransclusions-example-generator": "Gets pages containing the transclusions.",
"apihelp-query+allusers-param-witheditsonly": "Only list users who have made edits.",
"apihelp-query+allusers-param-activeusers": "Only list users active in the last $1 {{PLURAL:$1|day|days}}.",
"apihelp-query+allusers-param-attachedwiki": "With <kbd>$1prop=centralids</kbd>, also indicate whether the user is attached with the wiki identified by this ID.",
- "apihelp-query+allusers-example-Y": "List users starting at <kbd>Y</kbd>.",
+ "apihelp-query+allusers-example-y": "List users starting at <kbd>Y</kbd>.",
"apihelp-query+authmanagerinfo-summary": "Retrieve information about the current authentication status.",
"apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Test whether the user's current authentication status is sufficient for the specified security-sensitive operation.",
"apiwarn-validationfailed-cannotset": "cannot be set by this module.",
"apiwarn-validationfailed-keytoolong": "key too long (no more than $1 bytes allowed).",
"apiwarn-validationfailed": "Validation error for <kbd>$1</kbd>: $2",
- "apiwarn-wgDebugAPI": "<strong>Security Warning</strong>: <var>$wgDebugAPI</var> is enabled.",
+ "apiwarn-wgdebugapi": "<strong>Security Warning</strong>: <var>$wgDebugAPI</var> is enabled.",
"api-feed-error-title": "Error ($1)",
"api-usage-docref": "See $1 for API usage.",
"apihelp-query+allfileusages-paramvalue-prop-title": "{{doc-apihelp-paramvalue|query+allfileusages|prop|title}}",
"apihelp-query+allfileusages-param-limit": "{{doc-apihelp-param|query+allfileusages|limit}}",
"apihelp-query+allfileusages-param-dir": "{{doc-apihelp-param|query+allfileusages|dir}}",
- "apihelp-query+allfileusages-example-B": "{{doc-apihelp-example|query+allfileusages}}",
+ "apihelp-query+allfileusages-example-b": "{{doc-apihelp-example|query+allfileusages}}",
"apihelp-query+allfileusages-example-unique": "{{doc-apihelp-example|query+allfileusages}}",
"apihelp-query+allfileusages-example-unique-generator": "{{doc-apihelp-example|query+allfileusages}}",
"apihelp-query+allfileusages-example-generator": "{{doc-apihelp-example|query+allfileusages}}",
"apihelp-query+allimages-param-filterbots": "{{doc-apihelp-param|query+allimages|filterbots}}",
"apihelp-query+allimages-param-mime": "{{doc-apihelp-param|query+allimages|mime}}",
"apihelp-query+allimages-param-limit": "{{doc-apihelp-param|query+allimages|limit}}",
- "apihelp-query+allimages-example-B": "{{doc-apihelp-example|query+allimages}}",
+ "apihelp-query+allimages-example-b": "{{doc-apihelp-example|query+allimages}}",
"apihelp-query+allimages-example-recent": "{{doc-apihelp-example|query+allimages}}",
"apihelp-query+allimages-example-mimetypes": "{{doc-apihelp-example|query+allimages}}",
"apihelp-query+allimages-example-generator": "{{doc-apihelp-example|query+allimages}}",
"apihelp-query+alllinks-param-namespace": "{{doc-apihelp-param|query+alllinks|namespace}}",
"apihelp-query+alllinks-param-limit": "{{doc-apihelp-param|query+alllinks|limit}}",
"apihelp-query+alllinks-param-dir": "{{doc-apihelp-param|query+alllinks|dir}}",
- "apihelp-query+alllinks-example-B": "{{doc-apihelp-example|query+alllinks}}",
+ "apihelp-query+alllinks-example-b": "{{doc-apihelp-example|query+alllinks}}",
"apihelp-query+alllinks-example-unique": "{{doc-apihelp-example|query+alllinks}}",
"apihelp-query+alllinks-example-unique-generator": "{{doc-apihelp-example|query+alllinks}}",
"apihelp-query+alllinks-example-generator": "{{doc-apihelp-example|query+alllinks}}",
"apihelp-query+allpages-param-dir": "{{doc-apihelp-param|query+allpages|dir}}",
"apihelp-query+allpages-param-filterlanglinks": "{{doc-apihelp-param|query+allpages|filterlanglinks}}",
"apihelp-query+allpages-param-prexpiry": "{{doc-apihelp-param|query+allpages|prexpiry}}",
- "apihelp-query+allpages-example-B": "{{doc-apihelp-example|query+allpages}}",
+ "apihelp-query+allpages-example-b": "{{doc-apihelp-example|query+allpages}}",
"apihelp-query+allpages-example-generator": "{{doc-apihelp-example|query+allpages}}",
"apihelp-query+allpages-example-generator-revisions": "{{doc-apihelp-example|query+allpages}}",
"apihelp-query+allredirects-summary": "{{doc-apihelp-summary|query+allredirects}}",
"apihelp-query+allredirects-param-namespace": "{{doc-apihelp-param|query+allredirects|namespace}}",
"apihelp-query+allredirects-param-limit": "{{doc-apihelp-param|query+allredirects|limit}}",
"apihelp-query+allredirects-param-dir": "{{doc-apihelp-param|query+allredirects|dir}}",
- "apihelp-query+allredirects-example-B": "{{doc-apihelp-example|query+allredirects}}",
+ "apihelp-query+allredirects-example-b": "{{doc-apihelp-example|query+allredirects}}",
"apihelp-query+allredirects-example-unique": "{{doc-apihelp-example|query+allredirects}}",
"apihelp-query+allredirects-example-unique-generator": "{{doc-apihelp-example|query+allredirects}}",
"apihelp-query+allredirects-example-generator": "{{doc-apihelp-example|query+allredirects}}",
"apihelp-query+alltransclusions-param-namespace": "{{doc-apihelp-param|query+alltransclusions|namespace}}",
"apihelp-query+alltransclusions-param-limit": "{{doc-apihelp-param|query+alltransclusions|limit}}",
"apihelp-query+alltransclusions-param-dir": "{{doc-apihelp-param|query+alltransclusions|dir}}",
- "apihelp-query+alltransclusions-example-B": "{{doc-apihelp-example|query+alltransclusions}}",
+ "apihelp-query+alltransclusions-example-b": "{{doc-apihelp-example|query+alltransclusions}}",
"apihelp-query+alltransclusions-example-unique": "{{doc-apihelp-example|query+alltransclusions}}",
"apihelp-query+alltransclusions-example-unique-generator": "{{doc-apihelp-example|query+alltransclusions}}",
"apihelp-query+alltransclusions-example-generator": "{{doc-apihelp-example|query+alltransclusions}}",
"apihelp-query+allusers-param-witheditsonly": "{{doc-apihelp-param|query+allusers|witheditsonly}}",
"apihelp-query+allusers-param-activeusers": "{{doc-apihelp-param|query+allusers|activeusers|params=* $1 - Value of [[mw:Manual:$wgActiveUserDays]]|paramstart=2}}",
"apihelp-query+allusers-param-attachedwiki": "{{doc-apihelp-param|query+allusers|attachedwiki}}",
- "apihelp-query+allusers-example-Y": "{{doc-apihelp-example|query+allusers}}",
+ "apihelp-query+allusers-example-y": "{{doc-apihelp-example|query+allusers}}",
"apihelp-query+authmanagerinfo-summary": "{{doc-apihelp-summary|query+authmanagerinfo}}",
"apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "{{doc-apihelp-param|query+authmanagerinfo|securitysensitiveoperation}}",
"apihelp-query+authmanagerinfo-param-requestsfor": "{{doc-apihelp-param|query+authmanagerinfo|requestsfor}}",
"apiwarn-validationfailed-cannotset": "{{doc-apierror}}\n\nUsed with {{msg-mw|apiwarn-validationfailed}}.",
"apiwarn-validationfailed-keytoolong": "{{doc-apierror}}\n\nUsed with {{msg-mw|apiwarn-validationfailed}}.\n\nParameters:\n* $1 - Maximum allowed key length in bytes.",
"apiwarn-validationfailed": "{{doc-apierror}}\n\nParameters:\n* $1 - User preference name.\n* $2 - Failure message, such as {{msg-mw|apiwarn-validationfailed-badpref}}. Probably already ends with punctuation",
- "apiwarn-wgDebugAPI": "{{doc-apierror}}",
+ "apiwarn-wgdebugapi": "{{doc-apierror}}",
"api-feed-error-title": "Used as a feed item title when an error occurs in <kbd>action=feedwatchlist</kbd>.\n\nParameters:\n* $1 - API error code\n{{Identical|Error}}",
"api-usage-docref": "\n\nParameters:\n* $1 - URL of the API auto-generated documentation.",
"api-usage-mailinglist-ref": "{{doc-apierror}} Also used in the error response.",
return $this->watchMsgCache->getWithSetCallback(
"watching-users-msg:$count",
function () use ( $count ) {
- return $this->msg( 'number_of_watching_users_RCview' )
+ return $this->msg( 'number-of-watching-users-for-recent-changes' )
->numParams( $count )->escaped();
}
);
public function salvage( SalvageableService $other ) {
Assert::parameterType( self::class, $other, '$other' );
- /** @var ConfigRepository $other */
- $otherCurrentObj = $other->current();
foreach ( $other->configItems['public'] as $name => $otherConfig ) {
if ( isset( $this->configItems['public'][$name] ) ) {
continue;
}
$this->add( $name, $otherConfig );
-
- // recover the pointer of the other config repository
- if ( $otherCurrentObj === $otherConfig ) {
- end( $this->configItems['public'] );
- }
}
foreach ( $other->configItems['private'] as $name => $otherConfig ) {
if ( isset( $this->configItems['private'][$name] ) ) {
}
$this->add( $name, $otherConfig );
-
- // recover the pointer of the other config repository
- if ( $otherCurrentObj === $otherConfig ) {
- end( $this->configItems['private'] );
- }
}
// disable $other
// value_to_add_forward: a 0 or 1 that we add to the start
// offset to make it odd/even
- if ( ( $M & 1 ) == 1 ) {
+ if ( $M & 1 ) {
$value_to_add_forward = 1;
} else {
$value_to_add_forward = 0;
}
- if ( ( $N & 1 ) == 1 ) {
+ if ( $N & 1 ) {
$value_to_add_backward = 1;
} else {
$value_to_add_backward = 0;
$V1[$limit_min_1] = $N;
$limit = min( $this->maxDifferences, ceil( ( $N + $M ) / 2 ) );
- if ( ( $delta & 1 ) == 1 ) {
+ if ( $delta & 1 ) {
for ( $d = 0; $d <= $limit; ++$d ) {
$start_diag = max( $value_to_add_forward + $start_forward, -$d );
$end_diag = min( $end_forward, $d );
$good = true;
// Must go here because an old version of PCRE can prevent other checks from completing
- list( $pcreVersion ) = explode( ' ', PCRE_VERSION, 2 );
+ $pcreVersion = explode( ' ', PCRE_VERSION, 2 )[0];
if ( version_compare( $pcreVersion, self::MINIMUM_PCRE_VERSION, '<' ) ) {
$this->showError( 'config-pcre-old', self::MINIMUM_PCRE_VERSION, $pcreVersion );
$good = false;
$this->dumpForm( $file, $chunkLength, $indent + 1 );
} else {
fseek( $file, $chunkLength, SEEK_CUR );
- if ( ( $chunkLength & 1 ) == 1 ) {
+ if ( $chunkLength & 1 ) {
// Padding byte between chunks
fseek( $file, 1, SEEK_CUR );
}
private function skipChunk( $file, $chunkLength ) {
fseek( $file, $chunkLength, SEEK_CUR );
- if ( ( $chunkLength & 0x01 ) == 1 && !feof( $file ) ) {
+ if ( ( $chunkLength & 1 ) && !feof( $file ) ) {
// padding byte
fseek( $file, 1, SEEK_CUR );
}
$langName = Language::fetchLanguageName( $lowLang );
if ( $langName === '' ) {
// try just the base language name. (aka en-US -> en ).
- list( $langPrefix ) = explode( '-', $lowLang, 2 );
+ $langPrefix = explode( '-', $lowLang, 2 )[0];
$langName = Language::fetchLanguageName( $langPrefix );
if ( $langName === '' ) {
// give up.
use Profiler;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;
+use Wikimedia\AtEase\AtEase;
/**
* Class used for executing shell commands
// TODO replace with clear_last_error when requirements are bumped to PHP7
set_error_handler( function () {
}, 0 );
- \Wikimedia\suppressWarnings();
+ AtEase::suppressWarnings();
trigger_error( '' );
- \Wikimedia\restoreWarnings();
+ AtEase::restoreWarnings();
restore_error_handler();
$readPipes = array_filter( $pipes, function ( $fd ) use ( $desc ) {
* @return array Associative array of strings
*/
static function getSkinNames() {
- return SkinFactory::getDefaultInstance()->getSkinNames();
+ $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
+ return $skinFactory->getSkinNames();
}
/**
function getCategoryLinks() {
$out = $this->getOutput();
$allCats = $out->getCategoryLinks();
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
if ( $allCats === [] ) {
return '';
if ( !empty( $allCats['normal'] ) ) {
$t = $embed . implode( $pop . $embed, $allCats['normal'] ) . $pop;
- $msg = $this->msg( 'pagecategories' )->numParams( count( $allCats['normal'] ) )->escaped();
+ $msg = $this->msg( 'pagecategories' )->numParams( count( $allCats['normal'] ) );
$linkPage = $this->msg( 'pagecategorieslink' )->inContentLanguage()->text();
$title = Title::newFromText( $linkPage );
- $link = $title ? Linker::link( $title, $msg ) : $msg;
+ $link = $title ? $linkRenderer->makeLink( $title, $msg->text() ) : $msg->escaped();
$s .= '<div id="mw-normal-catlinks" class="mw-normal-catlinks">' .
$link . $colon . '<ul>' . $t . '</ul></div>';
}
*/
function drawCategoryBrowser( $tree ) {
$return = '';
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
foreach ( $tree as $element => $parent ) {
if ( empty( $parent ) ) {
# add our current element to the list
$eltitle = Title::newFromText( $element );
- $return .= Linker::link( $eltitle, htmlspecialchars( $eltitle->getText() ) );
+ $return .= $linkRenderer->makeLink( $eltitle, $eltitle->getText() );
}
return $return;
function getUndeleteLink() {
$action = $this->getRequest()->getVal( 'action', 'view' );
$title = $this->getTitle();
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
if ( ( !$title->exists() || $action == 'history' ) &&
$title->quickUserCan( 'deletedhistory', $this->getUser() )
}
return $this->msg( $msg )->rawParams(
- Linker::linkKnown(
+ $linkRenderer->makeKnownLink(
SpecialPage::getTitleFor( 'Undelete', $this->getTitle()->getPrefixedDBkey() ),
- $this->msg( 'restorelink' )->numParams( $n )->escaped() )
+ $this->msg( 'restorelink' )->numParams( $n )->text() )
)->escaped();
}
}
* @return string
*/
function subPageSubtitle( $out = null ) {
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
if ( $out === null ) {
$out = $this->getOutput();
}
$linkObj = Title::newFromText( $growinglink );
if ( is_object( $linkObj ) && $linkObj->isKnown() ) {
- $getlink = Linker::linkKnown(
- $linkObj,
- htmlspecialchars( $display )
+ $getlink = $linkRenderer->makeKnownLink(
+ $linkObj, $display
);
$c++;
* @return string
*/
function getCopyright( $type = 'detect' ) {
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
if ( $type == 'detect' ) {
if ( !$this->isRevisionCurrent()
&& !$this->msg( 'history_copyright' )->inContentLanguage()->isDisabled()
if ( $config->get( 'RightsPage' ) ) {
$title = Title::newFromText( $config->get( 'RightsPage' ) );
- $link = Linker::linkKnown( $title, $config->get( 'RightsText' ) );
+ $link = $linkRenderer->makeKnownLink(
+ $title, new HtmlArmor( $config->get( 'RightsText' ) )
+ );
} elseif ( $config->get( 'RightsUrl' ) ) {
$link = Linker::makeExternalLink( $config->get( 'RightsUrl' ), $config->get( 'RightsText' ) );
} elseif ( $config->get( 'RightsText' ) ) {
* @return string
*/
function mainPageLink() {
- $s = Linker::linkKnown(
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
+ $s = $linkRenderer->makeKnownLink(
Title::newMainPage(),
- $this->msg( 'mainpage' )->escaped()
+ $this->msg( 'mainpage' )->text()
);
return $s;
*/
public function footerLink( $desc, $page ) {
$title = $this->footerLinkTitle( $desc, $page );
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
if ( !$title ) {
return '';
}
- return Linker::linkKnown(
+ return $linkRenderer->makeKnownLink(
$title,
- $this->msg( $desc )->escaped()
+ $this->msg( $desc )->text()
);
}
$user = $this->getUser();
$newtalks = $user->getNewMessageLinks();
$out = $this->getOutput();
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
// Allow extensions to disable or modify the new messages alert
if ( !Hooks::run( 'GetNewMessagesAlert', [ &$newMessagesAlert, $newtalks, $user, $out ] ) ) {
// 999 signifies "more than one revision". We don't know how many, and even if we did,
// the number of revisions or authors is not necessarily the same as the number of
// "messages".
- $newMessagesLink = Linker::linkKnown(
+ $newMessagesLink = $linkRenderer->makeKnownLink(
$uTalkTitle,
- $this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(),
+ $this->msg( 'newmessageslinkplural' )->params( $plural )->text(),
[],
$uTalkTitle->isRedirect() ? [ 'redirect' => 'no' ] : []
);
- $newMessagesDiffLink = Linker::linkKnown(
+ $newMessagesDiffLink = $linkRenderer->makeKnownLink(
$uTalkTitle,
- $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(),
+ $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->text(),
[],
$lastSeenRev !== null
? [ 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' ]
$result = '<span class="mw-editsection"><span class="mw-editsection-bracket">[</span>';
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
$linksHtml = [];
foreach ( $links as $k => $linkDetails ) {
- $linksHtml[] = Linker::linkKnown(
+ $linksHtml[] = $linkRenderer->makeKnownLink(
$linkDetails['targetTitle'],
- htmlspecialchars( $linkDetails['text'] ),
+ $linkDetails['text'],
$linkDetails['attribs'],
$linkDetails['query']
);
[
'name' => 'userExpLevel',
- 'title' => 'rcfilters-filtergroup-userExpLevel',
+ 'title' => 'rcfilters-filtergroup-user-experience-level',
'class' => ChangesListStringOptionsFilterGroup::class,
'isFullCoverage' => true,
'filters' => [
[
'name' => 'lastRevision',
- 'title' => 'rcfilters-filtergroup-lastRevision',
+ 'title' => 'rcfilters-filtergroup-lastrevision',
'class' => ChangesListBooleanFilterGroup::class,
'priority' => -7,
'filters' => [
/**
* Get a DB connection to be used for slow recache queries
- * @return \Wikimedia\Rdbms\Database
+ * @return IDatabase
*/
function getRecacheDB() {
return wfGetDB( DB_REPLICA, [ $this->getName(), 'QueryPage::recache', 'vslow' ] );
$this->setHeaders();
if ( !$this->getConfig()->get( 'UseDatabaseMessages' ) ) {
- $out->addWikiMsg( 'allmessagesnotsupportedDB' );
+ $out->addWikiMsg( 'allmessages-not-supported-database' );
return;
}
'type' => 'radio',
'name' => 'filter',
'label-message' => 'allmessages-filter',
- 'options' => [
- $this->msg( 'allmessages-filter-unmodified' )->text() => 'unmodified',
- $this->msg( 'allmessages-filter-all' )->text() => 'all',
- $this->msg( 'allmessages-filter-modified' )->text() => 'modified',
+ 'options-messages' => [
+ 'allmessages-filter-unmodified' => 'unmodified',
+ 'allmessages-filter-all' => 'all',
+ 'allmessages-filter-modified' => 'modified',
],
'default' => 'all',
'flatlist' => true,
function setParameter( $subpage ) {
// parse $subpage to pull out the parts
$parts = explode( '/', $subpage, 2 );
- $this->mType = count( $parts ) > 0 ? $parts[0] : null;
- $this->mValue = count( $parts ) > 1 ? $parts[1] : null;
+ $this->mType = $parts[0];
+ $this->mValue = $parts[1] ?? null;
}
/**
*/
private function getRestrictionListHTML( stdClass $row ) {
$items = [];
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
foreach ( $this->restrictions as $restriction ) {
if ( $restriction->getBlockId() !== (int)$row->ipb_id ) {
$items[$restriction->getType()][] = Html::rawElement(
'li',
[],
- Linker::link( $restriction->getTitle() )
+ $linkRenderer->makeLink( $restriction->getTitle() )
);
}
break;
case NamespaceRestriction::TYPE:
$text = $restriction->getValue() === NS_MAIN
- ? $this->msg( 'blanknamespace' )
+ ? $this->msg( 'blanknamespace' )->text()
: $this->getLanguage()->getFormattedNsText(
$restriction->getValue()
);
$items[$restriction->getType()][] = Html::rawElement(
'li',
[],
- Linker::link(
+ $linkRenderer->makeLink(
SpecialPage::getTitleValueFor( 'Allpages' ),
$text,
[],
public $namespace = '';
/**
- * @var \Wikimedia\Rdbms\Database
+ * @var IDatabase
*/
public $mDb;
* @return string
*/
function getPageHeader() {
- list( $self ) = explode( '/', $this->getTitle()->getPrefixedDBkey() );
+ $self = explode( '/', $this->getTitle()->getPrefixedDBkey(), 2 )[0];
$groupOptions = [ $this->msg( 'group-all' )->text() => '' ];
foreach ( $this->getAllGroups() as $group => $groupText ) {
// link to the group description page, if it exists
$linkTitle = self::getGroupPage( $group );
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
if ( $linkTitle ) {
if ( $format === 'wiki' ) {
$linkPage = $linkTitle->getFullText();
$groupLink = "[[$linkPage|$groupName]]";
} else {
- $groupLink = Linker::link( $linkTitle, htmlspecialchars( $groupName ) );
+ $groupLink = $linkRenderer->makeLink( $linkTitle, $groupName );
}
} else {
$groupLink = htmlspecialchars( $groupName );
"virus-scanfailed": "scan failed (code $1)",
"virus-unknownscanner": "unknown antivirus:",
"logouttext": "<strong>You are now logged out.</strong>\n\nNote that some pages may continue to be displayed as if you were still logged in, until you clear your browser cache.",
+ "logging-out-notify": "You are being logged out, please wait.",
+ "logout-failed": "Cannot log out now: $1",
"cannotlogoutnow-title": "Cannot log out now",
"cannotlogoutnow-text": "Logging out is not possible when using $1.",
"welcomeuser": "Welcome, $1!",
"rcfilters-filter-editsbyself-description": "Your own contributions.",
"rcfilters-filter-editsbyother-label": "Changes by others",
"rcfilters-filter-editsbyother-description": "All changes except your own.",
- "rcfilters-filtergroup-userExpLevel": "User registration and experience",
+ "rcfilters-filtergroup-user-experience-level": "User registration and experience",
"rcfilters-filter-user-experience-level-registered-label": "Registered",
"rcfilters-filter-user-experience-level-registered-description": "Logged-in editors.",
"rcfilters-filter-user-experience-level-unregistered-label": "Unregistered",
"rcfilters-hideminor-conflicts-typeofchange-global": "The \"Minor edits\" filter conflicts with one or more Type of change filters, because certain types of change cannot be designated as \"minor\". The conflicting filters are marked in the Active filters area, above.",
"rcfilters-hideminor-conflicts-typeofchange": "Certain types of change cannot be designated as \"minor\", so this filter conflicts with the following Type of Change filters: $1",
"rcfilters-typeofchange-conflicts-hideminor": "This Type of change filter conflicts with the \"Minor edits\" filter. Certain types of change cannot be designated as \"minor\".",
- "rcfilters-filtergroup-lastRevision": "Latest revisions",
+ "rcfilters-filtergroup-lastrevision": "Latest revisions",
"rcfilters-filter-lastrevision-label": "Latest revision",
"rcfilters-filter-lastrevision-description": "Only the most recent change to a page.",
"rcfilters-filter-previousrevision-label": "Not the latest revision",
"newpageletter": "N",
"boteditletter": "b",
"unpatrolledletter": "!",
- "number_of_watching_users_RCview": "[$1]",
- "number_of_watching_users_pageview": "[$1 watching {{PLURAL:$1|user|users}}]",
+ "number-of-watching-users-for-recent-changes": "[$1]",
"rc-change-size": "$1",
"rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} after change",
"newsectionsummary": "/* $1 */ new section",
"img-auth-nopathinfo": "Missing path information.\nYour server must be set up to pass the REQUEST_URI and/or PATH_INFO variables.\nIf it is, try enabling $wgUsePathInfo.\nSee https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
"img-auth-notindir": "Requested path is not in the configured upload directory.",
"img-auth-badtitle": "Unable to construct a valid title from \"$1\".",
- "img-auth-nologinnWL": "You are not logged in and \"$1\" is not in the whitelist.",
"img-auth-nofile": "File \"$1\" does not exist.",
"img-auth-isdir": "You are trying to access a directory \"$1\".\nOnly file access is allowed.",
"img-auth-streaming": "Streaming \"$1\".",
"allmessagesdefault": "Default message text",
"allmessagescurrent": "Current message text",
"allmessagestext": "This is a list of system messages available in the MediaWiki namespace.\nPlease visit [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] and [https://translatewiki.net translatewiki.net] if you wish to contribute to the generic MediaWiki localisation.",
- "allmessagesnotsupportedDB": "This page cannot be used because <strong>$wgUseDatabaseMessages</strong> has been disabled.",
+ "allmessages-not-supported-database": "This page cannot be used because <strong>$wgUseDatabaseMessages</strong> has been disabled.",
"allmessages-filter-legend": "Filter",
"allmessages-filter": "Filter by customization state:",
"allmessages-filter-unmodified": "Unmodified",
"virus-scanfailed": "Used as error message. \"scan\" stands for \"virus scan\". Parameters:\n* $1 - exit code of virus scanner",
"virus-unknownscanner": "Used as error message. This message is followed by the virus scanner name.",
"logouttext": "Log out message. Parameters:\n* $1 - (Unused) an URL to [[Special:Userlogin]] containing <code>returnto</code> and <code>returntoquery</code> parameters",
+ "logging-out-notify": "The message when the user is being logged out",
+ "logout-failed": "Message when log out fails in notification popup. Parameters:\n* $1 - Error message",
"cannotlogoutnow-title": "Error page title shown when logging out is not possible.",
"cannotlogoutnow-text": "Error page text shown when logging out is not possible. Parameters:\n* $1 - Session type in use that makes it not possible to log out, from a message like {{msg-mw|sessionprovider-mediawiki-session-cookiesessionprovider}}.",
"welcomeuser": "Text for a welcome heading that users see after registering a user account.\n\nParameters:\n* $1 - the username of the new user. See [[phab:T44215]]",
"rcfilters-filter-editsbyself-description": "Description for the filter for showing edits made by the current user.",
"rcfilters-filter-editsbyother-label": "Label for the filter for showing edits made by anyone other than the current user.",
"rcfilters-filter-editsbyother-description": "Description for the filter for showing edits made by anyone other than the current user.",
- "rcfilters-filtergroup-userExpLevel": "Title for the filter group for user experience levels.",
+ "rcfilters-filtergroup-user-experience-level": "Title for the filter group for user experience levels.",
"rcfilters-filter-user-experience-level-registered-label": "Label for the filter for showing edits made by logged-in editors.",
"rcfilters-filter-user-experience-level-registered-description": "Description for the filter for showing edits made by logged-in editors.",
"rcfilters-filter-user-experience-level-unregistered-label": "Label for the filter for showing edits made by anonymous editors.",
"rcfilters-hideminor-conflicts-typeofchange-global": "The \"Minor edits\" filter is conflicting with one or more Type of Change filters, because certain types of change cannot be designated as \"minor.\" The conflicting filters are marked in the Active Filters area, above.\n\n\"Type of Change\" is {{msg-mw|Rcfilters-filtergroup-changetype}}.",
"rcfilters-hideminor-conflicts-typeofchange": "Tooltip shown when hovering over the Minor edits tag, when a Type of Change filter is also selected.\n\n\"Minor edits\" is {{msg-mw|rcfilters-filter-minor-label}}.\n\n\"Type of change\" is {{msg-mw|rcfilters-filtergroup-changetype}}.\n\nThis indicates that no results will be shown. Parameters:\n* $1 - Comma-separated string of selected Type of Change filters, e.g. \"Category, Logged Actions\"\n* $2 - Count of selected User Experience Level filters, for PLURAL",
"rcfilters-typeofchange-conflicts-hideminor": "Tooltip shown when hovering over a Type of change filter tag, when the Minor edits filter is also selected.\n\n\"Minor edits\" is {{msg-mw|rcfilters-filter-minor-label}}.\n\n\"Type of change\" is {{msg-mw|rcfilters-filtergroup-changetype}}.\n\nThis indicates that no results will be shown.",
- "rcfilters-filtergroup-lastRevision": "Title for the filter group for last revision",
+ "rcfilters-filtergroup-lastrevision": "Title for the filter group for last revision",
"rcfilters-filter-lastrevision-label": "Title for the filter for showing changes on last revision of a page.",
"rcfilters-filter-lastrevision-description": "Description for the filter for showing changes on last revision of a page.",
"rcfilters-filter-previousrevision-label": "Title for the filter for showing changes on previous revisions of a page.",
"newpageletter": "Very short form of \"'''new page'''\". Used in [[Special:RecentChanges]], [[Special:Watchlist]] and [[Special:Contributions]].\n\n{{Rc single letters}}",
"boteditletter": "Abbreviation of \"'''bot'''\". Appears in [[Special:RecentChanges]] and [[Special:Watchlist]].\n\n{{Rc single letters}}",
"unpatrolledletter": "{{optional}}\n\nUsed in {{msg-mw|Recentchanges-label-legend}}, meaning \"unpatrolled\".\n\n{{Rc single letters}}",
- "number_of_watching_users_RCview": "{{notranslate}}\nParameters:\n* $1 - number of users who are watching",
- "number_of_watching_users_pageview": "Used if <code>$wgPageShowWatchingUsers</code> is true.\n* $1 - number of watching user(s)",
+ "number-of-watching-users-for-recent-changes": "{{notranslate}}\nParameters:\n* $1 - number of users who are watching",
"rc-change-size": "{{optional}}\nDoes not work under $wgMiserMode ([[mwr:48986|r48986]]).\n\nParameters:\n* $1 - size of diff",
"rc-change-size-new": "Tooltip when hovering a change list diff size. Parameters:\n* $1 - the resulting new size (in bytes)",
"newsectionsummary": "Default summary when adding a new section to a page. Parameters:\n* $1 - section title",
"img-auth-nopathinfo": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Missing PATH_INFO - see english description\n{{Doc-important|This is plain text. Do not use any wiki syntax.}}",
"img-auth-notindir": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: When the specified path is not in upload directory.",
"img-auth-badtitle": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Bad title, $1 is the invalid title",
- "img-auth-nologinnWL": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Logged in and file not whitelisted. $1 is the file not in whitelist.",
"img-auth-nofile": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Non existent file, $1 is the file that does not exist.",
"img-auth-isdir": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Trying to access a directory instead of a file, $1 is the directory.",
"img-auth-streaming": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Is now streaming file specified by $1.",
"allmessagesdefault": "The header for the lower row of each column in the table of [[Special:AllMessages]].",
"allmessagescurrent": "The header for the upper row of each column in the table of [[Special:AllMessages]].",
"allmessagestext": "Summary displayed at the top of [[Special:AllMessages]].",
- "allmessagesnotsupportedDB": "This message is displayed on [[Special:AllMessages]] on wikis were the configuration variable $wgUseDatabaseMessages is disabled. It means that the MediaWiki namespace is not used.",
+ "allmessages-not-supported-database": "This message is displayed on [[Special:AllMessages]] on wikis were the configuration variable $wgUseDatabaseMessages is disabled. It means that the MediaWiki namespace is not used.",
"allmessages-filter-legend": "Used in [[Special:AllMessages]].\n\n{{Identical|Filter}}",
"allmessages-filter": "Option used in [[Special:AllMessages]].",
"allmessages-filter-unmodified": "Used in [[Special:AllMessages]].",
function compatChecks() {
$minimumPcreVersion = Installer::MINIMUM_PCRE_VERSION;
- list( $pcreVersion ) = explode( ' ', PCRE_VERSION, 2 );
+ $pcreVersion = explode( ' ', PCRE_VERSION, 2 )[0];
if ( version_compare( $pcreVersion, $minimumPcreVersion, '<' ) ) {
$this->fatalError(
"PCRE $minimumPcreVersion or later is required.\n" .
'dependencies' => [
'jquery.accessKeyLabel',
'jquery.checkboxShiftClick',
+ 'mediawiki.notify',
+ 'mediawiki.api'
],
'targets' => [ 'desktop', 'mobile' ],
+ 'messages' => [
+ 'logout-failed',
+ 'logging-out-notify'
+ ]
],
'mediawiki.page.startup' => [
'scripts' => 'resources/src/mediawiki.page.startup.js',
padding: 0.5em 1em;
}
-/* TODO: Remove this old class once the content caches have cleared */
-/* stylelint-disable-next-line selector-class-pattern */
-.mw-json .value,
.mw-json-value,
.mw-json-single-value {
background-color: #dcfae3;
window.print();
e.preventDefault();
} );
+
+ // Turn logout to a POST action
+ $( '#pt-logout a' ).on( 'click', function ( e ) {
+ var api = new mw.Api(), returnUrl;
+ returnUrl = $( '#pt-logout a' ).attr( 'href' );
+ mw.notify(
+ mw.message( 'logging-out-notify' ),
+ { tag: 'logout', autoHide: false }
+ );
+ api.postWithToken( 'csrf', {
+ action: 'logout'
+ } ).done( function () {
+ // Horrible hack until deprecation of logoutToken in GET is done
+ returnUrl = returnUrl.replace( /logoutToken=.+?($|&)/g, 'logoutToken=%2B%5C' );
+ window.location = returnUrl;
+ } ).fail( function ( e ) {
+ mw.notify(
+ mw.message( 'logout-failed', e ),
+ { type: 'error', tag: 'logout', autoHide: false }
+ );
+ } );
+ e.preventDefault();
+ } );
} );
}() );
*/
class MediaWikiVersionFetcherTest extends MediaWikiTestCase {
- use MediaWikiCoversValidator;
-
public function testReturnsResult() {
global $wgVersion;
$versionFetcher = new MediaWikiVersionFetcher();
$recentChange->numberofWatchingusers = 100;
$line = $oldChangesList->recentChangesLine( $recentChange, false, 1 );
- $this->assertRegExp( "/(number_of_watching_users_RCview: 100)/", $line );
+ $this->assertRegExp( "/(number-of-watching-users-for-recent-changes: 100)/", $line );
}
public function testRecentChangesLine_watchlistCssClass() {
*/
class MWMessagePackTest extends MediaWikiTestCase {
- use MediaWikiCoversValidator;
-
/**
* Provides test cases for MWMessagePackTest::testMessagePack
*
*/
class ResourceLoaderSkinModuleTest extends MediaWikiTestCase {
- use MediaWikiCoversValidator;
-
public static function provideGetStyles() {
// phpcs:disable Generic.Files.LineLength
return [
*/
class ShellTest extends MediaWikiTestCase {
- use MediaWikiCoversValidator;
-
public function testIsDisabled() {
$this->assertInternalType( 'bool', Shell::isDisabled() ); // sanity
}
*/
class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
- use MediaWikiCoversValidator;
-
/**
* @return PHPUnit_Framework_MockObject_MockObject|CommentStore
*/