MediaWiki  1.30.0
Title.php
Go to the documentation of this file.
1 <?php
30 
39 class Title implements LinkTarget {
41  static private $titleCache = null;
42 
48  const CACHE_MAX = 1000;
49 
54  const GAID_FOR_UPDATE = 1;
55 
61  // @{
62 
64  public $mTextform = '';
65 
67  public $mUrlform = '';
68 
70  public $mDbkeyform = '';
71 
73  protected $mUserCaseDBKey;
74 
76  public $mNamespace = NS_MAIN;
77 
79  public $mInterwiki = '';
80 
82  private $mLocalInterwiki = false;
83 
85  public $mFragment = '';
86 
88  public $mArticleID = -1;
89 
91  protected $mLatestID = false;
92 
97  private $mContentModel = false;
98 
103  private $mForcedContentModel = false;
104 
107 
109  public $mRestrictions = [];
110 
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 
2461  if ( $wgEmailConfirmToEdit && !$user->isEmailConfirmed() ) {
2462  $errors[] = [ 'confirmedittext' ];
2463  }
2464 
2465  $useSlave = ( $rigor !== 'secure' );
2466  if ( ( $action == 'edit' || $action == 'create' )
2467  && !$user->isBlockedFrom( $this, $useSlave )
2468  ) {
2469  // Don't block the user from editing their own talk page unless they've been
2470  // explicitly blocked from that too.
2471  } elseif ( $user->isBlocked() && $user->getBlock()->prevents( $action ) !== false ) {
2472  // @todo FIXME: Pass the relevant context into this function.
2473  $errors[] = $user->getBlock()->getPermissionsError( RequestContext::getMain() );
2474  }
2475 
2476  return $errors;
2477  }
2478 
2490  private function checkReadPermissions( $action, $user, $errors, $rigor, $short ) {
2492 
2493  $whitelisted = false;
2494  if ( User::isEveryoneAllowed( 'read' ) ) {
2495  # Shortcut for public wikis, allows skipping quite a bit of code
2496  $whitelisted = true;
2497  } elseif ( $user->isAllowed( 'read' ) ) {
2498  # If the user is allowed to read pages, he is allowed to read all pages
2499  $whitelisted = true;
2500  } elseif ( $this->isSpecial( 'Userlogin' )
2501  || $this->isSpecial( 'PasswordReset' )
2502  || $this->isSpecial( 'Userlogout' )
2503  ) {
2504  # Always grant access to the login page.
2505  # Even anons need to be able to log in.
2506  $whitelisted = true;
2507  } elseif ( is_array( $wgWhitelistRead ) && count( $wgWhitelistRead ) ) {
2508  # Time to check the whitelist
2509  # Only do these checks is there's something to check against
2510  $name = $this->getPrefixedText();
2511  $dbName = $this->getPrefixedDBkey();
2512 
2513  // Check for explicit whitelisting with and without underscores
2514  if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) {
2515  $whitelisted = true;
2516  } elseif ( $this->getNamespace() == NS_MAIN ) {
2517  # Old settings might have the title prefixed with
2518  # a colon for main-namespace pages
2519  if ( in_array( ':' . $name, $wgWhitelistRead ) ) {
2520  $whitelisted = true;
2521  }
2522  } elseif ( $this->isSpecialPage() ) {
2523  # If it's a special page, ditch the subpage bit and check again
2524  $name = $this->getDBkey();
2525  list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name );
2526  if ( $name ) {
2527  $pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
2528  if ( in_array( $pure, $wgWhitelistRead, true ) ) {
2529  $whitelisted = true;
2530  }
2531  }
2532  }
2533  }
2534 
2535  if ( !$whitelisted && is_array( $wgWhitelistReadRegexp ) && !empty( $wgWhitelistReadRegexp ) ) {
2536  $name = $this->getPrefixedText();
2537  // Check for regex whitelisting
2538  foreach ( $wgWhitelistReadRegexp as $listItem ) {
2539  if ( preg_match( $listItem, $name ) ) {
2540  $whitelisted = true;
2541  break;
2542  }
2543  }
2544  }
2545 
2546  if ( !$whitelisted ) {
2547  # If the title is not whitelisted, give extensions a chance to do so...
2548  Hooks::run( 'TitleReadWhitelist', [ $this, $user, &$whitelisted ] );
2549  if ( !$whitelisted ) {
2550  $errors[] = $this->missingPermissionError( $action, $short );
2551  }
2552  }
2553 
2554  return $errors;
2555  }
2556 
2565  private function missingPermissionError( $action, $short ) {
2566  // We avoid expensive display logic for quickUserCan's and such
2567  if ( $short ) {
2568  return [ 'badaccess-group0' ];
2569  }
2570 
2571  return User::newFatalPermissionDeniedStatus( $action )->getErrorsArray()[0];
2572  }
2573 
2589  $action, $user, $rigor = 'secure', $short = false
2590  ) {
2591  if ( $rigor === true ) {
2592  $rigor = 'secure'; // b/c
2593  } elseif ( $rigor === false ) {
2594  $rigor = 'quick'; // b/c
2595  } elseif ( !in_array( $rigor, [ 'quick', 'full', 'secure' ] ) ) {
2596  throw new Exception( "Invalid rigor parameter '$rigor'." );
2597  }
2598 
2599  # Read has special handling
2600  if ( $action == 'read' ) {
2601  $checks = [
2602  'checkPermissionHooks',
2603  'checkReadPermissions',
2604  'checkUserBlock', // for wgBlockDisablesLogin
2605  ];
2606  # Don't call checkSpecialsAndNSPermissions or checkCSSandJSPermissions
2607  # here as it will lead to duplicate error messages. This is okay to do
2608  # since anywhere that checks for create will also check for edit, and
2609  # those checks are called for edit.
2610  } elseif ( $action == 'create' ) {
2611  $checks = [
2612  'checkQuickPermissions',
2613  'checkPermissionHooks',
2614  'checkPageRestrictions',
2615  'checkCascadingSourcesRestrictions',
2616  'checkActionPermissions',
2617  'checkUserBlock'
2618  ];
2619  } else {
2620  $checks = [
2621  'checkQuickPermissions',
2622  'checkPermissionHooks',
2623  'checkSpecialsAndNSPermissions',
2624  'checkCSSandJSPermissions',
2625  'checkPageRestrictions',
2626  'checkCascadingSourcesRestrictions',
2627  'checkActionPermissions',
2628  'checkUserBlock'
2629  ];
2630  }
2631 
2632  $errors = [];
2633  while ( count( $checks ) > 0 &&
2634  !( $short && count( $errors ) > 0 ) ) {
2635  $method = array_shift( $checks );
2636  $errors = $this->$method( $action, $user, $errors, $rigor, $short );
2637  }
2638 
2639  return $errors;
2640  }
2641 
2649  public static function getFilteredRestrictionTypes( $exists = true ) {
2651  $types = $wgRestrictionTypes;
2652  if ( $exists ) {
2653  # Remove the create restriction for existing titles
2654  $types = array_diff( $types, [ 'create' ] );
2655  } else {
2656  # Only the create and upload restrictions apply to non-existing titles
2657  $types = array_intersect( $types, [ 'create', 'upload' ] );
2658  }
2659  return $types;
2660  }
2661 
2667  public function getRestrictionTypes() {
2668  if ( $this->isSpecialPage() ) {
2669  return [];
2670  }
2671 
2672  $types = self::getFilteredRestrictionTypes( $this->exists() );
2673 
2674  if ( $this->getNamespace() != NS_FILE ) {
2675  # Remove the upload restriction for non-file titles
2676  $types = array_diff( $types, [ 'upload' ] );
2677  }
2678 
2679  Hooks::run( 'TitleGetRestrictionTypes', [ $this, &$types ] );
2680 
2681  wfDebug( __METHOD__ . ': applicable restrictions to [[' .
2682  $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" );
2683 
2684  return $types;
2685  }
2686 
2694  public function getTitleProtection() {
2695  $protection = $this->getTitleProtectionInternal();
2696  if ( $protection ) {
2697  if ( $protection['permission'] == 'sysop' ) {
2698  $protection['permission'] = 'editprotected'; // B/C
2699  }
2700  if ( $protection['permission'] == 'autoconfirmed' ) {
2701  $protection['permission'] = 'editsemiprotected'; // B/C
2702  }
2703  }
2704  return $protection;
2705  }
2706 
2717  protected function getTitleProtectionInternal() {
2718  // Can't protect pages in special namespaces
2719  if ( $this->getNamespace() < 0 ) {
2720  return false;
2721  }
2722 
2723  // Can't protect pages that exist.
2724  if ( $this->exists() ) {
2725  return false;
2726  }
2727 
2728  if ( $this->mTitleProtection === null ) {
2729  $dbr = wfGetDB( DB_REPLICA );
2730  $commentStore = new CommentStore( 'pt_reason' );
2731  $commentQuery = $commentStore->getJoin();
2732  $res = $dbr->select(
2733  [ 'protected_titles' ] + $commentQuery['tables'],
2734  [
2735  'user' => 'pt_user',
2736  'expiry' => 'pt_expiry',
2737  'permission' => 'pt_create_perm'
2738  ] + $commentQuery['fields'],
2739  [ 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ],
2740  __METHOD__,
2741  [],
2742  $commentQuery['joins']
2743  );
2744 
2745  // fetchRow returns false if there are no rows.
2746  $row = $dbr->fetchRow( $res );
2747  if ( $row ) {
2748  $this->mTitleProtection = [
2749  'user' => $row['user'],
2750  'expiry' => $dbr->decodeExpiry( $row['expiry'] ),
2751  'permission' => $row['permission'],
2752  'reason' => $commentStore->getComment( $row )->text,
2753  ];
2754  } else {
2755  $this->mTitleProtection = false;
2756  }
2757  }
2758  return $this->mTitleProtection;
2759  }
2760 
2764  public function deleteTitleProtection() {
2765  $dbw = wfGetDB( DB_MASTER );
2766 
2767  $dbw->delete(
2768  'protected_titles',
2769  [ 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ],
2770  __METHOD__
2771  );
2772  $this->mTitleProtection = false;
2773  }
2774 
2782  public function isSemiProtected( $action = 'edit' ) {
2784 
2785  $restrictions = $this->getRestrictions( $action );
2787  if ( !$restrictions || !$semi ) {
2788  // Not protected, or all protection is full protection
2789  return false;
2790  }
2791 
2792  // Remap autoconfirmed to editsemiprotected for BC
2793  foreach ( array_keys( $semi, 'autoconfirmed' ) as $key ) {
2794  $semi[$key] = 'editsemiprotected';
2795  }
2796  foreach ( array_keys( $restrictions, 'autoconfirmed' ) as $key ) {
2797  $restrictions[$key] = 'editsemiprotected';
2798  }
2799 
2800  return !array_diff( $restrictions, $semi );
2801  }
2802 
2810  public function isProtected( $action = '' ) {
2812 
2813  $restrictionTypes = $this->getRestrictionTypes();
2814 
2815  # Special pages have inherent protection
2816  if ( $this->isSpecialPage() ) {
2817  return true;
2818  }
2819 
2820  # Check regular protection levels
2821  foreach ( $restrictionTypes as $type ) {
2822  if ( $action == $type || $action == '' ) {
2823  $r = $this->getRestrictions( $type );
2824  foreach ( $wgRestrictionLevels as $level ) {
2825  if ( in_array( $level, $r ) && $level != '' ) {
2826  return true;
2827  }
2828  }
2829  }
2830  }
2831 
2832  return false;
2833  }
2834 
2842  public function isNamespaceProtected( User $user ) {
2844 
2845  if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) {
2846  foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) {
2847  if ( $right != '' && !$user->isAllowed( $right ) ) {
2848  return true;
2849  }
2850  }
2851  }
2852  return false;
2853  }
2854 
2860  public function isCascadeProtected() {
2861  list( $sources, /* $restrictions */ ) = $this->getCascadeProtectionSources( false );
2862  return ( $sources > 0 );
2863  }
2864 
2874  public function areCascadeProtectionSourcesLoaded( $getPages = true ) {
2875  return $getPages ? $this->mCascadeSources !== null : $this->mHasCascadingRestrictions !== null;
2876  }
2877 
2891  public function getCascadeProtectionSources( $getPages = true ) {
2892  $pagerestrictions = [];
2893 
2894  if ( $this->mCascadeSources !== null && $getPages ) {
2896  } elseif ( $this->mHasCascadingRestrictions !== null && !$getPages ) {
2897  return [ $this->mHasCascadingRestrictions, $pagerestrictions ];
2898  }
2899 
2900  $dbr = wfGetDB( DB_REPLICA );
2901 
2902  if ( $this->getNamespace() == NS_FILE ) {
2903  $tables = [ 'imagelinks', 'page_restrictions' ];
2904  $where_clauses = [
2905  'il_to' => $this->getDBkey(),
2906  'il_from=pr_page',
2907  'pr_cascade' => 1
2908  ];
2909  } else {
2910  $tables = [ 'templatelinks', 'page_restrictions' ];
2911  $where_clauses = [
2912  'tl_namespace' => $this->getNamespace(),
2913  'tl_title' => $this->getDBkey(),
2914  'tl_from=pr_page',
2915  'pr_cascade' => 1
2916  ];
2917  }
2918 
2919  if ( $getPages ) {
2920  $cols = [ 'pr_page', 'page_namespace', 'page_title',
2921  'pr_expiry', 'pr_type', 'pr_level' ];
2922  $where_clauses[] = 'page_id=pr_page';
2923  $tables[] = 'page';
2924  } else {
2925  $cols = [ 'pr_expiry' ];
2926  }
2927 
2928  $res = $dbr->select( $tables, $cols, $where_clauses, __METHOD__ );
2929 
2930  $sources = $getPages ? [] : false;
2931  $now = wfTimestampNow();
2932 
2933  foreach ( $res as $row ) {
2934  $expiry = $dbr->decodeExpiry( $row->pr_expiry );
2935  if ( $expiry > $now ) {
2936  if ( $getPages ) {
2937  $page_id = $row->pr_page;
2938  $page_ns = $row->page_namespace;
2939  $page_title = $row->page_title;
2940  $sources[$page_id] = self::makeTitle( $page_ns, $page_title );
2941  # Add groups needed for each restriction type if its not already there
2942  # Make sure this restriction type still exists
2943 
2944  if ( !isset( $pagerestrictions[$row->pr_type] ) ) {
2945  $pagerestrictions[$row->pr_type] = [];
2946  }
2947 
2948  if (
2949  isset( $pagerestrictions[$row->pr_type] )
2950  && !in_array( $row->pr_level, $pagerestrictions[$row->pr_type] )
2951  ) {
2952  $pagerestrictions[$row->pr_type][] = $row->pr_level;
2953  }
2954  } else {
2955  $sources = true;
2956  }
2957  }
2958  }
2959 
2960  if ( $getPages ) {
2961  $this->mCascadeSources = $sources;
2962  $this->mCascadingRestrictions = $pagerestrictions;
2963  } else {
2964  $this->mHasCascadingRestrictions = $sources;
2965  }
2966 
2967  return [ $sources, $pagerestrictions ];
2968  }
2969 
2977  public function areRestrictionsLoaded() {
2979  }
2980 
2990  public function getRestrictions( $action ) {
2991  if ( !$this->mRestrictionsLoaded ) {
2992  $this->loadRestrictions();
2993  }
2994  return isset( $this->mRestrictions[$action] )
2995  ? $this->mRestrictions[$action]
2996  : [];
2997  }
2998 
3006  public function getAllRestrictions() {
3007  if ( !$this->mRestrictionsLoaded ) {
3008  $this->loadRestrictions();
3009  }
3010  return $this->mRestrictions;
3011  }
3012 
3020  public function getRestrictionExpiry( $action ) {
3021  if ( !$this->mRestrictionsLoaded ) {
3022  $this->loadRestrictions();
3023  }
3024  return isset( $this->mRestrictionsExpiry[$action] ) ? $this->mRestrictionsExpiry[$action] : false;
3025  }
3026 
3033  if ( !$this->mRestrictionsLoaded ) {
3034  $this->loadRestrictions();
3035  }
3036 
3038  }
3039 
3049  public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
3050  $dbr = wfGetDB( DB_REPLICA );
3051 
3052  $restrictionTypes = $this->getRestrictionTypes();
3053 
3054  foreach ( $restrictionTypes as $type ) {
3055  $this->mRestrictions[$type] = [];
3056  $this->mRestrictionsExpiry[$type] = 'infinity';
3057  }
3058 
3059  $this->mCascadeRestriction = false;
3060 
3061  # Backwards-compatibility: also load the restrictions from the page record (old format).
3062  if ( $oldFashionedRestrictions !== null ) {
3063  $this->mOldRestrictions = $oldFashionedRestrictions;
3064  }
3065 
3066  if ( $this->mOldRestrictions === false ) {
3067  $this->mOldRestrictions = $dbr->selectField( 'page', 'page_restrictions',
3068  [ 'page_id' => $this->getArticleID() ], __METHOD__ );
3069  }
3070 
3071  if ( $this->mOldRestrictions != '' ) {
3072  foreach ( explode( ':', trim( $this->mOldRestrictions ) ) as $restrict ) {
3073  $temp = explode( '=', trim( $restrict ) );
3074  if ( count( $temp ) == 1 ) {
3075  // old old format should be treated as edit/move restriction
3076  $this->mRestrictions['edit'] = explode( ',', trim( $temp[0] ) );
3077  $this->mRestrictions['move'] = explode( ',', trim( $temp[0] ) );
3078  } else {
3079  $restriction = trim( $temp[1] );
3080  if ( $restriction != '' ) { // some old entries are empty
3081  $this->mRestrictions[$temp[0]] = explode( ',', $restriction );
3082  }
3083  }
3084  }
3085  }
3086 
3087  if ( count( $rows ) ) {
3088  # Current system - load second to make them override.
3089  $now = wfTimestampNow();
3090 
3091  # Cycle through all the restrictions.
3092  foreach ( $rows as $row ) {
3093  // Don't take care of restrictions types that aren't allowed
3094  if ( !in_array( $row->pr_type, $restrictionTypes ) ) {
3095  continue;
3096  }
3097 
3098  $expiry = $dbr->decodeExpiry( $row->pr_expiry );
3099 
3100  // Only apply the restrictions if they haven't expired!
3101  if ( !$expiry || $expiry > $now ) {
3102  $this->mRestrictionsExpiry[$row->pr_type] = $expiry;
3103  $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) );
3104 
3105  $this->mCascadeRestriction |= $row->pr_cascade;
3106  }
3107  }
3108  }
3109 
3110  $this->mRestrictionsLoaded = true;
3111  }
3112 
3119  public function loadRestrictions( $oldFashionedRestrictions = null ) {
3120  if ( $this->mRestrictionsLoaded ) {
3121  return;
3122  }
3123 
3124  $id = $this->getArticleID();
3125  if ( $id ) {
3127  $rows = $cache->getWithSetCallback(
3128  // Page protections always leave a new null revision
3129  $cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID() ),
3130  $cache::TTL_DAY,
3131  function ( $curValue, &$ttl, array &$setOpts ) {
3132  $dbr = wfGetDB( DB_REPLICA );
3133 
3134  $setOpts += Database::getCacheSetOptions( $dbr );
3135 
3136  return iterator_to_array(
3137  $dbr->select(
3138  'page_restrictions',
3139  [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ],
3140  [ 'pr_page' => $this->getArticleID() ],
3141  __METHOD__
3142  )
3143  );
3144  }
3145  );
3146 
3147  $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
3148  } else {
3149  $title_protection = $this->getTitleProtectionInternal();
3150 
3151  if ( $title_protection ) {
3152  $now = wfTimestampNow();
3153  $expiry = wfGetDB( DB_REPLICA )->decodeExpiry( $title_protection['expiry'] );
3154 
3155  if ( !$expiry || $expiry > $now ) {
3156  // Apply the restrictions
3157  $this->mRestrictionsExpiry['create'] = $expiry;
3158  $this->mRestrictions['create'] =
3159  explode( ',', trim( $title_protection['permission'] ) );
3160  } else { // Get rid of the old restrictions
3161  $this->mTitleProtection = false;
3162  }
3163  } else {
3164  $this->mRestrictionsExpiry['create'] = 'infinity';
3165  }
3166  $this->mRestrictionsLoaded = true;
3167  }
3168  }
3169 
3174  public function flushRestrictions() {
3175  $this->mRestrictionsLoaded = false;
3176  $this->mTitleProtection = null;
3177  }
3178 
3184  static function purgeExpiredRestrictions() {
3185  if ( wfReadOnly() ) {
3186  return;
3187  }
3188 
3190  wfGetDB( DB_MASTER ),
3191  __METHOD__,
3192  function ( IDatabase $dbw, $fname ) {
3193  $config = MediaWikiServices::getInstance()->getMainConfig();
3194  $ids = $dbw->selectFieldValues(
3195  'page_restrictions',
3196  'pr_id',
3197  [ 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ],
3198  $fname,
3199  [ 'LIMIT' => $config->get( 'UpdateRowsPerQuery' ) ] // T135470
3200  );
3201  if ( $ids ) {
3202  $dbw->delete( 'page_restrictions', [ 'pr_id' => $ids ], $fname );
3203  }
3204  }
3205  ) );
3206 
3208  wfGetDB( DB_MASTER ),
3209  __METHOD__,
3210  function ( IDatabase $dbw, $fname ) {
3211  $dbw->delete(
3212  'protected_titles',
3213  [ 'pt_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ],
3214  $fname
3215  );
3216  }
3217  ) );
3218  }
3219 
3225  public function hasSubpages() {
3226  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
3227  # Duh
3228  return false;
3229  }
3230 
3231  # We dynamically add a member variable for the purpose of this method
3232  # alone to cache the result. There's no point in having it hanging
3233  # around uninitialized in every Title object; therefore we only add it
3234  # if needed and don't declare it statically.
3235  if ( $this->mHasSubpages === null ) {
3236  $this->mHasSubpages = false;
3237  $subpages = $this->getSubpages( 1 );
3238  if ( $subpages instanceof TitleArray ) {
3239  $this->mHasSubpages = (bool)$subpages->count();
3240  }
3241  }
3242 
3243  return $this->mHasSubpages;
3244  }
3245 
3253  public function getSubpages( $limit = -1 ) {
3254  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
3255  return [];
3256  }
3257 
3258  $dbr = wfGetDB( DB_REPLICA );
3259  $conds['page_namespace'] = $this->getNamespace();
3260  $conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() );
3261  $options = [];
3262  if ( $limit > -1 ) {
3263  $options['LIMIT'] = $limit;
3264  }
3266  $dbr->select( 'page',
3267  [ 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ],
3268  $conds,
3269  __METHOD__,
3270  $options
3271  )
3272  );
3273  }
3274 
3280  public function isDeleted() {
3281  if ( $this->getNamespace() < 0 ) {
3282  $n = 0;
3283  } else {
3284  $dbr = wfGetDB( DB_REPLICA );
3285 
3286  $n = $dbr->selectField( 'archive', 'COUNT(*)',
3287  [ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
3288  __METHOD__
3289  );
3290  if ( $this->getNamespace() == NS_FILE ) {
3291  $n += $dbr->selectField( 'filearchive', 'COUNT(*)',
3292  [ 'fa_name' => $this->getDBkey() ],
3293  __METHOD__
3294  );
3295  }
3296  }
3297  return (int)$n;
3298  }
3299 
3305  public function isDeletedQuick() {
3306  if ( $this->getNamespace() < 0 ) {
3307  return false;
3308  }
3309  $dbr = wfGetDB( DB_REPLICA );
3310  $deleted = (bool)$dbr->selectField( 'archive', '1',
3311  [ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
3312  __METHOD__
3313  );
3314  if ( !$deleted && $this->getNamespace() == NS_FILE ) {
3315  $deleted = (bool)$dbr->selectField( 'filearchive', '1',
3316  [ 'fa_name' => $this->getDBkey() ],
3317  __METHOD__
3318  );
3319  }
3320  return $deleted;
3321  }
3322 
3331  public function getArticleID( $flags = 0 ) {
3332  if ( $this->getNamespace() < 0 ) {
3333  $this->mArticleID = 0;
3334  return $this->mArticleID;
3335  }
3336  $linkCache = LinkCache::singleton();
3337  if ( $flags & self::GAID_FOR_UPDATE ) {
3338  $oldUpdate = $linkCache->forUpdate( true );
3339  $linkCache->clearLink( $this );
3340  $this->mArticleID = $linkCache->addLinkObj( $this );
3341  $linkCache->forUpdate( $oldUpdate );
3342  } else {
3343  if ( -1 == $this->mArticleID ) {
3344  $this->mArticleID = $linkCache->addLinkObj( $this );
3345  }
3346  }
3347  return $this->mArticleID;
3348  }
3349 
3357  public function isRedirect( $flags = 0 ) {
3358  if ( !is_null( $this->mRedirect ) ) {
3359  return $this->mRedirect;
3360  }
3361  if ( !$this->getArticleID( $flags ) ) {
3362  $this->mRedirect = false;
3363  return $this->mRedirect;
3364  }
3365 
3366  $linkCache = LinkCache::singleton();
3367  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3368  $cached = $linkCache->getGoodLinkFieldObj( $this, 'redirect' );
3369  if ( $cached === null ) {
3370  # Trust LinkCache's state over our own
3371  # LinkCache is telling us that the page doesn't exist, despite there being cached
3372  # data relating to an existing page in $this->mArticleID. Updaters should clear
3373  # LinkCache as appropriate, or use $flags = Title::GAID_FOR_UPDATE. If that flag is
3374  # set, then LinkCache will definitely be up to date here, since getArticleID() forces
3375  # LinkCache to refresh its data from the master.
3376  $this->mRedirect = false;
3377  return $this->mRedirect;
3378  }
3379 
3380  $this->mRedirect = (bool)$cached;
3381 
3382  return $this->mRedirect;
3383  }
3384 
3392  public function getLength( $flags = 0 ) {
3393  if ( $this->mLength != -1 ) {
3394  return $this->mLength;
3395  }
3396  if ( !$this->getArticleID( $flags ) ) {
3397  $this->mLength = 0;
3398  return $this->mLength;
3399  }
3400  $linkCache = LinkCache::singleton();
3401  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3402  $cached = $linkCache->getGoodLinkFieldObj( $this, 'length' );
3403  if ( $cached === null ) {
3404  # Trust LinkCache's state over our own, as for isRedirect()
3405  $this->mLength = 0;
3406  return $this->mLength;
3407  }
3408 
3409  $this->mLength = intval( $cached );
3410 
3411  return $this->mLength;
3412  }
3413 
3420  public function getLatestRevID( $flags = 0 ) {
3421  if ( !( $flags & self::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
3422  return intval( $this->mLatestID );
3423  }
3424  if ( !$this->getArticleID( $flags ) ) {
3425  $this->mLatestID = 0;
3426  return $this->mLatestID;
3427  }
3428  $linkCache = LinkCache::singleton();
3429  $linkCache->addLinkObj( $this ); # in case we already had an article ID
3430  $cached = $linkCache->getGoodLinkFieldObj( $this, 'revision' );
3431  if ( $cached === null ) {
3432  # Trust LinkCache's state over our own, as for isRedirect()
3433  $this->mLatestID = 0;
3434  return $this->mLatestID;
3435  }
3436 
3437  $this->mLatestID = intval( $cached );
3438 
3439  return $this->mLatestID;
3440  }
3441 
3452  public function resetArticleID( $newid ) {
3453  $linkCache = LinkCache::singleton();
3454  $linkCache->clearLink( $this );
3455 
3456  if ( $newid === false ) {
3457  $this->mArticleID = -1;
3458  } else {
3459  $this->mArticleID = intval( $newid );
3460  }
3461  $this->mRestrictionsLoaded = false;
3462  $this->mRestrictions = [];
3463  $this->mOldRestrictions = false;
3464  $this->mRedirect = null;
3465  $this->mLength = -1;
3466  $this->mLatestID = false;
3467  $this->mContentModel = false;
3468  $this->mEstimateRevisions = null;
3469  $this->mPageLanguage = false;
3470  $this->mDbPageLanguage = false;
3471  $this->mIsBigDeletion = null;
3472  }
3473 
3474  public static function clearCaches() {
3475  $linkCache = LinkCache::singleton();
3476  $linkCache->clear();
3477 
3479  $titleCache->clear();
3480  }
3481 
3489  public static function capitalize( $text, $ns = NS_MAIN ) {
3491 
3492  if ( MWNamespace::isCapitalized( $ns ) ) {
3493  return $wgContLang->ucfirst( $text );
3494  } else {
3495  return $text;
3496  }
3497  }
3498 
3511  private function secureAndSplit() {
3512  # Initialisation
3513  $this->mInterwiki = '';
3514  $this->mFragment = '';
3515  $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
3516 
3517  $dbkey = $this->mDbkeyform;
3518 
3519  // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
3520  // the parsing code with Title, while avoiding massive refactoring.
3521  // @todo: get rid of secureAndSplit, refactor parsing code.
3522  // @note: getTitleParser() returns a TitleParser implementation which does not have a
3523  // splitTitleString method, but the only implementation (MediaWikiTitleCodec) does
3524  $titleCodec = MediaWikiServices::getInstance()->getTitleParser();
3525  // MalformedTitleException can be thrown here
3526  $parts = $titleCodec->splitTitleString( $dbkey, $this->getDefaultNamespace() );
3527 
3528  # Fill fields
3529  $this->setFragment( '#' . $parts['fragment'] );
3530  $this->mInterwiki = $parts['interwiki'];
3531  $this->mLocalInterwiki = $parts['local_interwiki'];
3532  $this->mNamespace = $parts['namespace'];
3533  $this->mUserCaseDBKey = $parts['user_case_dbkey'];
3534 
3535  $this->mDbkeyform = $parts['dbkey'];
3536  $this->mUrlform = wfUrlencode( $this->mDbkeyform );
3537  $this->mTextform = strtr( $this->mDbkeyform, '_', ' ' );
3538 
3539  # We already know that some pages won't be in the database!
3540  if ( $this->isExternal() || $this->isSpecialPage() ) {
3541  $this->mArticleID = 0;
3542  }
3543 
3544  return true;
3545  }
3546 
3559  public function getLinksTo( $options = [], $table = 'pagelinks', $prefix = 'pl' ) {
3560  if ( count( $options ) > 0 ) {
3561  $db = wfGetDB( DB_MASTER );
3562  } else {
3563  $db = wfGetDB( DB_REPLICA );
3564  }
3565 
3566  $res = $db->select(
3567  [ 'page', $table ],
3568  self::getSelectFields(),
3569  [
3570  "{$prefix}_from=page_id",
3571  "{$prefix}_namespace" => $this->getNamespace(),
3572  "{$prefix}_title" => $this->getDBkey() ],
3573  __METHOD__,
3574  $options
3575  );
3576 
3577  $retVal = [];
3578  if ( $res->numRows() ) {
3579  $linkCache = LinkCache::singleton();
3580  foreach ( $res as $row ) {
3581  $titleObj = self::makeTitle( $row->page_namespace, $row->page_title );
3582  if ( $titleObj ) {
3583  $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
3584  $retVal[] = $titleObj;
3585  }
3586  }
3587  }
3588  return $retVal;
3589  }
3590 
3601  public function getTemplateLinksTo( $options = [] ) {
3602  return $this->getLinksTo( $options, 'templatelinks', 'tl' );
3603  }
3604 
3617  public function getLinksFrom( $options = [], $table = 'pagelinks', $prefix = 'pl' ) {
3618  $id = $this->getArticleID();
3619 
3620  # If the page doesn't exist; there can't be any link from this page
3621  if ( !$id ) {
3622  return [];
3623  }
3624 
3625  $db = wfGetDB( DB_REPLICA );
3626 
3627  $blNamespace = "{$prefix}_namespace";
3628  $blTitle = "{$prefix}_title";
3629 
3630  $res = $db->select(
3631  [ $table, 'page' ],
3632  array_merge(
3633  [ $blNamespace, $blTitle ],
3635  ),
3636  [ "{$prefix}_from" => $id ],
3637  __METHOD__,
3638  $options,
3639  [ 'page' => [
3640  'LEFT JOIN',
3641  [ "page_namespace=$blNamespace", "page_title=$blTitle" ]
3642  ] ]
3643  );
3644 
3645  $retVal = [];
3646  $linkCache = LinkCache::singleton();
3647  foreach ( $res as $row ) {
3648  if ( $row->page_id ) {
3649  $titleObj = self::newFromRow( $row );
3650  } else {
3651  $titleObj = self::makeTitle( $row->$blNamespace, $row->$blTitle );
3652  $linkCache->addBadLinkObj( $titleObj );
3653  }
3654  $retVal[] = $titleObj;
3655  }
3656 
3657  return $retVal;
3658  }
3659 
3670  public function getTemplateLinksFrom( $options = [] ) {
3671  return $this->getLinksFrom( $options, 'templatelinks', 'tl' );
3672  }
3673 
3682  public function getBrokenLinksFrom() {
3683  if ( $this->getArticleID() == 0 ) {
3684  # All links from article ID 0 are false positives
3685  return [];
3686  }
3687 
3688  $dbr = wfGetDB( DB_REPLICA );
3689  $res = $dbr->select(
3690  [ 'page', 'pagelinks' ],
3691  [ 'pl_namespace', 'pl_title' ],
3692  [
3693  'pl_from' => $this->getArticleID(),
3694  'page_namespace IS NULL'
3695  ],
3696  __METHOD__, [],
3697  [
3698  'page' => [
3699  'LEFT JOIN',
3700  [ 'pl_namespace=page_namespace', 'pl_title=page_title' ]
3701  ]
3702  ]
3703  );
3704 
3705  $retVal = [];
3706  foreach ( $res as $row ) {
3707  $retVal[] = self::makeTitle( $row->pl_namespace, $row->pl_title );
3708  }
3709  return $retVal;
3710  }
3711 
3718  public function getCdnUrls() {
3719  $urls = [
3720  $this->getInternalURL(),
3721  $this->getInternalURL( 'action=history' )
3722  ];
3723 
3724  $pageLang = $this->getPageLanguage();
3725  if ( $pageLang->hasVariants() ) {
3726  $variants = $pageLang->getVariants();
3727  foreach ( $variants as $vCode ) {
3728  $urls[] = $this->getInternalURL( $vCode );
3729  }
3730  }
3731 
3732  // If we are looking at a css/js user subpage, purge the action=raw.
3733  if ( $this->isJsSubpage() ) {
3734  $urls[] = $this->getInternalURL( 'action=raw&ctype=text/javascript' );
3735  } elseif ( $this->isCssSubpage() ) {
3736  $urls[] = $this->getInternalURL( 'action=raw&ctype=text/css' );
3737  }
3738 
3739  Hooks::run( 'TitleSquidURLs', [ $this, &$urls ] );
3740  return $urls;
3741  }
3742 
3746  public function getSquidURLs() {
3747  return $this->getCdnUrls();
3748  }
3749 
3753  public function purgeSquid() {
3755  new CdnCacheUpdate( $this->getCdnUrls() ),
3757  );
3758  }
3759 
3770  public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) {
3771  global $wgUser;
3772 
3773  if ( !( $nt instanceof Title ) ) {
3774  // Normally we'd add this to $errors, but we'll get
3775  // lots of syntax errors if $nt is not an object
3776  return [ [ 'badtitletext' ] ];
3777  }
3778 
3779  $mp = new MovePage( $this, $nt );
3780  $errors = $mp->isValidMove()->getErrorsArray();
3781  if ( $auth ) {
3782  $errors = wfMergeErrorArrays(
3783  $errors,
3784  $mp->checkPermissions( $wgUser, $reason )->getErrorsArray()
3785  );
3786  }
3787 
3788  return $errors ?: true;
3789  }
3790 
3797  protected function validateFileMoveOperation( $nt ) {
3798  global $wgUser;
3799 
3800  $errors = [];
3801 
3802  $destFile = wfLocalFile( $nt );
3803  $destFile->load( File::READ_LATEST );
3804  if ( !$wgUser->isAllowed( 'reupload-shared' )
3805  && !$destFile->exists() && wfFindFile( $nt )
3806  ) {
3807  $errors[] = [ 'file-exists-sharedrepo' ];
3808  }
3809 
3810  return $errors;
3811  }
3812 
3826  public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true,
3827  array $changeTags = []
3828  ) {
3829  global $wgUser;
3830  $err = $this->isValidMoveOperation( $nt, $auth, $reason );
3831  if ( is_array( $err ) ) {
3832  // Auto-block user's IP if the account was "hard" blocked
3833  $wgUser->spreadAnyEditBlock();
3834  return $err;
3835  }
3836  // Check suppressredirect permission
3837  if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
3838  $createRedirect = true;
3839  }
3840 
3841  $mp = new MovePage( $this, $nt );
3842  $status = $mp->move( $wgUser, $reason, $createRedirect, $changeTags );
3843  if ( $status->isOK() ) {
3844  return true;
3845  } else {
3846  return $status->getErrorsArray();
3847  }
3848  }
3849 
3864  public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true,
3865  array $changeTags = []
3866  ) {
3868  // Check permissions
3869  if ( !$this->userCan( 'move-subpages' ) ) {
3870  return [
3871  [ 'cant-move-subpages' ],
3872  ];
3873  }
3874  // Do the source and target namespaces support subpages?
3875  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
3876  return [
3877  [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $this->getNamespace() ) ],
3878  ];
3879  }
3880  if ( !MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
3881  return [
3882  [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $nt->getNamespace() ) ],
3883  ];
3884  }
3885 
3886  $subpages = $this->getSubpages( $wgMaximumMovedPages + 1 );
3887  $retval = [];
3888  $count = 0;
3889  foreach ( $subpages as $oldSubpage ) {
3890  $count++;
3891  if ( $count > $wgMaximumMovedPages ) {
3892  $retval[$oldSubpage->getPrefixedText()] = [
3893  [ 'movepage-max-pages', $wgMaximumMovedPages ],
3894  ];
3895  break;
3896  }
3897 
3898  // We don't know whether this function was called before
3899  // or after moving the root page, so check both
3900  // $this and $nt
3901  if ( $oldSubpage->getArticleID() == $this->getArticleID()
3902  || $oldSubpage->getArticleID() == $nt->getArticleID()
3903  ) {
3904  // When moving a page to a subpage of itself,
3905  // don't move it twice
3906  continue;
3907  }
3908  $newPageName = preg_replace(
3909  '#^' . preg_quote( $this->getDBkey(), '#' ) . '#',
3910  StringUtils::escapeRegexReplacement( $nt->getDBkey() ), # T23234
3911  $oldSubpage->getDBkey() );
3912  if ( $oldSubpage->isTalkPage() ) {
3913  $newNs = $nt->getTalkPage()->getNamespace();
3914  } else {
3915  $newNs = $nt->getSubjectPage()->getNamespace();
3916  }
3917  # T16385: we need makeTitleSafe because the new page names may
3918  # be longer than 255 characters.
3919  $newSubpage = self::makeTitleSafe( $newNs, $newPageName );
3920 
3921  $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect, $changeTags );
3922  if ( $success === true ) {
3923  $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
3924  } else {
3925  $retval[$oldSubpage->getPrefixedText()] = $success;
3926  }
3927  }
3928  return $retval;
3929  }
3930 
3937  public function isSingleRevRedirect() {
3939 
3940  $dbw = wfGetDB( DB_MASTER );
3941 
3942  # Is it a redirect?
3943  $fields = [ 'page_is_redirect', 'page_latest', 'page_id' ];
3944  if ( $wgContentHandlerUseDB ) {
3945  $fields[] = 'page_content_model';
3946  }
3947 
3948  $row = $dbw->selectRow( 'page',
3949  $fields,
3950  $this->pageCond(),
3951  __METHOD__,
3952  [ 'FOR UPDATE' ]
3953  );
3954  # Cache some fields we may want
3955  $this->mArticleID = $row ? intval( $row->page_id ) : 0;
3956  $this->mRedirect = $row ? (bool)$row->page_is_redirect : false;
3957  $this->mLatestID = $row ? intval( $row->page_latest ) : false;
3958  $this->mContentModel = $row && isset( $row->page_content_model )
3959  ? strval( $row->page_content_model )
3960  : false;
3961 
3962  if ( !$this->mRedirect ) {
3963  return false;
3964  }
3965  # Does the article have a history?
3966  $row = $dbw->selectField( [ 'page', 'revision' ],
3967  'rev_id',
3968  [ 'page_namespace' => $this->getNamespace(),
3969  'page_title' => $this->getDBkey(),
3970  'page_id=rev_page',
3971  'page_latest != rev_id'
3972  ],
3973  __METHOD__,
3974  [ 'FOR UPDATE' ]
3975  );
3976  # Return true if there was no history
3977  return ( $row === false );
3978  }
3979 
3988  public function isValidMoveTarget( $nt ) {
3989  # Is it an existing file?
3990  if ( $nt->getNamespace() == NS_FILE ) {
3991  $file = wfLocalFile( $nt );
3992  $file->load( File::READ_LATEST );
3993  if ( $file->exists() ) {
3994  wfDebug( __METHOD__ . ": file exists\n" );
3995  return false;
3996  }
3997  }
3998  # Is it a redirect with no history?
3999  if ( !$nt->isSingleRevRedirect() ) {
4000  wfDebug( __METHOD__ . ": not a one-rev redirect\n" );
4001  return false;
4002  }
4003  # Get the article text
4004  $rev = Revision::newFromTitle( $nt, false, Revision::READ_LATEST );
4005  if ( !is_object( $rev ) ) {
4006  return false;
4007  }
4008  $content = $rev->getContent();
4009  # Does the redirect point to the source?
4010  # Or is it a broken self-redirect, usually caused by namespace collisions?
4011  $redirTitle = $content ? $content->getRedirectTarget() : null;
4012 
4013  if ( $redirTitle ) {
4014  if ( $redirTitle->getPrefixedDBkey() != $this->getPrefixedDBkey() &&
4015  $redirTitle->getPrefixedDBkey() != $nt->getPrefixedDBkey() ) {
4016  wfDebug( __METHOD__ . ": redirect points to other page\n" );
4017  return false;
4018  } else {
4019  return true;
4020  }
4021  } else {
4022  # Fail safe (not a redirect after all. strange.)
4023  wfDebug( __METHOD__ . ": failsafe: database sais " . $nt->getPrefixedDBkey() .
4024  " is a redirect, but it doesn't contain a valid redirect.\n" );
4025  return false;
4026  }
4027  }
4028 
4036  public function getParentCategories() {
4038 
4039  $data = [];
4040 
4041  $titleKey = $this->getArticleID();
4042 
4043  if ( $titleKey === 0 ) {
4044  return $data;
4045  }
4046 
4047  $dbr = wfGetDB( DB_REPLICA );
4048 
4049  $res = $dbr->select(
4050  'categorylinks',
4051  'cl_to',
4052  [ 'cl_from' => $titleKey ],
4053  __METHOD__
4054  );
4055 
4056  if ( $res->numRows() > 0 ) {
4057  foreach ( $res as $row ) {
4058  // $data[] = Title::newFromText($wgContLang->getNsText ( NS_CATEGORY ).':'.$row->cl_to);
4059  $data[$wgContLang->getNsText( NS_CATEGORY ) . ':' . $row->cl_to] = $this->getFullText();
4060  }
4061  }
4062  return $data;
4063  }
4064 
4071  public function getParentCategoryTree( $children = [] ) {
4072  $stack = [];
4073  $parents = $this->getParentCategories();
4074 
4075  if ( $parents ) {
4076  foreach ( $parents as $parent => $current ) {
4077  if ( array_key_exists( $parent, $children ) ) {
4078  # Circular reference
4079  $stack[$parent] = [];
4080  } else {
4081  $nt = self::newFromText( $parent );
4082  if ( $nt ) {
4083  $stack[$parent] = $nt->getParentCategoryTree( $children + [ $parent => 1 ] );
4084  }
4085  }
4086  }
4087  }
4088 
4089  return $stack;
4090  }
4091 
4098  public function pageCond() {
4099  if ( $this->mArticleID > 0 ) {
4100  // PK avoids secondary lookups in InnoDB, shouldn't hurt other DBs
4101  return [ 'page_id' => $this->mArticleID ];
4102  } else {
4103  return [ 'page_namespace' => $this->mNamespace, 'page_title' => $this->mDbkeyform ];
4104  }
4105  }
4106 
4114  private function getRelativeRevisionID( $revId, $flags, $dir ) {
4115  $revId = (int)$revId;
4116  if ( $dir === 'next' ) {
4117  $op = '>';
4118  $sort = 'ASC';
4119  } elseif ( $dir === 'prev' ) {
4120  $op = '<';
4121  $sort = 'DESC';
4122  } else {
4123  throw new InvalidArgumentException( '$dir must be "next" or "prev"' );
4124  }
4125 
4126  if ( $flags & self::GAID_FOR_UPDATE ) {
4127  $db = wfGetDB( DB_MASTER );
4128  } else {
4129  $db = wfGetDB( DB_REPLICA, 'contributions' );
4130  }
4131 
4132  // Intentionally not caring if the specified revision belongs to this
4133  // page. We only care about the timestamp.
4134  $ts = $db->selectField( 'revision', 'rev_timestamp', [ 'rev_id' => $revId ], __METHOD__ );
4135  if ( $ts === false ) {
4136  $ts = $db->selectField( 'archive', 'ar_timestamp', [ 'ar_rev_id' => $revId ], __METHOD__ );
4137  if ( $ts === false ) {
4138  // Or should this throw an InvalidArgumentException or something?
4139  return false;
4140  }
4141  }
4142  $ts = $db->addQuotes( $ts );
4143 
4144  $revId = $db->selectField( 'revision', 'rev_id',
4145  [
4146  'rev_page' => $this->getArticleID( $flags ),
4147  "rev_timestamp $op $ts OR (rev_timestamp = $ts AND rev_id $op $revId)"
4148  ],
4149  __METHOD__,
4150  [
4151  'ORDER BY' => "rev_timestamp $sort, rev_id $sort",
4152  'IGNORE INDEX' => 'rev_timestamp', // Probably needed for T159319
4153  ]
4154  );
4155 
4156  if ( $revId === false ) {
4157  return false;
4158  } else {
4159  return intval( $revId );
4160  }
4161  }
4162 
4170  public function getPreviousRevisionID( $revId, $flags = 0 ) {
4171  return $this->getRelativeRevisionID( $revId, $flags, 'prev' );
4172  }
4173 
4181  public function getNextRevisionID( $revId, $flags = 0 ) {
4182  return $this->getRelativeRevisionID( $revId, $flags, 'next' );
4183  }
4184 
4191  public function getFirstRevision( $flags = 0 ) {
4192  $pageId = $this->getArticleID( $flags );
4193  if ( $pageId ) {
4195  $row = $db->selectRow( 'revision', Revision::selectFields(),
4196  [ 'rev_page' => $pageId ],
4197  __METHOD__,
4198  [
4199  'ORDER BY' => 'rev_timestamp ASC, rev_id ASC',
4200  'IGNORE INDEX' => 'rev_timestamp', // See T159319
4201  ]
4202  );
4203  if ( $row ) {
4204  return new Revision( $row );
4205  }
4206  }
4207  return null;
4208  }
4209 
4216  public function getEarliestRevTime( $flags = 0 ) {
4217  $rev = $this->getFirstRevision( $flags );
4218  return $rev ? $rev->getTimestamp() : null;
4219  }
4220 
4226  public function isNewPage() {
4227  $dbr = wfGetDB( DB_REPLICA );
4228  return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
4229  }
4230 
4236  public function isBigDeletion() {
4238 
4239  if ( !$wgDeleteRevisionsLimit ) {
4240  return false;
4241  }
4242 
4243  if ( $this->mIsBigDeletion === null ) {
4244  $dbr = wfGetDB( DB_REPLICA );
4245 
4246  $revCount = $dbr->selectRowCount(
4247  'revision',
4248  '1',
4249  [ 'rev_page' => $this->getArticleID() ],
4250  __METHOD__,
4251  [ 'LIMIT' => $wgDeleteRevisionsLimit + 1 ]
4252  );
4253 
4254  $this->mIsBigDeletion = $revCount > $wgDeleteRevisionsLimit;
4255  }
4256 
4257  return $this->mIsBigDeletion;
4258  }
4259 
4265  public function estimateRevisionCount() {
4266  if ( !$this->exists() ) {
4267  return 0;
4268  }
4269 
4270  if ( $this->mEstimateRevisions === null ) {
4271  $dbr = wfGetDB( DB_REPLICA );
4272  $this->mEstimateRevisions = $dbr->estimateRowCount( 'revision', '*',
4273  [ 'rev_page' => $this->getArticleID() ], __METHOD__ );
4274  }
4275 
4277  }
4278 
4288  public function countRevisionsBetween( $old, $new, $max = null ) {
4289  if ( !( $old instanceof Revision ) ) {
4290  $old = Revision::newFromTitle( $this, (int)$old );
4291  }
4292  if ( !( $new instanceof Revision ) ) {
4293  $new = Revision::newFromTitle( $this, (int)$new );
4294  }
4295  if ( !$old || !$new ) {
4296  return 0; // nothing to compare
4297  }
4298  $dbr = wfGetDB( DB_REPLICA );
4299  $conds = [
4300  'rev_page' => $this->getArticleID(),
4301  'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4302  'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4303  ];
4304  if ( $max !== null ) {
4305  return $dbr->selectRowCount( 'revision', '1',
4306  $conds,
4307  __METHOD__,
4308  [ 'LIMIT' => $max + 1 ] // extra to detect truncation
4309  );
4310  } else {
4311  return (int)$dbr->selectField( 'revision', 'count(*)', $conds, __METHOD__ );
4312  }
4313  }
4314 
4331  public function getAuthorsBetween( $old, $new, $limit, $options = [] ) {
4332  if ( !( $old instanceof Revision ) ) {
4333  $old = Revision::newFromTitle( $this, (int)$old );
4334  }
4335  if ( !( $new instanceof Revision ) ) {
4336  $new = Revision::newFromTitle( $this, (int)$new );
4337  }
4338  // XXX: what if Revision objects are passed in, but they don't refer to this title?
4339  // Add $old->getPage() != $new->getPage() || $old->getPage() != $this->getArticleID()
4340  // in the sanity check below?
4341  if ( !$old || !$new ) {
4342  return null; // nothing to compare
4343  }
4344  $authors = [];
4345  $old_cmp = '>';
4346  $new_cmp = '<';
4347  $options = (array)$options;
4348  if ( in_array( 'include_old', $options ) ) {
4349  $old_cmp = '>=';
4350  }
4351  if ( in_array( 'include_new', $options ) ) {
4352  $new_cmp = '<=';
4353  }
4354  if ( in_array( 'include_both', $options ) ) {
4355  $old_cmp = '>=';
4356  $new_cmp = '<=';
4357  }
4358  // No DB query needed if $old and $new are the same or successive revisions:
4359  if ( $old->getId() === $new->getId() ) {
4360  return ( $old_cmp === '>' && $new_cmp === '<' ) ?
4361  [] :
4362  [ $old->getUserText( Revision::RAW ) ];
4363  } elseif ( $old->getId() === $new->getParentId() ) {
4364  if ( $old_cmp === '>=' && $new_cmp === '<=' ) {
4365  $authors[] = $old->getUserText( Revision::RAW );
4366  if ( $old->getUserText( Revision::RAW ) != $new->getUserText( Revision::RAW ) ) {
4367  $authors[] = $new->getUserText( Revision::RAW );
4368  }
4369  } elseif ( $old_cmp === '>=' ) {
4370  $authors[] = $old->getUserText( Revision::RAW );
4371  } elseif ( $new_cmp === '<=' ) {
4372  $authors[] = $new->getUserText( Revision::RAW );
4373  }
4374  return $authors;
4375  }
4376  $dbr = wfGetDB( DB_REPLICA );
4377  $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
4378  [
4379  'rev_page' => $this->getArticleID(),
4380  "rev_timestamp $old_cmp " . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4381  "rev_timestamp $new_cmp " . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4382  ], __METHOD__,
4383  [ 'LIMIT' => $limit + 1 ] // add one so caller knows it was truncated
4384  );
4385  foreach ( $res as $row ) {
4386  $authors[] = $row->rev_user_text;
4387  }
4388  return $authors;
4389  }
4390 
4405  public function countAuthorsBetween( $old, $new, $limit, $options = [] ) {
4406  $authors = $this->getAuthorsBetween( $old, $new, $limit, $options );
4407  return $authors ? count( $authors ) : 0;
4408  }
4409 
4416  public function equals( Title $title ) {
4417  // Note: === is necessary for proper matching of number-like titles.
4418  return $this->getInterwiki() === $title->getInterwiki()
4419  && $this->getNamespace() == $title->getNamespace()
4420  && $this->getDBkey() === $title->getDBkey();
4421  }
4422 
4429  public function isSubpageOf( Title $title ) {
4430  return $this->getInterwiki() === $title->getInterwiki()
4431  && $this->getNamespace() == $title->getNamespace()
4432  && strpos( $this->getDBkey(), $title->getDBkey() . '/' ) === 0;
4433  }
4434 
4446  public function exists( $flags = 0 ) {
4447  $exists = $this->getArticleID( $flags ) != 0;
4448  Hooks::run( 'TitleExists', [ $this, &$exists ] );
4449  return $exists;
4450  }
4451 
4468  public function isAlwaysKnown() {
4469  $isKnown = null;
4470 
4481  Hooks::run( 'TitleIsAlwaysKnown', [ $this, &$isKnown ] );
4482 
4483  if ( !is_null( $isKnown ) ) {
4484  return $isKnown;
4485  }
4486 
4487  if ( $this->isExternal() ) {
4488  return true; // any interwiki link might be viewable, for all we know
4489  }
4490 
4491  switch ( $this->mNamespace ) {
4492  case NS_MEDIA:
4493  case NS_FILE:
4494  // file exists, possibly in a foreign repo
4495  return (bool)wfFindFile( $this );
4496  case NS_SPECIAL:
4497  // valid special page
4498  return SpecialPageFactory::exists( $this->getDBkey() );
4499  case NS_MAIN:
4500  // selflink, possibly with fragment
4501  return $this->mDbkeyform == '';
4502  case NS_MEDIAWIKI:
4503  // known system message
4504  return $this->hasSourceText() !== false;
4505  default:
4506  return false;
4507  }
4508  }
4509 
4521  public function isKnown() {
4522  return $this->isAlwaysKnown() || $this->exists();
4523  }
4524 
4530  public function hasSourceText() {
4531  if ( $this->exists() ) {
4532  return true;
4533  }
4534 
4535  if ( $this->mNamespace == NS_MEDIAWIKI ) {
4536  // If the page doesn't exist but is a known system message, default
4537  // message content will be displayed, same for language subpages-
4538  // Use always content language to avoid loading hundreds of languages
4539  // to get the link color.
4541  list( $name, ) = MessageCache::singleton()->figureMessage(
4542  $wgContLang->lcfirst( $this->getText() )
4543  );
4544  $message = wfMessage( $name )->inLanguage( $wgContLang )->useDatabase( false );
4545  return $message->exists();
4546  }
4547 
4548  return false;
4549  }
4550 
4556  public function getDefaultMessageText() {
4558 
4559  if ( $this->getNamespace() != NS_MEDIAWIKI ) { // Just in case
4560  return false;
4561  }
4562 
4563  list( $name, $lang ) = MessageCache::singleton()->figureMessage(
4564  $wgContLang->lcfirst( $this->getText() )
4565  );
4566  $message = wfMessage( $name )->inLanguage( $lang )->useDatabase( false );
4567 
4568  if ( $message->exists() ) {
4569  return $message->plain();
4570  } else {
4571  return false;
4572  }
4573  }
4574 
4581  public function invalidateCache( $purgeTime = null ) {
4582  if ( wfReadOnly() ) {
4583  return false;
4584  } elseif ( $this->mArticleID === 0 ) {
4585  return true; // avoid gap locking if we know it's not there
4586  }
4587 
4588  $dbw = wfGetDB( DB_MASTER );
4589  $dbw->onTransactionPreCommitOrIdle( function () {
4591  } );
4592 
4593  $conds = $this->pageCond();
4595  new AutoCommitUpdate(
4596  $dbw,
4597  __METHOD__,
4598  function ( IDatabase $dbw, $fname ) use ( $conds, $purgeTime ) {
4599  $dbTimestamp = $dbw->timestamp( $purgeTime ?: time() );
4600  $dbw->update(
4601  'page',
4602  [ 'page_touched' => $dbTimestamp ],
4603  $conds + [ 'page_touched < ' . $dbw->addQuotes( $dbTimestamp ) ],
4604  $fname
4605  );
4606  MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $this );
4607  }
4608  ),
4610  );
4611 
4612  return true;
4613  }
4614 
4620  public function touchLinks() {
4621  DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this, 'pagelinks' ) );
4622  if ( $this->getNamespace() == NS_CATEGORY ) {
4623  DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this, 'categorylinks' ) );
4624  }
4625  }
4626 
4633  public function getTouched( $db = null ) {
4634  if ( $db === null ) {
4635  $db = wfGetDB( DB_REPLICA );
4636  }
4637  $touched = $db->selectField( 'page', 'page_touched', $this->pageCond(), __METHOD__ );
4638  return $touched;
4639  }
4640 
4647  public function getNotificationTimestamp( $user = null ) {
4648  global $wgUser;
4649 
4650  // Assume current user if none given
4651  if ( !$user ) {
4652  $user = $wgUser;
4653  }
4654  // Check cache first
4655  $uid = $user->getId();
4656  if ( !$uid ) {
4657  return false;
4658  }
4659  // avoid isset here, as it'll return false for null entries
4660  if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) {
4661  return $this->mNotificationTimestamp[$uid];
4662  }
4663  // Don't cache too much!
4664  if ( count( $this->mNotificationTimestamp ) >= self::CACHE_MAX ) {
4665  $this->mNotificationTimestamp = [];
4666  }
4667 
4668  $store = MediaWikiServices::getInstance()->getWatchedItemStore();
4669  $watchedItem = $store->getWatchedItem( $user, $this );
4670  if ( $watchedItem ) {
4671  $this->mNotificationTimestamp[$uid] = $watchedItem->getNotificationTimestamp();
4672  } else {
4673  $this->mNotificationTimestamp[$uid] = false;
4674  }
4675 
4676  return $this->mNotificationTimestamp[$uid];
4677  }
4678 
4685  public function getNamespaceKey( $prepend = 'nstab-' ) {
4687  // Gets the subject namespace if this title
4688  $namespace = MWNamespace::getSubject( $this->getNamespace() );
4689  // Checks if canonical namespace name exists for namespace
4690  if ( MWNamespace::exists( $this->getNamespace() ) ) {
4691  // Uses canonical namespace name
4692  $namespaceKey = MWNamespace::getCanonicalName( $namespace );
4693  } else {
4694  // Uses text of namespace
4695  $namespaceKey = $this->getSubjectNsText();
4696  }
4697  // Makes namespace key lowercase
4698  $namespaceKey = $wgContLang->lc( $namespaceKey );
4699  // Uses main
4700  if ( $namespaceKey == '' ) {
4701  $namespaceKey = 'main';
4702  }
4703  // Changes file to image for backwards compatibility
4704  if ( $namespaceKey == 'file' ) {
4705  $namespaceKey = 'image';
4706  }
4707  return $prepend . $namespaceKey;
4708  }
4709 
4716  public function getRedirectsHere( $ns = null ) {
4717  $redirs = [];
4718 
4719  $dbr = wfGetDB( DB_REPLICA );
4720  $where = [
4721  'rd_namespace' => $this->getNamespace(),
4722  'rd_title' => $this->getDBkey(),
4723  'rd_from = page_id'
4724  ];
4725  if ( $this->isExternal() ) {
4726  $where['rd_interwiki'] = $this->getInterwiki();
4727  } else {
4728  $where[] = 'rd_interwiki = ' . $dbr->addQuotes( '' ) . ' OR rd_interwiki IS NULL';
4729  }
4730  if ( !is_null( $ns ) ) {
4731  $where['page_namespace'] = $ns;
4732  }
4733 
4734  $res = $dbr->select(
4735  [ 'redirect', 'page' ],
4736  [ 'page_namespace', 'page_title' ],
4737  $where,
4738  __METHOD__
4739  );
4740 
4741  foreach ( $res as $row ) {
4742  $redirs[] = self::newFromRow( $row );
4743  }
4744  return $redirs;
4745  }
4746 
4752  public function isValidRedirectTarget() {
4754 
4755  if ( $this->isSpecialPage() ) {
4756  // invalid redirect targets are stored in a global array, but explicitly disallow Userlogout here
4757  if ( $this->isSpecial( 'Userlogout' ) ) {
4758  return false;
4759  }
4760 
4761  foreach ( $wgInvalidRedirectTargets as $target ) {
4762  if ( $this->isSpecial( $target ) ) {
4763  return false;
4764  }
4765  }
4766  }
4767 
4768  return true;
4769  }
4770 
4776  public function getBacklinkCache() {
4777  return BacklinkCache::get( $this );
4778  }
4779 
4785  public function canUseNoindex() {
4787 
4788  $bannedNamespaces = is_null( $wgExemptFromUserRobotsControl )
4791 
4792  return !in_array( $this->mNamespace, $bannedNamespaces );
4793  }
4794 
4805  public function getCategorySortkey( $prefix = '' ) {
4806  $unprefixed = $this->getText();
4807 
4808  // Anything that uses this hook should only depend
4809  // on the Title object passed in, and should probably
4810  // tell the users to run updateCollations.php --force
4811  // in order to re-sort existing category relations.
4812  Hooks::run( 'GetDefaultSortkey', [ $this, &$unprefixed ] );
4813  if ( $prefix !== '' ) {
4814  # Separate with a line feed, so the unprefixed part is only used as
4815  # a tiebreaker when two pages have the exact same prefix.
4816  # In UCA, tab is the only character that can sort above LF
4817  # so we strip both of them from the original prefix.
4818  $prefix = strtr( $prefix, "\n\t", ' ' );
4819  return "$prefix\n$unprefixed";
4820  }
4821  return $unprefixed;
4822  }
4823 
4831  private function getDbPageLanguageCode() {
4833 
4834  // check, if the page language could be saved in the database, and if so and
4835  // the value is not requested already, lookup the page language using LinkCache
4836  if ( $wgPageLanguageUseDB && $this->mDbPageLanguage === false ) {
4837  $linkCache = LinkCache::singleton();
4838  $linkCache->addLinkObj( $this );
4839  $this->mDbPageLanguage = $linkCache->getGoodLinkFieldObj( $this, 'lang' );
4840  }
4841 
4842  return $this->mDbPageLanguage;
4843  }
4844 
4853  public function getPageLanguage() {
4855  if ( $this->isSpecialPage() ) {
4856  // special pages are in the user language
4857  return $wgLang;
4858  }
4859 
4860  // Checking if DB language is set
4861  $dbPageLanguage = $this->getDbPageLanguageCode();
4862  if ( $dbPageLanguage ) {
4863  return wfGetLangObj( $dbPageLanguage );
4864  }
4865 
4866  if ( !$this->mPageLanguage || $this->mPageLanguage[1] !== $wgLanguageCode ) {
4867  // Note that this may depend on user settings, so the cache should
4868  // be only per-request.
4869  // NOTE: ContentHandler::getPageLanguage() may need to load the
4870  // content to determine the page language!
4871  // Checking $wgLanguageCode hasn't changed for the benefit of unit
4872  // tests.
4873  $contentHandler = ContentHandler::getForTitle( $this );
4874  $langObj = $contentHandler->getPageLanguage( $this );
4875  $this->mPageLanguage = [ $langObj->getCode(), $wgLanguageCode ];
4876  } else {
4877  $langObj = wfGetLangObj( $this->mPageLanguage[0] );
4878  }
4879 
4880  return $langObj;
4881  }
4882 
4891  public function getPageViewLanguage() {
4892  global $wgLang;
4893 
4894  if ( $this->isSpecialPage() ) {
4895  // If the user chooses a variant, the content is actually
4896  // in a language whose code is the variant code.
4897  $variant = $wgLang->getPreferredVariant();
4898  if ( $wgLang->getCode() !== $variant ) {
4899  return Language::factory( $variant );
4900  }
4901 
4902  return $wgLang;
4903  }
4904 
4905  // Checking if DB language is set
4906  $dbPageLanguage = $this->getDbPageLanguageCode();
4907  if ( $dbPageLanguage ) {
4908  $pageLang = wfGetLangObj( $dbPageLanguage );
4909  $variant = $pageLang->getPreferredVariant();
4910  if ( $pageLang->getCode() !== $variant ) {
4911  $pageLang = Language::factory( $variant );
4912  }
4913 
4914  return $pageLang;
4915  }
4916 
4917  // @note Can't be cached persistently, depends on user settings.
4918  // @note ContentHandler::getPageViewLanguage() may need to load the
4919  // content to determine the page language!
4920  $contentHandler = ContentHandler::getForTitle( $this );
4921  $pageLang = $contentHandler->getPageViewLanguage( $this );
4922  return $pageLang;
4923  }
4924 
4935  public function getEditNotices( $oldid = 0 ) {
4936  $notices = [];
4937 
4938  // Optional notice for the entire namespace
4939  $editnotice_ns = 'editnotice-' . $this->getNamespace();
4940  $msg = wfMessage( $editnotice_ns );
4941  if ( $msg->exists() ) {
4942  $html = $msg->parseAsBlock();
4943  // Edit notices may have complex logic, but output nothing (T91715)
4944  if ( trim( $html ) !== '' ) {
4945  $notices[$editnotice_ns] = Html::rawElement(
4946  'div',
4947  [ 'class' => [
4948  'mw-editnotice',
4949  'mw-editnotice-namespace',
4950  Sanitizer::escapeClass( "mw-$editnotice_ns" )
4951  ] ],
4952  $html
4953  );
4954  }
4955  }
4956 
4957  if ( MWNamespace::hasSubpages( $this->getNamespace() ) ) {
4958  // Optional notice for page itself and any parent page
4959  $parts = explode( '/', $this->getDBkey() );
4960  $editnotice_base = $editnotice_ns;
4961  while ( count( $parts ) > 0 ) {
4962  $editnotice_base .= '-' . array_shift( $parts );
4963  $msg = wfMessage( $editnotice_base );
4964  if ( $msg->exists() ) {
4965  $html = $msg->parseAsBlock();
4966  if ( trim( $html ) !== '' ) {
4967  $notices[$editnotice_base] = Html::rawElement(
4968  'div',
4969  [ 'class' => [
4970  'mw-editnotice',
4971  'mw-editnotice-base',
4972  Sanitizer::escapeClass( "mw-$editnotice_base" )
4973  ] ],
4974  $html
4975  );
4976  }
4977  }
4978  }
4979  } else {
4980  // Even if there are no subpages in namespace, we still don't want "/" in MediaWiki message keys
4981  $editnoticeText = $editnotice_ns . '-' . strtr( $this->getDBkey(), '/', '-' );
4982  $msg = wfMessage( $editnoticeText );
4983  if ( $msg->exists() ) {
4984  $html = $msg->parseAsBlock();
4985  if ( trim( $html ) !== '' ) {
4986  $notices[$editnoticeText] = Html::rawElement(
4987  'div',
4988  [ 'class' => [
4989  'mw-editnotice',
4990  'mw-editnotice-page',
4991  Sanitizer::escapeClass( "mw-$editnoticeText" )
4992  ] ],
4993  $html
4994  );
4995  }
4996  }
4997  }
4998 
4999  Hooks::run( 'TitleGetEditNotices', [ $this, $oldid, &$notices ] );
5000  return $notices;
5001  }
5002 
5006  public function __sleep() {
5007  return [
5008  'mNamespace',
5009  'mDbkeyform',
5010  'mFragment',
5011  'mInterwiki',
5012  'mLocalInterwiki',
5013  'mUserCaseDBKey',
5014  'mDefaultNamespace',
5015  ];
5016  }
5017 
5018  public function __wakeup() {
5019  $this->mArticleID = ( $this->mNamespace >= 0 ) ? -1 : 0;
5020  $this->mUrlform = wfUrlencode( $this->mDbkeyform );
5021  $this->mTextform = strtr( $this->mDbkeyform, '_', ' ' );
5022  }
5023 
5024 }
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:2694
Title\canUseNoindex
canUseNoindex()
Whether the magic words INDEX and NOINDEX function for this page.
Definition: Title.php:4785
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:2782
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:2842
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:4181
Title\isBigDeletion
isBigDeletion()
Check whether the number of revisions of this page surpasses $wgDeleteRevisionsLimit.
Definition: Title.php:4236
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:2874
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:2649
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:7934
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:8387
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:4776
User\newFatalPermissionDeniedStatus
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
Definition: User.php:5523
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:3474
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:3864
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:4265
Title\countRevisionsBetween
countRevisionsBetween( $old, $new, $max=null)
Get the number of revisions between the given revision.
Definition: Title.php:4288
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:4831
$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:4647
Title\getParentCategoryTree
getParentCategoryTree( $children=[])
Get a tree of parent categories.
Definition: Title.php:4071
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:4620
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:3331
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:3746
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:3511
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:3049
$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:4556
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:236
Title\hasSourceText
hasSourceText()
Does this page have source text?
Definition: Title.php:4530
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:4791
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:2490
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:3452
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:8475
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:5018
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:4036
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:3559
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:3617
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:2860
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:3988
$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:3718
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:3826
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:2810
Title\flushRestrictions
flushRestrictions()
Flush the protection cache in this object and force reload from the database.
Definition: Title.php:3174
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:4805
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:4752
Title\deleteTitleProtection
deleteTitleProtection()
Remove any title protection due to page existing.
Definition: Title.php:2764
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:3682
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:2565
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:4716
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:4416
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:5006
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:8558
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:3601
Title\areRestrictionsCascading
areRestrictionsCascading()
Returns cascading restrictions for the current article.
Definition: Title.php:3032
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:3420
Title\areRestrictionsLoaded
areRestrictionsLoaded()
Accessor for mRestrictionsLoaded.
Definition: Title.php:2977
$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:3253
Title\isSingleRevRedirect
isSingleRevRedirect()
Checks if this page is just a one-rev redirect.
Definition: Title.php:3937
$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:4891
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:3020
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:3770
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:4429
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:4685
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:4521
$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:3797
Title\getFirstRevision
getFirstRevision( $flags=0)
Get the first revision of the page.
Definition: Title.php:4191
Title\getAllRestrictions
getAllRestrictions()
Accessor/initialisation for mRestrictions.
Definition: Title.php:3006
Title\getEarliestRevTime
getEarliestRevTime( $flags=0)
Get the oldest revision timestamp of this page.
Definition: Title.php:4216
User\isEveryoneAllowed
static isEveryoneAllowed( $right)
Check if all users may be assumed to have the given permission.
Definition: User.php:4811
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:2891
$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:2667
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:2717
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:3670
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:3357
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:2588
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:3280
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:3489
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:3753
Title\getPreviousRevisionID
getPreviousRevisionID( $revId, $flags=0)
Get the revision ID of the previous revision.
Definition: Title.php:4170
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:4853
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:4935
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:4633
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:4446
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:4468
Title\isDeletedQuick
isDeletedQuick()
Is there a version of this page in the deletion archive?
Definition: Title.php:3305
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:4226
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:4405
$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:4581
$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:2990
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:3225
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:4098
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:3184
$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:3119
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:4114
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:3392
Title\getAuthorsBetween
getAuthorsBetween( $old, $new, $limit, $options=[])
Get the authors between the given revisions or revision IDs.
Definition: Title.php:4331
Title\isWatchable
isWatchable()
Can this title be added to a user's watchlist?
Definition: Title.php:1113