MediaWiki  1.29.2
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 
112  protected $mOldRestrictions = false;
113 
116 
119 
121  protected $mRestrictionsExpiry = [];
122 
125 
128 
130  public $mRestrictionsLoaded = false;
131 
133  protected $mPrefixedText = null;
134 
137 
144 
146  protected $mLength = -1;
147 
149  public $mRedirect = null;
150 
153 
155  private $mHasSubpages;
156 
158  private $mPageLanguage = false;
159 
162  private $mDbPageLanguage = false;
163 
165  private $mTitleValue = null;
166 
168  private $mIsBigDeletion = null;
169  // @}
170 
179  private static function getTitleFormatter() {
180  return MediaWikiServices::getInstance()->getTitleFormatter();
181  }
182 
191  private static function getInterwikiLookup() {
192  return MediaWikiServices::getInstance()->getInterwikiLookup();
193  }
194 
198  function __construct() {
199  }
200 
209  public static function newFromDBkey( $key ) {
210  $t = new Title();
211  $t->mDbkeyform = $key;
212 
213  try {
214  $t->secureAndSplit();
215  return $t;
216  } catch ( MalformedTitleException $ex ) {
217  return null;
218  }
219  }
220 
228  public static function newFromTitleValue( TitleValue $titleValue ) {
229  return self::newFromLinkTarget( $titleValue );
230  }
231 
239  public static function newFromLinkTarget( LinkTarget $linkTarget ) {
240  if ( $linkTarget instanceof Title ) {
241  // Special case if it's already a Title object
242  return $linkTarget;
243  }
244  return self::makeTitle(
245  $linkTarget->getNamespace(),
246  $linkTarget->getText(),
247  $linkTarget->getFragment(),
248  $linkTarget->getInterwiki()
249  );
250  }
251 
265  public static function newFromText( $text, $defaultNamespace = NS_MAIN ) {
266  // DWIM: Integers can be passed in here when page titles are used as array keys.
267  if ( $text !== null && !is_string( $text ) && !is_int( $text ) ) {
268  throw new InvalidArgumentException( '$text must be a string.' );
269  }
270  if ( $text === null ) {
271  return null;
272  }
273 
274  try {
275  return Title::newFromTextThrow( strval( $text ), $defaultNamespace );
276  } catch ( MalformedTitleException $ex ) {
277  return null;
278  }
279  }
280 
295  public static function newFromTextThrow( $text, $defaultNamespace = NS_MAIN ) {
296  if ( is_object( $text ) ) {
297  throw new MWException( '$text must be a string, given an object' );
298  }
299 
301 
302  // Wiki pages often contain multiple links to the same page.
303  // Title normalization and parsing can become expensive on pages with many
304  // links, so we can save a little time by caching them.
305  // In theory these are value objects and won't get changed...
306  if ( $defaultNamespace == NS_MAIN ) {
307  $t = $titleCache->get( $text );
308  if ( $t ) {
309  return $t;
310  }
311  }
312 
313  // Convert things like &eacute; &#257; or &#x3017; into normalized (T16952) text
314  $filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text );
315 
316  $t = new Title();
317  $t->mDbkeyform = strtr( $filteredText, ' ', '_' );
318  $t->mDefaultNamespace = intval( $defaultNamespace );
319 
320  $t->secureAndSplit();
321  if ( $defaultNamespace == NS_MAIN ) {
322  $titleCache->set( $text, $t );
323  }
324  return $t;
325  }
326 
342  public static function newFromURL( $url ) {
343  $t = new Title();
344 
345  # For compatibility with old buggy URLs. "+" is usually not valid in titles,
346  # but some URLs used it as a space replacement and they still come
347  # from some external search tools.
348  if ( strpos( self::legalChars(), '+' ) === false ) {
349  $url = strtr( $url, '+', ' ' );
350  }
351 
352  $t->mDbkeyform = strtr( $url, ' ', '_' );
353 
354  try {
355  $t->secureAndSplit();
356  return $t;
357  } catch ( MalformedTitleException $ex ) {
358  return null;
359  }
360  }
361 
365  private static function getTitleCache() {
366  if ( self::$titleCache == null ) {
367  self::$titleCache = new HashBagOStuff( [ 'maxKeys' => self::CACHE_MAX ] );
368  }
369  return self::$titleCache;
370  }
371 
379  protected static function getSelectFields() {
380  global $wgContentHandlerUseDB, $wgPageLanguageUseDB;
381 
382  $fields = [
383  'page_namespace', 'page_title', 'page_id',
384  'page_len', 'page_is_redirect', 'page_latest',
385  ];
386 
387  if ( $wgContentHandlerUseDB ) {
388  $fields[] = 'page_content_model';
389  }
390 
391  if ( $wgPageLanguageUseDB ) {
392  $fields[] = 'page_lang';
393  }
394 
395  return $fields;
396  }
397 
405  public static function newFromID( $id, $flags = 0 ) {
407  $row = $db->selectRow(
408  'page',
409  self::getSelectFields(),
410  [ 'page_id' => $id ],
411  __METHOD__
412  );
413  if ( $row !== false ) {
414  $title = Title::newFromRow( $row );
415  } else {
416  $title = null;
417  }
418  return $title;
419  }
420 
427  public static function newFromIDs( $ids ) {
428  if ( !count( $ids ) ) {
429  return [];
430  }
431  $dbr = wfGetDB( DB_REPLICA );
432 
433  $res = $dbr->select(
434  'page',
435  self::getSelectFields(),
436  [ 'page_id' => $ids ],
437  __METHOD__
438  );
439 
440  $titles = [];
441  foreach ( $res as $row ) {
442  $titles[] = Title::newFromRow( $row );
443  }
444  return $titles;
445  }
446 
453  public static function newFromRow( $row ) {
454  $t = self::makeTitle( $row->page_namespace, $row->page_title );
455  $t->loadFromRow( $row );
456  return $t;
457  }
458 
465  public function loadFromRow( $row ) {
466  if ( $row ) { // page found
467  if ( isset( $row->page_id ) ) {
468  $this->mArticleID = (int)$row->page_id;
469  }
470  if ( isset( $row->page_len ) ) {
471  $this->mLength = (int)$row->page_len;
472  }
473  if ( isset( $row->page_is_redirect ) ) {
474  $this->mRedirect = (bool)$row->page_is_redirect;
475  }
476  if ( isset( $row->page_latest ) ) {
477  $this->mLatestID = (int)$row->page_latest;
478  }
479  if ( !$this->mForcedContentModel && isset( $row->page_content_model ) ) {
480  $this->mContentModel = strval( $row->page_content_model );
481  } elseif ( !$this->mForcedContentModel ) {
482  $this->mContentModel = false; # initialized lazily in getContentModel()
483  }
484  if ( isset( $row->page_lang ) ) {
485  $this->mDbPageLanguage = (string)$row->page_lang;
486  }
487  if ( isset( $row->page_restrictions ) ) {
488  $this->mOldRestrictions = $row->page_restrictions;
489  }
490  } else { // page not found
491  $this->mArticleID = 0;
492  $this->mLength = 0;
493  $this->mRedirect = false;
494  $this->mLatestID = 0;
495  if ( !$this->mForcedContentModel ) {
496  $this->mContentModel = false; # initialized lazily in getContentModel()
497  }
498  }
499  }
500 
514  public static function makeTitle( $ns, $title, $fragment = '', $interwiki = '' ) {
515  $t = new Title();
516  $t->mInterwiki = $interwiki;
517  $t->mFragment = $fragment;
518  $t->mNamespace = $ns = intval( $ns );
519  $t->mDbkeyform = strtr( $title, ' ', '_' );
520  $t->mArticleID = ( $ns >= 0 ) ? -1 : 0;
521  $t->mUrlform = wfUrlencode( $t->mDbkeyform );
522  $t->mTextform = strtr( $title, '_', ' ' );
523  $t->mContentModel = false; # initialized lazily in getContentModel()
524  return $t;
525  }
526 
538  public static function makeTitleSafe( $ns, $title, $fragment = '', $interwiki = '' ) {
539  if ( !MWNamespace::exists( $ns ) ) {
540  return null;
541  }
542 
543  $t = new Title();
544  $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki, true );
545 
546  try {
547  $t->secureAndSplit();
548  return $t;
549  } catch ( MalformedTitleException $ex ) {
550  return null;
551  }
552  }
553 
559  public static function newMainPage() {
560  $title = Title::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
561  // Don't give fatal errors if the message is broken
562  if ( !$title ) {
563  $title = Title::newFromText( 'Main Page' );
564  }
565  return $title;
566  }
567 
574  public static function nameOf( $id ) {
575  $dbr = wfGetDB( DB_REPLICA );
576 
577  $s = $dbr->selectRow(
578  'page',
579  [ 'page_namespace', 'page_title' ],
580  [ 'page_id' => $id ],
581  __METHOD__
582  );
583  if ( $s === false ) {
584  return null;
585  }
586 
587  $n = self::makeName( $s->page_namespace, $s->page_title );
588  return $n;
589  }
590 
596  public static function legalChars() {
597  global $wgLegalTitleChars;
598  return $wgLegalTitleChars;
599  }
600 
610  static function getTitleInvalidRegex() {
611  wfDeprecated( __METHOD__, '1.25' );
613  }
614 
624  public static function convertByteClassToUnicodeClass( $byteClass ) {
625  $length = strlen( $byteClass );
626  // Input token queue
627  $x0 = $x1 = $x2 = '';
628  // Decoded queue
629  $d0 = $d1 = $d2 = '';
630  // Decoded integer codepoints
631  $ord0 = $ord1 = $ord2 = 0;
632  // Re-encoded queue
633  $r0 = $r1 = $r2 = '';
634  // Output
635  $out = '';
636  // Flags
637  $allowUnicode = false;
638  for ( $pos = 0; $pos < $length; $pos++ ) {
639  // Shift the queues down
640  $x2 = $x1;
641  $x1 = $x0;
642  $d2 = $d1;
643  $d1 = $d0;
644  $ord2 = $ord1;
645  $ord1 = $ord0;
646  $r2 = $r1;
647  $r1 = $r0;
648  // Load the current input token and decoded values
649  $inChar = $byteClass[$pos];
650  if ( $inChar == '\\' ) {
651  if ( preg_match( '/x([0-9a-fA-F]{2})/A', $byteClass, $m, 0, $pos + 1 ) ) {
652  $x0 = $inChar . $m[0];
653  $d0 = chr( hexdec( $m[1] ) );
654  $pos += strlen( $m[0] );
655  } elseif ( preg_match( '/[0-7]{3}/A', $byteClass, $m, 0, $pos + 1 ) ) {
656  $x0 = $inChar . $m[0];
657  $d0 = chr( octdec( $m[0] ) );
658  $pos += strlen( $m[0] );
659  } elseif ( $pos + 1 >= $length ) {
660  $x0 = $d0 = '\\';
661  } else {
662  $d0 = $byteClass[$pos + 1];
663  $x0 = $inChar . $d0;
664  $pos += 1;
665  }
666  } else {
667  $x0 = $d0 = $inChar;
668  }
669  $ord0 = ord( $d0 );
670  // Load the current re-encoded value
671  if ( $ord0 < 32 || $ord0 == 0x7f ) {
672  $r0 = sprintf( '\x%02x', $ord0 );
673  } elseif ( $ord0 >= 0x80 ) {
674  // Allow unicode if a single high-bit character appears
675  $r0 = sprintf( '\x%02x', $ord0 );
676  $allowUnicode = true;
677  } elseif ( strpos( '-\\[]^', $d0 ) !== false ) {
678  $r0 = '\\' . $d0;
679  } else {
680  $r0 = $d0;
681  }
682  // Do the output
683  if ( $x0 !== '' && $x1 === '-' && $x2 !== '' ) {
684  // Range
685  if ( $ord2 > $ord0 ) {
686  // Empty range
687  } elseif ( $ord0 >= 0x80 ) {
688  // Unicode range
689  $allowUnicode = true;
690  if ( $ord2 < 0x80 ) {
691  // Keep the non-unicode section of the range
692  $out .= "$r2-\\x7F";
693  }
694  } else {
695  // Normal range
696  $out .= "$r2-$r0";
697  }
698  // Reset state to the initial value
699  $x0 = $x1 = $d0 = $d1 = $r0 = $r1 = '';
700  } elseif ( $ord2 < 0x80 ) {
701  // ASCII character
702  $out .= $r2;
703  }
704  }
705  if ( $ord1 < 0x80 ) {
706  $out .= $r1;
707  }
708  if ( $ord0 < 0x80 ) {
709  $out .= $r0;
710  }
711  if ( $allowUnicode ) {
712  $out .= '\u0080-\uFFFF';
713  }
714  return $out;
715  }
716 
728  public static function makeName( $ns, $title, $fragment = '', $interwiki = '',
729  $canonicalNamespace = false
730  ) {
732 
733  if ( $canonicalNamespace ) {
734  $namespace = MWNamespace::getCanonicalName( $ns );
735  } else {
736  $namespace = $wgContLang->getNsText( $ns );
737  }
738  $name = $namespace == '' ? $title : "$namespace:$title";
739  if ( strval( $interwiki ) != '' ) {
740  $name = "$interwiki:$name";
741  }
742  if ( strval( $fragment ) != '' ) {
743  $name .= '#' . $fragment;
744  }
745  return $name;
746  }
747 
754  static function escapeFragmentForURL( $fragment ) {
755  # Note that we don't urlencode the fragment. urlencoded Unicode
756  # fragments appear not to work in IE (at least up to 7) or in at least
757  # one version of Opera 9.x. The W3C validator, for one, doesn't seem
758  # to care if they aren't encoded.
759  return Sanitizer::escapeId( $fragment, 'noninitial' );
760  }
761 
770  public static function compare( LinkTarget $a, LinkTarget $b ) {
771  if ( $a->getNamespace() == $b->getNamespace() ) {
772  return strcmp( $a->getText(), $b->getText() );
773  } else {
774  return $a->getNamespace() - $b->getNamespace();
775  }
776  }
777 
785  public function isLocal() {
786  if ( $this->isExternal() ) {
787  $iw = self::getInterwikiLookup()->fetch( $this->mInterwiki );
788  if ( $iw ) {
789  return $iw->isLocal();
790  }
791  }
792  return true;
793  }
794 
800  public function isExternal() {
801  return $this->mInterwiki !== '';
802  }
803 
811  public function getInterwiki() {
812  return $this->mInterwiki;
813  }
814 
820  public function wasLocalInterwiki() {
821  return $this->mLocalInterwiki;
822  }
823 
830  public function isTrans() {
831  if ( !$this->isExternal() ) {
832  return false;
833  }
834 
835  return self::getInterwikiLookup()->fetch( $this->mInterwiki )->isTranscludable();
836  }
837 
843  public function getTransWikiID() {
844  if ( !$this->isExternal() ) {
845  return false;
846  }
847 
848  return self::getInterwikiLookup()->fetch( $this->mInterwiki )->getWikiID();
849  }
850 
860  public function getTitleValue() {
861  if ( $this->mTitleValue === null ) {
862  try {
863  $this->mTitleValue = new TitleValue(
864  $this->getNamespace(),
865  $this->getDBkey(),
866  $this->getFragment(),
867  $this->getInterwiki()
868  );
869  } catch ( InvalidArgumentException $ex ) {
870  wfDebug( __METHOD__ . ': Can\'t create a TitleValue for [[' .
871  $this->getPrefixedText() . ']]: ' . $ex->getMessage() . "\n" );
872  }
873  }
874 
875  return $this->mTitleValue;
876  }
877 
883  public function getText() {
884  return $this->mTextform;
885  }
886 
892  public function getPartialURL() {
893  return $this->mUrlform;
894  }
895 
901  public function getDBkey() {
902  return $this->mDbkeyform;
903  }
904 
910  function getUserCaseDBKey() {
911  if ( !is_null( $this->mUserCaseDBKey ) ) {
912  return $this->mUserCaseDBKey;
913  } else {
914  // If created via makeTitle(), $this->mUserCaseDBKey is not set.
915  return $this->mDbkeyform;
916  }
917  }
918 
924  public function getNamespace() {
925  return $this->mNamespace;
926  }
927 
934  public function getContentModel( $flags = 0 ) {
935  if ( !$this->mForcedContentModel
936  && ( !$this->mContentModel || $flags === Title::GAID_FOR_UPDATE )
937  && $this->getArticleID( $flags )
938  ) {
939  $linkCache = LinkCache::singleton();
940  $linkCache->addLinkObj( $this ); # in case we already had an article ID
941  $this->mContentModel = $linkCache->getGoodLinkFieldObj( $this, 'model' );
942  }
943 
944  if ( !$this->mContentModel ) {
945  $this->mContentModel = ContentHandler::getDefaultModelFor( $this );
946  }
947 
948  return $this->mContentModel;
949  }
950 
957  public function hasContentModel( $id ) {
958  return $this->getContentModel() == $id;
959  }
960 
972  public function setContentModel( $model ) {
973  $this->mContentModel = $model;
974  $this->mForcedContentModel = true;
975  }
976 
982  public function getNsText() {
983  if ( $this->isExternal() ) {
984  // This probably shouldn't even happen,
985  // but for interwiki transclusion it sometimes does.
986  // Use the canonical namespaces if possible to try to
987  // resolve a foreign namespace.
988  if ( MWNamespace::exists( $this->mNamespace ) ) {
989  return MWNamespace::getCanonicalName( $this->mNamespace );
990  }
991  }
992 
993  try {
994  $formatter = self::getTitleFormatter();
995  return $formatter->getNamespaceName( $this->mNamespace, $this->mDbkeyform );
996  } catch ( InvalidArgumentException $ex ) {
997  wfDebug( __METHOD__ . ': ' . $ex->getMessage() . "\n" );
998  return false;
999  }
1000  }
1001 
1007  public function getSubjectNsText() {
1009  return $wgContLang->getNsText( MWNamespace::getSubject( $this->mNamespace ) );
1010  }
1011 
1017  public function getTalkNsText() {
1019  return $wgContLang->getNsText( MWNamespace::getTalk( $this->mNamespace ) );
1020  }
1021 
1027  public function canTalk() {
1028  return MWNamespace::canTalk( $this->mNamespace );
1029  }
1030 
1036  public function canExist() {
1037  return $this->mNamespace >= NS_MAIN;
1038  }
1039 
1045  public function isWatchable() {
1046  return !$this->isExternal() && MWNamespace::isWatchable( $this->getNamespace() );
1047  }
1048 
1054  public function isSpecialPage() {
1055  return $this->getNamespace() == NS_SPECIAL;
1056  }
1057 
1064  public function isSpecial( $name ) {
1065  if ( $this->isSpecialPage() ) {
1066  list( $thisName, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $this->getDBkey() );
1067  if ( $name == $thisName ) {
1068  return true;
1069  }
1070  }
1071  return false;
1072  }
1073 
1080  public function fixSpecialName() {
1081  if ( $this->isSpecialPage() ) {
1082  list( $canonicalName, $par ) = SpecialPageFactory::resolveAlias( $this->mDbkeyform );
1083  if ( $canonicalName ) {
1084  $localName = SpecialPageFactory::getLocalNameFor( $canonicalName, $par );
1085  if ( $localName != $this->mDbkeyform ) {
1086  return Title::makeTitle( NS_SPECIAL, $localName );
1087  }
1088  }
1089  }
1090  return $this;
1091  }
1092 
1103  public function inNamespace( $ns ) {
1104  return MWNamespace::equals( $this->getNamespace(), $ns );
1105  }
1106 
1114  public function inNamespaces( /* ... */ ) {
1115  $namespaces = func_get_args();
1116  if ( count( $namespaces ) > 0 && is_array( $namespaces[0] ) ) {
1117  $namespaces = $namespaces[0];
1118  }
1119 
1120  foreach ( $namespaces as $ns ) {
1121  if ( $this->inNamespace( $ns ) ) {
1122  return true;
1123  }
1124  }
1125 
1126  return false;
1127  }
1128 
1142  public function hasSubjectNamespace( $ns ) {
1143  return MWNamespace::subjectEquals( $this->getNamespace(), $ns );
1144  }
1145 
1153  public function isContentPage() {
1154  return MWNamespace::isContent( $this->getNamespace() );
1155  }
1156 
1163  public function isMovable() {
1164  if ( !MWNamespace::isMovable( $this->getNamespace() ) || $this->isExternal() ) {
1165  // Interwiki title or immovable namespace. Hooks don't get to override here
1166  return false;
1167  }
1168 
1169  $result = true;
1170  Hooks::run( 'TitleIsMovable', [ $this, &$result ] );
1171  return $result;
1172  }
1173 
1184  public function isMainPage() {
1185  return $this->equals( Title::newMainPage() );
1186  }
1187 
1193  public function isSubpage() {
1194  return MWNamespace::hasSubpages( $this->mNamespace )
1195  ? strpos( $this->getText(), '/' ) !== false
1196  : false;
1197  }
1198 
1204  public function isConversionTable() {
1205  // @todo ConversionTable should become a separate content model.
1206 
1207  return $this->getNamespace() == NS_MEDIAWIKI &&
1208  strpos( $this->getText(), 'Conversiontable/' ) === 0;
1209  }
1210 
1216  public function isWikitextPage() {
1217  return $this->hasContentModel( CONTENT_MODEL_WIKITEXT );
1218  }
1219 
1234  public function isCssOrJsPage() {
1235  $isCssOrJsPage = NS_MEDIAWIKI == $this->mNamespace
1236  && ( $this->hasContentModel( CONTENT_MODEL_CSS )
1238 
1239  return $isCssOrJsPage;
1240  }
1241 
1247  public function isCssJsSubpage() {
1248  return ( NS_USER == $this->mNamespace && $this->isSubpage()
1249  && ( $this->hasContentModel( CONTENT_MODEL_CSS )
1250  || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
1251  }
1252 
1258  public function getSkinFromCssJsSubpage() {
1259  $subpage = explode( '/', $this->mTextform );
1260  $subpage = $subpage[count( $subpage ) - 1];
1261  $lastdot = strrpos( $subpage, '.' );
1262  if ( $lastdot === false ) {
1263  return $subpage; # Never happens: only called for names ending in '.css' or '.js'
1264  }
1265  return substr( $subpage, 0, $lastdot );
1266  }
1267 
1273  public function isCssSubpage() {
1274  return ( NS_USER == $this->mNamespace && $this->isSubpage()
1275  && $this->hasContentModel( CONTENT_MODEL_CSS ) );
1276  }
1277 
1283  public function isJsSubpage() {
1284  return ( NS_USER == $this->mNamespace && $this->isSubpage()
1286  }
1287 
1293  public function isTalkPage() {
1294  return MWNamespace::isTalk( $this->getNamespace() );
1295  }
1296 
1302  public function getTalkPage() {
1303  return Title::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
1304  }
1305 
1312  public function getSubjectPage() {
1313  // Is this the same title?
1314  $subjectNS = MWNamespace::getSubject( $this->getNamespace() );
1315  if ( $this->getNamespace() == $subjectNS ) {
1316  return $this;
1317  }
1318  return Title::makeTitle( $subjectNS, $this->getDBkey() );
1319  }
1320 
1329  public function getOtherPage() {
1330  if ( $this->isSpecialPage() ) {
1331  throw new MWException( 'Special pages cannot have other pages' );
1332  }
1333  if ( $this->isTalkPage() ) {
1334  return $this->getSubjectPage();
1335  } else {
1336  return $this->getTalkPage();
1337  }
1338  }
1339 
1345  public function getDefaultNamespace() {
1346  return $this->mDefaultNamespace;
1347  }
1348 
1356  public function getFragment() {
1357  return $this->mFragment;
1358  }
1359 
1366  public function hasFragment() {
1367  return $this->mFragment !== '';
1368  }
1369 
1374  public function getFragmentForURL() {
1375  if ( !$this->hasFragment() ) {
1376  return '';
1377  } else {
1378  return '#' . Title::escapeFragmentForURL( $this->getFragment() );
1379  }
1380  }
1381 
1394  public function setFragment( $fragment ) {
1395  $this->mFragment = strtr( substr( $fragment, 1 ), '_', ' ' );
1396  }
1397 
1405  public function createFragmentTarget( $fragment ) {
1406  return self::makeTitle(
1407  $this->getNamespace(),
1408  $this->getText(),
1409  $fragment,
1410  $this->getInterwiki()
1411  );
1412  }
1413 
1421  private function prefix( $name ) {
1422  $p = '';
1423  if ( $this->isExternal() ) {
1424  $p = $this->mInterwiki . ':';
1425  }
1426 
1427  if ( 0 != $this->mNamespace ) {
1428  $p .= $this->getNsText() . ':';
1429  }
1430  return $p . $name;
1431  }
1432 
1439  public function getPrefixedDBkey() {
1440  $s = $this->prefix( $this->mDbkeyform );
1441  $s = strtr( $s, ' ', '_' );
1442  return $s;
1443  }
1444 
1451  public function getPrefixedText() {
1452  if ( $this->mPrefixedText === null ) {
1453  $s = $this->prefix( $this->mTextform );
1454  $s = strtr( $s, '_', ' ' );
1455  $this->mPrefixedText = $s;
1456  }
1457  return $this->mPrefixedText;
1458  }
1459 
1465  public function __toString() {
1466  return $this->getPrefixedText();
1467  }
1468 
1475  public function getFullText() {
1476  $text = $this->getPrefixedText();
1477  if ( $this->hasFragment() ) {
1478  $text .= '#' . $this->getFragment();
1479  }
1480  return $text;
1481  }
1482 
1495  public function getRootText() {
1496  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1497  return $this->getText();
1498  }
1499 
1500  return strtok( $this->getText(), '/' );
1501  }
1502 
1515  public function getRootTitle() {
1516  return Title::makeTitle( $this->getNamespace(), $this->getRootText() );
1517  }
1518 
1530  public function getBaseText() {
1531  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1532  return $this->getText();
1533  }
1534 
1535  $parts = explode( '/', $this->getText() );
1536  # Don't discard the real title if there's no subpage involved
1537  if ( count( $parts ) > 1 ) {
1538  unset( $parts[count( $parts ) - 1] );
1539  }
1540  return implode( '/', $parts );
1541  }
1542 
1555  public function getBaseTitle() {
1556  return Title::makeTitle( $this->getNamespace(), $this->getBaseText() );
1557  }
1558 
1570  public function getSubpageText() {
1571  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1572  return $this->mTextform;
1573  }
1574  $parts = explode( '/', $this->mTextform );
1575  return $parts[count( $parts ) - 1];
1576  }
1577 
1591  public function getSubpage( $text ) {
1592  return Title::makeTitleSafe( $this->getNamespace(), $this->getText() . '/' . $text );
1593  }
1594 
1600  public function getSubpageUrlForm() {
1601  $text = $this->getSubpageText();
1602  $text = wfUrlencode( strtr( $text, ' ', '_' ) );
1603  return $text;
1604  }
1605 
1611  public function getPrefixedURL() {
1612  $s = $this->prefix( $this->mDbkeyform );
1613  $s = wfUrlencode( strtr( $s, ' ', '_' ) );
1614  return $s;
1615  }
1616 
1630  private static function fixUrlQueryArgs( $query, $query2 = false ) {
1631  if ( $query2 !== false ) {
1632  wfDeprecated( "Title::get{Canonical,Full,Link,Local,Internal}URL " .
1633  "method called with a second parameter is deprecated. Add your " .
1634  "parameter to an array passed as the first parameter.", "1.19" );
1635  }
1636  if ( is_array( $query ) ) {
1637  $query = wfArrayToCgi( $query );
1638  }
1639  if ( $query2 ) {
1640  if ( is_string( $query2 ) ) {
1641  // $query2 is a string, we will consider this to be
1642  // a deprecated $variant argument and add it to the query
1643  $query2 = wfArrayToCgi( [ 'variant' => $query2 ] );
1644  } else {
1645  $query2 = wfArrayToCgi( $query2 );
1646  }
1647  // If we have $query content add a & to it first
1648  if ( $query ) {
1649  $query .= '&';
1650  }
1651  // Now append the queries together
1652  $query .= $query2;
1653  }
1654  return $query;
1655  }
1656 
1668  public function getFullURL( $query = '', $query2 = false, $proto = PROTO_RELATIVE ) {
1669  $query = self::fixUrlQueryArgs( $query, $query2 );
1670 
1671  # Hand off all the decisions on urls to getLocalURL
1672  $url = $this->getLocalURL( $query );
1673 
1674  # Expand the url to make it a full url. Note that getLocalURL has the
1675  # potential to output full urls for a variety of reasons, so we use
1676  # wfExpandUrl instead of simply prepending $wgServer
1677  $url = wfExpandUrl( $url, $proto );
1678 
1679  # Finally, add the fragment.
1680  $url .= $this->getFragmentForURL();
1681  // Avoid PHP 7.1 warning from passing $this by reference
1682  $titleRef = $this;
1683  Hooks::run( 'GetFullURL', [ &$titleRef, &$url, $query ] );
1684  return $url;
1685  }
1686 
1703  public function getFullUrlForRedirect( $query = '', $proto = PROTO_CURRENT ) {
1704  $target = $this;
1705  if ( $this->isExternal() ) {
1706  $target = SpecialPage::getTitleFor(
1707  'GoToInterwiki',
1708  $this->getPrefixedDBKey()
1709  );
1710  }
1711  return $target->getFullUrl( $query, false, $proto );
1712  }
1713 
1737  public function getLocalURL( $query = '', $query2 = false ) {
1739 
1740  $query = self::fixUrlQueryArgs( $query, $query2 );
1741 
1742  $interwiki = self::getInterwikiLookup()->fetch( $this->mInterwiki );
1743  if ( $interwiki ) {
1744  $namespace = $this->getNsText();
1745  if ( $namespace != '' ) {
1746  # Can this actually happen? Interwikis shouldn't be parsed.
1747  # Yes! It can in interwiki transclusion. But... it probably shouldn't.
1748  $namespace .= ':';
1749  }
1750  $url = $interwiki->getURL( $namespace . $this->getDBkey() );
1751  $url = wfAppendQuery( $url, $query );
1752  } else {
1753  $dbkey = wfUrlencode( $this->getPrefixedDBkey() );
1754  if ( $query == '' ) {
1755  $url = str_replace( '$1', $dbkey, $wgArticlePath );
1756  // Avoid PHP 7.1 warning from passing $this by reference
1757  $titleRef = $this;
1758  Hooks::run( 'GetLocalURL::Article', [ &$titleRef, &$url ] );
1759  } else {
1760  global $wgVariantArticlePath, $wgActionPaths, $wgContLang;
1761  $url = false;
1762  $matches = [];
1763 
1764  if ( !empty( $wgActionPaths )
1765  && preg_match( '/^(.*&|)action=([^&]*)(&(.*)|)$/', $query, $matches )
1766  ) {
1767  $action = urldecode( $matches[2] );
1768  if ( isset( $wgActionPaths[$action] ) ) {
1769  $query = $matches[1];
1770  if ( isset( $matches[4] ) ) {
1771  $query .= $matches[4];
1772  }
1773  $url = str_replace( '$1', $dbkey, $wgActionPaths[$action] );
1774  if ( $query != '' ) {
1775  $url = wfAppendQuery( $url, $query );
1776  }
1777  }
1778  }
1779 
1780  if ( $url === false
1781  && $wgVariantArticlePath
1782  && preg_match( '/^variant=([^&]*)$/', $query, $matches )
1783  && $this->getPageLanguage()->equals( $wgContLang )
1784  && $this->getPageLanguage()->hasVariants()
1785  ) {
1786  $variant = urldecode( $matches[1] );
1787  if ( $this->getPageLanguage()->hasVariant( $variant ) ) {
1788  // Only do the variant replacement if the given variant is a valid
1789  // variant for the page's language.
1790  $url = str_replace( '$2', urlencode( $variant ), $wgVariantArticlePath );
1791  $url = str_replace( '$1', $dbkey, $url );
1792  }
1793  }
1794 
1795  if ( $url === false ) {
1796  if ( $query == '-' ) {
1797  $query = '';
1798  }
1799  $url = "{$wgScript}?title={$dbkey}&{$query}";
1800  }
1801  }
1802  // Avoid PHP 7.1 warning from passing $this by reference
1803  $titleRef = $this;
1804  Hooks::run( 'GetLocalURL::Internal', [ &$titleRef, &$url, $query ] );
1805 
1806  // @todo FIXME: This causes breakage in various places when we
1807  // actually expected a local URL and end up with dupe prefixes.
1808  if ( $wgRequest->getVal( 'action' ) == 'render' ) {
1809  $url = $wgServer . $url;
1810  }
1811  }
1812  // Avoid PHP 7.1 warning from passing $this by reference
1813  $titleRef = $this;
1814  Hooks::run( 'GetLocalURL', [ &$titleRef, &$url, $query ] );
1815  return $url;
1816  }
1817 
1835  public function getLinkURL( $query = '', $query2 = false, $proto = false ) {
1836  if ( $this->isExternal() || $proto !== false ) {
1837  $ret = $this->getFullURL( $query, $query2, $proto );
1838  } elseif ( $this->getPrefixedText() === '' && $this->hasFragment() ) {
1839  $ret = $this->getFragmentForURL();
1840  } else {
1841  $ret = $this->getLocalURL( $query, $query2 ) . $this->getFragmentForURL();
1842  }
1843  return $ret;
1844  }
1845 
1858  public function getInternalURL( $query = '', $query2 = false ) {
1860  $query = self::fixUrlQueryArgs( $query, $query2 );
1861  $server = $wgInternalServer !== false ? $wgInternalServer : $wgServer;
1862  $url = wfExpandUrl( $server . $this->getLocalURL( $query ), PROTO_HTTP );
1863  // Avoid PHP 7.1 warning from passing $this by reference
1864  $titleRef = $this;
1865  Hooks::run( 'GetInternalURL', [ &$titleRef, &$url, $query ] );
1866  return $url;
1867  }
1868 
1880  public function getCanonicalURL( $query = '', $query2 = false ) {
1881  $query = self::fixUrlQueryArgs( $query, $query2 );
1882  $url = wfExpandUrl( $this->getLocalURL( $query ) . $this->getFragmentForURL(), PROTO_CANONICAL );
1883  // Avoid PHP 7.1 warning from passing $this by reference
1884  $titleRef = $this;
1885  Hooks::run( 'GetCanonicalURL', [ &$titleRef, &$url, $query ] );
1886  return $url;
1887  }
1888 
1894  public function getEditURL() {
1895  if ( $this->isExternal() ) {
1896  return '';
1897  }
1898  $s = $this->getLocalURL( 'action=edit' );
1899 
1900  return $s;
1901  }
1902 
1917  public function quickUserCan( $action, $user = null ) {
1918  return $this->userCan( $action, $user, false );
1919  }
1920 
1930  public function userCan( $action, $user = null, $rigor = 'secure' ) {
1931  if ( !$user instanceof User ) {
1932  global $wgUser;
1933  $user = $wgUser;
1934  }
1935 
1936  return !count( $this->getUserPermissionsErrorsInternal( $action, $user, $rigor, true ) );
1937  }
1938 
1954  public function getUserPermissionsErrors(
1955  $action, $user, $rigor = 'secure', $ignoreErrors = []
1956  ) {
1957  $errors = $this->getUserPermissionsErrorsInternal( $action, $user, $rigor );
1958 
1959  // Remove the errors being ignored.
1960  foreach ( $errors as $index => $error ) {
1961  $errKey = is_array( $error ) ? $error[0] : $error;
1962 
1963  if ( in_array( $errKey, $ignoreErrors ) ) {
1964  unset( $errors[$index] );
1965  }
1966  if ( $errKey instanceof MessageSpecifier && in_array( $errKey->getKey(), $ignoreErrors ) ) {
1967  unset( $errors[$index] );
1968  }
1969  }
1970 
1971  return $errors;
1972  }
1973 
1985  private function checkQuickPermissions( $action, $user, $errors, $rigor, $short ) {
1986  if ( !Hooks::run( 'TitleQuickPermissions',
1987  [ $this, $user, $action, &$errors, ( $rigor !== 'quick' ), $short ] )
1988  ) {
1989  return $errors;
1990  }
1991 
1992  if ( $action == 'create' ) {
1993  if (
1994  ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
1995  ( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) )
1996  ) {
1997  $errors[] = $user->isAnon() ? [ 'nocreatetext' ] : [ 'nocreate-loggedin' ];
1998  }
1999  } elseif ( $action == 'move' ) {
2000  if ( !$user->isAllowed( 'move-rootuserpages' )
2001  && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
2002  // Show user page-specific message only if the user can move other pages
2003  $errors[] = [ 'cant-move-user-page' ];
2004  }
2005 
2006  // Check if user is allowed to move files if it's a file
2007  if ( $this->mNamespace == NS_FILE && !$user->isAllowed( 'movefile' ) ) {
2008  $errors[] = [ 'movenotallowedfile' ];
2009  }
2010 
2011  // Check if user is allowed to move category pages if it's a category page
2012  if ( $this->mNamespace == NS_CATEGORY && !$user->isAllowed( 'move-categorypages' ) ) {
2013  $errors[] = [ 'cant-move-category-page' ];
2014  }
2015 
2016  if ( !$user->isAllowed( 'move' ) ) {
2017  // User can't move anything
2018  $userCanMove = User::groupHasPermission( 'user', 'move' );
2019  $autoconfirmedCanMove = User::groupHasPermission( 'autoconfirmed', 'move' );
2020  if ( $user->isAnon() && ( $userCanMove || $autoconfirmedCanMove ) ) {
2021  // custom message if logged-in users without any special rights can move
2022  $errors[] = [ 'movenologintext' ];
2023  } else {
2024  $errors[] = [ 'movenotallowed' ];
2025  }
2026  }
2027  } elseif ( $action == 'move-target' ) {
2028  if ( !$user->isAllowed( 'move' ) ) {
2029  // User can't move anything
2030  $errors[] = [ 'movenotallowed' ];
2031  } elseif ( !$user->isAllowed( 'move-rootuserpages' )
2032  && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
2033  // Show user page-specific message only if the user can move other pages
2034  $errors[] = [ 'cant-move-to-user-page' ];
2035  } elseif ( !$user->isAllowed( 'move-categorypages' )
2036  && $this->mNamespace == NS_CATEGORY ) {
2037  // Show category page-specific message only if the user can move other pages
2038  $errors[] = [ 'cant-move-to-category-page' ];
2039  }
2040  } elseif ( !$user->isAllowed( $action ) ) {
2041  $errors[] = $this->missingPermissionError( $action, $short );
2042  }
2043 
2044  return $errors;
2045  }
2046 
2055  private function resultToError( $errors, $result ) {
2056  if ( is_array( $result ) && count( $result ) && !is_array( $result[0] ) ) {
2057  // A single array representing an error
2058  $errors[] = $result;
2059  } elseif ( is_array( $result ) && is_array( $result[0] ) ) {
2060  // A nested array representing multiple errors
2061  $errors = array_merge( $errors, $result );
2062  } elseif ( $result !== '' && is_string( $result ) ) {
2063  // A string representing a message-id
2064  $errors[] = [ $result ];
2065  } elseif ( $result instanceof MessageSpecifier ) {
2066  // A message specifier representing an error
2067  $errors[] = [ $result ];
2068  } elseif ( $result === false ) {
2069  // a generic "We don't want them to do that"
2070  $errors[] = [ 'badaccess-group0' ];
2071  }
2072  return $errors;
2073  }
2074 
2086  private function checkPermissionHooks( $action, $user, $errors, $rigor, $short ) {
2087  // Use getUserPermissionsErrors instead
2088  $result = '';
2089  // Avoid PHP 7.1 warning from passing $this by reference
2090  $titleRef = $this;
2091  if ( !Hooks::run( 'userCan', [ &$titleRef, &$user, $action, &$result ] ) ) {
2092  return $result ? [] : [ [ 'badaccess-group0' ] ];
2093  }
2094  // Check getUserPermissionsErrors hook
2095  // Avoid PHP 7.1 warning from passing $this by reference
2096  $titleRef = $this;
2097  if ( !Hooks::run( 'getUserPermissionsErrors', [ &$titleRef, &$user, $action, &$result ] ) ) {
2098  $errors = $this->resultToError( $errors, $result );
2099  }
2100  // Check getUserPermissionsErrorsExpensive hook
2101  if (
2102  $rigor !== 'quick'
2103  && !( $short && count( $errors ) > 0 )
2104  && !Hooks::run( 'getUserPermissionsErrorsExpensive', [ &$titleRef, &$user, $action, &$result ] )
2105  ) {
2106  $errors = $this->resultToError( $errors, $result );
2107  }
2108 
2109  return $errors;
2110  }
2111 
2123  private function checkSpecialsAndNSPermissions( $action, $user, $errors, $rigor, $short ) {
2124  # Only 'createaccount' can be performed on special pages,
2125  # which don't actually exist in the DB.
2126  if ( NS_SPECIAL == $this->mNamespace && $action !== 'createaccount' ) {
2127  $errors[] = [ 'ns-specialprotected' ];
2128  }
2129 
2130  # Check $wgNamespaceProtection for restricted namespaces
2131  if ( $this->isNamespaceProtected( $user ) ) {
2132  $ns = $this->mNamespace == NS_MAIN ?
2133  wfMessage( 'nstab-main' )->text() : $this->getNsText();
2134  $errors[] = $this->mNamespace == NS_MEDIAWIKI ?
2135  [ 'protectedinterface', $action ] : [ 'namespaceprotected', $ns, $action ];
2136  }
2137 
2138  return $errors;
2139  }
2140 
2152  private function checkCSSandJSPermissions( $action, $user, $errors, $rigor, $short ) {
2153  # Protect css/js subpages of user pages
2154  # XXX: this might be better using restrictions
2155  if ( $action != 'patrol' ) {
2156  if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
2157  if ( $this->isCssSubpage() && !$user->isAllowedAny( 'editmyusercss', 'editusercss' ) ) {
2158  $errors[] = [ 'mycustomcssprotected', $action ];
2159  } elseif ( $this->isJsSubpage() && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' ) ) {
2160  $errors[] = [ 'mycustomjsprotected', $action ];
2161  }
2162  } else {
2163  if ( $this->isCssSubpage() && !$user->isAllowed( 'editusercss' ) ) {
2164  $errors[] = [ 'customcssprotected', $action ];
2165  } elseif ( $this->isJsSubpage() && !$user->isAllowed( 'edituserjs' ) ) {
2166  $errors[] = [ 'customjsprotected', $action ];
2167  }
2168  }
2169  }
2170 
2171  return $errors;
2172  }
2173 
2187  private function checkPageRestrictions( $action, $user, $errors, $rigor, $short ) {
2188  foreach ( $this->getRestrictions( $action ) as $right ) {
2189  // Backwards compatibility, rewrite sysop -> editprotected
2190  if ( $right == 'sysop' ) {
2191  $right = 'editprotected';
2192  }
2193  // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
2194  if ( $right == 'autoconfirmed' ) {
2195  $right = 'editsemiprotected';
2196  }
2197  if ( $right == '' ) {
2198  continue;
2199  }
2200  if ( !$user->isAllowed( $right ) ) {
2201  $errors[] = [ 'protectedpagetext', $right, $action ];
2202  } elseif ( $this->mCascadeRestriction && !$user->isAllowed( 'protect' ) ) {
2203  $errors[] = [ 'protectedpagetext', 'protect', $action ];
2204  }
2205  }
2206 
2207  return $errors;
2208  }
2209 
2221  private function checkCascadingSourcesRestrictions( $action, $user, $errors, $rigor, $short ) {
2222  if ( $rigor !== 'quick' && !$this->isCssJsSubpage() ) {
2223  # We /could/ use the protection level on the source page, but it's
2224  # fairly ugly as we have to establish a precedence hierarchy for pages
2225  # included by multiple cascade-protected pages. So just restrict
2226  # it to people with 'protect' permission, as they could remove the
2227  # protection anyway.
2228  list( $cascadingSources, $restrictions ) = $this->getCascadeProtectionSources();
2229  # Cascading protection depends on more than this page...
2230  # Several cascading protected pages may include this page...
2231  # Check each cascading level
2232  # This is only for protection restrictions, not for all actions
2233  if ( isset( $restrictions[$action] ) ) {
2234  foreach ( $restrictions[$action] as $right ) {
2235  // Backwards compatibility, rewrite sysop -> editprotected
2236  if ( $right == 'sysop' ) {
2237  $right = 'editprotected';
2238  }
2239  // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
2240  if ( $right == 'autoconfirmed' ) {
2241  $right = 'editsemiprotected';
2242  }
2243  if ( $right != '' && !$user->isAllowedAll( 'protect', $right ) ) {
2244  $pages = '';
2245  foreach ( $cascadingSources as $page ) {
2246  $pages .= '* [[:' . $page->getPrefixedText() . "]]\n";
2247  }
2248  $errors[] = [ 'cascadeprotected', count( $cascadingSources ), $pages, $action ];
2249  }
2250  }
2251  }
2252  }
2253 
2254  return $errors;
2255  }
2256 
2268  private function checkActionPermissions( $action, $user, $errors, $rigor, $short ) {
2269  global $wgDeleteRevisionsLimit, $wgLang;
2270 
2271  if ( $action == 'protect' ) {
2272  if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $rigor, true ) ) ) {
2273  // If they can't edit, they shouldn't protect.
2274  $errors[] = [ 'protect-cantedit' ];
2275  }
2276  } elseif ( $action == 'create' ) {
2277  $title_protection = $this->getTitleProtection();
2278  if ( $title_protection ) {
2279  if ( $title_protection['permission'] == ''
2280  || !$user->isAllowed( $title_protection['permission'] )
2281  ) {
2282  $errors[] = [
2283  'titleprotected',
2284  User::whoIs( $title_protection['user'] ),
2285  $title_protection['reason']
2286  ];
2287  }
2288  }
2289  } elseif ( $action == 'move' ) {
2290  // Check for immobile pages
2291  if ( !MWNamespace::isMovable( $this->mNamespace ) ) {
2292  // Specific message for this case
2293  $errors[] = [ 'immobile-source-namespace', $this->getNsText() ];
2294  } elseif ( !$this->isMovable() ) {
2295  // Less specific message for rarer cases
2296  $errors[] = [ 'immobile-source-page' ];
2297  }
2298  } elseif ( $action == 'move-target' ) {
2299  if ( !MWNamespace::isMovable( $this->mNamespace ) ) {
2300  $errors[] = [ 'immobile-target-namespace', $this->getNsText() ];
2301  } elseif ( !$this->isMovable() ) {
2302  $errors[] = [ 'immobile-target-page' ];
2303  }
2304  } elseif ( $action == 'delete' ) {
2305  $tempErrors = $this->checkPageRestrictions( 'edit', $user, [], $rigor, true );
2306  if ( !$tempErrors ) {
2307  $tempErrors = $this->checkCascadingSourcesRestrictions( 'edit',
2308  $user, $tempErrors, $rigor, true );
2309  }
2310  if ( $tempErrors ) {
2311  // If protection keeps them from editing, they shouldn't be able to delete.
2312  $errors[] = [ 'deleteprotected' ];
2313  }
2314  if ( $rigor !== 'quick' && $wgDeleteRevisionsLimit
2315  && !$this->userCan( 'bigdelete', $user ) && $this->isBigDeletion()
2316  ) {
2317  $errors[] = [ 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ];
2318  }
2319  } elseif ( $action === 'undelete' ) {
2320  if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $rigor, true ) ) ) {
2321  // Undeleting implies editing
2322  $errors[] = [ 'undelete-cantedit' ];
2323  }
2324  if ( !$this->exists()
2325  && count( $this->getUserPermissionsErrorsInternal( 'create', $user, $rigor, true ) )
2326  ) {
2327  // Undeleting where nothing currently exists implies creating
2328  $errors[] = [ 'undelete-cantcreate' ];
2329  }
2330  }
2331  return $errors;
2332  }
2333 
2345  private function checkUserBlock( $action, $user, $errors, $rigor, $short ) {
2346  global $wgEmailConfirmToEdit, $wgBlockDisablesLogin;
2347  // Account creation blocks handled at userlogin.
2348  // Unblocking handled in SpecialUnblock
2349  if ( $rigor === 'quick' || in_array( $action, [ 'createaccount', 'unblock' ] ) ) {
2350  return $errors;
2351  }
2352 
2353  // Optimize for a very common case
2354  if ( $action === 'read' && !$wgBlockDisablesLogin ) {
2355  return $errors;
2356  }
2357 
2358  if ( $wgEmailConfirmToEdit && !$user->isEmailConfirmed() ) {
2359  $errors[] = [ 'confirmedittext' ];
2360  }
2361 
2362  $useSlave = ( $rigor !== 'secure' );
2363  if ( ( $action == 'edit' || $action == 'create' )
2364  && !$user->isBlockedFrom( $this, $useSlave )
2365  ) {
2366  // Don't block the user from editing their own talk page unless they've been
2367  // explicitly blocked from that too.
2368  } elseif ( $user->isBlocked() && $user->getBlock()->prevents( $action ) !== false ) {
2369  // @todo FIXME: Pass the relevant context into this function.
2370  $errors[] = $user->getBlock()->getPermissionsError( RequestContext::getMain() );
2371  }
2372 
2373  return $errors;
2374  }
2375 
2387  private function checkReadPermissions( $action, $user, $errors, $rigor, $short ) {
2388  global $wgWhitelistRead, $wgWhitelistReadRegexp;
2389 
2390  $whitelisted = false;
2391  if ( User::isEveryoneAllowed( 'read' ) ) {
2392  # Shortcut for public wikis, allows skipping quite a bit of code
2393  $whitelisted = true;
2394  } elseif ( $user->isAllowed( 'read' ) ) {
2395  # If the user is allowed to read pages, he is allowed to read all pages
2396  $whitelisted = true;
2397  } elseif ( $this->isSpecial( 'Userlogin' )
2398  || $this->isSpecial( 'PasswordReset' )
2399  || $this->isSpecial( 'Userlogout' )
2400  ) {
2401  # Always grant access to the login page.
2402  # Even anons need to be able to log in.
2403  $whitelisted = true;
2404  } elseif ( is_array( $wgWhitelistRead ) && count( $wgWhitelistRead ) ) {
2405  # Time to check the whitelist
2406  # Only do these checks is there's something to check against
2407  $name = $this->getPrefixedText();
2408  $dbName = $this->getPrefixedDBkey();
2409 
2410  // Check for explicit whitelisting with and without underscores
2411  if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) {
2412  $whitelisted = true;
2413  } elseif ( $this->getNamespace() == NS_MAIN ) {
2414  # Old settings might have the title prefixed with
2415  # a colon for main-namespace pages
2416  if ( in_array( ':' . $name, $wgWhitelistRead ) ) {
2417  $whitelisted = true;
2418  }
2419  } elseif ( $this->isSpecialPage() ) {
2420  # If it's a special page, ditch the subpage bit and check again
2421  $name = $this->getDBkey();
2422  list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name );
2423  if ( $name ) {
2424  $pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
2425  if ( in_array( $pure, $wgWhitelistRead, true ) ) {
2426  $whitelisted = true;
2427  }
2428  }
2429  }
2430  }
2431 
2432  if ( !$whitelisted && is_array( $wgWhitelistReadRegexp ) && !empty( $wgWhitelistReadRegexp ) ) {
2433  $name = $this->getPrefixedText();
2434  // Check for regex whitelisting
2435  foreach ( $wgWhitelistReadRegexp as $listItem ) {
2436  if ( preg_match( $listItem, $name ) ) {
2437  $whitelisted = true;
2438  break;
2439  }
2440  }
2441  }
2442 
2443  if ( !$whitelisted ) {
2444  # If the title is not whitelisted, give extensions a chance to do so...
2445  Hooks::run( 'TitleReadWhitelist', [ $this, $user, &$whitelisted ] );
2446  if ( !$whitelisted ) {
2447  $errors[] = $this->missingPermissionError( $action, $short );
2448  }
2449  }
2450 
2451  return $errors;
2452  }
2453 
2462  private function missingPermissionError( $action, $short ) {
2463  // We avoid expensive display logic for quickUserCan's and such
2464  if ( $short ) {
2465  return [ 'badaccess-group0' ];
2466  }
2467 
2468  return User::newFatalPermissionDeniedStatus( $action )->getErrorsArray()[0];
2469  }
2470 
2486  $action, $user, $rigor = 'secure', $short = false
2487  ) {
2488  if ( $rigor === true ) {
2489  $rigor = 'secure'; // b/c
2490  } elseif ( $rigor === false ) {
2491  $rigor = 'quick'; // b/c
2492  } elseif ( !in_array( $rigor, [ 'quick', 'full', 'secure' ] ) ) {
2493  throw new Exception( "Invalid rigor parameter '$rigor'." );
2494  }
2495 
2496  # Read has special handling
2497  if ( $action == 'read' ) {
2498  $checks = [
2499  'checkPermissionHooks',
2500  'checkReadPermissions',
2501  'checkUserBlock', // for wgBlockDisablesLogin
2502  ];
2503  # Don't call checkSpecialsAndNSPermissions or checkCSSandJSPermissions
2504  # here as it will lead to duplicate error messages. This is okay to do
2505  # since anywhere that checks for create will also check for edit, and
2506  # those checks are called for edit.
2507  } elseif ( $action == 'create' ) {
2508  $checks = [
2509  'checkQuickPermissions',
2510  'checkPermissionHooks',
2511  'checkPageRestrictions',
2512  'checkCascadingSourcesRestrictions',
2513  'checkActionPermissions',
2514  'checkUserBlock'
2515  ];
2516  } else {
2517  $checks = [
2518  'checkQuickPermissions',
2519  'checkPermissionHooks',
2520  'checkSpecialsAndNSPermissions',
2521  'checkCSSandJSPermissions',
2522  'checkPageRestrictions',
2523  'checkCascadingSourcesRestrictions',
2524  'checkActionPermissions',
2525  'checkUserBlock'
2526  ];
2527  }
2528 
2529  $errors = [];
2530  while ( count( $checks ) > 0 &&
2531  !( $short && count( $errors ) > 0 ) ) {
2532  $method = array_shift( $checks );
2533  $errors = $this->$method( $action, $user, $errors, $rigor, $short );
2534  }
2535 
2536  return $errors;
2537  }
2538 
2546  public static function getFilteredRestrictionTypes( $exists = true ) {
2547  global $wgRestrictionTypes;
2548  $types = $wgRestrictionTypes;
2549  if ( $exists ) {
2550  # Remove the create restriction for existing titles
2551  $types = array_diff( $types, [ 'create' ] );
2552  } else {
2553  # Only the create and upload restrictions apply to non-existing titles
2554  $types = array_intersect( $types, [ 'create', 'upload' ] );
2555  }
2556  return $types;
2557  }
2558 
2564  public function getRestrictionTypes() {
2565  if ( $this->isSpecialPage() ) {
2566  return [];
2567  }
2568 
2569  $types = self::getFilteredRestrictionTypes( $this->exists() );
2570 
2571  if ( $this->getNamespace() != NS_FILE ) {
2572  # Remove the upload restriction for non-file titles
2573  $types = array_diff( $types, [ 'upload' ] );
2574  }
2575 
2576  Hooks::run( 'TitleGetRestrictionTypes', [ $this, &$types ] );
2577 
2578  wfDebug( __METHOD__ . ': applicable restrictions to [[' .
2579  $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" );
2580 
2581  return $types;
2582  }
2583 
2591  public function getTitleProtection() {
2592  $protection = $this->getTitleProtectionInternal();
2593  if ( $protection ) {
2594  if ( $protection['permission'] == 'sysop' ) {
2595  $protection['permission'] = 'editprotected'; // B/C
2596  }
2597  if ( $protection['permission'] == 'autoconfirmed' ) {
2598  $protection['permission'] = 'editsemiprotected'; // B/C
2599  }
2600  }
2601  return $protection;
2602  }
2603 
2614  protected function getTitleProtectionInternal() {
2615  // Can't protect pages in special namespaces
2616  if ( $this->getNamespace() < 0 ) {
2617  return false;
2618  }
2619 
2620  // Can't protect pages that exist.
2621  if ( $this->exists() ) {
2622  return false;
2623  }
2624 
2625  if ( $this->mTitleProtection === null ) {
2626  $dbr = wfGetDB( DB_REPLICA );
2627  $res = $dbr->select(
2628  'protected_titles',
2629  [
2630  'user' => 'pt_user',
2631  'reason' => 'pt_reason',
2632  'expiry' => 'pt_expiry',
2633  'permission' => 'pt_create_perm'
2634  ],
2635  [ 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ],
2636  __METHOD__
2637  );
2638 
2639  // fetchRow returns false if there are no rows.
2640  $row = $dbr->fetchRow( $res );
2641  if ( $row ) {
2642  $row['expiry'] = $dbr->decodeExpiry( $row['expiry'] );
2643  }
2644  $this->mTitleProtection = $row;
2645  }
2646  return $this->mTitleProtection;
2647  }
2648 
2652  public function deleteTitleProtection() {
2653  $dbw = wfGetDB( DB_MASTER );
2654 
2655  $dbw->delete(
2656  'protected_titles',
2657  [ 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ],
2658  __METHOD__
2659  );
2660  $this->mTitleProtection = false;
2661  }
2662 
2670  public function isSemiProtected( $action = 'edit' ) {
2671  global $wgSemiprotectedRestrictionLevels;
2672 
2673  $restrictions = $this->getRestrictions( $action );
2674  $semi = $wgSemiprotectedRestrictionLevels;
2675  if ( !$restrictions || !$semi ) {
2676  // Not protected, or all protection is full protection
2677  return false;
2678  }
2679 
2680  // Remap autoconfirmed to editsemiprotected for BC
2681  foreach ( array_keys( $semi, 'autoconfirmed' ) as $key ) {
2682  $semi[$key] = 'editsemiprotected';
2683  }
2684  foreach ( array_keys( $restrictions, 'autoconfirmed' ) as $key ) {
2685  $restrictions[$key] = 'editsemiprotected';
2686  }
2687 
2688  return !array_diff( $restrictions, $semi );
2689  }
2690 
2698  public function isProtected( $action = '' ) {
2699  global $wgRestrictionLevels;
2700 
2701  $restrictionTypes = $this->getRestrictionTypes();
2702 
2703  # Special pages have inherent protection
2704  if ( $this->isSpecialPage() ) {
2705  return true;
2706  }
2707 
2708  # Check regular protection levels
2709  foreach ( $restrictionTypes as $type ) {
2710  if ( $action == $type || $action == '' ) {
2711  $r = $this->getRestrictions( $type );
2712  foreach ( $wgRestrictionLevels as $level ) {
2713  if ( in_array( $level, $r ) && $level != '' ) {
2714  return true;
2715  }
2716  }
2717  }
2718  }
2719 
2720  return false;
2721  }
2722 
2730  public function isNamespaceProtected( User $user ) {
2732 
2733  if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) {
2734  foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) {
2735  if ( $right != '' && !$user->isAllowed( $right ) ) {
2736  return true;
2737  }
2738  }
2739  }
2740  return false;
2741  }
2742 
2748  public function isCascadeProtected() {
2749  list( $sources, /* $restrictions */ ) = $this->getCascadeProtectionSources( false );
2750  return ( $sources > 0 );
2751  }
2752 
2762  public function areCascadeProtectionSourcesLoaded( $getPages = true ) {
2763  return $getPages ? $this->mCascadeSources !== null : $this->mHasCascadingRestrictions !== null;
2764  }
2765 
2779  public function getCascadeProtectionSources( $getPages = true ) {
2780  $pagerestrictions = [];
2781 
2782  if ( $this->mCascadeSources !== null && $getPages ) {
2784  } elseif ( $this->mHasCascadingRestrictions !== null && !$getPages ) {
2785  return [ $this->mHasCascadingRestrictions, $pagerestrictions ];
2786  }
2787 
2788  $dbr = wfGetDB( DB_REPLICA );
2789 
2790  if ( $this->getNamespace() == NS_FILE ) {
2791  $tables = [ 'imagelinks', 'page_restrictions' ];
2792  $where_clauses = [
2793  'il_to' => $this->getDBkey(),
2794  'il_from=pr_page',
2795  'pr_cascade' => 1
2796  ];
2797  } else {
2798  $tables = [ 'templatelinks', 'page_restrictions' ];
2799  $where_clauses = [
2800  'tl_namespace' => $this->getNamespace(),
2801  'tl_title' => $this->getDBkey(),
2802  'tl_from=pr_page',
2803  'pr_cascade' => 1
2804  ];
2805  }
2806 
2807  if ( $getPages ) {
2808  $cols = [ 'pr_page', 'page_namespace', 'page_title',
2809  'pr_expiry', 'pr_type', 'pr_level' ];
2810  $where_clauses[] = 'page_id=pr_page';
2811  $tables[] = 'page';
2812  } else {
2813  $cols = [ 'pr_expiry' ];
2814  }
2815 
2816  $res = $dbr->select( $tables, $cols, $where_clauses, __METHOD__ );
2817 
2818  $sources = $getPages ? [] : false;
2819  $now = wfTimestampNow();
2820 
2821  foreach ( $res as $row ) {
2822  $expiry = $dbr->decodeExpiry( $row->pr_expiry );
2823  if ( $expiry > $now ) {
2824  if ( $getPages ) {
2825  $page_id = $row->pr_page;
2826  $page_ns = $row->page_namespace;
2827  $page_title = $row->page_title;
2828  $sources[$page_id] = Title::makeTitle( $page_ns, $page_title );
2829  # Add groups needed for each restriction type if its not already there
2830  # Make sure this restriction type still exists
2831 
2832  if ( !isset( $pagerestrictions[$row->pr_type] ) ) {
2833  $pagerestrictions[$row->pr_type] = [];
2834  }
2835 
2836  if (
2837  isset( $pagerestrictions[$row->pr_type] )
2838  && !in_array( $row->pr_level, $pagerestrictions[$row->pr_type] )
2839  ) {
2840  $pagerestrictions[$row->pr_type][] = $row->pr_level;
2841  }
2842  } else {
2843  $sources = true;
2844  }
2845  }
2846  }
2847 
2848  if ( $getPages ) {
2849  $this->mCascadeSources = $sources;
2850  $this->mCascadingRestrictions = $pagerestrictions;
2851  } else {
2852  $this->mHasCascadingRestrictions = $sources;
2853  }
2854 
2855  return [ $sources, $pagerestrictions ];
2856  }
2857 
2865  public function areRestrictionsLoaded() {
2867  }
2868 
2878  public function getRestrictions( $action ) {
2879  if ( !$this->mRestrictionsLoaded ) {
2880  $this->loadRestrictions();
2881  }
2882  return isset( $this->mRestrictions[$action] )
2883  ? $this->mRestrictions[$action]
2884  : [];
2885  }
2886 
2894  public function getAllRestrictions() {
2895  if ( !$this->mRestrictionsLoaded ) {
2896  $this->loadRestrictions();
2897  }
2898  return $this->mRestrictions;
2899  }
2900 
2908  public function getRestrictionExpiry( $action ) {
2909  if ( !$this->mRestrictionsLoaded ) {
2910  $this->loadRestrictions();
2911  }
2912  return isset( $this->mRestrictionsExpiry[$action] ) ? $this->mRestrictionsExpiry[$action] : false;
2913  }
2914 
2921  if ( !$this->mRestrictionsLoaded ) {
2922  $this->loadRestrictions();
2923  }
2924 
2926  }
2927 
2937  public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
2938  $dbr = wfGetDB( DB_REPLICA );
2939 
2940  $restrictionTypes = $this->getRestrictionTypes();
2941 
2942  foreach ( $restrictionTypes as $type ) {
2943  $this->mRestrictions[$type] = [];
2944  $this->mRestrictionsExpiry[$type] = 'infinity';
2945  }
2946 
2947  $this->mCascadeRestriction = false;
2948 
2949  # Backwards-compatibility: also load the restrictions from the page record (old format).
2950  if ( $oldFashionedRestrictions !== null ) {
2951  $this->mOldRestrictions = $oldFashionedRestrictions;
2952  }
2953 
2954  if ( $this->mOldRestrictions === false ) {
2955  $this->mOldRestrictions = $dbr->selectField( 'page', 'page_restrictions',
2956  [ 'page_id' => $this->getArticleID() ], __METHOD__ );
2957  }
2958 
2959  if ( $this->mOldRestrictions != '' ) {
2960  foreach ( explode( ':', trim( $this->mOldRestrictions ) ) as $restrict ) {
2961  $temp = explode( '=', trim( $restrict ) );
2962  if ( count( $temp ) == 1 ) {
2963  // old old format should be treated as edit/move restriction
2964  $this->mRestrictions['edit'] = explode( ',', trim( $temp[0] ) );
2965  $this->mRestrictions['move'] = explode( ',', trim( $temp[0] ) );
2966  } else {
2967  $restriction = trim( $temp[1] );
2968  if ( $restriction != '' ) { // some old entries are empty
2969  $this->mRestrictions[$temp[0]] = explode( ',', $restriction );
2970  }
2971  }
2972  }
2973  }
2974 
2975  if ( count( $rows ) ) {
2976  # Current system - load second to make them override.
2977  $now = wfTimestampNow();
2978 
2979  # Cycle through all the restrictions.
2980  foreach ( $rows as $row ) {
2981  // Don't take care of restrictions types that aren't allowed
2982  if ( !in_array( $row->pr_type, $restrictionTypes ) ) {
2983  continue;
2984  }
2985 
2986  $expiry = $dbr->decodeExpiry( $row->pr_expiry );
2987 
2988  // Only apply the restrictions if they haven't expired!
2989  if ( !$expiry || $expiry > $now ) {
2990  $this->mRestrictionsExpiry[$row->pr_type] = $expiry;
2991  $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) );
2992 
2993  $this->mCascadeRestriction |= $row->pr_cascade;
2994  }
2995  }
2996  }
2997 
2998  $this->mRestrictionsLoaded = true;
2999  }
3000 
3007  public function loadRestrictions( $oldFashionedRestrictions = null ) {
3008  if ( $this->mRestrictionsLoaded ) {
3009  return;
3010  }
3011 
3012  $id = $this->getArticleID();
3013  if ( $id ) {
3015  $rows = $cache->getWithSetCallback(
3016  // Page protections always leave a new null revision
3017  $cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID() ),
3018  $cache::TTL_DAY,
3019  function ( $curValue, &$ttl, array &$setOpts ) {
3020  $dbr = wfGetDB( DB_REPLICA );
3021 
3022  $setOpts += Database::getCacheSetOptions( $dbr );
3023 
3024  return iterator_to_array(
3025  $dbr->select(
3026  'page_restrictions',
3027  [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ],
3028  [ 'pr_page' => $this->getArticleID() ],
3029  __METHOD__
3030  )
3031  );
3032  }
3033  );
3034 
3035  $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
3036  } else {
3037  $title_protection = $this->getTitleProtectionInternal();
3038 
3039  if ( $title_protection ) {
3040  $now = wfTimestampNow();
3041  $expiry = wfGetDB( DB_REPLICA )->decodeExpiry( $title_protection['expiry'] );
3042 
3043  if ( !$expiry || $expiry > $now ) {
3044  // Apply the restrictions
3045  $this->mRestrictionsExpiry['create'] = $expiry;
3046  $this->mRestrictions['create'] =
3047  explode( ',', trim( $title_protection['permission'] ) );
3048  } else { // Get rid of the old restrictions
3049  $this->mTitleProtection = false;
3050  }
3051  } else {
3052  $this->mRestrictionsExpiry['create'] = 'infinity';
3053  }
3054  $this->mRestrictionsLoaded = true;
3055  }
3056  }
3057 
3062  public function flushRestrictions() {
3063  $this->mRestrictionsLoaded = false;
3064  $this->mTitleProtection = null;
3065  }
3066 
3072  static function purgeExpiredRestrictions() {
3073  if ( wfReadOnly() ) {
3074  return;
3075  }
3076 
3078  wfGetDB( DB_MASTER ),
3079  __METHOD__,
3080  function ( IDatabase $dbw, $fname ) {
3081  $config = MediaWikiServices::getInstance()->getMainConfig();
3082  $ids = $dbw->selectFieldValues(
3083  'page_restrictions',
3084  'pr_id',
3085  [ 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ],
3086  $fname,
3087  [ 'LIMIT' => $config->get( 'UpdateRowsPerQuery' ) ] // T135470
3088  );
3089  if ( $ids ) {
3090  $dbw->delete( 'page_restrictions', [ 'pr_id' => $ids ], $fname );
3091  }
3092  }
3093  ) );
3094 
3096  wfGetDB( DB_MASTER ),
3097  __METHOD__,
3098  function ( IDatabase $dbw, $fname ) {
3099  $dbw->delete(
3100  'protected_titles',
3101  [ 'pt_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ],
3102  $fname
3103  );
3104  }
3105  ) );
3106  }
3107 
3113  public function hasSubpages() {
3114  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
3115  # Duh
3116  return false;
3117  }
3118 
3119  # We dynamically add a member variable for the purpose of this method
3120  # alone to cache the result. There's no point in having it hanging
3121  # around uninitialized in every Title object; therefore we only add it
3122  # if needed and don't declare it statically.
3123  if ( $this->mHasSubpages === null ) {
3124  $this->mHasSubpages = false;
3125  $subpages = $this->getSubpages( 1 );
3126  if ( $subpages instanceof TitleArray ) {
3127  $this->mHasSubpages = (bool)$subpages->count();
3128  }
3129  }
3130 
3131  return $this->mHasSubpages;
3132  }
3133 
3141  public function getSubpages( $limit = -1 ) {
3142  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
3143  return [];
3144  }
3145 
3146  $dbr = wfGetDB( DB_REPLICA );
3147  $conds['page_namespace'] = $this->getNamespace();
3148  $conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() );
3149  $options = [];
3150  if ( $limit > -1 ) {
3151  $options['LIMIT'] = $limit;
3152  }
3153  $this->mSubpages = TitleArray::newFromResult(
3154  $dbr->select( 'page',
3155  [ 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ],
3156  $conds,
3157  __METHOD__,
3158  $options
3159  )
3160  );
3161  return $this->mSubpages;
3162  }
3163 
3169  public function isDeleted() {
3170  if ( $this->getNamespace() < 0 ) {
3171  $n = 0;
3172  } else {
3173  $dbr = wfGetDB( DB_REPLICA );
3174 
3175  $n = $dbr->selectField( 'archive', 'COUNT(*)',
3176  [ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
3177  __METHOD__
3178  );
3179  if ( $this->getNamespace() == NS_FILE ) {
3180  $n += $dbr->selectField( 'filearchive', 'COUNT(*)',
3181  [ 'fa_name' => $this->getDBkey() ],
3182  __METHOD__
3183  );
3184  }
3185  }
3186  return (int)$n;
3187  }
3188 
3194  public function isDeletedQuick() {
3195  if ( $this->getNamespace() < 0 ) {
3196  return false;
3197  }
3198  $dbr = wfGetDB( DB_REPLICA );
3199  $deleted = (bool)$dbr->selectField( 'archive', '1',
3200  [ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
3201  __METHOD__
3202  );
3203  if ( !$deleted && $this->getNamespace() == NS_FILE ) {
3204  $deleted = (bool)$dbr->selectField( 'filearchive', '1',
3205  [ 'fa_name' => $this->getDBkey() ],
3206  __METHOD__
3207  );
3208  }
3209  return $deleted;
3210  }
3211 
3220  public function getArticleID( $flags = 0 ) {
3221  if ( $this->getNamespace() < 0 ) {
3222  $this->mArticleID = 0;
3223  return $this->mArticleID;
3224  }
3225  $linkCache = LinkCache::singleton();
3226  if ( $flags & self::GAID_FOR_UPDATE ) {
3227  $oldUpdate = $linkCache->forUpdate( true );
3228  $linkCache->clearLink( $this );
3229  $this->mArticleID = $linkCache->addLinkObj( $this );
3230  $linkCache->forUpdate( $oldUpdate );
3231  } else {
3232  if ( -1 == $this->mArticleID ) {
3233  $this->mArticleID = $linkCache->addLinkObj( $this );
3234  }
3235  }
3236  return $this->mArticleID;
3237  }
3238 
3246  public function isRedirect( $flags = 0 ) {
3247  if ( !is_null( $this->mRedirect ) ) {
3248  return $this->mRedirect;
3249  }
3250  if ( !$this->getArticleID( $flags ) ) {
3251  $this->mRedirect = false;
3252  return $this->mRedirect;
3253  }
3254 
3255  $linkCache = LinkCache::singleton();
3256  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3257  $cached = $linkCache->getGoodLinkFieldObj( $this, 'redirect' );
3258  if ( $cached === null ) {
3259  # Trust LinkCache's state over our own
3260  # LinkCache is telling us that the page doesn't exist, despite there being cached
3261  # data relating to an existing page in $this->mArticleID. Updaters should clear
3262  # LinkCache as appropriate, or use $flags = Title::GAID_FOR_UPDATE. If that flag is
3263  # set, then LinkCache will definitely be up to date here, since getArticleID() forces
3264  # LinkCache to refresh its data from the master.
3265  $this->mRedirect = false;
3266  return $this->mRedirect;
3267  }
3268 
3269  $this->mRedirect = (bool)$cached;
3270 
3271  return $this->mRedirect;
3272  }
3273 
3281  public function getLength( $flags = 0 ) {
3282  if ( $this->mLength != -1 ) {
3283  return $this->mLength;
3284  }
3285  if ( !$this->getArticleID( $flags ) ) {
3286  $this->mLength = 0;
3287  return $this->mLength;
3288  }
3289  $linkCache = LinkCache::singleton();
3290  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3291  $cached = $linkCache->getGoodLinkFieldObj( $this, 'length' );
3292  if ( $cached === null ) {
3293  # Trust LinkCache's state over our own, as for isRedirect()
3294  $this->mLength = 0;
3295  return $this->mLength;
3296  }
3297 
3298  $this->mLength = intval( $cached );
3299 
3300  return $this->mLength;
3301  }
3302 
3309  public function getLatestRevID( $flags = 0 ) {
3310  if ( !( $flags & Title::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
3311  return intval( $this->mLatestID );
3312  }
3313  if ( !$this->getArticleID( $flags ) ) {
3314  $this->mLatestID = 0;
3315  return $this->mLatestID;
3316  }
3317  $linkCache = LinkCache::singleton();
3318  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3319  $cached = $linkCache->getGoodLinkFieldObj( $this, 'revision' );
3320  if ( $cached === null ) {
3321  # Trust LinkCache's state over our own, as for isRedirect()
3322  $this->mLatestID = 0;
3323  return $this->mLatestID;
3324  }
3325 
3326  $this->mLatestID = intval( $cached );
3327 
3328  return $this->mLatestID;
3329  }
3330 
3341  public function resetArticleID( $newid ) {
3342  $linkCache = LinkCache::singleton();
3343  $linkCache->clearLink( $this );
3344 
3345  if ( $newid === false ) {
3346  $this->mArticleID = -1;
3347  } else {
3348  $this->mArticleID = intval( $newid );
3349  }
3350  $this->mRestrictionsLoaded = false;
3351  $this->mRestrictions = [];
3352  $this->mOldRestrictions = false;
3353  $this->mRedirect = null;
3354  $this->mLength = -1;
3355  $this->mLatestID = false;
3356  $this->mContentModel = false;
3357  $this->mEstimateRevisions = null;
3358  $this->mPageLanguage = false;
3359  $this->mDbPageLanguage = false;
3360  $this->mIsBigDeletion = null;
3361  }
3362 
3363  public static function clearCaches() {
3364  $linkCache = LinkCache::singleton();
3365  $linkCache->clear();
3366 
3368  $titleCache->clear();
3369  }
3370 
3378  public static function capitalize( $text, $ns = NS_MAIN ) {
3380 
3381  if ( MWNamespace::isCapitalized( $ns ) ) {
3382  return $wgContLang->ucfirst( $text );
3383  } else {
3384  return $text;
3385  }
3386  }
3387 
3400  private function secureAndSplit() {
3401  # Initialisation
3402  $this->mInterwiki = '';
3403  $this->mFragment = '';
3404  $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
3405 
3406  $dbkey = $this->mDbkeyform;
3407 
3408  // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
3409  // the parsing code with Title, while avoiding massive refactoring.
3410  // @todo: get rid of secureAndSplit, refactor parsing code.
3411  // @note: getTitleParser() returns a TitleParser implementation which does not have a
3412  // splitTitleString method, but the only implementation (MediaWikiTitleCodec) does
3413  $titleCodec = MediaWikiServices::getInstance()->getTitleParser();
3414  // MalformedTitleException can be thrown here
3415  $parts = $titleCodec->splitTitleString( $dbkey, $this->getDefaultNamespace() );
3416 
3417  # Fill fields
3418  $this->setFragment( '#' . $parts['fragment'] );
3419  $this->mInterwiki = $parts['interwiki'];
3420  $this->mLocalInterwiki = $parts['local_interwiki'];
3421  $this->mNamespace = $parts['namespace'];
3422  $this->mUserCaseDBKey = $parts['user_case_dbkey'];
3423 
3424  $this->mDbkeyform = $parts['dbkey'];
3425  $this->mUrlform = wfUrlencode( $this->mDbkeyform );
3426  $this->mTextform = strtr( $this->mDbkeyform, '_', ' ' );
3427 
3428  # We already know that some pages won't be in the database!
3429  if ( $this->isExternal() || $this->mNamespace == NS_SPECIAL ) {
3430  $this->mArticleID = 0;
3431  }
3432 
3433  return true;
3434  }
3435 
3448  public function getLinksTo( $options = [], $table = 'pagelinks', $prefix = 'pl' ) {
3449  if ( count( $options ) > 0 ) {
3450  $db = wfGetDB( DB_MASTER );
3451  } else {
3452  $db = wfGetDB( DB_REPLICA );
3453  }
3454 
3455  $res = $db->select(
3456  [ 'page', $table ],
3457  self::getSelectFields(),
3458  [
3459  "{$prefix}_from=page_id",
3460  "{$prefix}_namespace" => $this->getNamespace(),
3461  "{$prefix}_title" => $this->getDBkey() ],
3462  __METHOD__,
3463  $options
3464  );
3465 
3466  $retVal = [];
3467  if ( $res->numRows() ) {
3468  $linkCache = LinkCache::singleton();
3469  foreach ( $res as $row ) {
3470  $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title );
3471  if ( $titleObj ) {
3472  $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
3473  $retVal[] = $titleObj;
3474  }
3475  }
3476  }
3477  return $retVal;
3478  }
3479 
3490  public function getTemplateLinksTo( $options = [] ) {
3491  return $this->getLinksTo( $options, 'templatelinks', 'tl' );
3492  }
3493 
3506  public function getLinksFrom( $options = [], $table = 'pagelinks', $prefix = 'pl' ) {
3507  $id = $this->getArticleID();
3508 
3509  # If the page doesn't exist; there can't be any link from this page
3510  if ( !$id ) {
3511  return [];
3512  }
3513 
3514  $db = wfGetDB( DB_REPLICA );
3515 
3516  $blNamespace = "{$prefix}_namespace";
3517  $blTitle = "{$prefix}_title";
3518 
3519  $res = $db->select(
3520  [ $table, 'page' ],
3521  array_merge(
3522  [ $blNamespace, $blTitle ],
3524  ),
3525  [ "{$prefix}_from" => $id ],
3526  __METHOD__,
3527  $options,
3528  [ 'page' => [
3529  'LEFT JOIN',
3530  [ "page_namespace=$blNamespace", "page_title=$blTitle" ]
3531  ] ]
3532  );
3533 
3534  $retVal = [];
3535  $linkCache = LinkCache::singleton();
3536  foreach ( $res as $row ) {
3537  if ( $row->page_id ) {
3538  $titleObj = Title::newFromRow( $row );
3539  } else {
3540  $titleObj = Title::makeTitle( $row->$blNamespace, $row->$blTitle );
3541  $linkCache->addBadLinkObj( $titleObj );
3542  }
3543  $retVal[] = $titleObj;
3544  }
3545 
3546  return $retVal;
3547  }
3548 
3559  public function getTemplateLinksFrom( $options = [] ) {
3560  return $this->getLinksFrom( $options, 'templatelinks', 'tl' );
3561  }
3562 
3571  public function getBrokenLinksFrom() {
3572  if ( $this->getArticleID() == 0 ) {
3573  # All links from article ID 0 are false positives
3574  return [];
3575  }
3576 
3577  $dbr = wfGetDB( DB_REPLICA );
3578  $res = $dbr->select(
3579  [ 'page', 'pagelinks' ],
3580  [ 'pl_namespace', 'pl_title' ],
3581  [
3582  'pl_from' => $this->getArticleID(),
3583  'page_namespace IS NULL'
3584  ],
3585  __METHOD__, [],
3586  [
3587  'page' => [
3588  'LEFT JOIN',
3589  [ 'pl_namespace=page_namespace', 'pl_title=page_title' ]
3590  ]
3591  ]
3592  );
3593 
3594  $retVal = [];
3595  foreach ( $res as $row ) {
3596  $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
3597  }
3598  return $retVal;
3599  }
3600 
3607  public function getCdnUrls() {
3608  $urls = [
3609  $this->getInternalURL(),
3610  $this->getInternalURL( 'action=history' )
3611  ];
3612 
3613  $pageLang = $this->getPageLanguage();
3614  if ( $pageLang->hasVariants() ) {
3615  $variants = $pageLang->getVariants();
3616  foreach ( $variants as $vCode ) {
3617  $urls[] = $this->getInternalURL( $vCode );
3618  }
3619  }
3620 
3621  // If we are looking at a css/js user subpage, purge the action=raw.
3622  if ( $this->isJsSubpage() ) {
3623  $urls[] = $this->getInternalURL( 'action=raw&ctype=text/javascript' );
3624  } elseif ( $this->isCssSubpage() ) {
3625  $urls[] = $this->getInternalURL( 'action=raw&ctype=text/css' );
3626  }
3627 
3628  Hooks::run( 'TitleSquidURLs', [ $this, &$urls ] );
3629  return $urls;
3630  }
3631 
3635  public function getSquidURLs() {
3636  return $this->getCdnUrls();
3637  }
3638 
3642  public function purgeSquid() {
3644  new CdnCacheUpdate( $this->getCdnUrls() ),
3646  );
3647  }
3648 
3659  public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) {
3660  global $wgUser;
3661 
3662  if ( !( $nt instanceof Title ) ) {
3663  // Normally we'd add this to $errors, but we'll get
3664  // lots of syntax errors if $nt is not an object
3665  return [ [ 'badtitletext' ] ];
3666  }
3667 
3668  $mp = new MovePage( $this, $nt );
3669  $errors = $mp->isValidMove()->getErrorsArray();
3670  if ( $auth ) {
3671  $errors = wfMergeErrorArrays(
3672  $errors,
3673  $mp->checkPermissions( $wgUser, $reason )->getErrorsArray()
3674  );
3675  }
3676 
3677  return $errors ?: true;
3678  }
3679 
3686  protected function validateFileMoveOperation( $nt ) {
3687  global $wgUser;
3688 
3689  $errors = [];
3690 
3691  $destFile = wfLocalFile( $nt );
3692  $destFile->load( File::READ_LATEST );
3693  if ( !$wgUser->isAllowed( 'reupload-shared' )
3694  && !$destFile->exists() && wfFindFile( $nt )
3695  ) {
3696  $errors[] = [ 'file-exists-sharedrepo' ];
3697  }
3698 
3699  return $errors;
3700  }
3701 
3715  public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true,
3716  array $changeTags = [] ) {
3717 
3718  global $wgUser;
3719  $err = $this->isValidMoveOperation( $nt, $auth, $reason );
3720  if ( is_array( $err ) ) {
3721  // Auto-block user's IP if the account was "hard" blocked
3722  $wgUser->spreadAnyEditBlock();
3723  return $err;
3724  }
3725  // Check suppressredirect permission
3726  if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
3727  $createRedirect = true;
3728  }
3729 
3730  $mp = new MovePage( $this, $nt );
3731  $status = $mp->move( $wgUser, $reason, $createRedirect, $changeTags );
3732  if ( $status->isOK() ) {
3733  return true;
3734  } else {
3735  return $status->getErrorsArray();
3736  }
3737  }
3738 
3753  public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true,
3754  array $changeTags = [] ) {
3755 
3756  global $wgMaximumMovedPages;
3757  // Check permissions
3758  if ( !$this->userCan( 'move-subpages' ) ) {
3759  return [
3760  [ 'cant-move-subpages' ],
3761  ];
3762  }
3763  // Do the source and target namespaces support subpages?
3764  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
3765  return [
3766  [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $this->getNamespace() ) ],
3767  ];
3768  }
3769  if ( !MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
3770  return [
3771  [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $nt->getNamespace() ) ],
3772  ];
3773  }
3774 
3775  $subpages = $this->getSubpages( $wgMaximumMovedPages + 1 );
3776  $retval = [];
3777  $count = 0;
3778  foreach ( $subpages as $oldSubpage ) {
3779  $count++;
3780  if ( $count > $wgMaximumMovedPages ) {
3781  $retval[$oldSubpage->getPrefixedText()] = [
3782  [ 'movepage-max-pages', $wgMaximumMovedPages ],
3783  ];
3784  break;
3785  }
3786 
3787  // We don't know whether this function was called before
3788  // or after moving the root page, so check both
3789  // $this and $nt
3790  if ( $oldSubpage->getArticleID() == $this->getArticleID()
3791  || $oldSubpage->getArticleID() == $nt->getArticleID()
3792  ) {
3793  // When moving a page to a subpage of itself,
3794  // don't move it twice
3795  continue;
3796  }
3797  $newPageName = preg_replace(
3798  '#^' . preg_quote( $this->getDBkey(), '#' ) . '#',
3799  StringUtils::escapeRegexReplacement( $nt->getDBkey() ), # T23234
3800  $oldSubpage->getDBkey() );
3801  if ( $oldSubpage->isTalkPage() ) {
3802  $newNs = $nt->getTalkPage()->getNamespace();
3803  } else {
3804  $newNs = $nt->getSubjectPage()->getNamespace();
3805  }
3806  # T16385: we need makeTitleSafe because the new page names may
3807  # be longer than 255 characters.
3808  $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
3809 
3810  $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect, $changeTags );
3811  if ( $success === true ) {
3812  $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
3813  } else {
3814  $retval[$oldSubpage->getPrefixedText()] = $success;
3815  }
3816  }
3817  return $retval;
3818  }
3819 
3826  public function isSingleRevRedirect() {
3827  global $wgContentHandlerUseDB;
3828 
3829  $dbw = wfGetDB( DB_MASTER );
3830 
3831  # Is it a redirect?
3832  $fields = [ 'page_is_redirect', 'page_latest', 'page_id' ];
3833  if ( $wgContentHandlerUseDB ) {
3834  $fields[] = 'page_content_model';
3835  }
3836 
3837  $row = $dbw->selectRow( 'page',
3838  $fields,
3839  $this->pageCond(),
3840  __METHOD__,
3841  [ 'FOR UPDATE' ]
3842  );
3843  # Cache some fields we may want
3844  $this->mArticleID = $row ? intval( $row->page_id ) : 0;
3845  $this->mRedirect = $row ? (bool)$row->page_is_redirect : false;
3846  $this->mLatestID = $row ? intval( $row->page_latest ) : false;
3847  $this->mContentModel = $row && isset( $row->page_content_model )
3848  ? strval( $row->page_content_model )
3849  : false;
3850 
3851  if ( !$this->mRedirect ) {
3852  return false;
3853  }
3854  # Does the article have a history?
3855  $row = $dbw->selectField( [ 'page', 'revision' ],
3856  'rev_id',
3857  [ 'page_namespace' => $this->getNamespace(),
3858  'page_title' => $this->getDBkey(),
3859  'page_id=rev_page',
3860  'page_latest != rev_id'
3861  ],
3862  __METHOD__,
3863  [ 'FOR UPDATE' ]
3864  );
3865  # Return true if there was no history
3866  return ( $row === false );
3867  }
3868 
3877  public function isValidMoveTarget( $nt ) {
3878  # Is it an existing file?
3879  if ( $nt->getNamespace() == NS_FILE ) {
3880  $file = wfLocalFile( $nt );
3881  $file->load( File::READ_LATEST );
3882  if ( $file->exists() ) {
3883  wfDebug( __METHOD__ . ": file exists\n" );
3884  return false;
3885  }
3886  }
3887  # Is it a redirect with no history?
3888  if ( !$nt->isSingleRevRedirect() ) {
3889  wfDebug( __METHOD__ . ": not a one-rev redirect\n" );
3890  return false;
3891  }
3892  # Get the article text
3893  $rev = Revision::newFromTitle( $nt, false, Revision::READ_LATEST );
3894  if ( !is_object( $rev ) ) {
3895  return false;
3896  }
3897  $content = $rev->getContent();
3898  # Does the redirect point to the source?
3899  # Or is it a broken self-redirect, usually caused by namespace collisions?
3900  $redirTitle = $content ? $content->getRedirectTarget() : null;
3901 
3902  if ( $redirTitle ) {
3903  if ( $redirTitle->getPrefixedDBkey() != $this->getPrefixedDBkey() &&
3904  $redirTitle->getPrefixedDBkey() != $nt->getPrefixedDBkey() ) {
3905  wfDebug( __METHOD__ . ": redirect points to other page\n" );
3906  return false;
3907  } else {
3908  return true;
3909  }
3910  } else {
3911  # Fail safe (not a redirect after all. strange.)
3912  wfDebug( __METHOD__ . ": failsafe: database sais " . $nt->getPrefixedDBkey() .
3913  " is a redirect, but it doesn't contain a valid redirect.\n" );
3914  return false;
3915  }
3916  }
3917 
3925  public function getParentCategories() {
3927 
3928  $data = [];
3929 
3930  $titleKey = $this->getArticleID();
3931 
3932  if ( $titleKey === 0 ) {
3933  return $data;
3934  }
3935 
3936  $dbr = wfGetDB( DB_REPLICA );
3937 
3938  $res = $dbr->select(
3939  'categorylinks',
3940  'cl_to',
3941  [ 'cl_from' => $titleKey ],
3942  __METHOD__
3943  );
3944 
3945  if ( $res->numRows() > 0 ) {
3946  foreach ( $res as $row ) {
3947  // $data[] = Title::newFromText($wgContLang->getNsText ( NS_CATEGORY ).':'.$row->cl_to);
3948  $data[$wgContLang->getNsText( NS_CATEGORY ) . ':' . $row->cl_to] = $this->getFullText();
3949  }
3950  }
3951  return $data;
3952  }
3953 
3960  public function getParentCategoryTree( $children = [] ) {
3961  $stack = [];
3962  $parents = $this->getParentCategories();
3963 
3964  if ( $parents ) {
3965  foreach ( $parents as $parent => $current ) {
3966  if ( array_key_exists( $parent, $children ) ) {
3967  # Circular reference
3968  $stack[$parent] = [];
3969  } else {
3970  $nt = Title::newFromText( $parent );
3971  if ( $nt ) {
3972  $stack[$parent] = $nt->getParentCategoryTree( $children + [ $parent => 1 ] );
3973  }
3974  }
3975  }
3976  }
3977 
3978  return $stack;
3979  }
3980 
3987  public function pageCond() {
3988  if ( $this->mArticleID > 0 ) {
3989  // PK avoids secondary lookups in InnoDB, shouldn't hurt other DBs
3990  return [ 'page_id' => $this->mArticleID ];
3991  } else {
3992  return [ 'page_namespace' => $this->mNamespace, 'page_title' => $this->mDbkeyform ];
3993  }
3994  }
3995 
4003  public function getPreviousRevisionID( $revId, $flags = 0 ) {
4004  /* This function and getNextRevisionID have bad performance when
4005  used on a page with many revisions on mysql. An explicit extended
4006  primary key may help in some cases, if the PRIMARY KEY is banned:
4007  T159319 */
4008  if ( $flags & self::GAID_FOR_UPDATE ) {
4009  $db = wfGetDB( DB_MASTER );
4010  } else {
4011  $db = wfGetDB( DB_REPLICA, 'contributions' );
4012  }
4013  $revId = $db->selectField( 'revision', 'rev_id',
4014  [
4015  'rev_page' => $this->getArticleID( $flags ),
4016  'rev_id < ' . intval( $revId )
4017  ],
4018  __METHOD__,
4019  [ 'ORDER BY' => 'rev_id DESC', 'IGNORE INDEX' => 'PRIMARY' ]
4020  );
4021 
4022  if ( $revId === false ) {
4023  return false;
4024  } else {
4025  return intval( $revId );
4026  }
4027  }
4028 
4036  public function getNextRevisionID( $revId, $flags = 0 ) {
4037  if ( $flags & self::GAID_FOR_UPDATE ) {
4038  $db = wfGetDB( DB_MASTER );
4039  } else {
4040  $db = wfGetDB( DB_REPLICA, 'contributions' );
4041  }
4042  $revId = $db->selectField( 'revision', 'rev_id',
4043  [
4044  'rev_page' => $this->getArticleID( $flags ),
4045  'rev_id > ' . intval( $revId )
4046  ],
4047  __METHOD__,
4048  [ 'ORDER BY' => 'rev_id', 'IGNORE INDEX' => 'PRIMARY' ]
4049  );
4050 
4051  if ( $revId === false ) {
4052  return false;
4053  } else {
4054  return intval( $revId );
4055  }
4056  }
4057 
4064  public function getFirstRevision( $flags = 0 ) {
4065  $pageId = $this->getArticleID( $flags );
4066  if ( $pageId ) {
4068  $row = $db->selectRow( 'revision', Revision::selectFields(),
4069  [ 'rev_page' => $pageId ],
4070  __METHOD__,
4071  [
4072  'ORDER BY' => 'rev_timestamp ASC',
4073  'IGNORE INDEX' => 'rev_timestamp'
4074  ]
4075  );
4076  if ( $row ) {
4077  return new Revision( $row );
4078  }
4079  }
4080  return null;
4081  }
4082 
4089  public function getEarliestRevTime( $flags = 0 ) {
4090  $rev = $this->getFirstRevision( $flags );
4091  return $rev ? $rev->getTimestamp() : null;
4092  }
4093 
4099  public function isNewPage() {
4100  $dbr = wfGetDB( DB_REPLICA );
4101  return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
4102  }
4103 
4109  public function isBigDeletion() {
4110  global $wgDeleteRevisionsLimit;
4111 
4112  if ( !$wgDeleteRevisionsLimit ) {
4113  return false;
4114  }
4115 
4116  if ( $this->mIsBigDeletion === null ) {
4117  $dbr = wfGetDB( DB_REPLICA );
4118 
4119  $revCount = $dbr->selectRowCount(
4120  'revision',
4121  '1',
4122  [ 'rev_page' => $this->getArticleID() ],
4123  __METHOD__,
4124  [ 'LIMIT' => $wgDeleteRevisionsLimit + 1 ]
4125  );
4126 
4127  $this->mIsBigDeletion = $revCount > $wgDeleteRevisionsLimit;
4128  }
4129 
4130  return $this->mIsBigDeletion;
4131  }
4132 
4138  public function estimateRevisionCount() {
4139  if ( !$this->exists() ) {
4140  return 0;
4141  }
4142 
4143  if ( $this->mEstimateRevisions === null ) {
4144  $dbr = wfGetDB( DB_REPLICA );
4145  $this->mEstimateRevisions = $dbr->estimateRowCount( 'revision', '*',
4146  [ 'rev_page' => $this->getArticleID() ], __METHOD__ );
4147  }
4148 
4150  }
4151 
4161  public function countRevisionsBetween( $old, $new, $max = null ) {
4162  if ( !( $old instanceof Revision ) ) {
4163  $old = Revision::newFromTitle( $this, (int)$old );
4164  }
4165  if ( !( $new instanceof Revision ) ) {
4166  $new = Revision::newFromTitle( $this, (int)$new );
4167  }
4168  if ( !$old || !$new ) {
4169  return 0; // nothing to compare
4170  }
4171  $dbr = wfGetDB( DB_REPLICA );
4172  $conds = [
4173  'rev_page' => $this->getArticleID(),
4174  'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4175  'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4176  ];
4177  if ( $max !== null ) {
4178  return $dbr->selectRowCount( 'revision', '1',
4179  $conds,
4180  __METHOD__,
4181  [ 'LIMIT' => $max + 1 ] // extra to detect truncation
4182  );
4183  } else {
4184  return (int)$dbr->selectField( 'revision', 'count(*)', $conds, __METHOD__ );
4185  }
4186  }
4187 
4204  public function getAuthorsBetween( $old, $new, $limit, $options = [] ) {
4205  if ( !( $old instanceof Revision ) ) {
4206  $old = Revision::newFromTitle( $this, (int)$old );
4207  }
4208  if ( !( $new instanceof Revision ) ) {
4209  $new = Revision::newFromTitle( $this, (int)$new );
4210  }
4211  // XXX: what if Revision objects are passed in, but they don't refer to this title?
4212  // Add $old->getPage() != $new->getPage() || $old->getPage() != $this->getArticleID()
4213  // in the sanity check below?
4214  if ( !$old || !$new ) {
4215  return null; // nothing to compare
4216  }
4217  $authors = [];
4218  $old_cmp = '>';
4219  $new_cmp = '<';
4220  $options = (array)$options;
4221  if ( in_array( 'include_old', $options ) ) {
4222  $old_cmp = '>=';
4223  }
4224  if ( in_array( 'include_new', $options ) ) {
4225  $new_cmp = '<=';
4226  }
4227  if ( in_array( 'include_both', $options ) ) {
4228  $old_cmp = '>=';
4229  $new_cmp = '<=';
4230  }
4231  // No DB query needed if $old and $new are the same or successive revisions:
4232  if ( $old->getId() === $new->getId() ) {
4233  return ( $old_cmp === '>' && $new_cmp === '<' ) ?
4234  [] :
4235  [ $old->getUserText( Revision::RAW ) ];
4236  } elseif ( $old->getId() === $new->getParentId() ) {
4237  if ( $old_cmp === '>=' && $new_cmp === '<=' ) {
4238  $authors[] = $old->getUserText( Revision::RAW );
4239  if ( $old->getUserText( Revision::RAW ) != $new->getUserText( Revision::RAW ) ) {
4240  $authors[] = $new->getUserText( Revision::RAW );
4241  }
4242  } elseif ( $old_cmp === '>=' ) {
4243  $authors[] = $old->getUserText( Revision::RAW );
4244  } elseif ( $new_cmp === '<=' ) {
4245  $authors[] = $new->getUserText( Revision::RAW );
4246  }
4247  return $authors;
4248  }
4249  $dbr = wfGetDB( DB_REPLICA );
4250  $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
4251  [
4252  'rev_page' => $this->getArticleID(),
4253  "rev_timestamp $old_cmp " . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4254  "rev_timestamp $new_cmp " . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4255  ], __METHOD__,
4256  [ 'LIMIT' => $limit + 1 ] // add one so caller knows it was truncated
4257  );
4258  foreach ( $res as $row ) {
4259  $authors[] = $row->rev_user_text;
4260  }
4261  return $authors;
4262  }
4263 
4278  public function countAuthorsBetween( $old, $new, $limit, $options = [] ) {
4279  $authors = $this->getAuthorsBetween( $old, $new, $limit, $options );
4280  return $authors ? count( $authors ) : 0;
4281  }
4282 
4289  public function equals( Title $title ) {
4290  // Note: === is necessary for proper matching of number-like titles.
4291  return $this->getInterwiki() === $title->getInterwiki()
4292  && $this->getNamespace() == $title->getNamespace()
4293  && $this->getDBkey() === $title->getDBkey();
4294  }
4295 
4302  public function isSubpageOf( Title $title ) {
4303  return $this->getInterwiki() === $title->getInterwiki()
4304  && $this->getNamespace() == $title->getNamespace()
4305  && strpos( $this->getDBkey(), $title->getDBkey() . '/' ) === 0;
4306  }
4307 
4319  public function exists( $flags = 0 ) {
4320  $exists = $this->getArticleID( $flags ) != 0;
4321  Hooks::run( 'TitleExists', [ $this, &$exists ] );
4322  return $exists;
4323  }
4324 
4341  public function isAlwaysKnown() {
4342  $isKnown = null;
4343 
4354  Hooks::run( 'TitleIsAlwaysKnown', [ $this, &$isKnown ] );
4355 
4356  if ( !is_null( $isKnown ) ) {
4357  return $isKnown;
4358  }
4359 
4360  if ( $this->isExternal() ) {
4361  return true; // any interwiki link might be viewable, for all we know
4362  }
4363 
4364  switch ( $this->mNamespace ) {
4365  case NS_MEDIA:
4366  case NS_FILE:
4367  // file exists, possibly in a foreign repo
4368  return (bool)wfFindFile( $this );
4369  case NS_SPECIAL:
4370  // valid special page
4371  return SpecialPageFactory::exists( $this->getDBkey() );
4372  case NS_MAIN:
4373  // selflink, possibly with fragment
4374  return $this->mDbkeyform == '';
4375  case NS_MEDIAWIKI:
4376  // known system message
4377  return $this->hasSourceText() !== false;
4378  default:
4379  return false;
4380  }
4381  }
4382 
4394  public function isKnown() {
4395  return $this->isAlwaysKnown() || $this->exists();
4396  }
4397 
4403  public function hasSourceText() {
4404  if ( $this->exists() ) {
4405  return true;
4406  }
4407 
4408  if ( $this->mNamespace == NS_MEDIAWIKI ) {
4409  // If the page doesn't exist but is a known system message, default
4410  // message content will be displayed, same for language subpages-
4411  // Use always content language to avoid loading hundreds of languages
4412  // to get the link color.
4414  list( $name, ) = MessageCache::singleton()->figureMessage(
4415  $wgContLang->lcfirst( $this->getText() )
4416  );
4417  $message = wfMessage( $name )->inLanguage( $wgContLang )->useDatabase( false );
4418  return $message->exists();
4419  }
4420 
4421  return false;
4422  }
4423 
4429  public function getDefaultMessageText() {
4431 
4432  if ( $this->getNamespace() != NS_MEDIAWIKI ) { // Just in case
4433  return false;
4434  }
4435 
4436  list( $name, $lang ) = MessageCache::singleton()->figureMessage(
4437  $wgContLang->lcfirst( $this->getText() )
4438  );
4439  $message = wfMessage( $name )->inLanguage( $lang )->useDatabase( false );
4440 
4441  if ( $message->exists() ) {
4442  return $message->plain();
4443  } else {
4444  return false;
4445  }
4446  }
4447 
4454  public function invalidateCache( $purgeTime = null ) {
4455  if ( wfReadOnly() ) {
4456  return false;
4457  } elseif ( $this->mArticleID === 0 ) {
4458  return true; // avoid gap locking if we know it's not there
4459  }
4460 
4461  $dbw = wfGetDB( DB_MASTER );
4462  $dbw->onTransactionPreCommitOrIdle( function () {
4464  } );
4465 
4466  $conds = $this->pageCond();
4468  new AutoCommitUpdate(
4469  $dbw,
4470  __METHOD__,
4471  function ( IDatabase $dbw, $fname ) use ( $conds, $purgeTime ) {
4472  $dbTimestamp = $dbw->timestamp( $purgeTime ?: time() );
4473  $dbw->update(
4474  'page',
4475  [ 'page_touched' => $dbTimestamp ],
4476  $conds + [ 'page_touched < ' . $dbw->addQuotes( $dbTimestamp ) ],
4477  $fname
4478  );
4479  MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $this );
4480  }
4481  ),
4483  );
4484 
4485  return true;
4486  }
4487 
4493  public function touchLinks() {
4494  DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this, 'pagelinks' ) );
4495  if ( $this->getNamespace() == NS_CATEGORY ) {
4496  DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this, 'categorylinks' ) );
4497  }
4498  }
4499 
4506  public function getTouched( $db = null ) {
4507  if ( $db === null ) {
4508  $db = wfGetDB( DB_REPLICA );
4509  }
4510  $touched = $db->selectField( 'page', 'page_touched', $this->pageCond(), __METHOD__ );
4511  return $touched;
4512  }
4513 
4520  public function getNotificationTimestamp( $user = null ) {
4521  global $wgUser;
4522 
4523  // Assume current user if none given
4524  if ( !$user ) {
4525  $user = $wgUser;
4526  }
4527  // Check cache first
4528  $uid = $user->getId();
4529  if ( !$uid ) {
4530  return false;
4531  }
4532  // avoid isset here, as it'll return false for null entries
4533  if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) {
4534  return $this->mNotificationTimestamp[$uid];
4535  }
4536  // Don't cache too much!
4537  if ( count( $this->mNotificationTimestamp ) >= self::CACHE_MAX ) {
4538  $this->mNotificationTimestamp = [];
4539  }
4540 
4541  $store = MediaWikiServices::getInstance()->getWatchedItemStore();
4542  $watchedItem = $store->getWatchedItem( $user, $this );
4543  if ( $watchedItem ) {
4544  $this->mNotificationTimestamp[$uid] = $watchedItem->getNotificationTimestamp();
4545  } else {
4546  $this->mNotificationTimestamp[$uid] = false;
4547  }
4548 
4549  return $this->mNotificationTimestamp[$uid];
4550  }
4551 
4558  public function getNamespaceKey( $prepend = 'nstab-' ) {
4560  // Gets the subject namespace if this title
4561  $namespace = MWNamespace::getSubject( $this->getNamespace() );
4562  // Checks if canonical namespace name exists for namespace
4563  if ( MWNamespace::exists( $this->getNamespace() ) ) {
4564  // Uses canonical namespace name
4565  $namespaceKey = MWNamespace::getCanonicalName( $namespace );
4566  } else {
4567  // Uses text of namespace
4568  $namespaceKey = $this->getSubjectNsText();
4569  }
4570  // Makes namespace key lowercase
4571  $namespaceKey = $wgContLang->lc( $namespaceKey );
4572  // Uses main
4573  if ( $namespaceKey == '' ) {
4574  $namespaceKey = 'main';
4575  }
4576  // Changes file to image for backwards compatibility
4577  if ( $namespaceKey == 'file' ) {
4578  $namespaceKey = 'image';
4579  }
4580  return $prepend . $namespaceKey;
4581  }
4582 
4589  public function getRedirectsHere( $ns = null ) {
4590  $redirs = [];
4591 
4592  $dbr = wfGetDB( DB_REPLICA );
4593  $where = [
4594  'rd_namespace' => $this->getNamespace(),
4595  'rd_title' => $this->getDBkey(),
4596  'rd_from = page_id'
4597  ];
4598  if ( $this->isExternal() ) {
4599  $where['rd_interwiki'] = $this->getInterwiki();
4600  } else {
4601  $where[] = 'rd_interwiki = ' . $dbr->addQuotes( '' ) . ' OR rd_interwiki IS NULL';
4602  }
4603  if ( !is_null( $ns ) ) {
4604  $where['page_namespace'] = $ns;
4605  }
4606 
4607  $res = $dbr->select(
4608  [ 'redirect', 'page' ],
4609  [ 'page_namespace', 'page_title' ],
4610  $where,
4611  __METHOD__
4612  );
4613 
4614  foreach ( $res as $row ) {
4615  $redirs[] = self::newFromRow( $row );
4616  }
4617  return $redirs;
4618  }
4619 
4625  public function isValidRedirectTarget() {
4626  global $wgInvalidRedirectTargets;
4627 
4628  if ( $this->isSpecialPage() ) {
4629  // invalid redirect targets are stored in a global array, but explicitly disallow Userlogout here
4630  if ( $this->isSpecial( 'Userlogout' ) ) {
4631  return false;
4632  }
4633 
4634  foreach ( $wgInvalidRedirectTargets as $target ) {
4635  if ( $this->isSpecial( $target ) ) {
4636  return false;
4637  }
4638  }
4639  }
4640 
4641  return true;
4642  }
4643 
4649  public function getBacklinkCache() {
4650  return BacklinkCache::get( $this );
4651  }
4652 
4658  public function canUseNoindex() {
4659  global $wgExemptFromUserRobotsControl;
4660 
4661  $bannedNamespaces = is_null( $wgExemptFromUserRobotsControl )
4663  : $wgExemptFromUserRobotsControl;
4664 
4665  return !in_array( $this->mNamespace, $bannedNamespaces );
4666  }
4667 
4678  public function getCategorySortkey( $prefix = '' ) {
4679  $unprefixed = $this->getText();
4680 
4681  // Anything that uses this hook should only depend
4682  // on the Title object passed in, and should probably
4683  // tell the users to run updateCollations.php --force
4684  // in order to re-sort existing category relations.
4685  Hooks::run( 'GetDefaultSortkey', [ $this, &$unprefixed ] );
4686  if ( $prefix !== '' ) {
4687  # Separate with a line feed, so the unprefixed part is only used as
4688  # a tiebreaker when two pages have the exact same prefix.
4689  # In UCA, tab is the only character that can sort above LF
4690  # so we strip both of them from the original prefix.
4691  $prefix = strtr( $prefix, "\n\t", ' ' );
4692  return "$prefix\n$unprefixed";
4693  }
4694  return $unprefixed;
4695  }
4696 
4704  private function getDbPageLanguageCode() {
4705  global $wgPageLanguageUseDB;
4706 
4707  // check, if the page language could be saved in the database, and if so and
4708  // the value is not requested already, lookup the page language using LinkCache
4709  if ( $wgPageLanguageUseDB && $this->mDbPageLanguage === false ) {
4710  $linkCache = LinkCache::singleton();
4711  $linkCache->addLinkObj( $this );
4712  $this->mDbPageLanguage = $linkCache->getGoodLinkFieldObj( $this, 'lang' );
4713  }
4714 
4715  return $this->mDbPageLanguage;
4716  }
4717 
4726  public function getPageLanguage() {
4728  if ( $this->isSpecialPage() ) {
4729  // special pages are in the user language
4730  return $wgLang;
4731  }
4732 
4733  // Checking if DB language is set
4734  $dbPageLanguage = $this->getDbPageLanguageCode();
4735  if ( $dbPageLanguage ) {
4736  return wfGetLangObj( $dbPageLanguage );
4737  }
4738 
4739  if ( !$this->mPageLanguage || $this->mPageLanguage[1] !== $wgLanguageCode ) {
4740  // Note that this may depend on user settings, so the cache should
4741  // be only per-request.
4742  // NOTE: ContentHandler::getPageLanguage() may need to load the
4743  // content to determine the page language!
4744  // Checking $wgLanguageCode hasn't changed for the benefit of unit
4745  // tests.
4746  $contentHandler = ContentHandler::getForTitle( $this );
4747  $langObj = $contentHandler->getPageLanguage( $this );
4748  $this->mPageLanguage = [ $langObj->getCode(), $wgLanguageCode ];
4749  } else {
4750  $langObj = wfGetLangObj( $this->mPageLanguage[0] );
4751  }
4752 
4753  return $langObj;
4754  }
4755 
4764  public function getPageViewLanguage() {
4765  global $wgLang;
4766 
4767  if ( $this->isSpecialPage() ) {
4768  // If the user chooses a variant, the content is actually
4769  // in a language whose code is the variant code.
4770  $variant = $wgLang->getPreferredVariant();
4771  if ( $wgLang->getCode() !== $variant ) {
4772  return Language::factory( $variant );
4773  }
4774 
4775  return $wgLang;
4776  }
4777 
4778  // Checking if DB language is set
4779  $dbPageLanguage = $this->getDbPageLanguageCode();
4780  if ( $dbPageLanguage ) {
4781  $pageLang = wfGetLangObj( $dbPageLanguage );
4782  $variant = $pageLang->getPreferredVariant();
4783  if ( $pageLang->getCode() !== $variant ) {
4784  $pageLang = Language::factory( $variant );
4785  }
4786 
4787  return $pageLang;
4788  }
4789 
4790  // @note Can't be cached persistently, depends on user settings.
4791  // @note ContentHandler::getPageViewLanguage() may need to load the
4792  // content to determine the page language!
4793  $contentHandler = ContentHandler::getForTitle( $this );
4794  $pageLang = $contentHandler->getPageViewLanguage( $this );
4795  return $pageLang;
4796  }
4797 
4808  public function getEditNotices( $oldid = 0 ) {
4809  $notices = [];
4810 
4811  // Optional notice for the entire namespace
4812  $editnotice_ns = 'editnotice-' . $this->getNamespace();
4813  $msg = wfMessage( $editnotice_ns );
4814  if ( $msg->exists() ) {
4815  $html = $msg->parseAsBlock();
4816  // Edit notices may have complex logic, but output nothing (T91715)
4817  if ( trim( $html ) !== '' ) {
4818  $notices[$editnotice_ns] = Html::rawElement(
4819  'div',
4820  [ 'class' => [
4821  'mw-editnotice',
4822  'mw-editnotice-namespace',
4823  Sanitizer::escapeClass( "mw-$editnotice_ns" )
4824  ] ],
4825  $html
4826  );
4827  }
4828  }
4829 
4830  if ( MWNamespace::hasSubpages( $this->getNamespace() ) ) {
4831  // Optional notice for page itself and any parent page
4832  $parts = explode( '/', $this->getDBkey() );
4833  $editnotice_base = $editnotice_ns;
4834  while ( count( $parts ) > 0 ) {
4835  $editnotice_base .= '-' . array_shift( $parts );
4836  $msg = wfMessage( $editnotice_base );
4837  if ( $msg->exists() ) {
4838  $html = $msg->parseAsBlock();
4839  if ( trim( $html ) !== '' ) {
4840  $notices[$editnotice_base] = Html::rawElement(
4841  'div',
4842  [ 'class' => [
4843  'mw-editnotice',
4844  'mw-editnotice-base',
4845  Sanitizer::escapeClass( "mw-$editnotice_base" )
4846  ] ],
4847  $html
4848  );
4849  }
4850  }
4851  }
4852  } else {
4853  // Even if there are no subpages in namespace, we still don't want "/" in MediaWiki message keys
4854  $editnoticeText = $editnotice_ns . '-' . strtr( $this->getDBkey(), '/', '-' );
4855  $msg = wfMessage( $editnoticeText );
4856  if ( $msg->exists() ) {
4857  $html = $msg->parseAsBlock();
4858  if ( trim( $html ) !== '' ) {
4859  $notices[$editnoticeText] = Html::rawElement(
4860  'div',
4861  [ 'class' => [
4862  'mw-editnotice',
4863  'mw-editnotice-page',
4864  Sanitizer::escapeClass( "mw-$editnoticeText" )
4865  ] ],
4866  $html
4867  );
4868  }
4869  }
4870  }
4871 
4872  Hooks::run( 'TitleGetEditNotices', [ $this, $oldid, &$notices ] );
4873  return $notices;
4874  }
4875 
4879  public function __sleep() {
4880  return [
4881  'mNamespace',
4882  'mDbkeyform',
4883  'mFragment',
4884  'mInterwiki',
4885  'mLocalInterwiki',
4886  'mUserCaseDBKey',
4887  'mDefaultNamespace',
4888  ];
4889  }
4890 
4891  public function __wakeup() {
4892  $this->mArticleID = ( $this->mNamespace >= 0 ) ? -1 : 0;
4893  $this->mUrlform = wfUrlencode( $this->mDbkeyform );
4894  $this->mTextform = strtr( $this->mDbkeyform, '_', ' ' );
4895  }
4896 
4897 }
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\getTitleProtection
getTitleProtection()
Is this title subject to title protection? Title protection is the one applied against creation of su...
Definition: Title.php:2591
Title\canUseNoindex
canUseNoindex()
Whether the magic words INDEX and NOINDEX function for this page.
Definition: Title.php:4658
Title\inNamespaces
inNamespaces()
Returns true if the title is inside one of the specified namespaces.
Definition: Title.php:1114
Title\getLocalURL
getLocalURL( $query='', $query2=false)
Get a URL with no fragment or server name (relative URL) from a Title object.
Definition: Title.php:1737
Title\isSemiProtected
isSemiProtected( $action='edit')
Is this page "semi-protected" - the only protection levels are listed in $wgSemiprotectedRestrictionL...
Definition: Title.php:2670
Title\$mHasCascadingRestrictions
bool $mHasCascadingRestrictions
Are cascading restrictions in effect on this page?
Definition: Title.php:124
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:45
Title\isNamespaceProtected
isNamespaceProtected(User $user)
Determines if $user is unable to edit this page because it has been protected by $wgNamespaceProtecti...
Definition: Title.php:2730
Title\getTalkNsText
getTalkNsText()
Get the namespace text of the talk page.
Definition: Title.php:1017
$wgUser
$wgUser
Definition: Setup.php:781
Title\getSubpageText
getSubpageText()
Get the lowest-level subpage name, i.e.
Definition: Title.php:1570
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:265
MWNamespace\subjectEquals
static subjectEquals( $ns1, $ns2)
Returns whether the specified namespaces share the same subject.
Definition: MWNamespace.php:194
Title\getNextRevisionID
getNextRevisionID( $revId, $flags=0)
Get the revision ID of the next revision.
Definition: Title.php:4036
Title\isBigDeletion
isBigDeletion()
Check whether the number of revisions of this page surpasses $wgDeleteRevisionsLimit.
Definition: Title.php:4109
PROTO_CANONICAL
const PROTO_CANONICAL
Definition: Defines.php:221
Title\areCascadeProtectionSourcesLoaded
areCascadeProtectionSourcesLoaded( $getPages=true)
Determines whether cascading protection sources have already been loaded from the database.
Definition: Title.php:2762
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:1930
Title\getFilteredRestrictionTypes
static getFilteredRestrictionTypes( $exists=true)
Get a filtered list of all restriction types supported by this wiki.
Definition: Title.php:2546
wfMergeErrorArrays
wfMergeErrorArrays()
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
Definition: GlobalFunctions.php:242
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:728
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:189
Title\getInternalURL
getInternalURL( $query='', $query2=false)
Get the URL form for an internal link.
Definition: Title.php:1858
$wgActionPaths
$wgActionPaths
Definition: img_auth.php:46
Title\checkPageRestrictions
checkPageRestrictions( $action, $user, $errors, $rigor, $short)
Check against page_restrictions table requirements on this page.
Definition: Title.php:2187
$tables
this hook is for auditing only RecentChangesLinked and Watchlist 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:990
$wgNamespaceProtection
if( $wgScript===false) if( $wgLoadScript===false) if( $wgArticlePath===false) if(!empty( $wgActionPaths) &&!isset( $wgActionPaths['view'])) if( $wgResourceBasePath===null) if( $wgStylePath===false) if( $wgLocalStylePath===false) if( $wgExtensionAssetsPath===false) if( $wgLogo===false) if( $wgUploadPath===false) if( $wgUploadDirectory===false) if( $wgReadOnlyFile===false) if( $wgFileCacheDirectory===false) if( $wgDeletedDirectory===false) if( $wgGitInfoCacheDirectory===false && $wgCacheDirectory !==false) if( $wgEnableParserCache===false) if( $wgRightsIcon) if(isset( $wgFooterIcons['copyright']['copyright']) && $wgFooterIcons['copyright']['copyright']===[]) if(isset( $wgFooterIcons['poweredby']) &&isset( $wgFooterIcons['poweredby']['mediawiki']) && $wgFooterIcons['poweredby']['mediawiki']['src']===null) $wgNamespaceProtection[NS_MEDIAWIKI]
Unconditional protection for NS_MEDIAWIKI since otherwise it's too easy for a sysadmin to set $wgName...
Definition: Setup.php:157
Title\getFragment
getFragment()
Get the Title fragment (i.e.
Definition: Title.php:1356
MWNamespace\isTalk
static isTalk( $index)
Is the given namespace a talk namespace?
Definition: MWNamespace.php:96
HashBagOStuff
Simple store for keeping values in an associative array for the current process.
Definition: HashBagOStuff.php:31
Title\inNamespace
inNamespace( $ns)
Returns true if the title is inside the specified namespace.
Definition: Title.php:1103
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:1163
Title\isJsSubpage
isJsSubpage()
Is this a .js subpage of a user page?
Definition: Title.php:1283
Title\wasLocalInterwiki
wasLocalInterwiki()
Was this a local interwiki link?
Definition: Title.php:820
$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:1216
Title\getBacklinkCache
getBacklinkCache()
Get a backlink cache object.
Definition: Title.php:4649
User\newFatalPermissionDeniedStatus
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
Definition: User.php:5467
Title\getPartialURL
getPartialURL()
Get the URL-encoded form of the main part.
Definition: Title.php:892
Title\$mIsBigDeletion
bool $mIsBigDeletion
Would deleting this page be a big deletion?
Definition: Title.php:168
Title\clearCaches
static clearCaches()
Text form (spaces not underscores) of the main part.
Definition: Title.php:3363
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:3753
Title\getTitleValue
getTitleValue()
Get a TitleValue object representing this Title.
Definition: Title.php:860
Title\estimateRevisionCount
estimateRevisionCount()
Get the approximate revision count of this page.
Definition: Title.php:4138
Title\countRevisionsBetween
countRevisionsBetween( $old, $new, $max=null)
Get the number of revisions between the given revision.
Definition: Title.php:4161
captcha-old.count
count
Definition: captcha-old.py:225
Title\getPrefixedDBkey
getPrefixedDBkey()
Get the prefixed database key form.
Definition: Title.php:1439
Title\getDbPageLanguageCode
getDbPageLanguageCode()
Returns the page language code saved in the database, if $wgPageLanguageUseDB is set to true in Local...
Definition: Title.php:4704
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
Title\checkActionPermissions
checkActionPermissions( $action, $user, $errors, $rigor, $short)
Check action permissions not already checked in checkQuickPermissions.
Definition: Title.php:2268
$wgScript
$wgScript
The URL path to index.php.
Definition: DefaultSettings.php:202
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:155
Title\getTalkPage
getTalkPage()
Get a Title object associated with the talk page of this article.
Definition: Title.php:1302
Title\newMainPage
static newMainPage()
Create a new Title for the Main Page.
Definition: Title.php:559
Title\getNotificationTimestamp
getNotificationTimestamp( $user=null)
Get the timestamp when this page was updated since the user last saw it.
Definition: Title.php:4520
Title\getParentCategoryTree
getParentCategoryTree( $children=[])
Get a tree of parent categories.
Definition: Title.php:3960
CONTENT_MODEL_CSS
const CONTENT_MODEL_CSS
Definition: Defines.php:235
$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 '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! 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! 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:1954
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:1630
$namespaces
namespace and then decline to actually register it & $namespaces
Definition: hooks.txt:934
Title\checkSpecialsAndNSPermissions
checkSpecialsAndNSPermissions( $action, $user, $errors, $rigor, $short)
Check permissions on special pages & namespaces.
Definition: Title.php:2123
Title\fixSpecialName
fixSpecialName()
If the Title refers to a special page alias which is not the local default, resolve the alias,...
Definition: Title.php:1080
Title\getInterwikiLookup
static getInterwikiLookup()
B/C kludge: provide an InterwikiLookup for use by Title.
Definition: Title.php:191
$status
this hook is for auditing only RecentChangesLinked and Watchlist 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 and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
Definition: hooks.txt:1049
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:4493
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
wfUrlencode
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
Definition: GlobalFunctions.php:371
$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:246
Title\getPrefixedText
getPrefixedText()
Get the prefixed title with spaces.
Definition: Title.php:1451
MediaWikiTitleCodec\getTitleInvalidRegex
static getTitleInvalidRegex()
Returns a simple regex that will match on characters and sequences invalid in titles.
Definition: MediaWikiTitleCodec.php:475
Title\getTransWikiID
getTransWikiID()
Returns the DB name of the distant wiki which owns the object.
Definition: Title.php:843
Title\resultToError
resultToError( $errors, $result)
Add the resulting error code to the errors array.
Definition: Title.php:2055
Title\isCssJsSubpage
isCssJsSubpage()
Is this a .css or .js subpage of a user page?
Definition: Title.php:1247
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:76
Title\getArticleID
getArticleID( $flags=0)
Get the article ID for this Title from the link cache, adding it if necessary.
Definition: Title.php:3220
StringUtils\escapeRegexReplacement
static escapeRegexReplacement( $string)
Escape a string to make it suitable for inclusion in a preg_replace() replacement parameter.
Definition: StringUtils.php:322
$fname
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined.
Definition: Setup.php:36
Title\getSquidURLs
getSquidURLs()
Definition: Title.php:3635
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:1835
Title\secureAndSplit
secureAndSplit()
Secure and split - main initialisation function for this object.
Definition: Title.php:3400
Title\quickUserCan
quickUserCan( $action, $user=null)
Can $user perform $action on this page? This skips potentially expensive cascading permission checks ...
Definition: Title.php:1917
NS_FILE
const NS_FILE
Definition: Defines.php:68
Title\isTalkPage
isTalkPage()
Is this a talk page of some sort?
Definition: Title.php:1293
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1277
$s
$s
Definition: mergeMessageFileList.php:188
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:240
$res
$res
Definition: database.txt:21
Title\isExternal
isExternal()
Is this Title interwiki?
Definition: Title.php:800
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:304
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:2937
$success
$success
Definition: NoLocalSettings.php:44
Title\getDefaultMessageText
getDefaultMessageText()
Get the default message text or false if the message doesn't exist.
Definition: Title.php:4429
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:233
Title\hasSourceText
hasSourceText()
Does this page have source text?
Definition: Title.php:4403
Title\loadFromRow
loadFromRow( $row)
Load Title object fields from a DB row.
Definition: Title.php:465
User\groupHasPermission
static groupHasPermission( $group, $role)
Check, if the given group has the given permission.
Definition: User.php:4766
$type
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect as a string mapping parameter names to values & $type
Definition: hooks.txt:2536
Title\checkCSSandJSPermissions
checkCSSandJSPermissions( $action, $user, $errors, $rigor, $short)
Check CSS/JS sub-page permissions.
Definition: Title.php:2152
Title\checkReadPermissions
checkReadPermissions( $action, $user, $errors, $rigor, $short)
Check that the user is allowed to read this page.
Definition: Title.php:2387
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:3341
Title\setFragment
setFragment( $fragment)
Set the fragment for this title.
Definition: Title.php:1394
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:339
Title\convertByteClassToUnicodeClass
static convertByteClassToUnicodeClass( $byteClass)
Utility method for converting a character sequence from bytes to Unicode.
Definition: Title.php:624
Title\__wakeup
__wakeup()
Text form (spaces not underscores) of the main part.
Definition: Title.php:4891
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:3925
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:3448
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:40
HashBagOStuff\clear
clear()
Definition: HashBagOStuff.php:115
Title\getNsText
getNsText()
Get the namespace text.
Definition: Title.php:982
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
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:3506
Title\$mOldRestrictions
string bool $mOldRestrictions
Text form (spaces not underscores) of the main part.
Definition: Title.php:112
Title\getSkinFromCssJsSubpage
getSkinFromCssJsSubpage()
Trim down a .css or .js subpage title to get the corresponding skin name.
Definition: Title.php:1258
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:500
Revision
Definition: Revision.php:33
NS_MAIN
const NS_MAIN
Definition: Defines.php:62
HashBagOStuff\set
set( $key, $value, $exptime=0, $flags=0)
Set an item.
Definition: HashBagOStuff.php:92
Title\isCascadeProtected
isCascadeProtected()
Cascading protection: Return true if cascading restrictions apply to this page, false if not.
Definition: Title.php:2748
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:134
Title\isValidMoveTarget
isValidMoveTarget( $nt)
Checks if $this can be moved to a given Title.
Definition: Title.php:3877
$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:1572
Title\getUserPermissionsErrors
getUserPermissionsErrors( $action, $user, $rigor='secure', $ignoreErrors=[])
Can $user perform $action on this page?
Definition: Title.php:1954
Title\getCdnUrls
getCdnUrls()
Get a list of URLs to purge from the CDN cache when this page changes.
Definition: Title.php:3607
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:51
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:1329
MediaWiki\Linker\LinkTarget\getNamespace
getNamespace()
Get the namespace index.
article
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add in any and then calling but I prefer the flexibility This should also do the output encoding The system allocates a global one in $wgOut Title Represents the title of an article
Definition: design.txt:25
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:178
Title\getDBkey
getDBkey()
Get the main part with underscores.
Definition: Title.php:901
Title\nameOf
static nameOf( $id)
Get the prefixed DB key associated with an ID.
Definition: Title.php:574
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:162
$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:1956
Title\isSpecial
isSpecial( $name)
Returns true if this title resolves to the named special page.
Definition: Title.php:1064
MWException
MediaWiki exception.
Definition: MWException.php:26
Title\getTitleFormatter
static getTitleFormatter()
B/C kludge: provide a TitleParser for use by Title.
Definition: Title.php:179
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:934
Title\checkCascadingSourcesRestrictions
checkCascadingSourcesRestrictions( $action, $user, $errors, $rigor, $short)
Check restrictions on cascading pages.
Definition: Title.php:2221
Title\isMainPage
isMainPage()
Is this the mainpage?
Definition: Title.php:1184
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1128
$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:3715
Title\getSubpage
getSubpage( $text)
Get the title for a subpage of the current page.
Definition: Title.php:1591
Title\getBaseTitle
getBaseTitle()
Get the base page name title, i.e.
Definition: Title.php:1555
Title\getNamespace
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:924
BacklinkCache\get
static get(Title $title)
Create a new BacklinkCache or reuse any existing one.
Definition: BacklinkCache.php:107
Title\newFromRow
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:453
Title\isConversionTable
isConversionTable()
Is this a conversion table for the LanguageConverter?
Definition: Title.php:1204
MovePage
Handles the backend logic of moving a page from one title to another.
Definition: MovePage.php:30
Title\$titleCache
static HashBagOStuff $titleCache
Definition: Title.php:41
WikiPage\selectFields
static selectFields()
Return the list of revision fields that should be selected to create a new page.
Definition: WikiPage.php:286
Title\newFromLinkTarget
static newFromLinkTarget(LinkTarget $linkTarget)
Create a new Title from a LinkTarget.
Definition: Title.php:239
Title\isProtected
isProtected( $action='')
Does the title correspond to a protected article?
Definition: Title.php:2698
$content
this hook is for auditing only RecentChangesLinked and Watchlist 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 and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content $content
Definition: hooks.txt:1049
Title\flushRestrictions
flushRestrictions()
Flush the protection cache in this object and force reload from the database.
Definition: Title.php:3062
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3060
Title\getCategorySortkey
getCategorySortkey( $prefix='')
Returns the raw sort key to be used for categories, with the specified prefix.
Definition: Title.php:4678
$page
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached $page
Definition: hooks.txt:2536
Title\getInterwiki
getInterwiki()
Get the interwiki prefix.
Definition: Title.php:811
$matches
$matches
Definition: NoLocalSettings.php:24
in
null for the wiki Added in
Definition: hooks.txt:1572
MWNamespace\canTalk
static canTalk( $index)
Can this namespace ever have a talk namespace?
Definition: MWNamespace.php:286
Title\isValidRedirectTarget
isValidRedirectTarget()
Check if this Title is a valid redirect target.
Definition: Title.php:4625
Title\deleteTitleProtection
deleteTitleProtection()
Remove any title protection due to page existing.
Definition: Title.php:2652
Title\__construct
__construct()
Definition: Title.php:198
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:297
Title\getBrokenLinksFrom
getBrokenLinksFrom()
Get an array of Title objects referring to non-existent articles linked from this page.
Definition: Title.php:3571
PROTO_CURRENT
const PROTO_CURRENT
Definition: Defines.php:220
Title\missingPermissionError
missingPermissionError( $action, $short)
Get a description array when the user doesn't have the right to perform $action (i....
Definition: Title.php:2462
$limit
this hook is for auditing only RecentChangesLinked and Watchlist 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 and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers please use GetContentModels hook to make them known to core if desired 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 inclusive $limit
Definition: hooks.txt:1049
Title\$mCascadeSources
array $mCascadeSources
Where are the cascading restrictions coming from on this page?
Definition: Title.php:127
Title\getSubjectPage
getSubjectPage()
Get a title object associated with the subject page of this talk page.
Definition: Title.php:1312
wfGetLangObj
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
Definition: GlobalFunctions.php:1321
Title\getFullText
getFullText()
Get the prefixed title with spaces, plus any fragment (part beginning with '#')
Definition: Title.php:1475
MWNamespace\hasSubpages
static hasSubpages( $index)
Does the namespace allow subpages?
Definition: MWNamespace.php:330
Title\getTitleCache
static getTitleCache()
Definition: Title.php:365
Title\hasFragment
hasFragment()
Check if a Title fragment is set.
Definition: Title.php:1366
Title\isCssSubpage
isCssSubpage()
Is this a .css subpage of a user page?
Definition: Title.php:1273
$wgLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as $wgLang
Definition: design.txt:56
Title\getRedirectsHere
getRedirectsHere( $ns=null)
Get all extant redirects to this Title.
Definition: Title.php:4589
MWNamespace\isMovable
static isMovable( $index)
Can pages in the given namespace be moved?
Definition: MWNamespace.php:66
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:514
SpecialPageFactory\exists
static exists( $name)
Check if a given name exist as a special page or as a special page alias.
Definition: SpecialPageFactory.php:366
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
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:115
Title\equals
equals(Title $title)
Compare with another title.
Definition: Title.php:4289
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:2023
Title\getRootText
getRootText()
Get the root page name text without a namespace, i.e.
Definition: Title.php:1495
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:76
Title\canExist
canExist()
Is this in a namespace that allows actual pages?
Definition: Title.php:1036
DB_MASTER
const DB_MASTER
Definition: defines.php:26
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:999
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:177
Title\__sleep
__sleep()
Definition: Title.php:4879
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:1880
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:113
Title\$mRedirect
null $mRedirect
Is the article at this title a redirect?
Definition: Title.php:149
Title\createFragmentTarget
createFragmentTarget( $fragment)
Creates a new Title for a different fragment of the same page.
Definition: Title.php:1405
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:1345
or
or
Definition: COPYING.txt:140
Title\newFromTextThrow
static newFromTextThrow( $text, $defaultNamespace=NS_MAIN)
Like Title::newFromText(), but throws MalformedTitleException when the title is invalid,...
Definition: Title.php:295
Title\newFromTitleValue
static newFromTitleValue(TitleValue $titleValue)
Create a new Title from a TitleValue.
Definition: Title.php:228
Title\$mPageLanguage
bool $mPageLanguage
The (string) language code of the page's language and content code.
Definition: Title.php:158
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:1611
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:538
wfWikiID
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
Definition: GlobalFunctions.php:3011
Title\__toString
__toString()
Return a string representation of this title.
Definition: Title.php:1465
User\whoIs
static whoIs( $id)
Get the username corresponding to a given user ID.
Definition: User.php:739
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:3490
Wikimedia\Rdbms\IDatabase\selectFieldValues
selectFieldValues( $table, $var, $cond='', $fname=__METHOD__, $options=[])
A SELECT wrapper which returns a list of single field values from result rows.
Title\areRestrictionsCascading
areRestrictionsCascading()
Returns cascading restrictions for the current article.
Definition: Title.php:2920
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:50
CdnCacheUpdate
Handles purging appropriate CDN URLs given a title (or titles)
Definition: CdnCacheUpdate.php:31
Title\getLatestRevID
getLatestRevID( $flags=0)
What is the page_latest field for this page?
Definition: Title.php:3309
Title\areRestrictionsLoaded
areRestrictionsLoaded()
Accessor for mRestrictionsLoaded.
Definition: Title.php:2865
$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:246
Title\getSubpages
getSubpages( $limit=-1)
Get all subpages of this page.
Definition: Title.php:3141
Title\isSingleRevRedirect
isSingleRevRedirect()
Checks if this page is just a one-rev redirect.
Definition: Title.php:3826
$wgServer
$wgServer
URL of the server.
Definition: DefaultSettings.php:109
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:4764
Title\newFromDBkey
static newFromDBkey( $key)
Create a new Title from a prefixed DB key.
Definition: Title.php:209
$wgLanguageCode
$wgLanguageCode
Site language code.
Definition: DefaultSettings.php:2839
Title\isSubpage
isSubpage()
Is this a subpage?
Definition: Title.php:1193
Title\hasContentModel
hasContentModel( $id)
Convenience method for checking a title's content model name.
Definition: Title.php:957
Title\getRestrictionExpiry
getRestrictionExpiry( $action)
Get the expiry time for the restriction against a given action.
Definition: Title.php:2908
Title\newFromURL
static newFromURL( $url)
THIS IS NOT THE FUNCTION YOU WANT.
Definition: Title.php:342
Title\isValidMoveOperation
isValidMoveOperation(&$nt, $auth=true, $reason='')
Check whether a given move operation would be valid.
Definition: Title.php:3659
MediaWiki\Linker\LinkTarget\getFragment
getFragment()
Get the link fragment (i.e.
PROTO_HTTP
const PROTO_HTTP
Definition: Defines.php:217
Title\$mRestrictionsExpiry
array $mRestrictionsExpiry
When do the restrictions on this page expire?
Definition: Title.php:121
SpecialPageFactory\resolveAlias
static resolveAlias( $alias)
Given a special page name with a possible subpage, return an array where the first element is the spe...
Definition: SpecialPageFactory.php:338
PROTO_RELATIVE
const PROTO_RELATIVE
Definition: Defines.php:219
BagOStuff\get
get( $key, $flags=0, $oldFlags=null)
Get an item with the given key.
Definition: BagOStuff.php:179
$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:1956
SpecialPageFactory\getLocalNameFor
static getLocalNameFor( $name, $subpage=false)
Get the local name for a specified canonical name.
Definition: SpecialPageFactory.php:657
Title\$mTitleValue
TitleValue $mTitleValue
A corresponding TitleValue object.
Definition: Title.php:165
Title\isSubpageOf
isSubpageOf(Title $title)
Check if this title is a subpage of another title.
Definition: Title.php:4302
Revision\RAW
const RAW
Definition: Revision.php:100
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:785
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:934
Title\$mNotificationTimestamp
array $mNotificationTimestamp
Associative array of user ID -> timestamp/false.
Definition: Title.php:152
MWNamespace\exists
static exists( $index)
Returns whether the specified namespace exists.
Definition: MWNamespace.php:160
Title\newFromIDs
static newFromIDs( $ids)
Make an array of titles from an array of IDs.
Definition: Title.php:427
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:1985
RequestContext\getMain
static getMain()
Static methods.
Definition: RequestContext.php:468
Title\prefix
prefix( $name)
Prefix some arbitrary text with the namespace or interwiki prefix of this object.
Definition: Title.php:1421
Title\getNamespaceKey
getNamespaceKey( $prepend='nstab-')
Generate strings used for xml 'id' names in monobook tabs.
Definition: Title.php:4558
Title\getEditURL
getEditURL()
Get the edit URL for this Title.
Definition: Title.php:1894
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:1668
wfFindFile
wfFindFile( $title, $options=[])
Find a file.
Definition: GlobalFunctions.php:3101
Title\isKnown
isKnown()
Does this title refer to a page that can (or might) be meaningfully viewed? In particular,...
Definition: Title.php:4394
$wgArticlePath
$wgArticlePath
Definition: img_auth.php:45
Title\validateFileMoveOperation
validateFileMoveOperation( $nt)
Check if the requested move target is a valid file move target.
Definition: Title.php:3686
Title\getFirstRevision
getFirstRevision( $flags=0)
Get the first revision of the page.
Definition: Title.php:4064
Title\getAllRestrictions
getAllRestrictions()
Accessor/initialisation for mRestrictions.
Definition: Title.php:2894
Title\getEarliestRevTime
getEarliestRevTime( $flags=0)
Get the oldest revision timestamp of this page.
Definition: Title.php:4089
User\isEveryoneAllowed
static isEveryoneAllowed( $right)
Check if all users may be assumed to have the given permission.
Definition: User.php:4786
Title
Represents a title within MediaWiki.
Definition: Title.php:39
Title\$mCascadingRestrictions
$mCascadingRestrictions
Caching the results of getCascadeProtectionSources.
Definition: Title.php:118
Title\canTalk
canTalk()
Could this title have a corresponding talk page?
Definition: Title.php:1027
Title\getCascadeProtectionSources
getCascadeProtectionSources( $getPages=true)
Cascading protection: Get the source of any cascading restrictions on this page.
Definition: Title.php:2779
$dbr
if(! $regexes) $dbr
Definition: cleanup.php:94
$cache
$cache
Definition: mcc.php:33
Title\getRestrictionTypes
getRestrictionTypes()
Returns restriction types for the current Title.
Definition: Title.php:2564
MalformedTitleException
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Definition: MalformedTitleException.php:25
Title\getTitleProtectionInternal
getTitleProtectionInternal()
Fetch title protection settings.
Definition: Title.php:2614
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:3559
ObjectCache\getMainWANInstance
static getMainWANInstance()
Get the main WAN cache object.
Definition: ObjectCache.php:370
Title\isRedirect
isRedirect( $flags=0)
Is this an article that is a redirect page? Uses link cache, adding it if necessary.
Definition: Title.php:3246
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:2485
DeferredUpdates\PRESEND
const PRESEND
Definition: DeferredUpdates.php:60
MWNamespace\equals
static equals( $ns1, $ns2)
Returns whether the specified namespaces are the same namespace.
Definition: MWNamespace.php:179
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:3169
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:3378
Title\$mDefaultNamespace
int $mDefaultNamespace
Namespace index when there is no namespace.
Definition: Title.php:143
Title\$mLength
int $mLength
The page length, 0 for special pages.
Definition: Title.php:146
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:1142
LinkCache\singleton
static singleton()
Get an instance of this class.
Definition: LinkCache.php:67
Title\isSpecialPage
isSpecialPage()
Returns true if this is a special page.
Definition: Title.php:1054
Title\purgeSquid
purgeSquid()
Purge all applicable CDN URLs.
Definition: Title.php:3642
Title\getPreviousRevisionID
getPreviousRevisionID( $revId, $flags=0)
Get the revision ID of the previous revision.
Definition: Title.php:4003
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:1741
Title\getPageLanguage
getPageLanguage()
Get the language in which the content of this page is written in wikitext.
Definition: Title.php:4726
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:4808
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:4506
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
MWNamespace\isCapitalized
static isCapitalized( $index)
Is the namespace first-letter capitalized?
Definition: MWNamespace.php:383
Title\isCssOrJsPage
isCssOrJsPage()
Could this page contain custom CSS or JavaScript for the global UI.
Definition: Title.php:1234
Title\getFragmentForURL
getFragmentForURL()
Get the fragment in URL form, including the "#" character if there is one.
Definition: Title.php:1374
Title\exists
exists( $flags=0)
Check if page exists.
Definition: Title.php:4319
NS_USER
const NS_USER
Definition: Defines.php:64
$revId
this hook is for auditing only RecentChangesLinked and Watchlist 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 and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context $revId
Definition: hooks.txt:1049
Title\isAlwaysKnown
isAlwaysKnown()
Should links to this title be shown as potentially viewable (i.e.
Definition: Title.php:4341
Title\isDeletedQuick
isDeletedQuick()
Is there a version of this page in the deletion archive?
Definition: Title.php:3194
Title\getSubpageUrlForm
getSubpageUrlForm()
Get a URL-encoded form of the subpage text.
Definition: Title.php:1600
Language\factory
static factory( $code)
Get a cached or new language object for a given language code.
Definition: Language.php:183
Title\isNewPage
isNewPage()
Check if this is a new page.
Definition: Title.php:4099
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 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
Title\setContentModel
setContentModel( $model)
Set a proposed content model for the page for permissions checking.
Definition: Title.php:972
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:70
CONTENT_MODEL_JAVASCRIPT
const CONTENT_MODEL_JAVASCRIPT
Definition: Defines.php:234
Title\getTitleInvalidRegex
static getTitleInvalidRegex()
Returns a simple regex that will match on characters and sequences invalid in titles.
Definition: Title.php:610
Title\compare
static compare(LinkTarget $a, LinkTarget $b)
Callback for usort() to do title sorts by (namespace, title)
Definition: Title.php:770
Title\countAuthorsBetween
countAuthorsBetween( $old, $new, $limit, $options=[])
Get the number of authors between the given revisions or revision IDs.
Definition: Title.php:4278
$t
$t
Definition: testCompression.php:67
Title\legalChars
static legalChars()
Get a regex character class describing the legal characters in a link.
Definition: Title.php:596
MWNamespace\isWatchable
static isWatchable( $index)
Can pages in a namespace be watched?
Definition: MWNamespace.php:320
Title\invalidateCache
invalidateCache( $purgeTime=null)
Updates page_touched for this page; called from LinksUpdate.php.
Definition: Title.php:4454
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:639
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:27
Title\isTrans
isTrans()
Determine whether the object refers to a page within this project and is transcludable.
Definition: Title.php:830
Title\getRestrictions
getRestrictions( $action)
Accessor/initialisation for mRestrictions.
Definition: Title.php:2878
Revision\selectFields
static selectFields()
Return the list of revision fields that should be selected to create a new revision.
Definition: Revision.php:448
MWNamespace\getTalk
static getTalk( $index)
Get the talk namespace index for a given namespace.
Definition: MWNamespace.php:107
MWNamespace\getSubject
static getSubject( $index)
Get the subject namespace index for a given namespace Special namespaces (NS_MEDIA,...
Definition: MWNamespace.php:121
Title\checkPermissionHooks
checkPermissionHooks( $action, $user, $errors, $rigor, $short)
Check various permission hooks.
Definition: Title.php:2086
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:442
Title\isContentPage
isContentPage()
Is this Title in a namespace which contains content? In other words, is this a content page,...
Definition: Title.php:1153
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:50
wfLocalFile
wfLocalFile( $title)
Get an object referring to a locally registered file.
Definition: GlobalFunctions.php:3112
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:405
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
Title\getUserCaseDBKey
getUserCaseDBKey()
Get the DB key with the initial letter case as specified by the user.
Definition: Title.php:910
Title\getSelectFields
static getSelectFields()
Returns a list of fields that are to be selected for initializing Title objects or LinkCache entries.
Definition: Title.php:379
Title\hasSubpages
hasSubpages()
Does this have subpages? (Warning, usually requires an extra DB query.)
Definition: Title.php:3113
Title\escapeFragmentForURL
static escapeFragmentForURL( $fragment)
Escape a text fragment, say from a link, for a URL.
Definition: Title.php:754
Title\getBaseText
getBaseText()
Get the base page name without a namespace, i.e.
Definition: Title.php:1530
Title\pageCond
pageCond()
Get an associative array for selecting this title from the "page" table.
Definition: Title.php:3987
Title\getText
getText()
Get the text form (spaces not underscores) of the main part.
Definition: Title.php:883
$options
this hook is for auditing only RecentChangesLinked and Watchlist 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 and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition: hooks.txt:1049
MWNamespace\getCanonicalName
static getCanonicalName( $index)
Returns the canonical (English) name for a given index.
Definition: MWNamespace.php:228
Title\$mTextform
string $mTextform
Text form (spaces not underscores) of the main part.
Definition: Title.php:64
Title\$mPrefixedText
string $mPrefixedText
Text form including namespace/interwiki, initialised on demand.
Definition: Title.php:133
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:3072
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2749
$wgInternalServer
$wgInternalServer
Internal server name as known to CDN, if different.
Definition: DefaultSettings.php:2665
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:2345
Title\$mRestrictionsLoaded
bool $mRestrictionsLoaded
Boolean for initialisation on demand.
Definition: Title.php:130
wfExpandUrl
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
Definition: GlobalFunctions.php:552
array
the array() calling protocol came about after MediaWiki 1.4rc1.
Title\getSubjectNsText
getSubjectNsText()
Get the namespace text of the subject (rather than talk) page.
Definition: Title.php:1007
Title\$mTitleProtection
mixed $mTitleProtection
Cached value for getTitleProtection (create protection)
Definition: Title.php:136
Title\loadRestrictions
loadRestrictions( $oldFashionedRestrictions=null)
Load restrictions from the page_restrictions table.
Definition: Title.php:3007
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:36
$wgContLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
Definition: design.txt:56
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:408
Title\getFullUrlForRedirect
getFullUrlForRedirect( $query='', $proto=PROTO_CURRENT)
Get a url appropriate for making redirects based on an untrusted url arg.
Definition: Title.php:1703
$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:783
Title\getRootTitle
getRootTitle()
Get the root page name title, i.e.
Definition: Title.php:1515
Title\getLength
getLength( $flags=0)
What is the length of this page? Uses link cache, adding it if necessary.
Definition: Title.php:3281
Title\getAuthorsBetween
getAuthorsBetween( $old, $new, $limit, $options=[])
Get the authors between the given revisions or revision IDs.
Definition: Title.php:4204
Title\isWatchable
isWatchable()
Can this title be added to a user's watchlist?
Definition: Title.php:1045