MediaWiki  1.32.0
Title.php
Go to the documentation of this file.
1 <?php
30 
39 class Title implements LinkTarget {
41  static private $titleCache = null;
42 
48  const CACHE_MAX = 1000;
49 
54  const GAID_FOR_UPDATE = 1;
55 
61  // @{
62 
64  public $mTextform = '';
65 
67  public $mUrlform = '';
68 
70  public $mDbkeyform = '';
71 
73  protected $mUserCaseDBKey;
74 
76  public $mNamespace = NS_MAIN;
77 
79  public $mInterwiki = '';
80 
82  private $mLocalInterwiki = false;
83 
85  public $mFragment = '';
86 
88  public $mArticleID = -1;
89 
91  protected $mLatestID = false;
92 
97  private $mContentModel = false;
98 
103  private $mForcedContentModel = false;
104 
107 
109  public $mRestrictions = [];
110 
117  protected $mOldRestrictions = false;
118 
121 
124 
126  protected $mRestrictionsExpiry = [];
127 
130 
133 
135  public $mRestrictionsLoaded = false;
136 
145  public $prefixedText = null;
146 
149 
156 
158  protected $mLength = -1;
159 
161  public $mRedirect = null;
162 
165 
167  private $mHasSubpages;
168 
170  private $mPageLanguage = false;
171 
174  private $mDbPageLanguage = false;
175 
177  private $mTitleValue = null;
178 
180  private $mIsBigDeletion = null;
181  // @}
182 
191  private static function getTitleFormatter() {
192  return MediaWikiServices::getInstance()->getTitleFormatter();
193  }
194 
203  private static function getInterwikiLookup() {
204  return MediaWikiServices::getInstance()->getInterwikiLookup();
205  }
206 
210  function __construct() {
211  }
212 
221  public static function newFromDBkey( $key ) {
222  $t = new Title();
223  $t->mDbkeyform = $key;
224 
225  try {
226  $t->secureAndSplit();
227  return $t;
228  } catch ( MalformedTitleException $ex ) {
229  return null;
230  }
231  }
232 
240  public static function newFromTitleValue( TitleValue $titleValue ) {
241  return self::newFromLinkTarget( $titleValue );
242  }
243 
251  public static function newFromLinkTarget( LinkTarget $linkTarget ) {
252  if ( $linkTarget instanceof Title ) {
253  // Special case if it's already a Title object
254  return $linkTarget;
255  }
256  return self::makeTitle(
257  $linkTarget->getNamespace(),
258  $linkTarget->getText(),
259  $linkTarget->getFragment(),
260  $linkTarget->getInterwiki()
261  );
262  }
263 
280  public static function newFromText( $text, $defaultNamespace = NS_MAIN ) {
281  // DWIM: Integers can be passed in here when page titles are used as array keys.
282  if ( $text !== null && !is_string( $text ) && !is_int( $text ) ) {
283  throw new InvalidArgumentException( '$text must be a string.' );
284  }
285  if ( $text === null ) {
286  return null;
287  }
288 
289  try {
290  return self::newFromTextThrow( strval( $text ), $defaultNamespace );
291  } catch ( MalformedTitleException $ex ) {
292  return null;
293  }
294  }
295 
313  public static function newFromTextThrow( $text, $defaultNamespace = NS_MAIN ) {
314  if ( is_object( $text ) ) {
315  throw new MWException( '$text must be a string, given an object' );
316  } elseif ( $text === null ) {
317  // Legacy code relies on MalformedTitleException being thrown in this case
318  // (happens when URL with no title in it is parsed). TODO fix
319  throw new MalformedTitleException( 'title-invalid-empty' );
320  }
321 
323 
324  // Wiki pages often contain multiple links to the same page.
325  // Title normalization and parsing can become expensive on pages with many
326  // links, so we can save a little time by caching them.
327  // In theory these are value objects and won't get changed...
328  if ( $defaultNamespace == NS_MAIN ) {
329  $t = $titleCache->get( $text );
330  if ( $t ) {
331  return $t;
332  }
333  }
334 
335  // Convert things like &eacute; &#257; or &#x3017; into normalized (T16952) text
336  $filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text );
337 
338  $t = new Title();
339  $t->mDbkeyform = strtr( $filteredText, ' ', '_' );
340  $t->mDefaultNamespace = intval( $defaultNamespace );
341 
342  $t->secureAndSplit();
343  if ( $defaultNamespace == NS_MAIN ) {
344  $titleCache->set( $text, $t );
345  }
346  return $t;
347  }
348 
364  public static function newFromURL( $url ) {
365  $t = new Title();
366 
367  # For compatibility with old buggy URLs. "+" is usually not valid in titles,
368  # but some URLs used it as a space replacement and they still come
369  # from some external search tools.
370  if ( strpos( self::legalChars(), '+' ) === false ) {
371  $url = strtr( $url, '+', ' ' );
372  }
373 
374  $t->mDbkeyform = strtr( $url, ' ', '_' );
375 
376  try {
377  $t->secureAndSplit();
378  return $t;
379  } catch ( MalformedTitleException $ex ) {
380  return null;
381  }
382  }
383 
387  private static function getTitleCache() {
388  if ( self::$titleCache == null ) {
389  self::$titleCache = new MapCacheLRU( self::CACHE_MAX );
390  }
391  return self::$titleCache;
392  }
393 
401  protected static function getSelectFields() {
403 
404  $fields = [
405  'page_namespace', 'page_title', 'page_id',
406  'page_len', 'page_is_redirect', 'page_latest',
407  ];
408 
409  if ( $wgContentHandlerUseDB ) {
410  $fields[] = 'page_content_model';
411  }
412 
413  if ( $wgPageLanguageUseDB ) {
414  $fields[] = 'page_lang';
415  }
416 
417  return $fields;
418  }
419 
427  public static function newFromID( $id, $flags = 0 ) {
428  $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
429  $row = $db->selectRow(
430  'page',
431  self::getSelectFields(),
432  [ 'page_id' => $id ],
433  __METHOD__
434  );
435  if ( $row !== false ) {
436  $title = self::newFromRow( $row );
437  } else {
438  $title = null;
439  }
440  return $title;
441  }
442 
449  public static function newFromIDs( $ids ) {
450  if ( !count( $ids ) ) {
451  return [];
452  }
453  $dbr = wfGetDB( DB_REPLICA );
454 
455  $res = $dbr->select(
456  'page',
457  self::getSelectFields(),
458  [ 'page_id' => $ids ],
459  __METHOD__
460  );
461 
462  $titles = [];
463  foreach ( $res as $row ) {
464  $titles[] = self::newFromRow( $row );
465  }
466  return $titles;
467  }
468 
475  public static function newFromRow( $row ) {
476  $t = self::makeTitle( $row->page_namespace, $row->page_title );
477  $t->loadFromRow( $row );
478  return $t;
479  }
480 
487  public function loadFromRow( $row ) {
488  if ( $row ) { // page found
489  if ( isset( $row->page_id ) ) {
490  $this->mArticleID = (int)$row->page_id;
491  }
492  if ( isset( $row->page_len ) ) {
493  $this->mLength = (int)$row->page_len;
494  }
495  if ( isset( $row->page_is_redirect ) ) {
496  $this->mRedirect = (bool)$row->page_is_redirect;
497  }
498  if ( isset( $row->page_latest ) ) {
499  $this->mLatestID = (int)$row->page_latest;
500  }
501  if ( !$this->mForcedContentModel && isset( $row->page_content_model ) ) {
502  $this->mContentModel = strval( $row->page_content_model );
503  } elseif ( !$this->mForcedContentModel ) {
504  $this->mContentModel = false; # initialized lazily in getContentModel()
505  }
506  if ( isset( $row->page_lang ) ) {
507  $this->mDbPageLanguage = (string)$row->page_lang;
508  }
509  if ( isset( $row->page_restrictions ) ) {
510  $this->mOldRestrictions = $row->page_restrictions;
511  }
512  } else { // page not found
513  $this->mArticleID = 0;
514  $this->mLength = 0;
515  $this->mRedirect = false;
516  $this->mLatestID = 0;
517  if ( !$this->mForcedContentModel ) {
518  $this->mContentModel = false; # initialized lazily in getContentModel()
519  }
520  }
521  }
522 
545  public static function makeTitle( $ns, $title, $fragment = '', $interwiki = '' ) {
546  $t = new Title();
547  $t->mInterwiki = $interwiki;
548  $t->mFragment = $fragment;
549  $t->mNamespace = $ns = intval( $ns );
550  $t->mDbkeyform = strtr( $title, ' ', '_' );
551  $t->mArticleID = ( $ns >= 0 ) ? -1 : 0;
552  $t->mUrlform = wfUrlencode( $t->mDbkeyform );
553  $t->mTextform = strtr( $title, '_', ' ' );
554  $t->mContentModel = false; # initialized lazily in getContentModel()
555  return $t;
556  }
557 
573  public static function makeTitleSafe( $ns, $title, $fragment = '', $interwiki = '' ) {
574  // NOTE: ideally, this would just call makeTitle() and then isValid(),
575  // but presently, that means more overhead on a potential performance hotspot.
576 
577  if ( !MWNamespace::exists( $ns ) ) {
578  return null;
579  }
580 
581  $t = new Title();
582  $t->mDbkeyform = self::makeName( $ns, $title, $fragment, $interwiki, true );
583 
584  try {
585  $t->secureAndSplit();
586  return $t;
587  } catch ( MalformedTitleException $ex ) {
588  return null;
589  }
590  }
591 
597  public static function newMainPage() {
598  $title = self::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
599  // Don't give fatal errors if the message is broken
600  if ( !$title ) {
601  $title = self::newFromText( 'Main Page' );
602  }
603  return $title;
604  }
605 
612  public static function nameOf( $id ) {
613  $dbr = wfGetDB( DB_REPLICA );
614 
615  $s = $dbr->selectRow(
616  'page',
617  [ 'page_namespace', 'page_title' ],
618  [ 'page_id' => $id ],
619  __METHOD__
620  );
621  if ( $s === false ) {
622  return null;
623  }
624 
625  $n = self::makeName( $s->page_namespace, $s->page_title );
626  return $n;
627  }
628 
634  public static function legalChars() {
635  global $wgLegalTitleChars;
636  return $wgLegalTitleChars;
637  }
638 
648  public static function convertByteClassToUnicodeClass( $byteClass ) {
649  $length = strlen( $byteClass );
650  // Input token queue
651  $x0 = $x1 = $x2 = '';
652  // Decoded queue
653  $d0 = $d1 = $d2 = '';
654  // Decoded integer codepoints
655  $ord0 = $ord1 = $ord2 = 0;
656  // Re-encoded queue
657  $r0 = $r1 = $r2 = '';
658  // Output
659  $out = '';
660  // Flags
661  $allowUnicode = false;
662  for ( $pos = 0; $pos < $length; $pos++ ) {
663  // Shift the queues down
664  $x2 = $x1;
665  $x1 = $x0;
666  $d2 = $d1;
667  $d1 = $d0;
668  $ord2 = $ord1;
669  $ord1 = $ord0;
670  $r2 = $r1;
671  $r1 = $r0;
672  // Load the current input token and decoded values
673  $inChar = $byteClass[$pos];
674  if ( $inChar == '\\' ) {
675  if ( preg_match( '/x([0-9a-fA-F]{2})/A', $byteClass, $m, 0, $pos + 1 ) ) {
676  $x0 = $inChar . $m[0];
677  $d0 = chr( hexdec( $m[1] ) );
678  $pos += strlen( $m[0] );
679  } elseif ( preg_match( '/[0-7]{3}/A', $byteClass, $m, 0, $pos + 1 ) ) {
680  $x0 = $inChar . $m[0];
681  $d0 = chr( octdec( $m[0] ) );
682  $pos += strlen( $m[0] );
683  } elseif ( $pos + 1 >= $length ) {
684  $x0 = $d0 = '\\';
685  } else {
686  $d0 = $byteClass[$pos + 1];
687  $x0 = $inChar . $d0;
688  $pos += 1;
689  }
690  } else {
691  $x0 = $d0 = $inChar;
692  }
693  $ord0 = ord( $d0 );
694  // Load the current re-encoded value
695  if ( $ord0 < 32 || $ord0 == 0x7f ) {
696  $r0 = sprintf( '\x%02x', $ord0 );
697  } elseif ( $ord0 >= 0x80 ) {
698  // Allow unicode if a single high-bit character appears
699  $r0 = sprintf( '\x%02x', $ord0 );
700  $allowUnicode = true;
701  } elseif ( strpos( '-\\[]^', $d0 ) !== false ) {
702  $r0 = '\\' . $d0;
703  } else {
704  $r0 = $d0;
705  }
706  // Do the output
707  if ( $x0 !== '' && $x1 === '-' && $x2 !== '' ) {
708  // Range
709  if ( $ord2 > $ord0 ) {
710  // Empty range
711  } elseif ( $ord0 >= 0x80 ) {
712  // Unicode range
713  $allowUnicode = true;
714  if ( $ord2 < 0x80 ) {
715  // Keep the non-unicode section of the range
716  $out .= "$r2-\\x7F";
717  }
718  } else {
719  // Normal range
720  $out .= "$r2-$r0";
721  }
722  // Reset state to the initial value
723  $x0 = $x1 = $d0 = $d1 = $r0 = $r1 = '';
724  } elseif ( $ord2 < 0x80 ) {
725  // ASCII character
726  $out .= $r2;
727  }
728  }
729  if ( $ord1 < 0x80 ) {
730  $out .= $r1;
731  }
732  if ( $ord0 < 0x80 ) {
733  $out .= $r0;
734  }
735  if ( $allowUnicode ) {
736  $out .= '\u0080-\uFFFF';
737  }
738  return $out;
739  }
740 
752  public static function makeName( $ns, $title, $fragment = '', $interwiki = '',
753  $canonicalNamespace = false
754  ) {
755  if ( $canonicalNamespace ) {
756  $namespace = MWNamespace::getCanonicalName( $ns );
757  } else {
758  $namespace = MediaWikiServices::getInstance()->getContentLanguage()->getNsText( $ns );
759  }
760  $name = $namespace == '' ? $title : "$namespace:$title";
761  if ( strval( $interwiki ) != '' ) {
762  $name = "$interwiki:$name";
763  }
764  if ( strval( $fragment ) != '' ) {
765  $name .= '#' . $fragment;
766  }
767  return $name;
768  }
769 
778  static function escapeFragmentForURL( $fragment ) {
779  wfDeprecated( __METHOD__, '1.30' );
780  # Note that we don't urlencode the fragment. urlencoded Unicode
781  # fragments appear not to work in IE (at least up to 7) or in at least
782  # one version of Opera 9.x. The W3C validator, for one, doesn't seem
783  # to care if they aren't encoded.
784  return Sanitizer::escapeId( $fragment, 'noninitial' );
785  }
786 
795  public static function compare( LinkTarget $a, LinkTarget $b ) {
796  return $a->getNamespace() <=> $b->getNamespace()
797  ?: strcmp( $a->getText(), $b->getText() );
798  }
799 
814  public function isValid() {
815  if ( !MWNamespace::exists( $this->mNamespace ) ) {
816  return false;
817  }
818 
819  try {
820  $parser = MediaWikiServices::getInstance()->getTitleParser();
821  $parser->parseTitle( $this->mDbkeyform, $this->mNamespace );
822  return true;
823  } catch ( MalformedTitleException $ex ) {
824  return false;
825  }
826  }
827 
835  public function isLocal() {
836  if ( $this->isExternal() ) {
837  $iw = self::getInterwikiLookup()->fetch( $this->mInterwiki );
838  if ( $iw ) {
839  return $iw->isLocal();
840  }
841  }
842  return true;
843  }
844 
850  public function isExternal() {
851  return $this->mInterwiki !== '';
852  }
853 
861  public function getInterwiki() {
862  return $this->mInterwiki;
863  }
864 
870  public function wasLocalInterwiki() {
871  return $this->mLocalInterwiki;
872  }
873 
880  public function isTrans() {
881  if ( !$this->isExternal() ) {
882  return false;
883  }
884 
885  return self::getInterwikiLookup()->fetch( $this->mInterwiki )->isTranscludable();
886  }
887 
893  public function getTransWikiID() {
894  if ( !$this->isExternal() ) {
895  return false;
896  }
897 
898  return self::getInterwikiLookup()->fetch( $this->mInterwiki )->getWikiID();
899  }
900 
910  public function getTitleValue() {
911  if ( $this->mTitleValue === null ) {
912  try {
913  $this->mTitleValue = new TitleValue(
914  $this->mNamespace,
915  $this->mDbkeyform,
916  $this->mFragment,
917  $this->mInterwiki
918  );
919  } catch ( InvalidArgumentException $ex ) {
920  wfDebug( __METHOD__ . ': Can\'t create a TitleValue for [[' .
921  $this->getPrefixedText() . ']]: ' . $ex->getMessage() . "\n" );
922  }
923  }
924 
925  return $this->mTitleValue;
926  }
927 
933  public function getText() {
934  return $this->mTextform;
935  }
936 
942  public function getPartialURL() {
943  return $this->mUrlform;
944  }
945 
951  public function getDBkey() {
952  return $this->mDbkeyform;
953  }
954 
960  function getUserCaseDBKey() {
961  if ( !is_null( $this->mUserCaseDBKey ) ) {
962  return $this->mUserCaseDBKey;
963  } else {
964  // If created via makeTitle(), $this->mUserCaseDBKey is not set.
965  return $this->mDbkeyform;
966  }
967  }
968 
974  public function getNamespace() {
975  return $this->mNamespace;
976  }
977 
984  public function getContentModel( $flags = 0 ) {
985  if ( !$this->mForcedContentModel
986  && ( !$this->mContentModel || $flags === self::GAID_FOR_UPDATE )
987  && $this->getArticleID( $flags )
988  ) {
989  $linkCache = MediaWikiServices::getInstance()->getLinkCache();
990  $linkCache->addLinkObj( $this ); # in case we already had an article ID
991  $this->mContentModel = $linkCache->getGoodLinkFieldObj( $this, 'model' );
992  }
993 
994  if ( !$this->mContentModel ) {
995  $this->mContentModel = ContentHandler::getDefaultModelFor( $this );
996  }
997 
998  return $this->mContentModel;
999  }
1000 
1007  public function hasContentModel( $id ) {
1008  return $this->getContentModel() == $id;
1009  }
1010 
1022  public function setContentModel( $model ) {
1023  $this->mContentModel = $model;
1024  $this->mForcedContentModel = true;
1025  }
1026 
1032  public function getNsText() {
1033  if ( $this->isExternal() ) {
1034  // This probably shouldn't even happen, except for interwiki transclusion.
1035  // If possible, use the canonical name for the foreign namespace.
1036  $nsText = MWNamespace::getCanonicalName( $this->mNamespace );
1037  if ( $nsText !== false ) {
1038  return $nsText;
1039  }
1040  }
1041 
1042  try {
1043  $formatter = self::getTitleFormatter();
1044  return $formatter->getNamespaceName( $this->mNamespace, $this->mDbkeyform );
1045  } catch ( InvalidArgumentException $ex ) {
1046  wfDebug( __METHOD__ . ': ' . $ex->getMessage() . "\n" );
1047  return false;
1048  }
1049  }
1050 
1056  public function getSubjectNsText() {
1057  return MediaWikiServices::getInstance()->getContentLanguage()->
1058  getNsText( MWNamespace::getSubject( $this->mNamespace ) );
1059  }
1060 
1066  public function getTalkNsText() {
1067  return MediaWikiServices::getInstance()->getContentLanguage()->
1068  getNsText( MWNamespace::getTalk( $this->mNamespace ) );
1069  }
1070 
1078  public function canTalk() {
1079  return $this->canHaveTalkPage();
1080  }
1081 
1090  public function canHaveTalkPage() {
1091  return MWNamespace::hasTalkNamespace( $this->mNamespace );
1092  }
1093 
1099  public function canExist() {
1100  return $this->mNamespace >= NS_MAIN;
1101  }
1102 
1108  public function isWatchable() {
1109  return !$this->isExternal() && MWNamespace::isWatchable( $this->mNamespace );
1110  }
1111 
1117  public function isSpecialPage() {
1118  return $this->mNamespace == NS_SPECIAL;
1119  }
1120 
1127  public function isSpecial( $name ) {
1128  if ( $this->isSpecialPage() ) {
1129  list( $thisName, /* $subpage */ ) =
1130  MediaWikiServices::getInstance()->getSpecialPageFactory()->
1131  resolveAlias( $this->mDbkeyform );
1132  if ( $name == $thisName ) {
1133  return true;
1134  }
1135  }
1136  return false;
1137  }
1138 
1145  public function fixSpecialName() {
1146  if ( $this->isSpecialPage() ) {
1147  $spFactory = MediaWikiServices::getInstance()->getSpecialPageFactory();
1148  list( $canonicalName, $par ) = $spFactory->resolveAlias( $this->mDbkeyform );
1149  if ( $canonicalName ) {
1150  $localName = $spFactory->getLocalNameFor( $canonicalName, $par );
1151  if ( $localName != $this->mDbkeyform ) {
1152  return self::makeTitle( NS_SPECIAL, $localName );
1153  }
1154  }
1155  }
1156  return $this;
1157  }
1158 
1169  public function inNamespace( $ns ) {
1170  return MWNamespace::equals( $this->mNamespace, $ns );
1171  }
1172 
1180  public function inNamespaces( /* ... */ ) {
1181  $namespaces = func_get_args();
1182  if ( count( $namespaces ) > 0 && is_array( $namespaces[0] ) ) {
1183  $namespaces = $namespaces[0];
1184  }
1185 
1186  foreach ( $namespaces as $ns ) {
1187  if ( $this->inNamespace( $ns ) ) {
1188  return true;
1189  }
1190  }
1191 
1192  return false;
1193  }
1194 
1208  public function hasSubjectNamespace( $ns ) {
1209  return MWNamespace::subjectEquals( $this->mNamespace, $ns );
1210  }
1211 
1219  public function isContentPage() {
1220  return MWNamespace::isContent( $this->mNamespace );
1221  }
1222 
1229  public function isMovable() {
1230  if ( !MWNamespace::isMovable( $this->mNamespace ) || $this->isExternal() ) {
1231  // Interwiki title or immovable namespace. Hooks don't get to override here
1232  return false;
1233  }
1234 
1235  $result = true;
1236  Hooks::run( 'TitleIsMovable', [ $this, &$result ] );
1237  return $result;
1238  }
1239 
1250  public function isMainPage() {
1251  return $this->equals( self::newMainPage() );
1252  }
1253 
1259  public function isSubpage() {
1260  return MWNamespace::hasSubpages( $this->mNamespace )
1261  ? strpos( $this->getText(), '/' ) !== false
1262  : false;
1263  }
1264 
1270  public function isConversionTable() {
1271  // @todo ConversionTable should become a separate content model.
1272 
1273  return $this->mNamespace == NS_MEDIAWIKI &&
1274  strpos( $this->getText(), 'Conversiontable/' ) === 0;
1275  }
1276 
1282  public function isWikitextPage() {
1283  return $this->hasContentModel( CONTENT_MODEL_WIKITEXT );
1284  }
1285 
1300  public function isSiteConfigPage() {
1301  return (
1302  $this->isSiteCssConfigPage()
1303  || $this->isSiteJsonConfigPage()
1304  || $this->isSiteJsConfigPage()
1305  );
1306  }
1307 
1312  public function isCssOrJsPage() {
1313  wfDeprecated( __METHOD__, '1.31' );
1314  return ( NS_MEDIAWIKI == $this->mNamespace
1315  && ( $this->hasContentModel( CONTENT_MODEL_CSS )
1316  || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
1317  }
1318 
1325  public function isUserConfigPage() {
1326  return (
1327  $this->isUserCssConfigPage()
1328  || $this->isUserJsonConfigPage()
1329  || $this->isUserJsConfigPage()
1330  );
1331  }
1332 
1337  public function isCssJsSubpage() {
1338  wfDeprecated( __METHOD__, '1.31' );
1339  return ( NS_USER == $this->mNamespace && $this->isSubpage()
1340  && ( $this->hasContentModel( CONTENT_MODEL_CSS )
1341  || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
1342  }
1343 
1350  public function getSkinFromConfigSubpage() {
1351  $subpage = explode( '/', $this->mTextform );
1352  $subpage = $subpage[count( $subpage ) - 1];
1353  $lastdot = strrpos( $subpage, '.' );
1354  if ( $lastdot === false ) {
1355  return $subpage; # Never happens: only called for names ending in '.css'/'.json'/'.js'
1356  }
1357  return substr( $subpage, 0, $lastdot );
1358  }
1359 
1364  public function getSkinFromCssJsSubpage() {
1365  wfDeprecated( __METHOD__, '1.31' );
1366  return $this->getSkinFromConfigSubpage();
1367  }
1368 
1375  public function isUserCssConfigPage() {
1376  return (
1377  NS_USER == $this->mNamespace
1378  && $this->isSubpage()
1379  && $this->hasContentModel( CONTENT_MODEL_CSS )
1380  );
1381  }
1382 
1387  public function isCssSubpage() {
1388  wfDeprecated( __METHOD__, '1.31' );
1389  return $this->isUserCssConfigPage();
1390  }
1391 
1398  public function isUserJsonConfigPage() {
1399  return (
1400  NS_USER == $this->mNamespace
1401  && $this->isSubpage()
1402  && $this->hasContentModel( CONTENT_MODEL_JSON )
1403  );
1404  }
1405 
1412  public function isUserJsConfigPage() {
1413  return (
1414  NS_USER == $this->mNamespace
1415  && $this->isSubpage()
1417  );
1418  }
1419 
1424  public function isJsSubpage() {
1425  wfDeprecated( __METHOD__, '1.31' );
1426  return $this->isUserJsConfigPage();
1427  }
1428 
1435  public function isSiteCssConfigPage() {
1436  return (
1437  NS_MEDIAWIKI == $this->mNamespace
1438  && (
1440  // paranoia - a MediaWiki: namespace page with mismatching extension and content
1441  // model is probably by mistake and might get handled incorrectly (see e.g. T112937)
1442  || substr( $this->mDbkeyform, -4 ) === '.css'
1443  )
1444  );
1445  }
1446 
1453  public function isSiteJsonConfigPage() {
1454  return (
1455  NS_MEDIAWIKI == $this->mNamespace
1456  && (
1458  // paranoia - a MediaWiki: namespace page with mismatching extension and content
1459  // model is probably by mistake and might get handled incorrectly (see e.g. T112937)
1460  || substr( $this->mDbkeyform, -5 ) === '.json'
1461  )
1462  );
1463  }
1464 
1471  public function isSiteJsConfigPage() {
1472  return (
1473  NS_MEDIAWIKI == $this->mNamespace
1474  && (
1476  // paranoia - a MediaWiki: namespace page with mismatching extension and content
1477  // model is probably by mistake and might get handled incorrectly (see e.g. T112937)
1478  || substr( $this->mDbkeyform, -3 ) === '.js'
1479  )
1480  );
1481  }
1482 
1489  public function isRawHtmlMessage() {
1490  global $wgRawHtmlMessages;
1491 
1492  if ( !$this->inNamespace( NS_MEDIAWIKI ) ) {
1493  return false;
1494  }
1495  $message = lcfirst( $this->getRootTitle()->getDBkey() );
1496  return in_array( $message, $wgRawHtmlMessages, true );
1497  }
1498 
1504  public function isTalkPage() {
1505  return MWNamespace::isTalk( $this->mNamespace );
1506  }
1507 
1513  public function getTalkPage() {
1514  return self::makeTitle( MWNamespace::getTalk( $this->mNamespace ), $this->mDbkeyform );
1515  }
1516 
1526  public function getTalkPageIfDefined() {
1527  if ( !$this->canHaveTalkPage() ) {
1528  return null;
1529  }
1530 
1531  return $this->getTalkPage();
1532  }
1533 
1540  public function getSubjectPage() {
1541  // Is this the same title?
1542  $subjectNS = MWNamespace::getSubject( $this->mNamespace );
1543  if ( $this->mNamespace == $subjectNS ) {
1544  return $this;
1545  }
1546  return self::makeTitle( $subjectNS, $this->mDbkeyform );
1547  }
1548 
1557  public function getOtherPage() {
1558  if ( $this->isSpecialPage() ) {
1559  throw new MWException( 'Special pages cannot have other pages' );
1560  }
1561  if ( $this->isTalkPage() ) {
1562  return $this->getSubjectPage();
1563  } else {
1564  if ( !$this->canHaveTalkPage() ) {
1565  throw new MWException( "{$this->getPrefixedText()} does not have an other page" );
1566  }
1567  return $this->getTalkPage();
1568  }
1569  }
1570 
1576  public function getDefaultNamespace() {
1577  return $this->mDefaultNamespace;
1578  }
1579 
1587  public function getFragment() {
1588  return $this->mFragment;
1589  }
1590 
1597  public function hasFragment() {
1598  return $this->mFragment !== '';
1599  }
1600 
1606  public function getFragmentForURL() {
1607  if ( !$this->hasFragment() ) {
1608  return '';
1609  } elseif ( $this->isExternal()
1610  && !self::getInterwikiLookup()->fetch( $this->mInterwiki )->isLocal()
1611  ) {
1612  return '#' . Sanitizer::escapeIdForExternalInterwiki( $this->mFragment );
1613  }
1614  return '#' . Sanitizer::escapeIdForLink( $this->mFragment );
1615  }
1616 
1629  public function setFragment( $fragment ) {
1630  $this->mFragment = strtr( substr( $fragment, 1 ), '_', ' ' );
1631  }
1632 
1640  public function createFragmentTarget( $fragment ) {
1641  return self::makeTitle(
1642  $this->mNamespace,
1643  $this->getText(),
1644  $fragment,
1645  $this->mInterwiki
1646  );
1647  }
1648 
1656  private function prefix( $name ) {
1657  $p = '';
1658  if ( $this->isExternal() ) {
1659  $p = $this->mInterwiki . ':';
1660  }
1661 
1662  if ( 0 != $this->mNamespace ) {
1663  $nsText = $this->getNsText();
1664 
1665  if ( $nsText === false ) {
1666  // See T165149. Awkward, but better than erroneously linking to the main namespace.
1667  $nsText = MediaWikiServices::getInstance()->getContentLanguage()->
1668  getNsText( NS_SPECIAL ) . ":Badtitle/NS{$this->mNamespace}";
1669  }
1670 
1671  $p .= $nsText . ':';
1672  }
1673  return $p . $name;
1674  }
1675 
1682  public function getPrefixedDBkey() {
1683  $s = $this->prefix( $this->mDbkeyform );
1684  $s = strtr( $s, ' ', '_' );
1685  return $s;
1686  }
1687 
1694  public function getPrefixedText() {
1695  if ( $this->prefixedText === null ) {
1696  $s = $this->prefix( $this->mTextform );
1697  $s = strtr( $s, '_', ' ' );
1698  $this->prefixedText = $s;
1699  }
1700  return $this->prefixedText;
1701  }
1702 
1708  public function __toString() {
1709  return $this->getPrefixedText();
1710  }
1711 
1718  public function getFullText() {
1719  $text = $this->getPrefixedText();
1720  if ( $this->hasFragment() ) {
1721  $text .= '#' . $this->mFragment;
1722  }
1723  return $text;
1724  }
1725 
1738  public function getRootText() {
1739  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1740  return $this->getText();
1741  }
1742 
1743  return strtok( $this->getText(), '/' );
1744  }
1745 
1758  public function getRootTitle() {
1759  return self::makeTitle( $this->mNamespace, $this->getRootText() );
1760  }
1761 
1773  public function getBaseText() {
1774  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1775  return $this->getText();
1776  }
1777 
1778  $parts = explode( '/', $this->getText() );
1779  # Don't discard the real title if there's no subpage involved
1780  if ( count( $parts ) > 1 ) {
1781  unset( $parts[count( $parts ) - 1] );
1782  }
1783  return implode( '/', $parts );
1784  }
1785 
1798  public function getBaseTitle() {
1799  return self::makeTitle( $this->mNamespace, $this->getBaseText() );
1800  }
1801 
1813  public function getSubpageText() {
1814  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1815  return $this->mTextform;
1816  }
1817  $parts = explode( '/', $this->mTextform );
1818  return $parts[count( $parts ) - 1];
1819  }
1820 
1834  public function getSubpage( $text ) {
1835  return self::makeTitleSafe( $this->mNamespace, $this->getText() . '/' . $text );
1836  }
1837 
1843  public function getSubpageUrlForm() {
1844  $text = $this->getSubpageText();
1845  $text = wfUrlencode( strtr( $text, ' ', '_' ) );
1846  return $text;
1847  }
1848 
1854  public function getPrefixedURL() {
1855  $s = $this->prefix( $this->mDbkeyform );
1856  $s = wfUrlencode( strtr( $s, ' ', '_' ) );
1857  return $s;
1858  }
1859 
1873  private static function fixUrlQueryArgs( $query, $query2 = false ) {
1874  if ( $query2 !== false ) {
1875  wfDeprecated( "Title::get{Canonical,Full,Link,Local,Internal}URL " .
1876  "method called with a second parameter is deprecated. Add your " .
1877  "parameter to an array passed as the first parameter.", "1.19" );
1878  }
1879  if ( is_array( $query ) ) {
1880  $query = wfArrayToCgi( $query );
1881  }
1882  if ( $query2 ) {
1883  if ( is_string( $query2 ) ) {
1884  // $query2 is a string, we will consider this to be
1885  // a deprecated $variant argument and add it to the query
1886  $query2 = wfArrayToCgi( [ 'variant' => $query2 ] );
1887  } else {
1888  $query2 = wfArrayToCgi( $query2 );
1889  }
1890  // If we have $query content add a & to it first
1891  if ( $query ) {
1892  $query .= '&';
1893  }
1894  // Now append the queries together
1895  $query .= $query2;
1896  }
1897  return $query;
1898  }
1899 
1911  public function getFullURL( $query = '', $query2 = false, $proto = PROTO_RELATIVE ) {
1912  $query = self::fixUrlQueryArgs( $query, $query2 );
1913 
1914  # Hand off all the decisions on urls to getLocalURL
1915  $url = $this->getLocalURL( $query );
1916 
1917  # Expand the url to make it a full url. Note that getLocalURL has the
1918  # potential to output full urls for a variety of reasons, so we use
1919  # wfExpandUrl instead of simply prepending $wgServer
1920  $url = wfExpandUrl( $url, $proto );
1921 
1922  # Finally, add the fragment.
1923  $url .= $this->getFragmentForURL();
1924  // Avoid PHP 7.1 warning from passing $this by reference
1925  $titleRef = $this;
1926  Hooks::run( 'GetFullURL', [ &$titleRef, &$url, $query ] );
1927  return $url;
1928  }
1929 
1946  public function getFullUrlForRedirect( $query = '', $proto = PROTO_CURRENT ) {
1947  $target = $this;
1948  if ( $this->isExternal() ) {
1949  $target = SpecialPage::getTitleFor(
1950  'GoToInterwiki',
1951  $this->getPrefixedDBkey()
1952  );
1953  }
1954  return $target->getFullURL( $query, false, $proto );
1955  }
1956 
1980  public function getLocalURL( $query = '', $query2 = false ) {
1982 
1983  $query = self::fixUrlQueryArgs( $query, $query2 );
1984 
1985  $interwiki = self::getInterwikiLookup()->fetch( $this->mInterwiki );
1986  if ( $interwiki ) {
1987  $namespace = $this->getNsText();
1988  if ( $namespace != '' ) {
1989  # Can this actually happen? Interwikis shouldn't be parsed.
1990  # Yes! It can in interwiki transclusion. But... it probably shouldn't.
1991  $namespace .= ':';
1992  }
1993  $url = $interwiki->getURL( $namespace . $this->mDbkeyform );
1994  $url = wfAppendQuery( $url, $query );
1995  } else {
1996  $dbkey = wfUrlencode( $this->getPrefixedDBkey() );
1997  if ( $query == '' ) {
1998  $url = str_replace( '$1', $dbkey, $wgArticlePath );
1999  // Avoid PHP 7.1 warning from passing $this by reference
2000  $titleRef = $this;
2001  Hooks::run( 'GetLocalURL::Article', [ &$titleRef, &$url ] );
2002  } else {
2004  $url = false;
2005  $matches = [];
2006 
2007  if ( !empty( $wgActionPaths )
2008  && preg_match( '/^(.*&|)action=([^&]*)(&(.*)|)$/', $query, $matches )
2009  ) {
2010  $action = urldecode( $matches[2] );
2011  if ( isset( $wgActionPaths[$action] ) ) {
2012  $query = $matches[1];
2013  if ( isset( $matches[4] ) ) {
2014  $query .= $matches[4];
2015  }
2016  $url = str_replace( '$1', $dbkey, $wgActionPaths[$action] );
2017  if ( $query != '' ) {
2018  $url = wfAppendQuery( $url, $query );
2019  }
2020  }
2021  }
2022 
2023  if ( $url === false
2025  && preg_match( '/^variant=([^&]*)$/', $query, $matches )
2026  && $this->getPageLanguage()->equals(
2027  MediaWikiServices::getInstance()->getContentLanguage() )
2028  && $this->getPageLanguage()->hasVariants()
2029  ) {
2030  $variant = urldecode( $matches[1] );
2031  if ( $this->getPageLanguage()->hasVariant( $variant ) ) {
2032  // Only do the variant replacement if the given variant is a valid
2033  // variant for the page's language.
2034  $url = str_replace( '$2', urlencode( $variant ), $wgVariantArticlePath );
2035  $url = str_replace( '$1', $dbkey, $url );
2036  }
2037  }
2038 
2039  if ( $url === false ) {
2040  if ( $query == '-' ) {
2041  $query = '';
2042  }
2043  $url = "{$wgScript}?title={$dbkey}&{$query}";
2044  }
2045  }
2046  // Avoid PHP 7.1 warning from passing $this by reference
2047  $titleRef = $this;
2048  Hooks::run( 'GetLocalURL::Internal', [ &$titleRef, &$url, $query ] );
2049 
2050  // @todo FIXME: This causes breakage in various places when we
2051  // actually expected a local URL and end up with dupe prefixes.
2052  if ( $wgRequest->getVal( 'action' ) == 'render' ) {
2053  $url = $wgServer . $url;
2054  }
2055  }
2056  // Avoid PHP 7.1 warning from passing $this by reference
2057  $titleRef = $this;
2058  Hooks::run( 'GetLocalURL', [ &$titleRef, &$url, $query ] );
2059  return $url;
2060  }
2061 
2079  public function getLinkURL( $query = '', $query2 = false, $proto = false ) {
2080  if ( $this->isExternal() || $proto !== false ) {
2081  $ret = $this->getFullURL( $query, $query2, $proto );
2082  } elseif ( $this->getPrefixedText() === '' && $this->hasFragment() ) {
2083  $ret = $this->getFragmentForURL();
2084  } else {
2085  $ret = $this->getLocalURL( $query, $query2 ) . $this->getFragmentForURL();
2086  }
2087  return $ret;
2088  }
2089 
2104  public function getInternalURL( $query = '', $query2 = false ) {
2105  global $wgInternalServer, $wgServer;
2106  $query = self::fixUrlQueryArgs( $query, $query2 );
2107  $server = $wgInternalServer !== false ? $wgInternalServer : $wgServer;
2108  $url = wfExpandUrl( $server . $this->getLocalURL( $query ), PROTO_HTTP );
2109  // Avoid PHP 7.1 warning from passing $this by reference
2110  $titleRef = $this;
2111  Hooks::run( 'GetInternalURL', [ &$titleRef, &$url, $query ] );
2112  return $url;
2113  }
2114 
2128  public function getCanonicalURL( $query = '', $query2 = false ) {
2129  $query = self::fixUrlQueryArgs( $query, $query2 );
2130  $url = wfExpandUrl( $this->getLocalURL( $query ) . $this->getFragmentForURL(), PROTO_CANONICAL );
2131  // Avoid PHP 7.1 warning from passing $this by reference
2132  $titleRef = $this;
2133  Hooks::run( 'GetCanonicalURL', [ &$titleRef, &$url, $query ] );
2134  return $url;
2135  }
2136 
2142  public function getEditURL() {
2143  if ( $this->isExternal() ) {
2144  return '';
2145  }
2146  $s = $this->getLocalURL( 'action=edit' );
2147 
2148  return $s;
2149  }
2150 
2165  public function quickUserCan( $action, $user = null ) {
2166  return $this->userCan( $action, $user, false );
2167  }
2168 
2178  public function userCan( $action, $user = null, $rigor = 'secure' ) {
2179  if ( !$user instanceof User ) {
2180  global $wgUser;
2181  $user = $wgUser;
2182  }
2183 
2184  return !count( $this->getUserPermissionsErrorsInternal( $action, $user, $rigor, true ) );
2185  }
2186 
2202  public function getUserPermissionsErrors(
2203  $action, $user, $rigor = 'secure', $ignoreErrors = []
2204  ) {
2205  $errors = $this->getUserPermissionsErrorsInternal( $action, $user, $rigor );
2206 
2207  // Remove the errors being ignored.
2208  foreach ( $errors as $index => $error ) {
2209  $errKey = is_array( $error ) ? $error[0] : $error;
2210 
2211  if ( in_array( $errKey, $ignoreErrors ) ) {
2212  unset( $errors[$index] );
2213  }
2214  if ( $errKey instanceof MessageSpecifier && in_array( $errKey->getKey(), $ignoreErrors ) ) {
2215  unset( $errors[$index] );
2216  }
2217  }
2218 
2219  return $errors;
2220  }
2221 
2233  private function checkQuickPermissions( $action, $user, $errors, $rigor, $short ) {
2234  if ( !Hooks::run( 'TitleQuickPermissions',
2235  [ $this, $user, $action, &$errors, ( $rigor !== 'quick' ), $short ] )
2236  ) {
2237  return $errors;
2238  }
2239 
2240  if ( $action == 'create' ) {
2241  if (
2242  ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
2243  ( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) )
2244  ) {
2245  $errors[] = $user->isAnon() ? [ 'nocreatetext' ] : [ 'nocreate-loggedin' ];
2246  }
2247  } elseif ( $action == 'move' ) {
2248  if ( !$user->isAllowed( 'move-rootuserpages' )
2249  && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
2250  // Show user page-specific message only if the user can move other pages
2251  $errors[] = [ 'cant-move-user-page' ];
2252  }
2253 
2254  // Check if user is allowed to move files if it's a file
2255  if ( $this->mNamespace == NS_FILE && !$user->isAllowed( 'movefile' ) ) {
2256  $errors[] = [ 'movenotallowedfile' ];
2257  }
2258 
2259  // Check if user is allowed to move category pages if it's a category page
2260  if ( $this->mNamespace == NS_CATEGORY && !$user->isAllowed( 'move-categorypages' ) ) {
2261  $errors[] = [ 'cant-move-category-page' ];
2262  }
2263 
2264  if ( !$user->isAllowed( 'move' ) ) {
2265  // User can't move anything
2266  $userCanMove = User::groupHasPermission( 'user', 'move' );
2267  $autoconfirmedCanMove = User::groupHasPermission( 'autoconfirmed', 'move' );
2268  if ( $user->isAnon() && ( $userCanMove || $autoconfirmedCanMove ) ) {
2269  // custom message if logged-in users without any special rights can move
2270  $errors[] = [ 'movenologintext' ];
2271  } else {
2272  $errors[] = [ 'movenotallowed' ];
2273  }
2274  }
2275  } elseif ( $action == 'move-target' ) {
2276  if ( !$user->isAllowed( 'move' ) ) {
2277  // User can't move anything
2278  $errors[] = [ 'movenotallowed' ];
2279  } elseif ( !$user->isAllowed( 'move-rootuserpages' )
2280  && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
2281  // Show user page-specific message only if the user can move other pages
2282  $errors[] = [ 'cant-move-to-user-page' ];
2283  } elseif ( !$user->isAllowed( 'move-categorypages' )
2284  && $this->mNamespace == NS_CATEGORY ) {
2285  // Show category page-specific message only if the user can move other pages
2286  $errors[] = [ 'cant-move-to-category-page' ];
2287  }
2288  } elseif ( !$user->isAllowed( $action ) ) {
2289  $errors[] = $this->missingPermissionError( $action, $short );
2290  }
2291 
2292  return $errors;
2293  }
2294 
2303  private function resultToError( $errors, $result ) {
2304  if ( is_array( $result ) && count( $result ) && !is_array( $result[0] ) ) {
2305  // A single array representing an error
2306  $errors[] = $result;
2307  } elseif ( is_array( $result ) && is_array( $result[0] ) ) {
2308  // A nested array representing multiple errors
2309  $errors = array_merge( $errors, $result );
2310  } elseif ( $result !== '' && is_string( $result ) ) {
2311  // A string representing a message-id
2312  $errors[] = [ $result ];
2313  } elseif ( $result instanceof MessageSpecifier ) {
2314  // A message specifier representing an error
2315  $errors[] = [ $result ];
2316  } elseif ( $result === false ) {
2317  // a generic "We don't want them to do that"
2318  $errors[] = [ 'badaccess-group0' ];
2319  }
2320  return $errors;
2321  }
2322 
2334  private function checkPermissionHooks( $action, $user, $errors, $rigor, $short ) {
2335  // Use getUserPermissionsErrors instead
2336  $result = '';
2337  // Avoid PHP 7.1 warning from passing $this by reference
2338  $titleRef = $this;
2339  if ( !Hooks::run( 'userCan', [ &$titleRef, &$user, $action, &$result ] ) ) {
2340  return $result ? [] : [ [ 'badaccess-group0' ] ];
2341  }
2342  // Check getUserPermissionsErrors hook
2343  // Avoid PHP 7.1 warning from passing $this by reference
2344  $titleRef = $this;
2345  if ( !Hooks::run( 'getUserPermissionsErrors', [ &$titleRef, &$user, $action, &$result ] ) ) {
2346  $errors = $this->resultToError( $errors, $result );
2347  }
2348  // Check getUserPermissionsErrorsExpensive hook
2349  if (
2350  $rigor !== 'quick'
2351  && !( $short && count( $errors ) > 0 )
2352  && !Hooks::run( 'getUserPermissionsErrorsExpensive', [ &$titleRef, &$user, $action, &$result ] )
2353  ) {
2354  $errors = $this->resultToError( $errors, $result );
2355  }
2356 
2357  return $errors;
2358  }
2359 
2371  private function checkSpecialsAndNSPermissions( $action, $user, $errors, $rigor, $short ) {
2372  # Only 'createaccount' can be performed on special pages,
2373  # which don't actually exist in the DB.
2374  if ( $this->isSpecialPage() && $action !== 'createaccount' ) {
2375  $errors[] = [ 'ns-specialprotected' ];
2376  }
2377 
2378  # Check $wgNamespaceProtection for restricted namespaces
2379  if ( $this->isNamespaceProtected( $user ) ) {
2380  $ns = $this->mNamespace == NS_MAIN ?
2381  wfMessage( 'nstab-main' )->text() : $this->getNsText();
2382  $errors[] = $this->mNamespace == NS_MEDIAWIKI ?
2383  [ 'protectedinterface', $action ] : [ 'namespaceprotected', $ns, $action ];
2384  }
2385 
2386  return $errors;
2387  }
2388 
2400  private function checkSiteConfigPermissions( $action, $user, $errors, $rigor, $short ) {
2401  if ( $action != 'patrol' ) {
2402  $error = null;
2403  // Sitewide CSS/JSON/JS changes, like all NS_MEDIAWIKI changes, also require the
2404  // editinterface right. That's implemented as a restriction so no check needed here.
2405  if ( $this->isSiteCssConfigPage() && !$user->isAllowed( 'editsitecss' ) ) {
2406  $error = [ 'sitecssprotected', $action ];
2407  } elseif ( $this->isSiteJsonConfigPage() && !$user->isAllowed( 'editsitejson' ) ) {
2408  $error = [ 'sitejsonprotected', $action ];
2409  } elseif ( $this->isSiteJsConfigPage() && !$user->isAllowed( 'editsitejs' ) ) {
2410  $error = [ 'sitejsprotected', $action ];
2411  } elseif ( $this->isRawHtmlMessage() ) {
2412  // Raw HTML can be used to deploy CSS or JS so require rights for both.
2413  if ( !$user->isAllowed( 'editsitejs' ) ) {
2414  $error = [ 'sitejsprotected', $action ];
2415  } elseif ( !$user->isAllowed( 'editsitecss' ) ) {
2416  $error = [ 'sitecssprotected', $action ];
2417  }
2418  }
2419 
2420  if ( $error ) {
2421  if ( $user->isAllowed( 'editinterface' ) ) {
2422  // Most users / site admins will probably find out about the new, more restrictive
2423  // permissions by failing to edit something. Give them more info.
2424  // TODO remove this a few release cycles after 1.32
2425  $error = [ 'interfaceadmin-info', wfMessage( $error[0], $error[1] ) ];
2426  }
2427  $errors[] = $error;
2428  }
2429  }
2430 
2431  return $errors;
2432  }
2433 
2445  private function checkUserConfigPermissions( $action, $user, $errors, $rigor, $short ) {
2446  # Protect css/json/js subpages of user pages
2447  # XXX: this might be better using restrictions
2448 
2449  if ( $action === 'patrol' ) {
2450  return $errors;
2451  }
2452 
2453  if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
2454  // Users need editmyuser* to edit their own CSS/JSON/JS subpages.
2455  if (
2456  $this->isUserCssConfigPage()
2457  && !$user->isAllowedAny( 'editmyusercss', 'editusercss' )
2458  ) {
2459  $errors[] = [ 'mycustomcssprotected', $action ];
2460  } elseif (
2461  $this->isUserJsonConfigPage()
2462  && !$user->isAllowedAny( 'editmyuserjson', 'edituserjson' )
2463  ) {
2464  $errors[] = [ 'mycustomjsonprotected', $action ];
2465  } elseif (
2466  $this->isUserJsConfigPage()
2467  && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' )
2468  ) {
2469  $errors[] = [ 'mycustomjsprotected', $action ];
2470  }
2471  } else {
2472  // Users need editmyuser* to edit their own CSS/JSON/JS subpages, except for
2473  // deletion/suppression which cannot be used for attacks and we want to avoid the
2474  // situation where an unprivileged user can post abusive content on their subpages
2475  // and only very highly privileged users could remove it.
2476  if ( !in_array( $action, [ 'delete', 'deleterevision', 'suppressrevision' ], true ) ) {
2477  if (
2478  $this->isUserCssConfigPage()
2479  && !$user->isAllowed( 'editusercss' )
2480  ) {
2481  $errors[] = [ 'customcssprotected', $action ];
2482  } elseif (
2483  $this->isUserJsonConfigPage()
2484  && !$user->isAllowed( 'edituserjson' )
2485  ) {
2486  $errors[] = [ 'customjsonprotected', $action ];
2487  } elseif (
2488  $this->isUserJsConfigPage()
2489  && !$user->isAllowed( 'edituserjs' )
2490  ) {
2491  $errors[] = [ 'customjsprotected', $action ];
2492  }
2493  }
2494  }
2495 
2496  return $errors;
2497  }
2498 
2512  private function checkPageRestrictions( $action, $user, $errors, $rigor, $short ) {
2513  foreach ( $this->getRestrictions( $action ) as $right ) {
2514  // Backwards compatibility, rewrite sysop -> editprotected
2515  if ( $right == 'sysop' ) {
2516  $right = 'editprotected';
2517  }
2518  // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
2519  if ( $right == 'autoconfirmed' ) {
2520  $right = 'editsemiprotected';
2521  }
2522  if ( $right == '' ) {
2523  continue;
2524  }
2525  if ( !$user->isAllowed( $right ) ) {
2526  $errors[] = [ 'protectedpagetext', $right, $action ];
2527  } elseif ( $this->mCascadeRestriction && !$user->isAllowed( 'protect' ) ) {
2528  $errors[] = [ 'protectedpagetext', 'protect', $action ];
2529  }
2530  }
2531 
2532  return $errors;
2533  }
2534 
2546  private function checkCascadingSourcesRestrictions( $action, $user, $errors, $rigor, $short ) {
2547  if ( $rigor !== 'quick' && !$this->isUserConfigPage() ) {
2548  # We /could/ use the protection level on the source page, but it's
2549  # fairly ugly as we have to establish a precedence hierarchy for pages
2550  # included by multiple cascade-protected pages. So just restrict
2551  # it to people with 'protect' permission, as they could remove the
2552  # protection anyway.
2553  list( $cascadingSources, $restrictions ) = $this->getCascadeProtectionSources();
2554  # Cascading protection depends on more than this page...
2555  # Several cascading protected pages may include this page...
2556  # Check each cascading level
2557  # This is only for protection restrictions, not for all actions
2558  if ( isset( $restrictions[$action] ) ) {
2559  foreach ( $restrictions[$action] as $right ) {
2560  // Backwards compatibility, rewrite sysop -> editprotected
2561  if ( $right == 'sysop' ) {
2562  $right = 'editprotected';
2563  }
2564  // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
2565  if ( $right == 'autoconfirmed' ) {
2566  $right = 'editsemiprotected';
2567  }
2568  if ( $right != '' && !$user->isAllowedAll( 'protect', $right ) ) {
2569  $pages = '';
2570  foreach ( $cascadingSources as $page ) {
2571  $pages .= '* [[:' . $page->getPrefixedText() . "]]\n";
2572  }
2573  $errors[] = [ 'cascadeprotected', count( $cascadingSources ), $pages, $action ];
2574  }
2575  }
2576  }
2577  }
2578 
2579  return $errors;
2580  }
2581 
2593  private function checkActionPermissions( $action, $user, $errors, $rigor, $short ) {
2595 
2596  if ( $action == 'protect' ) {
2597  if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $rigor, true ) ) ) {
2598  // If they can't edit, they shouldn't protect.
2599  $errors[] = [ 'protect-cantedit' ];
2600  }
2601  } elseif ( $action == 'create' ) {
2602  $title_protection = $this->getTitleProtection();
2603  if ( $title_protection ) {
2604  if ( $title_protection['permission'] == ''
2605  || !$user->isAllowed( $title_protection['permission'] )
2606  ) {
2607  $errors[] = [
2608  'titleprotected',
2609  User::whoIs( $title_protection['user'] ),
2610  $title_protection['reason']
2611  ];
2612  }
2613  }
2614  } elseif ( $action == 'move' ) {
2615  // Check for immobile pages
2616  if ( !MWNamespace::isMovable( $this->mNamespace ) ) {
2617  // Specific message for this case
2618  $errors[] = [ 'immobile-source-namespace', $this->getNsText() ];
2619  } elseif ( !$this->isMovable() ) {
2620  // Less specific message for rarer cases
2621  $errors[] = [ 'immobile-source-page' ];
2622  }
2623  } elseif ( $action == 'move-target' ) {
2624  if ( !MWNamespace::isMovable( $this->mNamespace ) ) {
2625  $errors[] = [ 'immobile-target-namespace', $this->getNsText() ];
2626  } elseif ( !$this->isMovable() ) {
2627  $errors[] = [ 'immobile-target-page' ];
2628  }
2629  } elseif ( $action == 'delete' ) {
2630  $tempErrors = $this->checkPageRestrictions( 'edit', $user, [], $rigor, true );
2631  if ( !$tempErrors ) {
2632  $tempErrors = $this->checkCascadingSourcesRestrictions( 'edit',
2633  $user, $tempErrors, $rigor, true );
2634  }
2635  if ( $tempErrors ) {
2636  // If protection keeps them from editing, they shouldn't be able to delete.
2637  $errors[] = [ 'deleteprotected' ];
2638  }
2639  if ( $rigor !== 'quick' && $wgDeleteRevisionsLimit
2640  && !$this->userCan( 'bigdelete', $user ) && $this->isBigDeletion()
2641  ) {
2642  $errors[] = [ 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ];
2643  }
2644  } elseif ( $action === 'undelete' ) {
2645  if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $rigor, true ) ) ) {
2646  // Undeleting implies editing
2647  $errors[] = [ 'undelete-cantedit' ];
2648  }
2649  if ( !$this->exists()
2650  && count( $this->getUserPermissionsErrorsInternal( 'create', $user, $rigor, true ) )
2651  ) {
2652  // Undeleting where nothing currently exists implies creating
2653  $errors[] = [ 'undelete-cantcreate' ];
2654  }
2655  }
2656  return $errors;
2657  }
2658 
2670  private function checkUserBlock( $action, $user, $errors, $rigor, $short ) {
2672  // Account creation blocks handled at userlogin.
2673  // Unblocking handled in SpecialUnblock
2674  if ( $rigor === 'quick' || in_array( $action, [ 'createaccount', 'unblock' ] ) ) {
2675  return $errors;
2676  }
2677 
2678  // Optimize for a very common case
2679  if ( $action === 'read' && !$wgBlockDisablesLogin ) {
2680  return $errors;
2681  }
2682 
2684  && !$user->isEmailConfirmed()
2685  && $action === 'edit'
2686  ) {
2687  $errors[] = [ 'confirmedittext' ];
2688  }
2689 
2690  $useSlave = ( $rigor !== 'secure' );
2691  if ( ( $action == 'edit' || $action == 'create' )
2692  && !$user->isBlockedFrom( $this, $useSlave )
2693  ) {
2694  // Don't block the user from editing their own talk page unless they've been
2695  // explicitly blocked from that too.
2696  } elseif ( $user->isBlocked() && $user->getBlock()->prevents( $action ) !== false ) {
2697  // @todo FIXME: Pass the relevant context into this function.
2698  $errors[] = $user->getBlock()->getPermissionsError( RequestContext::getMain() );
2699  }
2700 
2701  return $errors;
2702  }
2703 
2715  private function checkReadPermissions( $action, $user, $errors, $rigor, $short ) {
2717 
2718  $whitelisted = false;
2719  if ( User::isEveryoneAllowed( 'read' ) ) {
2720  # Shortcut for public wikis, allows skipping quite a bit of code
2721  $whitelisted = true;
2722  } elseif ( $user->isAllowed( 'read' ) ) {
2723  # If the user is allowed to read pages, he is allowed to read all pages
2724  $whitelisted = true;
2725  } elseif ( $this->isSpecial( 'Userlogin' )
2726  || $this->isSpecial( 'PasswordReset' )
2727  || $this->isSpecial( 'Userlogout' )
2728  ) {
2729  # Always grant access to the login page.
2730  # Even anons need to be able to log in.
2731  $whitelisted = true;
2732  } elseif ( is_array( $wgWhitelistRead ) && count( $wgWhitelistRead ) ) {
2733  # Time to check the whitelist
2734  # Only do these checks is there's something to check against
2735  $name = $this->getPrefixedText();
2736  $dbName = $this->getPrefixedDBkey();
2737 
2738  // Check for explicit whitelisting with and without underscores
2739  if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) {
2740  $whitelisted = true;
2741  } elseif ( $this->mNamespace == NS_MAIN ) {
2742  # Old settings might have the title prefixed with
2743  # a colon for main-namespace pages
2744  if ( in_array( ':' . $name, $wgWhitelistRead ) ) {
2745  $whitelisted = true;
2746  }
2747  } elseif ( $this->isSpecialPage() ) {
2748  # If it's a special page, ditch the subpage bit and check again
2750  list( $name, /* $subpage */ ) =
2751  MediaWikiServices::getInstance()->getSpecialPageFactory()->
2752  resolveAlias( $name );
2753  if ( $name ) {
2754  $pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
2755  if ( in_array( $pure, $wgWhitelistRead, true ) ) {
2756  $whitelisted = true;
2757  }
2758  }
2759  }
2760  }
2761 
2762  if ( !$whitelisted && is_array( $wgWhitelistReadRegexp ) && !empty( $wgWhitelistReadRegexp ) ) {
2763  $name = $this->getPrefixedText();
2764  // Check for regex whitelisting
2765  foreach ( $wgWhitelistReadRegexp as $listItem ) {
2766  if ( preg_match( $listItem, $name ) ) {
2767  $whitelisted = true;
2768  break;
2769  }
2770  }
2771  }
2772 
2773  if ( !$whitelisted ) {
2774  # If the title is not whitelisted, give extensions a chance to do so...
2775  Hooks::run( 'TitleReadWhitelist', [ $this, $user, &$whitelisted ] );
2776  if ( !$whitelisted ) {
2777  $errors[] = $this->missingPermissionError( $action, $short );
2778  }
2779  }
2780 
2781  return $errors;
2782  }
2783 
2792  private function missingPermissionError( $action, $short ) {
2793  // We avoid expensive display logic for quickUserCan's and such
2794  if ( $short ) {
2795  return [ 'badaccess-group0' ];
2796  }
2797 
2798  return User::newFatalPermissionDeniedStatus( $action )->getErrorsArray()[0];
2799  }
2800 
2816  $action, $user, $rigor = 'secure', $short = false
2817  ) {
2818  if ( $rigor === true ) {
2819  $rigor = 'secure'; // b/c
2820  } elseif ( $rigor === false ) {
2821  $rigor = 'quick'; // b/c
2822  } elseif ( !in_array( $rigor, [ 'quick', 'full', 'secure' ] ) ) {
2823  throw new Exception( "Invalid rigor parameter '$rigor'." );
2824  }
2825 
2826  # Read has special handling
2827  if ( $action == 'read' ) {
2828  $checks = [
2829  'checkPermissionHooks',
2830  'checkReadPermissions',
2831  'checkUserBlock', // for wgBlockDisablesLogin
2832  ];
2833  # Don't call checkSpecialsAndNSPermissions, checkSiteConfigPermissions
2834  # or checkUserConfigPermissions here as it will lead to duplicate
2835  # error messages. This is okay to do since anywhere that checks for
2836  # create will also check for edit, and those checks are called for edit.
2837  } elseif ( $action == 'create' ) {
2838  $checks = [
2839  'checkQuickPermissions',
2840  'checkPermissionHooks',
2841  'checkPageRestrictions',
2842  'checkCascadingSourcesRestrictions',
2843  'checkActionPermissions',
2844  'checkUserBlock'
2845  ];
2846  } else {
2847  $checks = [
2848  'checkQuickPermissions',
2849  'checkPermissionHooks',
2850  'checkSpecialsAndNSPermissions',
2851  'checkSiteConfigPermissions',
2852  'checkUserConfigPermissions',
2853  'checkPageRestrictions',
2854  'checkCascadingSourcesRestrictions',
2855  'checkActionPermissions',
2856  'checkUserBlock'
2857  ];
2858  }
2859 
2860  $errors = [];
2861  while ( count( $checks ) > 0 &&
2862  !( $short && count( $errors ) > 0 ) ) {
2863  $method = array_shift( $checks );
2864  $errors = $this->$method( $action, $user, $errors, $rigor, $short );
2865  }
2866 
2867  return $errors;
2868  }
2869 
2877  public static function getFilteredRestrictionTypes( $exists = true ) {
2878  global $wgRestrictionTypes;
2879  $types = $wgRestrictionTypes;
2880  if ( $exists ) {
2881  # Remove the create restriction for existing titles
2882  $types = array_diff( $types, [ 'create' ] );
2883  } else {
2884  # Only the create and upload restrictions apply to non-existing titles
2885  $types = array_intersect( $types, [ 'create', 'upload' ] );
2886  }
2887  return $types;
2888  }
2889 
2895  public function getRestrictionTypes() {
2896  if ( $this->isSpecialPage() ) {
2897  return [];
2898  }
2899 
2900  $types = self::getFilteredRestrictionTypes( $this->exists() );
2901 
2902  if ( $this->mNamespace != NS_FILE ) {
2903  # Remove the upload restriction for non-file titles
2904  $types = array_diff( $types, [ 'upload' ] );
2905  }
2906 
2907  Hooks::run( 'TitleGetRestrictionTypes', [ $this, &$types ] );
2908 
2909  wfDebug( __METHOD__ . ': applicable restrictions to [[' .
2910  $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" );
2911 
2912  return $types;
2913  }
2914 
2922  public function getTitleProtection() {
2923  $protection = $this->getTitleProtectionInternal();
2924  if ( $protection ) {
2925  if ( $protection['permission'] == 'sysop' ) {
2926  $protection['permission'] = 'editprotected'; // B/C
2927  }
2928  if ( $protection['permission'] == 'autoconfirmed' ) {
2929  $protection['permission'] = 'editsemiprotected'; // B/C
2930  }
2931  }
2932  return $protection;
2933  }
2934 
2945  protected function getTitleProtectionInternal() {
2946  // Can't protect pages in special namespaces
2947  if ( $this->mNamespace < 0 ) {
2948  return false;
2949  }
2950 
2951  // Can't protect pages that exist.
2952  if ( $this->exists() ) {
2953  return false;
2954  }
2955 
2956  if ( $this->mTitleProtection === null ) {
2957  $dbr = wfGetDB( DB_REPLICA );
2958  $commentStore = CommentStore::getStore();
2959  $commentQuery = $commentStore->getJoin( 'pt_reason' );
2960  $res = $dbr->select(
2961  [ 'protected_titles' ] + $commentQuery['tables'],
2962  [
2963  'user' => 'pt_user',
2964  'expiry' => 'pt_expiry',
2965  'permission' => 'pt_create_perm'
2966  ] + $commentQuery['fields'],
2967  [ 'pt_namespace' => $this->mNamespace, 'pt_title' => $this->mDbkeyform ],
2968  __METHOD__,
2969  [],
2970  $commentQuery['joins']
2971  );
2972 
2973  // fetchRow returns false if there are no rows.
2974  $row = $dbr->fetchRow( $res );
2975  if ( $row ) {
2976  $this->mTitleProtection = [
2977  'user' => $row['user'],
2978  'expiry' => $dbr->decodeExpiry( $row['expiry'] ),
2979  'permission' => $row['permission'],
2980  'reason' => $commentStore->getComment( 'pt_reason', $row )->text,
2981  ];
2982  } else {
2983  $this->mTitleProtection = false;
2984  }
2985  }
2986  return $this->mTitleProtection;
2987  }
2988 
2992  public function deleteTitleProtection() {
2993  $dbw = wfGetDB( DB_MASTER );
2994 
2995  $dbw->delete(
2996  'protected_titles',
2997  [ 'pt_namespace' => $this->mNamespace, 'pt_title' => $this->mDbkeyform ],
2998  __METHOD__
2999  );
3000  $this->mTitleProtection = false;
3001  }
3002 
3010  public function isSemiProtected( $action = 'edit' ) {
3012 
3013  $restrictions = $this->getRestrictions( $action );
3015  if ( !$restrictions || !$semi ) {
3016  // Not protected, or all protection is full protection
3017  return false;
3018  }
3019 
3020  // Remap autoconfirmed to editsemiprotected for BC
3021  foreach ( array_keys( $semi, 'autoconfirmed' ) as $key ) {
3022  $semi[$key] = 'editsemiprotected';
3023  }
3024  foreach ( array_keys( $restrictions, 'autoconfirmed' ) as $key ) {
3025  $restrictions[$key] = 'editsemiprotected';
3026  }
3027 
3028  return !array_diff( $restrictions, $semi );
3029  }
3030 
3038  public function isProtected( $action = '' ) {
3039  global $wgRestrictionLevels;
3040 
3041  $restrictionTypes = $this->getRestrictionTypes();
3042 
3043  # Special pages have inherent protection
3044  if ( $this->isSpecialPage() ) {
3045  return true;
3046  }
3047 
3048  # Check regular protection levels
3049  foreach ( $restrictionTypes as $type ) {
3050  if ( $action == $type || $action == '' ) {
3051  $r = $this->getRestrictions( $type );
3052  foreach ( $wgRestrictionLevels as $level ) {
3053  if ( in_array( $level, $r ) && $level != '' ) {
3054  return true;
3055  }
3056  }
3057  }
3058  }
3059 
3060  return false;
3061  }
3062 
3070  public function isNamespaceProtected( User $user ) {
3071  global $wgNamespaceProtection;
3072 
3073  if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) {
3074  foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) {
3075  if ( $right != '' && !$user->isAllowed( $right ) ) {
3076  return true;
3077  }
3078  }
3079  }
3080  return false;
3081  }
3082 
3088  public function isCascadeProtected() {
3089  list( $sources, /* $restrictions */ ) = $this->getCascadeProtectionSources( false );
3090  return ( $sources > 0 );
3091  }
3092 
3102  public function areCascadeProtectionSourcesLoaded( $getPages = true ) {
3103  return $getPages ? $this->mCascadeSources !== null : $this->mHasCascadingRestrictions !== null;
3104  }
3105 
3119  public function getCascadeProtectionSources( $getPages = true ) {
3120  $pagerestrictions = [];
3121 
3122  if ( $this->mCascadeSources !== null && $getPages ) {
3124  } elseif ( $this->mHasCascadingRestrictions !== null && !$getPages ) {
3125  return [ $this->mHasCascadingRestrictions, $pagerestrictions ];
3126  }
3127 
3128  $dbr = wfGetDB( DB_REPLICA );
3129 
3130  if ( $this->mNamespace == NS_FILE ) {
3131  $tables = [ 'imagelinks', 'page_restrictions' ];
3132  $where_clauses = [
3133  'il_to' => $this->mDbkeyform,
3134  'il_from=pr_page',
3135  'pr_cascade' => 1
3136  ];
3137  } else {
3138  $tables = [ 'templatelinks', 'page_restrictions' ];
3139  $where_clauses = [
3140  'tl_namespace' => $this->mNamespace,
3141  'tl_title' => $this->mDbkeyform,
3142  'tl_from=pr_page',
3143  'pr_cascade' => 1
3144  ];
3145  }
3146 
3147  if ( $getPages ) {
3148  $cols = [ 'pr_page', 'page_namespace', 'page_title',
3149  'pr_expiry', 'pr_type', 'pr_level' ];
3150  $where_clauses[] = 'page_id=pr_page';
3151  $tables[] = 'page';
3152  } else {
3153  $cols = [ 'pr_expiry' ];
3154  }
3155 
3156  $res = $dbr->select( $tables, $cols, $where_clauses, __METHOD__ );
3157 
3158  $sources = $getPages ? [] : false;
3159  $now = wfTimestampNow();
3160 
3161  foreach ( $res as $row ) {
3162  $expiry = $dbr->decodeExpiry( $row->pr_expiry );
3163  if ( $expiry > $now ) {
3164  if ( $getPages ) {
3165  $page_id = $row->pr_page;
3166  $page_ns = $row->page_namespace;
3167  $page_title = $row->page_title;
3168  $sources[$page_id] = self::makeTitle( $page_ns, $page_title );
3169  # Add groups needed for each restriction type if its not already there
3170  # Make sure this restriction type still exists
3171 
3172  if ( !isset( $pagerestrictions[$row->pr_type] ) ) {
3173  $pagerestrictions[$row->pr_type] = [];
3174  }
3175 
3176  if (
3177  isset( $pagerestrictions[$row->pr_type] )
3178  && !in_array( $row->pr_level, $pagerestrictions[$row->pr_type] )
3179  ) {
3180  $pagerestrictions[$row->pr_type][] = $row->pr_level;
3181  }
3182  } else {
3183  $sources = true;
3184  }
3185  }
3186  }
3187 
3188  if ( $getPages ) {
3189  $this->mCascadeSources = $sources;
3190  $this->mCascadingRestrictions = $pagerestrictions;
3191  } else {
3192  $this->mHasCascadingRestrictions = $sources;
3193  }
3194 
3195  return [ $sources, $pagerestrictions ];
3196  }
3197 
3205  public function areRestrictionsLoaded() {
3207  }
3208 
3218  public function getRestrictions( $action ) {
3219  if ( !$this->mRestrictionsLoaded ) {
3220  $this->loadRestrictions();
3221  }
3222  return $this->mRestrictions[$action] ?? [];
3223  }
3224 
3232  public function getAllRestrictions() {
3233  if ( !$this->mRestrictionsLoaded ) {
3234  $this->loadRestrictions();
3235  }
3236  return $this->mRestrictions;
3237  }
3238 
3246  public function getRestrictionExpiry( $action ) {
3247  if ( !$this->mRestrictionsLoaded ) {
3248  $this->loadRestrictions();
3249  }
3250  return $this->mRestrictionsExpiry[$action] ?? false;
3251  }
3252 
3259  if ( !$this->mRestrictionsLoaded ) {
3260  $this->loadRestrictions();
3261  }
3262 
3264  }
3265 
3277  public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
3278  $dbr = wfGetDB( DB_REPLICA );
3279 
3280  $restrictionTypes = $this->getRestrictionTypes();
3281 
3282  foreach ( $restrictionTypes as $type ) {
3283  $this->mRestrictions[$type] = [];
3284  $this->mRestrictionsExpiry[$type] = 'infinity';
3285  }
3286 
3287  $this->mCascadeRestriction = false;
3288 
3289  # Backwards-compatibility: also load the restrictions from the page record (old format).
3290  if ( $oldFashionedRestrictions !== null ) {
3291  $this->mOldRestrictions = $oldFashionedRestrictions;
3292  }
3293 
3294  if ( $this->mOldRestrictions === false ) {
3295  $this->mOldRestrictions = $dbr->selectField( 'page', 'page_restrictions',
3296  [ 'page_id' => $this->getArticleID() ], __METHOD__ );
3297  }
3298 
3299  if ( $this->mOldRestrictions != '' ) {
3300  foreach ( explode( ':', trim( $this->mOldRestrictions ) ) as $restrict ) {
3301  $temp = explode( '=', trim( $restrict ) );
3302  if ( count( $temp ) == 1 ) {
3303  // old old format should be treated as edit/move restriction
3304  $this->mRestrictions['edit'] = explode( ',', trim( $temp[0] ) );
3305  $this->mRestrictions['move'] = explode( ',', trim( $temp[0] ) );
3306  } else {
3307  $restriction = trim( $temp[1] );
3308  if ( $restriction != '' ) { // some old entries are empty
3309  $this->mRestrictions[$temp[0]] = explode( ',', $restriction );
3310  }
3311  }
3312  }
3313  }
3314 
3315  if ( count( $rows ) ) {
3316  # Current system - load second to make them override.
3317  $now = wfTimestampNow();
3318 
3319  # Cycle through all the restrictions.
3320  foreach ( $rows as $row ) {
3321  // Don't take care of restrictions types that aren't allowed
3322  if ( !in_array( $row->pr_type, $restrictionTypes ) ) {
3323  continue;
3324  }
3325 
3326  $expiry = $dbr->decodeExpiry( $row->pr_expiry );
3327 
3328  // Only apply the restrictions if they haven't expired!
3329  if ( !$expiry || $expiry > $now ) {
3330  $this->mRestrictionsExpiry[$row->pr_type] = $expiry;
3331  $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) );
3332 
3333  $this->mCascadeRestriction |= $row->pr_cascade;
3334  }
3335  }
3336  }
3337 
3338  $this->mRestrictionsLoaded = true;
3339  }
3340 
3349  public function loadRestrictions( $oldFashionedRestrictions = null ) {
3350  if ( $this->mRestrictionsLoaded ) {
3351  return;
3352  }
3353 
3354  $id = $this->getArticleID();
3355  if ( $id ) {
3357  $fname = __METHOD__;
3358  $rows = $cache->getWithSetCallback(
3359  // Page protections always leave a new null revision
3360  $cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID() ),
3361  $cache::TTL_DAY,
3362  function ( $curValue, &$ttl, array &$setOpts ) use ( $fname ) {
3363  $dbr = wfGetDB( DB_REPLICA );
3364 
3365  $setOpts += Database::getCacheSetOptions( $dbr );
3366 
3367  return iterator_to_array(
3368  $dbr->select(
3369  'page_restrictions',
3370  [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ],
3371  [ 'pr_page' => $this->getArticleID() ],
3372  $fname
3373  )
3374  );
3375  }
3376  );
3377 
3378  $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
3379  } else {
3380  $title_protection = $this->getTitleProtectionInternal();
3381 
3382  if ( $title_protection ) {
3383  $now = wfTimestampNow();
3384  $expiry = wfGetDB( DB_REPLICA )->decodeExpiry( $title_protection['expiry'] );
3385 
3386  if ( !$expiry || $expiry > $now ) {
3387  // Apply the restrictions
3388  $this->mRestrictionsExpiry['create'] = $expiry;
3389  $this->mRestrictions['create'] =
3390  explode( ',', trim( $title_protection['permission'] ) );
3391  } else { // Get rid of the old restrictions
3392  $this->mTitleProtection = false;
3393  }
3394  } else {
3395  $this->mRestrictionsExpiry['create'] = 'infinity';
3396  }
3397  $this->mRestrictionsLoaded = true;
3398  }
3399  }
3400 
3405  public function flushRestrictions() {
3406  $this->mRestrictionsLoaded = false;
3407  $this->mTitleProtection = null;
3408  }
3409 
3415  static function purgeExpiredRestrictions() {
3416  if ( wfReadOnly() ) {
3417  return;
3418  }
3419 
3421  wfGetDB( DB_MASTER ),
3422  __METHOD__,
3423  function ( IDatabase $dbw, $fname ) {
3424  $config = MediaWikiServices::getInstance()->getMainConfig();
3425  $ids = $dbw->selectFieldValues(
3426  'page_restrictions',
3427  'pr_id',
3428  [ 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ],
3429  $fname,
3430  [ 'LIMIT' => $config->get( 'UpdateRowsPerQuery' ) ] // T135470
3431  );
3432  if ( $ids ) {
3433  $dbw->delete( 'page_restrictions', [ 'pr_id' => $ids ], $fname );
3434  }
3435  }
3436  ) );
3437 
3439  wfGetDB( DB_MASTER ),
3440  __METHOD__,
3441  function ( IDatabase $dbw, $fname ) {
3442  $dbw->delete(
3443  'protected_titles',
3444  [ 'pt_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ],
3445  $fname
3446  );
3447  }
3448  ) );
3449  }
3450 
3456  public function hasSubpages() {
3457  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
3458  # Duh
3459  return false;
3460  }
3461 
3462  # We dynamically add a member variable for the purpose of this method
3463  # alone to cache the result. There's no point in having it hanging
3464  # around uninitialized in every Title object; therefore we only add it
3465  # if needed and don't declare it statically.
3466  if ( $this->mHasSubpages === null ) {
3467  $this->mHasSubpages = false;
3468  $subpages = $this->getSubpages( 1 );
3469  if ( $subpages instanceof TitleArray ) {
3470  $this->mHasSubpages = (bool)$subpages->count();
3471  }
3472  }
3473 
3474  return $this->mHasSubpages;
3475  }
3476 
3484  public function getSubpages( $limit = -1 ) {
3485  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
3486  return [];
3487  }
3488 
3489  $dbr = wfGetDB( DB_REPLICA );
3490  $conds['page_namespace'] = $this->mNamespace;
3491  $conds[] = 'page_title ' . $dbr->buildLike( $this->mDbkeyform . '/', $dbr->anyString() );
3492  $options = [];
3493  if ( $limit > -1 ) {
3494  $options['LIMIT'] = $limit;
3495  }
3497  $dbr->select( 'page',
3498  [ 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ],
3499  $conds,
3500  __METHOD__,
3501  $options
3502  )
3503  );
3504  }
3505 
3511  public function isDeleted() {
3512  if ( $this->mNamespace < 0 ) {
3513  $n = 0;
3514  } else {
3515  $dbr = wfGetDB( DB_REPLICA );
3516 
3517  $n = $dbr->selectField( 'archive', 'COUNT(*)',
3518  [ 'ar_namespace' => $this->mNamespace, 'ar_title' => $this->mDbkeyform ],
3519  __METHOD__
3520  );
3521  if ( $this->mNamespace == NS_FILE ) {
3522  $n += $dbr->selectField( 'filearchive', 'COUNT(*)',
3523  [ 'fa_name' => $this->mDbkeyform ],
3524  __METHOD__
3525  );
3526  }
3527  }
3528  return (int)$n;
3529  }
3530 
3536  public function isDeletedQuick() {
3537  if ( $this->mNamespace < 0 ) {
3538  return false;
3539  }
3540  $dbr = wfGetDB( DB_REPLICA );
3541  $deleted = (bool)$dbr->selectField( 'archive', '1',
3542  [ 'ar_namespace' => $this->mNamespace, 'ar_title' => $this->mDbkeyform ],
3543  __METHOD__
3544  );
3545  if ( !$deleted && $this->mNamespace == NS_FILE ) {
3546  $deleted = (bool)$dbr->selectField( 'filearchive', '1',
3547  [ 'fa_name' => $this->mDbkeyform ],
3548  __METHOD__
3549  );
3550  }
3551  return $deleted;
3552  }
3553 
3562  public function getArticleID( $flags = 0 ) {
3563  if ( $this->mNamespace < 0 ) {
3564  $this->mArticleID = 0;
3565  return $this->mArticleID;
3566  }
3567  $linkCache = MediaWikiServices::getInstance()->getLinkCache();
3568  if ( $flags & self::GAID_FOR_UPDATE ) {
3569  $oldUpdate = $linkCache->forUpdate( true );
3570  $linkCache->clearLink( $this );
3571  $this->mArticleID = $linkCache->addLinkObj( $this );
3572  $linkCache->forUpdate( $oldUpdate );
3573  } else {
3574  if ( -1 == $this->mArticleID ) {
3575  $this->mArticleID = $linkCache->addLinkObj( $this );
3576  }
3577  }
3578  return $this->mArticleID;
3579  }
3580 
3588  public function isRedirect( $flags = 0 ) {
3589  if ( !is_null( $this->mRedirect ) ) {
3590  return $this->mRedirect;
3591  }
3592  if ( !$this->getArticleID( $flags ) ) {
3593  $this->mRedirect = false;
3594  return $this->mRedirect;
3595  }
3596 
3597  $linkCache = MediaWikiServices::getInstance()->getLinkCache();
3598  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3599  $cached = $linkCache->getGoodLinkFieldObj( $this, 'redirect' );
3600  if ( $cached === null ) {
3601  # Trust LinkCache's state over our own
3602  # LinkCache is telling us that the page doesn't exist, despite there being cached
3603  # data relating to an existing page in $this->mArticleID. Updaters should clear
3604  # LinkCache as appropriate, or use $flags = Title::GAID_FOR_UPDATE. If that flag is
3605  # set, then LinkCache will definitely be up to date here, since getArticleID() forces
3606  # LinkCache to refresh its data from the master.
3607  $this->mRedirect = false;
3608  return $this->mRedirect;
3609  }
3610 
3611  $this->mRedirect = (bool)$cached;
3612 
3613  return $this->mRedirect;
3614  }
3615 
3623  public function getLength( $flags = 0 ) {
3624  if ( $this->mLength != -1 ) {
3625  return $this->mLength;
3626  }
3627  if ( !$this->getArticleID( $flags ) ) {
3628  $this->mLength = 0;
3629  return $this->mLength;
3630  }
3631  $linkCache = MediaWikiServices::getInstance()->getLinkCache();
3632  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3633  $cached = $linkCache->getGoodLinkFieldObj( $this, 'length' );
3634  if ( $cached === null ) {
3635  # Trust LinkCache's state over our own, as for isRedirect()
3636  $this->mLength = 0;
3637  return $this->mLength;
3638  }
3639 
3640  $this->mLength = intval( $cached );
3641 
3642  return $this->mLength;
3643  }
3644 
3651  public function getLatestRevID( $flags = 0 ) {
3652  if ( !( $flags & self::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
3653  return intval( $this->mLatestID );
3654  }
3655  if ( !$this->getArticleID( $flags ) ) {
3656  $this->mLatestID = 0;
3657  return $this->mLatestID;
3658  }
3659  $linkCache = MediaWikiServices::getInstance()->getLinkCache();
3660  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3661  $cached = $linkCache->getGoodLinkFieldObj( $this, 'revision' );
3662  if ( $cached === null ) {
3663  # Trust LinkCache's state over our own, as for isRedirect()
3664  $this->mLatestID = 0;
3665  return $this->mLatestID;
3666  }
3667 
3668  $this->mLatestID = intval( $cached );
3669 
3670  return $this->mLatestID;
3671  }
3672 
3683  public function resetArticleID( $newid ) {
3684  $linkCache = MediaWikiServices::getInstance()->getLinkCache();
3685  $linkCache->clearLink( $this );
3686 
3687  if ( $newid === false ) {
3688  $this->mArticleID = -1;
3689  } else {
3690  $this->mArticleID = intval( $newid );
3691  }
3692  $this->mRestrictionsLoaded = false;
3693  $this->mRestrictions = [];
3694  $this->mOldRestrictions = false;
3695  $this->mRedirect = null;
3696  $this->mLength = -1;
3697  $this->mLatestID = false;
3698  $this->mContentModel = false;
3699  $this->mEstimateRevisions = null;
3700  $this->mPageLanguage = false;
3701  $this->mDbPageLanguage = false;
3702  $this->mIsBigDeletion = null;
3703  }
3704 
3705  public static function clearCaches() {
3706  $linkCache = MediaWikiServices::getInstance()->getLinkCache();
3707  $linkCache->clear();
3708 
3710  $titleCache->clear();
3711  }
3712 
3720  public static function capitalize( $text, $ns = NS_MAIN ) {
3721  if ( MWNamespace::isCapitalized( $ns ) ) {
3722  return MediaWikiServices::getInstance()->getContentLanguage()->ucfirst( $text );
3723  } else {
3724  return $text;
3725  }
3726  }
3727 
3740  private function secureAndSplit() {
3741  // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
3742  // the parsing code with Title, while avoiding massive refactoring.
3743  // @todo: get rid of secureAndSplit, refactor parsing code.
3744  // @note: getTitleParser() returns a TitleParser implementation which does not have a
3745  // splitTitleString method, but the only implementation (MediaWikiTitleCodec) does
3746  $titleCodec = MediaWikiServices::getInstance()->getTitleParser();
3747  // MalformedTitleException can be thrown here
3748  $parts = $titleCodec->splitTitleString( $this->mDbkeyform, $this->mDefaultNamespace );
3749 
3750  # Fill fields
3751  $this->setFragment( '#' . $parts['fragment'] );
3752  $this->mInterwiki = $parts['interwiki'];
3753  $this->mLocalInterwiki = $parts['local_interwiki'];
3754  $this->mNamespace = $parts['namespace'];
3755  $this->mUserCaseDBKey = $parts['user_case_dbkey'];
3756 
3757  $this->mDbkeyform = $parts['dbkey'];
3758  $this->mUrlform = wfUrlencode( $this->mDbkeyform );
3759  $this->mTextform = strtr( $this->mDbkeyform, '_', ' ' );
3760 
3761  # We already know that some pages won't be in the database!
3762  if ( $this->isExternal() || $this->isSpecialPage() ) {
3763  $this->mArticleID = 0;
3764  }
3765 
3766  return true;
3767  }
3768 
3781  public function getLinksTo( $options = [], $table = 'pagelinks', $prefix = 'pl' ) {
3782  if ( count( $options ) > 0 ) {
3783  $db = wfGetDB( DB_MASTER );
3784  } else {
3785  $db = wfGetDB( DB_REPLICA );
3786  }
3787 
3788  $res = $db->select(
3789  [ 'page', $table ],
3790  self::getSelectFields(),
3791  [
3792  "{$prefix}_from=page_id",
3793  "{$prefix}_namespace" => $this->mNamespace,
3794  "{$prefix}_title" => $this->mDbkeyform ],
3795  __METHOD__,
3796  $options
3797  );
3798 
3799  $retVal = [];
3800  if ( $res->numRows() ) {
3801  $linkCache = MediaWikiServices::getInstance()->getLinkCache();
3802  foreach ( $res as $row ) {
3803  $titleObj = self::makeTitle( $row->page_namespace, $row->page_title );
3804  if ( $titleObj ) {
3805  $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
3806  $retVal[] = $titleObj;
3807  }
3808  }
3809  }
3810  return $retVal;
3811  }
3812 
3823  public function getTemplateLinksTo( $options = [] ) {
3824  return $this->getLinksTo( $options, 'templatelinks', 'tl' );
3825  }
3826 
3839  public function getLinksFrom( $options = [], $table = 'pagelinks', $prefix = 'pl' ) {
3840  $id = $this->getArticleID();
3841 
3842  # If the page doesn't exist; there can't be any link from this page
3843  if ( !$id ) {
3844  return [];
3845  }
3846 
3847  $db = wfGetDB( DB_REPLICA );
3848 
3849  $blNamespace = "{$prefix}_namespace";
3850  $blTitle = "{$prefix}_title";
3851 
3852  $pageQuery = WikiPage::getQueryInfo();
3853  $res = $db->select(
3854  [ $table, 'nestpage' => $pageQuery['tables'] ],
3855  array_merge(
3856  [ $blNamespace, $blTitle ],
3857  $pageQuery['fields']
3858  ),
3859  [ "{$prefix}_from" => $id ],
3860  __METHOD__,
3861  $options,
3862  [ 'nestpage' => [
3863  'LEFT JOIN',
3864  [ "page_namespace=$blNamespace", "page_title=$blTitle" ]
3865  ] ] + $pageQuery['joins']
3866  );
3867 
3868  $retVal = [];
3869  $linkCache = MediaWikiServices::getInstance()->getLinkCache();
3870  foreach ( $res as $row ) {
3871  if ( $row->page_id ) {
3872  $titleObj = self::newFromRow( $row );
3873  } else {
3874  $titleObj = self::makeTitle( $row->$blNamespace, $row->$blTitle );
3875  $linkCache->addBadLinkObj( $titleObj );
3876  }
3877  $retVal[] = $titleObj;
3878  }
3879 
3880  return $retVal;
3881  }
3882 
3893  public function getTemplateLinksFrom( $options = [] ) {
3894  return $this->getLinksFrom( $options, 'templatelinks', 'tl' );
3895  }
3896 
3905  public function getBrokenLinksFrom() {
3906  if ( $this->getArticleID() == 0 ) {
3907  # All links from article ID 0 are false positives
3908  return [];
3909  }
3910 
3911  $dbr = wfGetDB( DB_REPLICA );
3912  $res = $dbr->select(
3913  [ 'page', 'pagelinks' ],
3914  [ 'pl_namespace', 'pl_title' ],
3915  [
3916  'pl_from' => $this->getArticleID(),
3917  'page_namespace IS NULL'
3918  ],
3919  __METHOD__, [],
3920  [
3921  'page' => [
3922  'LEFT JOIN',
3923  [ 'pl_namespace=page_namespace', 'pl_title=page_title' ]
3924  ]
3925  ]
3926  );
3927 
3928  $retVal = [];
3929  foreach ( $res as $row ) {
3930  $retVal[] = self::makeTitle( $row->pl_namespace, $row->pl_title );
3931  }
3932  return $retVal;
3933  }
3934 
3941  public function getCdnUrls() {
3942  $urls = [
3943  $this->getInternalURL(),
3944  $this->getInternalURL( 'action=history' )
3945  ];
3946 
3947  $pageLang = $this->getPageLanguage();
3948  if ( $pageLang->hasVariants() ) {
3949  $variants = $pageLang->getVariants();
3950  foreach ( $variants as $vCode ) {
3951  $urls[] = $this->getInternalURL( $vCode );
3952  }
3953  }
3954 
3955  // If we are looking at a css/js user subpage, purge the action=raw.
3956  if ( $this->isUserJsConfigPage() ) {
3957  $urls[] = $this->getInternalURL( 'action=raw&ctype=text/javascript' );
3958  } elseif ( $this->isUserJsonConfigPage() ) {
3959  $urls[] = $this->getInternalURL( 'action=raw&ctype=application/json' );
3960  } elseif ( $this->isUserCssConfigPage() ) {
3961  $urls[] = $this->getInternalURL( 'action=raw&ctype=text/css' );
3962  }
3963 
3964  Hooks::run( 'TitleSquidURLs', [ $this, &$urls ] );
3965  return $urls;
3966  }
3967 
3971  public function getSquidURLs() {
3972  return $this->getCdnUrls();
3973  }
3974 
3978  public function purgeSquid() {
3980  new CdnCacheUpdate( $this->getCdnUrls() ),
3982  );
3983  }
3984 
3995  public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) {
3996  global $wgUser;
3997 
3998  if ( !( $nt instanceof Title ) ) {
3999  // Normally we'd add this to $errors, but we'll get
4000  // lots of syntax errors if $nt is not an object
4001  return [ [ 'badtitletext' ] ];
4002  }
4003 
4004  $mp = new MovePage( $this, $nt );
4005  $errors = $mp->isValidMove()->getErrorsArray();
4006  if ( $auth ) {
4007  $errors = wfMergeErrorArrays(
4008  $errors,
4009  $mp->checkPermissions( $wgUser, $reason )->getErrorsArray()
4010  );
4011  }
4012 
4013  return $errors ?: true;
4014  }
4015 
4022  protected function validateFileMoveOperation( $nt ) {
4023  global $wgUser;
4024 
4025  $errors = [];
4026 
4027  $destFile = wfLocalFile( $nt );
4028  $destFile->load( File::READ_LATEST );
4029  if ( !$wgUser->isAllowed( 'reupload-shared' )
4030  && !$destFile->exists() && wfFindFile( $nt )
4031  ) {
4032  $errors[] = [ 'file-exists-sharedrepo' ];
4033  }
4034 
4035  return $errors;
4036  }
4037 
4051  public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true,
4052  array $changeTags = []
4053  ) {
4054  global $wgUser;
4055  $err = $this->isValidMoveOperation( $nt, $auth, $reason );
4056  if ( is_array( $err ) ) {
4057  // Auto-block user's IP if the account was "hard" blocked
4058  $wgUser->spreadAnyEditBlock();
4059  return $err;
4060  }
4061  // Check suppressredirect permission
4062  if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
4063  $createRedirect = true;
4064  }
4065 
4066  $mp = new MovePage( $this, $nt );
4067  $status = $mp->move( $wgUser, $reason, $createRedirect, $changeTags );
4068  if ( $status->isOK() ) {
4069  return true;
4070  } else {
4071  return $status->getErrorsArray();
4072  }
4073  }
4074 
4089  public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true,
4090  array $changeTags = []
4091  ) {
4092  global $wgMaximumMovedPages;
4093  // Check permissions
4094  if ( !$this->userCan( 'move-subpages' ) ) {
4095  return [
4096  [ 'cant-move-subpages' ],
4097  ];
4098  }
4099  // Do the source and target namespaces support subpages?
4100  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
4101  return [
4102  [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $this->mNamespace ) ],
4103  ];
4104  }
4105  if ( !MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
4106  return [
4107  [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $nt->getNamespace() ) ],
4108  ];
4109  }
4110 
4111  $subpages = $this->getSubpages( $wgMaximumMovedPages + 1 );
4112  $retval = [];
4113  $count = 0;
4114  foreach ( $subpages as $oldSubpage ) {
4115  $count++;
4116  if ( $count > $wgMaximumMovedPages ) {
4117  $retval[$oldSubpage->getPrefixedText()] = [
4118  [ 'movepage-max-pages', $wgMaximumMovedPages ],
4119  ];
4120  break;
4121  }
4122 
4123  // We don't know whether this function was called before
4124  // or after moving the root page, so check both
4125  // $this and $nt
4126  if ( $oldSubpage->getArticleID() == $this->getArticleID()
4127  || $oldSubpage->getArticleID() == $nt->getArticleID()
4128  ) {
4129  // When moving a page to a subpage of itself,
4130  // don't move it twice
4131  continue;
4132  }
4133  $newPageName = preg_replace(
4134  '#^' . preg_quote( $this->mDbkeyform, '#' ) . '#',
4135  StringUtils::escapeRegexReplacement( $nt->getDBkey() ), # T23234
4136  $oldSubpage->getDBkey() );
4137  if ( $oldSubpage->isTalkPage() ) {
4138  $newNs = $nt->getTalkPage()->getNamespace();
4139  } else {
4140  $newNs = $nt->getSubjectPage()->getNamespace();
4141  }
4142  # T16385: we need makeTitleSafe because the new page names may
4143  # be longer than 255 characters.
4144  $newSubpage = self::makeTitleSafe( $newNs, $newPageName );
4145 
4146  $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect, $changeTags );
4147  if ( $success === true ) {
4148  $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
4149  } else {
4150  $retval[$oldSubpage->getPrefixedText()] = $success;
4151  }
4152  }
4153  return $retval;
4154  }
4155 
4162  public function isSingleRevRedirect() {
4163  global $wgContentHandlerUseDB;
4164 
4165  $dbw = wfGetDB( DB_MASTER );
4166 
4167  # Is it a redirect?
4168  $fields = [ 'page_is_redirect', 'page_latest', 'page_id' ];
4169  if ( $wgContentHandlerUseDB ) {
4170  $fields[] = 'page_content_model';
4171  }
4172 
4173  $row = $dbw->selectRow( 'page',
4174  $fields,
4175  $this->pageCond(),
4176  __METHOD__,
4177  [ 'FOR UPDATE' ]
4178  );
4179  # Cache some fields we may want
4180  $this->mArticleID = $row ? intval( $row->page_id ) : 0;
4181  $this->mRedirect = $row ? (bool)$row->page_is_redirect : false;
4182  $this->mLatestID = $row ? intval( $row->page_latest ) : false;
4183  $this->mContentModel = $row && isset( $row->page_content_model )
4184  ? strval( $row->page_content_model )
4185  : false;
4186 
4187  if ( !$this->mRedirect ) {
4188  return false;
4189  }
4190  # Does the article have a history?
4191  $row = $dbw->selectField( [ 'page', 'revision' ],
4192  'rev_id',
4193  [ 'page_namespace' => $this->mNamespace,
4194  'page_title' => $this->mDbkeyform,
4195  'page_id=rev_page',
4196  'page_latest != rev_id'
4197  ],
4198  __METHOD__,
4199  [ 'FOR UPDATE' ]
4200  );
4201  # Return true if there was no history
4202  return ( $row === false );
4203  }
4204 
4213  public function isValidMoveTarget( $nt ) {
4214  # Is it an existing file?
4215  if ( $nt->getNamespace() == NS_FILE ) {
4216  $file = wfLocalFile( $nt );
4217  $file->load( File::READ_LATEST );
4218  if ( $file->exists() ) {
4219  wfDebug( __METHOD__ . ": file exists\n" );
4220  return false;
4221  }
4222  }
4223  # Is it a redirect with no history?
4224  if ( !$nt->isSingleRevRedirect() ) {
4225  wfDebug( __METHOD__ . ": not a one-rev redirect\n" );
4226  return false;
4227  }
4228  # Get the article text
4229  $rev = Revision::newFromTitle( $nt, false, Revision::READ_LATEST );
4230  if ( !is_object( $rev ) ) {
4231  return false;
4232  }
4233  $content = $rev->getContent();
4234  # Does the redirect point to the source?
4235  # Or is it a broken self-redirect, usually caused by namespace collisions?
4236  $redirTitle = $content ? $content->getRedirectTarget() : null;
4237 
4238  if ( $redirTitle ) {
4239  if ( $redirTitle->getPrefixedDBkey() != $this->getPrefixedDBkey() &&
4240  $redirTitle->getPrefixedDBkey() != $nt->getPrefixedDBkey() ) {
4241  wfDebug( __METHOD__ . ": redirect points to other page\n" );
4242  return false;
4243  } else {
4244  return true;
4245  }
4246  } else {
4247  # Fail safe (not a redirect after all. strange.)
4248  wfDebug( __METHOD__ . ": failsafe: database sais " . $nt->getPrefixedDBkey() .
4249  " is a redirect, but it doesn't contain a valid redirect.\n" );
4250  return false;
4251  }
4252  }
4253 
4261  public function getParentCategories() {
4262  $data = [];
4263 
4264  $titleKey = $this->getArticleID();
4265 
4266  if ( $titleKey === 0 ) {
4267  return $data;
4268  }
4269 
4270  $dbr = wfGetDB( DB_REPLICA );
4271 
4272  $res = $dbr->select(
4273  'categorylinks',
4274  'cl_to',
4275  [ 'cl_from' => $titleKey ],
4276  __METHOD__
4277  );
4278 
4279  if ( $res->numRows() > 0 ) {
4280  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
4281  foreach ( $res as $row ) {
4282  // $data[] = Title::newFromText( $contLang->getNsText ( NS_CATEGORY ).':'.$row->cl_to);
4283  $data[$contLang->getNsText( NS_CATEGORY ) . ':' . $row->cl_to] =
4284  $this->getFullText();
4285  }
4286  }
4287  return $data;
4288  }
4289 
4296  public function getParentCategoryTree( $children = [] ) {
4297  $stack = [];
4298  $parents = $this->getParentCategories();
4299 
4300  if ( $parents ) {
4301  foreach ( $parents as $parent => $current ) {
4302  if ( array_key_exists( $parent, $children ) ) {
4303  # Circular reference
4304  $stack[$parent] = [];
4305  } else {
4306  $nt = self::newFromText( $parent );
4307  if ( $nt ) {
4308  $stack[$parent] = $nt->getParentCategoryTree( $children + [ $parent => 1 ] );
4309  }
4310  }
4311  }
4312  }
4313 
4314  return $stack;
4315  }
4316 
4323  public function pageCond() {
4324  if ( $this->mArticleID > 0 ) {
4325  // PK avoids secondary lookups in InnoDB, shouldn't hurt other DBs
4326  return [ 'page_id' => $this->mArticleID ];
4327  } else {
4328  return [ 'page_namespace' => $this->mNamespace, 'page_title' => $this->mDbkeyform ];
4329  }
4330  }
4331 
4339  private function getRelativeRevisionID( $revId, $flags, $dir ) {
4340  $revId = (int)$revId;
4341  if ( $dir === 'next' ) {
4342  $op = '>';
4343  $sort = 'ASC';
4344  } elseif ( $dir === 'prev' ) {
4345  $op = '<';
4346  $sort = 'DESC';
4347  } else {
4348  throw new InvalidArgumentException( '$dir must be "next" or "prev"' );
4349  }
4350 
4351  if ( $flags & self::GAID_FOR_UPDATE ) {
4352  $db = wfGetDB( DB_MASTER );
4353  } else {
4354  $db = wfGetDB( DB_REPLICA, 'contributions' );
4355  }
4356 
4357  // Intentionally not caring if the specified revision belongs to this
4358  // page. We only care about the timestamp.
4359  $ts = $db->selectField( 'revision', 'rev_timestamp', [ 'rev_id' => $revId ], __METHOD__ );
4360  if ( $ts === false ) {
4361  $ts = $db->selectField( 'archive', 'ar_timestamp', [ 'ar_rev_id' => $revId ], __METHOD__ );
4362  if ( $ts === false ) {
4363  // Or should this throw an InvalidArgumentException or something?
4364  return false;
4365  }
4366  }
4367  $ts = $db->addQuotes( $ts );
4368 
4369  $revId = $db->selectField( 'revision', 'rev_id',
4370  [
4371  'rev_page' => $this->getArticleID( $flags ),
4372  "rev_timestamp $op $ts OR (rev_timestamp = $ts AND rev_id $op $revId)"
4373  ],
4374  __METHOD__,
4375  [
4376  'ORDER BY' => "rev_timestamp $sort, rev_id $sort",
4377  'IGNORE INDEX' => 'rev_timestamp', // Probably needed for T159319
4378  ]
4379  );
4380 
4381  if ( $revId === false ) {
4382  return false;
4383  } else {
4384  return intval( $revId );
4385  }
4386  }
4387 
4395  public function getPreviousRevisionID( $revId, $flags = 0 ) {
4396  return $this->getRelativeRevisionID( $revId, $flags, 'prev' );
4397  }
4398 
4406  public function getNextRevisionID( $revId, $flags = 0 ) {
4407  return $this->getRelativeRevisionID( $revId, $flags, 'next' );
4408  }
4409 
4416  public function getFirstRevision( $flags = 0 ) {
4417  $pageId = $this->getArticleID( $flags );
4418  if ( $pageId ) {
4419  $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
4421  $row = $db->selectRow( $revQuery['tables'], $revQuery['fields'],
4422  [ 'rev_page' => $pageId ],
4423  __METHOD__,
4424  [
4425  'ORDER BY' => 'rev_timestamp ASC, rev_id ASC',
4426  'IGNORE INDEX' => [ 'revision' => 'rev_timestamp' ], // See T159319
4427  ],
4428  $revQuery['joins']
4429  );
4430  if ( $row ) {
4431  return new Revision( $row, 0, $this );
4432  }
4433  }
4434  return null;
4435  }
4436 
4443  public function getEarliestRevTime( $flags = 0 ) {
4444  $rev = $this->getFirstRevision( $flags );
4445  return $rev ? $rev->getTimestamp() : null;
4446  }
4447 
4453  public function isNewPage() {
4454  $dbr = wfGetDB( DB_REPLICA );
4455  return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
4456  }
4457 
4463  public function isBigDeletion() {
4464  global $wgDeleteRevisionsLimit;
4465 
4466  if ( !$wgDeleteRevisionsLimit ) {
4467  return false;
4468  }
4469 
4470  if ( $this->mIsBigDeletion === null ) {
4471  $dbr = wfGetDB( DB_REPLICA );
4472 
4473  $revCount = $dbr->selectRowCount(
4474  'revision',
4475  '1',
4476  [ 'rev_page' => $this->getArticleID() ],
4477  __METHOD__,
4478  [ 'LIMIT' => $wgDeleteRevisionsLimit + 1 ]
4479  );
4480 
4481  $this->mIsBigDeletion = $revCount > $wgDeleteRevisionsLimit;
4482  }
4483 
4484  return $this->mIsBigDeletion;
4485  }
4486 
4492  public function estimateRevisionCount() {
4493  if ( !$this->exists() ) {
4494  return 0;
4495  }
4496 
4497  if ( $this->mEstimateRevisions === null ) {
4498  $dbr = wfGetDB( DB_REPLICA );
4499  $this->mEstimateRevisions = $dbr->estimateRowCount( 'revision', '*',
4500  [ 'rev_page' => $this->getArticleID() ], __METHOD__ );
4501  }
4502 
4504  }
4505 
4515  public function countRevisionsBetween( $old, $new, $max = null ) {
4516  if ( !( $old instanceof Revision ) ) {
4517  $old = Revision::newFromTitle( $this, (int)$old );
4518  }
4519  if ( !( $new instanceof Revision ) ) {
4520  $new = Revision::newFromTitle( $this, (int)$new );
4521  }
4522  if ( !$old || !$new ) {
4523  return 0; // nothing to compare
4524  }
4525  $dbr = wfGetDB( DB_REPLICA );
4526  $conds = [
4527  'rev_page' => $this->getArticleID(),
4528  'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4529  'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4530  ];
4531  if ( $max !== null ) {
4532  return $dbr->selectRowCount( 'revision', '1',
4533  $conds,
4534  __METHOD__,
4535  [ 'LIMIT' => $max + 1 ] // extra to detect truncation
4536  );
4537  } else {
4538  return (int)$dbr->selectField( 'revision', 'count(*)', $conds, __METHOD__ );
4539  }
4540  }
4541 
4558  public function getAuthorsBetween( $old, $new, $limit, $options = [] ) {
4559  if ( !( $old instanceof Revision ) ) {
4560  $old = Revision::newFromTitle( $this, (int)$old );
4561  }
4562  if ( !( $new instanceof Revision ) ) {
4563  $new = Revision::newFromTitle( $this, (int)$new );
4564  }
4565  // XXX: what if Revision objects are passed in, but they don't refer to this title?
4566  // Add $old->getPage() != $new->getPage() || $old->getPage() != $this->getArticleID()
4567  // in the sanity check below?
4568  if ( !$old || !$new ) {
4569  return null; // nothing to compare
4570  }
4571  $authors = [];
4572  $old_cmp = '>';
4573  $new_cmp = '<';
4574  $options = (array)$options;
4575  if ( in_array( 'include_old', $options ) ) {
4576  $old_cmp = '>=';
4577  }
4578  if ( in_array( 'include_new', $options ) ) {
4579  $new_cmp = '<=';
4580  }
4581  if ( in_array( 'include_both', $options ) ) {
4582  $old_cmp = '>=';
4583  $new_cmp = '<=';
4584  }
4585  // No DB query needed if $old and $new are the same or successive revisions:
4586  if ( $old->getId() === $new->getId() ) {
4587  return ( $old_cmp === '>' && $new_cmp === '<' ) ?
4588  [] :
4589  [ $old->getUserText( Revision::RAW ) ];
4590  } elseif ( $old->getId() === $new->getParentId() ) {
4591  if ( $old_cmp === '>=' && $new_cmp === '<=' ) {
4592  $authors[] = $old->getUserText( Revision::RAW );
4593  if ( $old->getUserText( Revision::RAW ) != $new->getUserText( Revision::RAW ) ) {
4594  $authors[] = $new->getUserText( Revision::RAW );
4595  }
4596  } elseif ( $old_cmp === '>=' ) {
4597  $authors[] = $old->getUserText( Revision::RAW );
4598  } elseif ( $new_cmp === '<=' ) {
4599  $authors[] = $new->getUserText( Revision::RAW );
4600  }
4601  return $authors;
4602  }
4603  $dbr = wfGetDB( DB_REPLICA );
4605  $authors = $dbr->selectFieldValues(
4606  $revQuery['tables'],
4607  $revQuery['fields']['rev_user_text'],
4608  [
4609  'rev_page' => $this->getArticleID(),
4610  "rev_timestamp $old_cmp " . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4611  "rev_timestamp $new_cmp " . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4612  ], __METHOD__,
4613  [ 'DISTINCT', 'LIMIT' => $limit + 1 ], // add one so caller knows it was truncated
4614  $revQuery['joins']
4615  );
4616  return $authors;
4617  }
4618 
4633  public function countAuthorsBetween( $old, $new, $limit, $options = [] ) {
4634  $authors = $this->getAuthorsBetween( $old, $new, $limit, $options );
4635  return $authors ? count( $authors ) : 0;
4636  }
4637 
4644  public function equals( Title $title ) {
4645  // Note: === is necessary for proper matching of number-like titles.
4646  return $this->mInterwiki === $title->mInterwiki
4647  && $this->mNamespace == $title->mNamespace
4648  && $this->mDbkeyform === $title->mDbkeyform;
4649  }
4650 
4657  public function isSubpageOf( Title $title ) {
4658  return $this->mInterwiki === $title->mInterwiki
4659  && $this->mNamespace == $title->mNamespace
4660  && strpos( $this->mDbkeyform, $title->mDbkeyform . '/' ) === 0;
4661  }
4662 
4674  public function exists( $flags = 0 ) {
4675  $exists = $this->getArticleID( $flags ) != 0;
4676  Hooks::run( 'TitleExists', [ $this, &$exists ] );
4677  return $exists;
4678  }
4679 
4696  public function isAlwaysKnown() {
4697  $isKnown = null;
4698 
4709  Hooks::run( 'TitleIsAlwaysKnown', [ $this, &$isKnown ] );
4710 
4711  if ( !is_null( $isKnown ) ) {
4712  return $isKnown;
4713  }
4714 
4715  if ( $this->isExternal() ) {
4716  return true; // any interwiki link might be viewable, for all we know
4717  }
4718 
4719  switch ( $this->mNamespace ) {
4720  case NS_MEDIA:
4721  case NS_FILE:
4722  // file exists, possibly in a foreign repo
4723  return (bool)wfFindFile( $this );
4724  case NS_SPECIAL:
4725  // valid special page
4726  return MediaWikiServices::getInstance()->getSpecialPageFactory()->
4727  exists( $this->mDbkeyform );
4728  case NS_MAIN:
4729  // selflink, possibly with fragment
4730  return $this->mDbkeyform == '';
4731  case NS_MEDIAWIKI:
4732  // known system message
4733  return $this->hasSourceText() !== false;
4734  default:
4735  return false;
4736  }
4737  }
4738 
4750  public function isKnown() {
4751  return $this->isAlwaysKnown() || $this->exists();
4752  }
4753 
4759  public function hasSourceText() {
4760  if ( $this->exists() ) {
4761  return true;
4762  }
4763 
4764  if ( $this->mNamespace == NS_MEDIAWIKI ) {
4765  // If the page doesn't exist but is a known system message, default
4766  // message content will be displayed, same for language subpages-
4767  // Use always content language to avoid loading hundreds of languages
4768  // to get the link color.
4769  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
4770  list( $name, ) = MessageCache::singleton()->figureMessage(
4771  $contLang->lcfirst( $this->getText() )
4772  );
4773  $message = wfMessage( $name )->inLanguage( $contLang )->useDatabase( false );
4774  return $message->exists();
4775  }
4776 
4777  return false;
4778  }
4779 
4817  public function getDefaultMessageText() {
4818  if ( $this->mNamespace != NS_MEDIAWIKI ) { // Just in case
4819  return false;
4820  }
4821 
4822  list( $name, $lang ) = MessageCache::singleton()->figureMessage(
4823  MediaWikiServices::getInstance()->getContentLanguage()->lcfirst( $this->getText() )
4824  );
4825  $message = wfMessage( $name )->inLanguage( $lang )->useDatabase( false );
4826 
4827  if ( $message->exists() ) {
4828  return $message->plain();
4829  } else {
4830  return false;
4831  }
4832  }
4833 
4840  public function invalidateCache( $purgeTime = null ) {
4841  if ( wfReadOnly() ) {
4842  return false;
4843  } elseif ( $this->mArticleID === 0 ) {
4844  return true; // avoid gap locking if we know it's not there
4845  }
4846 
4847  $dbw = wfGetDB( DB_MASTER );
4848  $dbw->onTransactionPreCommitOrIdle(
4849  function () use ( $dbw ) {
4851  $this, null, null, $dbw->getDomainId() );
4852  },
4853  __METHOD__
4854  );
4855 
4856  $conds = $this->pageCond();
4858  new AutoCommitUpdate(
4859  $dbw,
4860  __METHOD__,
4861  function ( IDatabase $dbw, $fname ) use ( $conds, $purgeTime ) {
4862  $dbTimestamp = $dbw->timestamp( $purgeTime ?: time() );
4863  $dbw->update(
4864  'page',
4865  [ 'page_touched' => $dbTimestamp ],
4866  $conds + [ 'page_touched < ' . $dbw->addQuotes( $dbTimestamp ) ],
4867  $fname
4868  );
4869  MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $this );
4870  }
4871  ),
4873  );
4874 
4875  return true;
4876  }
4877 
4883  public function touchLinks() {
4884  DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this, 'pagelinks', 'page-touch' ) );
4885  if ( $this->mNamespace == NS_CATEGORY ) {
4887  new HTMLCacheUpdate( $this, 'categorylinks', 'category-touch' )
4888  );
4889  }
4890  }
4891 
4898  public function getTouched( $db = null ) {
4899  if ( $db === null ) {
4900  $db = wfGetDB( DB_REPLICA );
4901  }
4902  $touched = $db->selectField( 'page', 'page_touched', $this->pageCond(), __METHOD__ );
4903  return $touched;
4904  }
4905 
4912  public function getNotificationTimestamp( $user = null ) {
4913  global $wgUser;
4914 
4915  // Assume current user if none given
4916  if ( !$user ) {
4917  $user = $wgUser;
4918  }
4919  // Check cache first
4920  $uid = $user->getId();
4921  if ( !$uid ) {
4922  return false;
4923  }
4924  // avoid isset here, as it'll return false for null entries
4925  if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) {
4926  return $this->mNotificationTimestamp[$uid];
4927  }
4928  // Don't cache too much!
4929  if ( count( $this->mNotificationTimestamp ) >= self::CACHE_MAX ) {
4930  $this->mNotificationTimestamp = [];
4931  }
4932 
4933  $store = MediaWikiServices::getInstance()->getWatchedItemStore();
4934  $watchedItem = $store->getWatchedItem( $user, $this );
4935  if ( $watchedItem ) {
4936  $this->mNotificationTimestamp[$uid] = $watchedItem->getNotificationTimestamp();
4937  } else {
4938  $this->mNotificationTimestamp[$uid] = false;
4939  }
4940 
4941  return $this->mNotificationTimestamp[$uid];
4942  }
4943 
4950  public function getNamespaceKey( $prepend = 'nstab-' ) {
4951  // Gets the subject namespace of this title
4952  $subjectNS = MWNamespace::getSubject( $this->mNamespace );
4953  // Prefer canonical namespace name for HTML IDs
4954  $namespaceKey = MWNamespace::getCanonicalName( $subjectNS );
4955  if ( $namespaceKey === false ) {
4956  // Fallback to localised text
4957  $namespaceKey = $this->getSubjectNsText();
4958  }
4959  // Makes namespace key lowercase
4960  $namespaceKey = MediaWikiServices::getInstance()->getContentLanguage()->lc( $namespaceKey );
4961  // Uses main
4962  if ( $namespaceKey == '' ) {
4963  $namespaceKey = 'main';
4964  }
4965  // Changes file to image for backwards compatibility
4966  if ( $namespaceKey == 'file' ) {
4967  $namespaceKey = 'image';
4968  }
4969  return $prepend . $namespaceKey;
4970  }
4971 
4978  public function getRedirectsHere( $ns = null ) {
4979  $redirs = [];
4980 
4981  $dbr = wfGetDB( DB_REPLICA );
4982  $where = [
4983  'rd_namespace' => $this->mNamespace,
4984  'rd_title' => $this->mDbkeyform,
4985  'rd_from = page_id'
4986  ];
4987  if ( $this->isExternal() ) {
4988  $where['rd_interwiki'] = $this->mInterwiki;
4989  } else {
4990  $where[] = 'rd_interwiki = ' . $dbr->addQuotes( '' ) . ' OR rd_interwiki IS NULL';
4991  }
4992  if ( !is_null( $ns ) ) {
4993  $where['page_namespace'] = $ns;
4994  }
4995 
4996  $res = $dbr->select(
4997  [ 'redirect', 'page' ],
4998  [ 'page_namespace', 'page_title' ],
4999  $where,
5000  __METHOD__
5001  );
5002 
5003  foreach ( $res as $row ) {
5004  $redirs[] = self::newFromRow( $row );
5005  }
5006  return $redirs;
5007  }
5008 
5014  public function isValidRedirectTarget() {
5016 
5017  if ( $this->isSpecialPage() ) {
5018  // invalid redirect targets are stored in a global array, but explicitly disallow Userlogout here
5019  if ( $this->isSpecial( 'Userlogout' ) ) {
5020  return false;
5021  }
5022 
5023  foreach ( $wgInvalidRedirectTargets as $target ) {
5024  if ( $this->isSpecial( $target ) ) {
5025  return false;
5026  }
5027  }
5028  }
5029 
5030  return true;
5031  }
5032 
5038  public function getBacklinkCache() {
5039  return BacklinkCache::get( $this );
5040  }
5041 
5047  public function canUseNoindex() {
5049 
5050  $bannedNamespaces = is_null( $wgExemptFromUserRobotsControl )
5053 
5054  return !in_array( $this->mNamespace, $bannedNamespaces );
5055  }
5056 
5067  public function getCategorySortkey( $prefix = '' ) {
5068  $unprefixed = $this->getText();
5069 
5070  // Anything that uses this hook should only depend
5071  // on the Title object passed in, and should probably
5072  // tell the users to run updateCollations.php --force
5073  // in order to re-sort existing category relations.
5074  Hooks::run( 'GetDefaultSortkey', [ $this, &$unprefixed ] );
5075  if ( $prefix !== '' ) {
5076  # Separate with a line feed, so the unprefixed part is only used as
5077  # a tiebreaker when two pages have the exact same prefix.
5078  # In UCA, tab is the only character that can sort above LF
5079  # so we strip both of them from the original prefix.
5080  $prefix = strtr( $prefix, "\n\t", ' ' );
5081  return "$prefix\n$unprefixed";
5082  }
5083  return $unprefixed;
5084  }
5085 
5093  private function getDbPageLanguageCode() {
5094  global $wgPageLanguageUseDB;
5095 
5096  // check, if the page language could be saved in the database, and if so and
5097  // the value is not requested already, lookup the page language using LinkCache
5098  if ( $wgPageLanguageUseDB && $this->mDbPageLanguage === false ) {
5099  $linkCache = MediaWikiServices::getInstance()->getLinkCache();
5100  $linkCache->addLinkObj( $this );
5101  $this->mDbPageLanguage = $linkCache->getGoodLinkFieldObj( $this, 'lang' );
5102  }
5103 
5104  return $this->mDbPageLanguage;
5105  }
5106 
5115  public function getPageLanguage() {
5116  global $wgLang, $wgLanguageCode;
5117  if ( $this->isSpecialPage() ) {
5118  // special pages are in the user language
5119  return $wgLang;
5120  }
5121 
5122  // Checking if DB language is set
5123  $dbPageLanguage = $this->getDbPageLanguageCode();
5124  if ( $dbPageLanguage ) {
5125  return wfGetLangObj( $dbPageLanguage );
5126  }
5127 
5128  if ( !$this->mPageLanguage || $this->mPageLanguage[1] !== $wgLanguageCode ) {
5129  // Note that this may depend on user settings, so the cache should
5130  // be only per-request.
5131  // NOTE: ContentHandler::getPageLanguage() may need to load the
5132  // content to determine the page language!
5133  // Checking $wgLanguageCode hasn't changed for the benefit of unit
5134  // tests.
5135  $contentHandler = ContentHandler::getForTitle( $this );
5136  $langObj = $contentHandler->getPageLanguage( $this );
5137  $this->mPageLanguage = [ $langObj->getCode(), $wgLanguageCode ];
5138  } else {
5139  $langObj = Language::factory( $this->mPageLanguage[0] );
5140  }
5141 
5142  return $langObj;
5143  }
5144 
5153  public function getPageViewLanguage() {
5154  global $wgLang;
5155 
5156  if ( $this->isSpecialPage() ) {
5157  // If the user chooses a variant, the content is actually
5158  // in a language whose code is the variant code.
5159  $variant = $wgLang->getPreferredVariant();
5160  if ( $wgLang->getCode() !== $variant ) {
5161  return Language::factory( $variant );
5162  }
5163 
5164  return $wgLang;
5165  }
5166 
5167  // Checking if DB language is set
5168  $dbPageLanguage = $this->getDbPageLanguageCode();
5169  if ( $dbPageLanguage ) {
5170  $pageLang = wfGetLangObj( $dbPageLanguage );
5171  $variant = $pageLang->getPreferredVariant();
5172  if ( $pageLang->getCode() !== $variant ) {
5173  $pageLang = Language::factory( $variant );
5174  }
5175 
5176  return $pageLang;
5177  }
5178 
5179  // @note Can't be cached persistently, depends on user settings.
5180  // @note ContentHandler::getPageViewLanguage() may need to load the
5181  // content to determine the page language!
5182  $contentHandler = ContentHandler::getForTitle( $this );
5183  $pageLang = $contentHandler->getPageViewLanguage( $this );
5184  return $pageLang;
5185  }
5186 
5197  public function getEditNotices( $oldid = 0 ) {
5198  $notices = [];
5199 
5200  // Optional notice for the entire namespace
5201  $editnotice_ns = 'editnotice-' . $this->mNamespace;
5202  $msg = wfMessage( $editnotice_ns );
5203  if ( $msg->exists() ) {
5204  $html = $msg->parseAsBlock();
5205  // Edit notices may have complex logic, but output nothing (T91715)
5206  if ( trim( $html ) !== '' ) {
5207  $notices[$editnotice_ns] = Html::rawElement(
5208  'div',
5209  [ 'class' => [
5210  'mw-editnotice',
5211  'mw-editnotice-namespace',
5212  Sanitizer::escapeClass( "mw-$editnotice_ns" )
5213  ] ],
5214  $html
5215  );
5216  }
5217  }
5218 
5219  if ( MWNamespace::hasSubpages( $this->mNamespace ) ) {
5220  // Optional notice for page itself and any parent page
5221  $parts = explode( '/', $this->mDbkeyform );
5222  $editnotice_base = $editnotice_ns;
5223  while ( count( $parts ) > 0 ) {
5224  $editnotice_base .= '-' . array_shift( $parts );
5225  $msg = wfMessage( $editnotice_base );
5226  if ( $msg->exists() ) {
5227  $html = $msg->parseAsBlock();
5228  if ( trim( $html ) !== '' ) {
5229  $notices[$editnotice_base] = Html::rawElement(
5230  'div',
5231  [ 'class' => [
5232  'mw-editnotice',
5233  'mw-editnotice-base',
5234  Sanitizer::escapeClass( "mw-$editnotice_base" )
5235  ] ],
5236  $html
5237  );
5238  }
5239  }
5240  }
5241  } else {
5242  // Even if there are no subpages in namespace, we still don't want "/" in MediaWiki message keys
5243  $editnoticeText = $editnotice_ns . '-' . strtr( $this->mDbkeyform, '/', '-' );
5244  $msg = wfMessage( $editnoticeText );
5245  if ( $msg->exists() ) {
5246  $html = $msg->parseAsBlock();
5247  if ( trim( $html ) !== '' ) {
5248  $notices[$editnoticeText] = Html::rawElement(
5249  'div',
5250  [ 'class' => [
5251  'mw-editnotice',
5252  'mw-editnotice-page',
5253  Sanitizer::escapeClass( "mw-$editnoticeText" )
5254  ] ],
5255  $html
5256  );
5257  }
5258  }
5259  }
5260 
5261  Hooks::run( 'TitleGetEditNotices', [ $this, $oldid, &$notices ] );
5262  return $notices;
5263  }
5264 
5268  public function __sleep() {
5269  return [
5270  'mNamespace',
5271  'mDbkeyform',
5272  'mFragment',
5273  'mInterwiki',
5274  'mLocalInterwiki',
5275  'mUserCaseDBKey',
5276  'mDefaultNamespace',
5277  ];
5278  }
5279 
5280  public function __wakeup() {
5281  $this->mArticleID = ( $this->mNamespace >= 0 ) ? -1 : 0;
5282  $this->mUrlform = wfUrlencode( $this->mDbkeyform );
5283  $this->mTextform = strtr( $this->mDbkeyform, '_', ' ' );
5284  }
5285 
5286 }
Title\$mLocalInterwiki
bool $mLocalInterwiki
Was this Title created from a string with a local interwiki prefix?
Definition: Title.php:82
Title\$mRestrictions
array $mRestrictions
Array of groups allowed to edit this article.
Definition: Title.php:109
Title\canHaveTalkPage
canHaveTalkPage()
Can this title have a corresponding talk page?
Definition: Title.php:1090
Title\getTitleProtection
getTitleProtection()
Is this title subject to title protection? Title protection is the one applied against creation of su...
Definition: Title.php:2922
Title\canUseNoindex
canUseNoindex()
Whether the magic words INDEX and NOINDEX function for this page.
Definition: Title.php:5047
$status
Status::newGood()` to allow deletion, and then `return false` from the hook function. Ensure you consume the 'ChangeTagAfterDelete' hook to carry out custom deletion actions. $tag:name of the tag $user:user initiating the action & $status:Status object. See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use. & $tags:list of all active tags. Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function. Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action, or null $user:User who performed the tagging when the tagging is subsequent to the action, or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change. & $allowedTags:List of all the tags the user is allowed to add. Any tags the user wants to add( $addTags) that are not in this array will cause it to fail. You may add or remove tags to this array as required. $addTags:List of tags user intends to add. $user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed. $performer:The User who will perform the change $user:The User whose groups will be changed & $add:The groups that will be added & $remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation. $collationName:Name of the collation in question & $collationObject:Null. Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user 's email has been confirmed successfully. $user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object. Called by Content::getParserOutput after parsing has finished. Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML). $content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput. May be used to override the normal model-specific rendering of page content. $content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering. To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state. $generateHtml:boolean, indicating whether full HTML should be generated. If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object. & $output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title. May be used to assign a different model for that title. $title:the Title in question & $model:the model name. Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers. Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core. $modeName:the requested content model name & $handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page. This is especially useful to prevent some content models to be used in some special location. $contentModel:ID of the content model in question $title:the Title in question. & $ok:Output parameter, whether it is OK to use $contentModel on $title. Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok. 'ContribsPager::getQueryInfo':Before the contributions query is about to run & $pager:Pager object for contributions & $queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions & $data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title & $tools:Array of tool links $specialPage:SpecialPage instance for context and services. Can be either SpecialContributions or DeletedContributionsPage. Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested. Handler functions that modify $result should generally return false to disable further attempts at conversion. $content:The Content object to be converted. $toModel:The ID of the content model to convert to. $lossy:boolean indicating whether lossy conversion is allowed. & $result:Output parameter, in case the handler function wants to provide a converted Content object. Note that $result->getContentModel() must return $toModel. 'ContentSecurityPolicyDefaultSource':Modify the allowed CSP load sources. This affects all directives except for the script directive. If you want to add a script source, see ContentSecurityPolicyScriptSource hook. & $defaultSrc:Array of Content-Security-Policy allowed sources $policyConfig:Current configuration for the Content-Security-Policy header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'ContentSecurityPolicyDirectives':Modify the content security policy directives. Use this only if ContentSecurityPolicyDefaultSource and ContentSecurityPolicyScriptSource do not meet your needs. & $directives:Array of CSP directives $policyConfig:Current configuration for the CSP header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'ContentSecurityPolicyScriptSource':Modify the allowed CSP script sources. Note that you also have to use ContentSecurityPolicyDefaultSource if you want non-script sources to be loaded from whatever you add. & $scriptSrc:Array of CSP directives $policyConfig:Current configuration for the CSP header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g. for a special namespace, etc. $article:Article being edited $user:User performing the edit 'DatabaseOraclePostInit':Called after initialising an Oracle database $db:the DatabaseOracle object 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery & $data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished. Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DeleteUnknownPreferences':Called by the cleanupPreferences.php maintenance script to build a WHERE clause with which to delete preferences that are not known about. This hook is used by extensions that have dynamically-named preferences that should not be deleted in the usual cleanup process. For example, the Gadgets extension creates preferences prefixed with 'gadget-', and so anything with that prefix is excluded from the deletion. &where:An array that will be passed as the $cond parameter to IDatabase::select() to determine what will be deleted from the user_properties table. $db:The IDatabase object, useful for accessing $db->buildLike() etc. 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function. $differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable 's value is null. This hook can be used to inject content into said class member variable. $differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the "mark as patrolled" link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink". $differenceEngine:DifferenceEngine object & $markAsPatrolledLink:The "mark as patrolled" link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter. For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions. & $rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision 's author, whether the revision was marked as a minor edit or not, etc. $differenceEngine:DifferenceEngine object & $newHeader:The string containing the various #mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition: hooks.txt:1305
Title\inNamespaces
inNamespaces()
Returns true if the title is inside one of the specified namespaces.
Definition: Title.php:1180
Title\getLocalURL
getLocalURL( $query='', $query2=false)
Get a URL with no fragment or server name (relative URL) from a Title object.
Definition: Title.php:1980
CONTENT_MODEL_JSON
const CONTENT_MODEL_JSON
Definition: Defines.php:239
Title\isSemiProtected
isSemiProtected( $action='edit')
Is this page "semi-protected" - the only protection levels are listed in $wgSemiprotectedRestrictionL...
Definition: Title.php:3010
Title\$mHasCascadingRestrictions
bool $mHasCascadingRestrictions
Are cascading restrictions in effect on this page?
Definition: Title.php:129
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:48
Title\isNamespaceProtected
isNamespaceProtected(User $user)
Determines if $user is unable to edit this page because it has been protected by $wgNamespaceProtecti...
Definition: Title.php:3070
Title\getTalkNsText
getTalkNsText()
Get the namespace text of the talk page.
Definition: Title.php:1066
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:244
wfMergeErrorArrays
wfMergeErrorArrays(... $args)
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
Definition: GlobalFunctions.php:203
Title\getSubpageText
getSubpageText()
Get the lowest-level subpage name, i.e.
Definition: Title.php:1813
MediaWiki\Linker\LinkTarget\getInterwiki
getInterwiki()
The interwiki component of this LinkTarget.
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:280
MWNamespace\subjectEquals
static subjectEquals( $ns1, $ns2)
Returns whether the specified namespaces share the same subject.
Definition: MWNamespace.php:216
Title\getNextRevisionID
getNextRevisionID( $revId, $flags=0)
Get the revision ID of the next revision.
Definition: Title.php:4406
Title\isBigDeletion
isBigDeletion()
Check whether the number of revisions of this page surpasses $wgDeleteRevisionsLimit.
Definition: Title.php:4463
PROTO_CANONICAL
const PROTO_CANONICAL
Definition: Defines.php:223
Title\areCascadeProtectionSourcesLoaded
areCascadeProtectionSourcesLoaded( $getPages=true)
Determines whether cascading protection sources have already been loaded from the database.
Definition: Title.php:3102
MediaWiki\Linker\LinkTarget\getText
getText()
Returns the link in text form, without namespace prefix or fragment.
Title\userCan
userCan( $action, $user=null, $rigor='secure')
Can $user perform $action on this page?
Definition: Title.php:2178
Title\getFilteredRestrictionTypes
static getFilteredRestrictionTypes( $exists=true)
Get a filtered list of all restriction types supported by this wiki.
Definition: Title.php:2877
Title\makeName
static makeName( $ns, $title, $fragment='', $interwiki='', $canonicalNamespace=false)
Make a prefixed DB key from a DB key and a namespace index.
Definition: Title.php:752
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
Title\getInternalURL
getInternalURL( $query='', $query2=false)
Get the URL form for an internal link.
Definition: Title.php:2104
$wgActionPaths
$wgActionPaths
Definition: img_auth.php:47
Title\checkSiteConfigPermissions
checkSiteConfigPermissions( $action, $user, $errors, $rigor, $short)
Check sitewide CSS/JSON/JS permissions.
Definition: Title.php:2400
Title\checkPageRestrictions
checkPageRestrictions( $action, $user, $errors, $rigor, $short)
Check against page_restrictions table requirements on this page.
Definition: Title.php:2512
Title\getFragment
getFragment()
Get the Title fragment (i.e.
Definition: Title.php:1587
MWNamespace\isTalk
static isTalk( $index)
Is the given namespace a talk namespace?
Definition: MWNamespace.php:118
$wgExemptFromUserRobotsControl
$wgExemptFromUserRobotsControl
An array of namespace keys in which the INDEX/__NOINDEX__ magic words will not function,...
Definition: DefaultSettings.php:8080
Title\getTalkPageIfDefined
getTalkPageIfDefined()
Get a Title object associated with the talk page of this article, if such a talk page can exist.
Definition: Title.php:1526
Title\inNamespace
inNamespace( $ns)
Returns true if the title is inside the specified namespace.
Definition: Title.php:1169
Title\$mForcedContentModel
bool $mForcedContentModel
If a content model was forced via setContentModel() this will be true to avoid having other code path...
Definition: Title.php:103
TitleArray\newFromResult
static newFromResult( $res)
Definition: TitleArray.php:40
Title\isMovable
isMovable()
Would anybody with sufficient privileges be able to move this page? Some pages just aren't movable.
Definition: Title.php:1229
$wgMaximumMovedPages
$wgMaximumMovedPages
Maximum number of pages to move at once when moving subpages with a page.
Definition: DefaultSettings.php:8536
Title\isJsSubpage
isJsSubpage()
Definition: Title.php:1424
Title\wasLocalInterwiki
wasLocalInterwiki()
Was this a local interwiki link?
Definition: Title.php:870
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
Title\isWikitextPage
isWikitextPage()
Does that page contain wikitext, or it is JS, CSS or whatever?
Definition: Title.php:1282
Title\isUserCssConfigPage
isUserCssConfigPage()
Is this a CSS "config" sub-page of a user page?
Definition: Title.php:1375
Title\getBacklinkCache
getBacklinkCache()
Get a backlink cache object.
Definition: Title.php:5038
User\newFatalPermissionDeniedStatus
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
Definition: User.php:5697
Title\getPartialURL
getPartialURL()
Get the URL-encoded form of the main part.
Definition: Title.php:942
$wgLegalTitleChars
$wgLegalTitleChars
Allowed title characters – regex character class Don't change this unless you know what you're doing.
Definition: DefaultSettings.php:3930
Title\$mIsBigDeletion
bool $mIsBigDeletion
Would deleting this page be a big deletion?
Definition: Title.php:180
Title\clearCaches
static clearCaches()
Text form (spaces not underscores) of the main part.
Definition: Title.php:3705
AutoCommitUpdate
Deferrable Update for closure/callback updates that should use auto-commit mode.
Definition: AutoCommitUpdate.php:9
Title\moveSubpages
moveSubpages( $nt, $auth=true, $reason='', $createRedirect=true, array $changeTags=[])
Move this page's subpages to be subpages of $nt.
Definition: Title.php:4089
Title\getTitleValue
getTitleValue()
Get a TitleValue object representing this Title.
Definition: Title.php:910
Title\estimateRevisionCount
estimateRevisionCount()
Get the approximate revision count of this page.
Definition: Title.php:4492
Title\countRevisionsBetween
countRevisionsBetween( $old, $new, $max=null)
Get the number of revisions between the given revision.
Definition: Title.php:4515
captcha-old.count
count
Definition: captcha-old.py:249
Title\getPrefixedDBkey
getPrefixedDBkey()
Get the prefixed database key form.
Definition: Title.php:1682
Title\getDbPageLanguageCode
getDbPageLanguageCode()
Returns the page language code saved in the database, if $wgPageLanguageUseDB is set to true in Local...
Definition: Title.php:5093
$wgWhitelistReadRegexp
$wgWhitelistReadRegexp
Pages anonymous user may see, set as an array of regular expressions.
Definition: DefaultSettings.php:5088
Title\checkActionPermissions
checkActionPermissions( $action, $user, $errors, $rigor, $short)
Check action permissions not already checked in checkQuickPermissions.
Definition: Title.php:2593
Title\getSkinFromConfigSubpage
getSkinFromConfigSubpage()
Trim down a .css, .json, or .js subpage title to get the corresponding skin name.
Definition: Title.php:1350
$wgScript
$wgScript
The URL path to index.php.
Definition: DefaultSettings.php:185
Title\$mUserCaseDBKey
string $mUserCaseDBKey
Database key with the initial letter in the case specified by the user.
Definition: Title.php:73
Title\$mHasSubpages
bool $mHasSubpages
Whether a page has any subpages.
Definition: Title.php:167
Title\getTalkPage
getTalkPage()
Get a Title object associated with the talk page of this article.
Definition: Title.php:1513
Title\newMainPage
static newMainPage()
Create a new Title for the Main Page.
Definition: Title.php:597
Title\getNotificationTimestamp
getNotificationTimestamp( $user=null)
Get the timestamp when this page was updated since the user last saw it.
Definition: Title.php:4912
Title\getParentCategoryTree
getParentCategoryTree( $children=[])
Get a tree of parent categories.
Definition: Title.php:4296
CONTENT_MODEL_CSS
const CONTENT_MODEL_CSS
Definition: Defines.php:237
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED since 1.16! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED since 1.28! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:2034
Title\fixUrlQueryArgs
static fixUrlQueryArgs( $query, $query2=false)
Helper to fix up the get{Canonical,Full,Link,Local,Internal}URL args get{Canonical,...
Definition: Title.php:1873
$namespaces
namespace and then decline to actually register it & $namespaces
Definition: hooks.txt:964
Title\checkSpecialsAndNSPermissions
checkSpecialsAndNSPermissions( $action, $user, $errors, $rigor, $short)
Check permissions on special pages & namespaces.
Definition: Title.php:2371
Title\fixSpecialName
fixSpecialName()
If the Title refers to a special page alias which is not the local default, resolve the alias,...
Definition: Title.php:1145
Title\getInterwikiLookup
static getInterwikiLookup()
B/C kludge: provide an InterwikiLookup for use by Title.
Definition: Title.php:203
Title\isUserJsConfigPage
isUserJsConfigPage()
Is this a JS "config" sub-page of a user page?
Definition: Title.php:1412
MessageSpecifier
Definition: MessageSpecifier.php:21
Title\touchLinks
touchLinks()
Update page_touched timestamps and send CDN purge messages for pages linking to this title.
Definition: Title.php:4883
wfUrlencode
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
Definition: GlobalFunctions.php:331
$tables
this hook is for auditing only RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist & $tables
Definition: hooks.txt:1018
Title\getPrefixedText
getPrefixedText()
Get the prefixed title with spaces.
Definition: Title.php:1694
Title\getTransWikiID
getTransWikiID()
Returns the DB name of the distant wiki which owns the object.
Definition: Title.php:893
Title\resultToError
resultToError( $errors, $result)
Add the resulting error code to the errors array.
Definition: Title.php:2303
Title\isCssJsSubpage
isCssJsSubpage()
Definition: Title.php:1337
DeferredUpdates\addUpdate
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the deferred list to be run later by execute()
Definition: DeferredUpdates.php:79
Title\getArticleID
getArticleID( $flags=0)
Get the article ID for this Title from the link cache, adding it if necessary.
Definition: Title.php:3562
StringUtils\escapeRegexReplacement
static escapeRegexReplacement( $string)
Escape a string to make it suitable for inclusion in a preg_replace() replacement parameter.
Definition: StringUtils.php:323
Title\getSquidURLs
getSquidURLs()
Definition: Title.php:3971
Title\getLinkURL
getLinkURL( $query='', $query2=false, $proto=false)
Get a URL that's the simplest URL that will be valid to link, locally, to the current Title.
Definition: Title.php:2079
Title\secureAndSplit
secureAndSplit()
Secure and split - main initialisation function for this object.
Definition: Title.php:3740
Title\quickUserCan
quickUserCan( $action, $user=null)
Can $user perform $action on this page? This skips potentially expensive cascading permission checks ...
Definition: Title.php:2165
NS_FILE
const NS_FILE
Definition: Defines.php:70
Title\isTalkPage
isTalkPage()
Is this a talk page of some sort?
Definition: Title.php:1504
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1237
$s
$s
Definition: mergeMessageFileList.php:187
SpecialPage\getTitleFor
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Definition: SpecialPage.php:82
ContentHandler\getForTitle
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
Definition: ContentHandler.php:244
$res
$res
Definition: database.txt:21
Title\isExternal
isExternal()
Is this Title interwiki?
Definition: Title.php:850
Title\loadRestrictionsFromRows
loadRestrictionsFromRows( $rows, $oldFashionedRestrictions=null)
Compiles list of active page restrictions from both page table (pre 1.10) and page_restrictions table...
Definition: Title.php:3277
$success
$success
Definition: NoLocalSettings.php:42
Title\getDefaultMessageText
getDefaultMessageText()
Get the default (plain) message contents for an page that overrides an interface message key.
Definition: Title.php:4817
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:235
Title\hasSourceText
hasSourceText()
Does this page have source text?
Definition: Title.php:4759
Title\loadFromRow
loadFromRow( $row)
Load Title object fields from a DB row.
Definition: Title.php:487
User\groupHasPermission
static groupHasPermission( $group, $role)
Check, if the given group has the given permission.
Definition: User.php:5014
Title\checkReadPermissions
checkReadPermissions( $action, $user, $errors, $rigor, $short)
Check that the user is allowed to read this page.
Definition: Title.php:2715
Title\resetArticleID
resetArticleID( $newid)
This clears some fields in this object, and clears any associated keys in the "bad links" section of ...
Definition: Title.php:3683
Title\setFragment
setFragment( $fragment)
Set the fragment for this title.
Definition: Title.php:1629
names
alter the names
Definition: COPYING.txt:329
MWNamespace\getContentNamespaces
static getContentNamespaces()
Get a list of all namespace indices which are considered to contain content.
Definition: MWNamespace.php:373
$wgContentHandlerUseDB
$wgContentHandlerUseDB
Set to false to disable use of the database fields introduced by the ContentHandler facility.
Definition: DefaultSettings.php:8624
Title\convertByteClassToUnicodeClass
static convertByteClassToUnicodeClass( $byteClass)
Utility method for converting a character sequence from bytes to Unicode.
Definition: Title.php:648
Title\__wakeup
__wakeup()
Text form (spaces not underscores) of the main part.
Definition: Title.php:5280
$revQuery
$revQuery
Definition: testCompression.php:51
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
Title\getParentCategories
getParentCategories()
Get categories to which this Title belongs and return an array of categories' names.
Definition: Title.php:4261
Title\getLinksTo
getLinksTo( $options=[], $table='pagelinks', $prefix='pl')
Get an array of Title objects linking to this Title Also stores the IDs in the link cache.
Definition: Title.php:3781
Title\$mContentModel
bool string $mContentModel
ID of the page's content model, i.e.
Definition: Title.php:97
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
Title\isSiteCssConfigPage
isSiteCssConfigPage()
Is this a sitewide CSS "config" page?
Definition: Title.php:1435
Title\getNsText
getNsText()
Get the namespace text.
Definition: Title.php:1032
Title\$mDbkeyform
string $mDbkeyform
Main part with underscores.
Definition: Title.php:70
Title\$mUrlform
string $mUrlform
URL-encoded form of the main part.
Definition: Title.php:67
$dbr
$dbr
Definition: testCompression.php:50
$wgWhitelistRead
$wgWhitelistRead
Pages anonymous user may see, set as an array of pages titles.
Definition: DefaultSettings.php:5060
Title\getLinksFrom
getLinksFrom( $options=[], $table='pagelinks', $prefix='pl')
Get an array of Title objects linked from this Title Also stores the IDs in the link cache.
Definition: Title.php:3839
Title\$mOldRestrictions
string bool $mOldRestrictions
Comma-separated set of permission keys indicating who can move or edit the page from the page table,...
Definition: Title.php:117
Title\getSkinFromCssJsSubpage
getSkinFromCssJsSubpage()
Definition: Title.php:1364
wfAppendQuery
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
Definition: GlobalFunctions.php:460
Revision
Definition: Revision.php:41
NS_MAIN
const NS_MAIN
Definition: Defines.php:64
Title\isCascadeProtected
isCascadeProtected()
Cascading protection: Return true if cascading restrictions apply to this page, false if not.
Definition: Title.php:3088
Revision\newFromTitle
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given link target.
Definition: Revision.php:133
Title\isValidMoveTarget
isValidMoveTarget( $nt)
Checks if $this can be moved to a given Title.
Definition: Title.php:4213
$query
null for the wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1627
Title\getUserPermissionsErrors
getUserPermissionsErrors( $action, $user, $rigor='secure', $ignoreErrors=[])
Can $user perform $action on this page?
Definition: Title.php:2202
Title\getCdnUrls
getCdnUrls()
Get a list of URLs to purge from the CDN cache when this page changes.
Definition: Title.php:3941
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:53
Title\getOtherPage
getOtherPage()
Get the other title for this page, if this is a subject page get the talk page, if it is a subject pa...
Definition: Title.php:1557
MediaWiki\Linker\LinkTarget\getNamespace
getNamespace()
Get the namespace index.
Title\isSiteConfigPage
isSiteConfigPage()
Could this MediaWiki namespace page contain custom CSS, JSON, or JavaScript for the global UI.
Definition: Title.php:1300
Title\isUserJsonConfigPage
isUserJsonConfigPage()
Is this a JSON "config" sub-page of a user page?
Definition: Title.php:1398
Title\$prefixedText
string $prefixedText
Text form including namespace/interwiki, initialised on demand.
Definition: Title.php:145
Wikimedia\Rdbms\IDatabase\timestamp
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
ContentHandler\getDefaultModelFor
static getDefaultModelFor(Title $title)
Returns the name of the default content model to be used for the page with the given title.
Definition: ContentHandler.php:182
$wgRawHtmlMessages
string[] $wgRawHtmlMessages
List of messages which might contain raw HTML.
Definition: DefaultSettings.php:8843
Title\getDBkey
getDBkey()
Get the main part with underscores.
Definition: Title.php:951
Revision\getQueryInfo
static getQueryInfo( $options=[])
Return the tables, fields, and join conditions to be selected to create a new revision object.
Definition: Revision.php:521
Title\nameOf
static nameOf( $id)
Get the prefixed DB key associated with an ID.
Definition: Title.php:612
Title\$mDbPageLanguage
string bool null $mDbPageLanguage
The page language code from the database, null if not saved in the database or false if not loaded,...
Definition: Title.php:174
$html
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses & $html
Definition: hooks.txt:2036
Title\isSpecial
isSpecial( $name)
Returns true if this title resolves to the named special page.
Definition: Title.php:1127
MWException
MediaWiki exception.
Definition: MWException.php:26
Title\getTitleFormatter
static getTitleFormatter()
B/C kludge: provide a TitleParser for use by Title.
Definition: Title.php:191
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:964
Title\checkCascadingSourcesRestrictions
checkCascadingSourcesRestrictions( $action, $user, $errors, $rigor, $short)
Check restrictions on cascading pages.
Definition: Title.php:2546
MapCacheLRU\get
get( $key, $maxAge=0.0)
Get the value for a key.
Definition: MapCacheLRU.php:163
Title\isMainPage
isMainPage()
Is this the mainpage?
Definition: Title.php:1250
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1118
$titles
linkcache txt The LinkCache class maintains a list of article titles and the information about whether or not the article exists in the database This is used to mark up links when displaying a page If the same link appears more than once on any page then it only has to be looked up once In most cases link lookups are done in batches with the LinkBatch class or the equivalent in so the link cache is mostly useful for short snippets of parsed and for links in the navigation areas of the skin The link cache was formerly used to track links used in a document for the purposes of updating the link tables This application is now deprecated To create a you can use the following $titles
Definition: linkcache.txt:17
Title\moveTo
moveTo(&$nt, $auth=true, $reason='', $createRedirect=true, array $changeTags=[])
Move a title to a new location.
Definition: Title.php:4051
Title\getSubpage
getSubpage( $text)
Get the title for a subpage of the current page.
Definition: Title.php:1834
Title\getBaseTitle
getBaseTitle()
Get the base page name title, i.e.
Definition: Title.php:1798
Title\getNamespace
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:974
BacklinkCache\get
static get(Title $title)
Create a new BacklinkCache or reuse any existing one.
Definition: BacklinkCache.php:113
Title\newFromRow
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:475
Title\isConversionTable
isConversionTable()
Is this a conversion table for the LanguageConverter?
Definition: Title.php:1270
MovePage
Handles the backend logic of moving a page from one title to another.
Definition: MovePage.php:30
Title\$titleCache
static MapCacheLRU $titleCache
Definition: Title.php:41
Title\newFromLinkTarget
static newFromLinkTarget(LinkTarget $linkTarget)
Create a new Title from a LinkTarget.
Definition: Title.php:251
Title\isProtected
isProtected( $action='')
Does the title correspond to a protected article?
Definition: Title.php:3038
Title\flushRestrictions
flushRestrictions()
Flush the protection cache in this object and force reload from the database.
Definition: Title.php:3405
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2693
Title\getCategorySortkey
getCategorySortkey( $prefix='')
Returns the raw sort key to be used for categories, with the specified prefix.
Definition: Title.php:5067
Title\getInterwiki
getInterwiki()
Get the interwiki prefix.
Definition: Title.php:861
Title\isSiteJsonConfigPage
isSiteJsonConfigPage()
Is this a sitewide JSON "config" page?
Definition: Title.php:1453
$matches
$matches
Definition: NoLocalSettings.php:24
in
null for the wiki Added in
Definition: hooks.txt:1627
Title\isValidRedirectTarget
isValidRedirectTarget()
Check if this Title is a valid redirect target.
Definition: Title.php:5014
Title\deleteTitleProtection
deleteTitleProtection()
Remove any title protection due to page existing.
Definition: Title.php:2992
Title\__construct
__construct()
Definition: Title.php:210
$wgLang
$wgLang
Definition: Setup.php:902
MediaWiki\Interwiki\InterwikiLookup
Service interface for looking up Interwiki records.
Definition: InterwikiLookup.php:31
MWNamespace\isContent
static isContent( $index)
Does this namespace contain content, for the purposes of calculating statistics, etc?
Definition: MWNamespace.php:331
Title\getBrokenLinksFrom
getBrokenLinksFrom()
Get an array of Title objects referring to non-existent articles linked from this page.
Definition: Title.php:3905
Title\isUserConfigPage
isUserConfigPage()
Is this a "config" (.css, .json, or .js) sub-page of a user page?
Definition: Title.php:1325
PROTO_CURRENT
const PROTO_CURRENT
Definition: Defines.php:222
Title\missingPermissionError
missingPermissionError( $action, $short)
Get a description array when the user doesn't have the right to perform $action (i....
Definition: Title.php:2792
Title\$mCascadeSources
array $mCascadeSources
Where are the cascading restrictions coming from on this page?
Definition: Title.php:132
Title\getSubjectPage
getSubjectPage()
Get a title object associated with the subject page of this talk page.
Definition: Title.php:1540
MapCacheLRU
Handles a simple LRU key/value map with a maximum number of entries.
Definition: MapCacheLRU.php:37
wfGetLangObj
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
Definition: GlobalFunctions.php:1281
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
$wgInvalidRedirectTargets
$wgInvalidRedirectTargets
Array of invalid page redirect targets.
Definition: DefaultSettings.php:4127
Title\getFullText
getFullText()
Get the prefixed title with spaces, plus any fragment (part beginning with '#')
Definition: Title.php:1718
MWNamespace\hasTalkNamespace
static hasTalkNamespace( $index)
Does this namespace ever have a talk namespace?
Definition: MWNamespace.php:320
MWNamespace\hasSubpages
static hasSubpages( $index)
Does the namespace allow subpages?
Definition: MWNamespace.php:364
Title\getTitleCache
static getTitleCache()
Definition: Title.php:387
Title\hasFragment
hasFragment()
Check if a Title fragment is set.
Definition: Title.php:1597
Title\isCssSubpage
isCssSubpage()
Definition: Title.php:1387
$parser
see documentation in includes Linker php for Linker::makeImageLink or false for current used if you return false $parser
Definition: hooks.txt:1841
Title\getRedirectsHere
getRedirectsHere( $ns=null)
Get all extant redirects to this Title.
Definition: Title.php:4978
MWNamespace\isMovable
static isMovable( $index)
Can pages in the given namespace be moved?
Definition: MWNamespace.php:88
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:545
Title\$mInterwiki
string $mInterwiki
Interwiki prefix.
Definition: Title.php:79
Title\$mCascadeRestriction
bool $mCascadeRestriction
Cascade restrictions on this page to included templates and images?
Definition: Title.php:120
Title\equals
equals(Title $title)
Compare with another title.
Definition: Title.php:4644
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1983
Title\getRootText
getRootText()
Get the root page name text without a namespace, i.e.
Definition: Title.php:1738
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:78
$wgNamespaceProtection
$wgNamespaceProtection
Set the minimum permissions required to edit pages in each namespace.
Definition: DefaultSettings.php:5363
Title\canExist
canExist()
Is this in a namespace that allows actual pages?
Definition: Title.php:1099
$wgDeleteRevisionsLimit
$wgDeleteRevisionsLimit
Optional to restrict deletion of pages with higher revision counts to users with the 'bigdelete' perm...
Definition: DefaultSettings.php:5540
DB_MASTER
const DB_MASTER
Definition: defines.php:26
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:988
string
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
Definition: hooks.txt:175
Title\__sleep
__sleep()
Definition: Title.php:5268
Title\getCanonicalURL
getCanonicalURL( $query='', $query2=false)
Get the URL for a canonical link, for use in things like IRC and e-mail notifications.
Definition: Title.php:2128
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
MessageCache\singleton
static singleton()
Get the signleton instance of this class.
Definition: MessageCache.php:114
$sort
$sort
Definition: profileinfo.php:328
Title\$mRedirect
null $mRedirect
Is the article at this title a redirect?
Definition: Title.php:161
Title\createFragmentTarget
createFragmentTarget( $fragment)
Creates a new Title for a different fragment of the same page.
Definition: Title.php:1640
Title\$mNamespace
int $mNamespace
Namespace index, i.e.
Definition: Title.php:76
Title\getDefaultNamespace
getDefaultNamespace()
Get the default namespace index, for when there is no namespace.
Definition: Title.php:1576
$fname
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings,...
Definition: Setup.php:121
Title\newFromTextThrow
static newFromTextThrow( $text, $defaultNamespace=NS_MAIN)
Like Title::newFromText(), but throws MalformedTitleException when the title is invalid,...
Definition: Title.php:313
$wgPageLanguageUseDB
bool $wgPageLanguageUseDB
Enable page language feature Allows setting page language in database.
Definition: DefaultSettings.php:8712
Title\newFromTitleValue
static newFromTitleValue(TitleValue $titleValue)
Create a new Title from a TitleValue.
Definition: Title.php:240
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
Title\isValid
isValid()
Returns true if the title is valid, false if it is invalid.
Definition: Title.php:814
Title\$mPageLanguage
bool $mPageLanguage
The (string) language code of the page's language and content code.
Definition: Title.php:170
AtomicSectionUpdate
Deferrable Update for closure/callback updates via IDatabase::doAtomicSection()
Definition: AtomicSectionUpdate.php:9
HTMLCacheUpdate
Class to invalidate the HTML cache of all the pages linking to a given title.
Definition: HTMLCacheUpdate.php:29
Title\getPrefixedURL
getPrefixedURL()
Get a URL-encoded title (not an actual URL) including interwiki.
Definition: Title.php:1854
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:573
Title\__toString
__toString()
Return a string representation of this title.
Definition: Title.php:1708
User\whoIs
static whoIs( $id)
Get the username corresponding to a given user ID.
Definition: User.php:891
Title\getTemplateLinksTo
getTemplateLinksTo( $options=[])
Get an array of Title objects using this Title as a template Also stores the IDs in the link cache.
Definition: Title.php:3823
Title\areRestrictionsCascading
areRestrictionsCascading()
Returns cascading restrictions for the current article.
Definition: Title.php:3258
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:52
CdnCacheUpdate
Handles purging appropriate CDN URLs given a title (or titles)
Definition: CdnCacheUpdate.php:30
Title\getLatestRevID
getLatestRevID( $flags=0)
What is the page_latest field for this page?
Definition: Title.php:3651
Title\areRestrictionsLoaded
areRestrictionsLoaded()
Accessor for mRestrictionsLoaded.
Definition: Title.php:3205
$retval
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account incomplete not yet checked for validity & $retval
Definition: hooks.txt:244
Title\getSubpages
getSubpages( $limit=-1)
Get all subpages of this page.
Definition: Title.php:3484
Title\isSingleRevRedirect
isSingleRevRedirect()
Checks if this page is just a one-rev redirect.
Definition: Title.php:4162
$wgServer
$wgServer
URL of the server.
Definition: DefaultSettings.php:105
Title\GAID_FOR_UPDATE
const GAID_FOR_UPDATE
Used to be GAID_FOR_UPDATE define.
Definition: Title.php:54
Title\getPageViewLanguage
getPageViewLanguage()
Get the language in which the content of this page is written when viewed by user.
Definition: Title.php:5153
Title\newFromDBkey
static newFromDBkey( $key)
Create a new Title from a prefixed DB key.
Definition: Title.php:221
$wgLanguageCode
$wgLanguageCode
Site language code.
Definition: DefaultSettings.php:2942
Title\isSubpage
isSubpage()
Is this a subpage?
Definition: Title.php:1259
Title\hasContentModel
hasContentModel( $id)
Convenience method for checking a title's content model name.
Definition: Title.php:1007
Title\getRestrictionExpiry
getRestrictionExpiry( $action)
Get the expiry time for the restriction against a given action.
Definition: Title.php:3246
Title\newFromURL
static newFromURL( $url)
THIS IS NOT THE FUNCTION YOU WANT.
Definition: Title.php:364
Title\isValidMoveOperation
isValidMoveOperation(&$nt, $auth=true, $reason='')
Check whether a given move operation would be valid.
Definition: Title.php:3995
MediaWiki\Linker\LinkTarget\getFragment
getFragment()
Get the link fragment (i.e.
PROTO_HTTP
const PROTO_HTTP
Definition: Defines.php:219
$wgBlockDisablesLogin
$wgBlockDisablesLogin
If true, blocked users will not be allowed to login.
Definition: DefaultSettings.php:5038
Title\$mRestrictionsExpiry
array $mRestrictionsExpiry
When do the restrictions on this page expire?
Definition: Title.php:126
MapCacheLRU\set
set( $key, $value, $rank=self::RANK_TOP)
Set a key/value pair.
Definition: MapCacheLRU.php:109
PROTO_RELATIVE
const PROTO_RELATIVE
Definition: Defines.php:221
$ret
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:2036
Title\$mTitleValue
TitleValue $mTitleValue
A corresponding TitleValue object.
Definition: Title.php:177
Title\isSubpageOf
isSubpageOf(Title $title)
Check if this title is a subpage of another title.
Definition: Title.php:4657
Revision\RAW
const RAW
Definition: Revision.php:57
Title\isLocal
isLocal()
Determine whether the object refers to a page within this project (either this wiki or a wiki with a ...
Definition: Title.php:835
Title\$mFragment
string $mFragment
Title fragment (i.e.
Definition: Title.php:85
Title\getContentModel
getContentModel( $flags=0)
Get the page's content model id, see the CONTENT_MODEL_XXX constants.
Definition: Title.php:984
Title\$mNotificationTimestamp
array $mNotificationTimestamp
Associative array of user ID -> timestamp/false.
Definition: Title.php:164
MWNamespace\exists
static exists( $index)
Returns whether the specified namespace exists.
Definition: MWNamespace.php:182
Title\newFromIDs
static newFromIDs( $ids)
Make an array of titles from an array of IDs.
Definition: Title.php:449
Wikimedia\Rdbms\IDatabase\update
update( $table, $values, $conds, $fname=__METHOD__, $options=[])
UPDATE wrapper.
Title\checkQuickPermissions
checkQuickPermissions( $action, $user, $errors, $rigor, $short)
Permissions checks that fail most often, and which are easiest to test.
Definition: Title.php:2233
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:432
WikiPage\getQueryInfo
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new page object.
Definition: WikiPage.php:365
Title\prefix
prefix( $name)
Prefix some arbitrary text with the namespace or interwiki prefix of this object.
Definition: Title.php:1656
Title\getNamespaceKey
getNamespaceKey( $prepend='nstab-')
Generate strings used for xml 'id' names in monobook tabs.
Definition: Title.php:4950
Title\getEditURL
getEditURL()
Get the edit URL for this Title.
Definition: Title.php:2142
Title\getFullURL
getFullURL( $query='', $query2=false, $proto=PROTO_RELATIVE)
Get a real URL referring to this title, with interwiki link and fragment.
Definition: Title.php:1911
wfFindFile
wfFindFile( $title, $options=[])
Find a file.
Definition: GlobalFunctions.php:2734
$wgEmailConfirmToEdit
$wgEmailConfirmToEdit
Should editors be required to have a validated e-mail address before being allowed to edit?
Definition: DefaultSettings.php:5094
Title\isKnown
isKnown()
Does this title refer to a page that can (or might) be meaningfully viewed? In particular,...
Definition: Title.php:4750
$wgArticlePath
$wgArticlePath
Definition: img_auth.php:46
Title\validateFileMoveOperation
validateFileMoveOperation( $nt)
Check if the requested move target is a valid file move target.
Definition: Title.php:4022
text
This list may contain false positives That usually means there is additional text with links below the first Each row contains links to the first and second as well as the first line of the second redirect text
Definition: All_system_messages.txt:1267
article
and how to run hooks for an and one after Each event has a preferably in CamelCase For ArticleDelete hook A clump of code and data that should be run when an event happens This can be either a function and a chunk of or an object and a method hook function The function part of a third party developers and administrators to define code that will be run at certain points in the mainline and to modify the data run by that mainline code Hooks can keep mainline code and make it easier to write extensions Hooks are a principled alternative to patches for two options in MediaWiki One reverses the order of a title before displaying the article
Definition: hooks.txt:23
Title\getFirstRevision
getFirstRevision( $flags=0)
Get the first revision of the page.
Definition: Title.php:4416
Title\getAllRestrictions
getAllRestrictions()
Accessor/initialisation for mRestrictions.
Definition: Title.php:3232
Title\getEarliestRevTime
getEarliestRevTime( $flags=0)
Get the oldest revision timestamp of this page.
Definition: Title.php:4443
User\isEveryoneAllowed
static isEveryoneAllowed( $right)
Check if all users may be assumed to have the given permission.
Definition: User.php:5034
$parent
$parent
Definition: pageupdater.txt:71
Title
Represents a title within MediaWiki.
Definition: Title.php:39
Title\$mCascadingRestrictions
$mCascadingRestrictions
Caching the results of getCascadeProtectionSources.
Definition: Title.php:123
Title\canTalk
canTalk()
Can this title have a corresponding talk page?
Definition: Title.php:1078
Title\getCascadeProtectionSources
getCascadeProtectionSources( $getPages=true)
Cascading protection: Get the source of any cascading restrictions on this page.
Definition: Title.php:3119
$cache
$cache
Definition: mcc.php:33
$rows
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction $rows
Definition: hooks.txt:2675
$wgSemiprotectedRestrictionLevels
$wgSemiprotectedRestrictionLevels
Restriction levels that should be considered "semiprotected".
Definition: DefaultSettings.php:5354
Title\getRestrictionTypes
getRestrictionTypes()
Returns restriction types for the current Title.
Definition: Title.php:2895
MalformedTitleException
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Definition: MalformedTitleException.php:25
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:2036
Title\getTitleProtectionInternal
getTitleProtectionInternal()
Fetch title protection settings.
Definition: Title.php:2945
Wikimedia\Rdbms\IDatabase\addQuotes
addQuotes( $s)
Adds quotes and backslashes.
Title\getTemplateLinksFrom
getTemplateLinksFrom( $options=[])
Get an array of Title objects used on this Title as a template Also stores the IDs in the link cache.
Definition: Title.php:3893
ObjectCache\getMainWANInstance
static getMainWANInstance()
Get the main WAN cache object.
Definition: ObjectCache.php:378
Title\isRedirect
isRedirect( $flags=0)
Is this an article that is a redirect page? Uses link cache, adding it if necessary.
Definition: Title.php:3588
Title\getUserPermissionsErrorsInternal
getUserPermissionsErrorsInternal( $action, $user, $rigor='secure', $short=false)
Can $user perform $action on this page? This is an internal function, with multiple levels of checks ...
Definition: Title.php:2815
DeferredUpdates\PRESEND
const PRESEND
Definition: DeferredUpdates.php:63
MWNamespace\equals
static equals( $ns1, $ns2)
Returns whether the specified namespaces are the same namespace.
Definition: MWNamespace.php:201
Title\$mArticleID
int $mArticleID
Article ID, fetched from the link cache on demand.
Definition: Title.php:88
Title\isDeleted
isDeleted()
Is there a version of this page in the deletion archive?
Definition: Title.php:3511
Title\capitalize
static capitalize( $text, $ns=NS_MAIN)
Capitalize a text string for a title if it belongs to a namespace that capitalizes.
Definition: Title.php:3720
Title\$mDefaultNamespace
int $mDefaultNamespace
Namespace index when there is no namespace.
Definition: Title.php:155
Title\$mLength
int $mLength
The page length, 0 for special pages.
Definition: Title.php:158
Title\$mEstimateRevisions
int $mEstimateRevisions
Estimated number of revisions; null of not loaded.
Definition: Title.php:106
Title\hasSubjectNamespace
hasSubjectNamespace( $ns)
Returns true if the title has the same subject namespace as the namespace specified.
Definition: Title.php:1208
Title\isSpecialPage
isSpecialPage()
Returns true if this is a special page.
Definition: Title.php:1117
Title\purgeSquid
purgeSquid()
Purge all applicable CDN URLs.
Definition: Title.php:3978
Title\getPreviousRevisionID
getPreviousRevisionID( $revId, $flags=0)
Get the revision ID of the previous revision.
Definition: Title.php:4395
TitleArray
The TitleArray class only exists to provide the newFromResult method at pre- sent.
Definition: TitleArray.php:33
$rev
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition: hooks.txt:1808
Title\getPageLanguage
getPageLanguage()
Get the language in which the content of this page is written in wikitext.
Definition: Title.php:5115
Title\CACHE_MAX
const CACHE_MAX
Title::newFromText maintains a cache to avoid expensive re-normalization of commonly used titles.
Definition: Title.php:48
Title\getEditNotices
getEditNotices( $oldid=0)
Get a list of rendered edit notices for this page.
Definition: Title.php:5197
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
Title\getTouched
getTouched( $db=null)
Get the last touched timestamp.
Definition: Title.php:4898
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:210
MWNamespace\isCapitalized
static isCapitalized( $index)
Is the namespace first-letter capitalized?
Definition: MWNamespace.php:417
Title\isCssOrJsPage
isCssOrJsPage()
Definition: Title.php:1312
Wikimedia\Rdbms\IDatabase\selectFieldValues
selectFieldValues( $table, $var, $cond='', $fname=__METHOD__, $options=[], $join_conds=[])
A SELECT wrapper which returns a list of single field values from result rows.
$wgRestrictionLevels
$wgRestrictionLevels
Rights which can be required for each protection level (via action=protect)
Definition: DefaultSettings.php:5329
Title\getFragmentForURL
getFragmentForURL()
Get the fragment in URL form, including the "#" character if there is one.
Definition: Title.php:1606
Title\exists
exists( $flags=0)
Check if page exists.
Definition: Title.php:4674
NS_USER
const NS_USER
Definition: Defines.php:66
$content
$content
Definition: pageupdater.txt:72
Title\isAlwaysKnown
isAlwaysKnown()
Should links to this title be shown as potentially viewable (i.e.
Definition: Title.php:4696
Title\isDeletedQuick
isDeletedQuick()
Is there a version of this page in the deletion archive?
Definition: Title.php:3536
Title\getSubpageUrlForm
getSubpageUrlForm()
Get a URL-encoded form of the subpage text.
Definition: Title.php:1843
MapCacheLRU\clear
clear( $keys=null)
Clear one or several cache entries, or all cache entries.
Definition: MapCacheLRU.php:277
Language\factory
static factory( $code)
Get a cached or new language object for a given language code.
Definition: Language.php:214
Title\isNewPage
isNewPage()
Check if this is a new page.
Definition: Title.php:4453
$wgRestrictionTypes
$wgRestrictionTypes
Set of available actions that can be restricted via action=protect You probably shouldn't change this...
Definition: DefaultSettings.php:5316
Title\setContentModel
setContentModel( $model)
Set a proposed content model for the page for permissions checking.
Definition: Title.php:1022
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:72
CONTENT_MODEL_JAVASCRIPT
const CONTENT_MODEL_JAVASCRIPT
Definition: Defines.php:236
Title\compare
static compare(LinkTarget $a, LinkTarget $b)
Callback for usort() to do title sorts by (namespace, title)
Definition: Title.php:795
Title\countAuthorsBetween
countAuthorsBetween( $old, $new, $limit, $options=[])
Get the number of authors between the given revisions or revision IDs.
Definition: Title.php:4633
$t
$t
Definition: testCompression.php:69
Title\legalChars
static legalChars()
Get a regex character class describing the legal characters in a link.
Definition: Title.php:634
Title\isSiteJsConfigPage
isSiteJsConfigPage()
Is this a sitewide JS "config" page?
Definition: Title.php:1471
MWNamespace\isWatchable
static isWatchable( $index)
Can pages in a namespace be watched?
Definition: MWNamespace.php:354
Title\invalidateCache
invalidateCache( $purgeTime=null)
Updates page_touched for this page; called from LinksUpdate.php.
Definition: Title.php:4840
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:747
MediaWikiServices
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
Title\isTrans
isTrans()
Determine whether the object refers to a page within this project and is transcludable.
Definition: Title.php:880
Title\checkUserConfigPermissions
checkUserConfigPermissions( $action, $user, $errors, $rigor, $short)
Check CSS/JSON/JS sub-page permissions.
Definition: Title.php:2445
Title\getRestrictions
getRestrictions( $action)
Accessor/initialisation for mRestrictions.
Definition: Title.php:3218
MWNamespace\getTalk
static getTalk( $index)
Get the talk namespace index for a given namespace.
Definition: MWNamespace.php:129
wfMessage
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
MWNamespace\getSubject
static getSubject( $index)
Get the subject namespace index for a given namespace Special namespaces (NS_MEDIA,...
Definition: MWNamespace.php:143
Title\checkPermissionHooks
checkPermissionHooks( $action, $user, $errors, $rigor, $short)
Check various permission hooks.
Definition: Title.php:2334
ResourceLoaderWikiModule\invalidateModuleCache
static invalidateModuleCache(Title $title, Revision $old=null, Revision $new=null, $wikiId)
Clear the preloadTitleInfo() cache for all wiki modules on this wiki on page change if it was a JS or...
Definition: ResourceLoaderWikiModule.php:534
CommentStore\getStore
static getStore()
Definition: CommentStore.php:125
Title\isContentPage
isContentPage()
Is this Title in a namespace which contains content? In other words, is this a content page,...
Definition: Title.php:1219
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:47
wfLocalFile
wfLocalFile( $title)
Get an object referring to a locally registered file.
Definition: GlobalFunctions.php:2745
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:427
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
$wgVariantArticlePath
$wgVariantArticlePath
Like $wgArticlePath, but on multi-variant wikis, this provides a path format that describes which par...
Definition: DefaultSettings.php:3157
Title\getUserCaseDBKey
getUserCaseDBKey()
Get the DB key with the initial letter case as specified by the user.
Definition: Title.php:960
Title\isRawHtmlMessage
isRawHtmlMessage()
Is this a message which can contain raw HTML?
Definition: Title.php:1489
Title\getSelectFields
static getSelectFields()
Returns a list of fields that are to be selected for initializing Title objects or LinkCache entries.
Definition: Title.php:401
Title\hasSubpages
hasSubpages()
Does this have subpages? (Warning, usually requires an extra DB query.)
Definition: Title.php:3456
Title\escapeFragmentForURL
static escapeFragmentForURL( $fragment)
Escape a text fragment, say from a link, for a URL.
Definition: Title.php:778
Title\getBaseText
getBaseText()
Get the base page name without a namespace, i.e.
Definition: Title.php:1773
Title\pageCond
pageCond()
Get an associative array for selecting this title from the "page" table.
Definition: Title.php:4323
Title\getText
getText()
Get the text form (spaces not underscores) of the main part.
Definition: Title.php:933
MWNamespace\getCanonicalName
static getCanonicalName( $index)
Returns the canonical (English) name for a given index.
Definition: MWNamespace.php:255
Title\$mTextform
string $mTextform
Text form (spaces not underscores) of the main part.
Definition: Title.php:64
Wikimedia\Rdbms\IDatabase\delete
delete( $table, $conds, $fname=__METHOD__)
DELETE query wrapper.
Title\purgeExpiredRestrictions
static purgeExpiredRestrictions()
Purge expired restrictions from the page_restrictions table.
Definition: Title.php:3415
$wgInternalServer
$wgInternalServer
Internal server name as known to CDN, if different.
Definition: DefaultSettings.php:2768
Title\$mLatestID
bool int $mLatestID
ID of most recent revision.
Definition: Title.php:91
Title\checkUserBlock
checkUserBlock( $action, $user, $errors, $rigor, $short)
Check that the user isn't blocked from editing.
Definition: Title.php:2670
Title\$mRestrictionsLoaded
bool $mRestrictionsLoaded
Boolean for initialisation on demand.
Definition: Title.php:135
wfExpandUrl
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
Definition: GlobalFunctions.php:512
Title\getSubjectNsText
getSubjectNsText()
Get the namespace text of the subject (rather than talk) page.
Definition: Title.php:1056
Title\$mTitleProtection
mixed $mTitleProtection
Cached value for getTitleProtection (create protection)
Definition: Title.php:148
Title\loadRestrictions
loadRestrictions( $oldFashionedRestrictions=null)
Load restrictions from the page_restrictions table.
Definition: Title.php:3349
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:36
Title\getRelativeRevisionID
getRelativeRevisionID( $revId, $flags, $dir)
Get next/previous revision ID relative to another revision ID.
Definition: Title.php:4339
wfArrayToCgi
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
Definition: GlobalFunctions.php:368
Title\getFullUrlForRedirect
getFullUrlForRedirect( $query='', $proto=PROTO_CURRENT)
Get a url appropriate for making redirects based on an untrusted url arg.
Definition: Title.php:1946
$out
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition: hooks.txt:813
Title\getRootTitle
getRootTitle()
Get the root page name title, i.e.
Definition: Title.php:1758
$type
$type
Definition: testCompression.php:48
Title\getLength
getLength( $flags=0)
What is the length of this page? Uses link cache, adding it if necessary.
Definition: Title.php:3623
Title\getAuthorsBetween
getAuthorsBetween( $old, $new, $limit, $options=[])
Get the authors between the given revisions or revision IDs.
Definition: Title.php:4558
Title\isWatchable
isWatchable()
Can this title be added to a user's watchlist?
Definition: Title.php:1108