For notes on 1.32.x and older releases, see HISTORY.
=== Configuration changes for system administrators in 1.33 ===
+
==== New configuration ====
* $wgEnablePartialBlocks – This enables the Partial Blocks feature, which gives
accounts with block permissions the ability to block users, IPs, and IP ranges
associated with this entry in the patrol log.
=== External library changes in 1.33 ===
+
==== New external libraries ====
* Added wikimedia/password-blacklist 0.1.4.
* Added guzzlehttp/guzzle 6.3.3.
* MessageBlobStore::getBlob(), deprecated in 1.27, has been removed.
Use ::getBlobs() instead.
* The .background-size() LESS mixin, deprecated in 1.27, has been removed.
+* MWNamespace::clearCaches() has been removed. So has the $rebuild parameter
+ to MWNamespace::getCanonicalNamespaces(), which was deprecated since 1.31.
+ Instead, create a new NamespaceInfo, such as by calling
+ resetServiceForTesting( 'NamespaceInfo' ) on a MediaWikiServices.
+ For classes that inherit from MediaWikiTestCase and used setMwGlobals() to
+ modify a variable that affects namespaces, caches will automatically be
+ reset and any calls to MWNamespace::clearCaches() can be removed entirely.
=== Deprecations in 1.33 ===
* The configuration option $wgUseESI has been deprecated, and is expected
* Block::isValid is deprecated, since it is no longer needed in core.
* Calling Maintenance::hasArg() as well as Maintenance::getArg() with no
parameter has been deprecated. Please pass the argument number 0.
+* The MWNamespace class is deprecated. Use MediaWikiServices::getNamespaceInfo.
=== Other changes in 1.33 ===
* (T201747) Html::openElement() warns if given an element name with a space
'NamespaceAwareForeignTitleFactory' => __DIR__ . '/includes/title/NamespaceAwareForeignTitleFactory.php',
'NamespaceDupes' => __DIR__ . '/maintenance/namespaceDupes.php',
'NamespaceImportTitleFactory' => __DIR__ . '/includes/title/NamespaceImportTitleFactory.php',
+ 'NamespaceInfo' => __DIR__ . '/includes/title/NamespaceInfo.php',
'NewFilesPager' => __DIR__ . '/includes/specials/pagers/NewFilesPager.php',
'NewPagesPager' => __DIR__ . '/includes/specials/pagers/NewPagesPager.php',
'NewUsersLogFormatter' => __DIR__ . '/includes/logging/NewUsersLogFormatter.php',
$index: Integer; the index of the namespace being checked.
&$result: Boolean; whether MediaWiki currently thinks that pages in this
namespace are movable. Hooks may change this value to override the return
- value of MWNamespace::isMovable().
+ value of NamespaceInfo::isMovable().
'NewDifferenceEngine': Called when a new DifferenceEngine object is made
$title: the diff page title (nullable)
$wgCapitalLinks = true;
/**
- * @since 1.16 - This can now be set per-namespace. Some special namespaces (such
- * as Special, see MWNamespace::$alwaysCapitalizedNamespaces for the full list) must be
- * true by default (and setting them has no effect), due to various things that
- * require them to be so. Also, since Talk namespaces need to directly mirror their
- * associated content namespaces, the values for those are ignored in favor of the
- * subject namespace's setting. Setting for NS_MEDIA is taken automatically from
- * NS_FILE.
+ * @since 1.16 - This can now be set per-namespace. Some special namespaces (such as Special, see
+ * NamespaceInfo::$alwaysCapitalizedNamespaces for the full list) must be true by default (and
+ * setting them has no effect), due to various things that require them to be so. Also, since Talk
+ * namespaces need to directly mirror their associated content namespaces, the values for those are
+ * ignored in favor of the subject namespace's setting. Setting for NS_MEDIA is taken automatically
+ * from NS_FILE.
*
* @par Example:
* @code
use MediaWiki\MediaWikiServices;
/**
- * This is a utility class with only static functions
- * for dealing with namespaces that encodes all the
- * "magic" behaviors of them based on index. The textual
- * names of the namespaces are handled by Language.php.
- *
- * These are synonyms for the names given in the language file
- * Users and translators should not change them
+ * @deprecated since 1.33, use NamespaceInfo instead
*/
class MWNamespace {
-
- /**
- * These namespaces should always be first-letter capitalized, now and
- * forevermore. Historically, they could've probably been lowercased too,
- * but some things are just too ingrained now. :)
- */
- private static $alwaysCapitalizedNamespaces = [ NS_SPECIAL, NS_USER, NS_MEDIAWIKI ];
-
- /** @var string[]|null Canonical namespaces cache */
- private static $canonicalNamespaces = null;
-
- /** @var array|false Canonical namespaces index cache */
- private static $namespaceIndexes = false;
-
- /** @var int[]|null Valid namespaces cache */
- private static $validNamespaces = null;
-
- /**
- * Throw an exception when trying to get the subject or talk page
- * for a given namespace where it does not make sense.
- * Special namespaces are defined in includes/Defines.php and have
- * a value below 0 (ex: NS_SPECIAL = -1 , NS_MEDIA = -2)
- *
- * @param int $index
- * @param string $method
- *
- * @throws MWException
- * @return bool
- */
- private static function isMethodValidFor( $index, $method ) {
- if ( $index < NS_MAIN ) {
- throw new MWException( "$method does not make any sense for given namespace $index" );
- }
- return true;
- }
-
- /**
- * Clear internal caches
- *
- * For use in unit testing when namespace configuration is changed.
- *
- * @since 1.31
- */
- public static function clearCaches() {
- self::$canonicalNamespaces = null;
- self::$namespaceIndexes = false;
- self::$validNamespaces = null;
- }
-
/**
* Can pages in the given namespace be moved?
*
* @return bool
*/
public static function isMovable( $index ) {
- global $wgAllowImageMoving;
-
- $result = !( $index < NS_MAIN || ( $index == NS_FILE && !$wgAllowImageMoving ) );
-
- /**
- * @since 1.20
- */
- Hooks::run( 'NamespaceIsMovable', [ $index, &$result ] );
-
- return $result;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->isMovable( $index );
}
/**
* @since 1.19
*/
public static function isSubject( $index ) {
- return !self::isTalk( $index );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->isSubject( $index );
}
/**
* @return bool
*/
public static function isTalk( $index ) {
- return $index > NS_MAIN
- && $index % 2;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->isTalk( $index );
}
/**
* @return int
*/
public static function getTalk( $index ) {
- self::isMethodValidFor( $index, __METHOD__ );
- return self::isTalk( $index )
- ? $index
- : $index + 1;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->getTalk( $index );
}
/**
* @return int
*/
public static function getSubject( $index ) {
- # Handle special namespaces
- if ( $index < NS_MAIN ) {
- return $index;
- }
-
- return self::isTalk( $index )
- ? $index - 1
- : $index;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->getSubject( $index );
}
/**
* @return int|null If no associated namespace could be found
*/
public static function getAssociated( $index ) {
- self::isMethodValidFor( $index, __METHOD__ );
-
- if ( self::isSubject( $index ) ) {
- return self::getTalk( $index );
- } elseif ( self::isTalk( $index ) ) {
- return self::getSubject( $index );
- } else {
- return null;
- }
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->getAssociated( $index );
}
/**
* @since 1.19
*/
public static function exists( $index ) {
- $nslist = self::getCanonicalNamespaces();
- return isset( $nslist[$index] );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->exists( $index );
}
/**
* @since 1.19
*/
public static function equals( $ns1, $ns2 ) {
- return $ns1 == $ns2;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->equals( $ns1, $ns2 );
}
/**
* @since 1.19
*/
public static function subjectEquals( $ns1, $ns2 ) {
- return self::getSubject( $ns1 ) == self::getSubject( $ns2 );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->
+ subjectEquals( $ns1, $ns2 );
}
/**
* Returns array of all defined namespaces with their canonical
* (English) names.
*
- * @param bool $rebuild Rebuild namespace list (default = false). Used for testing.
- * Deprecated since 1.31, use self::clearCaches() instead.
- *
* @return array
* @since 1.17
*/
- public static function getCanonicalNamespaces( $rebuild = false ) {
- if ( $rebuild ) {
- self::clearCaches();
- }
-
- if ( self::$canonicalNamespaces === null ) {
- global $wgExtraNamespaces, $wgCanonicalNamespaceNames;
- self::$canonicalNamespaces = [ NS_MAIN => '' ] + $wgCanonicalNamespaceNames;
- // Add extension namespaces
- self::$canonicalNamespaces +=
- ExtensionRegistry::getInstance()->getAttribute( 'ExtensionNamespaces' );
- if ( is_array( $wgExtraNamespaces ) ) {
- self::$canonicalNamespaces += $wgExtraNamespaces;
- }
- Hooks::run( 'CanonicalNamespaces', [ &self::$canonicalNamespaces ] );
- }
- return self::$canonicalNamespaces;
+ public static function getCanonicalNamespaces() {
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->getCanonicalNamespaces();
}
/**
* @return string|bool If no canonical definition.
*/
public static function getCanonicalName( $index ) {
- $nslist = self::getCanonicalNamespaces();
- return $nslist[$index] ?? false;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->getCanonicalName( $index );
}
/**
* @return int
*/
public static function getCanonicalIndex( $name ) {
- if ( self::$namespaceIndexes === false ) {
- self::$namespaceIndexes = [];
- foreach ( self::getCanonicalNamespaces() as $i => $text ) {
- self::$namespaceIndexes[strtolower( $text )] = $i;
- }
- }
- if ( array_key_exists( $name, self::$namespaceIndexes ) ) {
- return self::$namespaceIndexes[$name];
- } else {
- return null;
- }
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->getCanonicalIndex( $name );
}
/**
* @return array
*/
public static function getValidNamespaces() {
- if ( is_null( self::$validNamespaces ) ) {
- foreach ( array_keys( self::getCanonicalNamespaces() ) as $ns ) {
- if ( $ns >= 0 ) {
- self::$validNamespaces[] = $ns;
- }
- }
- // T109137: sort numerically
- sort( self::$validNamespaces, SORT_NUMERIC );
- }
-
- return self::$validNamespaces;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->getValidNamespaces();
}
/**
* @return bool True if this namespace either is or has a corresponding talk namespace.
*/
public static function hasTalkNamespace( $index ) {
- return $index >= NS_MAIN;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->hasTalkNamespace( $index );
}
/**
* @return bool
*/
public static function isContent( $index ) {
- global $wgContentNamespaces;
- return $index == NS_MAIN || in_array( $index, $wgContentNamespaces );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->isContent( $index );
}
/**
* @return bool
*/
public static function wantSignatures( $index ) {
- global $wgExtraSignatureNamespaces;
- return self::isTalk( $index ) || in_array( $index, $wgExtraSignatureNamespaces );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->wantSignatures( $index );
}
/**
* @return bool
*/
public static function isWatchable( $index ) {
- return $index >= NS_MAIN;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->isWatchable( $index );
}
/**
* @return bool
*/
public static function hasSubpages( $index ) {
- global $wgNamespacesWithSubpages;
- return !empty( $wgNamespacesWithSubpages[$index] );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->hasSubpages( $index );
}
/**
* @return array Array of namespace indices
*/
public static function getContentNamespaces() {
- global $wgContentNamespaces;
- if ( !is_array( $wgContentNamespaces ) || $wgContentNamespaces === [] ) {
- return [ NS_MAIN ];
- } elseif ( !in_array( NS_MAIN, $wgContentNamespaces ) ) {
- // always force NS_MAIN to be part of array (to match the algorithm used by isContent)
- return array_merge( [ NS_MAIN ], $wgContentNamespaces );
- } else {
- return $wgContentNamespaces;
- }
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->getContentNamespaces();
}
/**
* @return array Array of namespace indices
*/
public static function getSubjectNamespaces() {
- return array_filter(
- self::getValidNamespaces(),
- 'MWNamespace::isSubject'
- );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->getSubjectNamespaces();
}
/**
* @return array Array of namespace indices
*/
public static function getTalkNamespaces() {
- return array_filter(
- self::getValidNamespaces(),
- 'MWNamespace::isTalk'
- );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->getTalkNamespaces();
}
/**
* @return bool
*/
public static function isCapitalized( $index ) {
- global $wgCapitalLinks, $wgCapitalLinkOverrides;
- // Turn NS_MEDIA into NS_FILE
- $index = $index === NS_MEDIA ? NS_FILE : $index;
-
- // Make sure to get the subject of our namespace
- $index = self::getSubject( $index );
-
- // Some namespaces are special and should always be upper case
- if ( in_array( $index, self::$alwaysCapitalizedNamespaces ) ) {
- return true;
- }
- if ( isset( $wgCapitalLinkOverrides[$index] ) ) {
- // $wgCapitalLinkOverrides is explicitly set
- return $wgCapitalLinkOverrides[$index];
- }
- // Default to the global setting
- return $wgCapitalLinks;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->isCapitalized( $index );
}
/**
* @return bool
*/
public static function hasGenderDistinction( $index ) {
- return $index == NS_USER || $index == NS_USER_TALK;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->
+ hasGenderDistinction( $index );
}
/**
* @return bool
*/
public static function isNonincludable( $index ) {
- global $wgNonincludableNamespaces;
- return $wgNonincludableNamespaces && in_array( $index, $wgNonincludableNamespaces );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->isNonincludable( $index );
}
/**
* @return null|string Default model name for the given namespace, if set
*/
public static function getNamespaceContentModel( $index ) {
- $config = MediaWikiServices::getInstance()->getMainConfig();
- $models = $config->get( 'NamespaceContentModels' );
- return $models[$index] ?? null;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->
+ getNamespaceContentModel( $index );
}
/**
* @return array
*/
public static function getRestrictionLevels( $index, User $user = null ) {
- global $wgNamespaceProtection, $wgRestrictionLevels;
-
- if ( !isset( $wgNamespaceProtection[$index] ) ) {
- // All levels are valid if there's no namespace restriction.
- // But still filter by user, if necessary
- $levels = $wgRestrictionLevels;
- if ( $user ) {
- $levels = array_values( array_filter( $levels, function ( $level ) use ( $user ) {
- $right = $level;
- if ( $right == 'sysop' ) {
- $right = 'editprotected'; // BC
- }
- if ( $right == 'autoconfirmed' ) {
- $right = 'editsemiprotected'; // BC
- }
- return ( $right == '' || $user->isAllowed( $right ) );
- } ) );
- }
- return $levels;
- }
-
- // First, get the list of groups that can edit this namespace.
- $namespaceGroups = [];
- $combine = 'array_merge';
- foreach ( (array)$wgNamespaceProtection[$index] as $right ) {
- if ( $right == 'sysop' ) {
- $right = 'editprotected'; // BC
- }
- if ( $right == 'autoconfirmed' ) {
- $right = 'editsemiprotected'; // BC
- }
- if ( $right != '' ) {
- $namespaceGroups = call_user_func( $combine, $namespaceGroups,
- User::getGroupsWithPermission( $right ) );
- $combine = 'array_intersect';
- }
- }
-
- // Now, keep only those restriction levels where there is at least one
- // group that can edit the namespace but would be blocked by the
- // restriction.
- $usableLevels = [ '' ];
- foreach ( $wgRestrictionLevels as $level ) {
- $right = $level;
- if ( $right == 'sysop' ) {
- $right = 'editprotected'; // BC
- }
- if ( $right == 'autoconfirmed' ) {
- $right = 'editsemiprotected'; // BC
- }
- if ( $right != '' && ( !$user || $user->isAllowed( $right ) ) &&
- array_diff( $namespaceGroups, User::getGroupsWithPermission( $right ) )
- ) {
- $usableLevels[] = $level;
- }
- }
-
- return $usableLevels;
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->
+ getRestrictionLevels( $index, $user );
}
/**
* @return string One of 'subcat', 'file', 'page'
*/
public static function getCategoryLinkType( $index ) {
- self::isMethodValidFor( $index, __METHOD__ );
-
- if ( $index == NS_CATEGORY ) {
- return 'subcat';
- } elseif ( $index == NS_FILE ) {
- return 'file';
- } else {
- return 'page';
- }
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->
+ getCategoryLinkType( $index );
}
}
use MediaWiki\Linker\LinkRendererFactory;
use MWException;
use MimeAnalyzer;
+use NamespaceInfo;
use ObjectCache;
use Parser;
use ParserCache;
}
/**
+ * @since 1.33
+ * @return NamespaceInfo
+ */
+ public function getNamespaceInfo() : NamespaceInfo {
+ return $this->getService( 'NamespaceInfo' );
+ }
+
+ /**
+ * @since 1.32
* @return OldRevisionImporter
*/
public function getOldRevisionImporter() {
[ 'cl_from' => $pageid ],
__METHOD__
);
- $type = MWNamespace::getCategoryLinkType( $this->newTitle->getNamespace() );
+ $type = MediaWikiServices::getInstance()->getNamespaceInfo()->
+ getCategoryLinkType( $this->newTitle->getNamespace() );
foreach ( $prefixes as $prefixRow ) {
$prefix = $prefixRow->cl_sortkey_prefix;
$catTo = $prefixRow->cl_to;
);
},
+ 'NamespaceInfo' => function ( MediaWikiServices $services ) : NamespaceInfo {
+ return new NamespaceInfo( $services->getMainConfig() );
+ },
+
'OldRevisionImporter' => function ( MediaWikiServices $services ) : OldRevisionImporter {
return new ImportableOldRevisionImporter(
true,
$nt = Title::makeTitleSafe( NS_CATEGORY, $name );
$contLang->findVariantLink( $name, $nt, true );
- $type = MWNamespace::getCategoryLinkType( $this->mTitle->getNamespace() );
+ $type = MediaWikiServices::getInstance()->getNamespaceInfo()->
+ getCategoryLinkType( $this->mTitle->getNamespace() );
# Treat custom sortkeys as a prefix, so that if multiple
# things are forced to sort as '*' or something, they'll
*/
public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
$id = $id ?: $this->getId();
- $type = MWNamespace::getCategoryLinkType( $this->getTitle()->getNamespace() );
+ $type = MediaWikiServices::getInstance()->getNamespaceInfo()->
+ getCategoryLinkType( $this->getTitle()->getNamespace() );
$addFields = [ 'cat_pages = cat_pages + 1' ];
$removeFields = [ 'cat_pages = cat_pages - 1' ];
--- /dev/null
+<?php
+/**
+ * Provide things related to namespaces.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of
+ * them based on index. The textual names of the namespaces are handled by Language.php.
+ *
+ * @since 1.33
+ */
+class NamespaceInfo {
+
+ /**
+ * These namespaces should always be first-letter capitalized, now and
+ * forevermore. Historically, they could've probably been lowercased too,
+ * but some things are just too ingrained now. :)
+ */
+ private $alwaysCapitalizedNamespaces = [ NS_SPECIAL, NS_USER, NS_MEDIAWIKI ];
+
+ /** @var string[]|null Canonical namespaces cache */
+ private $canonicalNamespaces = null;
+
+ /** @var array|false Canonical namespaces index cache */
+ private $namespaceIndexes = false;
+
+ /** @var int[]|null Valid namespaces cache */
+ private $validNamespaces = null;
+
+ /** @var Config */
+ private $config;
+
+ /**
+ * @param Config $config
+ */
+ public function __construct( Config $config ) {
+ $this->config = $config;
+ }
+
+ /**
+ * Throw an exception when trying to get the subject or talk page
+ * for a given namespace where it does not make sense.
+ * Special namespaces are defined in includes/Defines.php and have
+ * a value below 0 (ex: NS_SPECIAL = -1 , NS_MEDIA = -2)
+ *
+ * @param int $index
+ * @param string $method
+ *
+ * @throws MWException
+ * @return bool
+ */
+ private function isMethodValidFor( $index, $method ) {
+ if ( $index < NS_MAIN ) {
+ throw new MWException( "$method does not make any sense for given namespace $index" );
+ }
+ return true;
+ }
+
+ /**
+ * Can pages in the given namespace be moved?
+ *
+ * @param int $index Namespace index
+ * @return bool
+ */
+ public function isMovable( $index ) {
+ $result = !( $index < NS_MAIN ||
+ ( $index == NS_FILE && !$this->config->get( 'AllowImageMoving' ) ) );
+
+ /**
+ * @since 1.20
+ */
+ Hooks::run( 'NamespaceIsMovable', [ $index, &$result ] );
+
+ return $result;
+ }
+
+ /**
+ * Is the given namespace is a subject (non-talk) namespace?
+ *
+ * @param int $index Namespace index
+ * @return bool
+ */
+ public function isSubject( $index ) {
+ return !$this->isTalk( $index );
+ }
+
+ /**
+ * Is the given namespace a talk namespace?
+ *
+ * @param int $index Namespace index
+ * @return bool
+ */
+ public function isTalk( $index ) {
+ return $index > NS_MAIN
+ && $index % 2;
+ }
+
+ /**
+ * Get the talk namespace index for a given namespace
+ *
+ * @param int $index Namespace index
+ * @return int
+ */
+ public function getTalk( $index ) {
+ $this->isMethodValidFor( $index, __METHOD__ );
+ return $this->isTalk( $index )
+ ? $index
+ : $index + 1;
+ }
+
+ /**
+ * Get the subject namespace index for a given namespace
+ * Special namespaces (NS_MEDIA, NS_SPECIAL) are always the subject.
+ *
+ * @param int $index Namespace index
+ * @return int
+ */
+ public function getSubject( $index ) {
+ # Handle special namespaces
+ if ( $index < NS_MAIN ) {
+ return $index;
+ }
+
+ return $this->isTalk( $index )
+ ? $index - 1
+ : $index;
+ }
+
+ /**
+ * Get the associated namespace.
+ * For talk namespaces, returns the subject (non-talk) namespace
+ * For subject (non-talk) namespaces, returns the talk namespace
+ *
+ * @param int $index Namespace index
+ * @return int|null If no associated namespace could be found
+ */
+ public function getAssociated( $index ) {
+ $this->isMethodValidFor( $index, __METHOD__ );
+
+ if ( $this->isSubject( $index ) ) {
+ return $this->getTalk( $index );
+ } elseif ( $this->isTalk( $index ) ) {
+ return $this->getSubject( $index );
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns whether the specified namespace exists
+ *
+ * @param int $index
+ *
+ * @return bool
+ */
+ public function exists( $index ) {
+ $nslist = $this->getCanonicalNamespaces();
+ return isset( $nslist[$index] );
+ }
+
+ /**
+ * Returns whether the specified namespaces are the same namespace
+ *
+ * @note It's possible that in the future we may start using something
+ * other than just namespace indexes. Under that circumstance making use
+ * of this function rather than directly doing comparison will make
+ * sure that code will not potentially break.
+ *
+ * @param int $ns1 The first namespace index
+ * @param int $ns2 The second namespace index
+ *
+ * @return bool
+ */
+ public function equals( $ns1, $ns2 ) {
+ return $ns1 == $ns2;
+ }
+
+ /**
+ * Returns whether the specified namespaces share the same subject.
+ * eg: NS_USER and NS_USER wil return true, as well
+ * NS_USER and NS_USER_TALK will return true.
+ *
+ * @param int $ns1 The first namespace index
+ * @param int $ns2 The second namespace index
+ *
+ * @return bool
+ */
+ public function subjectEquals( $ns1, $ns2 ) {
+ return $this->getSubject( $ns1 ) == $this->getSubject( $ns2 );
+ }
+
+ /**
+ * Returns array of all defined namespaces with their canonical
+ * (English) names.
+ *
+ * @return array
+ */
+ public function getCanonicalNamespaces() {
+ if ( $this->canonicalNamespaces === null ) {
+ $this->canonicalNamespaces =
+ [ NS_MAIN => '' ] + $this->config->get( 'CanonicalNamespaceNames' );
+ $this->canonicalNamespaces +=
+ ExtensionRegistry::getInstance()->getAttribute( 'ExtensionNamespaces' );
+ if ( is_array( $this->config->get( 'ExtraNamespaces' ) ) ) {
+ $this->canonicalNamespaces += $this->config->get( 'ExtraNamespaces' );
+ }
+ Hooks::run( 'CanonicalNamespaces', [ &$this->canonicalNamespaces ] );
+ }
+ return $this->canonicalNamespaces;
+ }
+
+ /**
+ * Returns the canonical (English) name for a given index
+ *
+ * @param int $index Namespace index
+ * @return string|bool If no canonical definition.
+ */
+ public function getCanonicalName( $index ) {
+ $nslist = $this->getCanonicalNamespaces();
+ return $nslist[$index] ?? false;
+ }
+
+ /**
+ * Returns the index for a given canonical name, or NULL
+ * The input *must* be converted to lower case first
+ *
+ * @param string $name Namespace name
+ * @return int
+ */
+ public function getCanonicalIndex( $name ) {
+ if ( $this->namespaceIndexes === false ) {
+ $this->namespaceIndexes = [];
+ foreach ( $this->getCanonicalNamespaces() as $i => $text ) {
+ $this->namespaceIndexes[strtolower( $text )] = $i;
+ }
+ }
+ if ( array_key_exists( $name, $this->namespaceIndexes ) ) {
+ return $this->namespaceIndexes[$name];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns an array of the namespaces (by integer id) that exist on the
+ * wiki. Used primarily by the api in help documentation.
+ * @return array
+ */
+ public function getValidNamespaces() {
+ if ( is_null( $this->validNamespaces ) ) {
+ foreach ( array_keys( $this->getCanonicalNamespaces() ) as $ns ) {
+ if ( $ns >= 0 ) {
+ $this->validNamespaces[] = $ns;
+ }
+ }
+ // T109137: sort numerically
+ sort( $this->validNamespaces, SORT_NUMERIC );
+ }
+
+ return $this->validNamespaces;
+ }
+
+ /*
+
+ /**
+ * Does this namespace ever have a talk namespace?
+ *
+ * @param int $index Namespace ID
+ * @return bool True if this namespace either is or has a corresponding talk namespace.
+ */
+ public function hasTalkNamespace( $index ) {
+ return $index >= NS_MAIN;
+ }
+
+ /**
+ * Does this namespace contain content, for the purposes of calculating
+ * statistics, etc?
+ *
+ * @param int $index Index to check
+ * @return bool
+ */
+ public function isContent( $index ) {
+ return $index == NS_MAIN || in_array( $index, $this->config->get( 'ContentNamespaces' ) );
+ }
+
+ /**
+ * Might pages in this namespace require the use of the Signature button on
+ * the edit toolbar?
+ *
+ * @param int $index Index to check
+ * @return bool
+ */
+ public function wantSignatures( $index ) {
+ return $this->isTalk( $index ) ||
+ in_array( $index, $this->config->get( 'ExtraSignatureNamespaces' ) );
+ }
+
+ /**
+ * Can pages in a namespace be watched?
+ *
+ * @param int $index
+ * @return bool
+ */
+ public function isWatchable( $index ) {
+ return $index >= NS_MAIN;
+ }
+
+ /**
+ * Does the namespace allow subpages?
+ *
+ * @param int $index Index to check
+ * @return bool
+ */
+ public function hasSubpages( $index ) {
+ return !empty( $this->config->get( 'NamespacesWithSubpages' )[$index] );
+ }
+
+ /**
+ * Get a list of all namespace indices which are considered to contain content
+ * @return array Array of namespace indices
+ */
+ public function getContentNamespaces() {
+ $contentNamespaces = $this->config->get( 'ContentNamespaces' );
+ if ( !is_array( $contentNamespaces ) || $contentNamespaces === [] ) {
+ return [ NS_MAIN ];
+ } elseif ( !in_array( NS_MAIN, $contentNamespaces ) ) {
+ // always force NS_MAIN to be part of array (to match the algorithm used by isContent)
+ return array_merge( [ NS_MAIN ], $contentNamespaces );
+ } else {
+ return $contentNamespaces;
+ }
+ }
+
+ /**
+ * List all namespace indices which are considered subject, aka not a talk
+ * or special namespace. See also NamespaceInfo::isSubject
+ *
+ * @return array Array of namespace indices
+ */
+ public function getSubjectNamespaces() {
+ return array_filter(
+ $this->getValidNamespaces(),
+ [ $this, 'isSubject' ]
+ );
+ }
+
+ /**
+ * List all namespace indices which are considered talks, aka not a subject
+ * or special namespace. See also NamespaceInfo::isTalk
+ *
+ * @return array Array of namespace indices
+ */
+ public function getTalkNamespaces() {
+ return array_filter(
+ $this->getValidNamespaces(),
+ [ $this, 'isTalk' ]
+ );
+ }
+
+ /**
+ * Is the namespace first-letter capitalized?
+ *
+ * @param int $index Index to check
+ * @return bool
+ */
+ public function isCapitalized( $index ) {
+ // Turn NS_MEDIA into NS_FILE
+ $index = $index === NS_MEDIA ? NS_FILE : $index;
+
+ // Make sure to get the subject of our namespace
+ $index = $this->getSubject( $index );
+
+ // Some namespaces are special and should always be upper case
+ if ( in_array( $index, $this->alwaysCapitalizedNamespaces ) ) {
+ return true;
+ }
+ $overrides = $this->config->get( 'CapitalLinkOverrides' );
+ if ( isset( $overrides[$index] ) ) {
+ // CapitalLinkOverrides is explicitly set
+ return $overrides[$index];
+ }
+ // Default to the global setting
+ return $this->config->get( 'CapitalLinks' );
+ }
+
+ /**
+ * Does the namespace (potentially) have different aliases for different
+ * genders. Not all languages make a distinction here.
+ *
+ * @param int $index Index to check
+ * @return bool
+ */
+ public function hasGenderDistinction( $index ) {
+ return $index == NS_USER || $index == NS_USER_TALK;
+ }
+
+ /**
+ * It is not possible to use pages from this namespace as template?
+ *
+ * @param int $index Index to check
+ * @return bool
+ */
+ public function isNonincludable( $index ) {
+ $namespaces = $this->config->get( 'NonincludableNamespaces' );
+ return $namespaces && in_array( $index, $namespaces );
+ }
+
+ /**
+ * Get the default content model for a namespace
+ * This does not mean that all pages in that namespace have the model
+ *
+ * @note To determine the default model for a new page's main slot, or any slot in general,
+ * use SlotRoleHandler::getDefaultModel() together with SlotRoleRegistry::getRoleHandler().
+ *
+ * @param int $index Index to check
+ * @return null|string Default model name for the given namespace, if set
+ */
+ public function getNamespaceContentModel( $index ) {
+ return $this->config->get( 'NamespaceContentModels' )[$index] ?? null;
+ }
+
+ /**
+ * Determine which restriction levels it makes sense to use in a namespace,
+ * optionally filtered by a user's rights.
+ *
+ * @param int $index Index to check
+ * @param User|null $user User to check
+ * @return array
+ */
+ public function getRestrictionLevels( $index, User $user = null ) {
+ if ( !isset( $this->config->get( 'NamespaceProtection' )[$index] ) ) {
+ // All levels are valid if there's no namespace restriction.
+ // But still filter by user, if necessary
+ $levels = $this->config->get( 'RestrictionLevels' );
+ if ( $user ) {
+ $levels = array_values( array_filter( $levels, function ( $level ) use ( $user ) {
+ $right = $level;
+ if ( $right == 'sysop' ) {
+ $right = 'editprotected'; // BC
+ }
+ if ( $right == 'autoconfirmed' ) {
+ $right = 'editsemiprotected'; // BC
+ }
+ return ( $right == '' || $user->isAllowed( $right ) );
+ } ) );
+ }
+ return $levels;
+ }
+
+ // First, get the list of groups that can edit this namespace.
+ $namespaceGroups = [];
+ $combine = 'array_merge';
+ foreach ( (array)$this->config->get( 'NamespaceProtection' )[$index] as $right ) {
+ if ( $right == 'sysop' ) {
+ $right = 'editprotected'; // BC
+ }
+ if ( $right == 'autoconfirmed' ) {
+ $right = 'editsemiprotected'; // BC
+ }
+ if ( $right != '' ) {
+ $namespaceGroups = call_user_func( $combine, $namespaceGroups,
+ User::getGroupsWithPermission( $right ) );
+ $combine = 'array_intersect';
+ }
+ }
+
+ // Now, keep only those restriction levels where there is at least one
+ // group that can edit the namespace but would be blocked by the
+ // restriction.
+ $usableLevels = [ '' ];
+ foreach ( $this->config->get( 'RestrictionLevels' ) as $level ) {
+ $right = $level;
+ if ( $right == 'sysop' ) {
+ $right = 'editprotected'; // BC
+ }
+ if ( $right == 'autoconfirmed' ) {
+ $right = 'editsemiprotected'; // BC
+ }
+ if ( $right != '' && ( !$user || $user->isAllowed( $right ) ) &&
+ array_diff( $namespaceGroups, User::getGroupsWithPermission( $right ) )
+ ) {
+ $usableLevels[] = $level;
+ }
+ }
+
+ return $usableLevels;
+ }
+
+ /**
+ * Returns the link type to be used for categories.
+ *
+ * This determines which section of a category page titles
+ * in the namespace will appear within.
+ *
+ * @param int $index Namespace index
+ * @return string One of 'subcat', 'file', 'page'
+ */
+ public function getCategoryLinkType( $index ) {
+ $this->isMethodValidFor( $index, __METHOD__ );
+
+ if ( $index == NS_CATEGORY ) {
+ return 'subcat';
+ } elseif ( $index == NS_FILE ) {
+ return 'file';
+ } else {
+ return 'page';
+ }
+ }
+}
require_once __DIR__ . '/Maintenance.php';
+use MediaWiki\MediaWikiServices;
use Wikimedia\Rdbms\IDatabase;
/**
}
# cl_type will be wrong for lots of pages if cl_collation is 0,
# so let's update it while we're here.
- $type = MWNamespace::getCategoryLinkType( $title->getNamespace() );
+ $type = MediaWikiServices::getInstance()->getNamespaceInfo()->
+ getCategoryLinkType( $title->getNamespace() );
$newSortKey = $collation->getSortKey(
$title->getCategorySortkey( $prefix ) );
if ( $verboseStats ) {
100 => 'MemoryAlpha',
101 => 'MemoryAlpha_talk'
];
- // Changing wgExtraNamespaces invalidates caches in MWNamespace and
- // any live Language object, both on setup and teardown
+ // Changing wgExtraNamespaces invalidates caches in NamespaceInfo and any live Language
+ // object, both on setup and teardown
$reset = function () {
- MWNamespace::clearCaches();
+ MediaWikiServices::getInstance()->resetServiceForTesting( 'NamespaceInfo' );
MediaWikiServices::getInstance()->getContentLanguage()->resetNamespaces();
};
$setup[] = $reset;
$this->tmpFiles = array_merge( $this->tmpFiles, (array)$files );
}
+ // @todo Make const when we no longer support HHVM (T192166)
+ private static $namespaceAffectingSettings = [
+ 'wgAllowImageMoving',
+ 'wgCanonicalNamespaceNames',
+ 'wgCapitalLinkOverrides',
+ 'wgCapitalLinks',
+ 'wgContentNamespaces',
+ 'wgExtensionMessagesFiles',
+ 'wgExtensionNamespaces',
+ 'wgExtraNamespaces',
+ 'wgExtraSignatureNamespaces',
+ 'wgNamespaceContentModels',
+ 'wgNamespaceProtection',
+ 'wgNamespacesWithSubpages',
+ 'wgNonincludableNamespaces',
+ 'wgRestrictionLevels',
+ ];
+
protected function tearDown() {
global $wgRequest, $wgSQLMode;
ini_set( $name, $value );
}
if (
- array_key_exists( 'wgExtraNamespaces', $this->mwGlobals ) ||
- in_array( 'wgExtraNamespaces', $this->mwGlobalsToUnset )
+ array_intersect( self::$namespaceAffectingSettings, array_keys( $this->mwGlobals ) ) ||
+ array_intersect( self::$namespaceAffectingSettings, $this->mwGlobalsToUnset )
) {
$this->resetNamespaces();
}
$GLOBALS[$key] = $value;
}
- if ( array_key_exists( 'wgExtraNamespaces', $pairs ) ) {
+ if ( array_intersect( self::$namespaceAffectingSettings, array_keys( $pairs ) ) ) {
$this->resetNamespaces();
}
}
. 'instance has been replaced by test code.' );
}
- MWNamespace::clearCaches();
Language::clearCaches();
-
- // We can't have the TitleFormatter holding on to an old Language object either
- // @todo We shouldn't need to reset all the aliases here.
- $this->localServices->resetServiceForTesting( 'TitleFormatter' );
- $this->localServices->resetServiceForTesting( 'TitleParser' );
- $this->localServices->resetServiceForTesting( '_MediaWikiTitleCodec' );
}
/**
+++ /dev/null
-<?php
-/**
- * @author Antoine Musso
- * @copyright Copyright © 2011, Antoine Musso
- * @file
- */
-
-class MWNamespaceTest extends MediaWikiTestCase {
-
- protected function setUp() {
- parent::setUp();
-
- $this->setMwGlobals( [
- 'wgContentNamespaces' => [ NS_MAIN ],
- 'wgNamespacesWithSubpages' => [
- NS_TALK => true,
- NS_USER => true,
- NS_USER_TALK => true,
- ],
- 'wgCapitalLinks' => true,
- 'wgCapitalLinkOverrides' => [],
- 'wgNonincludableNamespaces' => [],
- ] );
- }
-
- /**
- * @todo Write more texts, handle $wgAllowImageMoving setting
- * @covers MWNamespace::isMovable
- */
- public function testIsMovable() {
- $this->assertFalse( MWNamespace::isMovable( NS_SPECIAL ) );
- }
-
- private function assertIsSubject( $ns ) {
- $this->assertTrue( MWNamespace::isSubject( $ns ) );
- }
-
- private function assertIsNotSubject( $ns ) {
- $this->assertFalse( MWNamespace::isSubject( $ns ) );
- }
-
- /**
- * Please make sure to change testIsTalk() if you change the assertions below
- * @covers MWNamespace::isSubject
- */
- public function testIsSubject() {
- // Special namespaces
- $this->assertIsSubject( NS_MEDIA );
- $this->assertIsSubject( NS_SPECIAL );
-
- // Subject pages
- $this->assertIsSubject( NS_MAIN );
- $this->assertIsSubject( NS_USER );
- $this->assertIsSubject( 100 ); # user defined
-
- // Talk pages
- $this->assertIsNotSubject( NS_TALK );
- $this->assertIsNotSubject( NS_USER_TALK );
- $this->assertIsNotSubject( 101 ); # user defined
- }
-
- private function assertIsTalk( $ns ) {
- $this->assertTrue( MWNamespace::isTalk( $ns ) );
- }
-
- private function assertIsNotTalk( $ns ) {
- $this->assertFalse( MWNamespace::isTalk( $ns ) );
- }
-
- /**
- * Reverse of testIsSubject().
- * Please update testIsSubject() if you change assertions below
- * @covers MWNamespace::isTalk
- */
- public function testIsTalk() {
- // Special namespaces
- $this->assertIsNotTalk( NS_MEDIA );
- $this->assertIsNotTalk( NS_SPECIAL );
-
- // Subject pages
- $this->assertIsNotTalk( NS_MAIN );
- $this->assertIsNotTalk( NS_USER );
- $this->assertIsNotTalk( 100 ); # user defined
-
- // Talk pages
- $this->assertIsTalk( NS_TALK );
- $this->assertIsTalk( NS_USER_TALK );
- $this->assertIsTalk( 101 ); # user defined
- }
-
- /**
- * @covers MWNamespace::getSubject
- */
- public function testGetSubject() {
- // Special namespaces are their own subjects
- $this->assertEquals( NS_MEDIA, MWNamespace::getSubject( NS_MEDIA ) );
- $this->assertEquals( NS_SPECIAL, MWNamespace::getSubject( NS_SPECIAL ) );
-
- $this->assertEquals( NS_MAIN, MWNamespace::getSubject( NS_TALK ) );
- $this->assertEquals( NS_USER, MWNamespace::getSubject( NS_USER_TALK ) );
- }
-
- /**
- * Regular getTalk() calls
- * Namespaces without a talk page (NS_MEDIA, NS_SPECIAL) are tested in
- * the function testGetTalkExceptions()
- * @covers MWNamespace::getTalk
- */
- public function testGetTalk() {
- $this->assertEquals( NS_TALK, MWNamespace::getTalk( NS_MAIN ) );
- $this->assertEquals( NS_TALK, MWNamespace::getTalk( NS_TALK ) );
- $this->assertEquals( NS_USER_TALK, MWNamespace::getTalk( NS_USER ) );
- $this->assertEquals( NS_USER_TALK, MWNamespace::getTalk( NS_USER_TALK ) );
- }
-
- /**
- * Exceptions with getTalk()
- * NS_MEDIA does not have talk pages. MediaWiki raise an exception for them.
- * @expectedException MWException
- * @covers MWNamespace::getTalk
- */
- public function testGetTalkExceptionsForNsMedia() {
- $this->assertNull( MWNamespace::getTalk( NS_MEDIA ) );
- }
-
- /**
- * Exceptions with getTalk()
- * NS_SPECIAL does not have talk pages. MediaWiki raise an exception for them.
- * @expectedException MWException
- * @covers MWNamespace::getTalk
- */
- public function testGetTalkExceptionsForNsSpecial() {
- $this->assertNull( MWNamespace::getTalk( NS_SPECIAL ) );
- }
-
- /**
- * Regular getAssociated() calls
- * Namespaces without an associated page (NS_MEDIA, NS_SPECIAL) are tested in
- * the function testGetAssociatedExceptions()
- * @covers MWNamespace::getAssociated
- */
- public function testGetAssociated() {
- $this->assertEquals( NS_TALK, MWNamespace::getAssociated( NS_MAIN ) );
- $this->assertEquals( NS_MAIN, MWNamespace::getAssociated( NS_TALK ) );
- }
-
- # ## Exceptions with getAssociated()
- # ## NS_MEDIA and NS_SPECIAL do not have talk pages. MediaWiki raises
- # ## an exception for them.
- /**
- * @expectedException MWException
- * @covers MWNamespace::getAssociated
- */
- public function testGetAssociatedExceptionsForNsMedia() {
- $this->assertNull( MWNamespace::getAssociated( NS_MEDIA ) );
- }
-
- /**
- * @expectedException MWException
- * @covers MWNamespace::getAssociated
- */
- public function testGetAssociatedExceptionsForNsSpecial() {
- $this->assertNull( MWNamespace::getAssociated( NS_SPECIAL ) );
- }
-
- /**
- * Test MWNamespace::equals
- * Note if we add a namespace registration system with keys like 'MAIN'
- * we should add tests here for equivilance on things like 'MAIN' == 0
- * and 'MAIN' == NS_MAIN.
- * @covers MWNamespace::equals
- */
- public function testEquals() {
- $this->assertTrue( MWNamespace::equals( NS_MAIN, NS_MAIN ) );
- $this->assertTrue( MWNamespace::equals( NS_MAIN, 0 ) ); // In case we make NS_MAIN 'MAIN'
- $this->assertTrue( MWNamespace::equals( NS_USER, NS_USER ) );
- $this->assertTrue( MWNamespace::equals( NS_USER, 2 ) );
- $this->assertTrue( MWNamespace::equals( NS_USER_TALK, NS_USER_TALK ) );
- $this->assertTrue( MWNamespace::equals( NS_SPECIAL, NS_SPECIAL ) );
- $this->assertFalse( MWNamespace::equals( NS_MAIN, NS_TALK ) );
- $this->assertFalse( MWNamespace::equals( NS_USER, NS_USER_TALK ) );
- $this->assertFalse( MWNamespace::equals( NS_PROJECT, NS_TEMPLATE ) );
- }
-
- /**
- * @covers MWNamespace::subjectEquals
- */
- public function testSubjectEquals() {
- $this->assertSameSubject( NS_MAIN, NS_MAIN );
- $this->assertSameSubject( NS_MAIN, 0 ); // In case we make NS_MAIN 'MAIN'
- $this->assertSameSubject( NS_USER, NS_USER );
- $this->assertSameSubject( NS_USER, 2 );
- $this->assertSameSubject( NS_USER_TALK, NS_USER_TALK );
- $this->assertSameSubject( NS_SPECIAL, NS_SPECIAL );
- $this->assertSameSubject( NS_MAIN, NS_TALK );
- $this->assertSameSubject( NS_USER, NS_USER_TALK );
-
- $this->assertDifferentSubject( NS_PROJECT, NS_TEMPLATE );
- $this->assertDifferentSubject( NS_SPECIAL, NS_MAIN );
- }
-
- /**
- * @covers MWNamespace::subjectEquals
- */
- public function testSpecialAndMediaAreDifferentSubjects() {
- $this->assertDifferentSubject(
- NS_MEDIA, NS_SPECIAL,
- "NS_MEDIA and NS_SPECIAL are different subject namespaces"
- );
- $this->assertDifferentSubject(
- NS_SPECIAL, NS_MEDIA,
- "NS_SPECIAL and NS_MEDIA are different subject namespaces"
- );
- }
-
- public function provideHasTalkNamespace() {
- return [
- [ NS_MEDIA, false ],
- [ NS_SPECIAL, false ],
-
- [ NS_MAIN, true ],
- [ NS_TALK, true ],
- [ NS_USER, true ],
- [ NS_USER_TALK, true ],
-
- [ 100, true ],
- [ 101, true ],
- ];
- }
-
- /**
- * @dataProvider provideHasTalkNamespace
- * @covers MWNamespace::hasTalkNamespace
- *
- * @param int $index
- * @param bool $expected
- */
- public function testHasTalkNamespace( $index, $expected ) {
- $actual = MWNamespace::hasTalkNamespace( $index );
- $this->assertSame( $actual, $expected, "NS $index" );
- }
-
- /**
- * @dataProvider provideHasTalkNamespace
- * @covers MWNamespace::canTalk
- *
- * @param int $index
- * @param bool $expected
- */
- public function testCanTalk( $index, $expected ) {
- $this->hideDeprecated( 'MWNamespace::canTalk' );
- $actual = MWNamespace::canTalk( $index );
- $this->assertSame( $actual, $expected, "NS $index" );
- }
-
- private function assertIsContent( $ns ) {
- $this->assertTrue( MWNamespace::isContent( $ns ) );
- }
-
- private function assertIsNotContent( $ns ) {
- $this->assertFalse( MWNamespace::isContent( $ns ) );
- }
-
- /**
- * @covers MWNamespace::isContent
- */
- public function testIsContent() {
- // NS_MAIN is a content namespace per DefaultSettings.php
- // and per function definition.
-
- $this->assertIsContent( NS_MAIN );
-
- // Other namespaces which are not expected to be content
-
- $this->assertIsNotContent( NS_MEDIA );
- $this->assertIsNotContent( NS_SPECIAL );
- $this->assertIsNotContent( NS_TALK );
- $this->assertIsNotContent( NS_USER );
- $this->assertIsNotContent( NS_CATEGORY );
- $this->assertIsNotContent( 100 );
- }
-
- /**
- * Similar to testIsContent() but alters the $wgContentNamespaces
- * global variable.
- * @covers MWNamespace::isContent
- */
- public function testIsContentAdvanced() {
- global $wgContentNamespaces;
-
- // Test that user defined namespace #252 is not content
- $this->assertIsNotContent( 252 );
-
- // Bless namespace # 252 as a content namespace
- $wgContentNamespaces[] = 252;
-
- $this->assertIsContent( 252 );
-
- // Makes sure NS_MAIN was not impacted
- $this->assertIsContent( NS_MAIN );
- }
-
- private function assertIsWatchable( $ns ) {
- $this->assertTrue( MWNamespace::isWatchable( $ns ) );
- }
-
- private function assertIsNotWatchable( $ns ) {
- $this->assertFalse( MWNamespace::isWatchable( $ns ) );
- }
-
- /**
- * @covers MWNamespace::isWatchable
- */
- public function testIsWatchable() {
- // Specials namespaces are not watchable
- $this->assertIsNotWatchable( NS_MEDIA );
- $this->assertIsNotWatchable( NS_SPECIAL );
-
- // Core defined namespaces are watchables
- $this->assertIsWatchable( NS_MAIN );
- $this->assertIsWatchable( NS_TALK );
-
- // Additional, user defined namespaces are watchables
- $this->assertIsWatchable( 100 );
- $this->assertIsWatchable( 101 );
- }
-
- private function assertHasSubpages( $ns ) {
- $this->assertTrue( MWNamespace::hasSubpages( $ns ) );
- }
-
- private function assertHasNotSubpages( $ns ) {
- $this->assertFalse( MWNamespace::hasSubpages( $ns ) );
- }
-
- /**
- * @covers MWNamespace::hasSubpages
- */
- public function testHasSubpages() {
- global $wgNamespacesWithSubpages;
-
- // Special namespaces:
- $this->assertHasNotSubpages( NS_MEDIA );
- $this->assertHasNotSubpages( NS_SPECIAL );
-
- // Namespaces without subpages
- $this->assertHasNotSubpages( NS_MAIN );
-
- $wgNamespacesWithSubpages[NS_MAIN] = true;
- $this->assertHasSubpages( NS_MAIN );
-
- $wgNamespacesWithSubpages[NS_MAIN] = false;
- $this->assertHasNotSubpages( NS_MAIN );
-
- // Some namespaces with subpages
- $this->assertHasSubpages( NS_TALK );
- $this->assertHasSubpages( NS_USER );
- $this->assertHasSubpages( NS_USER_TALK );
- }
-
- /**
- * @covers MWNamespace::getContentNamespaces
- */
- public function testGetContentNamespaces() {
- global $wgContentNamespaces;
-
- $this->assertEquals(
- [ NS_MAIN ],
- MWNamespace::getContentNamespaces(),
- '$wgContentNamespaces is an array with only NS_MAIN by default'
- );
-
- # test !is_array( $wgcontentNamespaces )
- $wgContentNamespaces = '';
- $this->assertEquals( [ NS_MAIN ], MWNamespace::getContentNamespaces() );
-
- $wgContentNamespaces = false;
- $this->assertEquals( [ NS_MAIN ], MWNamespace::getContentNamespaces() );
-
- $wgContentNamespaces = null;
- $this->assertEquals( [ NS_MAIN ], MWNamespace::getContentNamespaces() );
-
- $wgContentNamespaces = 5;
- $this->assertEquals( [ NS_MAIN ], MWNamespace::getContentNamespaces() );
-
- # test $wgContentNamespaces === []
- $wgContentNamespaces = [];
- $this->assertEquals( [ NS_MAIN ], MWNamespace::getContentNamespaces() );
-
- # test !in_array( NS_MAIN, $wgContentNamespaces )
- $wgContentNamespaces = [ NS_USER, NS_CATEGORY ];
- $this->assertEquals(
- [ NS_MAIN, NS_USER, NS_CATEGORY ],
- MWNamespace::getContentNamespaces(),
- 'NS_MAIN is forced in $wgContentNamespaces even if unwanted'
- );
-
- # test other cases, return $wgcontentNamespaces as is
- $wgContentNamespaces = [ NS_MAIN ];
- $this->assertEquals(
- [ NS_MAIN ],
- MWNamespace::getContentNamespaces()
- );
-
- $wgContentNamespaces = [ NS_MAIN, NS_USER, NS_CATEGORY ];
- $this->assertEquals(
- [ NS_MAIN, NS_USER, NS_CATEGORY ],
- MWNamespace::getContentNamespaces()
- );
- }
-
- /**
- * @covers MWNamespace::getSubjectNamespaces
- */
- public function testGetSubjectNamespaces() {
- $subjectsNS = MWNamespace::getSubjectNamespaces();
- $this->assertContains( NS_MAIN, $subjectsNS,
- "Talk namespaces should have NS_MAIN" );
- $this->assertNotContains( NS_TALK, $subjectsNS,
- "Talk namespaces should have NS_TALK" );
-
- $this->assertNotContains( NS_MEDIA, $subjectsNS,
- "Talk namespaces should not have NS_MEDIA" );
- $this->assertNotContains( NS_SPECIAL, $subjectsNS,
- "Talk namespaces should not have NS_SPECIAL" );
- }
-
- /**
- * @covers MWNamespace::getTalkNamespaces
- */
- public function testGetTalkNamespaces() {
- $talkNS = MWNamespace::getTalkNamespaces();
- $this->assertContains( NS_TALK, $talkNS,
- "Subject namespaces should have NS_TALK" );
- $this->assertNotContains( NS_MAIN, $talkNS,
- "Subject namespaces should not have NS_MAIN" );
-
- $this->assertNotContains( NS_MEDIA, $talkNS,
- "Subject namespaces should not have NS_MEDIA" );
- $this->assertNotContains( NS_SPECIAL, $talkNS,
- "Subject namespaces should not have NS_SPECIAL" );
- }
-
- private function assertIsCapitalized( $ns ) {
- $this->assertTrue( MWNamespace::isCapitalized( $ns ) );
- }
-
- private function assertIsNotCapitalized( $ns ) {
- $this->assertFalse( MWNamespace::isCapitalized( $ns ) );
- }
-
- /**
- * Some namespaces are always capitalized per code definition
- * in MWNamespace::$alwaysCapitalizedNamespaces
- * @covers MWNamespace::isCapitalized
- */
- public function testIsCapitalizedHardcodedAssertions() {
- // NS_MEDIA and NS_FILE are treated the same
- $this->assertEquals(
- MWNamespace::isCapitalized( NS_MEDIA ),
- MWNamespace::isCapitalized( NS_FILE ),
- 'NS_MEDIA and NS_FILE have same capitalization rendering'
- );
-
- // Boths are capitalized by default
- $this->assertIsCapitalized( NS_MEDIA );
- $this->assertIsCapitalized( NS_FILE );
-
- // Always capitalized namespaces
- // @see MWNamespace::$alwaysCapitalizedNamespaces
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
- }
-
- /**
- * Follows up for testIsCapitalizedHardcodedAssertions() but alter the
- * global $wgCapitalLink setting to have extended coverage.
- *
- * MWNamespace::isCapitalized() rely on two global settings:
- * $wgCapitalLinkOverrides = []; by default
- * $wgCapitalLinks = true; by default
- * This function test $wgCapitalLinks
- *
- * Global setting correctness is tested against the NS_PROJECT and
- * NS_PROJECT_TALK namespaces since they are not hardcoded nor specials
- * @covers MWNamespace::isCapitalized
- */
- public function testIsCapitalizedWithWgCapitalLinks() {
- global $wgCapitalLinks;
-
- $this->assertIsCapitalized( NS_PROJECT );
- $this->assertIsCapitalized( NS_PROJECT_TALK );
-
- $wgCapitalLinks = false;
-
- // hardcoded namespaces (see above function) are still capitalized:
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
-
- // setting is correctly applied
- $this->assertIsNotCapitalized( NS_PROJECT );
- $this->assertIsNotCapitalized( NS_PROJECT_TALK );
- }
-
- /**
- * Counter part for MWNamespace::testIsCapitalizedWithWgCapitalLinks() now
- * testing the $wgCapitalLinkOverrides global.
- *
- * @todo split groups of assertions in autonomous testing functions
- * @covers MWNamespace::isCapitalized
- */
- public function testIsCapitalizedWithWgCapitalLinkOverrides() {
- global $wgCapitalLinkOverrides;
-
- // Test default settings
- $this->assertIsCapitalized( NS_PROJECT );
- $this->assertIsCapitalized( NS_PROJECT_TALK );
-
- // hardcoded namespaces (see above function) are capitalized:
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
-
- // Hardcoded namespaces remains capitalized
- $wgCapitalLinkOverrides[NS_SPECIAL] = false;
- $wgCapitalLinkOverrides[NS_USER] = false;
- $wgCapitalLinkOverrides[NS_MEDIAWIKI] = false;
-
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
-
- $wgCapitalLinkOverrides[NS_PROJECT] = false;
- $this->assertIsNotCapitalized( NS_PROJECT );
-
- $wgCapitalLinkOverrides[NS_PROJECT] = true;
- $this->assertIsCapitalized( NS_PROJECT );
-
- unset( $wgCapitalLinkOverrides[NS_PROJECT] );
- $this->assertIsCapitalized( NS_PROJECT );
- }
-
- /**
- * @covers MWNamespace::hasGenderDistinction
- */
- public function testHasGenderDistinction() {
- // Namespaces with gender distinctions
- $this->assertTrue( MWNamespace::hasGenderDistinction( NS_USER ) );
- $this->assertTrue( MWNamespace::hasGenderDistinction( NS_USER_TALK ) );
-
- // Other ones, "genderless"
- $this->assertFalse( MWNamespace::hasGenderDistinction( NS_MEDIA ) );
- $this->assertFalse( MWNamespace::hasGenderDistinction( NS_SPECIAL ) );
- $this->assertFalse( MWNamespace::hasGenderDistinction( NS_MAIN ) );
- $this->assertFalse( MWNamespace::hasGenderDistinction( NS_TALK ) );
- }
-
- /**
- * @covers MWNamespace::isNonincludable
- */
- public function testIsNonincludable() {
- global $wgNonincludableNamespaces;
-
- $wgNonincludableNamespaces = [ NS_USER ];
-
- $this->assertTrue( MWNamespace::isNonincludable( NS_USER ) );
- $this->assertFalse( MWNamespace::isNonincludable( NS_TEMPLATE ) );
- }
-
- private function assertSameSubject( $ns1, $ns2, $msg = '' ) {
- $this->assertTrue( MWNamespace::subjectEquals( $ns1, $ns2 ), $msg );
- }
-
- private function assertDifferentSubject( $ns1, $ns2, $msg = '' ) {
- $this->assertFalse( MWNamespace::subjectEquals( $ns1, $ns2 ), $msg );
- }
-
- public function provideGetCategoryLinkType() {
- return [
- [ NS_MAIN, 'page' ],
- [ NS_TALK, 'page' ],
- [ NS_USER, 'page' ],
- [ NS_USER_TALK, 'page' ],
-
- [ NS_FILE, 'file' ],
- [ NS_FILE_TALK, 'page' ],
-
- [ NS_CATEGORY, 'subcat' ],
- [ NS_CATEGORY_TALK, 'page' ],
-
- [ 100, 'page' ],
- [ 101, 'page' ],
- ];
- }
-
- /**
- * @dataProvider provideGetCategoryLinkType
- * @covers MWNamespace::getCategoryLinkType
- *
- * @param int $index
- * @param string $expected
- */
- public function testGetCategoryLinkType( $index, $expected ) {
- $actual = MWNamespace::getCategoryLinkType( $index );
- $this->assertSame( $expected, $actual, "NS $index" );
- }
-}
--- /dev/null
+<?php
+/**
+ * @author Antoine Musso
+ * @copyright Copyright © 2011, Antoine Musso
+ * @file
+ */
+
+use MediaWiki\MediaWikiServices;
+
+class NamespaceInfoTest extends MediaWikiTestCase {
+
+ /** @var NamespaceInfo */
+ private $obj;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->setMwGlobals( [
+ 'wgContentNamespaces' => [ NS_MAIN ],
+ 'wgNamespacesWithSubpages' => [
+ NS_TALK => true,
+ NS_USER => true,
+ NS_USER_TALK => true,
+ ],
+ 'wgCapitalLinks' => true,
+ 'wgCapitalLinkOverrides' => [],
+ 'wgNonincludableNamespaces' => [],
+ ] );
+
+ $this->obj = MediaWikiServices::getInstance()->getNamespaceInfo();
+ }
+
+ /**
+ * @todo Write more texts, handle $wgAllowImageMoving setting
+ * @covers NamespaceInfo::isMovable
+ */
+ public function testIsMovable() {
+ $this->assertFalse( $this->obj->isMovable( NS_SPECIAL ) );
+ }
+
+ private function assertIsSubject( $ns ) {
+ $this->assertTrue( $this->obj->isSubject( $ns ) );
+ }
+
+ private function assertIsNotSubject( $ns ) {
+ $this->assertFalse( $this->obj->isSubject( $ns ) );
+ }
+
+ /**
+ * Please make sure to change testIsTalk() if you change the assertions below
+ * @covers NamespaceInfo::isSubject
+ */
+ public function testIsSubject() {
+ // Special namespaces
+ $this->assertIsSubject( NS_MEDIA );
+ $this->assertIsSubject( NS_SPECIAL );
+
+ // Subject pages
+ $this->assertIsSubject( NS_MAIN );
+ $this->assertIsSubject( NS_USER );
+ $this->assertIsSubject( 100 ); # user defined
+
+ // Talk pages
+ $this->assertIsNotSubject( NS_TALK );
+ $this->assertIsNotSubject( NS_USER_TALK );
+ $this->assertIsNotSubject( 101 ); # user defined
+ }
+
+ private function assertIsTalk( $ns ) {
+ $this->assertTrue( $this->obj->isTalk( $ns ) );
+ }
+
+ private function assertIsNotTalk( $ns ) {
+ $this->assertFalse( $this->obj->isTalk( $ns ) );
+ }
+
+ /**
+ * Reverse of testIsSubject().
+ * Please update testIsSubject() if you change assertions below
+ * @covers NamespaceInfo::isTalk
+ */
+ public function testIsTalk() {
+ // Special namespaces
+ $this->assertIsNotTalk( NS_MEDIA );
+ $this->assertIsNotTalk( NS_SPECIAL );
+
+ // Subject pages
+ $this->assertIsNotTalk( NS_MAIN );
+ $this->assertIsNotTalk( NS_USER );
+ $this->assertIsNotTalk( 100 ); # user defined
+
+ // Talk pages
+ $this->assertIsTalk( NS_TALK );
+ $this->assertIsTalk( NS_USER_TALK );
+ $this->assertIsTalk( 101 ); # user defined
+ }
+
+ /**
+ * @covers NamespaceInfo::getSubject
+ */
+ public function testGetSubject() {
+ // Special namespaces are their own subjects
+ $this->assertEquals( NS_MEDIA, $this->obj->getSubject( NS_MEDIA ) );
+ $this->assertEquals( NS_SPECIAL, $this->obj->getSubject( NS_SPECIAL ) );
+
+ $this->assertEquals( NS_MAIN, $this->obj->getSubject( NS_TALK ) );
+ $this->assertEquals( NS_USER, $this->obj->getSubject( NS_USER_TALK ) );
+ }
+
+ /**
+ * Regular getTalk() calls
+ * Namespaces without a talk page (NS_MEDIA, NS_SPECIAL) are tested in
+ * the function testGetTalkExceptions()
+ * @covers NamespaceInfo::getTalk
+ */
+ public function testGetTalk() {
+ $this->assertEquals( NS_TALK, $this->obj->getTalk( NS_MAIN ) );
+ $this->assertEquals( NS_TALK, $this->obj->getTalk( NS_TALK ) );
+ $this->assertEquals( NS_USER_TALK, $this->obj->getTalk( NS_USER ) );
+ $this->assertEquals( NS_USER_TALK, $this->obj->getTalk( NS_USER_TALK ) );
+ }
+
+ /**
+ * Exceptions with getTalk()
+ * NS_MEDIA does not have talk pages. MediaWiki raise an exception for them.
+ * @expectedException MWException
+ * @covers NamespaceInfo::getTalk
+ */
+ public function testGetTalkExceptionsForNsMedia() {
+ $this->assertNull( $this->obj->getTalk( NS_MEDIA ) );
+ }
+
+ /**
+ * Exceptions with getTalk()
+ * NS_SPECIAL does not have talk pages. MediaWiki raise an exception for them.
+ * @expectedException MWException
+ * @covers NamespaceInfo::getTalk
+ */
+ public function testGetTalkExceptionsForNsSpecial() {
+ $this->assertNull( $this->obj->getTalk( NS_SPECIAL ) );
+ }
+
+ /**
+ * Regular getAssociated() calls
+ * Namespaces without an associated page (NS_MEDIA, NS_SPECIAL) are tested in
+ * the function testGetAssociatedExceptions()
+ * @covers NamespaceInfo::getAssociated
+ */
+ public function testGetAssociated() {
+ $this->assertEquals( NS_TALK, $this->obj->getAssociated( NS_MAIN ) );
+ $this->assertEquals( NS_MAIN, $this->obj->getAssociated( NS_TALK ) );
+ }
+
+ # ## Exceptions with getAssociated()
+ # ## NS_MEDIA and NS_SPECIAL do not have talk pages. MediaWiki raises
+ # ## an exception for them.
+ /**
+ * @expectedException MWException
+ * @covers NamespaceInfo::getAssociated
+ */
+ public function testGetAssociatedExceptionsForNsMedia() {
+ $this->assertNull( $this->obj->getAssociated( NS_MEDIA ) );
+ }
+
+ /**
+ * @expectedException MWException
+ * @covers NamespaceInfo::getAssociated
+ */
+ public function testGetAssociatedExceptionsForNsSpecial() {
+ $this->assertNull( $this->obj->getAssociated( NS_SPECIAL ) );
+ }
+
+ /**
+ * Note if we add a namespace registration system with keys like 'MAIN'
+ * we should add tests here for equivilance on things like 'MAIN' == 0
+ * and 'MAIN' == NS_MAIN.
+ * @covers NamespaceInfo::equals
+ */
+ public function testEquals() {
+ $this->assertTrue( $this->obj->equals( NS_MAIN, NS_MAIN ) );
+ $this->assertTrue( $this->obj->equals( NS_MAIN, 0 ) ); // In case we make NS_MAIN 'MAIN'
+ $this->assertTrue( $this->obj->equals( NS_USER, NS_USER ) );
+ $this->assertTrue( $this->obj->equals( NS_USER, 2 ) );
+ $this->assertTrue( $this->obj->equals( NS_USER_TALK, NS_USER_TALK ) );
+ $this->assertTrue( $this->obj->equals( NS_SPECIAL, NS_SPECIAL ) );
+ $this->assertFalse( $this->obj->equals( NS_MAIN, NS_TALK ) );
+ $this->assertFalse( $this->obj->equals( NS_USER, NS_USER_TALK ) );
+ $this->assertFalse( $this->obj->equals( NS_PROJECT, NS_TEMPLATE ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::subjectEquals
+ */
+ public function testSubjectEquals() {
+ $this->assertSameSubject( NS_MAIN, NS_MAIN );
+ $this->assertSameSubject( NS_MAIN, 0 ); // In case we make NS_MAIN 'MAIN'
+ $this->assertSameSubject( NS_USER, NS_USER );
+ $this->assertSameSubject( NS_USER, 2 );
+ $this->assertSameSubject( NS_USER_TALK, NS_USER_TALK );
+ $this->assertSameSubject( NS_SPECIAL, NS_SPECIAL );
+ $this->assertSameSubject( NS_MAIN, NS_TALK );
+ $this->assertSameSubject( NS_USER, NS_USER_TALK );
+
+ $this->assertDifferentSubject( NS_PROJECT, NS_TEMPLATE );
+ $this->assertDifferentSubject( NS_SPECIAL, NS_MAIN );
+ }
+
+ /**
+ * @covers NamespaceInfo::subjectEquals
+ */
+ public function testSpecialAndMediaAreDifferentSubjects() {
+ $this->assertDifferentSubject(
+ NS_MEDIA, NS_SPECIAL,
+ "NS_MEDIA and NS_SPECIAL are different subject namespaces"
+ );
+ $this->assertDifferentSubject(
+ NS_SPECIAL, NS_MEDIA,
+ "NS_SPECIAL and NS_MEDIA are different subject namespaces"
+ );
+ }
+
+ public function provideHasTalkNamespace() {
+ return [
+ [ NS_MEDIA, false ],
+ [ NS_SPECIAL, false ],
+
+ [ NS_MAIN, true ],
+ [ NS_TALK, true ],
+ [ NS_USER, true ],
+ [ NS_USER_TALK, true ],
+
+ [ 100, true ],
+ [ 101, true ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideHasTalkNamespace
+ * @covers NamespaceInfo::hasTalkNamespace
+ *
+ * @param int $index
+ * @param bool $expected
+ */
+ public function testHasTalkNamespace( $index, $expected ) {
+ $actual = $this->obj->hasTalkNamespace( $index );
+ $this->assertSame( $actual, $expected, "NS $index" );
+ }
+
+ /**
+ * @dataProvider provideHasTalkNamespace
+ * @covers MWNamespace::canTalk
+ *
+ * @param int $index
+ * @param bool $expected
+ */
+ public function testCanTalk( $index, $expected ) {
+ $this->hideDeprecated( 'MWNamespace::canTalk' );
+ $actual = MWNamespace::canTalk( $index );
+ $this->assertSame( $actual, $expected, "NS $index" );
+ }
+
+ private function assertIsContent( $ns ) {
+ $this->assertTrue( $this->obj->isContent( $ns ) );
+ }
+
+ private function assertIsNotContent( $ns ) {
+ $this->assertFalse( $this->obj->isContent( $ns ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::isContent
+ */
+ public function testIsContent() {
+ // NS_MAIN is a content namespace per DefaultSettings.php
+ // and per function definition.
+
+ $this->assertIsContent( NS_MAIN );
+
+ // Other namespaces which are not expected to be content
+
+ $this->assertIsNotContent( NS_MEDIA );
+ $this->assertIsNotContent( NS_SPECIAL );
+ $this->assertIsNotContent( NS_TALK );
+ $this->assertIsNotContent( NS_USER );
+ $this->assertIsNotContent( NS_CATEGORY );
+ $this->assertIsNotContent( 100 );
+ }
+
+ /**
+ * Similar to testIsContent() but alters the $wgContentNamespaces
+ * global variable.
+ * @covers NamespaceInfo::isContent
+ */
+ public function testIsContentAdvanced() {
+ global $wgContentNamespaces;
+
+ // Test that user defined namespace #252 is not content
+ $this->assertIsNotContent( 252 );
+
+ // Bless namespace # 252 as a content namespace
+ $wgContentNamespaces[] = 252;
+
+ $this->assertIsContent( 252 );
+
+ // Makes sure NS_MAIN was not impacted
+ $this->assertIsContent( NS_MAIN );
+ }
+
+ private function assertIsWatchable( $ns ) {
+ $this->assertTrue( $this->obj->isWatchable( $ns ) );
+ }
+
+ private function assertIsNotWatchable( $ns ) {
+ $this->assertFalse( $this->obj->isWatchable( $ns ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::isWatchable
+ */
+ public function testIsWatchable() {
+ // Specials namespaces are not watchable
+ $this->assertIsNotWatchable( NS_MEDIA );
+ $this->assertIsNotWatchable( NS_SPECIAL );
+
+ // Core defined namespaces are watchables
+ $this->assertIsWatchable( NS_MAIN );
+ $this->assertIsWatchable( NS_TALK );
+
+ // Additional, user defined namespaces are watchables
+ $this->assertIsWatchable( 100 );
+ $this->assertIsWatchable( 101 );
+ }
+
+ private function assertHasSubpages( $ns ) {
+ $this->assertTrue( $this->obj->hasSubpages( $ns ) );
+ }
+
+ private function assertHasNotSubpages( $ns ) {
+ $this->assertFalse( $this->obj->hasSubpages( $ns ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::hasSubpages
+ */
+ public function testHasSubpages() {
+ global $wgNamespacesWithSubpages;
+
+ // Special namespaces:
+ $this->assertHasNotSubpages( NS_MEDIA );
+ $this->assertHasNotSubpages( NS_SPECIAL );
+
+ // Namespaces without subpages
+ $this->assertHasNotSubpages( NS_MAIN );
+
+ $wgNamespacesWithSubpages[NS_MAIN] = true;
+ $this->assertHasSubpages( NS_MAIN );
+
+ $wgNamespacesWithSubpages[NS_MAIN] = false;
+ $this->assertHasNotSubpages( NS_MAIN );
+
+ // Some namespaces with subpages
+ $this->assertHasSubpages( NS_TALK );
+ $this->assertHasSubpages( NS_USER );
+ $this->assertHasSubpages( NS_USER_TALK );
+ }
+
+ /**
+ * @covers NamespaceInfo::getContentNamespaces
+ */
+ public function testGetContentNamespaces() {
+ global $wgContentNamespaces;
+
+ $this->assertEquals(
+ [ NS_MAIN ],
+ $this->obj->getContentNamespaces(),
+ '$wgContentNamespaces is an array with only NS_MAIN by default'
+ );
+
+ # test !is_array( $wgcontentNamespaces )
+ $wgContentNamespaces = '';
+ $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
+
+ $wgContentNamespaces = false;
+ $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
+
+ $wgContentNamespaces = null;
+ $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
+
+ $wgContentNamespaces = 5;
+ $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
+
+ # test $wgContentNamespaces === []
+ $wgContentNamespaces = [];
+ $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
+
+ # test !in_array( NS_MAIN, $wgContentNamespaces )
+ $wgContentNamespaces = [ NS_USER, NS_CATEGORY ];
+ $this->assertEquals(
+ [ NS_MAIN, NS_USER, NS_CATEGORY ],
+ $this->obj->getContentNamespaces(),
+ 'NS_MAIN is forced in $wgContentNamespaces even if unwanted'
+ );
+
+ # test other cases, return $wgcontentNamespaces as is
+ $wgContentNamespaces = [ NS_MAIN ];
+ $this->assertEquals(
+ [ NS_MAIN ],
+ $this->obj->getContentNamespaces()
+ );
+
+ $wgContentNamespaces = [ NS_MAIN, NS_USER, NS_CATEGORY ];
+ $this->assertEquals(
+ [ NS_MAIN, NS_USER, NS_CATEGORY ],
+ $this->obj->getContentNamespaces()
+ );
+ }
+
+ /**
+ * @covers NamespaceInfo::getSubjectNamespaces
+ */
+ public function testGetSubjectNamespaces() {
+ $subjectsNS = $this->obj->getSubjectNamespaces();
+ $this->assertContains( NS_MAIN, $subjectsNS,
+ "Talk namespaces should have NS_MAIN" );
+ $this->assertNotContains( NS_TALK, $subjectsNS,
+ "Talk namespaces should have NS_TALK" );
+
+ $this->assertNotContains( NS_MEDIA, $subjectsNS,
+ "Talk namespaces should not have NS_MEDIA" );
+ $this->assertNotContains( NS_SPECIAL, $subjectsNS,
+ "Talk namespaces should not have NS_SPECIAL" );
+ }
+
+ /**
+ * @covers NamespaceInfo::getTalkNamespaces
+ */
+ public function testGetTalkNamespaces() {
+ $talkNS = $this->obj->getTalkNamespaces();
+ $this->assertContains( NS_TALK, $talkNS,
+ "Subject namespaces should have NS_TALK" );
+ $this->assertNotContains( NS_MAIN, $talkNS,
+ "Subject namespaces should not have NS_MAIN" );
+
+ $this->assertNotContains( NS_MEDIA, $talkNS,
+ "Subject namespaces should not have NS_MEDIA" );
+ $this->assertNotContains( NS_SPECIAL, $talkNS,
+ "Subject namespaces should not have NS_SPECIAL" );
+ }
+
+ private function assertIsCapitalized( $ns ) {
+ $this->assertTrue( $this->obj->isCapitalized( $ns ) );
+ }
+
+ private function assertIsNotCapitalized( $ns ) {
+ $this->assertFalse( $this->obj->isCapitalized( $ns ) );
+ }
+
+ /**
+ * Some namespaces are always capitalized per code definition
+ * in NamespaceInfo::$alwaysCapitalizedNamespaces
+ * @covers NamespaceInfo::isCapitalized
+ */
+ public function testIsCapitalizedHardcodedAssertions() {
+ // NS_MEDIA and NS_FILE are treated the same
+ $this->assertEquals(
+ $this->obj->isCapitalized( NS_MEDIA ),
+ $this->obj->isCapitalized( NS_FILE ),
+ 'NS_MEDIA and NS_FILE have same capitalization rendering'
+ );
+
+ // Boths are capitalized by default
+ $this->assertIsCapitalized( NS_MEDIA );
+ $this->assertIsCapitalized( NS_FILE );
+
+ // Always capitalized namespaces
+ // @see NamespaceInfo::$alwaysCapitalizedNamespaces
+ $this->assertIsCapitalized( NS_SPECIAL );
+ $this->assertIsCapitalized( NS_USER );
+ $this->assertIsCapitalized( NS_MEDIAWIKI );
+ }
+
+ /**
+ * Follows up for testIsCapitalizedHardcodedAssertions() but alter the
+ * global $wgCapitalLink setting to have extended coverage.
+ *
+ * NamespaceInfo::isCapitalized() rely on two global settings:
+ * $wgCapitalLinkOverrides = []; by default
+ * $wgCapitalLinks = true; by default
+ * This function test $wgCapitalLinks
+ *
+ * Global setting correctness is tested against the NS_PROJECT and
+ * NS_PROJECT_TALK namespaces since they are not hardcoded nor specials
+ * @covers NamespaceInfo::isCapitalized
+ */
+ public function testIsCapitalizedWithWgCapitalLinks() {
+ $this->assertIsCapitalized( NS_PROJECT );
+ $this->assertIsCapitalized( NS_PROJECT_TALK );
+
+ $this->setMwGlobals( 'wgCapitalLinks', false );
+
+ // hardcoded namespaces (see above function) are still capitalized:
+ $this->assertIsCapitalized( NS_SPECIAL );
+ $this->assertIsCapitalized( NS_USER );
+ $this->assertIsCapitalized( NS_MEDIAWIKI );
+
+ // setting is correctly applied
+ $this->assertIsNotCapitalized( NS_PROJECT );
+ $this->assertIsNotCapitalized( NS_PROJECT_TALK );
+ }
+
+ /**
+ * Counter part for NamespaceInfo::testIsCapitalizedWithWgCapitalLinks() now
+ * testing the $wgCapitalLinkOverrides global.
+ *
+ * @todo split groups of assertions in autonomous testing functions
+ * @covers NamespaceInfo::isCapitalized
+ */
+ public function testIsCapitalizedWithWgCapitalLinkOverrides() {
+ global $wgCapitalLinkOverrides;
+
+ // Test default settings
+ $this->assertIsCapitalized( NS_PROJECT );
+ $this->assertIsCapitalized( NS_PROJECT_TALK );
+
+ // hardcoded namespaces (see above function) are capitalized:
+ $this->assertIsCapitalized( NS_SPECIAL );
+ $this->assertIsCapitalized( NS_USER );
+ $this->assertIsCapitalized( NS_MEDIAWIKI );
+
+ // Hardcoded namespaces remains capitalized
+ $wgCapitalLinkOverrides[NS_SPECIAL] = false;
+ $wgCapitalLinkOverrides[NS_USER] = false;
+ $wgCapitalLinkOverrides[NS_MEDIAWIKI] = false;
+
+ $this->assertIsCapitalized( NS_SPECIAL );
+ $this->assertIsCapitalized( NS_USER );
+ $this->assertIsCapitalized( NS_MEDIAWIKI );
+
+ $wgCapitalLinkOverrides[NS_PROJECT] = false;
+ $this->assertIsNotCapitalized( NS_PROJECT );
+
+ $wgCapitalLinkOverrides[NS_PROJECT] = true;
+ $this->assertIsCapitalized( NS_PROJECT );
+
+ unset( $wgCapitalLinkOverrides[NS_PROJECT] );
+ $this->assertIsCapitalized( NS_PROJECT );
+ }
+
+ /**
+ * @covers NamespaceInfo::hasGenderDistinction
+ */
+ public function testHasGenderDistinction() {
+ // Namespaces with gender distinctions
+ $this->assertTrue( $this->obj->hasGenderDistinction( NS_USER ) );
+ $this->assertTrue( $this->obj->hasGenderDistinction( NS_USER_TALK ) );
+
+ // Other ones, "genderless"
+ $this->assertFalse( $this->obj->hasGenderDistinction( NS_MEDIA ) );
+ $this->assertFalse( $this->obj->hasGenderDistinction( NS_SPECIAL ) );
+ $this->assertFalse( $this->obj->hasGenderDistinction( NS_MAIN ) );
+ $this->assertFalse( $this->obj->hasGenderDistinction( NS_TALK ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::isNonincludable
+ */
+ public function testIsNonincludable() {
+ global $wgNonincludableNamespaces;
+
+ $wgNonincludableNamespaces = [ NS_USER ];
+
+ $this->assertTrue( $this->obj->isNonincludable( NS_USER ) );
+ $this->assertFalse( $this->obj->isNonincludable( NS_TEMPLATE ) );
+ }
+
+ private function assertSameSubject( $ns1, $ns2, $msg = '' ) {
+ $this->assertTrue( $this->obj->subjectEquals( $ns1, $ns2 ), $msg );
+ }
+
+ private function assertDifferentSubject( $ns1, $ns2, $msg = '' ) {
+ $this->assertFalse( $this->obj->subjectEquals( $ns1, $ns2 ), $msg );
+ }
+
+ public function provideGetCategoryLinkType() {
+ return [
+ [ NS_MAIN, 'page' ],
+ [ NS_TALK, 'page' ],
+ [ NS_USER, 'page' ],
+ [ NS_USER_TALK, 'page' ],
+
+ [ NS_FILE, 'file' ],
+ [ NS_FILE_TALK, 'page' ],
+
+ [ NS_CATEGORY, 'subcat' ],
+ [ NS_CATEGORY_TALK, 'page' ],
+
+ [ 100, 'page' ],
+ [ 101, 'page' ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideGetCategoryLinkType
+ * @covers NamespaceInfo::getCategoryLinkType
+ *
+ * @param int $index
+ * @param string $expected
+ */
+ public function testGetCategoryLinkType( $index, $expected ) {
+ $actual = $this->obj->getCategoryLinkType( $index );
+ $this->assertSame( $expected, $actual, "NS $index" );
+ }
+}