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 }
array false $namespaceIndexes
Canonical namespaces index cache.
const NS_MAIN
Definition: Defines.php:60
__construct(ServiceOptions $options)
getCategoryLinkType( $index)
Returns the link type to be used for categories.
exists( $index)
Returns whether the specified namespace exists.
getSubjectPage(LinkTarget $target)
static getCommonNamespaces()
Retrieve the indexes for the namespaces defined by core.
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:36
string [] null $canonicalNamespaces
Canonical namespaces cache.
hasSubpages( $index)
Does the namespace allow subpages?
const NS_TEMPLATE
Definition: Defines.php:70
getNamespace()
Get the namespace index.
getSubject( $index)
Get the subject namespace index for a given namespace Special namespaces (NS_MEDIA, NS_SPECIAL) are always the subject.
A class for passing options to services.
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:51
canHaveTalkPage(LinkTarget $target)
Can the title have a corresponding talk page?
const NS_PROJECT
Definition: Defines.php:64
getAssociatedPage(LinkTarget $target)
getTalk( $index)
Get the talk namespace index for a given namespace.
const NS_MEDIAWIKI_TALK
Definition: Defines.php:69
getNamespaceContentModel( $index)
Get the default content model for a namespace This does not mean that all pages in that namespace hav...
const NS_PROJECT_TALK
Definition: Defines.php:65
const NS_MEDIA
Definition: Defines.php:48
hasGenderDistinction( $index)
Does the namespace (potentially) have different aliases for different genders.
hasTalkNamespace( $index)
Does this namespace ever have a talk namespace?
getValidNamespaces()
Returns an array of the namespaces (by integer id) that exist on the wiki.
getCanonicalIndex( $name)
Returns the index for a given canonical name, or NULL The input must be converted to lower case first...
equals( $ns1, $ns2)
Returns whether the specified namespaces are the same namespace.
getSubjectNamespaces()
List all namespace indices which are considered subject, aka not a talk or special namespace...
getDBkey()
Get the main part with underscores.
const NS_CATEGORY
Definition: Defines.php:74
isMovable( $index)
Can pages in the given namespace be moved?
subjectEquals( $ns1, $ns2)
Returns whether the specified namespaces share the same subject.
const NS_FILE
Definition: Defines.php:66
ServiceOptions $options
const NS_FILE_TALK
Definition: Defines.php:67
int [] null $validNamespaces
Valid namespaces cache.
const NS_MEDIAWIKI
Definition: Defines.php:68
isMethodValidFor( $index, $method)
Throw an exception when trying to get the subject or talk page for a given namespace where it does no...
getTalkNamespaces()
List all namespace indices which are considered talks, aka not a subject or special namespace...
const CONSTRUCTOR_OPTIONS
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys, without regard for order.
isWatchable( $index)
Can pages in a namespace be watched?
const NS_CATEGORY_TALK
Definition: Defines.php:75
getAssociated( $index)
Get the associated namespace.
const NS_HELP
Definition: Defines.php:72
isNonincludable( $index)
It is not possible to use pages from this namespace as template?
const NS_HELP_TALK
Definition: Defines.php:73
isCapitalized( $index)
Is the namespace first-letter capitalized?
getTalkPage(LinkTarget $target)
Get a LinkTarget referring to the talk page of $target.
wantSignatures( $index)
Might pages in this namespace require the use of the Signature button on the edit toolbar...
isContent( $index)
Does this namespace contain content, for the purposes of calculating statistics, etc?
getInterwiki()
The interwiki component of this LinkTarget.
getText()
Returns the link in text form, without namespace prefix or fragment.
isTalk( $index)
Is the given namespace a talk namespace?
getCanonicalName( $index)
Returns the canonical (English) name for a given index.
const NS_TALK
Definition: Defines.php:61
getRestrictionLevels( $index, User $user=null)
Determine which restriction levels it makes sense to use in a namespace, optionally filtered by a use...
const NS_USER_TALK
Definition: Defines.php:63
getContentNamespaces()
Get a list of all namespace indices which are considered to contain content.
$alwaysCapitalizedNamespaces
These namespaces should always be first-letter capitalized, now and forevermore.
const CANONICAL_NAMES
Definitions of the NS_ constants are in Defines.php.
getCanonicalNamespaces()
Returns array of all defined namespaces with their canonical (English) names.
const NS_TEMPLATE_TALK
Definition: Defines.php:71
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
isSubject( $index)
Is the given namespace is a subject (non-talk) namespace?