* Specifying both the class and factory parameters for
ApiModuleManager::addModule is now deprecated. The ObjectFactory spec should
be used instead.
+* The UserIsHidden hook is deprecated. Use GetUserBlock instead, and add a
+ system block that hides the user.
=== Other changes in 1.34 ===
* …
false if a UserGetRights hook might remove the named right.
$right: The user right being checked
-'UserIsHidden': Check if the user's name should be hidden. See User::isHidden().
+'UserIsHidden': DEPRECATED since 1.34 - use GetUserBlock instead, and add a
+system block that hides the user. Check if the user's name should be hidden.
+See User::isHidden().
$user: User in question.
&$hidden: Set true if the user's name should be hidden.
$services = MediaWikiServices::getInstance();
$contLang = $services->getContentLanguage();
- foreach ( $titles as $title ) {
+ $titleObjects = [];
+ foreach ( $titles as $index => $title ) {
if ( is_string( $title ) ) {
try {
$titleObj = Title::newFromTextThrow( $title, $this->mDefaultNamespace );
} else {
$titleObj = $title;
}
+
+ $titleObjects[$index] = $titleObj;
+ }
+
+ // Get gender information
+ $genderCache = $services->getGenderCache();
+ $genderCache->doTitlesArray( $titleObjects, __METHOD__ );
+
+ foreach ( $titleObjects as $index => $titleObj ) {
+ $title = is_string( $titles[$index] ) ? $titles[$index] : false;
$unconvertedTitle = $titleObj->getPrefixedText();
$titleWasConverted = false;
if ( $titleObj->isExternal() ) {
) {
// Language::findVariantLink will modify titleText and titleObj into
// the canonical variant if possible
- $titleText = is_string( $title ) ? $title : $titleObj->getPrefixedText();
+ $titleText = $title !== false ? $title : $titleObj->getPrefixedText();
$contLang->findVariantLink( $titleText, $titleObj );
$titleWasConverted = $unconvertedTitle !== $titleObj->getPrefixedText();
}
if ( $titleWasConverted ) {
$this->mConvertedTitles[$unconvertedTitle] = $titleObj->getPrefixedText();
// In this case the page can't be Special.
- if ( is_string( $title ) && $title !== $unconvertedTitle ) {
+ if ( $title !== false && $title !== $unconvertedTitle ) {
$this->mNormalizedTitles[$title] = $unconvertedTitle;
}
- } elseif ( is_string( $title ) && $title !== $titleObj->getPrefixedText() ) {
+ } elseif ( $title !== false && $title !== $titleObj->getPrefixedText() ) {
$this->mNormalizedTitles[$title] = $titleObj->getPrefixedText();
}
-
- // Need gender information
- if (
- $services->getNamespaceInfo()->
- hasGenderDistinction( $titleObj->getNamespace() )
- ) {
- $usernames[] = $titleObj->getText();
- }
}
- // Get gender information
- $genderCache = $services->getGenderCache();
- $genderCache->doQuery( $usernames, __METHOD__ );
return $linkBatch;
}
}
$this->setVar( '_Extensions', $status->value );
} elseif ( isset( $options['with-extensions'] ) ) {
- $this->setVar( '_Extensions', array_keys( $this->findExtensions() ) );
+ $status = $this->findExtensions();
+ if ( !$status->isOK() ) {
+ throw new InstallException( $status );
+ }
+ $this->setVar( '_Extensions', array_keys( $status->value ) );
}
// Set up the default skins
}
$skins = $status->value;
} else {
- $skins = array_keys( $this->findExtensions( 'skins' ) );
+ $status = $this->findExtensions( 'skins' );
+ if ( !$status->isOK() ) {
+ throw new InstallException( $status );
+ }
+ $skins = array_keys( $status->value );
}
$this->setVar( '_Skins', $skins );
*
* @param string $directory Directory to search in, relative to $IP, must be either "extensions"
* or "skins"
- * @return array[][] [ $extName => [ 'screenshots' => [ '...' ] ]
+ * @return Status An object containing an error list. If there were no errors, an associative
+ * array of information about the extension can be found in $status->value.
*/
public function findExtensions( $directory = 'extensions' ) {
switch ( $directory ) {
*
* @param string $type Either "extension" or "skin"
* @param string $directory Directory to search in, relative to $IP
- * @return array [ $extName => [ 'screenshots' => [ '...' ] ]
+ * @return Status An object containing an error list. If there were no errors, an associative
+ * array of information about the extension can be found in $status->value.
*/
protected function findExtensionsByType( $type = 'extension', $directory = 'extensions' ) {
if ( $this->getVar( 'IP' ) === null ) {
- return [];
+ return Status::newGood( [] );
}
$extDir = $this->getVar( 'IP' ) . '/' . $directory;
if ( !is_readable( $extDir ) || !is_dir( $extDir ) ) {
- return [];
+ return Status::newGood( [] );
}
$dh = opendir( $extDir );
$exts = [];
+ $status = new Status;
while ( ( $file = readdir( $dh ) ) !== false ) {
- if ( !is_dir( "$extDir/$file" ) ) {
+ // skip non-dirs and hidden directories
+ if ( !is_dir( "$extDir/$file" ) || $file[0] === '.' ) {
continue;
}
- $status = $this->getExtensionInfo( $type, $directory, $file );
- if ( $status->isOK() ) {
- $exts[$file] = $status->value;
+ $extStatus = $this->getExtensionInfo( $type, $directory, $file );
+ if ( $extStatus->isOK() ) {
+ $exts[$file] = $extStatus->value;
+ } elseif ( $extStatus->hasMessage( 'config-extension-not-found' ) ) {
+ // (T225512) The directory is not actually an extension. Downgrade to warning.
+ $status->warning( 'config-extension-not-found', $file );
+ } else {
+ $status->merge( $extStatus );
}
}
closedir( $dh );
uksort( $exts, 'strnatcasecmp' );
- return $exts;
+ $status->value = $exts;
+
+ return $status;
}
/**
} elseif ( $e->missingExtensions || $e->missingSkins ) {
// There's an extension missing in the dependency tree,
// so add those to the dependency list and try again
- return $this->readExtension(
+ $status = $this->readExtension(
$fullJsonFile,
array_merge( $extDeps, $e->missingExtensions ),
array_merge( $skinDeps, $e->missingSkins )
);
+ if ( !$status->isOK() && !$status->hasMessage( 'config-extension-dependency' ) ) {
+ $status = Status::newFatal( 'config-extension-dependency',
+ basename( dirname( $fullJsonFile ) ), $status->getMessage() );
+ }
+ return $status;
}
// Some other kind of dependency error?
return Status::newFatal( 'config-extension-dependency',
$this->getFieldsetEnd()
);
- $skins = $this->parent->findExtensions( 'skins' );
+ $skins = $this->parent->findExtensions( 'skins' )->value;
+ '@phan-var array[] $skins';
$skinHtml = $this->getFieldsetStart( 'config-skins' );
$skinNames = array_map( 'strtolower', array_keys( $skins ) );
$this->getFieldsetEnd();
$this->addHTML( $skinHtml );
- $extensions = $this->parent->findExtensions();
+ $extensions = $this->parent->findExtensions()->value;
+ '@phan-var array[] $extensions';
$dependencyMap = [];
if ( $extensions ) {
return null;
}
+ /**
+ * @param string $name
+ * @param array $screenshots
+ */
private function makeScreenshotsLink( $name, $screenshots ) {
global $wgLang;
if ( count( $screenshots ) > 1 ) {
$links = [];
$counter = 1;
+
foreach ( $screenshots as $shot ) {
$links[] = Html::element(
'a',
* @return bool
*/
public function submitSkins() {
- $skins = array_keys( $this->parent->findExtensions( 'skins' ) );
+ $skins = array_keys( $this->parent->findExtensions( 'skins' )->value );
$this->parent->setVar( '_Skins', $skins );
if ( $skins ) {
$this->setVar( 'wgRightsIcon', '' );
}
- $skinsAvailable = array_keys( $this->parent->findExtensions( 'skins' ) );
+ $skinsAvailable = array_keys( $this->parent->findExtensions( 'skins' )->value );
$skinsToInstall = [];
foreach ( $skinsAvailable as $skin ) {
$this->parent->setVarsFromRequest( [ "skin-$skin" ] );
$retVal = false;
}
- $extsAvailable = array_keys( $this->parent->findExtensions() );
+ $extsAvailable = array_keys( $this->parent->findExtensions()->value );
$extsToInstall = [];
foreach ( $extsAvailable as $ext ) {
$this->parent->setVarsFromRequest( [ "ext-$ext" ] );
*/
use Wikimedia\AtEase\AtEase;
+use Wikimedia\Timestamp\ConvertibleTimestamp;
/**
* Simulation of a backend storage in memory.
$this->files[$dst] = [
'data' => $params['content'],
- 'mtime' => wfTimestamp( TS_MW, time() )
+ 'mtime' => ConvertibleTimestamp::convert( TS_MW, time() )
];
return $status;
$this->files[$dst] = [
'data' => $data,
- 'mtime' => wfTimestamp( TS_MW, time() )
+ 'mtime' => ConvertibleTimestamp::convert( TS_MW, time() )
];
return $status;
$this->files[$dst] = [
'data' => $this->files[$src]['data'],
- 'mtime' => wfTimestamp( TS_MW, time() )
+ 'mtime' => ConvertibleTimestamp::convert( TS_MW, time() )
];
return $status;
*/
use Wikimedia\AtEase\AtEase;
+use Wikimedia\Timestamp\ConvertibleTimestamp;
/**
* Class representing a non-directory file on the file system
* @return int|bool
*/
public function getSize() {
- return filesize( $this->path );
+ AtEase::suppressWarnings();
+ $size = filesize( $this->path );
+ AtEase::restoreWarnings();
+
+ return $size;
}
/**
$timestamp = filemtime( $this->path );
AtEase::restoreWarnings();
if ( $timestamp !== false ) {
- $timestamp = wfTimestamp( TS_MW, $timestamp );
+ $timestamp = ConvertibleTimestamp::convert( TS_MW, $timestamp );
}
return $timestamp;
if ( !$this->mHideName ) {
// Reset for hook
$this->mHideName = false;
- Hooks::run( 'UserIsHidden', [ $this, &$this->mHideName ] );
+ Hooks::run( 'UserIsHidden', [ $this, &$this->mHideName ], '1.34' );
}
return (bool)$this->mHideName;
}
try {
$installer = InstallerOverrides::getCliInstaller( $siteName, $adminName, $this->mOptions );
} catch ( \MediaWiki\Installer\InstallException $e ) {
- $this->output( $e->getStatus()->getMessage()->parse() . "\n" );
+ $this->output( $e->getStatus()->getMessage()->text() . "\n" );
return false;
}
<?php
+use MediaWiki\MediaWikiServices;
+use Wikimedia\TestingAccessWrapper;
+
/**
* @group API
* @group medium
3 => [ "$userDbkey/subpage" => -3 ],
], $pageSet->getAllTitlesByNamespace() );
}
+
+ /**
+ * Test that ApiPageSet is calling GenderCache for provided user names to prefill the
+ * GenderCache and avoid a performance issue when loading each users' gender on it's own.
+ * The test is setting the "missLimit" to 0 on the GenderCache to trigger misses logic.
+ * When the "misses" property is no longer 0 at the end of the test,
+ * something was requested which is not part of the cache. Than the test is failing.
+ */
+ public function testGenderCaching() {
+ // Set up the user namespace to have gender aliases to trigger the gender cache
+ $this->setMwGlobals( [
+ 'wgExtraGenderNamespaces' => [ NS_USER => [ 'male' => 'Male', 'female' => 'Female' ] ]
+ ] );
+ $this->overrideMwServices();
+
+ // User names to test with - it is not needed that the user exists in the database
+ // to trigger gender cache
+ $userNames = [
+ 'Female',
+ 'Unknown',
+ 'Male',
+ ];
+
+ // Prepare the gender cache for testing - this is a fresh instance due to service override
+ $genderCache = TestingAccessWrapper::newFromObject(
+ MediaWikiServices::getInstance()->getGenderCache()
+ );
+ $genderCache->missLimit = 0;
+
+ // Do an api request to trigger ApiPageSet code
+ $this->doApiRequest( [
+ 'action' => 'query',
+ 'titles' => 'User:' . implode( '|User:', $userNames ),
+ ] );
+
+ $this->assertEquals( 0, $genderCache->misses,
+ 'ApiPageSet does not prefill the gender cache correctly' );
+ $this->assertEquals( $userNames, array_keys( $genderCache->cache ),
+ 'ApiPageSet does not prefill all users into the gender cache' );
+ }
}