MediaWiki  master
NamespaceInfo.php
Go to the documentation of this file.
1 <?php
26 
34 
41 
43  private $canonicalNamespaces = null;
44 
46  private $namespaceIndexes = false;
47 
49  private $validNamespaces = null;
50 
52  private $options;
53 
59  public const CANONICAL_NAMES = [
60  NS_MEDIA => 'Media',
61  NS_SPECIAL => 'Special',
62  NS_MAIN => '',
63  NS_TALK => 'Talk',
64  NS_USER => 'User',
65  NS_USER_TALK => 'User_talk',
66  NS_PROJECT => 'Project',
67  NS_PROJECT_TALK => 'Project_talk',
68  NS_FILE => 'File',
69  NS_FILE_TALK => 'File_talk',
70  NS_MEDIAWIKI => 'MediaWiki',
71  NS_MEDIAWIKI_TALK => 'MediaWiki_talk',
72  NS_TEMPLATE => 'Template',
73  NS_TEMPLATE_TALK => 'Template_talk',
74  NS_HELP => 'Help',
75  NS_HELP_TALK => 'Help_talk',
76  NS_CATEGORY => 'Category',
77  NS_CATEGORY_TALK => 'Category_talk',
78  ];
79 
84  public const CONSTRUCTOR_OPTIONS = [
85  'AllowImageMoving',
86  'CanonicalNamespaceNames',
87  'CapitalLinkOverrides',
88  'CapitalLinks',
89  'ContentNamespaces',
90  'ExtraNamespaces',
91  'ExtraSignatureNamespaces',
92  'NamespaceContentModels',
93  'NamespacesWithSubpages',
94  'NonincludableNamespaces',
95  ];
96 
100  public function __construct( ServiceOptions $options ) {
101  $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
102  $this->options = $options;
103  }
104 
117  private function isMethodValidFor( $index, $method ) {
118  if ( $index < NS_MAIN ) {
119  throw new MWException( "$method does not make any sense for given namespace $index" );
120  }
121  return true;
122  }
123 
130  public function isMovable( $index ) {
131  $result = $index >= NS_MAIN &&
132  ( $index != NS_FILE || $this->options->get( 'AllowImageMoving' ) );
133 
137  Hooks::run( 'NamespaceIsMovable', [ $index, &$result ] );
138 
139  return $result;
140  }
141 
148  public function isSubject( $index ) {
149  return !$this->isTalk( $index );
150  }
151 
158  public function isTalk( $index ) {
159  return $index > NS_MAIN
160  && $index % 2;
161  }
162 
171  public function getTalk( $index ) {
172  $this->isMethodValidFor( $index, __METHOD__ );
173  return $this->isTalk( $index )
174  ? $index
175  : $index + 1;
176  }
177 
187  public function getTalkPage( LinkTarget $target ) : LinkTarget {
188  if ( $target->getText() === '' ) {
189  throw new MWException( 'Can\'t determine talk page associated with relative section link' );
190  }
191 
192  if ( $target->getInterwiki() !== '' ) {
193  throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
194  }
195 
196  if ( $this->isTalk( $target->getNamespace() ) ) {
197  return $target;
198  }
199 
200  // NOTE: getTalk throws on bad namespaces!
201  return new TitleValue( $this->getTalk( $target->getNamespace() ), $target->getDBkey() );
202  }
203 
215  public function canHaveTalkPage( LinkTarget $target ) {
216  if ( $target->getText() === '' || $target->getInterwiki() !== '' ) {
217  return false;
218  }
219 
220  if ( $target->getNamespace() < NS_MAIN ) {
221  return false;
222  }
223 
224  return true;
225  }
226 
234  public function getSubject( $index ) {
235  # Handle special namespaces
236  if ( $index < NS_MAIN ) {
237  return $index;
238  }
239 
240  return $this->isTalk( $index )
241  ? $index - 1
242  : $index;
243  }
244 
249  public function getSubjectPage( LinkTarget $target ) : LinkTarget {
250  if ( $this->isSubject( $target->getNamespace() ) ) {
251  return $target;
252  }
253  return new TitleValue( $this->getSubject( $target->getNamespace() ), $target->getDBkey() );
254  }
255 
265  public function getAssociated( $index ) {
266  $this->isMethodValidFor( $index, __METHOD__ );
267 
268  if ( $this->isSubject( $index ) ) {
269  return $this->getTalk( $index );
270  }
271  return $this->getSubject( $index );
272  }
273 
280  public function getAssociatedPage( LinkTarget $target ) : LinkTarget {
281  if ( $target->getText() === '' ) {
282  throw new MWException( 'Can\'t determine talk page associated with relative section link' );
283  }
284 
285  if ( $target->getInterwiki() !== '' ) {
286  throw new MWException( 'Can\'t determine talk page associated with interwiki link' );
287  }
288 
289  return new TitleValue(
290  $this->getAssociated( $target->getNamespace() ), $target->getDBkey() );
291  }
292 
300  public function exists( $index ) {
301  $nslist = $this->getCanonicalNamespaces();
302  return isset( $nslist[$index] );
303  }
304 
318  public function equals( $ns1, $ns2 ) {
319  return $ns1 == $ns2;
320  }
321 
332  public function subjectEquals( $ns1, $ns2 ) {
333  return $this->getSubject( $ns1 ) == $this->getSubject( $ns2 );
334  }
335 
342  public function getCanonicalNamespaces() {
343  if ( $this->canonicalNamespaces === null ) {
344  $this->canonicalNamespaces =
345  [ NS_MAIN => '' ] + $this->options->get( 'CanonicalNamespaceNames' );
346  $this->canonicalNamespaces +=
347  ExtensionRegistry::getInstance()->getAttribute( 'ExtensionNamespaces' );
348  if ( is_array( $this->options->get( 'ExtraNamespaces' ) ) ) {
349  $this->canonicalNamespaces += $this->options->get( 'ExtraNamespaces' );
350  }
351  Hooks::run( 'CanonicalNamespaces', [ &$this->canonicalNamespaces ] );
352  }
354  }
355 
362  public function getCanonicalName( $index ) {
363  $nslist = $this->getCanonicalNamespaces();
364  return $nslist[$index] ?? false;
365  }
366 
374  public function getCanonicalIndex( $name ) {
375  if ( $this->namespaceIndexes === false ) {
376  $this->namespaceIndexes = [];
377  foreach ( $this->getCanonicalNamespaces() as $i => $text ) {
378  $this->namespaceIndexes[strtolower( $text )] = $i;
379  }
380  }
381  if ( array_key_exists( $name, $this->namespaceIndexes ) ) {
382  return $this->namespaceIndexes[$name];
383  } else {
384  return null;
385  }
386  }
387 
393  public function getValidNamespaces() {
394  if ( is_null( $this->validNamespaces ) ) {
395  $this->validNamespaces = [];
396  foreach ( array_keys( $this->getCanonicalNamespaces() ) as $ns ) {
397  if ( $ns >= 0 ) {
398  $this->validNamespaces[] = $ns;
399  }
400  }
401  // T109137: sort numerically
402  sort( $this->validNamespaces, SORT_NUMERIC );
403  }
404 
405  return $this->validNamespaces;
406  }
407 
414  public function hasTalkNamespace( $index ) {
415  return $index >= NS_MAIN;
416  }
417 
425  public function isContent( $index ) {
426  return $index == NS_MAIN || in_array( $index, $this->options->get( 'ContentNamespaces' ) );
427  }
428 
436  public function wantSignatures( $index ) {
437  return $this->isTalk( $index ) ||
438  in_array( $index, $this->options->get( 'ExtraSignatureNamespaces' ) );
439  }
440 
447  public function isWatchable( $index ) {
448  return $index >= NS_MAIN;
449  }
450 
457  public function hasSubpages( $index ) {
458  return !empty( $this->options->get( 'NamespacesWithSubpages' )[$index] );
459  }
460 
465  public function getContentNamespaces() {
466  $contentNamespaces = $this->options->get( 'ContentNamespaces' );
467  if ( !is_array( $contentNamespaces ) || $contentNamespaces === [] ) {
468  return [ NS_MAIN ];
469  } elseif ( !in_array( NS_MAIN, $contentNamespaces ) ) {
470  // always force NS_MAIN to be part of array (to match the algorithm used by isContent)
471  return array_merge( [ NS_MAIN ], $contentNamespaces );
472  } else {
473  return $contentNamespaces;
474  }
475  }
476 
483  public function getSubjectNamespaces() {
484  return array_filter(
485  $this->getValidNamespaces(),
486  [ $this, 'isSubject' ]
487  );
488  }
489 
496  public function getTalkNamespaces() {
497  return array_filter(
498  $this->getValidNamespaces(),
499  [ $this, 'isTalk' ]
500  );
501  }
502 
509  public function isCapitalized( $index ) {
510  // Turn NS_MEDIA into NS_FILE
511  $index = $index === NS_MEDIA ? NS_FILE : $index;
512 
513  // Make sure to get the subject of our namespace
514  $index = $this->getSubject( $index );
515 
516  // Some namespaces are special and should always be upper case
517  if ( in_array( $index, $this->alwaysCapitalizedNamespaces ) ) {
518  return true;
519  }
520  $overrides = $this->options->get( 'CapitalLinkOverrides' );
521  if ( isset( $overrides[$index] ) ) {
522  // CapitalLinkOverrides is explicitly set
523  return $overrides[$index];
524  }
525  // Default to the global setting
526  return $this->options->get( 'CapitalLinks' );
527  }
528 
536  public function hasGenderDistinction( $index ) {
537  return $index == NS_USER || $index == NS_USER_TALK;
538  }
539 
546  public function isNonincludable( $index ) {
547  $namespaces = $this->options->get( 'NonincludableNamespaces' );
548  return $namespaces && in_array( $index, $namespaces );
549  }
550 
561  public function getNamespaceContentModel( $index ) {
562  return $this->options->get( 'NamespaceContentModels' )[$index] ?? null;
563  }
564 
574  public function getRestrictionLevels( $index, User $user = null ) {
575  // PermissionManager is not injected because adding an explicit dependency
576  // breaks MW installer by adding a dependency chain on the database before
577  // it was set up. Also, the method is deprecated and will be soon removed.
578  return MediaWikiServices::getInstance()
579  ->getPermissionManager()
580  ->getNamespaceRestrictionLevels( $index, $user );
581  }
582 
592  public function getCategoryLinkType( $index ) {
593  $this->isMethodValidFor( $index, __METHOD__ );
594 
595  if ( $index == NS_CATEGORY ) {
596  return 'subcat';
597  } elseif ( $index == NS_FILE ) {
598  return 'file';
599  } else {
600  return 'page';
601  }
602  }
603 
611  public static function getCommonNamespaces() {
612  return array_keys( self::CANONICAL_NAMES );
613  }
614 }
MediaWiki\Linker\LinkTarget\getInterwiki
getInterwiki()
The interwiki component of this LinkTarget.
NamespaceInfo\__construct
__construct(ServiceOptions $options)
Definition: NamespaceInfo.php:100
MediaWiki\Linker\LinkTarget\getText
getText()
Returns the link in text form, without namespace prefix or fragment.
NamespaceInfo\isMovable
isMovable( $index)
Can pages in the given namespace be moved?
Definition: NamespaceInfo.php:130
NS_HELP
const NS_HELP
Definition: Defines.php:72
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:129
NS_TEMPLATE_TALK
const NS_TEMPLATE_TALK
Definition: Defines.php:71
NamespaceInfo\isWatchable
isWatchable( $index)
Can pages in a namespace be watched?
Definition: NamespaceInfo.php:447
NamespaceInfo\getAssociatedPage
getAssociatedPage(LinkTarget $target)
Definition: NamespaceInfo.php:280
NamespaceInfo\hasTalkNamespace
hasTalkNamespace( $index)
Does this namespace ever have a talk namespace?
Definition: NamespaceInfo.php:414
NamespaceInfo\getValidNamespaces
getValidNamespaces()
Returns an array of the namespaces (by integer id) that exist on the wiki.
Definition: NamespaceInfo.php:393
NamespaceInfo\CANONICAL_NAMES
const CANONICAL_NAMES
Definitions of the NS_ constants are in Defines.php.
Definition: NamespaceInfo.php:59
NS_FILE
const NS_FILE
Definition: Defines.php:66
NamespaceInfo\getCanonicalNamespaces
getCanonicalNamespaces()
Returns array of all defined namespaces with their canonical (English) names.
Definition: NamespaceInfo.php:342
NS_TEMPLATE
const NS_TEMPLATE
Definition: Defines.php:70
NamespaceInfo\getCategoryLinkType
getCategoryLinkType( $index)
Returns the link type to be used for categories.
Definition: NamespaceInfo.php:592
NamespaceInfo\wantSignatures
wantSignatures( $index)
Might pages in this namespace require the use of the Signature button on the edit toolbar?
Definition: NamespaceInfo.php:436
NamespaceInfo\$options
ServiceOptions $options
Definition: NamespaceInfo.php:52
NamespaceInfo\subjectEquals
subjectEquals( $ns1, $ns2)
Returns whether the specified namespaces share the same subject.
Definition: NamespaceInfo.php:332
NamespaceInfo\isSubject
isSubject( $index)
Is the given namespace is a subject (non-talk) namespace?
Definition: NamespaceInfo.php:148
NamespaceInfo\$canonicalNamespaces
string[] null $canonicalNamespaces
Canonical namespaces cache.
Definition: NamespaceInfo.php:43
NamespaceInfo\$namespaceIndexes
array false $namespaceIndexes
Canonical namespaces index cache.
Definition: NamespaceInfo.php:46
NamespaceInfo\hasSubpages
hasSubpages( $index)
Does the namespace allow subpages?
Definition: NamespaceInfo.php:457
NS_MAIN
const NS_MAIN
Definition: Defines.php:60
ExtensionRegistry\getInstance
static getInstance()
Definition: ExtensionRegistry.php:127
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:49
MediaWiki\Linker\LinkTarget\getNamespace
getNamespace()
Get the namespace index.
MWException
MediaWiki exception.
Definition: MWException.php:26
NS_PROJECT
const NS_PROJECT
Definition: Defines.php:64
NamespaceInfo\getSubjectNamespaces
getSubjectNamespaces()
List all namespace indices which are considered subject, aka not a talk or special namespace.
Definition: NamespaceInfo.php:483
MediaWiki\Config\ServiceOptions
A class for passing options to services.
Definition: ServiceOptions.php:25
NamespaceInfo\getSubject
getSubject( $index)
Get the subject namespace index for a given namespace Special namespaces (NS_MEDIA,...
Definition: NamespaceInfo.php:234
NamespaceInfo\getSubjectPage
getSubjectPage(LinkTarget $target)
Definition: NamespaceInfo.php:249
NamespaceInfo\getContentNamespaces
getContentNamespaces()
Get a list of all namespace indices which are considered to contain content.
Definition: NamespaceInfo.php:465
NamespaceInfo\canHaveTalkPage
canHaveTalkPage(LinkTarget $target)
Can the title have a corresponding talk page?
Definition: NamespaceInfo.php:215
NS_MEDIAWIKI_TALK
const NS_MEDIAWIKI_TALK
Definition: Defines.php:69
NamespaceInfo\getTalkNamespaces
getTalkNamespaces()
List all namespace indices which are considered talks, aka not a subject or special namespace.
Definition: NamespaceInfo.php:496
NamespaceInfo\isContent
isContent( $index)
Does this namespace contain content, for the purposes of calculating statistics, etc?
Definition: NamespaceInfo.php:425
NamespaceInfo\getTalk
getTalk( $index)
Get the talk namespace index for a given namespace.
Definition: NamespaceInfo.php:171
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:74
NamespaceInfo\CONSTRUCTOR_OPTIONS
const CONSTRUCTOR_OPTIONS
Definition: NamespaceInfo.php:84
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:63
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:48
NamespaceInfo\exists
exists( $index)
Returns whether the specified namespace exists.
Definition: NamespaceInfo.php:300
NamespaceInfo\$alwaysCapitalizedNamespaces
$alwaysCapitalizedNamespaces
These namespaces should always be first-letter capitalized, now and forevermore.
Definition: NamespaceInfo.php:40
MediaWiki\Linker\LinkTarget\getDBkey
getDBkey()
Get the main part with underscores.
NS_PROJECT_TALK
const NS_PROJECT_TALK
Definition: Defines.php:65
NamespaceInfo\getCanonicalName
getCanonicalName( $index)
Returns the canonical (English) name for a given index.
Definition: NamespaceInfo.php:362
NamespaceInfo\isTalk
isTalk( $index)
Is the given namespace a talk namespace?
Definition: NamespaceInfo.php:158
NamespaceInfo\getNamespaceContentModel
getNamespaceContentModel( $index)
Get the default content model for a namespace This does not mean that all pages in that namespace hav...
Definition: NamespaceInfo.php:561
NamespaceInfo\hasGenderDistinction
hasGenderDistinction( $index)
Does the namespace (potentially) have different aliases for different genders.
Definition: NamespaceInfo.php:536
NamespaceInfo\equals
equals( $ns1, $ns2)
Returns whether the specified namespaces are the same namespace.
Definition: NamespaceInfo.php:318
NamespaceInfo\isNonincludable
isNonincludable( $index)
It is not possible to use pages from this namespace as template?
Definition: NamespaceInfo.php:546
NamespaceInfo\getCanonicalIndex
getCanonicalIndex( $name)
Returns the index for a given canonical name, or NULL The input must be converted to lower case first...
Definition: NamespaceInfo.php:374
NS_HELP_TALK
const NS_HELP_TALK
Definition: Defines.php:73
NamespaceInfo\isCapitalized
isCapitalized( $index)
Is the namespace first-letter capitalized?
Definition: NamespaceInfo.php:509
NS_USER
const NS_USER
Definition: Defines.php:62
NS_TALK
const NS_TALK
Definition: Defines.php:61
NamespaceInfo\getTalkPage
getTalkPage(LinkTarget $target)
Get a LinkTarget referring to the talk page of $target.
Definition: NamespaceInfo.php:187
NamespaceInfo
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Definition: NamespaceInfo.php:33
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:68
NamespaceInfo\$validNamespaces
int[] null $validNamespaces
Valid namespaces cache.
Definition: NamespaceInfo.php:49
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
NS_FILE_TALK
const NS_FILE_TALK
Definition: Defines.php:67
NS_CATEGORY_TALK
const NS_CATEGORY_TALK
Definition: Defines.php:75
NamespaceInfo\getCommonNamespaces
static getCommonNamespaces()
Retrieve the indexes for the namespaces defined by core.
Definition: NamespaceInfo.php:611
NamespaceInfo\isMethodValidFor
isMethodValidFor( $index, $method)
Throw an exception when trying to get the subject or talk page for a given namespace where it does no...
Definition: NamespaceInfo.php:117
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:51
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
NamespaceInfo\getAssociated
getAssociated( $index)
Get the associated namespace.
Definition: NamespaceInfo.php:265
NamespaceInfo\getRestrictionLevels
getRestrictionLevels( $index, User $user=null)
Determine which restriction levels it makes sense to use in a namespace, optionally filtered by a use...
Definition: NamespaceInfo.php:574
MediaWiki\Config\ServiceOptions\assertRequiredOptions
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
Definition: ServiceOptions.php:62
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:37