MediaWiki  1.30.1
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 
268  public static function newFromText( $text, $defaultNamespace = NS_MAIN ) {
269  // DWIM: Integers can be passed in here when page titles are used as array keys.
270  if ( $text !== null && !is_string( $text ) && !is_int( $text ) ) {
271  throw new InvalidArgumentException( '$text must be a string.' );
272  }
273  if ( $text === null ) {
274  return null;
275  }
276 
277  try {
278  return self::newFromTextThrow( strval( $text ), $defaultNamespace );
279  } catch ( MalformedTitleException $ex ) {
280  return null;
281  }
282  }
283 
301  public static function newFromTextThrow( $text, $defaultNamespace = NS_MAIN ) {
302  if ( is_object( $text ) ) {
303  throw new MWException( '$text must be a string, given an object' );
304  }
305 
307 
308  // Wiki pages often contain multiple links to the same page.
309  // Title normalization and parsing can become expensive on pages with many
310  // links, so we can save a little time by caching them.
311  // In theory these are value objects and won't get changed...
312  if ( $defaultNamespace == NS_MAIN ) {
313  $t = $titleCache->get( $text );
314  if ( $t ) {
315  return $t;
316  }
317  }
318 
319  // Convert things like &eacute; &#257; or &#x3017; into normalized (T16952) text
320  $filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text );
321 
322  $t = new Title();
323  $t->mDbkeyform = strtr( $filteredText, ' ', '_' );
324  $t->mDefaultNamespace = intval( $defaultNamespace );
325 
326  $t->secureAndSplit();
327  if ( $defaultNamespace == NS_MAIN ) {
328  $titleCache->set( $text, $t );
329  }
330  return $t;
331  }
332 
348  public static function newFromURL( $url ) {
349  $t = new Title();
350 
351  # For compatibility with old buggy URLs. "+" is usually not valid in titles,
352  # but some URLs used it as a space replacement and they still come
353  # from some external search tools.
354  if ( strpos( self::legalChars(), '+' ) === false ) {
355  $url = strtr( $url, '+', ' ' );
356  }
357 
358  $t->mDbkeyform = strtr( $url, ' ', '_' );
359 
360  try {
361  $t->secureAndSplit();
362  return $t;
363  } catch ( MalformedTitleException $ex ) {
364  return null;
365  }
366  }
367 
371  private static function getTitleCache() {
372  if ( self::$titleCache == null ) {
373  self::$titleCache = new HashBagOStuff( [ 'maxKeys' => self::CACHE_MAX ] );
374  }
375  return self::$titleCache;
376  }
377 
385  protected static function getSelectFields() {
387 
388  $fields = [
389  'page_namespace', 'page_title', 'page_id',
390  'page_len', 'page_is_redirect', 'page_latest',
391  ];
392 
393  if ( $wgContentHandlerUseDB ) {
394  $fields[] = 'page_content_model';
395  }
396 
397  if ( $wgPageLanguageUseDB ) {
398  $fields[] = 'page_lang';
399  }
400 
401  return $fields;
402  }
403 
411  public static function newFromID( $id, $flags = 0 ) {
413  $row = $db->selectRow(
414  'page',
415  self::getSelectFields(),
416  [ 'page_id' => $id ],
417  __METHOD__
418  );
419  if ( $row !== false ) {
420  $title = self::newFromRow( $row );
421  } else {
422  $title = null;
423  }
424  return $title;
425  }
426 
433  public static function newFromIDs( $ids ) {
434  if ( !count( $ids ) ) {
435  return [];
436  }
437  $dbr = wfGetDB( DB_REPLICA );
438 
439  $res = $dbr->select(
440  'page',
441  self::getSelectFields(),
442  [ 'page_id' => $ids ],
443  __METHOD__
444  );
445 
446  $titles = [];
447  foreach ( $res as $row ) {
448  $titles[] = self::newFromRow( $row );
449  }
450  return $titles;
451  }
452 
459  public static function newFromRow( $row ) {
460  $t = self::makeTitle( $row->page_namespace, $row->page_title );
461  $t->loadFromRow( $row );
462  return $t;
463  }
464 
471  public function loadFromRow( $row ) {
472  if ( $row ) { // page found
473  if ( isset( $row->page_id ) ) {
474  $this->mArticleID = (int)$row->page_id;
475  }
476  if ( isset( $row->page_len ) ) {
477  $this->mLength = (int)$row->page_len;
478  }
479  if ( isset( $row->page_is_redirect ) ) {
480  $this->mRedirect = (bool)$row->page_is_redirect;
481  }
482  if ( isset( $row->page_latest ) ) {
483  $this->mLatestID = (int)$row->page_latest;
484  }
485  if ( !$this->mForcedContentModel && isset( $row->page_content_model ) ) {
486  $this->mContentModel = strval( $row->page_content_model );
487  } elseif ( !$this->mForcedContentModel ) {
488  $this->mContentModel = false; # initialized lazily in getContentModel()
489  }
490  if ( isset( $row->page_lang ) ) {
491  $this->mDbPageLanguage = (string)$row->page_lang;
492  }
493  if ( isset( $row->page_restrictions ) ) {
494  $this->mOldRestrictions = $row->page_restrictions;
495  }
496  } else { // page not found
497  $this->mArticleID = 0;
498  $this->mLength = 0;
499  $this->mRedirect = false;
500  $this->mLatestID = 0;
501  if ( !$this->mForcedContentModel ) {
502  $this->mContentModel = false; # initialized lazily in getContentModel()
503  }
504  }
505  }
506 
529  public static function makeTitle( $ns, $title, $fragment = '', $interwiki = '' ) {
530  $t = new Title();
531  $t->mInterwiki = $interwiki;
532  $t->mFragment = $fragment;
533  $t->mNamespace = $ns = intval( $ns );
534  $t->mDbkeyform = strtr( $title, ' ', '_' );
535  $t->mArticleID = ( $ns >= 0 ) ? -1 : 0;
536  $t->mUrlform = wfUrlencode( $t->mDbkeyform );
537  $t->mTextform = strtr( $title, '_', ' ' );
538  $t->mContentModel = false; # initialized lazily in getContentModel()
539  return $t;
540  }
541 
557  public static function makeTitleSafe( $ns, $title, $fragment = '', $interwiki = '' ) {
558  // NOTE: ideally, this would just call makeTitle() and then isValid(),
559  // but presently, that means more overhead on a potential performance hotspot.
560 
561  if ( !MWNamespace::exists( $ns ) ) {
562  return null;
563  }
564 
565  $t = new Title();
566  $t->mDbkeyform = self::makeName( $ns, $title, $fragment, $interwiki, true );
567 
568  try {
569  $t->secureAndSplit();
570  return $t;
571  } catch ( MalformedTitleException $ex ) {
572  return null;
573  }
574  }
575 
581  public static function newMainPage() {
582  $title = self::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
583  // Don't give fatal errors if the message is broken
584  if ( !$title ) {
585  $title = self::newFromText( 'Main Page' );
586  }
587  return $title;
588  }
589 
596  public static function nameOf( $id ) {
597  $dbr = wfGetDB( DB_REPLICA );
598 
599  $s = $dbr->selectRow(
600  'page',
601  [ 'page_namespace', 'page_title' ],
602  [ 'page_id' => $id ],
603  __METHOD__
604  );
605  if ( $s === false ) {
606  return null;
607  }
608 
609  $n = self::makeName( $s->page_namespace, $s->page_title );
610  return $n;
611  }
612 
618  public static function legalChars() {
620  return $wgLegalTitleChars;
621  }
622 
632  static function getTitleInvalidRegex() {
633  wfDeprecated( __METHOD__, '1.25' );
635  }
636 
646  public static function convertByteClassToUnicodeClass( $byteClass ) {
647  $length = strlen( $byteClass );
648  // Input token queue
649  $x0 = $x1 = $x2 = '';
650  // Decoded queue
651  $d0 = $d1 = $d2 = '';
652  // Decoded integer codepoints
653  $ord0 = $ord1 = $ord2 = 0;
654  // Re-encoded queue
655  $r0 = $r1 = $r2 = '';
656  // Output
657  $out = '';
658  // Flags
659  $allowUnicode = false;
660  for ( $pos = 0; $pos < $length; $pos++ ) {
661  // Shift the queues down
662  $x2 = $x1;
663  $x1 = $x0;
664  $d2 = $d1;
665  $d1 = $d0;
666  $ord2 = $ord1;
667  $ord1 = $ord0;
668  $r2 = $r1;
669  $r1 = $r0;
670  // Load the current input token and decoded values
671  $inChar = $byteClass[$pos];
672  if ( $inChar == '\\' ) {
673  if ( preg_match( '/x([0-9a-fA-F]{2})/A', $byteClass, $m, 0, $pos + 1 ) ) {
674  $x0 = $inChar . $m[0];
675  $d0 = chr( hexdec( $m[1] ) );
676  $pos += strlen( $m[0] );
677  } elseif ( preg_match( '/[0-7]{3}/A', $byteClass, $m, 0, $pos + 1 ) ) {
678  $x0 = $inChar . $m[0];
679  $d0 = chr( octdec( $m[0] ) );
680  $pos += strlen( $m[0] );
681  } elseif ( $pos + 1 >= $length ) {
682  $x0 = $d0 = '\\';
683  } else {
684  $d0 = $byteClass[$pos + 1];
685  $x0 = $inChar . $d0;
686  $pos += 1;
687  }
688  } else {
689  $x0 = $d0 = $inChar;
690  }
691  $ord0 = ord( $d0 );
692  // Load the current re-encoded value
693  if ( $ord0 < 32 || $ord0 == 0x7f ) {
694  $r0 = sprintf( '\x%02x', $ord0 );
695  } elseif ( $ord0 >= 0x80 ) {
696  // Allow unicode if a single high-bit character appears
697  $r0 = sprintf( '\x%02x', $ord0 );
698  $allowUnicode = true;
699  } elseif ( strpos( '-\\[]^', $d0 ) !== false ) {
700  $r0 = '\\' . $d0;
701  } else {
702  $r0 = $d0;
703  }
704  // Do the output
705  if ( $x0 !== '' && $x1 === '-' && $x2 !== '' ) {
706  // Range
707  if ( $ord2 > $ord0 ) {
708  // Empty range
709  } elseif ( $ord0 >= 0x80 ) {
710  // Unicode range
711  $allowUnicode = true;
712  if ( $ord2 < 0x80 ) {
713  // Keep the non-unicode section of the range
714  $out .= "$r2-\\x7F";
715  }
716  } else {
717  // Normal range
718  $out .= "$r2-$r0";
719  }
720  // Reset state to the initial value
721  $x0 = $x1 = $d0 = $d1 = $r0 = $r1 = '';
722  } elseif ( $ord2 < 0x80 ) {
723  // ASCII character
724  $out .= $r2;
725  }
726  }
727  if ( $ord1 < 0x80 ) {
728  $out .= $r1;
729  }
730  if ( $ord0 < 0x80 ) {
731  $out .= $r0;
732  }
733  if ( $allowUnicode ) {
734  $out .= '\u0080-\uFFFF';
735  }
736  return $out;
737  }
738 
750  public static function makeName( $ns, $title, $fragment = '', $interwiki = '',
751  $canonicalNamespace = false
752  ) {
754 
755  if ( $canonicalNamespace ) {
756  $namespace = MWNamespace::getCanonicalName( $ns );
757  } else {
758  $namespace = $wgContLang->getNsText( $ns );
759  }
760  $name = $namespace == '' ? $title : "$namespace:$title";
761  if ( strval( $interwiki ) != '' ) {
762  $name = "$interwiki:$name";
763  }
764  if ( strval( $fragment ) != '' ) {
765  $name .= '#' . $fragment;
766  }
767  return $name;
768  }
769 
778  static function escapeFragmentForURL( $fragment ) {
779  # Note that we don't urlencode the fragment. urlencoded Unicode
780  # fragments appear not to work in IE (at least up to 7) or in at least
781  # one version of Opera 9.x. The W3C validator, for one, doesn't seem
782  # to care if they aren't encoded.
783  return Sanitizer::escapeId( $fragment, 'noninitial' );
784  }
785 
794  public static function compare( LinkTarget $a, LinkTarget $b ) {
795  if ( $a->getNamespace() == $b->getNamespace() ) {
796  return strcmp( $a->getText(), $b->getText() );
797  } else {
798  return $a->getNamespace() - $b->getNamespace();
799  }
800  }
801 
816  public function isValid() {
817  $ns = $this->getNamespace();
818 
819  if ( !MWNamespace::exists( $ns ) ) {
820  return false;
821  }
822 
823  try {
824  $parser = MediaWikiServices::getInstance()->getTitleParser();
825  $parser->parseTitle( $this->getDBkey(), $ns );
826  return true;
827  } catch ( MalformedTitleException $ex ) {
828  return false;
829  }
830  }
831 
839  public function isLocal() {
840  if ( $this->isExternal() ) {
841  $iw = self::getInterwikiLookup()->fetch( $this->mInterwiki );
842  if ( $iw ) {
843  return $iw->isLocal();
844  }
845  }
846  return true;
847  }
848 
854  public function isExternal() {
855  return $this->mInterwiki !== '';
856  }
857 
865  public function getInterwiki() {
866  return $this->mInterwiki;
867  }
868 
874  public function wasLocalInterwiki() {
875  return $this->mLocalInterwiki;
876  }
877 
884  public function isTrans() {
885  if ( !$this->isExternal() ) {
886  return false;
887  }
888 
889  return self::getInterwikiLookup()->fetch( $this->mInterwiki )->isTranscludable();
890  }
891 
897  public function getTransWikiID() {
898  if ( !$this->isExternal() ) {
899  return false;
900  }
901 
902  return self::getInterwikiLookup()->fetch( $this->mInterwiki )->getWikiID();
903  }
904 
914  public function getTitleValue() {
915  if ( $this->mTitleValue === null ) {
916  try {
917  $this->mTitleValue = new TitleValue(
918  $this->getNamespace(),
919  $this->getDBkey(),
920  $this->getFragment(),
921  $this->getInterwiki()
922  );
923  } catch ( InvalidArgumentException $ex ) {
924  wfDebug( __METHOD__ . ': Can\'t create a TitleValue for [[' .
925  $this->getPrefixedText() . ']]: ' . $ex->getMessage() . "\n" );
926  }
927  }
928 
929  return $this->mTitleValue;
930  }
931 
937  public function getText() {
938  return $this->mTextform;
939  }
940 
946  public function getPartialURL() {
947  return $this->mUrlform;
948  }
949 
955  public function getDBkey() {
956  return $this->mDbkeyform;
957  }
958 
964  function getUserCaseDBKey() {
965  if ( !is_null( $this->mUserCaseDBKey ) ) {
966  return $this->mUserCaseDBKey;
967  } else {
968  // If created via makeTitle(), $this->mUserCaseDBKey is not set.
969  return $this->mDbkeyform;
970  }
971  }
972 
978  public function getNamespace() {
979  return $this->mNamespace;
980  }
981 
988  public function getContentModel( $flags = 0 ) {
989  if ( !$this->mForcedContentModel
990  && ( !$this->mContentModel || $flags === self::GAID_FOR_UPDATE )
991  && $this->getArticleID( $flags )
992  ) {
993  $linkCache = LinkCache::singleton();
994  $linkCache->addLinkObj( $this ); # in case we already had an article ID
995  $this->mContentModel = $linkCache->getGoodLinkFieldObj( $this, 'model' );
996  }
997 
998  if ( !$this->mContentModel ) {
999  $this->mContentModel = ContentHandler::getDefaultModelFor( $this );
1000  }
1001 
1002  return $this->mContentModel;
1003  }
1004 
1011  public function hasContentModel( $id ) {
1012  return $this->getContentModel() == $id;
1013  }
1014 
1026  public function setContentModel( $model ) {
1027  $this->mContentModel = $model;
1028  $this->mForcedContentModel = true;
1029  }
1030 
1036  public function getNsText() {
1037  if ( $this->isExternal() ) {
1038  // This probably shouldn't even happen,
1039  // but for interwiki transclusion it sometimes does.
1040  // Use the canonical namespaces if possible to try to
1041  // resolve a foreign namespace.
1042  if ( MWNamespace::exists( $this->mNamespace ) ) {
1043  return MWNamespace::getCanonicalName( $this->mNamespace );
1044  }
1045  }
1046 
1047  try {
1048  $formatter = self::getTitleFormatter();
1049  return $formatter->getNamespaceName( $this->mNamespace, $this->mDbkeyform );
1050  } catch ( InvalidArgumentException $ex ) {
1051  wfDebug( __METHOD__ . ': ' . $ex->getMessage() . "\n" );
1052  return false;
1053  }
1054  }
1055 
1061  public function getSubjectNsText() {
1063  return $wgContLang->getNsText( MWNamespace::getSubject( $this->mNamespace ) );
1064  }
1065 
1071  public function getTalkNsText() {
1073  return $wgContLang->getNsText( MWNamespace::getTalk( $this->mNamespace ) );
1074  }
1075 
1083  public function canTalk() {
1084  return $this->canHaveTalkPage();
1085  }
1086 
1095  public function canHaveTalkPage() {
1096  return MWNamespace::hasTalkNamespace( $this->mNamespace );
1097  }
1098 
1104  public function canExist() {
1105  return $this->mNamespace >= NS_MAIN;
1106  }
1107 
1113  public function isWatchable() {
1114  return !$this->isExternal() && MWNamespace::isWatchable( $this->getNamespace() );
1115  }
1116 
1122  public function isSpecialPage() {
1123  return $this->getNamespace() == NS_SPECIAL;
1124  }
1125 
1132  public function isSpecial( $name ) {
1133  if ( $this->isSpecialPage() ) {
1134  list( $thisName, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $this->getDBkey() );
1135  if ( $name == $thisName ) {
1136  return true;
1137  }
1138  }
1139  return false;
1140  }
1141 
1148  public function fixSpecialName() {
1149  if ( $this->isSpecialPage() ) {
1150  list( $canonicalName, $par ) = SpecialPageFactory::resolveAlias( $this->mDbkeyform );
1151  if ( $canonicalName ) {
1152  $localName = SpecialPageFactory::getLocalNameFor( $canonicalName, $par );
1153  if ( $localName != $this->mDbkeyform ) {
1154  return self::makeTitle( NS_SPECIAL, $localName );
1155  }
1156  }
1157  }
1158  return $this;
1159  }
1160 
1171  public function inNamespace( $ns ) {
1172  return MWNamespace::equals( $this->getNamespace(), $ns );
1173  }
1174 
1182  public function inNamespaces( /* ... */ ) {
1183  $namespaces = func_get_args();
1184  if ( count( $namespaces ) > 0 && is_array( $namespaces[0] ) ) {
1185  $namespaces = $namespaces[0];
1186  }
1187 
1188  foreach ( $namespaces as $ns ) {
1189  if ( $this->inNamespace( $ns ) ) {
1190  return true;
1191  }
1192  }
1193 
1194  return false;
1195  }
1196 
1210  public function hasSubjectNamespace( $ns ) {
1211  return MWNamespace::subjectEquals( $this->getNamespace(), $ns );
1212  }
1213 
1221  public function isContentPage() {
1222  return MWNamespace::isContent( $this->getNamespace() );
1223  }
1224 
1231  public function isMovable() {
1232  if ( !MWNamespace::isMovable( $this->getNamespace() ) || $this->isExternal() ) {
1233  // Interwiki title or immovable namespace. Hooks don't get to override here
1234  return false;
1235  }
1236 
1237  $result = true;
1238  Hooks::run( 'TitleIsMovable', [ $this, &$result ] );
1239  return $result;
1240  }
1241 
1252  public function isMainPage() {
1253  return $this->equals( self::newMainPage() );
1254  }
1255 
1261  public function isSubpage() {
1262  return MWNamespace::hasSubpages( $this->mNamespace )
1263  ? strpos( $this->getText(), '/' ) !== false
1264  : false;
1265  }
1266 
1272  public function isConversionTable() {
1273  // @todo ConversionTable should become a separate content model.
1274 
1275  return $this->getNamespace() == NS_MEDIAWIKI &&
1276  strpos( $this->getText(), 'Conversiontable/' ) === 0;
1277  }
1278 
1284  public function isWikitextPage() {
1285  return $this->hasContentModel( CONTENT_MODEL_WIKITEXT );
1286  }
1287 
1302  public function isCssOrJsPage() {
1303  $isCssOrJsPage = NS_MEDIAWIKI == $this->mNamespace
1304  && ( $this->hasContentModel( CONTENT_MODEL_CSS )
1306 
1307  return $isCssOrJsPage;
1308  }
1309 
1315  public function isCssJsSubpage() {
1316  return ( NS_USER == $this->mNamespace && $this->isSubpage()
1317  && ( $this->hasContentModel( CONTENT_MODEL_CSS )
1318  || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
1319  }
1320 
1326  public function getSkinFromCssJsSubpage() {
1327  $subpage = explode( '/', $this->mTextform );
1328  $subpage = $subpage[count( $subpage ) - 1];
1329  $lastdot = strrpos( $subpage, '.' );
1330  if ( $lastdot === false ) {
1331  return $subpage; # Never happens: only called for names ending in '.css' or '.js'
1332  }
1333  return substr( $subpage, 0, $lastdot );
1334  }
1335 
1341  public function isCssSubpage() {
1342  return ( NS_USER == $this->mNamespace && $this->isSubpage()
1343  && $this->hasContentModel( CONTENT_MODEL_CSS ) );
1344  }
1345 
1351  public function isJsSubpage() {
1352  return ( NS_USER == $this->mNamespace && $this->isSubpage()
1354  }
1355 
1361  public function isTalkPage() {
1362  return MWNamespace::isTalk( $this->getNamespace() );
1363  }
1364 
1370  public function getTalkPage() {
1371  return self::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
1372  }
1373 
1383  public function getTalkPageIfDefined() {
1384  if ( !$this->canHaveTalkPage() ) {
1385  return null;
1386  }
1387 
1388  return $this->getTalkPage();
1389  }
1390 
1397  public function getSubjectPage() {
1398  // Is this the same title?
1399  $subjectNS = MWNamespace::getSubject( $this->getNamespace() );
1400  if ( $this->getNamespace() == $subjectNS ) {
1401  return $this;
1402  }
1403  return self::makeTitle( $subjectNS, $this->getDBkey() );
1404  }
1405 
1414  public function getOtherPage() {
1415  if ( $this->isSpecialPage() ) {
1416  throw new MWException( 'Special pages cannot have other pages' );
1417  }
1418  if ( $this->isTalkPage() ) {
1419  return $this->getSubjectPage();
1420  } else {
1421  if ( !$this->canHaveTalkPage() ) {
1422  throw new MWException( "{$this->getPrefixedText()} does not have an other page" );
1423  }
1424  return $this->getTalkPage();
1425  }
1426  }
1427 
1433  public function getDefaultNamespace() {
1434  return $this->mDefaultNamespace;
1435  }
1436 
1444  public function getFragment() {
1445  return $this->mFragment;
1446  }
1447 
1454  public function hasFragment() {
1455  return $this->mFragment !== '';
1456  }
1457 
1463  public function getFragmentForURL() {
1464  if ( !$this->hasFragment() ) {
1465  return '';
1466  } elseif ( $this->isExternal() && !$this->getTransWikiID() ) {
1467  return '#' . Sanitizer::escapeIdForExternalInterwiki( $this->getFragment() );
1468  }
1469  return '#' . Sanitizer::escapeIdForLink( $this->getFragment() );
1470  }
1471 
1484  public function setFragment( $fragment ) {
1485  $this->mFragment = strtr( substr( $fragment, 1 ), '_', ' ' );
1486  }
1487 
1495  public function createFragmentTarget( $fragment ) {
1496  return self::makeTitle(
1497  $this->getNamespace(),
1498  $this->getText(),
1499  $fragment,
1500  $this->getInterwiki()
1501  );
1502  }
1503 
1511  private function prefix( $name ) {
1513 
1514  $p = '';
1515  if ( $this->isExternal() ) {
1516  $p = $this->mInterwiki . ':';
1517  }
1518 
1519  if ( 0 != $this->mNamespace ) {
1520  $nsText = $this->getNsText();
1521 
1522  if ( $nsText === false ) {
1523  // See T165149. Awkward, but better than erroneously linking to the main namespace.
1524  $nsText = $wgContLang->getNsText( NS_SPECIAL ) . ":Badtitle/NS{$this->mNamespace}";
1525  }
1526 
1527  $p .= $nsText . ':';
1528  }
1529  return $p . $name;
1530  }
1531 
1538  public function getPrefixedDBkey() {
1539  $s = $this->prefix( $this->mDbkeyform );
1540  $s = strtr( $s, ' ', '_' );
1541  return $s;
1542  }
1543 
1550  public function getPrefixedText() {
1551  if ( $this->mPrefixedText === null ) {
1552  $s = $this->prefix( $this->mTextform );
1553  $s = strtr( $s, '_', ' ' );
1554  $this->mPrefixedText = $s;
1555  }
1556  return $this->mPrefixedText;
1557  }
1558 
1564  public function __toString() {
1565  return $this->getPrefixedText();
1566  }
1567 
1574  public function getFullText() {
1575  $text = $this->getPrefixedText();
1576  if ( $this->hasFragment() ) {
1577  $text .= '#' . $this->getFragment();
1578  }
1579  return $text;
1580  }
1581 
1594  public function getRootText() {
1595  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1596  return $this->getText();
1597  }
1598 
1599  return strtok( $this->getText(), '/' );
1600  }
1601 
1614  public function getRootTitle() {
1615  return self::makeTitle( $this->getNamespace(), $this->getRootText() );
1616  }
1617 
1629  public function getBaseText() {
1630  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1631  return $this->getText();
1632  }
1633 
1634  $parts = explode( '/', $this->getText() );
1635  # Don't discard the real title if there's no subpage involved
1636  if ( count( $parts ) > 1 ) {
1637  unset( $parts[count( $parts ) - 1] );
1638  }
1639  return implode( '/', $parts );
1640  }
1641 
1654  public function getBaseTitle() {
1655  return self::makeTitle( $this->getNamespace(), $this->getBaseText() );
1656  }
1657 
1669  public function getSubpageText() {
1670  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1671  return $this->mTextform;
1672  }
1673  $parts = explode( '/', $this->mTextform );
1674  return $parts[count( $parts ) - 1];
1675  }
1676 
1690  public function getSubpage( $text ) {
1691  return self::makeTitleSafe( $this->getNamespace(), $this->getText() . '/' . $text );
1692  }
1693 
1699  public function getSubpageUrlForm() {
1700  $text = $this->getSubpageText();
1701  $text = wfUrlencode( strtr( $text, ' ', '_' ) );
1702  return $text;
1703  }
1704 
1710  public function getPrefixedURL() {
1711  $s = $this->prefix( $this->mDbkeyform );
1712  $s = wfUrlencode( strtr( $s, ' ', '_' ) );
1713  return $s;
1714  }
1715 
1729  private static function fixUrlQueryArgs( $query, $query2 = false ) {
1730  if ( $query2 !== false ) {
1731  wfDeprecated( "Title::get{Canonical,Full,Link,Local,Internal}URL " .
1732  "method called with a second parameter is deprecated. Add your " .
1733  "parameter to an array passed as the first parameter.", "1.19" );
1734  }
1735  if ( is_array( $query ) ) {
1736  $query = wfArrayToCgi( $query );
1737  }
1738  if ( $query2 ) {
1739  if ( is_string( $query2 ) ) {
1740  // $query2 is a string, we will consider this to be
1741  // a deprecated $variant argument and add it to the query
1742  $query2 = wfArrayToCgi( [ 'variant' => $query2 ] );
1743  } else {
1744  $query2 = wfArrayToCgi( $query2 );
1745  }
1746  // If we have $query content add a & to it first
1747  if ( $query ) {
1748  $query .= '&';
1749  }
1750  // Now append the queries together
1751  $query .= $query2;
1752  }
1753  return $query;
1754  }
1755 
1767  public function getFullURL( $query = '', $query2 = false, $proto = PROTO_RELATIVE ) {
1768  $query = self::fixUrlQueryArgs( $query, $query2 );
1769 
1770  # Hand off all the decisions on urls to getLocalURL
1771  $url = $this->getLocalURL( $query );
1772 
1773  # Expand the url to make it a full url. Note that getLocalURL has the
1774  # potential to output full urls for a variety of reasons, so we use
1775  # wfExpandUrl instead of simply prepending $wgServer
1776  $url = wfExpandUrl( $url, $proto );
1777 
1778  # Finally, add the fragment.
1779  $url .= $this->getFragmentForURL();
1780  // Avoid PHP 7.1 warning from passing $this by reference
1781  $titleRef = $this;
1782  Hooks::run( 'GetFullURL', [ &$titleRef, &$url, $query ] );
1783  return $url;
1784  }
1785 
1802  public function getFullUrlForRedirect( $query = '', $proto = PROTO_CURRENT ) {
1803  $target = $this;
1804  if ( $this->isExternal() ) {
1805  $target = SpecialPage::getTitleFor(
1806  'GoToInterwiki',
1807  $this->getPrefixedDBKey()
1808  );
1809  }
1810  return $target->getFullUrl( $query, false, $proto );
1811  }
1812 
1836  public function getLocalURL( $query = '', $query2 = false ) {
1838 
1839  $query = self::fixUrlQueryArgs( $query, $query2 );
1840 
1841  $interwiki = self::getInterwikiLookup()->fetch( $this->mInterwiki );
1842  if ( $interwiki ) {
1843  $namespace = $this->getNsText();
1844  if ( $namespace != '' ) {
1845  # Can this actually happen? Interwikis shouldn't be parsed.
1846  # Yes! It can in interwiki transclusion. But... it probably shouldn't.
1847  $namespace .= ':';
1848  }
1849  $url = $interwiki->getURL( $namespace . $this->getDBkey() );
1850  $url = wfAppendQuery( $url, $query );
1851  } else {
1852  $dbkey = wfUrlencode( $this->getPrefixedDBkey() );
1853  if ( $query == '' ) {
1854  $url = str_replace( '$1', $dbkey, $wgArticlePath );
1855  // Avoid PHP 7.1 warning from passing $this by reference
1856  $titleRef = $this;
1857  Hooks::run( 'GetLocalURL::Article', [ &$titleRef, &$url ] );
1858  } else {
1860  $url = false;
1861  $matches = [];
1862 
1863  if ( !empty( $wgActionPaths )
1864  && preg_match( '/^(.*&|)action=([^&]*)(&(.*)|)$/', $query, $matches )
1865  ) {
1866  $action = urldecode( $matches[2] );
1867  if ( isset( $wgActionPaths[$action] ) ) {
1868  $query = $matches[1];
1869  if ( isset( $matches[4] ) ) {
1870  $query .= $matches[4];
1871  }
1872  $url = str_replace( '$1', $dbkey, $wgActionPaths[$action] );
1873  if ( $query != '' ) {
1874  $url = wfAppendQuery( $url, $query );
1875  }
1876  }
1877  }
1878 
1879  if ( $url === false
1881  && preg_match( '/^variant=([^&]*)$/', $query, $matches )
1882  && $this->getPageLanguage()->equals( $wgContLang )
1883  && $this->getPageLanguage()->hasVariants()
1884  ) {
1885  $variant = urldecode( $matches[1] );
1886  if ( $this->getPageLanguage()->hasVariant( $variant ) ) {
1887  // Only do the variant replacement if the given variant is a valid
1888  // variant for the page's language.
1889  $url = str_replace( '$2', urlencode( $variant ), $wgVariantArticlePath );
1890  $url = str_replace( '$1', $dbkey, $url );
1891  }
1892  }
1893 
1894  if ( $url === false ) {
1895  if ( $query == '-' ) {
1896  $query = '';
1897  }
1898  $url = "{$wgScript}?title={$dbkey}&{$query}";
1899  }
1900  }
1901  // Avoid PHP 7.1 warning from passing $this by reference
1902  $titleRef = $this;
1903  Hooks::run( 'GetLocalURL::Internal', [ &$titleRef, &$url, $query ] );
1904 
1905  // @todo FIXME: This causes breakage in various places when we
1906  // actually expected a local URL and end up with dupe prefixes.
1907  if ( $wgRequest->getVal( 'action' ) == 'render' ) {
1908  $url = $wgServer . $url;
1909  }
1910  }
1911  // Avoid PHP 7.1 warning from passing $this by reference
1912  $titleRef = $this;
1913  Hooks::run( 'GetLocalURL', [ &$titleRef, &$url, $query ] );
1914  return $url;
1915  }
1916 
1934  public function getLinkURL( $query = '', $query2 = false, $proto = false ) {
1935  if ( $this->isExternal() || $proto !== false ) {
1936  $ret = $this->getFullURL( $query, $query2, $proto );
1937  } elseif ( $this->getPrefixedText() === '' && $this->hasFragment() ) {
1938  $ret = $this->getFragmentForURL();
1939  } else {
1940  $ret = $this->getLocalURL( $query, $query2 ) . $this->getFragmentForURL();
1941  }
1942  return $ret;
1943  }
1944 
1959  public function getInternalURL( $query = '', $query2 = false ) {
1961  $query = self::fixUrlQueryArgs( $query, $query2 );
1962  $server = $wgInternalServer !== false ? $wgInternalServer : $wgServer;
1963  $url = wfExpandUrl( $server . $this->getLocalURL( $query ), PROTO_HTTP );
1964  // Avoid PHP 7.1 warning from passing $this by reference
1965  $titleRef = $this;
1966  Hooks::run( 'GetInternalURL', [ &$titleRef, &$url, $query ] );
1967  return $url;
1968  }
1969 
1983  public function getCanonicalURL( $query = '', $query2 = false ) {
1984  $query = self::fixUrlQueryArgs( $query, $query2 );
1985  $url = wfExpandUrl( $this->getLocalURL( $query ) . $this->getFragmentForURL(), PROTO_CANONICAL );
1986  // Avoid PHP 7.1 warning from passing $this by reference
1987  $titleRef = $this;
1988  Hooks::run( 'GetCanonicalURL', [ &$titleRef, &$url, $query ] );
1989  return $url;
1990  }
1991 
1997  public function getEditURL() {
1998  if ( $this->isExternal() ) {
1999  return '';
2000  }
2001  $s = $this->getLocalURL( 'action=edit' );
2002 
2003  return $s;
2004  }
2005 
2020  public function quickUserCan( $action, $user = null ) {
2021  return $this->userCan( $action, $user, false );
2022  }
2023 
2033  public function userCan( $action, $user = null, $rigor = 'secure' ) {
2034  if ( !$user instanceof User ) {
2035  global $wgUser;
2036  $user = $wgUser;
2037  }
2038 
2039  return !count( $this->getUserPermissionsErrorsInternal( $action, $user, $rigor, true ) );
2040  }
2041 
2057  public function getUserPermissionsErrors(
2058  $action, $user, $rigor = 'secure', $ignoreErrors = []
2059  ) {
2060  $errors = $this->getUserPermissionsErrorsInternal( $action, $user, $rigor );
2061 
2062  // Remove the errors being ignored.
2063  foreach ( $errors as $index => $error ) {
2064  $errKey = is_array( $error ) ? $error[0] : $error;
2065 
2066  if ( in_array( $errKey, $ignoreErrors ) ) {
2067  unset( $errors[$index] );
2068  }
2069  if ( $errKey instanceof MessageSpecifier && in_array( $errKey->getKey(), $ignoreErrors ) ) {
2070  unset( $errors[$index] );
2071  }
2072  }
2073 
2074  return $errors;
2075  }
2076 
2088  private function checkQuickPermissions( $action, $user, $errors, $rigor, $short ) {
2089  if ( !Hooks::run( 'TitleQuickPermissions',
2090  [ $this, $user, $action, &$errors, ( $rigor !== 'quick' ), $short ] )
2091  ) {
2092  return $errors;
2093  }
2094 
2095  if ( $action == 'create' ) {
2096  if (
2097  ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
2098  ( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) )
2099  ) {
2100  $errors[] = $user->isAnon() ? [ 'nocreatetext' ] : [ 'nocreate-loggedin' ];
2101  }
2102  } elseif ( $action == 'move' ) {
2103  if ( !$user->isAllowed( 'move-rootuserpages' )
2104  && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
2105  // Show user page-specific message only if the user can move other pages
2106  $errors[] = [ 'cant-move-user-page' ];
2107  }
2108 
2109  // Check if user is allowed to move files if it's a file
2110  if ( $this->mNamespace == NS_FILE && !$user->isAllowed( 'movefile' ) ) {
2111  $errors[] = [ 'movenotallowedfile' ];
2112  }
2113 
2114  // Check if user is allowed to move category pages if it's a category page
2115  if ( $this->mNamespace == NS_CATEGORY && !$user->isAllowed( 'move-categorypages' ) ) {
2116  $errors[] = [ 'cant-move-category-page' ];
2117  }
2118 
2119  if ( !$user->isAllowed( 'move' ) ) {
2120  // User can't move anything
2121  $userCanMove = User::groupHasPermission( 'user', 'move' );
2122  $autoconfirmedCanMove = User::groupHasPermission( 'autoconfirmed', 'move' );
2123  if ( $user->isAnon() && ( $userCanMove || $autoconfirmedCanMove ) ) {
2124  // custom message if logged-in users without any special rights can move
2125  $errors[] = [ 'movenologintext' ];
2126  } else {
2127  $errors[] = [ 'movenotallowed' ];
2128  }
2129  }
2130  } elseif ( $action == 'move-target' ) {
2131  if ( !$user->isAllowed( 'move' ) ) {
2132  // User can't move anything
2133  $errors[] = [ 'movenotallowed' ];
2134  } elseif ( !$user->isAllowed( 'move-rootuserpages' )
2135  && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
2136  // Show user page-specific message only if the user can move other pages
2137  $errors[] = [ 'cant-move-to-user-page' ];
2138  } elseif ( !$user->isAllowed( 'move-categorypages' )
2139  && $this->mNamespace == NS_CATEGORY ) {
2140  // Show category page-specific message only if the user can move other pages
2141  $errors[] = [ 'cant-move-to-category-page' ];
2142  }
2143  } elseif ( !$user->isAllowed( $action ) ) {
2144  $errors[] = $this->missingPermissionError( $action, $short );
2145  }
2146 
2147  return $errors;
2148  }
2149 
2158  private function resultToError( $errors, $result ) {
2159  if ( is_array( $result ) && count( $result ) && !is_array( $result[0] ) ) {
2160  // A single array representing an error
2161  $errors[] = $result;
2162  } elseif ( is_array( $result ) && is_array( $result[0] ) ) {
2163  // A nested array representing multiple errors
2164  $errors = array_merge( $errors, $result );
2165  } elseif ( $result !== '' && is_string( $result ) ) {
2166  // A string representing a message-id
2167  $errors[] = [ $result ];
2168  } elseif ( $result instanceof MessageSpecifier ) {
2169  // A message specifier representing an error
2170  $errors[] = [ $result ];
2171  } elseif ( $result === false ) {
2172  // a generic "We don't want them to do that"
2173  $errors[] = [ 'badaccess-group0' ];
2174  }
2175  return $errors;
2176  }
2177 
2189  private function checkPermissionHooks( $action, $user, $errors, $rigor, $short ) {
2190  // Use getUserPermissionsErrors instead
2191  $result = '';
2192  // Avoid PHP 7.1 warning from passing $this by reference
2193  $titleRef = $this;
2194  if ( !Hooks::run( 'userCan', [ &$titleRef, &$user, $action, &$result ] ) ) {
2195  return $result ? [] : [ [ 'badaccess-group0' ] ];
2196  }
2197  // Check getUserPermissionsErrors hook
2198  // Avoid PHP 7.1 warning from passing $this by reference
2199  $titleRef = $this;
2200  if ( !Hooks::run( 'getUserPermissionsErrors', [ &$titleRef, &$user, $action, &$result ] ) ) {
2201  $errors = $this->resultToError( $errors, $result );
2202  }
2203  // Check getUserPermissionsErrorsExpensive hook
2204  if (
2205  $rigor !== 'quick'
2206  && !( $short && count( $errors ) > 0 )
2207  && !Hooks::run( 'getUserPermissionsErrorsExpensive', [ &$titleRef, &$user, $action, &$result ] )
2208  ) {
2209  $errors = $this->resultToError( $errors, $result );
2210  }
2211 
2212  return $errors;
2213  }
2214 
2226  private function checkSpecialsAndNSPermissions( $action, $user, $errors, $rigor, $short ) {
2227  # Only 'createaccount' can be performed on special pages,
2228  # which don't actually exist in the DB.
2229  if ( $this->isSpecialPage() && $action !== 'createaccount' ) {
2230  $errors[] = [ 'ns-specialprotected' ];
2231  }
2232 
2233  # Check $wgNamespaceProtection for restricted namespaces
2234  if ( $this->isNamespaceProtected( $user ) ) {
2235  $ns = $this->mNamespace == NS_MAIN ?
2236  wfMessage( 'nstab-main' )->text() : $this->getNsText();
2237  $errors[] = $this->mNamespace == NS_MEDIAWIKI ?
2238  [ 'protectedinterface', $action ] : [ 'namespaceprotected', $ns, $action ];
2239  }
2240 
2241  return $errors;
2242  }
2243 
2255  private function checkCSSandJSPermissions( $action, $user, $errors, $rigor, $short ) {
2256  # Protect css/js subpages of user pages
2257  # XXX: this might be better using restrictions
2258  if ( $action != 'patrol' ) {
2259  if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
2260  if ( $this->isCssSubpage() && !$user->isAllowedAny( 'editmyusercss', 'editusercss' ) ) {
2261  $errors[] = [ 'mycustomcssprotected', $action ];
2262  } elseif ( $this->isJsSubpage() && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' ) ) {
2263  $errors[] = [ 'mycustomjsprotected', $action ];
2264  }
2265  } else {
2266  if ( $this->isCssSubpage() && !$user->isAllowed( 'editusercss' ) ) {
2267  $errors[] = [ 'customcssprotected', $action ];
2268  } elseif ( $this->isJsSubpage() && !$user->isAllowed( 'edituserjs' ) ) {
2269  $errors[] = [ 'customjsprotected', $action ];
2270  }
2271  }
2272  }
2273 
2274  return $errors;
2275  }
2276 
2290  private function checkPageRestrictions( $action, $user, $errors, $rigor, $short ) {
2291  foreach ( $this->getRestrictions( $action ) as $right ) {
2292  // Backwards compatibility, rewrite sysop -> editprotected
2293  if ( $right == 'sysop' ) {
2294  $right = 'editprotected';
2295  }
2296  // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
2297  if ( $right == 'autoconfirmed' ) {
2298  $right = 'editsemiprotected';
2299  }
2300  if ( $right == '' ) {
2301  continue;
2302  }
2303  if ( !$user->isAllowed( $right ) ) {
2304  $errors[] = [ 'protectedpagetext', $right, $action ];
2305  } elseif ( $this->mCascadeRestriction && !$user->isAllowed( 'protect' ) ) {
2306  $errors[] = [ 'protectedpagetext', 'protect', $action ];
2307  }
2308  }
2309 
2310  return $errors;
2311  }
2312 
2324  private function checkCascadingSourcesRestrictions( $action, $user, $errors, $rigor, $short ) {
2325  if ( $rigor !== 'quick' && !$this->isCssJsSubpage() ) {
2326  # We /could/ use the protection level on the source page, but it's
2327  # fairly ugly as we have to establish a precedence hierarchy for pages
2328  # included by multiple cascade-protected pages. So just restrict
2329  # it to people with 'protect' permission, as they could remove the
2330  # protection anyway.
2331  list( $cascadingSources, $restrictions ) = $this->getCascadeProtectionSources();
2332  # Cascading protection depends on more than this page...
2333  # Several cascading protected pages may include this page...
2334  # Check each cascading level
2335  # This is only for protection restrictions, not for all actions
2336  if ( isset( $restrictions[$action] ) ) {
2337  foreach ( $restrictions[$action] as $right ) {
2338  // Backwards compatibility, rewrite sysop -> editprotected
2339  if ( $right == 'sysop' ) {
2340  $right = 'editprotected';
2341  }
2342  // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
2343  if ( $right == 'autoconfirmed' ) {
2344  $right = 'editsemiprotected';
2345  }
2346  if ( $right != '' && !$user->isAllowedAll( 'protect', $right ) ) {
2347  $pages = '';
2348  foreach ( $cascadingSources as $page ) {
2349  $pages .= '* [[:' . $page->getPrefixedText() . "]]\n";
2350  }
2351  $errors[] = [ 'cascadeprotected', count( $cascadingSources ), $pages, $action ];
2352  }
2353  }
2354  }
2355  }
2356 
2357  return $errors;
2358  }
2359 
2371  private function checkActionPermissions( $action, $user, $errors, $rigor, $short ) {
2373 
2374  if ( $action == 'protect' ) {
2375  if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $rigor, true ) ) ) {
2376  // If they can't edit, they shouldn't protect.
2377  $errors[] = [ 'protect-cantedit' ];
2378  }
2379  } elseif ( $action == 'create' ) {
2380  $title_protection = $this->getTitleProtection();
2381  if ( $title_protection ) {
2382  if ( $title_protection['permission'] == ''
2383  || !$user->isAllowed( $title_protection['permission'] )
2384  ) {
2385  $errors[] = [
2386  'titleprotected',
2387  User::whoIs( $title_protection['user'] ),
2388  $title_protection['reason']
2389  ];
2390  }
2391  }
2392  } elseif ( $action == 'move' ) {
2393  // Check for immobile pages
2394  if ( !MWNamespace::isMovable( $this->mNamespace ) ) {
2395  // Specific message for this case
2396  $errors[] = [ 'immobile-source-namespace', $this->getNsText() ];
2397  } elseif ( !$this->isMovable() ) {
2398  // Less specific message for rarer cases
2399  $errors[] = [ 'immobile-source-page' ];
2400  }
2401  } elseif ( $action == 'move-target' ) {
2402  if ( !MWNamespace::isMovable( $this->mNamespace ) ) {
2403  $errors[] = [ 'immobile-target-namespace', $this->getNsText() ];
2404  } elseif ( !$this->isMovable() ) {
2405  $errors[] = [ 'immobile-target-page' ];
2406  }
2407  } elseif ( $action == 'delete' ) {
2408  $tempErrors = $this->checkPageRestrictions( 'edit', $user, [], $rigor, true );
2409  if ( !$tempErrors ) {
2410  $tempErrors = $this->checkCascadingSourcesRestrictions( 'edit',
2411  $user, $tempErrors, $rigor, true );
2412  }
2413  if ( $tempErrors ) {
2414  // If protection keeps them from editing, they shouldn't be able to delete.
2415  $errors[] = [ 'deleteprotected' ];
2416  }
2417  if ( $rigor !== 'quick' && $wgDeleteRevisionsLimit
2418  && !$this->userCan( 'bigdelete', $user ) && $this->isBigDeletion()
2419  ) {
2420  $errors[] = [ 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ];
2421  }
2422  } elseif ( $action === 'undelete' ) {
2423  if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $rigor, true ) ) ) {
2424  // Undeleting implies editing
2425  $errors[] = [ 'undelete-cantedit' ];
2426  }
2427  if ( !$this->exists()
2428  && count( $this->getUserPermissionsErrorsInternal( 'create', $user, $rigor, true ) )
2429  ) {
2430  // Undeleting where nothing currently exists implies creating
2431  $errors[] = [ 'undelete-cantcreate' ];
2432  }
2433  }
2434  return $errors;
2435  }
2436 
2448  private function checkUserBlock( $action, $user, $errors, $rigor, $short ) {
2450  // Account creation blocks handled at userlogin.
2451  // Unblocking handled in SpecialUnblock
2452  if ( $rigor === 'quick' || in_array( $action, [ 'createaccount', 'unblock' ] ) ) {
2453  return $errors;
2454  }
2455 
2456  // Optimize for a very common case
2457  if ( $action === 'read' && !$wgBlockDisablesLogin ) {
2458  return $errors;
2459  }
2460 
2462  && !$user->isEmailConfirmed()
2463  && $action === 'edit'
2464  ) {
2465  $errors[] = [ 'confirmedittext' ];
2466  }
2467 
2468  $useSlave = ( $rigor !== 'secure' );
2469  if ( ( $action == 'edit' || $action == 'create' )
2470  && !$user->isBlockedFrom( $this, $useSlave )
2471  ) {
2472  // Don't block the user from editing their own talk page unless they've been
2473  // explicitly blocked from that too.
2474  } elseif ( $user->isBlocked() && $user->getBlock()->prevents( $action ) !== false ) {
2475  // @todo FIXME: Pass the relevant context into this function.
2476  $errors[] = $user->getBlock()->getPermissionsError( RequestContext::getMain() );
2477  }
2478 
2479  return $errors;
2480  }
2481 
2493  private function checkReadPermissions( $action, $user, $errors, $rigor, $short ) {
2495 
2496  $whitelisted = false;
2497  if ( User::isEveryoneAllowed( 'read' ) ) {
2498  # Shortcut for public wikis, allows skipping quite a bit of code
2499  $whitelisted = true;
2500  } elseif ( $user->isAllowed( 'read' ) ) {
2501  # If the user is allowed to read pages, he is allowed to read all pages
2502  $whitelisted = true;
2503  } elseif ( $this->isSpecial( 'Userlogin' )
2504  || $this->isSpecial( 'PasswordReset' )
2505  || $this->isSpecial( 'Userlogout' )
2506  ) {
2507  # Always grant access to the login page.
2508  # Even anons need to be able to log in.
2509  $whitelisted = true;
2510  } elseif ( is_array( $wgWhitelistRead ) && count( $wgWhitelistRead ) ) {
2511  # Time to check the whitelist
2512  # Only do these checks is there's something to check against
2513  $name = $this->getPrefixedText();
2514  $dbName = $this->getPrefixedDBkey();
2515 
2516  // Check for explicit whitelisting with and without underscores
2517  if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) {
2518  $whitelisted = true;
2519  } elseif ( $this->getNamespace() == NS_MAIN ) {
2520  # Old settings might have the title prefixed with
2521  # a colon for main-namespace pages
2522  if ( in_array( ':' . $name, $wgWhitelistRead ) ) {
2523  $whitelisted = true;
2524  }
2525  } elseif ( $this->isSpecialPage() ) {
2526  # If it's a special page, ditch the subpage bit and check again
2527  $name = $this->getDBkey();
2528  list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name );
2529  if ( $name ) {
2530  $pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
2531  if ( in_array( $pure, $wgWhitelistRead, true ) ) {
2532  $whitelisted = true;
2533  }
2534  }
2535  }
2536  }
2537 
2538  if ( !$whitelisted && is_array( $wgWhitelistReadRegexp ) && !empty( $wgWhitelistReadRegexp ) ) {
2539  $name = $this->getPrefixedText();
2540  // Check for regex whitelisting
2541  foreach ( $wgWhitelistReadRegexp as $listItem ) {
2542  if ( preg_match( $listItem, $name ) ) {
2543  $whitelisted = true;
2544  break;
2545  }
2546  }
2547  }
2548 
2549  if ( !$whitelisted ) {
2550  # If the title is not whitelisted, give extensions a chance to do so...
2551  Hooks::run( 'TitleReadWhitelist', [ $this, $user, &$whitelisted ] );
2552  if ( !$whitelisted ) {
2553  $errors[] = $this->missingPermissionError( $action, $short );
2554  }
2555  }
2556 
2557  return $errors;
2558  }
2559 
2568  private function missingPermissionError( $action, $short ) {
2569  // We avoid expensive display logic for quickUserCan's and such
2570  if ( $short ) {
2571  return [ 'badaccess-group0' ];
2572  }
2573 
2574  return User::newFatalPermissionDeniedStatus( $action )->getErrorsArray()[0];
2575  }
2576 
2592  $action, $user, $rigor = 'secure', $short = false
2593  ) {
2594  if ( $rigor === true ) {
2595  $rigor = 'secure'; // b/c
2596  } elseif ( $rigor === false ) {
2597  $rigor = 'quick'; // b/c
2598  } elseif ( !in_array( $rigor, [ 'quick', 'full', 'secure' ] ) ) {
2599  throw new Exception( "Invalid rigor parameter '$rigor'." );
2600  }
2601 
2602  # Read has special handling
2603  if ( $action == 'read' ) {
2604  $checks = [
2605  'checkPermissionHooks',
2606  'checkReadPermissions',
2607  'checkUserBlock', // for wgBlockDisablesLogin
2608  ];
2609  # Don't call checkSpecialsAndNSPermissions or checkCSSandJSPermissions
2610  # here as it will lead to duplicate error messages. This is okay to do
2611  # since anywhere that checks for create will also check for edit, and
2612  # those checks are called for edit.
2613  } elseif ( $action == 'create' ) {
2614  $checks = [
2615  'checkQuickPermissions',
2616  'checkPermissionHooks',
2617  'checkPageRestrictions',
2618  'checkCascadingSourcesRestrictions',
2619  'checkActionPermissions',
2620  'checkUserBlock'
2621  ];
2622  } else {
2623  $checks = [
2624  'checkQuickPermissions',
2625  'checkPermissionHooks',
2626  'checkSpecialsAndNSPermissions',
2627  'checkCSSandJSPermissions',
2628  'checkPageRestrictions',
2629  'checkCascadingSourcesRestrictions',
2630  'checkActionPermissions',
2631  'checkUserBlock'
2632  ];
2633  }
2634 
2635  $errors = [];
2636  while ( count( $checks ) > 0 &&
2637  !( $short && count( $errors ) > 0 ) ) {
2638  $method = array_shift( $checks );
2639  $errors = $this->$method( $action, $user, $errors, $rigor, $short );
2640  }
2641 
2642  return $errors;
2643  }
2644 
2652  public static function getFilteredRestrictionTypes( $exists = true ) {
2654  $types = $wgRestrictionTypes;
2655  if ( $exists ) {
2656  # Remove the create restriction for existing titles
2657  $types = array_diff( $types, [ 'create' ] );
2658  } else {
2659  # Only the create and upload restrictions apply to non-existing titles
2660  $types = array_intersect( $types, [ 'create', 'upload' ] );
2661  }
2662  return $types;
2663  }
2664 
2670  public function getRestrictionTypes() {
2671  if ( $this->isSpecialPage() ) {
2672  return [];
2673  }
2674 
2675  $types = self::getFilteredRestrictionTypes( $this->exists() );
2676 
2677  if ( $this->getNamespace() != NS_FILE ) {
2678  # Remove the upload restriction for non-file titles
2679  $types = array_diff( $types, [ 'upload' ] );
2680  }
2681 
2682  Hooks::run( 'TitleGetRestrictionTypes', [ $this, &$types ] );
2683 
2684  wfDebug( __METHOD__ . ': applicable restrictions to [[' .
2685  $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" );
2686 
2687  return $types;
2688  }
2689 
2697  public function getTitleProtection() {
2698  $protection = $this->getTitleProtectionInternal();
2699  if ( $protection ) {
2700  if ( $protection['permission'] == 'sysop' ) {
2701  $protection['permission'] = 'editprotected'; // B/C
2702  }
2703  if ( $protection['permission'] == 'autoconfirmed' ) {
2704  $protection['permission'] = 'editsemiprotected'; // B/C
2705  }
2706  }
2707  return $protection;
2708  }
2709 
2720  protected function getTitleProtectionInternal() {
2721  // Can't protect pages in special namespaces
2722  if ( $this->getNamespace() < 0 ) {
2723  return false;
2724  }
2725 
2726  // Can't protect pages that exist.
2727  if ( $this->exists() ) {
2728  return false;
2729  }
2730 
2731  if ( $this->mTitleProtection === null ) {
2732  $dbr = wfGetDB( DB_REPLICA );
2733  $commentStore = new CommentStore( 'pt_reason' );
2734  $commentQuery = $commentStore->getJoin();
2735  $res = $dbr->select(
2736  [ 'protected_titles' ] + $commentQuery['tables'],
2737  [
2738  'user' => 'pt_user',
2739  'expiry' => 'pt_expiry',
2740  'permission' => 'pt_create_perm'
2741  ] + $commentQuery['fields'],
2742  [ 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ],
2743  __METHOD__,
2744  [],
2745  $commentQuery['joins']
2746  );
2747 
2748  // fetchRow returns false if there are no rows.
2749  $row = $dbr->fetchRow( $res );
2750  if ( $row ) {
2751  $this->mTitleProtection = [
2752  'user' => $row['user'],
2753  'expiry' => $dbr->decodeExpiry( $row['expiry'] ),
2754  'permission' => $row['permission'],
2755  'reason' => $commentStore->getComment( $row )->text,
2756  ];
2757  } else {
2758  $this->mTitleProtection = false;
2759  }
2760  }
2761  return $this->mTitleProtection;
2762  }
2763 
2767  public function deleteTitleProtection() {
2768  $dbw = wfGetDB( DB_MASTER );
2769 
2770  $dbw->delete(
2771  'protected_titles',
2772  [ 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ],
2773  __METHOD__
2774  );
2775  $this->mTitleProtection = false;
2776  }
2777 
2785  public function isSemiProtected( $action = 'edit' ) {
2787 
2788  $restrictions = $this->getRestrictions( $action );
2790  if ( !$restrictions || !$semi ) {
2791  // Not protected, or all protection is full protection
2792  return false;
2793  }
2794 
2795  // Remap autoconfirmed to editsemiprotected for BC
2796  foreach ( array_keys( $semi, 'autoconfirmed' ) as $key ) {
2797  $semi[$key] = 'editsemiprotected';
2798  }
2799  foreach ( array_keys( $restrictions, 'autoconfirmed' ) as $key ) {
2800  $restrictions[$key] = 'editsemiprotected';
2801  }
2802 
2803  return !array_diff( $restrictions, $semi );
2804  }
2805 
2813  public function isProtected( $action = '' ) {
2815 
2816  $restrictionTypes = $this->getRestrictionTypes();
2817 
2818  # Special pages have inherent protection
2819  if ( $this->isSpecialPage() ) {
2820  return true;
2821  }
2822 
2823  # Check regular protection levels
2824  foreach ( $restrictionTypes as $type ) {
2825  if ( $action == $type || $action == '' ) {
2826  $r = $this->getRestrictions( $type );
2827  foreach ( $wgRestrictionLevels as $level ) {
2828  if ( in_array( $level, $r ) && $level != '' ) {
2829  return true;
2830  }
2831  }
2832  }
2833  }
2834 
2835  return false;
2836  }
2837 
2845  public function isNamespaceProtected( User $user ) {
2847 
2848  if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) {
2849  foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) {
2850  if ( $right != '' && !$user->isAllowed( $right ) ) {
2851  return true;
2852  }
2853  }
2854  }
2855  return false;
2856  }
2857 
2863  public function isCascadeProtected() {
2864  list( $sources, /* $restrictions */ ) = $this->getCascadeProtectionSources( false );
2865  return ( $sources > 0 );
2866  }
2867 
2877  public function areCascadeProtectionSourcesLoaded( $getPages = true ) {
2878  return $getPages ? $this->mCascadeSources !== null : $this->mHasCascadingRestrictions !== null;
2879  }
2880 
2894  public function getCascadeProtectionSources( $getPages = true ) {
2895  $pagerestrictions = [];
2896 
2897  if ( $this->mCascadeSources !== null && $getPages ) {
2899  } elseif ( $this->mHasCascadingRestrictions !== null && !$getPages ) {
2900  return [ $this->mHasCascadingRestrictions, $pagerestrictions ];
2901  }
2902 
2903  $dbr = wfGetDB( DB_REPLICA );
2904 
2905  if ( $this->getNamespace() == NS_FILE ) {
2906  $tables = [ 'imagelinks', 'page_restrictions' ];
2907  $where_clauses = [
2908  'il_to' => $this->getDBkey(),
2909  'il_from=pr_page',
2910  'pr_cascade' => 1
2911  ];
2912  } else {
2913  $tables = [ 'templatelinks', 'page_restrictions' ];
2914  $where_clauses = [
2915  'tl_namespace' => $this->getNamespace(),
2916  'tl_title' => $this->getDBkey(),
2917  'tl_from=pr_page',
2918  'pr_cascade' => 1
2919  ];
2920  }
2921 
2922  if ( $getPages ) {
2923  $cols = [ 'pr_page', 'page_namespace', 'page_title',
2924  'pr_expiry', 'pr_type', 'pr_level' ];
2925  $where_clauses[] = 'page_id=pr_page';
2926  $tables[] = 'page';
2927  } else {
2928  $cols = [ 'pr_expiry' ];
2929  }
2930 
2931  $res = $dbr->select( $tables, $cols, $where_clauses, __METHOD__ );
2932 
2933  $sources = $getPages ? [] : false;
2934  $now = wfTimestampNow();
2935 
2936  foreach ( $res as $row ) {
2937  $expiry = $dbr->decodeExpiry( $row->pr_expiry );
2938  if ( $expiry > $now ) {
2939  if ( $getPages ) {
2940  $page_id = $row->pr_page;
2941  $page_ns = $row->page_namespace;
2942  $page_title = $row->page_title;
2943  $sources[$page_id] = self::makeTitle( $page_ns, $page_title );
2944  # Add groups needed for each restriction type if its not already there
2945  # Make sure this restriction type still exists
2946 
2947  if ( !isset( $pagerestrictions[$row->pr_type] ) ) {
2948  $pagerestrictions[$row->pr_type] = [];
2949  }
2950 
2951  if (
2952  isset( $pagerestrictions[$row->pr_type] )
2953  && !in_array( $row->pr_level, $pagerestrictions[$row->pr_type] )
2954  ) {
2955  $pagerestrictions[$row->pr_type][] = $row->pr_level;
2956  }
2957  } else {
2958  $sources = true;
2959  }
2960  }
2961  }
2962 
2963  if ( $getPages ) {
2964  $this->mCascadeSources = $sources;
2965  $this->mCascadingRestrictions = $pagerestrictions;
2966  } else {
2967  $this->mHasCascadingRestrictions = $sources;
2968  }
2969 
2970  return [ $sources, $pagerestrictions ];
2971  }
2972 
2980  public function areRestrictionsLoaded() {
2982  }
2983 
2993  public function getRestrictions( $action ) {
2994  if ( !$this->mRestrictionsLoaded ) {
2995  $this->loadRestrictions();
2996  }
2997  return isset( $this->mRestrictions[$action] )
2998  ? $this->mRestrictions[$action]
2999  : [];
3000  }
3001 
3009  public function getAllRestrictions() {
3010  if ( !$this->mRestrictionsLoaded ) {
3011  $this->loadRestrictions();
3012  }
3013  return $this->mRestrictions;
3014  }
3015 
3023  public function getRestrictionExpiry( $action ) {
3024  if ( !$this->mRestrictionsLoaded ) {
3025  $this->loadRestrictions();
3026  }
3027  return isset( $this->mRestrictionsExpiry[$action] ) ? $this->mRestrictionsExpiry[$action] : false;
3028  }
3029 
3036  if ( !$this->mRestrictionsLoaded ) {
3037  $this->loadRestrictions();
3038  }
3039 
3041  }
3042 
3052  public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
3053  $dbr = wfGetDB( DB_REPLICA );
3054 
3055  $restrictionTypes = $this->getRestrictionTypes();
3056 
3057  foreach ( $restrictionTypes as $type ) {
3058  $this->mRestrictions[$type] = [];
3059  $this->mRestrictionsExpiry[$type] = 'infinity';
3060  }
3061 
3062  $this->mCascadeRestriction = false;
3063 
3064  # Backwards-compatibility: also load the restrictions from the page record (old format).
3065  if ( $oldFashionedRestrictions !== null ) {
3066  $this->mOldRestrictions = $oldFashionedRestrictions;
3067  }
3068 
3069  if ( $this->mOldRestrictions === false ) {
3070  $this->mOldRestrictions = $dbr->selectField( 'page', 'page_restrictions',
3071  [ 'page_id' => $this->getArticleID() ], __METHOD__ );
3072  }
3073 
3074  if ( $this->mOldRestrictions != '' ) {
3075  foreach ( explode( ':', trim( $this->mOldRestrictions ) ) as $restrict ) {
3076  $temp = explode( '=', trim( $restrict ) );
3077  if ( count( $temp ) == 1 ) {
3078  // old old format should be treated as edit/move restriction
3079  $this->mRestrictions['edit'] = explode( ',', trim( $temp[0] ) );
3080  $this->mRestrictions['move'] = explode( ',', trim( $temp[0] ) );
3081  } else {
3082  $restriction = trim( $temp[1] );
3083  if ( $restriction != '' ) { // some old entries are empty
3084  $this->mRestrictions[$temp[0]] = explode( ',', $restriction );
3085  }
3086  }
3087  }
3088  }
3089 
3090  if ( count( $rows ) ) {
3091  # Current system - load second to make them override.
3092  $now = wfTimestampNow();
3093 
3094  # Cycle through all the restrictions.
3095  foreach ( $rows as $row ) {
3096  // Don't take care of restrictions types that aren't allowed
3097  if ( !in_array( $row->pr_type, $restrictionTypes ) ) {
3098  continue;
3099  }
3100 
3101  $expiry = $dbr->decodeExpiry( $row->pr_expiry );
3102 
3103  // Only apply the restrictions if they haven't expired!
3104  if ( !$expiry || $expiry > $now ) {
3105  $this->mRestrictionsExpiry[$row->pr_type] = $expiry;
3106  $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) );
3107 
3108  $this->mCascadeRestriction |= $row->pr_cascade;
3109  }
3110  }
3111  }
3112 
3113  $this->mRestrictionsLoaded = true;
3114  }
3115 
3122  public function loadRestrictions( $oldFashionedRestrictions = null ) {
3123  if ( $this->mRestrictionsLoaded ) {
3124  return;
3125  }
3126 
3127  $id = $this->getArticleID();
3128  if ( $id ) {
3130  $rows = $cache->getWithSetCallback(
3131  // Page protections always leave a new null revision
3132  $cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID() ),
3133  $cache::TTL_DAY,
3134  function ( $curValue, &$ttl, array &$setOpts ) {
3135  $dbr = wfGetDB( DB_REPLICA );
3136 
3137  $setOpts += Database::getCacheSetOptions( $dbr );
3138 
3139  return iterator_to_array(
3140  $dbr->select(
3141  'page_restrictions',
3142  [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ],
3143  [ 'pr_page' => $this->getArticleID() ],
3144  __METHOD__
3145  )
3146  );
3147  }
3148  );
3149 
3150  $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
3151  } else {
3152  $title_protection = $this->getTitleProtectionInternal();
3153 
3154  if ( $title_protection ) {
3155  $now = wfTimestampNow();
3156  $expiry = wfGetDB( DB_REPLICA )->decodeExpiry( $title_protection['expiry'] );
3157 
3158  if ( !$expiry || $expiry > $now ) {
3159  // Apply the restrictions
3160  $this->mRestrictionsExpiry['create'] = $expiry;
3161  $this->mRestrictions['create'] =
3162  explode( ',', trim( $title_protection['permission'] ) );
3163  } else { // Get rid of the old restrictions
3164  $this->mTitleProtection = false;
3165  }
3166  } else {
3167  $this->mRestrictionsExpiry['create'] = 'infinity';
3168  }
3169  $this->mRestrictionsLoaded = true;
3170  }
3171  }
3172 
3177  public function flushRestrictions() {
3178  $this->mRestrictionsLoaded = false;
3179  $this->mTitleProtection = null;
3180  }
3181 
3187  static function purgeExpiredRestrictions() {
3188  if ( wfReadOnly() ) {
3189  return;
3190  }
3191 
3193  wfGetDB( DB_MASTER ),
3194  __METHOD__,
3195  function ( IDatabase $dbw, $fname ) {
3196  $config = MediaWikiServices::getInstance()->getMainConfig();
3197  $ids = $dbw->selectFieldValues(
3198  'page_restrictions',
3199  'pr_id',
3200  [ 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ],
3201  $fname,
3202  [ 'LIMIT' => $config->get( 'UpdateRowsPerQuery' ) ] // T135470
3203  );
3204  if ( $ids ) {
3205  $dbw->delete( 'page_restrictions', [ 'pr_id' => $ids ], $fname );
3206  }
3207  }
3208  ) );
3209 
3211  wfGetDB( DB_MASTER ),
3212  __METHOD__,
3213  function ( IDatabase $dbw, $fname ) {
3214  $dbw->delete(
3215  'protected_titles',
3216  [ 'pt_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ],
3217  $fname
3218  );
3219  }
3220  ) );
3221  }
3222 
3228  public function hasSubpages() {
3229  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
3230  # Duh
3231  return false;
3232  }
3233 
3234  # We dynamically add a member variable for the purpose of this method
3235  # alone to cache the result. There's no point in having it hanging
3236  # around uninitialized in every Title object; therefore we only add it
3237  # if needed and don't declare it statically.
3238  if ( $this->mHasSubpages === null ) {
3239  $this->mHasSubpages = false;
3240  $subpages = $this->getSubpages( 1 );
3241  if ( $subpages instanceof TitleArray ) {
3242  $this->mHasSubpages = (bool)$subpages->count();
3243  }
3244  }
3245 
3246  return $this->mHasSubpages;
3247  }
3248 
3256  public function getSubpages( $limit = -1 ) {
3257  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
3258  return [];
3259  }
3260 
3261  $dbr = wfGetDB( DB_REPLICA );
3262  $conds['page_namespace'] = $this->getNamespace();
3263  $conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() );
3264  $options = [];
3265  if ( $limit > -1 ) {
3266  $options['LIMIT'] = $limit;
3267  }
3269  $dbr->select( 'page',
3270  [ 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ],
3271  $conds,
3272  __METHOD__,
3273  $options
3274  )
3275  );
3276  }
3277 
3283  public function isDeleted() {
3284  if ( $this->getNamespace() < 0 ) {
3285  $n = 0;
3286  } else {
3287  $dbr = wfGetDB( DB_REPLICA );
3288 
3289  $n = $dbr->selectField( 'archive', 'COUNT(*)',
3290  [ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
3291  __METHOD__
3292  );
3293  if ( $this->getNamespace() == NS_FILE ) {
3294  $n += $dbr->selectField( 'filearchive', 'COUNT(*)',
3295  [ 'fa_name' => $this->getDBkey() ],
3296  __METHOD__
3297  );
3298  }
3299  }
3300  return (int)$n;
3301  }
3302 
3308  public function isDeletedQuick() {
3309  if ( $this->getNamespace() < 0 ) {
3310  return false;
3311  }
3312  $dbr = wfGetDB( DB_REPLICA );
3313  $deleted = (bool)$dbr->selectField( 'archive', '1',
3314  [ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
3315  __METHOD__
3316  );
3317  if ( !$deleted && $this->getNamespace() == NS_FILE ) {
3318  $deleted = (bool)$dbr->selectField( 'filearchive', '1',
3319  [ 'fa_name' => $this->getDBkey() ],
3320  __METHOD__
3321  );
3322  }
3323  return $deleted;
3324  }
3325 
3334  public function getArticleID( $flags = 0 ) {
3335  if ( $this->getNamespace() < 0 ) {
3336  $this->mArticleID = 0;
3337  return $this->mArticleID;
3338  }
3339  $linkCache = LinkCache::singleton();
3340  if ( $flags & self::GAID_FOR_UPDATE ) {
3341  $oldUpdate = $linkCache->forUpdate( true );
3342  $linkCache->clearLink( $this );
3343  $this->mArticleID = $linkCache->addLinkObj( $this );
3344  $linkCache->forUpdate( $oldUpdate );
3345  } else {
3346  if ( -1 == $this->mArticleID ) {
3347  $this->mArticleID = $linkCache->addLinkObj( $this );
3348  }
3349  }
3350  return $this->mArticleID;
3351  }
3352 
3360  public function isRedirect( $flags = 0 ) {
3361  if ( !is_null( $this->mRedirect ) ) {
3362  return $this->mRedirect;
3363  }
3364  if ( !$this->getArticleID( $flags ) ) {
3365  $this->mRedirect = false;
3366  return $this->mRedirect;
3367  }
3368 
3369  $linkCache = LinkCache::singleton();
3370  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3371  $cached = $linkCache->getGoodLinkFieldObj( $this, 'redirect' );
3372  if ( $cached === null ) {
3373  # Trust LinkCache's state over our own
3374  # LinkCache is telling us that the page doesn't exist, despite there being cached
3375  # data relating to an existing page in $this->mArticleID. Updaters should clear
3376  # LinkCache as appropriate, or use $flags = Title::GAID_FOR_UPDATE. If that flag is
3377  # set, then LinkCache will definitely be up to date here, since getArticleID() forces
3378  # LinkCache to refresh its data from the master.
3379  $this->mRedirect = false;
3380  return $this->mRedirect;
3381  }
3382 
3383  $this->mRedirect = (bool)$cached;
3384 
3385  return $this->mRedirect;
3386  }
3387 
3395  public function getLength( $flags = 0 ) {
3396  if ( $this->mLength != -1 ) {
3397  return $this->mLength;
3398  }
3399  if ( !$this->getArticleID( $flags ) ) {
3400  $this->mLength = 0;
3401  return $this->mLength;
3402  }
3403  $linkCache = LinkCache::singleton();
3404  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3405  $cached = $linkCache->getGoodLinkFieldObj( $this, 'length' );
3406  if ( $cached === null ) {
3407  # Trust LinkCache's state over our own, as for isRedirect()
3408  $this->mLength = 0;
3409  return $this->mLength;
3410  }
3411 
3412  $this->mLength = intval( $cached );
3413 
3414  return $this->mLength;
3415  }
3416 
3423  public function getLatestRevID( $flags = 0 ) {
3424  if ( !( $flags & self::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
3425  return intval( $this->mLatestID );
3426  }
3427  if ( !$this->getArticleID( $flags ) ) {
3428  $this->mLatestID = 0;
3429  return $this->mLatestID;
3430  }
3431  $linkCache = LinkCache::singleton();
3432  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3433  $cached = $linkCache->getGoodLinkFieldObj( $this, 'revision' );
3434  if ( $cached === null ) {
3435  # Trust LinkCache's state over our own, as for isRedirect()
3436  $this->mLatestID = 0;
3437  return $this->mLatestID;
3438  }
3439 
3440  $this->mLatestID = intval( $cached );
3441 
3442  return $this->mLatestID;
3443  }
3444 
3455  public function resetArticleID( $newid ) {
3456  $linkCache = LinkCache::singleton();
3457  $linkCache->clearLink( $this );
3458 
3459  if ( $newid === false ) {
3460  $this->mArticleID = -1;
3461  } else {
3462  $this->mArticleID = intval( $newid );
3463  }
3464  $this->mRestrictionsLoaded = false;
3465  $this->mRestrictions = [];
3466  $this->mOldRestrictions = false;
3467  $this->mRedirect = null;
3468  $this->mLength = -1;
3469  $this->mLatestID = false;
3470  $this->mContentModel = false;
3471  $this->mEstimateRevisions = null;
3472  $this->mPageLanguage = false;
3473  $this->mDbPageLanguage = false;
3474  $this->mIsBigDeletion = null;
3475  }
3476 
3477  public static function clearCaches() {
3478  $linkCache = LinkCache::singleton();
3479  $linkCache->clear();
3480 
3482  $titleCache->clear();
3483  }
3484 
3492  public static function capitalize( $text, $ns = NS_MAIN ) {
3494 
3495  if ( MWNamespace::isCapitalized( $ns ) ) {
3496  return $wgContLang->ucfirst( $text );
3497  } else {
3498  return $text;
3499  }
3500  }
3501 
3514  private function secureAndSplit() {
3515  # Initialisation
3516  $this->mInterwiki = '';
3517  $this->mFragment = '';
3518  $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
3519 
3520  $dbkey = $this->mDbkeyform;
3521 
3522  // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
3523  // the parsing code with Title, while avoiding massive refactoring.
3524  // @todo: get rid of secureAndSplit, refactor parsing code.
3525  // @note: getTitleParser() returns a TitleParser implementation which does not have a
3526  // splitTitleString method, but the only implementation (MediaWikiTitleCodec) does
3527  $titleCodec = MediaWikiServices::getInstance()->getTitleParser();
3528  // MalformedTitleException can be thrown here
3529  $parts = $titleCodec->splitTitleString( $dbkey, $this->getDefaultNamespace() );
3530 
3531  # Fill fields
3532  $this->setFragment( '#' . $parts['fragment'] );
3533  $this->mInterwiki = $parts['interwiki'];
3534  $this->mLocalInterwiki = $parts['local_interwiki'];
3535  $this->mNamespace = $parts['namespace'];
3536  $this->mUserCaseDBKey = $parts['user_case_dbkey'];
3537 
3538  $this->mDbkeyform = $parts['dbkey'];
3539  $this->mUrlform = wfUrlencode( $this->mDbkeyform );
3540  $this->mTextform = strtr( $this->mDbkeyform, '_', ' ' );
3541 
3542  # We already know that some pages won't be in the database!
3543  if ( $this->isExternal() || $this->isSpecialPage() ) {
3544  $this->mArticleID = 0;
3545  }
3546 
3547  return true;
3548  }
3549 
3562  public function getLinksTo( $options = [], $table = 'pagelinks', $prefix = 'pl' ) {
3563  if ( count( $options ) > 0 ) {
3564  $db = wfGetDB( DB_MASTER );
3565  } else {
3566  $db = wfGetDB( DB_REPLICA );
3567  }
3568 
3569  $res = $db->select(
3570  [ 'page', $table ],
3571  self::getSelectFields(),
3572  [
3573  "{$prefix}_from=page_id",
3574  "{$prefix}_namespace" => $this->getNamespace(),
3575  "{$prefix}_title" => $this->getDBkey() ],
3576  __METHOD__,
3577  $options
3578  );
3579 
3580  $retVal = [];
3581  if ( $res->numRows() ) {
3582  $linkCache = LinkCache::singleton();
3583  foreach ( $res as $row ) {
3584  $titleObj = self::makeTitle( $row->page_namespace, $row->page_title );
3585  if ( $titleObj ) {
3586  $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
3587  $retVal[] = $titleObj;
3588  }
3589  }
3590  }
3591  return $retVal;
3592  }
3593 
3604  public function getTemplateLinksTo( $options = [] ) {
3605  return $this->getLinksTo( $options, 'templatelinks', 'tl' );
3606  }
3607 
3620  public function getLinksFrom( $options = [], $table = 'pagelinks', $prefix = 'pl' ) {
3621  $id = $this->getArticleID();
3622 
3623  # If the page doesn't exist; there can't be any link from this page
3624  if ( !$id ) {
3625  return [];
3626  }
3627 
3628  $db = wfGetDB( DB_REPLICA );
3629 
3630  $blNamespace = "{$prefix}_namespace";
3631  $blTitle = "{$prefix}_title";
3632 
3633  $res = $db->select(
3634  [ $table, 'page' ],
3635  array_merge(
3636  [ $blNamespace, $blTitle ],
3638  ),
3639  [ "{$prefix}_from" => $id ],
3640  __METHOD__,
3641  $options,
3642  [ 'page' => [
3643  'LEFT JOIN',
3644  [ "page_namespace=$blNamespace", "page_title=$blTitle" ]
3645  ] ]
3646  );
3647 
3648  $retVal = [];
3649  $linkCache = LinkCache::singleton();
3650  foreach ( $res as $row ) {
3651  if ( $row->page_id ) {
3652  $titleObj = self::newFromRow( $row );
3653  } else {
3654  $titleObj = self::makeTitle( $row->$blNamespace, $row->$blTitle );
3655  $linkCache->addBadLinkObj( $titleObj );
3656  }
3657  $retVal[] = $titleObj;
3658  }
3659 
3660  return $retVal;
3661  }
3662 
3673  public function getTemplateLinksFrom( $options = [] ) {
3674  return $this->getLinksFrom( $options, 'templatelinks', 'tl' );
3675  }
3676 
3685  public function getBrokenLinksFrom() {
3686  if ( $this->getArticleID() == 0 ) {
3687  # All links from article ID 0 are false positives
3688  return [];
3689  }
3690 
3691  $dbr = wfGetDB( DB_REPLICA );
3692  $res = $dbr->select(
3693  [ 'page', 'pagelinks' ],
3694  [ 'pl_namespace', 'pl_title' ],
3695  [
3696  'pl_from' => $this->getArticleID(),
3697  'page_namespace IS NULL'
3698  ],
3699  __METHOD__, [],
3700  [
3701  'page' => [
3702  'LEFT JOIN',
3703  [ 'pl_namespace=page_namespace', 'pl_title=page_title' ]
3704  ]
3705  ]
3706  );
3707 
3708  $retVal = [];
3709  foreach ( $res as $row ) {
3710  $retVal[] = self::makeTitle( $row->pl_namespace, $row->pl_title );
3711  }
3712  return $retVal;
3713  }
3714 
3721  public function getCdnUrls() {
3722  $urls = [
3723  $this->getInternalURL(),
3724  $this->getInternalURL( 'action=history' )
3725  ];
3726 
3727  $pageLang = $this->getPageLanguage();
3728  if ( $pageLang->hasVariants() ) {
3729  $variants = $pageLang->getVariants();
3730  foreach ( $variants as $vCode ) {
3731  $urls[] = $this->getInternalURL( $vCode );
3732  }
3733  }
3734 
3735  // If we are looking at a css/js user subpage, purge the action=raw.
3736  if ( $this->isJsSubpage() ) {
3737  $urls[] = $this->getInternalURL( 'action=raw&ctype=text/javascript' );
3738  } elseif ( $this->isCssSubpage() ) {
3739  $urls[] = $this->getInternalURL( 'action=raw&ctype=text/css' );
3740  }
3741 
3742  Hooks::run( 'TitleSquidURLs', [ $this, &$urls ] );
3743  return $urls;
3744  }
3745 
3749  public function getSquidURLs() {
3750  return $this->getCdnUrls();
3751  }
3752 
3756  public function purgeSquid() {
3758  new CdnCacheUpdate( $this->getCdnUrls() ),
3760  );
3761  }
3762 
3773  public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) {
3774  global $wgUser;
3775 
3776  if ( !( $nt instanceof Title ) ) {
3777  // Normally we'd add this to $errors, but we'll get
3778  // lots of syntax errors if $nt is not an object
3779  return [ [ 'badtitletext' ] ];
3780  }
3781 
3782  $mp = new MovePage( $this, $nt );
3783  $errors = $mp->isValidMove()->getErrorsArray();
3784  if ( $auth ) {
3785  $errors = wfMergeErrorArrays(
3786  $errors,
3787  $mp->checkPermissions( $wgUser, $reason )->getErrorsArray()
3788  );
3789  }
3790 
3791  return $errors ?: true;
3792  }
3793 
3800  protected function validateFileMoveOperation( $nt ) {
3801  global $wgUser;
3802 
3803  $errors = [];
3804 
3805  $destFile = wfLocalFile( $nt );
3806  $destFile->load( File::READ_LATEST );
3807  if ( !$wgUser->isAllowed( 'reupload-shared' )
3808  && !$destFile->exists() && wfFindFile( $nt )
3809  ) {
3810  $errors[] = [ 'file-exists-sharedrepo' ];
3811  }
3812 
3813  return $errors;
3814  }
3815 
3829  public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true,
3830  array $changeTags = []
3831  ) {
3832  global $wgUser;
3833  $err = $this->isValidMoveOperation( $nt, $auth, $reason );
3834  if ( is_array( $err ) ) {
3835  // Auto-block user's IP if the account was "hard" blocked
3836  $wgUser->spreadAnyEditBlock();
3837  return $err;
3838  }
3839  // Check suppressredirect permission
3840  if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
3841  $createRedirect = true;
3842  }
3843 
3844  $mp = new MovePage( $this, $nt );
3845  $status = $mp->move( $wgUser, $reason, $createRedirect, $changeTags );
3846  if ( $status->isOK() ) {
3847  return true;
3848  } else {
3849  return $status->getErrorsArray();
3850  }
3851  }
3852 
3867  public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true,
3868  array $changeTags = []
3869  ) {
3871  // Check permissions
3872  if ( !$this->userCan( 'move-subpages' ) ) {
3873  return [
3874  [ 'cant-move-subpages' ],
3875  ];
3876  }
3877  // Do the source and target namespaces support subpages?
3878  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
3879  return [
3880  [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $this->getNamespace() ) ],
3881  ];
3882  }
3883  if ( !MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
3884  return [
3885  [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $nt->getNamespace() ) ],
3886  ];
3887  }
3888 
3889  $subpages = $this->getSubpages( $wgMaximumMovedPages + 1 );
3890  $retval = [];
3891  $count = 0;
3892  foreach ( $subpages as $oldSubpage ) {
3893  $count++;
3894  if ( $count > $wgMaximumMovedPages ) {
3895  $retval[$oldSubpage->getPrefixedText()] = [
3896  [ 'movepage-max-pages', $wgMaximumMovedPages ],
3897  ];
3898  break;
3899  }
3900 
3901  // We don't know whether this function was called before
3902  // or after moving the root page, so check both
3903  // $this and $nt
3904  if ( $oldSubpage->getArticleID() == $this->getArticleID()
3905  || $oldSubpage->getArticleID() == $nt->getArticleID()
3906  ) {
3907  // When moving a page to a subpage of itself,
3908  // don't move it twice
3909  continue;
3910  }
3911  $newPageName = preg_replace(
3912  '#^' . preg_quote( $this->getDBkey(), '#' ) . '#',
3913  StringUtils::escapeRegexReplacement( $nt->getDBkey() ), # T23234
3914  $oldSubpage->getDBkey() );
3915  if ( $oldSubpage->isTalkPage() ) {
3916  $newNs = $nt->getTalkPage()->getNamespace();
3917  } else {
3918  $newNs = $nt->getSubjectPage()->getNamespace();
3919  }
3920  # T16385: we need makeTitleSafe because the new page names may
3921  # be longer than 255 characters.
3922  $newSubpage = self::makeTitleSafe( $newNs, $newPageName );
3923 
3924  $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect, $changeTags );
3925  if ( $success === true ) {
3926  $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
3927  } else {
3928  $retval[$oldSubpage->getPrefixedText()] = $success;
3929  }
3930  }
3931  return $retval;
3932  }
3933 
3940  public function isSingleRevRedirect() {
3942 
3943  $dbw = wfGetDB( DB_MASTER );
3944 
3945  # Is it a redirect?
3946  $fields = [ 'page_is_redirect', 'page_latest', 'page_id' ];
3947  if ( $wgContentHandlerUseDB ) {
3948  $fields[] = 'page_content_model';
3949  }
3950 
3951  $row = $dbw->selectRow( 'page',
3952  $fields,
3953  $this->pageCond(),
3954  __METHOD__,
3955  [ 'FOR UPDATE' ]
3956  );
3957  # Cache some fields we may want
3958  $this->mArticleID = $row ? intval( $row->page_id ) : 0;
3959  $this->mRedirect = $row ? (bool)$row->page_is_redirect : false;
3960  $this->mLatestID = $row ? intval( $row->page_latest ) : false;
3961  $this->mContentModel = $row && isset( $row->page_content_model )
3962  ? strval( $row->page_content_model )
3963  : false;
3964 
3965  if ( !$this->mRedirect ) {
3966  return false;
3967  }
3968  # Does the article have a history?
3969  $row = $dbw->selectField( [ 'page', 'revision' ],
3970  'rev_id',
3971  [ 'page_namespace' => $this->getNamespace(),
3972  'page_title' => $this->getDBkey(),
3973  'page_id=rev_page',
3974  'page_latest != rev_id'
3975  ],
3976  __METHOD__,
3977  [ 'FOR UPDATE' ]
3978  );
3979  # Return true if there was no history
3980  return ( $row === false );
3981  }
3982 
3991  public function isValidMoveTarget( $nt ) {
3992  # Is it an existing file?
3993  if ( $nt->getNamespace() == NS_FILE ) {
3994  $file = wfLocalFile( $nt );
3995  $file->load( File::READ_LATEST );
3996  if ( $file->exists() ) {
3997  wfDebug( __METHOD__ . ": file exists\n" );
3998  return false;
3999  }
4000  }
4001  # Is it a redirect with no history?
4002  if ( !$nt->isSingleRevRedirect() ) {
4003  wfDebug( __METHOD__ . ": not a one-rev redirect\n" );
4004  return false;
4005  }
4006  # Get the article text
4007  $rev = Revision::newFromTitle( $nt, false, Revision::READ_LATEST );
4008  if ( !is_object( $rev ) ) {
4009  return false;
4010  }
4011  $content = $rev->getContent();
4012  # Does the redirect point to the source?
4013  # Or is it a broken self-redirect, usually caused by namespace collisions?
4014  $redirTitle = $content ? $content->getRedirectTarget() : null;
4015 
4016  if ( $redirTitle ) {
4017  if ( $redirTitle->getPrefixedDBkey() != $this->getPrefixedDBkey() &&
4018  $redirTitle->getPrefixedDBkey() != $nt->getPrefixedDBkey() ) {
4019  wfDebug( __METHOD__ . ": redirect points to other page\n" );
4020  return false;
4021  } else {
4022  return true;
4023  }
4024  } else {
4025  # Fail safe (not a redirect after all. strange.)
4026  wfDebug( __METHOD__ . ": failsafe: database sais " . $nt->getPrefixedDBkey() .
4027  " is a redirect, but it doesn't contain a valid redirect.\n" );
4028  return false;
4029  }
4030  }
4031 
4039  public function getParentCategories() {
4041 
4042  $data = [];
4043 
4044  $titleKey = $this->getArticleID();
4045 
4046  if ( $titleKey === 0 ) {
4047  return $data;
4048  }
4049 
4050  $dbr = wfGetDB( DB_REPLICA );
4051 
4052  $res = $dbr->select(
4053  'categorylinks',
4054  'cl_to',
4055  [ 'cl_from' => $titleKey ],
4056  __METHOD__
4057  );
4058 
4059  if ( $res->numRows() > 0 ) {
4060  foreach ( $res as $row ) {
4061  // $data[] = Title::newFromText($wgContLang->getNsText ( NS_CATEGORY ).':'.$row->cl_to);
4062  $data[$wgContLang->getNsText( NS_CATEGORY ) . ':' . $row->cl_to] = $this->getFullText();
4063  }
4064  }
4065  return $data;
4066  }
4067 
4074  public function getParentCategoryTree( $children = [] ) {
4075  $stack = [];
4076  $parents = $this->getParentCategories();
4077 
4078  if ( $parents ) {
4079  foreach ( $parents as $parent => $current ) {
4080  if ( array_key_exists( $parent, $children ) ) {
4081  # Circular reference
4082  $stack[$parent] = [];
4083  } else {
4084  $nt = self::newFromText( $parent );
4085  if ( $nt ) {
4086  $stack[$parent] = $nt->getParentCategoryTree( $children + [ $parent => 1 ] );
4087  }
4088  }
4089  }
4090  }
4091 
4092  return $stack;
4093  }
4094 
4101  public function pageCond() {
4102  if ( $this->mArticleID > 0 ) {
4103  // PK avoids secondary lookups in InnoDB, shouldn't hurt other DBs
4104  return [ 'page_id' => $this->mArticleID ];
4105  } else {
4106  return [ 'page_namespace' => $this->mNamespace, 'page_title' => $this->mDbkeyform ];
4107  }
4108  }
4109 
4117  private function getRelativeRevisionID( $revId, $flags, $dir ) {
4118  $revId = (int)$revId;
4119  if ( $dir === 'next' ) {
4120  $op = '>';
4121  $sort = 'ASC';
4122  } elseif ( $dir === 'prev' ) {
4123  $op = '<';
4124  $sort = 'DESC';
4125  } else {
4126  throw new InvalidArgumentException( '$dir must be "next" or "prev"' );
4127  }
4128 
4129  if ( $flags & self::GAID_FOR_UPDATE ) {
4130  $db = wfGetDB( DB_MASTER );
4131  } else {
4132  $db = wfGetDB( DB_REPLICA, 'contributions' );
4133  }
4134 
4135  // Intentionally not caring if the specified revision belongs to this
4136  // page. We only care about the timestamp.
4137  $ts = $db->selectField( 'revision', 'rev_timestamp', [ 'rev_id' => $revId ], __METHOD__ );
4138  if ( $ts === false ) {
4139  $ts = $db->selectField( 'archive', 'ar_timestamp', [ 'ar_rev_id' => $revId ], __METHOD__ );
4140  if ( $ts === false ) {
4141  // Or should this throw an InvalidArgumentException or something?
4142  return false;
4143  }
4144  }
4145  $ts = $db->addQuotes( $ts );
4146 
4147  $revId = $db->selectField( 'revision', 'rev_id',
4148  [
4149  'rev_page' => $this->getArticleID( $flags ),
4150  "rev_timestamp $op $ts OR (rev_timestamp = $ts AND rev_id $op $revId)"
4151  ],
4152  __METHOD__,
4153  [
4154  'ORDER BY' => "rev_timestamp $sort, rev_id $sort",
4155  'IGNORE INDEX' => 'rev_timestamp', // Probably needed for T159319
4156  ]
4157  );
4158 
4159  if ( $revId === false ) {
4160  return false;
4161  } else {
4162  return intval( $revId );
4163  }
4164  }
4165 
4173  public function getPreviousRevisionID( $revId, $flags = 0 ) {
4174  return $this->getRelativeRevisionID( $revId, $flags, 'prev' );
4175  }
4176 
4184  public function getNextRevisionID( $revId, $flags = 0 ) {
4185  return $this->getRelativeRevisionID( $revId, $flags, 'next' );
4186  }
4187 
4194  public function getFirstRevision( $flags = 0 ) {
4195  $pageId = $this->getArticleID( $flags );
4196  if ( $pageId ) {
4198  $row = $db->selectRow( 'revision', Revision::selectFields(),
4199  [ 'rev_page' => $pageId ],
4200  __METHOD__,
4201  [
4202  'ORDER BY' => 'rev_timestamp ASC, rev_id ASC',
4203  'IGNORE INDEX' => 'rev_timestamp', // See T159319
4204  ]
4205  );
4206  if ( $row ) {
4207  return new Revision( $row );
4208  }
4209  }
4210  return null;
4211  }
4212 
4219  public function getEarliestRevTime( $flags = 0 ) {
4220  $rev = $this->getFirstRevision( $flags );
4221  return $rev ? $rev->getTimestamp() : null;
4222  }
4223 
4229  public function isNewPage() {
4230  $dbr = wfGetDB( DB_REPLICA );
4231  return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
4232  }
4233 
4239  public function isBigDeletion() {
4241 
4242  if ( !$wgDeleteRevisionsLimit ) {
4243  return false;
4244  }
4245 
4246  if ( $this->mIsBigDeletion === null ) {
4247  $dbr = wfGetDB( DB_REPLICA );
4248 
4249  $revCount = $dbr->selectRowCount(
4250  'revision',
4251  '1',
4252  [ 'rev_page' => $this->getArticleID() ],
4253  __METHOD__,
4254  [ 'LIMIT' => $wgDeleteRevisionsLimit + 1 ]
4255  );
4256 
4257  $this->mIsBigDeletion = $revCount > $wgDeleteRevisionsLimit;
4258  }
4259 
4260  return $this->mIsBigDeletion;
4261  }
4262 
4268  public function estimateRevisionCount() {
4269  if ( !$this->exists() ) {
4270  return 0;
4271  }
4272 
4273  if ( $this->mEstimateRevisions === null ) {
4274  $dbr = wfGetDB( DB_REPLICA );
4275  $this->mEstimateRevisions = $dbr->estimateRowCount( 'revision', '*',
4276  [ 'rev_page' => $this->getArticleID() ], __METHOD__ );
4277  }
4278 
4280  }
4281 
4291  public function countRevisionsBetween( $old, $new, $max = null ) {
4292  if ( !( $old instanceof Revision ) ) {
4293  $old = Revision::newFromTitle( $this, (int)$old );
4294  }
4295  if ( !( $new instanceof Revision ) ) {
4296  $new = Revision::newFromTitle( $this, (int)$new );
4297  }
4298  if ( !$old || !$new ) {
4299  return 0; // nothing to compare
4300  }
4301  $dbr = wfGetDB( DB_REPLICA );
4302  $conds = [
4303  'rev_page' => $this->getArticleID(),
4304  'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4305  'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4306  ];
4307  if ( $max !== null ) {
4308  return $dbr->selectRowCount( 'revision', '1',
4309  $conds,
4310  __METHOD__,
4311  [ 'LIMIT' => $max + 1 ] // extra to detect truncation
4312  );
4313  } else {
4314  return (int)$dbr->selectField( 'revision', 'count(*)', $conds, __METHOD__ );
4315  }
4316  }
4317 
4334  public function getAuthorsBetween( $old, $new, $limit, $options = [] ) {
4335  if ( !( $old instanceof Revision ) ) {
4336  $old = Revision::newFromTitle( $this, (int)$old );
4337  }
4338  if ( !( $new instanceof Revision ) ) {
4339  $new = Revision::newFromTitle( $this, (int)$new );
4340  }
4341  // XXX: what if Revision objects are passed in, but they don't refer to this title?
4342  // Add $old->getPage() != $new->getPage() || $old->getPage() != $this->getArticleID()
4343  // in the sanity check below?
4344  if ( !$old || !$new ) {
4345  return null; // nothing to compare
4346  }
4347  $authors = [];
4348  $old_cmp = '>';
4349  $new_cmp = '<';
4350  $options = (array)$options;
4351  if ( in_array( 'include_old', $options ) ) {
4352  $old_cmp = '>=';
4353  }
4354  if ( in_array( 'include_new', $options ) ) {
4355  $new_cmp = '<=';
4356  }
4357  if ( in_array( 'include_both', $options ) ) {
4358  $old_cmp = '>=';
4359  $new_cmp = '<=';
4360  }
4361  // No DB query needed if $old and $new are the same or successive revisions:
4362  if ( $old->getId() === $new->getId() ) {
4363  return ( $old_cmp === '>' && $new_cmp === '<' ) ?
4364  [] :
4365  [ $old->getUserText( Revision::RAW ) ];
4366  } elseif ( $old->getId() === $new->getParentId() ) {
4367  if ( $old_cmp === '>=' && $new_cmp === '<=' ) {
4368  $authors[] = $old->getUserText( Revision::RAW );
4369  if ( $old->getUserText( Revision::RAW ) != $new->getUserText( Revision::RAW ) ) {
4370  $authors[] = $new->getUserText( Revision::RAW );
4371  }
4372  } elseif ( $old_cmp === '>=' ) {
4373  $authors[] = $old->getUserText( Revision::RAW );
4374  } elseif ( $new_cmp === '<=' ) {
4375  $authors[] = $new->getUserText( Revision::RAW );
4376  }
4377  return $authors;
4378  }
4379  $dbr = wfGetDB( DB_REPLICA );
4380  $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
4381  [
4382  'rev_page' => $this->getArticleID(),
4383  "rev_timestamp $old_cmp " . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4384  "rev_timestamp $new_cmp " . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4385  ], __METHOD__,
4386  [ 'LIMIT' => $limit + 1 ] // add one so caller knows it was truncated
4387  );
4388  foreach ( $res as $row ) {
4389  $authors[] = $row->rev_user_text;
4390  }
4391  return $authors;
4392  }
4393 
4408  public function countAuthorsBetween( $old, $new, $limit, $options = [] ) {
4409  $authors = $this->getAuthorsBetween( $old, $new, $limit, $options );
4410  return $authors ? count( $authors ) : 0;
4411  }
4412 
4419  public function equals( Title $title ) {
4420  // Note: === is necessary for proper matching of number-like titles.
4421  return $this->getInterwiki() === $title->getInterwiki()
4422  && $this->getNamespace() == $title->getNamespace()
4423  && $this->getDBkey() === $title->getDBkey();
4424  }
4425 
4432  public function isSubpageOf( Title $title ) {
4433  return $this->getInterwiki() === $title->getInterwiki()
4434  && $this->getNamespace() == $title->getNamespace()
4435  && strpos( $this->getDBkey(), $title->getDBkey() . '/' ) === 0;
4436  }
4437 
4449  public function exists( $flags = 0 ) {
4450  $exists = $this->getArticleID( $flags ) != 0;
4451  Hooks::run( 'TitleExists', [ $this, &$exists ] );
4452  return $exists;
4453  }
4454 
4471  public function isAlwaysKnown() {
4472  $isKnown = null;
4473 
4484  Hooks::run( 'TitleIsAlwaysKnown', [ $this, &$isKnown ] );
4485 
4486  if ( !is_null( $isKnown ) ) {
4487  return $isKnown;
4488  }
4489 
4490  if ( $this->isExternal() ) {
4491  return true; // any interwiki link might be viewable, for all we know
4492  }
4493 
4494  switch ( $this->mNamespace ) {
4495  case NS_MEDIA:
4496  case NS_FILE:
4497  // file exists, possibly in a foreign repo
4498  return (bool)wfFindFile( $this );
4499  case NS_SPECIAL:
4500  // valid special page
4501  return SpecialPageFactory::exists( $this->getDBkey() );
4502  case NS_MAIN:
4503  // selflink, possibly with fragment
4504  return $this->mDbkeyform == '';
4505  case NS_MEDIAWIKI:
4506  // known system message
4507  return $this->hasSourceText() !== false;
4508  default:
4509  return false;
4510  }
4511  }
4512 
4524  public function isKnown() {
4525  return $this->isAlwaysKnown() || $this->exists();
4526  }
4527 
4533  public function hasSourceText() {
4534  if ( $this->exists() ) {
4535  return true;
4536  }
4537 
4538  if ( $this->mNamespace == NS_MEDIAWIKI ) {
4539  // If the page doesn't exist but is a known system message, default
4540  // message content will be displayed, same for language subpages-
4541  // Use always content language to avoid loading hundreds of languages
4542  // to get the link color.
4544  list( $name, ) = MessageCache::singleton()->figureMessage(
4545  $wgContLang->lcfirst( $this->getText() )
4546  );
4547  $message = wfMessage( $name )->inLanguage( $wgContLang )->useDatabase( false );
4548  return $message->exists();
4549  }
4550 
4551  return false;
4552  }
4553 
4559  public function getDefaultMessageText() {
4561 
4562  if ( $this->getNamespace() != NS_MEDIAWIKI ) { // Just in case
4563  return false;
4564  }
4565 
4566  list( $name, $lang ) = MessageCache::singleton()->figureMessage(
4567  $wgContLang->lcfirst( $this->getText() )
4568  );
4569  $message = wfMessage( $name )->inLanguage( $lang )->useDatabase( false );
4570 
4571  if ( $message->exists() ) {
4572  return $message->plain();
4573  } else {
4574  return false;
4575  }
4576  }
4577 
4584  public function invalidateCache( $purgeTime = null ) {
4585  if ( wfReadOnly() ) {
4586  return false;
4587  } elseif ( $this->mArticleID === 0 ) {
4588  return true; // avoid gap locking if we know it's not there
4589  }
4590 
4591  $dbw = wfGetDB( DB_MASTER );
4592  $dbw->onTransactionPreCommitOrIdle( function () {
4594  } );
4595 
4596  $conds = $this->pageCond();
4598  new AutoCommitUpdate(
4599  $dbw,
4600  __METHOD__,
4601  function ( IDatabase $dbw, $fname ) use ( $conds, $purgeTime ) {
4602  $dbTimestamp = $dbw->timestamp( $purgeTime ?: time() );
4603  $dbw->update(
4604  'page',
4605  [ 'page_touched' => $dbTimestamp ],
4606  $conds + [ 'page_touched < ' . $dbw->addQuotes( $dbTimestamp ) ],
4607  $fname
4608  );
4609  MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $this );
4610  }
4611  ),
4613  );
4614 
4615  return true;
4616  }
4617 
4623  public function touchLinks() {
4624  DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this, 'pagelinks' ) );
4625  if ( $this->getNamespace() == NS_CATEGORY ) {
4626  DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this, 'categorylinks' ) );
4627  }
4628  }
4629 
4636  public function getTouched( $db = null ) {
4637  if ( $db === null ) {
4638  $db = wfGetDB( DB_REPLICA );
4639  }
4640  $touched = $db->selectField( 'page', 'page_touched', $this->pageCond(), __METHOD__ );
4641  return $touched;
4642  }
4643 
4650  public function getNotificationTimestamp( $user = null ) {
4651  global $wgUser;
4652 
4653  // Assume current user if none given
4654  if ( !$user ) {
4655  $user = $wgUser;
4656  }
4657  // Check cache first
4658  $uid = $user->getId();
4659  if ( !$uid ) {
4660  return false;
4661  }
4662  // avoid isset here, as it'll return false for null entries
4663  if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) {
4664  return $this->mNotificationTimestamp[$uid];
4665  }
4666  // Don't cache too much!
4667  if ( count( $this->mNotificationTimestamp ) >= self::CACHE_MAX ) {
4668  $this->mNotificationTimestamp = [];
4669  }
4670 
4671  $store = MediaWikiServices::getInstance()->getWatchedItemStore();
4672  $watchedItem = $store->getWatchedItem( $user, $this );
4673  if ( $watchedItem ) {
4674  $this->mNotificationTimestamp[$uid] = $watchedItem->getNotificationTimestamp();
4675  } else {
4676  $this->mNotificationTimestamp[$uid] = false;
4677  }
4678 
4679  return $this->mNotificationTimestamp[$uid];
4680  }
4681 
4688  public function getNamespaceKey( $prepend = 'nstab-' ) {
4690  // Gets the subject namespace if this title
4691  $namespace = MWNamespace::getSubject( $this->getNamespace() );
4692  // Checks if canonical namespace name exists for namespace
4693  if ( MWNamespace::exists( $this->getNamespace() ) ) {
4694  // Uses canonical namespace name
4695  $namespaceKey = MWNamespace::getCanonicalName( $namespace );
4696  } else {
4697  // Uses text of namespace
4698  $namespaceKey = $this->getSubjectNsText();
4699  }
4700  // Makes namespace key lowercase
4701  $namespaceKey = $wgContLang->lc( $namespaceKey );
4702  // Uses main
4703  if ( $namespaceKey == '' ) {
4704  $namespaceKey = 'main';
4705  }
4706  // Changes file to image for backwards compatibility
4707  if ( $namespaceKey == 'file' ) {
4708  $namespaceKey = 'image';
4709  }
4710  return $prepend . $namespaceKey;
4711  }
4712 
4719  public function getRedirectsHere( $ns = null ) {
4720  $redirs = [];
4721 
4722  $dbr = wfGetDB( DB_REPLICA );
4723  $where = [
4724  'rd_namespace' => $this->getNamespace(),
4725  'rd_title' => $this->getDBkey(),
4726  'rd_from = page_id'
4727  ];
4728  if ( $this->isExternal() ) {
4729  $where['rd_interwiki'] = $this->getInterwiki();
4730  } else {
4731  $where[] = 'rd_interwiki = ' . $dbr->addQuotes( '' ) . ' OR rd_interwiki IS NULL';
4732  }
4733  if ( !is_null( $ns ) ) {
4734  $where['page_namespace'] = $ns;
4735  }
4736 
4737  $res = $dbr->select(
4738  [ 'redirect', 'page' ],
4739  [ 'page_namespace', 'page_title' ],
4740  $where,
4741  __METHOD__
4742  );
4743 
4744  foreach ( $res as $row ) {
4745  $redirs[] = self::newFromRow( $row );
4746  }
4747  return $redirs;
4748  }
4749 
4755  public function isValidRedirectTarget() {
4757 
4758  if ( $this->isSpecialPage() ) {
4759  // invalid redirect targets are stored in a global array, but explicitly disallow Userlogout here
4760  if ( $this->isSpecial( 'Userlogout' ) ) {
4761  return false;
4762  }
4763 
4764  foreach ( $wgInvalidRedirectTargets as $target ) {
4765  if ( $this->isSpecial( $target ) ) {
4766  return false;
4767  }
4768  }
4769  }
4770 
4771  return true;
4772  }
4773 
4779  public function getBacklinkCache() {
4780  return BacklinkCache::get( $this );
4781  }
4782 
4788  public function canUseNoindex() {
4790 
4791  $bannedNamespaces = is_null( $wgExemptFromUserRobotsControl )
4794 
4795  return !in_array( $this->mNamespace, $bannedNamespaces );
4796  }
4797 
4808  public function getCategorySortkey( $prefix = '' ) {
4809  $unprefixed = $this->getText();
4810 
4811  // Anything that uses this hook should only depend
4812  // on the Title object passed in, and should probably
4813  // tell the users to run updateCollations.php --force
4814  // in order to re-sort existing category relations.
4815  Hooks::run( 'GetDefaultSortkey', [ $this, &$unprefixed ] );
4816  if ( $prefix !== '' ) {
4817  # Separate with a line feed, so the unprefixed part is only used as
4818  # a tiebreaker when two pages have the exact same prefix.
4819  # In UCA, tab is the only character that can sort above LF
4820  # so we strip both of them from the original prefix.
4821  $prefix = strtr( $prefix, "\n\t", ' ' );
4822  return "$prefix\n$unprefixed";
4823  }
4824  return $unprefixed;
4825  }
4826 
4834  private function getDbPageLanguageCode() {
4836 
4837  // check, if the page language could be saved in the database, and if so and
4838  // the value is not requested already, lookup the page language using LinkCache
4839  if ( $wgPageLanguageUseDB && $this->mDbPageLanguage === false ) {
4840  $linkCache = LinkCache::singleton();
4841  $linkCache->addLinkObj( $this );
4842  $this->mDbPageLanguage = $linkCache->getGoodLinkFieldObj( $this, 'lang' );
4843  }
4844 
4845  return $this->mDbPageLanguage;
4846  }
4847 
4856  public function getPageLanguage() {
4858  if ( $this->isSpecialPage() ) {
4859  // special pages are in the user language
4860  return $wgLang;
4861  }
4862 
4863  // Checking if DB language is set
4864  $dbPageLanguage = $this->getDbPageLanguageCode();
4865  if ( $dbPageLanguage ) {
4866  return wfGetLangObj( $dbPageLanguage );
4867  }
4868 
4869  if ( !$this->mPageLanguage || $this->mPageLanguage[1] !== $wgLanguageCode ) {
4870  // Note that this may depend on user settings, so the cache should
4871  // be only per-request.
4872  // NOTE: ContentHandler::getPageLanguage() may need to load the
4873  // content to determine the page language!
4874  // Checking $wgLanguageCode hasn't changed for the benefit of unit
4875  // tests.
4876  $contentHandler = ContentHandler::getForTitle( $this );
4877  $langObj = $contentHandler->getPageLanguage( $this );
4878  $this->mPageLanguage = [ $langObj->getCode(), $wgLanguageCode ];
4879  } else {
4880  $langObj = wfGetLangObj( $this->mPageLanguage[0] );
4881  }
4882 
4883  return $langObj;
4884  }
4885 
4894  public function getPageViewLanguage() {
4895  global $wgLang;
4896 
4897  if ( $this->isSpecialPage() ) {
4898  // If the user chooses a variant, the content is actually
4899  // in a language whose code is the variant code.
4900  $variant = $wgLang->getPreferredVariant();
4901  if ( $wgLang->getCode() !== $variant ) {
4902  return Language::factory( $variant );
4903  }
4904 
4905  return $wgLang;
4906  }
4907 
4908  // Checking if DB language is set
4909  $dbPageLanguage = $this->getDbPageLanguageCode();
4910  if ( $dbPageLanguage ) {
4911  $pageLang = wfGetLangObj( $dbPageLanguage );
4912  $variant = $pageLang->getPreferredVariant();
4913  if ( $pageLang->getCode() !== $variant ) {
4914  $pageLang = Language::factory( $variant );
4915  }
4916 
4917  return $pageLang;
4918  }
4919 
4920  // @note Can't be cached persistently, depends on user settings.
4921  // @note ContentHandler::getPageViewLanguage() may need to load the
4922  // content to determine the page language!
4923  $contentHandler = ContentHandler::getForTitle( $this );
4924  $pageLang = $contentHandler->getPageViewLanguage( $this );
4925  return $pageLang;
4926  }
4927 
4938  public function getEditNotices( $oldid = 0 ) {
4939  $notices = [];
4940 
4941  // Optional notice for the entire namespace
4942  $editnotice_ns = 'editnotice-' . $this->getNamespace();
4943  $msg = wfMessage( $editnotice_ns );
4944  if ( $msg->exists() ) {
4945  $html = $msg->parseAsBlock();
4946  // Edit notices may have complex logic, but output nothing (T91715)
4947  if ( trim( $html ) !== '' ) {
4948  $notices[$editnotice_ns] = Html::rawElement(
4949  'div',
4950  [ 'class' => [
4951  'mw-editnotice',
4952  'mw-editnotice-namespace',
4953  Sanitizer::escapeClass( "mw-$editnotice_ns" )
4954  ] ],
4955  $html
4956  );
4957  }
4958  }
4959 
4960  if ( MWNamespace::hasSubpages( $this->getNamespace() ) ) {
4961  // Optional notice for page itself and any parent page
4962  $parts = explode( '/', $this->getDBkey() );
4963  $editnotice_base = $editnotice_ns;
4964  while ( count( $parts ) > 0 ) {
4965  $editnotice_base .= '-' . array_shift( $parts );
4966  $msg = wfMessage( $editnotice_base );
4967  if ( $msg->exists() ) {
4968  $html = $msg->parseAsBlock();
4969  if ( trim( $html ) !== '' ) {
4970  $notices[$editnotice_base] = Html::rawElement(
4971  'div',
4972  [ 'class' => [
4973  'mw-editnotice',
4974  'mw-editnotice-base',
4975  Sanitizer::escapeClass( "mw-$editnotice_base" )
4976  ] ],
4977  $html
4978  );
4979  }
4980  }
4981  }
4982  } else {
4983  // Even if there are no subpages in namespace, we still don't want "/" in MediaWiki message keys
4984  $editnoticeText = $editnotice_ns . '-' . strtr( $this->getDBkey(), '/', '-' );
4985  $msg = wfMessage( $editnoticeText );
4986  if ( $msg->exists() ) {
4987  $html = $msg->parseAsBlock();
4988  if ( trim( $html ) !== '' ) {
4989  $notices[$editnoticeText] = Html::rawElement(
4990  'div',
4991  [ 'class' => [
4992  'mw-editnotice',
4993  'mw-editnotice-page',
4994  Sanitizer::escapeClass( "mw-$editnoticeText" )
4995  ] ],
4996  $html
4997  );
4998  }
4999  }
5000  }
5001 
5002  Hooks::run( 'TitleGetEditNotices', [ $this, $oldid, &$notices ] );
5003  return $notices;
5004  }
5005 
5009  public function __sleep() {
5010  return [
5011  'mNamespace',
5012  'mDbkeyform',
5013  'mFragment',
5014  'mInterwiki',
5015  'mLocalInterwiki',
5016  'mUserCaseDBKey',
5017  'mDefaultNamespace',
5018  ];
5019  }
5020 
5021  public function __wakeup() {
5022  $this->mArticleID = ( $this->mNamespace >= 0 ) ? -1 : 0;
5023  $this->mUrlform = wfUrlencode( $this->mDbkeyform );
5024  $this->mTextform = strtr( $this->mDbkeyform, '_', ' ' );
5025  }
5026 
5027 }
Title\$mLocalInterwiki
bool $mLocalInterwiki
Was this Title created from a string with a local interwiki prefix?
Definition: Title.php:82
Title\$mRestrictions
array $mRestrictions
Array of groups allowed to edit this article.
Definition: Title.php:109
Title\canHaveTalkPage
canHaveTalkPage()
Can this title have a corresponding talk page?
Definition: Title.php:1095
Title\getTitleProtection
getTitleProtection()
Is this title subject to title protection? Title protection is the one applied against creation of su...
Definition: Title.php:2697
Title\canUseNoindex
canUseNoindex()
Whether the magic words INDEX and NOINDEX function for this page.
Definition: Title.php:4788
Title\inNamespaces
inNamespaces()
Returns true if the title is inside one of the specified namespaces.
Definition: Title.php:1182
Title\getLocalURL
getLocalURL( $query='', $query2=false)
Get a URL with no fragment or server name (relative URL) from a Title object.
Definition: Title.php:1836
Title\isSemiProtected
isSemiProtected( $action='edit')
Is this page "semi-protected" - the only protection levels are listed in $wgSemiprotectedRestrictionL...
Definition: Title.php:2785
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:2845
Title\getTalkNsText
getTalkNsText()
Get the namespace text of the talk page.
Definition: Title.php:1071
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:244
$wgUser
$wgUser
Definition: Setup.php:809
Title\getSubpageText
getSubpageText()
Get the lowest-level subpage name, i.e.
Definition: Title.php:1669
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:268
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:4184
Title\isBigDeletion
isBigDeletion()
Check whether the number of revisions of this page surpasses $wgDeleteRevisionsLimit.
Definition: Title.php:4239
PROTO_CANONICAL
const PROTO_CANONICAL
Definition: Defines.php:224
Title\areCascadeProtectionSourcesLoaded
areCascadeProtectionSourcesLoaded( $getPages=true)
Determines whether cascading protection sources have already been loaded from the database.
Definition: Title.php:2877
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:2033
Title\getFilteredRestrictionTypes
static getFilteredRestrictionTypes( $exists=true)
Get a filtered list of all restriction types supported by this wiki.
Definition: Title.php:2652
wfMergeErrorArrays
wfMergeErrorArrays()
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
Definition: GlobalFunctions.php:276
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:750
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
Title\getInternalURL
getInternalURL( $query='', $query2=false)
Get the URL form for an internal link.
Definition: Title.php:1959
$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:2290
$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:988
Title\getFragment
getFragment()
Get the Title fragment (i.e.
Definition: Title.php:1444
MWNamespace\isTalk
static isTalk( $index)
Is the given namespace a talk namespace?
Definition: MWNamespace.php:96
$wgExemptFromUserRobotsControl
$wgExemptFromUserRobotsControl
An array of namespace keys in which the INDEX/__NOINDEX__ magic words will not function,...
Definition: DefaultSettings.php:7935
HashBagOStuff
Simple store for keeping values in an associative array for the current process.
Definition: HashBagOStuff.php:31
Title\getTalkPageIfDefined
getTalkPageIfDefined()
Get a Title object associated with the talk page of this article, if such a talk page can exist.
Definition: Title.php:1383
Title\inNamespace
inNamespace( $ns)
Returns true if the title is inside the specified namespace.
Definition: Title.php:1171
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:1231
$wgMaximumMovedPages
$wgMaximumMovedPages
Maximum number of pages to move at once when moving subpages with a page.
Definition: DefaultSettings.php:8388
Title\isJsSubpage
isJsSubpage()
Is this a .js subpage of a user page?
Definition: Title.php:1351
Title\wasLocalInterwiki
wasLocalInterwiki()
Was this a local interwiki link?
Definition: Title.php:874
$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:1284
Title\getBacklinkCache
getBacklinkCache()
Get a backlink cache object.
Definition: Title.php:4779
User\newFatalPermissionDeniedStatus
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
Definition: User.php:5524
Title\getPartialURL
getPartialURL()
Get the URL-encoded form of the main part.
Definition: Title.php:946
$wgLegalTitleChars
$wgLegalTitleChars
Allowed title characters – regex character class Don't change this unless you know what you're doing.
Definition: DefaultSettings.php:3950
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:3477
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:3867
Title\getTitleValue
getTitleValue()
Get a TitleValue object representing this Title.
Definition: Title.php:914
Title\estimateRevisionCount
estimateRevisionCount()
Get the approximate revision count of this page.
Definition: Title.php:4268
Title\countRevisionsBetween
countRevisionsBetween( $old, $new, $max=null)
Get the number of revisions between the given revision.
Definition: Title.php:4291
captcha-old.count
count
Definition: captcha-old.py:249
Title\getPrefixedDBkey
getPrefixedDBkey()
Get the prefixed database key form.
Definition: Title.php:1538
Title\getDbPageLanguageCode
getDbPageLanguageCode()
Returns the page language code saved in the database, if $wgPageLanguageUseDB is set to true in Local...
Definition: Title.php:4834
$wgWhitelistReadRegexp
$wgWhitelistReadRegexp
Pages anonymous user may see, set as an array of regular expressions.
Definition: DefaultSettings.php:5091
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:2371
$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:1370
Title\newMainPage
static newMainPage()
Create a new Title for the Main Page.
Definition: Title.php:581
Title\getNotificationTimestamp
getNotificationTimestamp( $user=null)
Get the timestamp when this page was updated since the user last saw it.
Definition: Title.php:4650
Title\getParentCategoryTree
getParentCategoryTree( $children=[])
Get a tree of parent categories.
Definition: Title.php:4074
CONTENT_MODEL_CSS
const CONTENT_MODEL_CSS
Definition: Defines.php:238
$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:1963
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:1729
$namespaces
namespace and then decline to actually register it & $namespaces
Definition: hooks.txt:932
Title\checkSpecialsAndNSPermissions
checkSpecialsAndNSPermissions( $action, $user, $errors, $rigor, $short)
Check permissions on special pages & namespaces.
Definition: Title.php:2226
Title\fixSpecialName
fixSpecialName()
If the Title refers to a special page alias which is not the local default, resolve the alias,...
Definition: Title.php:1148
Title\getInterwikiLookup
static getInterwikiLookup()
B/C kludge: provide an InterwikiLookup for use by Title.
Definition: Title.php:191
$status
Status::newGood()` to allow deletion, and then `return false` from the hook function. Ensure you consume the 'ChangeTagAfterDelete' hook to carry out custom deletion actions. $tag:name of the tag $user:user initiating the action & $status:Status object. See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use. & $tags:list of all active tags. Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function. Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action or null $user:User who performed the tagging when the tagging is subsequent to the action or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change. & $allowedTags:List of all the tags the user is allowed to add. Any tags the user wants to add( $addTags) that are not in this array will cause it to fail. You may add or remove tags to this array as required. $addTags:List of tags user intends to add. $user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed. $performer:The User who will perform the change $user:The User whose groups will be changed & $add:The groups that will be added & $remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation. $collationName:Name of the collation in question & $collationObject:Null. Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user 's email has been confirmed successfully. $user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object. Called by Content::getParserOutput after parsing has finished. Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML). $content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput. May be used to override the normal model-specific rendering of page content. $content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering. To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state. $generateHtml:boolean, indicating whether full HTML should be generated. If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object. & $output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title. May be used to assign a different model for that title. $title:the Title in question & $model:the model name. Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers. Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core. $modeName:the requested content model name & $handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page. This is especially useful to prevent some content models to be used in some special location. $contentModel:ID of the content model in question $title:the Title in question. & $ok:Output parameter, whether it is OK to use $contentModel on $title. Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok. 'ContribsPager::getQueryInfo':Before the contributions query is about to run & $pager:Pager object for contributions & $queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions & $data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title & $tools:Array of tool links $specialPage:SpecialPage instance for context and services. Can be either SpecialContributions or DeletedContributionsPage. Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested. Handler functions that modify $result should generally return false to disable further attempts at conversion. $content:The Content object to be converted. $toModel:The ID of the content model to convert to. $lossy:boolean indicating whether lossy conversion is allowed. & $result:Output parameter, in case the handler function wants to provide a converted Content object. Note that $result->getContentModel() must return $toModel. 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g. for a special namespace, etc. $article:Article being edited $user:User performing the edit 'DatabaseOraclePostInit':Called after initialising an Oracle database $db:the DatabaseOracle object 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery & $data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished. Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function. $differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable 's value is null. This hook can be used to inject content into said class member variable. $differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the "mark as patrolled" link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink". $differenceEngine:DifferenceEngine object & $markAsPatrolledLink:The "mark as patrolled" link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter. For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions. & $rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision 's author, whether the revision was marked as a minor edit or not, etc. $differenceEngine:DifferenceEngine object & $newHeader:The string containing the various #mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition: hooks.txt:1245
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:4623
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:405
Title\getPrefixedText
getPrefixedText()
Get the prefixed title with spaces.
Definition: Title.php:1550
MediaWikiTitleCodec\getTitleInvalidRegex
static getTitleInvalidRegex()
Returns a simple regex that will match on characters and sequences invalid in titles.
Definition: MediaWikiTitleCodec.php:474
Title\getTransWikiID
getTransWikiID()
Returns the DB name of the distant wiki which owns the object.
Definition: Title.php:897
Title\resultToError
resultToError( $errors, $result)
Add the resulting error code to the errors array.
Definition: Title.php:2158
Title\isCssJsSubpage
isCssJsSubpage()
Is this a .css or .js subpage of a user page?
Definition: Title.php:1315
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:3334
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:3749
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:1934
Title\secureAndSplit
secureAndSplit()
Secure and split - main initialisation function for this object.
Definition: Title.php:3514
Title\quickUserCan
quickUserCan( $action, $user=null)
Can $user perform $action on this page? This skips potentially expensive cascading permission checks ...
Definition: Title.php:2020
NS_FILE
const NS_FILE
Definition: Defines.php:71
CommentStore
CommentStore handles storage of comments (edit summaries, log reasons, etc) in the database.
Definition: CommentStore.php:30
Title\isTalkPage
isTalkPage()
Is this a talk page of some sort?
Definition: Title.php:1361
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1324
$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:854
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
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:3052
$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:4559
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:236
Title\hasSourceText
hasSourceText()
Does this page have source text?
Definition: Title.php:4533
Title\loadFromRow
loadFromRow( $row)
Load Title object fields from a DB row.
Definition: Title.php:471
User\groupHasPermission
static groupHasPermission( $group, $role)
Check, if the given group has the given permission.
Definition: User.php:4792
Title\checkCSSandJSPermissions
checkCSSandJSPermissions( $action, $user, $errors, $rigor, $short)
Check CSS/JS sub-page permissions.
Definition: Title.php:2255
Title\checkReadPermissions
checkReadPermissions( $action, $user, $errors, $rigor, $short)
Check that the user is allowed to read this page.
Definition: Title.php:2493
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:3455
Title\setFragment
setFragment( $fragment)
Set the fragment for this title.
Definition: Title.php:1484
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:353
$wgContentHandlerUseDB
$wgContentHandlerUseDB
Set to false to disable use of the database fields introduced by the ContentHandler facility.
Definition: DefaultSettings.php:8476
Title\convertByteClassToUnicodeClass
static convertByteClassToUnicodeClass( $byteClass)
Utility method for converting a character sequence from bytes to Unicode.
Definition: Title.php:646
Title\__wakeup
__wakeup()
Text form (spaces not underscores) of the main part.
Definition: Title.php:5021
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:4039
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:3562
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:1036
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
$wgWhitelistRead
$wgWhitelistRead
Pages anonymous user may see, set as an array of pages titles.
Definition: DefaultSettings.php:5063
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:3620
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:1326
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:534
Revision
Definition: Revision.php:33
NS_MAIN
const NS_MAIN
Definition: Defines.php:65
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:2863
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:3991
$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:1581
Title\getUserPermissionsErrors
getUserPermissionsErrors( $action, $user, $rigor='secure', $ignoreErrors=[])
Can $user perform $action on this page?
Definition: Title.php:2057
Title\getCdnUrls
getCdnUrls()
Get a list of URLs to purge from the CDN cache when this page changes.
Definition: Title.php:3721
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:54
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:1414
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:955
Title\nameOf
static nameOf( $id)
Get the prefixed DB key associated with an ID.
Definition: Title.php:596
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:1965
Title\isSpecial
isSpecial( $name)
Returns true if this title resolves to the named special page.
Definition: Title.php:1132
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:932
Title\checkCascadingSourcesRestrictions
checkCascadingSourcesRestrictions( $action, $user, $errors, $rigor, $short)
Check restrictions on cascading pages.
Definition: Title.php:2324
Title\isMainPage
isMainPage()
Is this the mainpage?
Definition: Title.php:1252
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1176
$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:3829
Title\getSubpage
getSubpage( $text)
Get the title for a subpage of the current page.
Definition: Title.php:1690
Title\getBaseTitle
getBaseTitle()
Get the base page name title, i.e.
Definition: Title.php:1654
Title\getNamespace
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:978
BacklinkCache\get
static get(Title $title)
Create a new BacklinkCache or reuse any existing one.
Definition: BacklinkCache.php:106
Title\newFromRow
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:459
Title\isConversionTable
isConversionTable()
Is this a conversion table for the LanguageConverter?
Definition: Title.php:1272
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:288
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:2813
Title\flushRestrictions
flushRestrictions()
Flush the protection cache in this object and force reload from the database.
Definition: Title.php:3177
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2856
Title\getCategorySortkey
getCategorySortkey( $prefix='')
Returns the raw sort key to be used for categories, with the specified prefix.
Definition: Title.php:4808
Title\getInterwiki
getInterwiki()
Get the interwiki prefix.
Definition: Title.php:865
$matches
$matches
Definition: NoLocalSettings.php:24
in
null for the wiki Added in
Definition: hooks.txt:1581
Title\isValidRedirectTarget
isValidRedirectTarget()
Check if this Title is a valid redirect target.
Definition: Title.php:4755
Title\deleteTitleProtection
deleteTitleProtection()
Remove any title protection due to page existing.
Definition: Title.php:2767
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:311
Title\getBrokenLinksFrom
getBrokenLinksFrom()
Get an array of Title objects referring to non-existent articles linked from this page.
Definition: Title.php:3685
PROTO_CURRENT
const PROTO_CURRENT
Definition: Defines.php:223
Title\missingPermissionError
missingPermissionError( $action, $short)
Get a description array when the user doesn't have the right to perform $action (i....
Definition: Title.php:2568
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:1397
wfGetLangObj
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
Definition: GlobalFunctions.php:1368
$wgInvalidRedirectTargets
$wgInvalidRedirectTargets
Array of invalid page redirect targets.
Definition: DefaultSettings.php:4147
Title\getFullText
getFullText()
Get the prefixed title with spaces, plus any fragment (part beginning with '#')
Definition: Title.php:1574
MWNamespace\hasTalkNamespace
static hasTalkNamespace( $index)
Does this namespace ever have a talk namespace?
Definition: MWNamespace.php:300
MWNamespace\hasSubpages
static hasSubpages( $index)
Does the namespace allow subpages?
Definition: MWNamespace.php:344
Title\getTitleCache
static getTitleCache()
Definition: Title.php:371
Title\hasFragment
hasFragment()
Check if a Title fragment is set.
Definition: Title.php:1454
Title\isCssSubpage
isCssSubpage()
Is this a .css subpage of a user page?
Definition: Title.php:1341
$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
$parser
do that in ParserLimitReportFormat instead $parser
Definition: hooks.txt:2572
Title\getRedirectsHere
getRedirectsHere( $ns=null)
Get all extant redirects to this Title.
Definition: Title.php:4719
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:529
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:4419
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:2069
Title\getRootText
getRootText()
Get the root page name text without a namespace, i.e.
Definition: Title.php:1594
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:79
$wgNamespaceProtection
$wgNamespaceProtection
Set the minimum permissions required to edit pages in each namespace.
Definition: DefaultSettings.php:5357
Title\canExist
canExist()
Is this in a namespace that allows actual pages?
Definition: Title.php:1104
$wgDeleteRevisionsLimit
$wgDeleteRevisionsLimit
Optional to restrict deletion of pages with higher revision counts to users with the 'bigdelete' perm...
Definition: DefaultSettings.php:5534
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:1047
string
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
Definition: hooks.txt:175
Title\__sleep
__sleep()
Definition: Title.php:5009
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:1983
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
$dir
$dir
Definition: Autoload.php:8
MessageCache\singleton
static singleton()
Get the signleton instance of this class.
Definition: MessageCache.php:113
$sort
$sort
Definition: profileinfo.php:323
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:1495
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:1433
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:301
$wgPageLanguageUseDB
bool $wgPageLanguageUseDB
Enable page language feature Allows setting page language in database.
Definition: DefaultSettings.php:8559
Title\newFromTitleValue
static newFromTitleValue(TitleValue $titleValue)
Create a new Title from a TitleValue.
Definition: Title.php:228
Title\isValid
isValid()
Returns true if the title is valid, false if it is invalid.
Definition: Title.php:816
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:1710
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:557
wfWikiID
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
Definition: GlobalFunctions.php:2807
Title\__toString
__toString()
Return a string representation of this title.
Definition: Title.php:1564
User\whoIs
static whoIs( $id)
Get the username corresponding to a given user ID.
Definition: User.php:745
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:3604
Title\areRestrictionsCascading
areRestrictionsCascading()
Returns cascading restrictions for the current article.
Definition: Title.php:3035
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:53
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:3423
Title\areRestrictionsLoaded
areRestrictionsLoaded()
Accessor for mRestrictionsLoaded.
Definition: Title.php:2980
$retval
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account incomplete not yet checked for validity & $retval
Definition: hooks.txt:244
Title\getSubpages
getSubpages( $limit=-1)
Get all subpages of this page.
Definition: Title.php:3256
Title\isSingleRevRedirect
isSingleRevRedirect()
Checks if this page is just a one-rev redirect.
Definition: Title.php:3940
$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:4894
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:2861
Title\isSubpage
isSubpage()
Is this a subpage?
Definition: Title.php:1261
Title\hasContentModel
hasContentModel( $id)
Convenience method for checking a title's content model name.
Definition: Title.php:1011
Title\getRestrictionExpiry
getRestrictionExpiry( $action)
Get the expiry time for the restriction against a given action.
Definition: Title.php:3023
Title\newFromURL
static newFromURL( $url)
THIS IS NOT THE FUNCTION YOU WANT.
Definition: Title.php:348
Title\isValidMoveOperation
isValidMoveOperation(&$nt, $auth=true, $reason='')
Check whether a given move operation would be valid.
Definition: Title.php:3773
MediaWiki\Linker\LinkTarget\getFragment
getFragment()
Get the link fragment (i.e.
PROTO_HTTP
const PROTO_HTTP
Definition: Defines.php:220
$wgBlockDisablesLogin
$wgBlockDisablesLogin
If true, blocked users will not be allowed to login.
Definition: DefaultSettings.php:5041
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:222
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:1965
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:4432
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:839
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:988
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:433
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:2088
RequestContext\getMain
static getMain()
Static methods.
Definition: RequestContext.php:470
Title\prefix
prefix( $name)
Prefix some arbitrary text with the namespace or interwiki prefix of this object.
Definition: Title.php:1511
Title\getNamespaceKey
getNamespaceKey( $prepend='nstab-')
Generate strings used for xml 'id' names in monobook tabs.
Definition: Title.php:4688
Title\getEditURL
getEditURL()
Get the edit URL for this Title.
Definition: Title.php:1997
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:1767
wfFindFile
wfFindFile( $title, $options=[])
Find a file.
Definition: GlobalFunctions.php:2897
$wgEmailConfirmToEdit
$wgEmailConfirmToEdit
Should editors be required to have a validated e-mail address before being allowed to edit?
Definition: DefaultSettings.php:5097
Title\isKnown
isKnown()
Does this title refer to a page that can (or might) be meaningfully viewed? In particular,...
Definition: Title.php:4524
$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:3800
Title\getFirstRevision
getFirstRevision( $flags=0)
Get the first revision of the page.
Definition: Title.php:4194
Title\getAllRestrictions
getAllRestrictions()
Accessor/initialisation for mRestrictions.
Definition: Title.php:3009
Title\getEarliestRevTime
getEarliestRevTime( $flags=0)
Get the oldest revision timestamp of this page.
Definition: Title.php:4219
User\isEveryoneAllowed
static isEveryoneAllowed( $right)
Check if all users may be assumed to have the given permission.
Definition: User.php:4812
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()
Can this title have a corresponding talk page?
Definition: Title.php:1083
Title\getCascadeProtectionSources
getCascadeProtectionSources( $getPages=true)
Cascading protection: Get the source of any cascading restrictions on this page.
Definition: Title.php:2894
$dbr
if(! $regexes) $dbr
Definition: cleanup.php:94
$cache
$cache
Definition: mcc.php:33
$rows
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction $rows
Definition: hooks.txt:2581
$wgSemiprotectedRestrictionLevels
$wgSemiprotectedRestrictionLevels
Restriction levels that should be considered "semiprotected".
Definition: DefaultSettings.php:5348
Title\getRestrictionTypes
getRestrictionTypes()
Returns restriction types for the current Title.
Definition: Title.php:2670
MalformedTitleException
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Definition: MalformedTitleException.php:25
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1965
Title\getTitleProtectionInternal
getTitleProtectionInternal()
Fetch title protection settings.
Definition: Title.php:2720
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:3673
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:3360
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:2591
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:3283
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:3492
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:1210
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:1122
Title\purgeSquid
purgeSquid()
Purge all applicable CDN URLs.
Definition: Title.php:3756
Title\getPreviousRevisionID
getPreviousRevisionID( $revId, $flags=0)
Get the revision ID of the previous revision.
Definition: Title.php:4173
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:1750
Title\getPageLanguage
getPageLanguage()
Get the language in which the content of this page is written in wikitext.
Definition: Title.php:4856
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:4938
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:4636
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:397
Title\isCssOrJsPage
isCssOrJsPage()
Could this page contain custom CSS or JavaScript for the global UI.
Definition: Title.php:1302
Wikimedia\Rdbms\IDatabase\selectFieldValues
selectFieldValues( $table, $var, $cond='', $fname=__METHOD__, $options=[], $join_conds=[])
A SELECT wrapper which returns a list of single field values from result rows.
$wgRestrictionLevels
$wgRestrictionLevels
Rights which can be required for each protection level (via action=protect)
Definition: DefaultSettings.php:5323
Title\getFragmentForURL
getFragmentForURL()
Get the fragment in URL form, including the "#" character if there is one.
Definition: Title.php:1463
Title\exists
exists( $flags=0)
Check if page exists.
Definition: Title.php:4449
NS_USER
const NS_USER
Definition: Defines.php:67
Title\isAlwaysKnown
isAlwaysKnown()
Should links to this title be shown as potentially viewable (i.e.
Definition: Title.php:4471
Title\isDeletedQuick
isDeletedQuick()
Is there a version of this page in the deletion archive?
Definition: Title.php:3308
Title\getSubpageUrlForm
getSubpageUrlForm()
Get a URL-encoded form of the subpage text.
Definition: Title.php:1699
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:4229
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
$wgRestrictionTypes
$wgRestrictionTypes
Set of available actions that can be restricted via action=protect You probably shouldn't change this...
Definition: DefaultSettings.php:5310
Title\setContentModel
setContentModel( $model)
Set a proposed content model for the page for permissions checking.
Definition: Title.php:1026
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:73
CONTENT_MODEL_JAVASCRIPT
const CONTENT_MODEL_JAVASCRIPT
Definition: Defines.php:237
Title\getTitleInvalidRegex
static getTitleInvalidRegex()
Returns a simple regex that will match on characters and sequences invalid in titles.
Definition: Title.php:632
Title\compare
static compare(LinkTarget $a, LinkTarget $b)
Callback for usort() to do title sorts by (namespace, title)
Definition: Title.php:794
Title\countAuthorsBetween
countAuthorsBetween( $old, $new, $limit, $options=[])
Get the number of authors between the given revisions or revision IDs.
Definition: Title.php:4408
$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:618
MWNamespace\isWatchable
static isWatchable( $index)
Can pages in a namespace be watched?
Definition: MWNamespace.php:334
Title\invalidateCache
invalidateCache( $purgeTime=null)
Updates page_touched for this page; called from LinksUpdate.php.
Definition: Title.php:4584
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:662
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:884
Title\getRestrictions
getRestrictions( $action)
Accessor/initialisation for mRestrictions.
Definition: Title.php:2993
Revision\selectFields
static selectFields()
Return the list of revision fields that should be selected to create a new revision.
Definition: Revision.php:452
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:2189
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:445
Title\isContentPage
isContentPage()
Is this Title in a namespace which contains content? In other words, is this a content page,...
Definition: Title.php:1221
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:51
wfLocalFile
wfLocalFile( $title)
Get an object referring to a locally registered file.
Definition: GlobalFunctions.php:2908
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:411
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
$wgVariantArticlePath
$wgVariantArticlePath
Like $wgArticlePath, but on multi-variant wikis, this provides a path format that describes which par...
Definition: DefaultSettings.php:3078
Title\getUserCaseDBKey
getUserCaseDBKey()
Get the DB key with the initial letter case as specified by the user.
Definition: Title.php:964
Title\getSelectFields
static getSelectFields()
Returns a list of fields that are to be selected for initializing Title objects or LinkCache entries.
Definition: Title.php:385
Title\hasSubpages
hasSubpages()
Does this have subpages? (Warning, usually requires an extra DB query.)
Definition: Title.php:3228
Title\escapeFragmentForURL
static escapeFragmentForURL( $fragment)
Escape a text fragment, say from a link, for a URL.
Definition: Title.php:778
Title\getBaseText
getBaseText()
Get the base page name without a namespace, i.e.
Definition: Title.php:1629
Title\pageCond
pageCond()
Get an associative array for selecting this title from the "page" table.
Definition: Title.php:4101
Title\getText
getText()
Get the text form (spaces not underscores) of the main part.
Definition: Title.php:937
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:3187
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2801
$wgInternalServer
$wgInternalServer
Internal server name as known to CDN, if different.
Definition: DefaultSettings.php:2687
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:2448
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:586
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:1061
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:3122
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
Title\getRelativeRevisionID
getRelativeRevisionID( $revId, $flags, $dir)
Get next/previous revision ID relative to another revision ID.
Definition: Title.php:4117
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:442
Title\getFullUrlForRedirect
getFullUrlForRedirect( $query='', $proto=PROTO_CURRENT)
Get a url appropriate for making redirects based on an untrusted url arg.
Definition: Title.php:1802
$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:781
Title\getRootTitle
getRootTitle()
Get the root page name title, i.e.
Definition: Title.php:1614
$type
$type
Definition: testCompression.php:48
Title\getLength
getLength( $flags=0)
What is the length of this page? Uses link cache, adding it if necessary.
Definition: Title.php:3395
Title\getAuthorsBetween
getAuthorsBetween( $old, $new, $limit, $options=[])
Get the authors between the given revisions or revision IDs.
Definition: Title.php:4334
Title\isWatchable
isWatchable()
Can this title be added to a user's watchlist?
Definition: Title.php:1113