MediaWiki  1.23.16
Title.php
Go to the documentation of this file.
1 <?php
35 class Title {
37  static private $titleCache = null;
38 
44  const CACHE_MAX = 1000;
45 
50  const GAID_FOR_UPDATE = 1;
51 
57  // @{
58 
59  var $mTextform = ''; // /< Text form (spaces not underscores) of the main part
60  var $mUrlform = ''; // /< URL-encoded form of the main part
61  var $mDbkeyform = ''; // /< Main part with underscores
62  var $mUserCaseDBKey; // /< DB key with the initial letter in the case specified by the user
63  var $mNamespace = NS_MAIN; // /< Namespace index, i.e. one of the NS_xxxx constants
64  var $mInterwiki = ''; // /< Interwiki prefix
65  var $mFragment = ''; // /< Title fragment (i.e. the bit after the #)
66  var $mArticleID = -1; // /< Article ID, fetched from the link cache on demand
67  var $mLatestID = false; // /< ID of most recent revision
68  var $mContentModel = false; // /< ID of the page's content model, i.e. one of the CONTENT_MODEL_XXX constants
69  private $mEstimateRevisions; // /< Estimated number of revisions; null of not loaded
70  var $mRestrictions = array(); // /< Array of groups allowed to edit this article
71  var $mOldRestrictions = false;
73  var $mCascadingRestrictions; // Caching the results of getCascadeProtectionSources
77  var $mRestrictionsLoaded = false;
78  var $mPrefixedText = null;
80  # Don't change the following default, NS_MAIN is hardcoded in several
81  # places. See bug 696.
82  # Zero except in {{transclusion}} tags
83  var $mDefaultNamespace = NS_MAIN; // /< Namespace index when there is no namespace
84  var $mWatched = null; // /< Is $wgUser watching this page? null if unfilled, accessed through userIsWatching()
85  var $mLength = -1; // /< The page length, 0 for special pages
86  var $mRedirect = null; // /< Is the article at this title a redirect?
87  var $mNotificationTimestamp = array(); // /< Associative array of user ID -> timestamp/false
88  var $mHasSubpage; // /< Whether a page has any subpages
89  private $mPageLanguage = false; // /< The (string) language code of the page's language and content code.
90  private $mTitleValue = null; // /< A corresponding TitleValue object
91  // @}
92 
101  private static function getTitleParser() {
102  global $wgContLang, $wgLocalInterwikis;
103 
104  static $titleCodec = null;
105  static $titleCodecFingerprint = null;
106 
107  // $wgContLang and $wgLocalInterwikis may change (especially while testing),
108  // make sure we are using the right one. To detect changes over the course
109  // of a request, we remember a fingerprint of the config used to create the
110  // codec singleton, and re-create it if the fingerprint doesn't match.
111  $fingerprint = spl_object_hash( $wgContLang ) . '|' . join( '+', $wgLocalInterwikis );
112 
113  if ( $fingerprint !== $titleCodecFingerprint ) {
114  $titleCodec = null;
115  }
116 
117  if ( !$titleCodec ) {
118  $titleCodec = new MediaWikiTitleCodec( $wgContLang, GenderCache::singleton(), $wgLocalInterwikis );
119  $titleCodecFingerprint = $fingerprint;
120  }
121 
122  return $titleCodec;
123  }
124 
133  private static function getTitleFormatter() {
134  //NOTE: we know that getTitleParser() returns a MediaWikiTitleCodec,
135  // which implements TitleFormatter.
136  return self::getTitleParser();
137  }
138 
142  /*protected*/ function __construct() { }
143 
152  public static function newFromDBkey( $key ) {
153  $t = new Title();
154  $t->mDbkeyform = $key;
155  if ( $t->secureAndSplit() ) {
156  return $t;
157  } else {
158  return null;
159  }
160  }
161 
169  public static function newFromTitleValue( TitleValue $titleValue ) {
170  return self::makeTitle(
171  $titleValue->getNamespace(),
172  $titleValue->getText(),
173  $titleValue->getFragment() );
174  }
175 
189  public static function newFromText( $text, $defaultNamespace = NS_MAIN ) {
190  if ( is_object( $text ) ) {
191  throw new MWException( 'Title::newFromText given an object' );
192  }
193 
195 
204  if ( $defaultNamespace == NS_MAIN && $cache->has( $text ) ) {
205  return $cache->get( $text );
206  }
207 
208  # Convert things like &eacute; &#257; or &#x3017; into normalized (bug 14952) text
209  $filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text );
210 
211  $t = new Title();
212  $t->mDbkeyform = str_replace( ' ', '_', $filteredText );
213  $t->mDefaultNamespace = intval( $defaultNamespace );
214 
215  if ( $t->secureAndSplit() ) {
216  if ( $defaultNamespace == NS_MAIN ) {
217  $cache->set( $text, $t );
218  }
219  return $t;
220  } else {
221  $ret = null;
222  return $ret;
223  }
224  }
225 
241  public static function newFromURL( $url ) {
242  $t = new Title();
243 
244  # For compatibility with old buggy URLs. "+" is usually not valid in titles,
245  # but some URLs used it as a space replacement and they still come
246  # from some external search tools.
247  if ( strpos( self::legalChars(), '+' ) === false ) {
248  $url = str_replace( '+', ' ', $url );
249  }
250 
251  $t->mDbkeyform = str_replace( ' ', '_', $url );
252  if ( $t->secureAndSplit() ) {
253  return $t;
254  } else {
255  return null;
256  }
257  }
258 
262  private static function getTitleCache() {
263  if ( self::$titleCache == null ) {
264  self::$titleCache = new MapCacheLRU( self::CACHE_MAX );
265  }
266  return self::$titleCache;
267  }
268 
275  protected static function getSelectFields() {
276  global $wgContentHandlerUseDB;
277 
278  $fields = array(
279  'page_namespace', 'page_title', 'page_id',
280  'page_len', 'page_is_redirect', 'page_latest',
281  );
282 
283  if ( $wgContentHandlerUseDB ) {
284  $fields[] = 'page_content_model';
285  }
286 
287  return $fields;
288  }
289 
297  public static function newFromID( $id, $flags = 0 ) {
299  $row = $db->selectRow(
300  'page',
301  self::getSelectFields(),
302  array( 'page_id' => $id ),
303  __METHOD__
304  );
305  if ( $row !== false ) {
306  $title = Title::newFromRow( $row );
307  } else {
308  $title = null;
309  }
310  return $title;
311  }
312 
319  public static function newFromIDs( $ids ) {
320  if ( !count( $ids ) ) {
321  return array();
322  }
323  $dbr = wfGetDB( DB_SLAVE );
324 
325  $res = $dbr->select(
326  'page',
327  self::getSelectFields(),
328  array( 'page_id' => $ids ),
329  __METHOD__
330  );
331 
332  $titles = array();
333  foreach ( $res as $row ) {
334  $titles[] = Title::newFromRow( $row );
335  }
336  return $titles;
337  }
338 
345  public static function newFromRow( $row ) {
346  $t = self::makeTitle( $row->page_namespace, $row->page_title );
347  $t->loadFromRow( $row );
348  return $t;
349  }
350 
357  public function loadFromRow( $row ) {
358  if ( $row ) { // page found
359  if ( isset( $row->page_id ) ) {
360  $this->mArticleID = (int)$row->page_id;
361  }
362  if ( isset( $row->page_len ) ) {
363  $this->mLength = (int)$row->page_len;
364  }
365  if ( isset( $row->page_is_redirect ) ) {
366  $this->mRedirect = (bool)$row->page_is_redirect;
367  }
368  if ( isset( $row->page_latest ) ) {
369  $this->mLatestID = (int)$row->page_latest;
370  }
371  if ( isset( $row->page_content_model ) ) {
372  $this->mContentModel = strval( $row->page_content_model );
373  } else {
374  $this->mContentModel = false; # initialized lazily in getContentModel()
375  }
376  } else { // page not found
377  $this->mArticleID = 0;
378  $this->mLength = 0;
379  $this->mRedirect = false;
380  $this->mLatestID = 0;
381  $this->mContentModel = false; # initialized lazily in getContentModel()
382  }
383  }
384 
398  public static function &makeTitle( $ns, $title, $fragment = '', $interwiki = '' ) {
399  $t = new Title();
400  $t->mInterwiki = $interwiki;
401  $t->mFragment = $fragment;
402  $t->mNamespace = $ns = intval( $ns );
403  $t->mDbkeyform = str_replace( ' ', '_', $title );
404  $t->mArticleID = ( $ns >= 0 ) ? -1 : 0;
405  $t->mUrlform = wfUrlencode( $t->mDbkeyform );
406  $t->mTextform = str_replace( '_', ' ', $title );
407  $t->mContentModel = false; # initialized lazily in getContentModel()
408  return $t;
409  }
410 
422  public static function makeTitleSafe( $ns, $title, $fragment = '', $interwiki = '' ) {
423  if ( !MWNamespace::exists( $ns ) ) {
424  return null;
425  }
426 
427  $t = new Title();
428  $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki );
429  if ( $t->secureAndSplit() ) {
430  return $t;
431  } else {
432  return null;
433  }
434  }
435 
441  public static function newMainPage() {
442  $title = Title::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
443  // Don't give fatal errors if the message is broken
444  if ( !$title ) {
445  $title = Title::newFromText( 'Main Page' );
446  }
447  return $title;
448  }
449 
460  public static function newFromRedirect( $text ) {
461  ContentHandler::deprecated( __METHOD__, '1.21' );
462 
463  $content = ContentHandler::makeContent( $text, null, CONTENT_MODEL_WIKITEXT );
464  return $content->getRedirectTarget();
465  }
466 
477  public static function newFromRedirectRecurse( $text ) {
478  ContentHandler::deprecated( __METHOD__, '1.21' );
479 
480  $content = ContentHandler::makeContent( $text, null, CONTENT_MODEL_WIKITEXT );
481  return $content->getUltimateRedirectTarget();
482  }
483 
494  public static function newFromRedirectArray( $text ) {
495  ContentHandler::deprecated( __METHOD__, '1.21' );
496 
497  $content = ContentHandler::makeContent( $text, null, CONTENT_MODEL_WIKITEXT );
498  return $content->getRedirectChain();
499  }
500 
507  public static function nameOf( $id ) {
508  $dbr = wfGetDB( DB_SLAVE );
509 
510  $s = $dbr->selectRow(
511  'page',
512  array( 'page_namespace', 'page_title' ),
513  array( 'page_id' => $id ),
514  __METHOD__
515  );
516  if ( $s === false ) {
517  return null;
518  }
519 
520  $n = self::makeName( $s->page_namespace, $s->page_title );
521  return $n;
522  }
523 
529  public static function legalChars() {
530  global $wgLegalTitleChars;
531  return $wgLegalTitleChars;
532  }
533 
543  static function getTitleInvalidRegex() {
544  static $rxTc = false;
545  if ( !$rxTc ) {
546  # Matching titles will be held as illegal.
547  $rxTc = '/' .
548  # Any character not allowed is forbidden...
549  '[^' . self::legalChars() . ']' .
550  # URL percent encoding sequences interfere with the ability
551  # to round-trip titles -- you can't link to them consistently.
552  '|%[0-9A-Fa-f]{2}' .
553  # XML/HTML character references produce similar issues.
554  '|&[A-Za-z0-9\x80-\xff]+;' .
555  '|&#[0-9]+;' .
556  '|&#x[0-9A-Fa-f]+;' .
557  '/S';
558  }
559 
560  return $rxTc;
561  }
562 
572  public static function convertByteClassToUnicodeClass( $byteClass ) {
573  $length = strlen( $byteClass );
574  // Input token queue
575  $x0 = $x1 = $x2 = '';
576  // Decoded queue
577  $d0 = $d1 = $d2 = '';
578  // Decoded integer codepoints
579  $ord0 = $ord1 = $ord2 = 0;
580  // Re-encoded queue
581  $r0 = $r1 = $r2 = '';
582  // Output
583  $out = '';
584  // Flags
585  $allowUnicode = false;
586  for ( $pos = 0; $pos < $length; $pos++ ) {
587  // Shift the queues down
588  $x2 = $x1;
589  $x1 = $x0;
590  $d2 = $d1;
591  $d1 = $d0;
592  $ord2 = $ord1;
593  $ord1 = $ord0;
594  $r2 = $r1;
595  $r1 = $r0;
596  // Load the current input token and decoded values
597  $inChar = $byteClass[$pos];
598  if ( $inChar == '\\' ) {
599  if ( preg_match( '/x([0-9a-fA-F]{2})/A', $byteClass, $m, 0, $pos + 1 ) ) {
600  $x0 = $inChar . $m[0];
601  $d0 = chr( hexdec( $m[1] ) );
602  $pos += strlen( $m[0] );
603  } elseif ( preg_match( '/[0-7]{3}/A', $byteClass, $m, 0, $pos + 1 ) ) {
604  $x0 = $inChar . $m[0];
605  $d0 = chr( octdec( $m[0] ) );
606  $pos += strlen( $m[0] );
607  } elseif ( $pos + 1 >= $length ) {
608  $x0 = $d0 = '\\';
609  } else {
610  $d0 = $byteClass[$pos + 1];
611  $x0 = $inChar . $d0;
612  $pos += 1;
613  }
614  } else {
615  $x0 = $d0 = $inChar;
616  }
617  $ord0 = ord( $d0 );
618  // Load the current re-encoded value
619  if ( $ord0 < 32 || $ord0 == 0x7f ) {
620  $r0 = sprintf( '\x%02x', $ord0 );
621  } elseif ( $ord0 >= 0x80 ) {
622  // Allow unicode if a single high-bit character appears
623  $r0 = sprintf( '\x%02x', $ord0 );
624  $allowUnicode = true;
625  } elseif ( strpos( '-\\[]^', $d0 ) !== false ) {
626  $r0 = '\\' . $d0;
627  } else {
628  $r0 = $d0;
629  }
630  // Do the output
631  if ( $x0 !== '' && $x1 === '-' && $x2 !== '' ) {
632  // Range
633  if ( $ord2 > $ord0 ) {
634  // Empty range
635  } elseif ( $ord0 >= 0x80 ) {
636  // Unicode range
637  $allowUnicode = true;
638  if ( $ord2 < 0x80 ) {
639  // Keep the non-unicode section of the range
640  $out .= "$r2-\\x7F";
641  }
642  } else {
643  // Normal range
644  $out .= "$r2-$r0";
645  }
646  // Reset state to the initial value
647  $x0 = $x1 = $d0 = $d1 = $r0 = $r1 = '';
648  } elseif ( $ord2 < 0x80 ) {
649  // ASCII character
650  $out .= $r2;
651  }
652  }
653  if ( $ord1 < 0x80 ) {
654  $out .= $r1;
655  }
656  if ( $ord0 < 0x80 ) {
657  $out .= $r0;
658  }
659  if ( $allowUnicode ) {
660  $out .= '\u0080-\uFFFF';
661  }
662  return $out;
663  }
664 
673  public static function indexTitle( $ns, $title ) {
675 
676  $lc = SearchEngine::legalSearchChars() . '&#;';
677  $t = $wgContLang->normalizeForSearch( $title );
678  $t = preg_replace( "/[^{$lc}]+/", ' ', $t );
679  $t = $wgContLang->lc( $t );
680 
681  # Handle 's, s'
682  $t = preg_replace( "/([{$lc}]+)'s( |$)/", "\\1 \\1's ", $t );
683  $t = preg_replace( "/([{$lc}]+)s'( |$)/", "\\1s ", $t );
684 
685  $t = preg_replace( "/\\s+/", ' ', $t );
686 
687  if ( $ns == NS_FILE ) {
688  $t = preg_replace( "/ (png|gif|jpg|jpeg|ogg)$/", "", $t );
689  }
690  return trim( $t );
691  }
692 
702  public static function makeName( $ns, $title, $fragment = '', $interwiki = '' ) {
704 
705  $namespace = $wgContLang->getNsText( $ns );
706  $name = $namespace == '' ? $title : "$namespace:$title";
707  if ( strval( $interwiki ) != '' ) {
708  $name = "$interwiki:$name";
709  }
710  if ( strval( $fragment ) != '' ) {
711  $name .= '#' . $fragment;
712  }
713  return $name;
714  }
715 
722  static function escapeFragmentForURL( $fragment ) {
723  # Note that we don't urlencode the fragment. urlencoded Unicode
724  # fragments appear not to work in IE (at least up to 7) or in at least
725  # one version of Opera 9.x. The W3C validator, for one, doesn't seem
726  # to care if they aren't encoded.
727  return Sanitizer::escapeId( $fragment, 'noninitial' );
728  }
729 
738  public static function compare( $a, $b ) {
739  if ( $a->getNamespace() == $b->getNamespace() ) {
740  return strcmp( $a->getText(), $b->getText() );
741  } else {
742  return $a->getNamespace() - $b->getNamespace();
743  }
744  }
745 
752  public function isLocal() {
753  if ( $this->isExternal() ) {
754  $iw = Interwiki::fetch( $this->mInterwiki );
755  if ( $iw ) {
756  return $iw->isLocal();
757  }
758  }
759  return true;
760  }
761 
767  public function isExternal() {
768  return $this->mInterwiki !== '';
769  }
770 
778  public function getInterwiki() {
779  return $this->mInterwiki;
780  }
781 
788  public function isTrans() {
789  if ( !$this->isExternal() ) {
790  return false;
791  }
792 
793  return Interwiki::fetch( $this->mInterwiki )->isTranscludable();
794  }
795 
801  public function getTransWikiID() {
802  if ( !$this->isExternal() ) {
803  return false;
804  }
805 
806  return Interwiki::fetch( $this->mInterwiki )->getWikiID();
807  }
808 
818  public function getTitleValue() {
819  if ( $this->mTitleValue === null ) {
820  try {
821  $this->mTitleValue = new TitleValue(
822  $this->getNamespace(),
823  $this->getDBkey(),
824  $this->getFragment() );
825  } catch ( InvalidArgumentException $ex ) {
826  wfDebug( __METHOD__ . ': Can\'t create a TitleValue for [[' .
827  $this->getPrefixedText() . ']]: ' . $ex->getMessage() . "\n" );
828  }
829  }
830 
831  return $this->mTitleValue;
832  }
833 
839  public function getText() {
840  return $this->mTextform;
841  }
842 
848  public function getPartialURL() {
849  return $this->mUrlform;
850  }
851 
857  public function getDBkey() {
858  return $this->mDbkeyform;
859  }
860 
866  function getUserCaseDBKey() {
867  if ( !is_null( $this->mUserCaseDBKey ) ) {
868  return $this->mUserCaseDBKey;
869  } else {
870  // If created via makeTitle(), $this->mUserCaseDBKey is not set.
871  return $this->mDbkeyform;
872  }
873  }
874 
880  public function getNamespace() {
881  return $this->mNamespace;
882  }
883 
890  public function getContentModel() {
891  if ( !$this->mContentModel ) {
892  $linkCache = LinkCache::singleton();
893  $this->mContentModel = $linkCache->getGoodLinkFieldObj( $this, 'model' );
894  }
895 
896  if ( !$this->mContentModel ) {
897  $this->mContentModel = ContentHandler::getDefaultModelFor( $this );
898  }
899 
900  if ( !$this->mContentModel ) {
901  throw new MWException( 'Failed to determine content model!' );
902  }
903 
904  return $this->mContentModel;
905  }
906 
913  public function hasContentModel( $id ) {
914  return $this->getContentModel() == $id;
915  }
916 
922  public function getNsText() {
923  if ( $this->isExternal() ) {
924  // This probably shouldn't even happen. ohh man, oh yuck.
925  // But for interwiki transclusion it sometimes does.
926  // Shit. Shit shit shit.
927  //
928  // Use the canonical namespaces if possible to try to
929  // resolve a foreign namespace.
930  if ( MWNamespace::exists( $this->mNamespace ) ) {
931  return MWNamespace::getCanonicalName( $this->mNamespace );
932  }
933  }
934 
935  try {
936  $formatter = $this->getTitleFormatter();
937  return $formatter->getNamespaceName( $this->mNamespace, $this->mDbkeyform );
938  } catch ( InvalidArgumentException $ex ) {
939  wfDebug( __METHOD__ . ': ' . $ex->getMessage() . "\n" );
940  return false;
941  }
942  }
943 
949  public function getSubjectNsText() {
951  return $wgContLang->getNsText( MWNamespace::getSubject( $this->mNamespace ) );
952  }
953 
959  public function getTalkNsText() {
961  return $wgContLang->getNsText( MWNamespace::getTalk( $this->mNamespace ) );
962  }
963 
969  public function canTalk() {
970  return MWNamespace::canTalk( $this->mNamespace );
971  }
972 
979  public function canExist() {
980  return $this->mNamespace >= NS_MAIN;
981  }
982 
988  public function isWatchable() {
989  return !$this->isExternal() && MWNamespace::isWatchable( $this->getNamespace() );
990  }
991 
997  public function isSpecialPage() {
998  return $this->getNamespace() == NS_SPECIAL;
999  }
1000 
1007  public function isSpecial( $name ) {
1008  if ( $this->isSpecialPage() ) {
1009  list( $thisName, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $this->getDBkey() );
1010  if ( $name == $thisName ) {
1011  return true;
1012  }
1013  }
1014  return false;
1015  }
1016 
1023  public function fixSpecialName() {
1024  if ( $this->isSpecialPage() ) {
1025  list( $canonicalName, $par ) = SpecialPageFactory::resolveAlias( $this->mDbkeyform );
1026  if ( $canonicalName ) {
1027  $localName = SpecialPageFactory::getLocalNameFor( $canonicalName, $par );
1028  if ( $localName != $this->mDbkeyform ) {
1029  return Title::makeTitle( NS_SPECIAL, $localName );
1030  }
1031  }
1032  }
1033  return $this;
1034  }
1035 
1046  public function inNamespace( $ns ) {
1047  return MWNamespace::equals( $this->getNamespace(), $ns );
1048  }
1049 
1057  public function inNamespaces( /* ... */ ) {
1058  $namespaces = func_get_args();
1059  if ( count( $namespaces ) > 0 && is_array( $namespaces[0] ) ) {
1060  $namespaces = $namespaces[0];
1061  }
1062 
1063  foreach ( $namespaces as $ns ) {
1064  if ( $this->inNamespace( $ns ) ) {
1065  return true;
1066  }
1067  }
1068 
1069  return false;
1070  }
1071 
1085  public function hasSubjectNamespace( $ns ) {
1086  return MWNamespace::subjectEquals( $this->getNamespace(), $ns );
1087  }
1088 
1096  public function isContentPage() {
1097  return MWNamespace::isContent( $this->getNamespace() );
1098  }
1099 
1106  public function isMovable() {
1107  if ( !MWNamespace::isMovable( $this->getNamespace() ) || $this->isExternal() ) {
1108  // Interwiki title or immovable namespace. Hooks don't get to override here
1109  return false;
1110  }
1111 
1112  $result = true;
1113  wfRunHooks( 'TitleIsMovable', array( $this, &$result ) );
1114  return $result;
1115  }
1116 
1127  public function isMainPage() {
1128  return $this->equals( Title::newMainPage() );
1129  }
1130 
1136  public function isSubpage() {
1137  return MWNamespace::hasSubpages( $this->mNamespace )
1138  ? strpos( $this->getText(), '/' ) !== false
1139  : false;
1140  }
1141 
1147  public function isConversionTable() {
1148  // @todo ConversionTable should become a separate content model.
1149 
1150  return $this->getNamespace() == NS_MEDIAWIKI &&
1151  strpos( $this->getText(), 'Conversiontable/' ) === 0;
1152  }
1153 
1159  public function isWikitextPage() {
1160  return $this->hasContentModel( CONTENT_MODEL_WIKITEXT );
1161  }
1162 
1174  public function isCssOrJsPage() {
1175  $isCssOrJsPage = NS_MEDIAWIKI == $this->mNamespace
1176  && ( $this->hasContentModel( CONTENT_MODEL_CSS )
1178 
1179  #NOTE: this hook is also called in ContentHandler::getDefaultModel. It's called here again to make sure
1180  # hook functions can force this method to return true even outside the mediawiki namespace.
1181 
1182  wfRunHooks( 'TitleIsCssOrJsPage', array( $this, &$isCssOrJsPage ) );
1183 
1184  return $isCssOrJsPage;
1185  }
1186 
1191  public function isCssJsSubpage() {
1192  return ( NS_USER == $this->mNamespace && $this->isSubpage()
1193  && ( $this->hasContentModel( CONTENT_MODEL_CSS )
1194  || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
1195  }
1196 
1202  public function getSkinFromCssJsSubpage() {
1203  $subpage = explode( '/', $this->mTextform );
1204  $subpage = $subpage[count( $subpage ) - 1];
1205  $lastdot = strrpos( $subpage, '.' );
1206  if ( $lastdot === false ) {
1207  return $subpage; # Never happens: only called for names ending in '.css' or '.js'
1208  }
1209  return substr( $subpage, 0, $lastdot );
1210  }
1211 
1217  public function isCssSubpage() {
1218  return ( NS_USER == $this->mNamespace && $this->isSubpage()
1219  && $this->hasContentModel( CONTENT_MODEL_CSS ) );
1220  }
1221 
1227  public function isJsSubpage() {
1228  return ( NS_USER == $this->mNamespace && $this->isSubpage()
1230  }
1231 
1237  public function isTalkPage() {
1238  return MWNamespace::isTalk( $this->getNamespace() );
1239  }
1240 
1246  public function getTalkPage() {
1247  return Title::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
1248  }
1249 
1256  public function getSubjectPage() {
1257  // Is this the same title?
1258  $subjectNS = MWNamespace::getSubject( $this->getNamespace() );
1259  if ( $this->getNamespace() == $subjectNS ) {
1260  return $this;
1261  }
1262  return Title::makeTitle( $subjectNS, $this->getDBkey() );
1263  }
1264 
1270  public function getDefaultNamespace() {
1271  return $this->mDefaultNamespace;
1272  }
1273 
1280  public function getIndexTitle() {
1281  return Title::indexTitle( $this->mNamespace, $this->mTextform );
1282  }
1283 
1291  public function getFragment() {
1292  return $this->mFragment;
1293  }
1294 
1301  public function hasFragment() {
1302  return $this->mFragment !== '';
1303  }
1304 
1309  public function getFragmentForURL() {
1310  if ( !$this->hasFragment() ) {
1311  return '';
1312  } else {
1313  return '#' . Title::escapeFragmentForURL( $this->getFragment() );
1314  }
1315  }
1316 
1327  public function setFragment( $fragment ) {
1328  $this->mFragment = str_replace( '_', ' ', substr( $fragment, 1 ) );
1329  }
1330 
1339  private function prefix( $name ) {
1340  $p = '';
1341  if ( $this->isExternal() ) {
1342  $p = $this->mInterwiki . ':';
1343  }
1344 
1345  if ( 0 != $this->mNamespace ) {
1346  $p .= $this->getNsText() . ':';
1347  }
1348  return $p . $name;
1349  }
1350 
1357  public function getPrefixedDBkey() {
1358  $s = $this->prefix( $this->mDbkeyform );
1359  $s = str_replace( ' ', '_', $s );
1360  return $s;
1361  }
1362 
1369  public function getPrefixedText() {
1370  if ( $this->mPrefixedText === null ) {
1371  $s = $this->prefix( $this->mTextform );
1372  $s = str_replace( '_', ' ', $s );
1373  $this->mPrefixedText = $s;
1374  }
1375  return $this->mPrefixedText;
1376  }
1377 
1383  public function __toString() {
1384  return $this->getPrefixedText();
1385  }
1386 
1393  public function getFullText() {
1394  $text = $this->getPrefixedText();
1395  if ( $this->hasFragment() ) {
1396  $text .= '#' . $this->getFragment();
1397  }
1398  return $text;
1399  }
1400 
1413  public function getRootText() {
1414  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1415  return $this->getText();
1416  }
1417 
1418  return strtok( $this->getText(), '/' );
1419  }
1420 
1433  public function getRootTitle() {
1434  return Title::makeTitle( $this->getNamespace(), $this->getRootText() );
1435  }
1436 
1448  public function getBaseText() {
1449  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1450  return $this->getText();
1451  }
1452 
1453  $parts = explode( '/', $this->getText() );
1454  # Don't discard the real title if there's no subpage involved
1455  if ( count( $parts ) > 1 ) {
1456  unset( $parts[count( $parts ) - 1] );
1457  }
1458  return implode( '/', $parts );
1459  }
1460 
1473  public function getBaseTitle() {
1474  return Title::makeTitle( $this->getNamespace(), $this->getBaseText() );
1475  }
1476 
1488  public function getSubpageText() {
1489  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
1490  return $this->mTextform;
1491  }
1492  $parts = explode( '/', $this->mTextform );
1493  return $parts[count( $parts ) - 1];
1494  }
1495 
1509  public function getSubpage( $text ) {
1510  return Title::makeTitleSafe( $this->getNamespace(), $this->getText() . '/' . $text );
1511  }
1512 
1520  public function getEscapedText() {
1521  wfDeprecated( __METHOD__, '1.19' );
1522  return htmlspecialchars( $this->getPrefixedText() );
1523  }
1524 
1530  public function getSubpageUrlForm() {
1531  $text = $this->getSubpageText();
1532  $text = wfUrlencode( str_replace( ' ', '_', $text ) );
1533  return $text;
1534  }
1535 
1541  public function getPrefixedURL() {
1542  $s = $this->prefix( $this->mDbkeyform );
1543  $s = wfUrlencode( str_replace( ' ', '_', $s ) );
1544  return $s;
1545  }
1546 
1560  private static function fixUrlQueryArgs( $query, $query2 = false ) {
1561  if ( $query2 !== false ) {
1562  wfDeprecated( "Title::get{Canonical,Full,Link,Local,Internal}URL " .
1563  "method called with a second parameter is deprecated. Add your " .
1564  "parameter to an array passed as the first parameter.", "1.19" );
1565  }
1566  if ( is_array( $query ) ) {
1567  $query = wfArrayToCgi( $query );
1568  }
1569  if ( $query2 ) {
1570  if ( is_string( $query2 ) ) {
1571  // $query2 is a string, we will consider this to be
1572  // a deprecated $variant argument and add it to the query
1573  $query2 = wfArrayToCgi( array( 'variant' => $query2 ) );
1574  } else {
1575  $query2 = wfArrayToCgi( $query2 );
1576  }
1577  // If we have $query content add a & to it first
1578  if ( $query ) {
1579  $query .= '&';
1580  }
1581  // Now append the queries together
1582  $query .= $query2;
1583  }
1584  return $query;
1585  }
1586 
1598  public function getFullURL( $query = '', $query2 = false, $proto = PROTO_RELATIVE ) {
1599  $query = self::fixUrlQueryArgs( $query, $query2 );
1600 
1601  # Hand off all the decisions on urls to getLocalURL
1602  $url = $this->getLocalURL( $query );
1603 
1604  # Expand the url to make it a full url. Note that getLocalURL has the
1605  # potential to output full urls for a variety of reasons, so we use
1606  # wfExpandUrl instead of simply prepending $wgServer
1607  $url = wfExpandUrl( $url, $proto );
1608 
1609  # Finally, add the fragment.
1610  $url .= $this->getFragmentForURL();
1611 
1612  wfRunHooks( 'GetFullURL', array( &$this, &$url, $query ) );
1613  return $url;
1614  }
1615 
1632  public function getFullUrlForRedirect( $query = '', $proto = PROTO_CURRENT ) {
1633  $target = $this;
1634  if ( $this->isExternal() ) {
1635  $target = SpecialPage::getTitleFor(
1636  'GoToInterwiki',
1637  $this->getPrefixedDBKey()
1638  );
1639  }
1640  return $target->getFullUrl( $query, false, $proto );
1641  }
1642 
1664  public function getLocalURL( $query = '', $query2 = false ) {
1665  global $wgArticlePath, $wgScript, $wgServer, $wgRequest;
1666 
1667  $query = self::fixUrlQueryArgs( $query, $query2 );
1668 
1669  $interwiki = Interwiki::fetch( $this->mInterwiki );
1670  if ( $interwiki ) {
1671  $namespace = $this->getNsText();
1672  if ( $namespace != '' ) {
1673  # Can this actually happen? Interwikis shouldn't be parsed.
1674  # Yes! It can in interwiki transclusion. But... it probably shouldn't.
1675  $namespace .= ':';
1676  }
1677  $url = $interwiki->getURL( $namespace . $this->getDBkey() );
1678  $url = wfAppendQuery( $url, $query );
1679  } else {
1680  $dbkey = wfUrlencode( $this->getPrefixedDBkey() );
1681  if ( $query == '' ) {
1682  $url = str_replace( '$1', $dbkey, $wgArticlePath );
1683  wfRunHooks( 'GetLocalURL::Article', array( &$this, &$url ) );
1684  } else {
1685  global $wgVariantArticlePath, $wgActionPaths, $wgContLang;
1686  $url = false;
1687  $matches = array();
1688 
1689  if ( !empty( $wgActionPaths )
1690  && preg_match( '/^(.*&|)action=([^&]*)(&(.*)|)$/', $query, $matches )
1691  ) {
1692  $action = urldecode( $matches[2] );
1693  if ( isset( $wgActionPaths[$action] ) ) {
1694  $query = $matches[1];
1695  if ( isset( $matches[4] ) ) {
1696  $query .= $matches[4];
1697  }
1698  $url = str_replace( '$1', $dbkey, $wgActionPaths[$action] );
1699  if ( $query != '' ) {
1700  $url = wfAppendQuery( $url, $query );
1701  }
1702  }
1703  }
1704 
1705  if ( $url === false
1706  && $wgVariantArticlePath
1707  && $wgContLang->getCode() === $this->getPageLanguage()->getCode()
1708  && $this->getPageLanguage()->hasVariants()
1709  && preg_match( '/^variant=([^&]*)$/', $query, $matches )
1710  ) {
1711  $variant = urldecode( $matches[1] );
1712  if ( $this->getPageLanguage()->hasVariant( $variant ) ) {
1713  // Only do the variant replacement if the given variant is a valid
1714  // variant for the page's language.
1715  $url = str_replace( '$2', urlencode( $variant ), $wgVariantArticlePath );
1716  $url = str_replace( '$1', $dbkey, $url );
1717  }
1718  }
1719 
1720  if ( $url === false ) {
1721  if ( $query == '-' ) {
1722  $query = '';
1723  }
1724  $url = "{$wgScript}?title={$dbkey}&{$query}";
1725  }
1726  }
1727 
1728  wfRunHooks( 'GetLocalURL::Internal', array( &$this, &$url, $query ) );
1729 
1730  // @todo FIXME: This causes breakage in various places when we
1731  // actually expected a local URL and end up with dupe prefixes.
1732  if ( $wgRequest->getVal( 'action' ) == 'render' ) {
1733  $url = $wgServer . $url;
1734  }
1735  }
1736  wfRunHooks( 'GetLocalURL', array( &$this, &$url, $query ) );
1737  return $url;
1738  }
1739 
1756  public function getLinkURL( $query = '', $query2 = false, $proto = PROTO_RELATIVE ) {
1757  wfProfileIn( __METHOD__ );
1758  if ( $this->isExternal() || $proto !== PROTO_RELATIVE ) {
1759  $ret = $this->getFullURL( $query, $query2, $proto );
1760  } elseif ( $this->getPrefixedText() === '' && $this->hasFragment() ) {
1761  $ret = $this->getFragmentForURL();
1762  } else {
1763  $ret = $this->getLocalURL( $query, $query2 ) . $this->getFragmentForURL();
1764  }
1765  wfProfileOut( __METHOD__ );
1766  return $ret;
1767  }
1768 
1779  public function escapeLocalURL( $query = '', $query2 = false ) {
1780  wfDeprecated( __METHOD__, '1.19' );
1781  return htmlspecialchars( $this->getLocalURL( $query, $query2 ) );
1782  }
1783 
1792  public function escapeFullURL( $query = '', $query2 = false ) {
1793  wfDeprecated( __METHOD__, '1.19' );
1794  return htmlspecialchars( $this->getFullURL( $query, $query2 ) );
1795  }
1796 
1809  public function getInternalURL( $query = '', $query2 = false ) {
1810  global $wgInternalServer, $wgServer;
1811  $query = self::fixUrlQueryArgs( $query, $query2 );
1812  $server = $wgInternalServer !== false ? $wgInternalServer : $wgServer;
1813  $url = wfExpandUrl( $server . $this->getLocalURL( $query ), PROTO_HTTP );
1814  wfRunHooks( 'GetInternalURL', array( &$this, &$url, $query ) );
1815  return $url;
1816  }
1817 
1829  public function getCanonicalURL( $query = '', $query2 = false ) {
1830  $query = self::fixUrlQueryArgs( $query, $query2 );
1831  $url = wfExpandUrl( $this->getLocalURL( $query ) . $this->getFragmentForURL(), PROTO_CANONICAL );
1832  wfRunHooks( 'GetCanonicalURL', array( &$this, &$url, $query ) );
1833  return $url;
1834  }
1835 
1844  public function escapeCanonicalURL( $query = '', $query2 = false ) {
1845  wfDeprecated( __METHOD__, '1.19' );
1846  return htmlspecialchars( $this->getCanonicalURL( $query, $query2 ) );
1847  }
1848 
1855  public function getEditURL() {
1856  if ( $this->isExternal() ) {
1857  return '';
1858  }
1859  $s = $this->getLocalURL( 'action=edit' );
1860 
1861  return $s;
1862  }
1863 
1870  public function userIsWatching() {
1871  global $wgUser;
1872 
1873  if ( is_null( $this->mWatched ) ) {
1874  if ( NS_SPECIAL == $this->mNamespace || !$wgUser->isLoggedIn() ) {
1875  $this->mWatched = false;
1876  } else {
1877  $this->mWatched = $wgUser->isWatched( $this );
1878  }
1879  }
1880  return $this->mWatched;
1881  }
1882 
1889  public function userCanRead() {
1890  wfDeprecated( __METHOD__, '1.19' );
1891  return $this->userCan( 'read' );
1892  }
1893 
1909  public function quickUserCan( $action, $user = null ) {
1910  return $this->userCan( $action, $user, false );
1911  }
1912 
1923  public function userCan( $action, $user = null, $doExpensiveQueries = true ) {
1924  if ( !$user instanceof User ) {
1925  global $wgUser;
1926  $user = $wgUser;
1927  }
1928  return !count( $this->getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries, true ) );
1929  }
1930 
1944  public function getUserPermissionsErrors( $action, $user, $doExpensiveQueries = true, $ignoreErrors = array() ) {
1945  $errors = $this->getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries );
1946 
1947  // Remove the errors being ignored.
1948  foreach ( $errors as $index => $error ) {
1949  $error_key = is_array( $error ) ? $error[0] : $error;
1950 
1951  if ( in_array( $error_key, $ignoreErrors ) ) {
1952  unset( $errors[$index] );
1953  }
1954  }
1955 
1956  return $errors;
1957  }
1958 
1970  private function checkQuickPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
1971  if ( !wfRunHooks( 'TitleQuickPermissions', array( $this, $user, $action, &$errors, $doExpensiveQueries, $short ) ) ) {
1972  return $errors;
1973  }
1974 
1975  if ( $action == 'create' ) {
1976  if (
1977  ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
1978  ( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) )
1979  ) {
1980  $errors[] = $user->isAnon() ? array( 'nocreatetext' ) : array( 'nocreate-loggedin' );
1981  }
1982  } elseif ( $action == 'move' ) {
1983  if ( !$user->isAllowed( 'move-rootuserpages' )
1984  && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
1985  // Show user page-specific message only if the user can move other pages
1986  $errors[] = array( 'cant-move-user-page' );
1987  }
1988 
1989  // Check if user is allowed to move files if it's a file
1990  if ( $this->mNamespace == NS_FILE && !$user->isAllowed( 'movefile' ) ) {
1991  $errors[] = array( 'movenotallowedfile' );
1992  }
1993 
1994  if ( !$user->isAllowed( 'move' ) ) {
1995  // User can't move anything
1996  $userCanMove = User::groupHasPermission( 'user', 'move' );
1997  $autoconfirmedCanMove = User::groupHasPermission( 'autoconfirmed', 'move' );
1998  if ( $user->isAnon() && ( $userCanMove || $autoconfirmedCanMove ) ) {
1999  // custom message if logged-in users without any special rights can move
2000  $errors[] = array( 'movenologintext' );
2001  } else {
2002  $errors[] = array( 'movenotallowed' );
2003  }
2004  }
2005  } elseif ( $action == 'move-target' ) {
2006  if ( !$user->isAllowed( 'move' ) ) {
2007  // User can't move anything
2008  $errors[] = array( 'movenotallowed' );
2009  } elseif ( !$user->isAllowed( 'move-rootuserpages' )
2010  && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
2011  // Show user page-specific message only if the user can move other pages
2012  $errors[] = array( 'cant-move-to-user-page' );
2013  }
2014  } elseif ( !$user->isAllowed( $action ) ) {
2015  $errors[] = $this->missingPermissionError( $action, $short );
2016  }
2017 
2018  return $errors;
2019  }
2020 
2029  private function resultToError( $errors, $result ) {
2030  if ( is_array( $result ) && count( $result ) && !is_array( $result[0] ) ) {
2031  // A single array representing an error
2032  $errors[] = $result;
2033  } elseif ( is_array( $result ) && is_array( $result[0] ) ) {
2034  // A nested array representing multiple errors
2035  $errors = array_merge( $errors, $result );
2036  } elseif ( $result !== '' && is_string( $result ) ) {
2037  // A string representing a message-id
2038  $errors[] = array( $result );
2039  } elseif ( $result === false ) {
2040  // a generic "We don't want them to do that"
2041  $errors[] = array( 'badaccess-group0' );
2042  }
2043  return $errors;
2044  }
2045 
2057  private function checkPermissionHooks( $action, $user, $errors, $doExpensiveQueries, $short ) {
2058  // Use getUserPermissionsErrors instead
2059  $result = '';
2060  if ( !wfRunHooks( 'userCan', array( &$this, &$user, $action, &$result ) ) ) {
2061  return $result ? array() : array( array( 'badaccess-group0' ) );
2062  }
2063  // Check getUserPermissionsErrors hook
2064  if ( !wfRunHooks( 'getUserPermissionsErrors', array( &$this, &$user, $action, &$result ) ) ) {
2065  $errors = $this->resultToError( $errors, $result );
2066  }
2067  // Check getUserPermissionsErrorsExpensive hook
2068  if (
2069  $doExpensiveQueries
2070  && !( $short && count( $errors ) > 0 )
2071  && !wfRunHooks( 'getUserPermissionsErrorsExpensive', array( &$this, &$user, $action, &$result ) )
2072  ) {
2073  $errors = $this->resultToError( $errors, $result );
2074  }
2075 
2076  return $errors;
2077  }
2078 
2090  private function checkSpecialsAndNSPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2091  # Only 'createaccount' can be performed on special pages,
2092  # which don't actually exist in the DB.
2093  if ( NS_SPECIAL == $this->mNamespace && $action !== 'createaccount' ) {
2094  $errors[] = array( 'ns-specialprotected' );
2095  }
2096 
2097  # Check $wgNamespaceProtection for restricted namespaces
2098  if ( $this->isNamespaceProtected( $user ) ) {
2099  $ns = $this->mNamespace == NS_MAIN ?
2100  wfMessage( 'nstab-main' )->text() : $this->getNsText();
2101  $errors[] = $this->mNamespace == NS_MEDIAWIKI ?
2102  array( 'protectedinterface' ) : array( 'namespaceprotected', $ns );
2103  }
2104 
2105  return $errors;
2106  }
2107 
2119  private function checkCSSandJSPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2120  # Protect css/js subpages of user pages
2121  # XXX: this might be better using restrictions
2122  # XXX: right 'editusercssjs' is deprecated, for backward compatibility only
2123  if ( $action != 'patrol' && !$user->isAllowed( 'editusercssjs' ) ) {
2124  if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
2125  if ( $this->isCssSubpage() && !$user->isAllowedAny( 'editmyusercss', 'editusercss' ) ) {
2126  $errors[] = array( 'mycustomcssprotected' );
2127  } elseif ( $this->isJsSubpage() && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' ) ) {
2128  $errors[] = array( 'mycustomjsprotected' );
2129  }
2130  } else {
2131  if ( $this->isCssSubpage() && !$user->isAllowed( 'editusercss' ) ) {
2132  $errors[] = array( 'customcssprotected' );
2133  } elseif ( $this->isJsSubpage() && !$user->isAllowed( 'edituserjs' ) ) {
2134  $errors[] = array( 'customjsprotected' );
2135  }
2136  }
2137  }
2138 
2139  return $errors;
2140  }
2141 
2155  private function checkPageRestrictions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2156  foreach ( $this->getRestrictions( $action ) as $right ) {
2157  // Backwards compatibility, rewrite sysop -> editprotected
2158  if ( $right == 'sysop' ) {
2159  $right = 'editprotected';
2160  }
2161  // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
2162  if ( $right == 'autoconfirmed' ) {
2163  $right = 'editsemiprotected';
2164  }
2165  if ( $right == '' ) {
2166  continue;
2167  }
2168  if ( !$user->isAllowed( $right ) ) {
2169  $errors[] = array( 'protectedpagetext', $right );
2170  } elseif ( $this->mCascadeRestriction && !$user->isAllowed( 'protect' ) ) {
2171  $errors[] = array( 'protectedpagetext', 'protect' );
2172  }
2173  }
2174 
2175  return $errors;
2176  }
2177 
2189  private function checkCascadingSourcesRestrictions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2190  if ( $doExpensiveQueries && !$this->isCssJsSubpage() ) {
2191  # We /could/ use the protection level on the source page, but it's
2192  # fairly ugly as we have to establish a precedence hierarchy for pages
2193  # included by multiple cascade-protected pages. So just restrict
2194  # it to people with 'protect' permission, as they could remove the
2195  # protection anyway.
2196  list( $cascadingSources, $restrictions ) = $this->getCascadeProtectionSources();
2197  # Cascading protection depends on more than this page...
2198  # Several cascading protected pages may include this page...
2199  # Check each cascading level
2200  # This is only for protection restrictions, not for all actions
2201  if ( isset( $restrictions[$action] ) ) {
2202  foreach ( $restrictions[$action] as $right ) {
2203  // Backwards compatibility, rewrite sysop -> editprotected
2204  if ( $right == 'sysop' ) {
2205  $right = 'editprotected';
2206  }
2207  // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
2208  if ( $right == 'autoconfirmed' ) {
2209  $right = 'editsemiprotected';
2210  }
2211  if ( $right != '' && !$user->isAllowedAll( 'protect', $right ) ) {
2212  $pages = '';
2213  foreach ( $cascadingSources as $page ) {
2214  $pages .= '* [[:' . $page->getPrefixedText() . "]]\n";
2215  }
2216  $errors[] = array( 'cascadeprotected', count( $cascadingSources ), $pages );
2217  }
2218  }
2219  }
2220  }
2221 
2222  return $errors;
2223  }
2224 
2236  private function checkActionPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2237  global $wgDeleteRevisionsLimit, $wgLang;
2238 
2239  if ( $action == 'protect' ) {
2240  if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $doExpensiveQueries, true ) ) ) {
2241  // If they can't edit, they shouldn't protect.
2242  $errors[] = array( 'protect-cantedit' );
2243  }
2244  } elseif ( $action == 'create' ) {
2245  $title_protection = $this->getTitleProtection();
2246  if ( $title_protection ) {
2247  if ( $title_protection['pt_create_perm'] == 'sysop' ) {
2248  $title_protection['pt_create_perm'] = 'editprotected'; // B/C
2249  }
2250  if ( $title_protection['pt_create_perm'] == 'autoconfirmed' ) {
2251  $title_protection['pt_create_perm'] = 'editsemiprotected'; // B/C
2252  }
2253  if ( $title_protection['pt_create_perm'] == ''
2254  || !$user->isAllowed( $title_protection['pt_create_perm'] )
2255  ) {
2256  $errors[] = array( 'titleprotected', User::whoIs( $title_protection['pt_user'] ), $title_protection['pt_reason'] );
2257  }
2258  }
2259  } elseif ( $action == 'move' ) {
2260  // Check for immobile pages
2261  if ( !MWNamespace::isMovable( $this->mNamespace ) ) {
2262  // Specific message for this case
2263  $errors[] = array( 'immobile-source-namespace', $this->getNsText() );
2264  } elseif ( !$this->isMovable() ) {
2265  // Less specific message for rarer cases
2266  $errors[] = array( 'immobile-source-page' );
2267  }
2268  } elseif ( $action == 'move-target' ) {
2269  if ( !MWNamespace::isMovable( $this->mNamespace ) ) {
2270  $errors[] = array( 'immobile-target-namespace', $this->getNsText() );
2271  } elseif ( !$this->isMovable() ) {
2272  $errors[] = array( 'immobile-target-page' );
2273  }
2274  } elseif ( $action == 'delete' ) {
2275  if ( $doExpensiveQueries && $wgDeleteRevisionsLimit
2276  && !$this->userCan( 'bigdelete', $user ) && $this->isBigDeletion()
2277  ) {
2278  $errors[] = array( 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) );
2279  }
2280  } elseif ( $action === 'undelete' ) {
2281  if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $doExpensiveQueries, true ) ) ) {
2282  // Undeleting implies editing
2283  $errors[] = [ 'undelete-cantedit' ];
2284  }
2285  if ( !$this->exists()
2286  && count( $this->getUserPermissionsErrorsInternal( 'create', $user, $doExpensiveQueries, true ) )
2287  ) {
2288  // Undeleting where nothing currently exists implies creating
2289  $errors[] = [ 'undelete-cantcreate' ];
2290  }
2291  }
2292  return $errors;
2293  }
2294 
2306  private function checkUserBlock( $action, $user, $errors, $doExpensiveQueries, $short ) {
2307  global $wgEmailConfirmToEdit, $wgBlockDisablesLogin;
2308  // Account creation blocks handled at userlogin.
2309  // Unblocking handled in SpecialUnblock
2310  if ( !$doExpensiveQueries || in_array( $action, array( 'createaccount', 'unblock' ) ) ) {
2311  return $errors;
2312  }
2313 
2314  // Optimize for a very common case
2315  if ( $action === 'read' && !$wgBlockDisablesLogin ) {
2316  return $errors;
2317  }
2318 
2319 
2320  if ( $wgEmailConfirmToEdit && !$user->isEmailConfirmed() ) {
2321  $errors[] = array( 'confirmedittext' );
2322  }
2323 
2324  if ( ( $action == 'edit' || $action == 'create' ) && !$user->isBlockedFrom( $this ) ) {
2325  // Don't block the user from editing their own talk page unless they've been
2326  // explicitly blocked from that too.
2327  } elseif ( $user->isBlocked() && $user->mBlock->prevents( $action ) !== false ) {
2328  // @todo FIXME: Pass the relevant context into this function.
2329  $errors[] = $user->getBlock()->getPermissionsError( RequestContext::getMain() );
2330  }
2331 
2332  return $errors;
2333  }
2334 
2346  private function checkReadPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2347  global $wgWhitelistRead, $wgWhitelistReadRegexp;
2348 
2349  $whitelisted = false;
2350  if ( User::isEveryoneAllowed( 'read' ) ) {
2351  # Shortcut for public wikis, allows skipping quite a bit of code
2352  $whitelisted = true;
2353  } elseif ( $user->isAllowed( 'read' ) ) {
2354  # If the user is allowed to read pages, he is allowed to read all pages
2355  $whitelisted = true;
2356  } elseif ( $this->isSpecial( 'Userlogin' )
2357  || $this->isSpecial( 'ChangePassword' )
2358  || $this->isSpecial( 'PasswordReset' )
2359  ) {
2360  # Always grant access to the login page.
2361  # Even anons need to be able to log in.
2362  $whitelisted = true;
2363  } elseif ( is_array( $wgWhitelistRead ) && count( $wgWhitelistRead ) ) {
2364  # Time to check the whitelist
2365  # Only do these checks is there's something to check against
2366  $name = $this->getPrefixedText();
2367  $dbName = $this->getPrefixedDBkey();
2368 
2369  // Check for explicit whitelisting with and without underscores
2370  if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) {
2371  $whitelisted = true;
2372  } elseif ( $this->getNamespace() == NS_MAIN ) {
2373  # Old settings might have the title prefixed with
2374  # a colon for main-namespace pages
2375  if ( in_array( ':' . $name, $wgWhitelistRead ) ) {
2376  $whitelisted = true;
2377  }
2378  } elseif ( $this->isSpecialPage() ) {
2379  # If it's a special page, ditch the subpage bit and check again
2380  $name = $this->getDBkey();
2381  list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name );
2382  if ( $name ) {
2383  $pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
2384  if ( in_array( $pure, $wgWhitelistRead, true ) ) {
2385  $whitelisted = true;
2386  }
2387  }
2388  }
2389  }
2390 
2391  if ( !$whitelisted && is_array( $wgWhitelistReadRegexp ) && !empty( $wgWhitelistReadRegexp ) ) {
2392  $name = $this->getPrefixedText();
2393  // Check for regex whitelisting
2394  foreach ( $wgWhitelistReadRegexp as $listItem ) {
2395  if ( preg_match( $listItem, $name ) ) {
2396  $whitelisted = true;
2397  break;
2398  }
2399  }
2400  }
2401 
2402  if ( !$whitelisted ) {
2403  # If the title is not whitelisted, give extensions a chance to do so...
2404  wfRunHooks( 'TitleReadWhitelist', array( $this, $user, &$whitelisted ) );
2405  if ( !$whitelisted ) {
2406  $errors[] = $this->missingPermissionError( $action, $short );
2407  }
2408  }
2409 
2410  return $errors;
2411  }
2412 
2421  private function missingPermissionError( $action, $short ) {
2422  // We avoid expensive display logic for quickUserCan's and such
2423  if ( $short ) {
2424  return array( 'badaccess-group0' );
2425  }
2426 
2427  $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
2428  User::getGroupsWithPermission( $action ) );
2429 
2430  if ( count( $groups ) ) {
2431  global $wgLang;
2432  return array(
2433  'badaccess-groups',
2434  $wgLang->commaList( $groups ),
2435  count( $groups )
2436  );
2437  } else {
2438  return array( 'badaccess-group0' );
2439  }
2440  }
2441 
2453  protected function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries = true, $short = false ) {
2454  wfProfileIn( __METHOD__ );
2455 
2456  # Read has special handling
2457  if ( $action == 'read' ) {
2458  $checks = array(
2459  'checkPermissionHooks',
2460  'checkReadPermissions',
2461  'checkUserBlock', // for wgBlockDisablesLogin
2462  );
2463  } else {
2464  $checks = array(
2465  'checkQuickPermissions',
2466  'checkPermissionHooks',
2467  'checkSpecialsAndNSPermissions',
2468  'checkCSSandJSPermissions',
2469  'checkPageRestrictions',
2470  'checkCascadingSourcesRestrictions',
2471  'checkActionPermissions',
2472  'checkUserBlock'
2473  );
2474  }
2475 
2476  $errors = array();
2477  while ( count( $checks ) > 0 &&
2478  !( $short && count( $errors ) > 0 ) ) {
2479  $method = array_shift( $checks );
2480  $errors = $this->$method( $action, $user, $errors, $doExpensiveQueries, $short );
2481  }
2482 
2483  wfProfileOut( __METHOD__ );
2484  return $errors;
2485  }
2486 
2494  public static function getFilteredRestrictionTypes( $exists = true ) {
2495  global $wgRestrictionTypes;
2496  $types = $wgRestrictionTypes;
2497  if ( $exists ) {
2498  # Remove the create restriction for existing titles
2499  $types = array_diff( $types, array( 'create' ) );
2500  } else {
2501  # Only the create and upload restrictions apply to non-existing titles
2502  $types = array_intersect( $types, array( 'create', 'upload' ) );
2503  }
2504  return $types;
2505  }
2506 
2512  public function getRestrictionTypes() {
2513  if ( $this->isSpecialPage() ) {
2514  return array();
2515  }
2516 
2517  $types = self::getFilteredRestrictionTypes( $this->exists() );
2518 
2519  if ( $this->getNamespace() != NS_FILE ) {
2520  # Remove the upload restriction for non-file titles
2521  $types = array_diff( $types, array( 'upload' ) );
2522  }
2523 
2524  wfRunHooks( 'TitleGetRestrictionTypes', array( $this, &$types ) );
2525 
2526  wfDebug( __METHOD__ . ': applicable restrictions to [[' .
2527  $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" );
2528 
2529  return $types;
2530  }
2531 
2539  private function getTitleProtection() {
2540  // Can't protect pages in special namespaces
2541  if ( $this->getNamespace() < 0 ) {
2542  return false;
2543  }
2544 
2545  // Can't protect pages that exist.
2546  if ( $this->exists() ) {
2547  return false;
2548  }
2549 
2550  if ( !isset( $this->mTitleProtection ) ) {
2551  $dbr = wfGetDB( DB_SLAVE );
2552  $res = $dbr->select(
2553  'protected_titles',
2554  array( 'pt_user', 'pt_reason', 'pt_expiry', 'pt_create_perm' ),
2555  array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ),
2556  __METHOD__
2557  );
2558 
2559  // fetchRow returns false if there are no rows.
2560  $this->mTitleProtection = $dbr->fetchRow( $res );
2561  }
2562  return $this->mTitleProtection;
2563  }
2564 
2574  public function updateTitleProtection( $create_perm, $reason, $expiry ) {
2575  wfDeprecated( __METHOD__, '1.19' );
2576 
2577  global $wgUser;
2578 
2579  $limit = array( 'create' => $create_perm );
2580  $expiry = array( 'create' => $expiry );
2581 
2582  $page = WikiPage::factory( $this );
2583  $cascade = false;
2584  $status = $page->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $wgUser );
2585 
2586  return $status->isOK();
2587  }
2588 
2592  public function deleteTitleProtection() {
2593  $dbw = wfGetDB( DB_MASTER );
2594 
2595  $dbw->delete(
2596  'protected_titles',
2597  array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ),
2598  __METHOD__
2599  );
2600  $this->mTitleProtection = false;
2601  }
2602 
2610  public function isSemiProtected( $action = 'edit' ) {
2611  global $wgSemiprotectedRestrictionLevels;
2612 
2613  $restrictions = $this->getRestrictions( $action );
2614  $semi = $wgSemiprotectedRestrictionLevels;
2615  if ( !$restrictions || !$semi ) {
2616  // Not protected, or all protection is full protection
2617  return false;
2618  }
2619 
2620  // Remap autoconfirmed to editsemiprotected for BC
2621  foreach ( array_keys( $semi, 'autoconfirmed' ) as $key ) {
2622  $semi[$key] = 'editsemiprotected';
2623  }
2624  foreach ( array_keys( $restrictions, 'autoconfirmed' ) as $key ) {
2625  $restrictions[$key] = 'editsemiprotected';
2626  }
2627 
2628  return !array_diff( $restrictions, $semi );
2629  }
2630 
2638  public function isProtected( $action = '' ) {
2639  global $wgRestrictionLevels;
2640 
2641  $restrictionTypes = $this->getRestrictionTypes();
2642 
2643  # Special pages have inherent protection
2644  if ( $this->isSpecialPage() ) {
2645  return true;
2646  }
2647 
2648  # Check regular protection levels
2649  foreach ( $restrictionTypes as $type ) {
2650  if ( $action == $type || $action == '' ) {
2651  $r = $this->getRestrictions( $type );
2652  foreach ( $wgRestrictionLevels as $level ) {
2653  if ( in_array( $level, $r ) && $level != '' ) {
2654  return true;
2655  }
2656  }
2657  }
2658  }
2659 
2660  return false;
2661  }
2662 
2670  public function isNamespaceProtected( User $user ) {
2672 
2673  if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) {
2674  foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) {
2675  if ( $right != '' && !$user->isAllowed( $right ) ) {
2676  return true;
2677  }
2678  }
2679  }
2680  return false;
2681  }
2682 
2688  public function isCascadeProtected() {
2689  list( $sources, /* $restrictions */ ) = $this->getCascadeProtectionSources( false );
2690  return ( $sources > 0 );
2691  }
2692 
2702  public function areCascadeProtectionSourcesLoaded( $getPages = true ) {
2703  return $getPages ? isset( $this->mCascadeSources ) : isset( $this->mHasCascadingRestrictions );
2704  }
2705 
2716  public function getCascadeProtectionSources( $getPages = true ) {
2718  $pagerestrictions = array();
2719 
2720  if ( isset( $this->mCascadeSources ) && $getPages ) {
2721  return array( $this->mCascadeSources, $this->mCascadingRestrictions );
2722  } elseif ( isset( $this->mHasCascadingRestrictions ) && !$getPages ) {
2723  return array( $this->mHasCascadingRestrictions, $pagerestrictions );
2724  }
2725 
2726  wfProfileIn( __METHOD__ );
2727 
2728  $dbr = wfGetDB( DB_SLAVE );
2729 
2730  if ( $this->getNamespace() == NS_FILE ) {
2731  $tables = array( 'imagelinks', 'page_restrictions' );
2732  $where_clauses = array(
2733  'il_to' => $this->getDBkey(),
2734  'il_from=pr_page',
2735  'pr_cascade' => 1
2736  );
2737  } else {
2738  $tables = array( 'templatelinks', 'page_restrictions' );
2739  $where_clauses = array(
2740  'tl_namespace' => $this->getNamespace(),
2741  'tl_title' => $this->getDBkey(),
2742  'tl_from=pr_page',
2743  'pr_cascade' => 1
2744  );
2745  }
2746 
2747  if ( $getPages ) {
2748  $cols = array( 'pr_page', 'page_namespace', 'page_title',
2749  'pr_expiry', 'pr_type', 'pr_level' );
2750  $where_clauses[] = 'page_id=pr_page';
2751  $tables[] = 'page';
2752  } else {
2753  $cols = array( 'pr_expiry' );
2754  }
2755 
2756  $res = $dbr->select( $tables, $cols, $where_clauses, __METHOD__ );
2757 
2758  $sources = $getPages ? array() : false;
2759  $now = wfTimestampNow();
2760  $purgeExpired = false;
2761 
2762  foreach ( $res as $row ) {
2763  $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW );
2764  if ( $expiry > $now ) {
2765  if ( $getPages ) {
2766  $page_id = $row->pr_page;
2767  $page_ns = $row->page_namespace;
2768  $page_title = $row->page_title;
2769  $sources[$page_id] = Title::makeTitle( $page_ns, $page_title );
2770  # Add groups needed for each restriction type if its not already there
2771  # Make sure this restriction type still exists
2772 
2773  if ( !isset( $pagerestrictions[$row->pr_type] ) ) {
2774  $pagerestrictions[$row->pr_type] = array();
2775  }
2776 
2777  if (
2778  isset( $pagerestrictions[$row->pr_type] )
2779  && !in_array( $row->pr_level, $pagerestrictions[$row->pr_type] )
2780  ) {
2781  $pagerestrictions[$row->pr_type][] = $row->pr_level;
2782  }
2783  } else {
2784  $sources = true;
2785  }
2786  } else {
2787  // Trigger lazy purge of expired restrictions from the db
2788  $purgeExpired = true;
2789  }
2790  }
2791  if ( $purgeExpired ) {
2793  }
2794 
2795  if ( $getPages ) {
2796  $this->mCascadeSources = $sources;
2797  $this->mCascadingRestrictions = $pagerestrictions;
2798  } else {
2799  $this->mHasCascadingRestrictions = $sources;
2800  }
2801 
2802  wfProfileOut( __METHOD__ );
2803  return array( $sources, $pagerestrictions );
2804  }
2805 
2813  public function areRestrictionsLoaded() {
2815  }
2816 
2823  public function getRestrictions( $action ) {
2824  if ( !$this->mRestrictionsLoaded ) {
2825  $this->loadRestrictions();
2826  }
2827  return isset( $this->mRestrictions[$action] )
2828  ? $this->mRestrictions[$action]
2829  : array();
2830  }
2831 
2840  public function getAllRestrictions() {
2841  if ( !$this->mRestrictionsLoaded ) {
2842  $this->loadRestrictions();
2843  }
2844  return $this->mRestrictions;
2845  }
2846 
2854  public function getRestrictionExpiry( $action ) {
2855  if ( !$this->mRestrictionsLoaded ) {
2856  $this->loadRestrictions();
2857  }
2858  return isset( $this->mRestrictionsExpiry[$action] ) ? $this->mRestrictionsExpiry[$action] : false;
2859  }
2860 
2867  if ( !$this->mRestrictionsLoaded ) {
2868  $this->loadRestrictions();
2869  }
2870 
2872  }
2873 
2881  private function loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions = null ) {
2882  $rows = array();
2883 
2884  foreach ( $res as $row ) {
2885  $rows[] = $row;
2886  }
2887 
2888  $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
2889  }
2890 
2900  public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
2902  $dbr = wfGetDB( DB_SLAVE );
2903 
2904  $restrictionTypes = $this->getRestrictionTypes();
2905 
2906  foreach ( $restrictionTypes as $type ) {
2907  $this->mRestrictions[$type] = array();
2908  $this->mRestrictionsExpiry[$type] = $wgContLang->formatExpiry( '', TS_MW );
2909  }
2910 
2911  $this->mCascadeRestriction = false;
2912 
2913  # Backwards-compatibility: also load the restrictions from the page record (old format).
2914 
2915  if ( $oldFashionedRestrictions === null ) {
2916  $oldFashionedRestrictions = $dbr->selectField( 'page', 'page_restrictions',
2917  array( 'page_id' => $this->getArticleID() ), __METHOD__ );
2918  }
2919 
2920  if ( $oldFashionedRestrictions != '' ) {
2921 
2922  foreach ( explode( ':', trim( $oldFashionedRestrictions ) ) as $restrict ) {
2923  $temp = explode( '=', trim( $restrict ) );
2924  if ( count( $temp ) == 1 ) {
2925  // old old format should be treated as edit/move restriction
2926  $this->mRestrictions['edit'] = explode( ',', trim( $temp[0] ) );
2927  $this->mRestrictions['move'] = explode( ',', trim( $temp[0] ) );
2928  } else {
2929  $restriction = trim( $temp[1] );
2930  if ( $restriction != '' ) { //some old entries are empty
2931  $this->mRestrictions[$temp[0]] = explode( ',', $restriction );
2932  }
2933  }
2934  }
2935 
2936  $this->mOldRestrictions = true;
2937 
2938  }
2939 
2940  if ( count( $rows ) ) {
2941  # Current system - load second to make them override.
2942  $now = wfTimestampNow();
2943  $purgeExpired = false;
2944 
2945  # Cycle through all the restrictions.
2946  foreach ( $rows as $row ) {
2947 
2948  // Don't take care of restrictions types that aren't allowed
2949  if ( !in_array( $row->pr_type, $restrictionTypes ) ) {
2950  continue;
2951  }
2952 
2953  // This code should be refactored, now that it's being used more generally,
2954  // But I don't really see any harm in leaving it in Block for now -werdna
2955  $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW );
2956 
2957  // Only apply the restrictions if they haven't expired!
2958  if ( !$expiry || $expiry > $now ) {
2959  $this->mRestrictionsExpiry[$row->pr_type] = $expiry;
2960  $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) );
2961 
2962  $this->mCascadeRestriction |= $row->pr_cascade;
2963  } else {
2964  // Trigger a lazy purge of expired restrictions
2965  $purgeExpired = true;
2966  }
2967  }
2968 
2969  if ( $purgeExpired ) {
2971  }
2972  }
2973 
2974  $this->mRestrictionsLoaded = true;
2975  }
2976 
2983  public function loadRestrictions( $oldFashionedRestrictions = null ) {
2985  if ( !$this->mRestrictionsLoaded ) {
2986  if ( $this->exists() ) {
2987  $dbr = wfGetDB( DB_SLAVE );
2988 
2989  $res = $dbr->select(
2990  'page_restrictions',
2991  array( 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ),
2992  array( 'pr_page' => $this->getArticleID() ),
2993  __METHOD__
2994  );
2995 
2996  $this->loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions );
2997  } else {
2998  $title_protection = $this->getTitleProtection();
2999 
3000  if ( $title_protection ) {
3001  $now = wfTimestampNow();
3002  $expiry = $wgContLang->formatExpiry( $title_protection['pt_expiry'], TS_MW );
3003 
3004  if ( !$expiry || $expiry > $now ) {
3005  // Apply the restrictions
3006  $this->mRestrictionsExpiry['create'] = $expiry;
3007  $this->mRestrictions['create'] = explode( ',', trim( $title_protection['pt_create_perm'] ) );
3008  } else { // Get rid of the old restrictions
3010  $this->mTitleProtection = false;
3011  }
3012  } else {
3013  $this->mRestrictionsExpiry['create'] = $wgContLang->formatExpiry( '', TS_MW );
3014  }
3015  $this->mRestrictionsLoaded = true;
3016  }
3017  }
3018  }
3019 
3024  public function flushRestrictions() {
3025  $this->mRestrictionsLoaded = false;
3026  $this->mTitleProtection = null;
3027  }
3028 
3032  static function purgeExpiredRestrictions() {
3033  if ( wfReadOnly() ) {
3034  return;
3035  }
3036 
3037  $method = __METHOD__;
3038  $dbw = wfGetDB( DB_MASTER );
3039  $dbw->onTransactionIdle( function() use ( $dbw, $method ) {
3040  $dbw->delete(
3041  'page_restrictions',
3042  array( 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
3043  $method
3044  );
3045  $dbw->delete(
3046  'protected_titles',
3047  array( 'pt_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
3048  $method
3049  );
3050  } );
3051  }
3052 
3058  public function hasSubpages() {
3059  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
3060  # Duh
3061  return false;
3062  }
3063 
3064  # We dynamically add a member variable for the purpose of this method
3065  # alone to cache the result. There's no point in having it hanging
3066  # around uninitialized in every Title object; therefore we only add it
3067  # if needed and don't declare it statically.
3068  if ( !isset( $this->mHasSubpages ) ) {
3069  $this->mHasSubpages = false;
3070  $subpages = $this->getSubpages( 1 );
3071  if ( $subpages instanceof TitleArray ) {
3072  $this->mHasSubpages = (bool)$subpages->count();
3073  }
3074  }
3075 
3076  return $this->mHasSubpages;
3077  }
3078 
3086  public function getSubpages( $limit = -1 ) {
3087  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
3088  return array();
3089  }
3090 
3091  $dbr = wfGetDB( DB_SLAVE );
3092  $conds['page_namespace'] = $this->getNamespace();
3093  $conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() );
3094  $options = array();
3095  if ( $limit > -1 ) {
3096  $options['LIMIT'] = $limit;
3097  }
3098  $this->mSubpages = TitleArray::newFromResult(
3099  $dbr->select( 'page',
3100  array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ),
3101  $conds,
3102  __METHOD__,
3103  $options
3104  )
3105  );
3106  return $this->mSubpages;
3107  }
3108 
3114  public function isDeleted() {
3115  if ( $this->getNamespace() < 0 ) {
3116  $n = 0;
3117  } else {
3118  $dbr = wfGetDB( DB_SLAVE );
3119 
3120  $n = $dbr->selectField( 'archive', 'COUNT(*)',
3121  array( 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ),
3122  __METHOD__
3123  );
3124  if ( $this->getNamespace() == NS_FILE ) {
3125  $n += $dbr->selectField( 'filearchive', 'COUNT(*)',
3126  array( 'fa_name' => $this->getDBkey() ),
3127  __METHOD__
3128  );
3129  }
3130  }
3131  return (int)$n;
3132  }
3133 
3139  public function isDeletedQuick() {
3140  if ( $this->getNamespace() < 0 ) {
3141  return false;
3142  }
3143  $dbr = wfGetDB( DB_SLAVE );
3144  $deleted = (bool)$dbr->selectField( 'archive', '1',
3145  array( 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ),
3146  __METHOD__
3147  );
3148  if ( !$deleted && $this->getNamespace() == NS_FILE ) {
3149  $deleted = (bool)$dbr->selectField( 'filearchive', '1',
3150  array( 'fa_name' => $this->getDBkey() ),
3151  __METHOD__
3152  );
3153  }
3154  return $deleted;
3155  }
3156 
3165  public function getArticleID( $flags = 0 ) {
3166  if ( $this->getNamespace() < 0 ) {
3167  $this->mArticleID = 0;
3168  return $this->mArticleID;
3169  }
3170  $linkCache = LinkCache::singleton();
3171  if ( $flags & self::GAID_FOR_UPDATE ) {
3172  $oldUpdate = $linkCache->forUpdate( true );
3173  $linkCache->clearLink( $this );
3174  $this->mArticleID = $linkCache->addLinkObj( $this );
3175  $linkCache->forUpdate( $oldUpdate );
3176  } else {
3177  if ( -1 == $this->mArticleID ) {
3178  $this->mArticleID = $linkCache->addLinkObj( $this );
3179  }
3180  }
3181  return $this->mArticleID;
3182  }
3183 
3191  public function isRedirect( $flags = 0 ) {
3192  if ( !is_null( $this->mRedirect ) ) {
3193  return $this->mRedirect;
3194  }
3195  # Calling getArticleID() loads the field from cache as needed
3196  if ( !$this->getArticleID( $flags ) ) {
3197  $this->mRedirect = false;
3198  return $this->mRedirect;
3199  }
3200 
3201  $linkCache = LinkCache::singleton();
3202  $cached = $linkCache->getGoodLinkFieldObj( $this, 'redirect' );
3203  if ( $cached === null ) {
3204  # Trust LinkCache's state over our own
3205  # LinkCache is telling us that the page doesn't exist, despite there being cached
3206  # data relating to an existing page in $this->mArticleID. Updaters should clear
3207  # LinkCache as appropriate, or use $flags = Title::GAID_FOR_UPDATE. If that flag is
3208  # set, then LinkCache will definitely be up to date here, since getArticleID() forces
3209  # LinkCache to refresh its data from the master.
3210  $this->mRedirect = false;
3211  return $this->mRedirect;
3212  }
3213 
3214  $this->mRedirect = (bool)$cached;
3215 
3216  return $this->mRedirect;
3217  }
3218 
3226  public function getLength( $flags = 0 ) {
3227  if ( $this->mLength != -1 ) {
3228  return $this->mLength;
3229  }
3230  # Calling getArticleID() loads the field from cache as needed
3231  if ( !$this->getArticleID( $flags ) ) {
3232  $this->mLength = 0;
3233  return $this->mLength;
3234  }
3235  $linkCache = LinkCache::singleton();
3236  $cached = $linkCache->getGoodLinkFieldObj( $this, 'length' );
3237  if ( $cached === null ) {
3238  # Trust LinkCache's state over our own, as for isRedirect()
3239  $this->mLength = 0;
3240  return $this->mLength;
3241  }
3242 
3243  $this->mLength = intval( $cached );
3244 
3245  return $this->mLength;
3246  }
3247 
3254  public function getLatestRevID( $flags = 0 ) {
3255  if ( !( $flags & Title::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
3256  return intval( $this->mLatestID );
3257  }
3258  # Calling getArticleID() loads the field from cache as needed
3259  if ( !$this->getArticleID( $flags ) ) {
3260  $this->mLatestID = 0;
3261  return $this->mLatestID;
3262  }
3263  $linkCache = LinkCache::singleton();
3264  $linkCache->addLinkObj( $this );
3265  $cached = $linkCache->getGoodLinkFieldObj( $this, 'revision' );
3266  if ( $cached === null ) {
3267  # Trust LinkCache's state over our own, as for isRedirect()
3268  $this->mLatestID = 0;
3269  return $this->mLatestID;
3270  }
3271 
3272  $this->mLatestID = intval( $cached );
3273 
3274  return $this->mLatestID;
3275  }
3276 
3287  public function resetArticleID( $newid ) {
3288  $linkCache = LinkCache::singleton();
3289  $linkCache->clearLink( $this );
3290 
3291  if ( $newid === false ) {
3292  $this->mArticleID = -1;
3293  } else {
3294  $this->mArticleID = intval( $newid );
3295  }
3296  $this->mRestrictionsLoaded = false;
3297  $this->mRestrictions = array();
3298  $this->mRedirect = null;
3299  $this->mLength = -1;
3300  $this->mLatestID = false;
3301  $this->mContentModel = false;
3302  $this->mEstimateRevisions = null;
3303  $this->mPageLanguage = false;
3304  }
3305 
3313  public static function capitalize( $text, $ns = NS_MAIN ) {
3315 
3316  if ( MWNamespace::isCapitalized( $ns ) ) {
3317  return $wgContLang->ucfirst( $text );
3318  } else {
3319  return $text;
3320  }
3321  }
3322 
3334  private function secureAndSplit() {
3335  # Initialisation
3336  $this->mInterwiki = '';
3337  $this->mFragment = '';
3338  $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
3339 
3340  $dbkey = $this->mDbkeyform;
3341 
3342  try {
3343  // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
3344  // the parsing code with Title, while avoiding massive refactoring.
3345  // @todo: get rid of secureAndSplit, refactor parsing code.
3346  $parser = $this->getTitleParser();
3347  $parts = $parser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
3348  } catch ( MalformedTitleException $ex ) {
3349  return false;
3350  }
3351 
3352  # Fill fields
3353  $this->setFragment( '#' . $parts['fragment'] );
3354  $this->mInterwiki = $parts['interwiki'];
3355  $this->mNamespace = $parts['namespace'];
3356  $this->mUserCaseDBKey = $parts['user_case_dbkey'];
3357 
3358  $this->mDbkeyform = $parts['dbkey'];
3359  $this->mUrlform = wfUrlencode( $this->mDbkeyform );
3360  $this->mTextform = str_replace( '_', ' ', $this->mDbkeyform );
3361 
3362  # We already know that some pages won't be in the database!
3363  if ( $this->isExternal() || $this->mNamespace == NS_SPECIAL ) {
3364  $this->mArticleID = 0;
3365  }
3366 
3367  return true;
3368  }
3369 
3382  public function getLinksTo( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
3383  if ( count( $options ) > 0 ) {
3384  $db = wfGetDB( DB_MASTER );
3385  } else {
3386  $db = wfGetDB( DB_SLAVE );
3387  }
3388 
3389  $res = $db->select(
3390  array( 'page', $table ),
3391  self::getSelectFields(),
3392  array(
3393  "{$prefix}_from=page_id",
3394  "{$prefix}_namespace" => $this->getNamespace(),
3395  "{$prefix}_title" => $this->getDBkey() ),
3396  __METHOD__,
3397  $options
3398  );
3399 
3400  $retVal = array();
3401  if ( $res->numRows() ) {
3402  $linkCache = LinkCache::singleton();
3403  foreach ( $res as $row ) {
3404  $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title );
3405  if ( $titleObj ) {
3406  $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
3407  $retVal[] = $titleObj;
3408  }
3409  }
3410  }
3411  return $retVal;
3412  }
3413 
3424  public function getTemplateLinksTo( $options = array() ) {
3425  return $this->getLinksTo( $options, 'templatelinks', 'tl' );
3426  }
3427 
3440  public function getLinksFrom( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
3441  global $wgContentHandlerUseDB;
3442 
3443  $id = $this->getArticleID();
3444 
3445  # If the page doesn't exist; there can't be any link from this page
3446  if ( !$id ) {
3447  return array();
3448  }
3449 
3450  if ( count( $options ) > 0 ) {
3451  $db = wfGetDB( DB_MASTER );
3452  } else {
3453  $db = wfGetDB( DB_SLAVE );
3454  }
3455 
3456  $namespaceFiled = "{$prefix}_namespace";
3457  $titleField = "{$prefix}_title";
3458 
3459  $fields = array( $namespaceFiled, $titleField, 'page_id', 'page_len', 'page_is_redirect', 'page_latest' );
3460  if ( $wgContentHandlerUseDB ) {
3461  $fields[] = 'page_content_model';
3462  }
3463 
3464  $res = $db->select(
3465  array( $table, 'page' ),
3466  $fields,
3467  array( "{$prefix}_from" => $id ),
3468  __METHOD__,
3469  $options,
3470  array( 'page' => array( 'LEFT JOIN', array( "page_namespace=$namespaceFiled", "page_title=$titleField" ) ) )
3471  );
3472 
3473  $retVal = array();
3474  if ( $res->numRows() ) {
3475  $linkCache = LinkCache::singleton();
3476  foreach ( $res as $row ) {
3477  $titleObj = Title::makeTitle( $row->$namespaceFiled, $row->$titleField );
3478  if ( $titleObj ) {
3479  if ( $row->page_id ) {
3480  $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
3481  } else {
3482  $linkCache->addBadLinkObj( $titleObj );
3483  }
3484  $retVal[] = $titleObj;
3485  }
3486  }
3487  }
3488  return $retVal;
3489  }
3490 
3501  public function getTemplateLinksFrom( $options = array() ) {
3502  return $this->getLinksFrom( $options, 'templatelinks', 'tl' );
3503  }
3504 
3511  public function getBrokenLinksFrom() {
3512  if ( $this->getArticleID() == 0 ) {
3513  # All links from article ID 0 are false positives
3514  return array();
3515  }
3516 
3517  $dbr = wfGetDB( DB_SLAVE );
3518  $res = $dbr->select(
3519  array( 'page', 'pagelinks' ),
3520  array( 'pl_namespace', 'pl_title' ),
3521  array(
3522  'pl_from' => $this->getArticleID(),
3523  'page_namespace IS NULL'
3524  ),
3525  __METHOD__, array(),
3526  array(
3527  'page' => array(
3528  'LEFT JOIN',
3529  array( 'pl_namespace=page_namespace', 'pl_title=page_title' )
3530  )
3531  )
3532  );
3533 
3534  $retVal = array();
3535  foreach ( $res as $row ) {
3536  $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
3537  }
3538  return $retVal;
3539  }
3540 
3547  public function getSquidURLs() {
3548  $urls = array(
3549  $this->getInternalURL(),
3550  $this->getInternalURL( 'action=history' )
3551  );
3552 
3553  $pageLang = $this->getPageLanguage();
3554  if ( $pageLang->hasVariants() ) {
3555  $variants = $pageLang->getVariants();
3556  foreach ( $variants as $vCode ) {
3557  $urls[] = $this->getInternalURL( '', $vCode );
3558  }
3559  }
3560 
3561  // If we are looking at a css/js user subpage, purge the action=raw.
3562  if ( $this->isJsSubpage() ) {
3563  $urls[] = $this->getInternalUrl( 'action=raw&ctype=text/javascript' );
3564  } elseif ( $this->isCssSubpage() ) {
3565  $urls[] = $this->getInternalUrl( 'action=raw&ctype=text/css' );
3566  }
3567 
3568  wfRunHooks( 'TitleSquidURLs', array( $this, &$urls ) );
3569  return $urls;
3570  }
3571 
3575  public function purgeSquid() {
3576  global $wgUseSquid;
3577  if ( $wgUseSquid ) {
3578  $urls = $this->getSquidURLs();
3579  $u = new SquidUpdate( $urls );
3580  $u->doUpdate();
3581  }
3582  }
3583 
3590  public function moveNoAuth( &$nt ) {
3591  return $this->moveTo( $nt, false );
3592  }
3593 
3604  public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) {
3605  global $wgUser, $wgContentHandlerUseDB;
3606 
3607  $errors = array();
3608  if ( !$nt ) {
3609  // Normally we'd add this to $errors, but we'll get
3610  // lots of syntax errors if $nt is not an object
3611  return array( array( 'badtitletext' ) );
3612  }
3613  if ( $this->equals( $nt ) ) {
3614  $errors[] = array( 'selfmove' );
3615  }
3616  if ( !$this->isMovable() ) {
3617  $errors[] = array( 'immobile-source-namespace', $this->getNsText() );
3618  }
3619  if ( $nt->isExternal() ) {
3620  $errors[] = array( 'immobile-target-namespace-iw' );
3621  }
3622  if ( !$nt->isMovable() ) {
3623  $errors[] = array( 'immobile-target-namespace', $nt->getNsText() );
3624  }
3625 
3626  $oldid = $this->getArticleID();
3627  $newid = $nt->getArticleID();
3628 
3629  if ( strlen( $nt->getDBkey() ) < 1 ) {
3630  $errors[] = array( 'articleexists' );
3631  }
3632  if (
3633  ( $this->getDBkey() == '' ) ||
3634  ( !$oldid ) ||
3635  ( $nt->getDBkey() == '' )
3636  ) {
3637  $errors[] = array( 'badarticleerror' );
3638  }
3639 
3640  // Content model checks
3641  if ( !$wgContentHandlerUseDB &&
3642  $this->getContentModel() !== $nt->getContentModel() ) {
3643  // can't move a page if that would change the page's content model
3644  $errors[] = array(
3645  'bad-target-model',
3647  ContentHandler::getLocalizedName( $nt->getContentModel() )
3648  );
3649  }
3650 
3651  // Image-specific checks
3652  if ( $this->getNamespace() == NS_FILE ) {
3653  $errors = array_merge( $errors, $this->validateFileMoveOperation( $nt ) );
3654  }
3655 
3656  if ( $nt->getNamespace() == NS_FILE && $this->getNamespace() != NS_FILE ) {
3657  $errors[] = array( 'nonfile-cannot-move-to-file' );
3658  }
3659 
3660  if ( $auth ) {
3661  $errors = wfMergeErrorArrays( $errors,
3662  $this->getUserPermissionsErrors( 'move', $wgUser ),
3663  $this->getUserPermissionsErrors( 'edit', $wgUser ),
3664  $nt->getUserPermissionsErrors( 'move-target', $wgUser ),
3665  $nt->getUserPermissionsErrors( 'edit', $wgUser ) );
3666  }
3667 
3668  $match = EditPage::matchSummarySpamRegex( $reason );
3669  if ( $match !== false ) {
3670  // This is kind of lame, won't display nice
3671  $errors[] = array( 'spamprotectiontext' );
3672  }
3673 
3674  $err = null;
3675  if ( !wfRunHooks( 'AbortMove', array( $this, $nt, $wgUser, &$err, $reason ) ) ) {
3676  $errors[] = array( 'hookaborted', $err );
3677  }
3678 
3679  # The move is allowed only if (1) the target doesn't exist, or
3680  # (2) the target is a redirect to the source, and has no history
3681  # (so we can undo bad moves right after they're done).
3682 
3683  if ( 0 != $newid ) { # Target exists; check for validity
3684  if ( !$this->isValidMoveTarget( $nt ) ) {
3685  $errors[] = array( 'articleexists' );
3686  }
3687  } else {
3688  $tp = $nt->getTitleProtection();
3689  $right = $tp['pt_create_perm'];
3690  if ( $right == 'sysop' ) {
3691  $right = 'editprotected'; // B/C
3692  }
3693  if ( $right == 'autoconfirmed' ) {
3694  $right = 'editsemiprotected'; // B/C
3695  }
3696  if ( $tp and !$wgUser->isAllowed( $right ) ) {
3697  $errors[] = array( 'cantmove-titleprotected' );
3698  }
3699  }
3700  if ( empty( $errors ) ) {
3701  return true;
3702  }
3703  return $errors;
3704  }
3705 
3711  protected function validateFileMoveOperation( $nt ) {
3712  global $wgUser;
3713 
3714  $errors = array();
3715 
3716  // wfFindFile( $nt ) / wfLocalFile( $nt ) is not allowed until below
3717 
3718  $file = wfLocalFile( $this );
3719  if ( $file->exists() ) {
3720  if ( $nt->getText() != wfStripIllegalFilenameChars( $nt->getText() ) ) {
3721  $errors[] = array( 'imageinvalidfilename' );
3722  }
3723  if ( !File::checkExtensionCompatibility( $file, $nt->getDBkey() ) ) {
3724  $errors[] = array( 'imagetypemismatch' );
3725  }
3726  }
3727 
3728  if ( $nt->getNamespace() != NS_FILE ) {
3729  $errors[] = array( 'imagenocrossnamespace' );
3730  // From here we want to do checks on a file object, so if we can't
3731  // create one, we must return.
3732  return $errors;
3733  }
3734 
3735  // wfFindFile( $nt ) / wfLocalFile( $nt ) is allowed below here
3736 
3737  $destFile = wfLocalFile( $nt );
3738  if ( !$wgUser->isAllowed( 'reupload-shared' ) && !$destFile->exists() && wfFindFile( $nt ) ) {
3739  $errors[] = array( 'file-exists-sharedrepo' );
3740  }
3741 
3742  return $errors;
3743  }
3744 
3756  public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true ) {
3757  global $wgUser;
3758  $err = $this->isValidMoveOperation( $nt, $auth, $reason );
3759  if ( is_array( $err ) ) {
3760  // Auto-block user's IP if the account was "hard" blocked
3761  $wgUser->spreadAnyEditBlock();
3762  return $err;
3763  }
3764  // Check suppressredirect permission
3765  if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
3766  $createRedirect = true;
3767  }
3768 
3769  wfRunHooks( 'TitleMove', array( $this, $nt, $wgUser ) );
3770 
3771  // If it is a file, move it first.
3772  // It is done before all other moving stuff is done because it's hard to revert.
3773  $dbw = wfGetDB( DB_MASTER );
3774  if ( $this->getNamespace() == NS_FILE ) {
3775  $file = wfLocalFile( $this );
3776  if ( $file->exists() ) {
3777  $status = $file->move( $nt );
3778  if ( !$status->isOk() ) {
3779  return $status->getErrorsArray();
3780  }
3781  }
3782  // Clear RepoGroup process cache
3783  RepoGroup::singleton()->clearCache( $this );
3784  RepoGroup::singleton()->clearCache( $nt ); # clear false negative cache
3785  }
3786 
3787  $dbw->begin( __METHOD__ ); # If $file was a LocalFile, its transaction would have closed our own.
3788  $pageid = $this->getArticleID( self::GAID_FOR_UPDATE );
3789  $protected = $this->isProtected();
3790 
3791  // Do the actual move
3792  $this->moveToInternal( $nt, $reason, $createRedirect );
3793 
3794  // Refresh the sortkey for this row. Be careful to avoid resetting
3795  // cl_timestamp, which may disturb time-based lists on some sites.
3796  $prefixes = $dbw->select(
3797  'categorylinks',
3798  array( 'cl_sortkey_prefix', 'cl_to' ),
3799  array( 'cl_from' => $pageid ),
3800  __METHOD__
3801  );
3802  foreach ( $prefixes as $prefixRow ) {
3803  $prefix = $prefixRow->cl_sortkey_prefix;
3804  $catTo = $prefixRow->cl_to;
3805  $dbw->update( 'categorylinks',
3806  array(
3807  'cl_sortkey' => Collation::singleton()->getSortKey(
3808  $nt->getCategorySortkey( $prefix ) ),
3809  'cl_timestamp=cl_timestamp' ),
3810  array(
3811  'cl_from' => $pageid,
3812  'cl_to' => $catTo ),
3813  __METHOD__
3814  );
3815  }
3816 
3817  $redirid = $this->getArticleID();
3818 
3819  if ( $protected ) {
3820  # Protect the redirect title as the title used to be...
3821  $dbw->insertSelect( 'page_restrictions', 'page_restrictions',
3822  array(
3823  'pr_page' => $redirid,
3824  'pr_type' => 'pr_type',
3825  'pr_level' => 'pr_level',
3826  'pr_cascade' => 'pr_cascade',
3827  'pr_user' => 'pr_user',
3828  'pr_expiry' => 'pr_expiry'
3829  ),
3830  array( 'pr_page' => $pageid ),
3831  __METHOD__,
3832  array( 'IGNORE' )
3833  );
3834  # Update the protection log
3835  $log = new LogPage( 'protect' );
3836  $comment = wfMessage(
3837  'prot_1movedto2',
3838  $this->getPrefixedText(),
3839  $nt->getPrefixedText()
3840  )->inContentLanguage()->text();
3841  if ( $reason ) {
3842  $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
3843  }
3844  // @todo FIXME: $params?
3845  $logId = $log->addEntry( 'move_prot', $nt, $comment, array( $this->getPrefixedText() ), $wgUser );
3846 
3847  // reread inserted pr_ids for log relation
3848  $insertedPrIds = $dbw->select(
3849  'page_restrictions',
3850  'pr_id',
3851  array( 'pr_page' => $redirid ),
3852  __METHOD__
3853  );
3854  $logRelationsValues = array();
3855  foreach ( $insertedPrIds as $prid ) {
3856  $logRelationsValues[] = $prid->pr_id;
3857  }
3858  $log->addRelations( 'pr_id', $logRelationsValues, $logId );
3859  }
3860 
3861  # Update watchlists
3862  $oldnamespace = MWNamespace::getSubject( $this->getNamespace() );
3863  $newnamespace = MWNamespace::getSubject( $nt->getNamespace() );
3864  $oldtitle = $this->getDBkey();
3865  $newtitle = $nt->getDBkey();
3866 
3867  if ( $oldnamespace != $newnamespace || $oldtitle != $newtitle ) {
3868  WatchedItem::duplicateEntries( $this, $nt );
3869  }
3870 
3871  $dbw->commit( __METHOD__ );
3872 
3873  wfRunHooks( 'TitleMoveComplete', array( &$this, &$nt, &$wgUser, $pageid, $redirid, $reason ) );
3874  return true;
3875  }
3876 
3887  private function moveToInternal( &$nt, $reason = '', $createRedirect = true ) {
3889 
3890  if ( $nt->exists() ) {
3891  $moveOverRedirect = true;
3892  $logType = 'move_redir';
3893  } else {
3894  $moveOverRedirect = false;
3895  $logType = 'move';
3896  }
3897 
3898  if ( $createRedirect ) {
3899  $contentHandler = ContentHandler::getForTitle( $this );
3900  $redirectContent = $contentHandler->makeRedirectContent( $nt,
3901  wfMessage( 'move-redirect-text' )->inContentLanguage()->plain() );
3902 
3903  // NOTE: If this page's content model does not support redirects, $redirectContent will be null.
3904  } else {
3905  $redirectContent = null;
3906  }
3907 
3908  $logEntry = new ManualLogEntry( 'move', $logType );
3909  $logEntry->setPerformer( $wgUser );
3910  $logEntry->setTarget( $this );
3911  $logEntry->setComment( $reason );
3912  $logEntry->setParameters( array(
3913  '4::target' => $nt->getPrefixedText(),
3914  '5::noredir' => $redirectContent ? '0': '1',
3915  ) );
3916 
3917  $formatter = LogFormatter::newFromEntry( $logEntry );
3918  $formatter->setContext( RequestContext::newExtraneousContext( $this ) );
3919  $comment = $formatter->getPlainActionText();
3920  if ( $reason ) {
3921  $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
3922  }
3923  # Truncate for whole multibyte characters.
3924  $comment = $wgContLang->truncate( $comment, 255 );
3925 
3926  $oldid = $this->getArticleID();
3927 
3928  $dbw = wfGetDB( DB_MASTER );
3929 
3930  $newpage = WikiPage::factory( $nt );
3931 
3932  if ( $moveOverRedirect ) {
3933  $newid = $nt->getArticleID();
3934  $newcontent = $newpage->getContent();
3935 
3936  # Delete the old redirect. We don't save it to history since
3937  # by definition if we've got here it's rather uninteresting.
3938  # We have to remove it so that the next step doesn't trigger
3939  # a conflict on the unique namespace+title index...
3940  $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ );
3941 
3942  $newpage->doDeleteUpdates( $newid, $newcontent );
3943  }
3944 
3945  # Save a null revision in the page's history notifying of the move
3946  $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true );
3947  if ( !is_object( $nullRevision ) ) {
3948  throw new MWException( 'No valid null revision produced in ' . __METHOD__ );
3949  }
3950 
3951  $nullRevision->insertOn( $dbw );
3952 
3953  # Change the name of the target page:
3954  $dbw->update( 'page',
3955  /* SET */ array(
3956  'page_namespace' => $nt->getNamespace(),
3957  'page_title' => $nt->getDBkey(),
3958  ),
3959  /* WHERE */ array( 'page_id' => $oldid ),
3960  __METHOD__
3961  );
3962 
3963  // clean up the old title before reset article id - bug 45348
3964  if ( !$redirectContent ) {
3965  WikiPage::onArticleDelete( $this );
3966  }
3967 
3968  $this->resetArticleID( 0 ); // 0 == non existing
3969  $nt->resetArticleID( $oldid );
3970  $newpage->loadPageData( WikiPage::READ_LOCKING ); // bug 46397
3971 
3972  $newpage->updateRevisionOn( $dbw, $nullRevision );
3973 
3974  wfRunHooks( 'NewRevisionFromEditComplete',
3975  array( $newpage, $nullRevision, $nullRevision->getParentId(), $wgUser ) );
3976 
3977  $newpage->doEditUpdates( $nullRevision, $wgUser, array( 'changed' => false ) );
3978 
3979  if ( !$moveOverRedirect ) {
3981  }
3982 
3983  # Recreate the redirect, this time in the other direction.
3984  if ( $redirectContent ) {
3985  $redirectArticle = WikiPage::factory( $this );
3986  $redirectArticle->loadFromRow( false, WikiPage::READ_LOCKING ); // bug 46397
3987  $newid = $redirectArticle->insertOn( $dbw );
3988  if ( $newid ) { // sanity
3989  $this->resetArticleID( $newid );
3990  $redirectRevision = new Revision( array(
3991  'title' => $this, // for determining the default content model
3992  'page' => $newid,
3993  'comment' => $comment,
3994  'content' => $redirectContent ) );
3995  $redirectRevision->insertOn( $dbw );
3996  $redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 );
3997 
3998  wfRunHooks( 'NewRevisionFromEditComplete',
3999  array( $redirectArticle, $redirectRevision, false, $wgUser ) );
4000 
4001  $redirectArticle->doEditUpdates( $redirectRevision, $wgUser, array( 'created' => true ) );
4002  }
4003  }
4004 
4005  # Log the move
4006  $logid = $logEntry->insert();
4007  $logEntry->publish( $logid );
4008  }
4009 
4022  public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true ) {
4023  global $wgMaximumMovedPages;
4024  // Check permissions
4025  if ( !$this->userCan( 'move-subpages' ) ) {
4026  return array( 'cant-move-subpages' );
4027  }
4028  // Do the source and target namespaces support subpages?
4029  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
4030  return array( 'namespace-nosubpages',
4032  }
4033  if ( !MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
4034  return array( 'namespace-nosubpages',
4035  MWNamespace::getCanonicalName( $nt->getNamespace() ) );
4036  }
4037 
4038  $subpages = $this->getSubpages( $wgMaximumMovedPages + 1 );
4039  $retval = array();
4040  $count = 0;
4041  foreach ( $subpages as $oldSubpage ) {
4042  $count++;
4043  if ( $count > $wgMaximumMovedPages ) {
4044  $retval[$oldSubpage->getPrefixedText()] =
4045  array( 'movepage-max-pages',
4046  $wgMaximumMovedPages );
4047  break;
4048  }
4049 
4050  // We don't know whether this function was called before
4051  // or after moving the root page, so check both
4052  // $this and $nt
4053  if ( $oldSubpage->getArticleID() == $this->getArticleID()
4054  || $oldSubpage->getArticleID() == $nt->getArticleID()
4055  ) {
4056  // When moving a page to a subpage of itself,
4057  // don't move it twice
4058  continue;
4059  }
4060  $newPageName = preg_replace(
4061  '#^' . preg_quote( $this->getDBkey(), '#' ) . '#',
4062  StringUtils::escapeRegexReplacement( $nt->getDBkey() ), # bug 21234
4063  $oldSubpage->getDBkey() );
4064  if ( $oldSubpage->isTalkPage() ) {
4065  $newNs = $nt->getTalkPage()->getNamespace();
4066  } else {
4067  $newNs = $nt->getSubjectPage()->getNamespace();
4068  }
4069  # Bug 14385: we need makeTitleSafe because the new page names may
4070  # be longer than 255 characters.
4071  $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
4072 
4073  $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect );
4074  if ( $success === true ) {
4075  $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
4076  } else {
4077  $retval[$oldSubpage->getPrefixedText()] = $success;
4078  }
4079  }
4080  return $retval;
4081  }
4082 
4089  public function isSingleRevRedirect() {
4090  global $wgContentHandlerUseDB;
4091 
4092  $dbw = wfGetDB( DB_MASTER );
4093 
4094  # Is it a redirect?
4095  $fields = array( 'page_is_redirect', 'page_latest', 'page_id' );
4096  if ( $wgContentHandlerUseDB ) {
4097  $fields[] = 'page_content_model';
4098  }
4099 
4100  $row = $dbw->selectRow( 'page',
4101  $fields,
4102  $this->pageCond(),
4103  __METHOD__,
4104  array( 'FOR UPDATE' )
4105  );
4106  # Cache some fields we may want
4107  $this->mArticleID = $row ? intval( $row->page_id ) : 0;
4108  $this->mRedirect = $row ? (bool)$row->page_is_redirect : false;
4109  $this->mLatestID = $row ? intval( $row->page_latest ) : false;
4110  $this->mContentModel = $row && isset( $row->page_content_model ) ? strval( $row->page_content_model ) : false;
4111  if ( !$this->mRedirect ) {
4112  return false;
4113  }
4114  # Does the article have a history?
4115  $row = $dbw->selectField( array( 'page', 'revision' ),
4116  'rev_id',
4117  array( 'page_namespace' => $this->getNamespace(),
4118  'page_title' => $this->getDBkey(),
4119  'page_id=rev_page',
4120  'page_latest != rev_id'
4121  ),
4122  __METHOD__,
4123  array( 'FOR UPDATE' )
4124  );
4125  # Return true if there was no history
4126  return ( $row === false );
4127  }
4128 
4136  public function isValidMoveTarget( $nt ) {
4137  # Is it an existing file?
4138  if ( $nt->getNamespace() == NS_FILE ) {
4139  $file = wfLocalFile( $nt );
4140  if ( $file->exists() ) {
4141  wfDebug( __METHOD__ . ": file exists\n" );
4142  return false;
4143  }
4144  }
4145  # Is it a redirect with no history?
4146  if ( !$nt->isSingleRevRedirect() ) {
4147  wfDebug( __METHOD__ . ": not a one-rev redirect\n" );
4148  return false;
4149  }
4150  # Get the article text
4152  if ( !is_object( $rev ) ) {
4153  return false;
4154  }
4155  $content = $rev->getContent();
4156  # Does the redirect point to the source?
4157  # Or is it a broken self-redirect, usually caused by namespace collisions?
4158  $redirTitle = $content ? $content->getRedirectTarget() : null;
4159 
4160  if ( $redirTitle ) {
4161  if ( $redirTitle->getPrefixedDBkey() != $this->getPrefixedDBkey() &&
4162  $redirTitle->getPrefixedDBkey() != $nt->getPrefixedDBkey() ) {
4163  wfDebug( __METHOD__ . ": redirect points to other page\n" );
4164  return false;
4165  } else {
4166  return true;
4167  }
4168  } else {
4169  # Fail safe (not a redirect after all. strange.)
4170  wfDebug( __METHOD__ . ": failsafe: database sais " . $nt->getPrefixedDBkey() .
4171  " is a redirect, but it doesn't contain a valid redirect.\n" );
4172  return false;
4173  }
4174  }
4175 
4183  public function getParentCategories() {
4185 
4186  $data = array();
4187 
4188  $titleKey = $this->getArticleID();
4189 
4190  if ( $titleKey === 0 ) {
4191  return $data;
4192  }
4193 
4194  $dbr = wfGetDB( DB_SLAVE );
4195 
4196  $res = $dbr->select(
4197  'categorylinks',
4198  'cl_to',
4199  array( 'cl_from' => $titleKey ),
4200  __METHOD__
4201  );
4202 
4203  if ( $res->numRows() > 0 ) {
4204  foreach ( $res as $row ) {
4205  // $data[] = Title::newFromText($wgContLang->getNsText ( NS_CATEGORY ).':'.$row->cl_to);
4206  $data[$wgContLang->getNsText( NS_CATEGORY ) . ':' . $row->cl_to] = $this->getFullText();
4207  }
4208  }
4209  return $data;
4210  }
4211 
4218  public function getParentCategoryTree( $children = array() ) {
4219  $stack = array();
4220  $parents = $this->getParentCategories();
4221 
4222  if ( $parents ) {
4223  foreach ( $parents as $parent => $current ) {
4224  if ( array_key_exists( $parent, $children ) ) {
4225  # Circular reference
4226  $stack[$parent] = array();
4227  } else {
4228  $nt = Title::newFromText( $parent );
4229  if ( $nt ) {
4230  $stack[$parent] = $nt->getParentCategoryTree( $children + array( $parent => 1 ) );
4231  }
4232  }
4233  }
4234  }
4235 
4236  return $stack;
4237  }
4238 
4245  public function pageCond() {
4246  if ( $this->mArticleID > 0 ) {
4247  // PK avoids secondary lookups in InnoDB, shouldn't hurt other DBs
4248  return array( 'page_id' => $this->mArticleID );
4249  } else {
4250  return array( 'page_namespace' => $this->mNamespace, 'page_title' => $this->mDbkeyform );
4251  }
4252  }
4253 
4261  public function getPreviousRevisionID( $revId, $flags = 0 ) {
4263  $revId = $db->selectField( 'revision', 'rev_id',
4264  array(
4265  'rev_page' => $this->getArticleID( $flags ),
4266  'rev_id < ' . intval( $revId )
4267  ),
4268  __METHOD__,
4269  array( 'ORDER BY' => 'rev_id DESC' )
4270  );
4271 
4272  if ( $revId === false ) {
4273  return false;
4274  } else {
4275  return intval( $revId );
4276  }
4277  }
4278 
4286  public function getNextRevisionID( $revId, $flags = 0 ) {
4288  $revId = $db->selectField( 'revision', 'rev_id',
4289  array(
4290  'rev_page' => $this->getArticleID( $flags ),
4291  'rev_id > ' . intval( $revId )
4292  ),
4293  __METHOD__,
4294  array( 'ORDER BY' => 'rev_id' )
4295  );
4296 
4297  if ( $revId === false ) {
4298  return false;
4299  } else {
4300  return intval( $revId );
4301  }
4302  }
4303 
4310  public function getFirstRevision( $flags = 0 ) {
4311  $pageId = $this->getArticleID( $flags );
4312  if ( $pageId ) {
4314  $row = $db->selectRow( 'revision', Revision::selectFields(),
4315  array( 'rev_page' => $pageId ),
4316  __METHOD__,
4317  array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 )
4318  );
4319  if ( $row ) {
4320  return new Revision( $row );
4321  }
4322  }
4323  return null;
4324  }
4325 
4332  public function getEarliestRevTime( $flags = 0 ) {
4333  $rev = $this->getFirstRevision( $flags );
4334  return $rev ? $rev->getTimestamp() : null;
4335  }
4336 
4342  public function isNewPage() {
4343  $dbr = wfGetDB( DB_SLAVE );
4344  return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
4345  }
4346 
4352  public function isBigDeletion() {
4353  global $wgDeleteRevisionsLimit;
4354 
4355  if ( !$wgDeleteRevisionsLimit ) {
4356  return false;
4357  }
4358 
4359  $revCount = $this->estimateRevisionCount();
4360  return $revCount > $wgDeleteRevisionsLimit;
4361  }
4362 
4368  public function estimateRevisionCount() {
4369  if ( !$this->exists() ) {
4370  return 0;
4371  }
4372 
4373  if ( $this->mEstimateRevisions === null ) {
4374  $dbr = wfGetDB( DB_SLAVE );
4375  $this->mEstimateRevisions = $dbr->estimateRowCount( 'revision', '*',
4376  array( 'rev_page' => $this->getArticleID() ), __METHOD__ );
4377  }
4378 
4380  }
4381 
4391  public function countRevisionsBetween( $old, $new, $max = null ) {
4392  if ( !( $old instanceof Revision ) ) {
4393  $old = Revision::newFromTitle( $this, (int)$old );
4394  }
4395  if ( !( $new instanceof Revision ) ) {
4396  $new = Revision::newFromTitle( $this, (int)$new );
4397  }
4398  if ( !$old || !$new ) {
4399  return 0; // nothing to compare
4400  }
4401  $dbr = wfGetDB( DB_SLAVE );
4402  $conds = array(
4403  'rev_page' => $this->getArticleID(),
4404  'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4405  'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4406  );
4407  if ( $max !== null ) {
4408  $res = $dbr->select( 'revision', '1',
4409  $conds,
4410  __METHOD__,
4411  array( 'LIMIT' => $max + 1 ) // extra to detect truncation
4412  );
4413  return $res->numRows();
4414  } else {
4415  return (int)$dbr->selectField( 'revision', 'count(*)', $conds, __METHOD__ );
4416  }
4417  }
4418 
4435  public function getAuthorsBetween( $old, $new, $limit, $options = array() ) {
4436  if ( !( $old instanceof Revision ) ) {
4437  $old = Revision::newFromTitle( $this, (int)$old );
4438  }
4439  if ( !( $new instanceof Revision ) ) {
4440  $new = Revision::newFromTitle( $this, (int)$new );
4441  }
4442  // XXX: what if Revision objects are passed in, but they don't refer to this title?
4443  // Add $old->getPage() != $new->getPage() || $old->getPage() != $this->getArticleID()
4444  // in the sanity check below?
4445  if ( !$old || !$new ) {
4446  return null; // nothing to compare
4447  }
4448  $authors = array();
4449  $old_cmp = '>';
4450  $new_cmp = '<';
4451  $options = (array)$options;
4452  if ( in_array( 'include_old', $options ) ) {
4453  $old_cmp = '>=';
4454  }
4455  if ( in_array( 'include_new', $options ) ) {
4456  $new_cmp = '<=';
4457  }
4458  if ( in_array( 'include_both', $options ) ) {
4459  $old_cmp = '>=';
4460  $new_cmp = '<=';
4461  }
4462  // No DB query needed if $old and $new are the same or successive revisions:
4463  if ( $old->getId() === $new->getId() ) {
4464  return ( $old_cmp === '>' && $new_cmp === '<' ) ? array() : array( $old->getRawUserText() );
4465  } elseif ( $old->getId() === $new->getParentId() ) {
4466  if ( $old_cmp === '>=' && $new_cmp === '<=' ) {
4467  $authors[] = $old->getRawUserText();
4468  if ( $old->getRawUserText() != $new->getRawUserText() ) {
4469  $authors[] = $new->getRawUserText();
4470  }
4471  } elseif ( $old_cmp === '>=' ) {
4472  $authors[] = $old->getRawUserText();
4473  } elseif ( $new_cmp === '<=' ) {
4474  $authors[] = $new->getRawUserText();
4475  }
4476  return $authors;
4477  }
4478  $dbr = wfGetDB( DB_SLAVE );
4479  $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
4480  array(
4481  'rev_page' => $this->getArticleID(),
4482  "rev_timestamp $old_cmp " . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4483  "rev_timestamp $new_cmp " . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4484  ), __METHOD__,
4485  array( 'LIMIT' => $limit + 1 ) // add one so caller knows it was truncated
4486  );
4487  foreach ( $res as $row ) {
4488  $authors[] = $row->rev_user_text;
4489  }
4490  return $authors;
4491  }
4492 
4507  public function countAuthorsBetween( $old, $new, $limit, $options = array() ) {
4508  $authors = $this->getAuthorsBetween( $old, $new, $limit, $options );
4509  return $authors ? count( $authors ) : 0;
4510  }
4511 
4518  public function equals( Title $title ) {
4519  // Note: === is necessary for proper matching of number-like titles.
4520  return $this->getInterwiki() === $title->getInterwiki()
4521  && $this->getNamespace() == $title->getNamespace()
4522  && $this->getDBkey() === $title->getDBkey();
4523  }
4524 
4531  public function isSubpageOf( Title $title ) {
4532  return $this->getInterwiki() === $title->getInterwiki()
4533  && $this->getNamespace() == $title->getNamespace()
4534  && strpos( $this->getDBkey(), $title->getDBkey() . '/' ) === 0;
4535  }
4536 
4546  public function exists() {
4547  return $this->getArticleID() != 0;
4548  }
4549 
4566  public function isAlwaysKnown() {
4567  $isKnown = null;
4568 
4579  wfRunHooks( 'TitleIsAlwaysKnown', array( $this, &$isKnown ) );
4580 
4581  if ( !is_null( $isKnown ) ) {
4582  return $isKnown;
4583  }
4584 
4585  if ( $this->isExternal() ) {
4586  return true; // any interwiki link might be viewable, for all we know
4587  }
4588 
4589  switch ( $this->mNamespace ) {
4590  case NS_MEDIA:
4591  case NS_FILE:
4592  // file exists, possibly in a foreign repo
4593  return (bool)wfFindFile( $this );
4594  case NS_SPECIAL:
4595  // valid special page
4596  return SpecialPageFactory::exists( $this->getDBkey() );
4597  case NS_MAIN:
4598  // selflink, possibly with fragment
4599  return $this->mDbkeyform == '';
4600  case NS_MEDIAWIKI:
4601  // known system message
4602  return $this->hasSourceText() !== false;
4603  default:
4604  return false;
4605  }
4606  }
4607 
4619  public function isKnown() {
4620  return $this->isAlwaysKnown() || $this->exists();
4621  }
4622 
4628  public function hasSourceText() {
4629  if ( $this->exists() ) {
4630  return true;
4631  }
4632 
4633  if ( $this->mNamespace == NS_MEDIAWIKI ) {
4634  // If the page doesn't exist but is a known system message, default
4635  // message content will be displayed, same for language subpages-
4636  // Use always content language to avoid loading hundreds of languages
4637  // to get the link color.
4639  list( $name, ) = MessageCache::singleton()->figureMessage( $wgContLang->lcfirst( $this->getText() ) );
4640  $message = wfMessage( $name )->inLanguage( $wgContLang )->useDatabase( false );
4641  return $message->exists();
4642  }
4643 
4644  return false;
4645  }
4646 
4652  public function getDefaultMessageText() {
4654 
4655  if ( $this->getNamespace() != NS_MEDIAWIKI ) { // Just in case
4656  return false;
4657  }
4658 
4659  list( $name, $lang ) = MessageCache::singleton()->figureMessage( $wgContLang->lcfirst( $this->getText() ) );
4660  $message = wfMessage( $name )->inLanguage( $lang )->useDatabase( false );
4661 
4662  if ( $message->exists() ) {
4663  return $message->plain();
4664  } else {
4665  return false;
4666  }
4667  }
4668 
4674  public function invalidateCache() {
4675  if ( wfReadOnly() ) {
4676  return false;
4677  }
4678 
4679  $method = __METHOD__;
4680  $dbw = wfGetDB( DB_MASTER );
4681  $conds = $this->pageCond();
4682  $dbw->onTransactionIdle( function() use ( $dbw, $conds, $method ) {
4683  $dbw->update(
4684  'page',
4685  array( 'page_touched' => $dbw->timestamp() ),
4686  $conds,
4687  $method
4688  );
4689  } );
4690 
4691  return true;
4692  }
4693 
4699  public function touchLinks() {
4700  $u = new HTMLCacheUpdate( $this, 'pagelinks' );
4701  $u->doUpdate();
4702 
4703  if ( $this->getNamespace() == NS_CATEGORY ) {
4704  $u = new HTMLCacheUpdate( $this, 'categorylinks' );
4705  $u->doUpdate();
4706  }
4707  }
4708 
4715  public function getTouched( $db = null ) {
4716  $db = isset( $db ) ? $db : wfGetDB( DB_SLAVE );
4717  $touched = $db->selectField( 'page', 'page_touched', $this->pageCond(), __METHOD__ );
4718  return $touched;
4719  }
4720 
4727  public function getNotificationTimestamp( $user = null ) {
4728  global $wgUser, $wgShowUpdatedMarker;
4729  // Assume current user if none given
4730  if ( !$user ) {
4731  $user = $wgUser;
4732  }
4733  // Check cache first
4734  $uid = $user->getId();
4735  // avoid isset here, as it'll return false for null entries
4736  if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) {
4737  return $this->mNotificationTimestamp[$uid];
4738  }
4739  if ( !$uid || !$wgShowUpdatedMarker || !$user->isAllowed( 'viewmywatchlist' ) ) {
4740  $this->mNotificationTimestamp[$uid] = false;
4741  return $this->mNotificationTimestamp[$uid];
4742  }
4743  // Don't cache too much!
4744  if ( count( $this->mNotificationTimestamp ) >= self::CACHE_MAX ) {
4745  $this->mNotificationTimestamp = array();
4746  }
4747  $dbr = wfGetDB( DB_SLAVE );
4748  $this->mNotificationTimestamp[$uid] = $dbr->selectField( 'watchlist',
4749  'wl_notificationtimestamp',
4750  array(
4751  'wl_user' => $user->getId(),
4752  'wl_namespace' => $this->getNamespace(),
4753  'wl_title' => $this->getDBkey(),
4754  ),
4755  __METHOD__
4756  );
4757  return $this->mNotificationTimestamp[$uid];
4758  }
4759 
4766  public function getNamespaceKey( $prepend = 'nstab-' ) {
4768  // Gets the subject namespace if this title
4769  $namespace = MWNamespace::getSubject( $this->getNamespace() );
4770  // Checks if canonical namespace name exists for namespace
4771  if ( MWNamespace::exists( $this->getNamespace() ) ) {
4772  // Uses canonical namespace name
4773  $namespaceKey = MWNamespace::getCanonicalName( $namespace );
4774  } else {
4775  // Uses text of namespace
4776  $namespaceKey = $this->getSubjectNsText();
4777  }
4778  // Makes namespace key lowercase
4779  $namespaceKey = $wgContLang->lc( $namespaceKey );
4780  // Uses main
4781  if ( $namespaceKey == '' ) {
4782  $namespaceKey = 'main';
4783  }
4784  // Changes file to image for backwards compatibility
4785  if ( $namespaceKey == 'file' ) {
4786  $namespaceKey = 'image';
4787  }
4788  return $prepend . $namespaceKey;
4789  }
4790 
4797  public function getRedirectsHere( $ns = null ) {
4798  $redirs = array();
4799 
4800  $dbr = wfGetDB( DB_SLAVE );
4801  $where = array(
4802  'rd_namespace' => $this->getNamespace(),
4803  'rd_title' => $this->getDBkey(),
4804  'rd_from = page_id'
4805  );
4806  if ( $this->isExternal() ) {
4807  $where['rd_interwiki'] = $this->getInterwiki();
4808  } else {
4809  $where[] = 'rd_interwiki = ' . $dbr->addQuotes( '' ) . ' OR rd_interwiki IS NULL';
4810  }
4811  if ( !is_null( $ns ) ) {
4812  $where['page_namespace'] = $ns;
4813  }
4814 
4815  $res = $dbr->select(
4816  array( 'redirect', 'page' ),
4817  array( 'page_namespace', 'page_title' ),
4818  $where,
4819  __METHOD__
4820  );
4821 
4822  foreach ( $res as $row ) {
4823  $redirs[] = self::newFromRow( $row );
4824  }
4825  return $redirs;
4826  }
4827 
4833  public function isValidRedirectTarget() {
4834  global $wgInvalidRedirectTargets;
4835 
4836  // invalid redirect targets are stored in a global array, but explicitly disallow Userlogout here
4837  if ( $this->isSpecial( 'Userlogout' ) ) {
4838  return false;
4839  }
4840 
4841  foreach ( $wgInvalidRedirectTargets as $target ) {
4842  if ( $this->isSpecial( $target ) ) {
4843  return false;
4844  }
4845  }
4846 
4847  return true;
4848  }
4849 
4855  public function getBacklinkCache() {
4856  return BacklinkCache::get( $this );
4857  }
4858 
4864  public function canUseNoindex() {
4865  global $wgContentNamespaces, $wgExemptFromUserRobotsControl;
4866 
4867  $bannedNamespaces = is_null( $wgExemptFromUserRobotsControl )
4868  ? $wgContentNamespaces
4869  : $wgExemptFromUserRobotsControl;
4870 
4871  return !in_array( $this->mNamespace, $bannedNamespaces );
4872 
4873  }
4874 
4885  public function getCategorySortkey( $prefix = '' ) {
4886  $unprefixed = $this->getText();
4887 
4888  // Anything that uses this hook should only depend
4889  // on the Title object passed in, and should probably
4890  // tell the users to run updateCollations.php --force
4891  // in order to re-sort existing category relations.
4892  wfRunHooks( 'GetDefaultSortkey', array( $this, &$unprefixed ) );
4893  if ( $prefix !== '' ) {
4894  # Separate with a line feed, so the unprefixed part is only used as
4895  # a tiebreaker when two pages have the exact same prefix.
4896  # In UCA, tab is the only character that can sort above LF
4897  # so we strip both of them from the original prefix.
4898  $prefix = strtr( $prefix, "\n\t", ' ' );
4899  return "$prefix\n$unprefixed";
4900  }
4901  return $unprefixed;
4902  }
4903 
4912  public function getPageLanguage() {
4913  global $wgLang, $wgLanguageCode;
4914  wfProfileIn( __METHOD__ );
4915  if ( $this->isSpecialPage() ) {
4916  // special pages are in the user language
4917  wfProfileOut( __METHOD__ );
4918  return $wgLang;
4919  }
4920 
4921  if ( !$this->mPageLanguage || $this->mPageLanguage[1] !== $wgLanguageCode ) {
4922  // Note that this may depend on user settings, so the cache should be only per-request.
4923  // NOTE: ContentHandler::getPageLanguage() may need to load the content to determine the page language!
4924  // Checking $wgLanguageCode hasn't changed for the benefit of unit tests.
4925  $contentHandler = ContentHandler::getForTitle( $this );
4926  $langObj = wfGetLangObj( $contentHandler->getPageLanguage( $this ) );
4927  $this->mPageLanguage = array( $langObj->getCode(), $wgLanguageCode );
4928  } else {
4929  $langObj = wfGetLangObj( $this->mPageLanguage[0] );
4930  }
4931  wfProfileOut( __METHOD__ );
4932  return $langObj;
4933  }
4934 
4943  public function getPageViewLanguage() {
4944  global $wgLang;
4945 
4946  if ( $this->isSpecialPage() ) {
4947  // If the user chooses a variant, the content is actually
4948  // in a language whose code is the variant code.
4949  $variant = $wgLang->getPreferredVariant();
4950  if ( $wgLang->getCode() !== $variant ) {
4951  return Language::factory( $variant );
4952  }
4953 
4954  return $wgLang;
4955  }
4956 
4957  //NOTE: can't be cached persistently, depends on user settings
4958  //NOTE: ContentHandler::getPageViewLanguage() may need to load the content to determine the page language!
4959  $contentHandler = ContentHandler::getForTitle( $this );
4960  $pageLang = $contentHandler->getPageViewLanguage( $this );
4961  return $pageLang;
4962  }
4963 
4974  public function getEditNotices( $oldid = 0 ) {
4975  $notices = array();
4976 
4977  # Optional notices on a per-namespace and per-page basis
4978  $editnotice_ns = 'editnotice-' . $this->getNamespace();
4979  $editnotice_ns_message = wfMessage( $editnotice_ns );
4980  if ( $editnotice_ns_message->exists() ) {
4981  $notices[$editnotice_ns] = $editnotice_ns_message->parseAsBlock();
4982  }
4983  if ( MWNamespace::hasSubpages( $this->getNamespace() ) ) {
4984  $parts = explode( '/', $this->getDBkey() );
4985  $editnotice_base = $editnotice_ns;
4986  while ( count( $parts ) > 0 ) {
4987  $editnotice_base .= '-' . array_shift( $parts );
4988  $editnotice_base_msg = wfMessage( $editnotice_base );
4989  if ( $editnotice_base_msg->exists() ) {
4990  $notices[$editnotice_base] = $editnotice_base_msg->parseAsBlock();
4991  }
4992  }
4993  } else {
4994  # Even if there are no subpages in namespace, we still don't want / in MW ns.
4995  $editnoticeText = $editnotice_ns . '-' . str_replace( '/', '-', $this->getDBkey() );
4996  $editnoticeMsg = wfMessage( $editnoticeText );
4997  if ( $editnoticeMsg->exists() ) {
4998  $notices[$editnoticeText] = $editnoticeMsg->parseAsBlock();
4999  }
5000  }
5001 
5002  wfRunHooks( 'TitleGetEditNotices', array( $this, $oldid, &$notices ) );
5003  return $notices;
5004  }
5005 }
Title\$mInterwiki
$mInterwiki
Cascade restrictions on this page to included templates and images?
Definition: Title.php:64
GenderCache\singleton
static singleton()
Definition: GenderCache.php:39
Title\getTitleProtection
getTitleProtection()
Is this title subject to title protection? Title protection is the one applied against creation of su...
Definition: Title.php:2539
ContentHandler\deprecated
static deprecated( $func, $version, $component=false)
Logs a deprecation warning, visible if $wgDevelopmentWarnings, but only if self::$enableDeprecationWa...
Definition: ContentHandler.php:1030
Title\canUseNoindex
canUseNoindex()
Whether the magic words INDEX and NOINDEX function for this page.
Definition: Title.php:4864
Title\inNamespaces
inNamespaces()
Returns true if the title is inside one of the specified namespaces.
Definition: Title.php:1057
Title\getLocalURL
getLocalURL( $query='', $query2=false)
Get a URL with no fragment or server name (relative URL) from a Title object.
Definition: Title.php:1664
Title\makeTitle
static & makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:398
Title\isSemiProtected
isSemiProtected( $action='edit')
Is this page "semi-protected" - the only protection levels are listed in $wgSemiprotectedRestrictionL...
Definition: Title.php:2610
$wgUser
$wgUser
Definition: Setup.php:572
Title\isNamespaceProtected
isNamespaceProtected(User $user)
Determines if $user is unable to edit this page because it has been protected by $wgNamespaceProtecti...
Definition: Title.php:2670
Title\getTalkNsText
getTalkNsText()
Get the namespace text of the talk page.
Definition: Title.php:959
$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. $reader:XMLReader object $logInfo:Array of information Return false to stop further processing of the tag 'ImportHandlePageXMLTag':When parsing a XML tag in a page. $reader:XMLReader object $pageInfo:Array of information Return false to stop further processing of the tag 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information Return false to stop further processing of the tag 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. $reader:XMLReader object Return false to stop further processing of the tag 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. $reader:XMLReader object $revisionInfo:Array of information Return false to stop further processing of the tag '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 '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. '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 '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 '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 wfIsTrustedProxy() $ip:IP being check $result:Change this value to override the result of wfIsTrustedProxy() '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 User::isValidEmailAddr(), 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. '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 '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) '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:Associative array mapping language codes to prefixed links of the form "language:title". & $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. 'LinkBegin':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:1528
File\checkExtensionCompatibility
static checkExtensionCompatibility(File $old, $new)
Checks if file extensions are compatible.
Definition: File.php:225
check
in this case you re responsible for computing and outputting the entire conflict i the difference between revisions and your text headers and sections and Diff overridable Default is either copyrightwarning or copyrightwarning2 overridable Default is editpage tos summary such as anonymity and the real check
Definition: hooks.txt:1038
Title\getSubpageText
getSubpageText()
Get the lowest-level subpage name, i.e.
Definition: Title.php:1488
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:189
MWNamespace\subjectEquals
static subjectEquals( $ns1, $ns2)
Returns whether the specified namespaces share the same subject.
Definition: Namespace.php:205
WikiPage\onArticleCreate
static onArticleCreate( $title)
The onArticle*() functions are supposed to be a kind of hooks which should be called whenever any of ...
Definition: WikiPage.php:3066
Title\getNextRevisionID
getNextRevisionID( $revId, $flags=0)
Get the revision ID of the next revision.
Definition: Title.php:4286
Title\isBigDeletion
isBigDeletion()
Check whether the number of revisions of this page surpasses $wgDeleteRevisionsLimit.
Definition: Title.php:4352
DB_MASTER
const DB_MASTER
Definition: Defines.php:56
MediaWikiTitleCodec
A codec for MediaWiki page titles.
Definition: MediaWikiTitleCodec.php:35
PROTO_CANONICAL
const PROTO_CANONICAL
Definition: Defines.php:271
Title\areCascadeProtectionSourcesLoaded
areCascadeProtectionSourcesLoaded( $getPages=true)
Determines whether cascading protection sources have already been loaded from the database.
Definition: Title.php:2702
RepoGroup\singleton
static singleton()
Get a RepoGroup instance.
Definition: RepoGroup.php:53
Title\getAuthorsBetween
getAuthorsBetween( $old, $new, $limit, $options=array())
Get the authors between the given revisions or revision IDs.
Definition: Title.php:4435
Title\getFilteredRestrictionTypes
static getFilteredRestrictionTypes( $exists=true)
Get a filtered list of all restriction types supported by this wiki.
Definition: Title.php:2494
wfMergeErrorArrays
wfMergeErrorArrays()
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
Definition: GlobalFunctions.php:260
Title\getInternalURL
getInternalURL( $query='', $query2=false)
Get the URL form for an internal link.
Definition: Title.php:1809
$wgActionPaths
$wgActionPaths
Definition: img_auth.php:49
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
Title\getFragment
getFragment()
Get the Title fragment (i.e. the bit after the #) in text form.
Definition: Title.php:1291
MWNamespace\isTalk
static isTalk( $index)
Is the given namespace a talk namespace?
Definition: Namespace.php:107
Revision\newNullRevision
static newNullRevision( $dbw, $pageId, $summary, $minor)
Create a new null-revision for insertion into a page's history.
Definition: Revision.php:1567
Title\inNamespace
inNamespace( $ns)
Returns true if the title is inside the specified namespace.
Definition: Title.php:1046
$tables
namespace and then decline to actually register it RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist & $tables
Definition: hooks.txt:815
TitleArray\newFromResult
static newFromResult( $res)
Definition: TitleArray.php:38
Title\isMovable
isMovable()
Would anybody with sufficient privileges be able to move this page? Some pages just aren't movable.
Definition: Title.php:1106
Title\checkReadPermissions
checkReadPermissions( $action, $user, $errors, $doExpensiveQueries, $short)
Check that the user is allowed to read this page.
Definition: Title.php:2346
Title\$mOldRestrictions
$mOldRestrictions
Cascade restrictions on this page to included templates and images?
Definition: Title.php:71
Title\isJsSubpage
isJsSubpage()
Is this a .js subpage of a user page?
Definition: Title.php:1227
is
We use the convention $dbr for read and $dbw for write to help you keep track of whether the database object is a the world will explode Or to be a subsequent write query which succeeded on the master may fail when replicated to the slave due to a unique key collision Replication on the slave will stop and it may take hours to repair the database and get it back online Setting read_only in my cnf on the slave will avoid this but given the dire we prefer to have as many checks as possible We provide a but the wrapper functions like please read the documentation for except in special pages derived from QueryPage It s a common pitfall for new developers to submit code containing SQL queries which examine huge numbers of rows Remember that COUNT * is(N), counting rows in atable is like counting beans in a bucket.------------------------------------------------------------------------ Replication------------------------------------------------------------------------The largest installation of MediaWiki, Wikimedia, uses a large set ofslave MySQL servers replicating writes made to a master MySQL server. Itis important to understand the issues associated with this setup if youwant to write code destined for Wikipedia.It 's often the case that the best algorithm to use for a given taskdepends on whether or not replication is in use. Due to our unabashedWikipedia-centrism, we often just use the replication-friendly version, but if you like, you can use wfGetLB() ->getServerCount() > 1 tocheck to see if replication is in use.===Lag===Lag primarily occurs when large write queries are sent to the master.Writes on the master are executed in parallel, but they are executed inserial when they are replicated to the slaves. The master writes thequery to the binlog when the transaction is committed. The slaves pollthe binlog and start executing the query as soon as it appears. They canservice reads while they are performing a write query, but will not readanything more from the binlog and thus will perform no more writes. Thismeans that if the write query runs for a long time, the slaves will lagbehind the master for the time it takes for the write query to complete.Lag can be exacerbated by high read load. MediaWiki 's load balancer willstop sending reads to a slave when it is lagged by more than 30 seconds.If the load ratios are set incorrectly, or if there is too much loadgenerally, this may lead to a slave permanently hovering around 30seconds lag.If all slaves are lagged by more than 30 seconds, MediaWiki will stopwriting to the database. All edits and other write operations will berefused, with an error returned to the user. This gives the slaves achance to catch up. Before we had this mechanism, the slaves wouldregularly lag by several minutes, making review of recent editsdifficult.In addition to this, MediaWiki attempts to ensure that the user seesevents occurring on the wiki in chronological order. A few seconds of lagcan be tolerated, as long as the user sees a consistent picture fromsubsequent requests. This is done by saving the master binlog positionin the session, and then at the start of each request, waiting for theslave to catch up to that position before doing any reads from it. Ifthis wait times out, reads are allowed anyway, but the request isconsidered to be in "lagged slave mode". Lagged slave mode can bechecked by calling wfGetLB() ->getLaggedSlaveMode(). The onlypractical consequence at present is a warning displayed in the pagefooter.===Lag avoidance===To avoid excessive lag, queries which write large numbers of rows shouldbe split up, generally to write one row at a time. Multi-row INSERT ...SELECT queries are the worst offenders should be avoided altogether.Instead do the select first and then the insert.===Working with lag===Despite our best efforts, it 's not practical to guarantee a low-lagenvironment. Lag will usually be less than one second, but mayoccasionally be up to 30 seconds. For scalability, it 's very importantto keep load on the master low, so simply sending all your queries tothe master is not the answer. So when you have a genuine need forup-to-date data, the following approach is advised:1) Do a quick query to the master for a sequence number or timestamp 2) Run the full query on the slave and check if it matches the data you gotfrom the master 3) If it doesn 't, run the full query on the masterTo avoid swamping the master every time the slaves lag, use of thisapproach should be kept to a minimum. In most cases you should just readfrom the slave and let the user deal with the delay.------------------------------------------------------------------------ Lock contention------------------------------------------------------------------------Due to the high write rate on Wikipedia(and some other wikis), MediaWiki developers need to be very careful to structure their writesto avoid long-lasting locks. By default, MediaWiki opens a transactionat the first query, and commits it before the output is sent. Locks willbe held from the time when the query is done until the commit. So youcan reduce lock time by doing as much processing as possible before youdo your write queries.Often this approach is not good enough, and it becomes necessary toenclose small groups of queries in their own transaction. Use thefollowing syntax:$dbw=wfGetDB(DB_MASTER
Title\isWikitextPage
isWikitextPage()
Does that page contain wikitext, or it is JS, CSS or whatever?
Definition: Title.php:1159
Title\getBacklinkCache
getBacklinkCache()
Get a backlink cache object.
Definition: Title.php:4855
Title\getPartialURL
getPartialURL()
Get the URL-encoded form of the main part.
Definition: Title.php:848
Title\getTitleValue
getTitleValue()
Get a TitleValue object representing this Title.
Definition: Title.php:818
Title\estimateRevisionCount
estimateRevisionCount()
Get the approximate revision count of this page.
Definition: Title.php:4368
Title\countRevisionsBetween
countRevisionsBetween( $old, $new, $max=null)
Get the number of revisions between the given revision.
Definition: Title.php:4391
Title\getPrefixedDBkey
getPrefixedDBkey()
Get the prefixed database key form.
Definition: Title.php:1357
Title\getLinksTo
getLinksTo( $options=array(), $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:3382
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3714
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\$mNotificationTimestamp
$mNotificationTimestamp
Cascade restrictions on this page to included templates and images?
Definition: Title.php:87
Title\checkActionPermissions
checkActionPermissions( $action, $user, $errors, $doExpensiveQueries, $short)
Check action permissions not already checked in checkQuickPermissions.
Definition: Title.php:2236
Title\getTalkPage
getTalkPage()
Get a Title object associated with the talk page of this article.
Definition: Title.php:1246
Title\newMainPage
static newMainPage()
Create a new Title for the Main Page.
Definition: Title.php:441
Title\getNotificationTimestamp
getNotificationTimestamp( $user=null)
Get the timestamp when this page was updated since the user last saw it.
Definition: Title.php:4727
CONTENT_MODEL_CSS
const CONTENT_MODEL_CSS
Definition: Defines.php:285
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:1560
Title\fixSpecialName
fixSpecialName()
If the Title refers to a special page alias which is not the local default, resolve the alias,...
Definition: Title.php:1023
character
</p > ! end ! test Bare pipe character(bug 52363) !! wikitext|!! html< p >|</p > !! end !! test Bare pipe character from a template(bug 52363) !! wikitext
Definition: parserTests.txt:918
Title\$mDefaultNamespace
$mDefaultNamespace
Cascade restrictions on this page to included templates and images?
Definition: Title.php:83
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
$n
$n
Definition: RandomTest.php:76
Title\touchLinks
touchLinks()
Update page_touched timestamps and send squid purge messages for pages linking to this title.
Definition: Title.php:4699
$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:1530
Title\checkPageRestrictions
checkPageRestrictions( $action, $user, $errors, $doExpensiveQueries, $short)
Check against page_restrictions table requirements on this page.
Definition: Title.php:2155
wfUrlencode
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
Definition: GlobalFunctions.php:377
Title\getPrefixedText
getPrefixedText()
Get the prefixed title with spaces.
Definition: Title.php:1369
Title\getTransWikiID
getTransWikiID()
Returns the DB name of the distant wiki which owns the object.
Definition: Title.php:801
Title\resultToError
resultToError( $errors, $result)
Add the resulting error code to the errors array.
Definition: Title.php:2029
Title\isCssJsSubpage
isCssJsSubpage()
Is this a .css or .js subpage of a user page?
Definition: Title.php:1191
Title\getLinkURL
getLinkURL( $query='', $query2=false, $proto=PROTO_RELATIVE)
Get a URL that's the simplest URL that will be valid to link, locally, to the current Title.
Definition: Title.php:1756
Title\getArticleID
getArticleID( $flags=0)
Get the article ID for this Title from the link cache, adding it if necessary.
Definition: Title.php:3165
$right
return false if a UserGetRights hook might remove the named right $right
Definition: hooks.txt:2809
StringUtils\escapeRegexReplacement
static escapeRegexReplacement( $string)
Escape a string to make it suitable for inclusion in a preg_replace() replacement parameter.
Definition: StringUtils.php:296
Title\$mContentModel
$mContentModel
Cascade restrictions on this page to included templates and images?
Definition: Title.php:68
Title\getSquidURLs
getSquidURLs()
Get a list of URLs to purge from the Squid cache when this page changes.
Definition: Title.php:3547
Title\secureAndSplit
secureAndSplit()
Secure and split - main initialisation function for this object.
Definition: Title.php:3334
Title\quickUserCan
quickUserCan( $action, $user=null)
Can $user perform $action on this page? This skips potentially expensive cascading permission checks ...
Definition: Title.php:1909
NS_FILE
const NS_FILE
Definition: Defines.php:85
Title\checkSpecialsAndNSPermissions
checkSpecialsAndNSPermissions( $action, $user, $errors, $doExpensiveQueries, $short)
Check permissions on special pages & namespaces.
Definition: Title.php:2090
$limit
if( $sleep) $limit
Definition: importImages.php:99
Title\moveNoAuth
moveNoAuth(&$nt)
Move this page without authentication.
Definition: Title.php:3590
Title\isTalkPage
isTalkPage()
Is this a talk page of some sort?
Definition: Title.php:1237
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1360
Interwiki\fetch
static fetch( $prefix)
Fetch an Interwiki object.
Definition: Interwiki.php:77
Sanitizer\decodeCharReferencesAndNormalize
static decodeCharReferencesAndNormalize( $text)
Decode any character references, numeric or named entities, in the next and normalize the resulting s...
Definition: Sanitizer.php:1431
$s
$s
Definition: mergeMessageFileList.php:156
SpecialPage\getTitleFor
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name.
Definition: SpecialPage.php:74
Title\escapeCanonicalURL
escapeCanonicalURL( $query='', $query2=false)
HTML-escaped version of getCanonicalURL()
Definition: Title.php:1844
ContentHandler\getForTitle
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
Definition: ContentHandler.php:259
Title\isExternal
isExternal()
Is this Title interwiki?
Definition: Title.php:767
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:2900
$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\getDefaultMessageText
getDefaultMessageText()
Get the default message text or false if the message doesn't exist.
Definition: Title.php:4652
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:283
Title\hasSourceText
hasSourceText()
Does this page have source text?
Definition: Title.php:4628
Title\$mCascadeRestriction
$mCascadeRestriction
Cascade restrictions on this page to included templates and images?
Definition: Title.php:72
Title\loadFromRow
loadFromRow( $row)
Load Title object fields from a DB row.
Definition: Title.php:357
User\groupHasPermission
static groupHasPermission( $group, $role)
Check, if the given group has the given permission.
Definition: User.php:4166
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:3287
IDBAccessObject\READ_LATEST
const READ_LATEST
Definition: IDBAccessObject.php:49
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2124
Title\setFragment
setFragment( $fragment)
Set the fragment for this title.
Definition: Title.php:1327
Title\convertByteClassToUnicodeClass
static convertByteClassToUnicodeClass( $byteClass)
Utility method for converting a character sequence from bytes to Unicode.
Definition: Title.php:572
cache
you have access to all of the normal MediaWiki so you can get a DB use the cache
Definition: maintenance.txt:52
Title\getUserPermissionsErrorsInternal
getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries=true, $short=false)
Can $user perform $action on this page? This is an internal function, which checks ONLY that previous...
Definition: Title.php:2453
Title\exists
exists()
Check if page exists.
Definition: Title.php:4546
Title\$mHasCascadingRestrictions
$mHasCascadingRestrictions
Are cascading restrictions in effect on this page?
Definition: Title.php:75
Title\getIndexTitle
getIndexTitle()
Get title for search index.
Definition: Title.php:1280
Title\getParentCategories
getParentCategories()
Get categories to which this Title belongs and return an array of categories' names.
Definition: Title.php:4183
Title\userCanRead
userCanRead()
Can $wgUser read this page?
Definition: Title.php:1889
Title\$mRestrictionsLoaded
$mRestrictionsLoaded
Boolean for initialisation on demand.
Definition: Title.php:77
Title\getNsText
getNsText()
Get the namespace text.
Definition: Title.php:922
$dbr
$dbr
Definition: testCompression.php:48
IDBAccessObject\READ_LOCKING
const READ_LOCKING
Definition: IDBAccessObject.php:50
Collation\singleton
static singleton()
Definition: Collation.php:29
ContentHandler\getLocalizedName
static getLocalizedName( $name)
Returns the localized name for a given content model.
Definition: ContentHandler.php:360
Title\getSkinFromCssJsSubpage
getSkinFromCssJsSubpage()
Trim down a .css or .js subpage title to get the corresponding skin name.
Definition: Title.php:1202
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:506
Revision
Definition: Revision.php:26
NS_MAIN
const NS_MAIN
Definition: Defines.php:79
Title\checkCascadingSourcesRestrictions
checkCascadingSourcesRestrictions( $action, $user, $errors, $doExpensiveQueries, $short)
Check restrictions on cascading pages.
Definition: Title.php:2189
Title\isCascadeProtected
isCascadeProtected()
Cascading protection: Return true if cascading restrictions apply to this page, false if not.
Definition: Title.php:2688
Title\isValidMoveTarget
isValidMoveTarget( $nt)
Checks if $this can be moved to a given Title.
Definition: Title.php:4136
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:68
Title\getUserPermissionsErrors
getUserPermissionsErrors( $action, $user, $doExpensiveQueries=true, $ignoreErrors=array())
Can $user perform $action on this page?
Definition: Title.php:1944
$success
$success
Definition: Utf8Test.php:91
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:193
Title\getDBkey
getDBkey()
Get the main part with underscores.
Definition: Title.php:857
Title\nameOf
static nameOf( $id)
Get the prefixed DB key associated with an ID.
Definition: Title.php:507
Title\isSpecial
isSpecial( $name)
Returns true if this title resolves to the named special page.
Definition: Title.php:1007
MWException
MediaWiki exception.
Definition: MWException.php:26
wfStripIllegalFilenameChars
wfStripIllegalFilenameChars( $name)
Replace all invalid characters with - Additional characters can be defined in $wgIllegalFileChars (se...
Definition: GlobalFunctions.php:3908
Title\getTitleFormatter
static getTitleFormatter()
B/C kludge: provide a TitleParser for use by Title.
Definition: Title.php:133
$out
$out
Definition: UtfNormalGenerate.php:167
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:103
Title\isMainPage
isMainPage()
Is this the mainpage?
Definition: Title.php:1127
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1174
$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\escapeLocalURL
escapeLocalURL( $query='', $query2=false)
Get an HTML-escaped version of the URL form, suitable for using in a link, without a server name or f...
Definition: Title.php:1779
Title\getSubpage
getSubpage( $text)
Get the title for a subpage of the current page.
Definition: Title.php:1509
Title\$mTitleValue
$mTitleValue
Cascade restrictions on this page to included templates and images?
Definition: Title.php:90
Title\getBaseTitle
getBaseTitle()
Get the base page name title, i.e.
Definition: Title.php:1473
Title\getNamespace
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:880
BacklinkCache\get
static get(Title $title)
Create a new BacklinkCache or reuse any existing one.
Definition: BacklinkCache.php:103
Title\newFromRow
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:345
Title\isConversionTable
isConversionTable()
Is this a conversion table for the LanguageConverter?
Definition: Title.php:1147
Title\checkPermissionHooks
checkPermissionHooks( $action, $user, $errors, $doExpensiveQueries, $short)
Check various permission hooks.
Definition: Title.php:2057
Title\isProtected
isProtected( $action='')
Does the title correspond to a protected article?
Definition: Title.php:2638
Title\flushRestrictions
flushRestrictions()
Flush the protection cache in this object and force reload from the database.
Definition: Title.php:3024
Title\getCategorySortkey
getCategorySortkey( $prefix='')
Returns the raw sort key to be used for categories, with the specified prefix.
Definition: Title.php:4885
Title\getContentModel
getContentModel()
Get the page's content model id, see the CONTENT_MODEL_XXX constants.
Definition: Title.php:890
Title\getInterwiki
getInterwiki()
Get the interwiki prefix.
Definition: Title.php:778
$parser
do that in ParserLimitReportFormat instead $parser
Definition: hooks.txt:1961
WikiPage\onArticleDelete
static onArticleDelete( $title)
Clears caches when article is deleted.
Definition: WikiPage.php:3087
MWNamespace\canTalk
static canTalk( $index)
Can this namespace ever have a talk namespace?
Definition: Namespace.php:293
Title\isValidRedirectTarget
isValidRedirectTarget()
Check if this Title is a valid redirect target.
Definition: Title.php:4833
Title\deleteTitleProtection
deleteTitleProtection()
Remove any title protection due to page existing.
Definition: Title.php:2592
Title\__construct
__construct()
Constructor.
Definition: Title.php:142
MWNamespace\isContent
static isContent( $index)
Does this namespace contain content, for the purposes of calculating statistics, etc?
Definition: Namespace.php:304
Title\$mEstimateRevisions
$mEstimateRevisions
Cascade restrictions on this page to included templates and images?
Definition: Title.php:69
Title\getBrokenLinksFrom
getBrokenLinksFrom()
Get an array of Title objects referring to non-existent articles linked from this page.
Definition: Title.php:3511
Title\moveToInternal
moveToInternal(&$nt, $reason='', $createRedirect=true)
Move page to a title which is either a redirect to the source page or nonexistent.
Definition: Title.php:3887
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
LogPage
Class to simplify the use of log pages.
Definition: LogPage.php:32
PROTO_CURRENT
const PROTO_CURRENT
Definition: Defines.php:270
wfMessage
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 after processing after in associative array form externallinks including delete and has completed for all link tables default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
Title\missingPermissionError
missingPermissionError( $action, $short)
Get a description array when the user doesn't have the right to perform $action (i....
Definition: Title.php:2421
Title\checkCSSandJSPermissions
checkCSSandJSPermissions( $action, $user, $errors, $doExpensiveQueries, $short)
Check CSS/JS sub-page permissions.
Definition: Title.php:2119
RequestContext\newExtraneousContext
static newExtraneousContext(Title $title, $request=array())
Create a new extraneous context.
Definition: RequestContext.php:535
Title\$mRestrictions
$mRestrictions
Cascade restrictions on this page to included templates and images?
Definition: Title.php:70
WatchedItem\duplicateEntries
static duplicateEntries( $ot, $nt)
Check if the given title already is watched by the user, and if so add watches on a new title.
Definition: WatchedItem.php:351
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4066
Title\getSubjectPage
getSubjectPage()
Get a title object associated with the subject page of this talk page.
Definition: Title.php:1256
MapCacheLRU
Handles a simple LRU key/value map with a maximum number of entries.
Definition: MapCacheLRU.php:33
wfGetLangObj
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
Definition: GlobalFunctions.php:1399
Title\getFullText
getFullText()
Get the prefixed title with spaces, plus any fragment (part beginning with '#')
Definition: Title.php:1393
MWNamespace\hasSubpages
static hasSubpages( $index)
Does the namespace allow subpages?
Definition: Namespace.php:325
Title\getTitleCache
static getTitleCache()
Definition: Title.php:262
Title\hasFragment
hasFragment()
Check if a Title fragment is set.
Definition: Title.php:1301
Title\isCssSubpage
isCssSubpage()
Is this a .css subpage of a user page?
Definition: Title.php:1217
Title\$titleCache
static $titleCache
Definition: Title.php:37
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
Title\getRedirectsHere
getRedirectsHere( $ns=null)
Get all extant redirects to this Title.
Definition: Title.php:4797
Title\checkQuickPermissions
checkQuickPermissions( $action, $user, $errors, $doExpensiveQueries, $short)
Permissions checks that fail most often, and which are easiest to test.
Definition: Title.php:1970
Title\newFromRedirectRecurse
static newFromRedirectRecurse( $text)
Extract a redirect destination from a string and return the Title, or null if the text doesn't contai...
Definition: Title.php:477
MWNamespace\isMovable
static isMovable( $index)
Can pages in the given namespace be moved?
Definition: Namespace.php:67
SpecialPageFactory\exists
static exists( $name)
Check if a given name exist as a special page or as a special page alias.
Definition: SpecialPageFactory.php:327
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
Title\equals
equals(Title $title)
Compare with another title.
Definition: Title.php:4518
Title\$mLength
$mLength
Cascade restrictions on this page to included templates and images?
Definition: Title.php:85
$comment
$comment
Definition: importImages.php:107
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:2561
Title\getRootText
getRootText()
Get the root page name text without a namespace, i.e.
Definition: Title.php:1413
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:93
Title\escapeFullURL
escapeFullURL( $query='', $query2=false)
Get an HTML-escaped version of the URL form, suitable for using in a link, including the server name ...
Definition: Title.php:1792
Title\canExist
canExist()
Is this in a namespace that allows actual pages?
Definition: Title.php:979
SquidUpdate
Handles purging appropriate Squid URLs given a title (or titles)
Definition: SquidUpdate.php:28
Title\checkUserBlock
checkUserBlock( $action, $user, $errors, $doExpensiveQueries, $short)
Check that the user isn't blocked from editing.
Definition: Title.php:2306
URL
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 and does all the work of translating among various forms such as plain URL
Definition: design.txt:25
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:1829
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
LocalFile
Class to represent a local file in the wiki's own database.
Definition: LocalFile.php:46
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:188
MessageCache\singleton
static singleton()
Get the signleton instance of this class.
Definition: MessageCache.php:101
ContentHandler\makeContent
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Definition: ContentHandler.php:144
Title\newFromRedirectArray
static newFromRedirectArray( $text)
Extract a redirect destination from a string and return an array of Titles, or null if the text doesn...
Definition: Title.php:494
Sanitizer\escapeId
static escapeId( $id, $options=array())
Given a value, escape it so that it can be used in an id attribute and return it.
Definition: Sanitizer.php:1100
Title\getDefaultNamespace
getDefaultNamespace()
Get the default namespace index, for when there is no namespace.
Definition: Title.php:1270
$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:1530
Title\newFromTitleValue
static newFromTitleValue(TitleValue $titleValue)
Create a new Title from a TitleValue.
Definition: Title.php:169
Title\compare
static compare( $a, $b)
Callback for usort() to do title sorts by (namespace, title)
Definition: Title.php:738
TS_MW
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
Definition: GlobalFunctions.php:2478
Revision\newFromTitle
static newFromTitle( $title, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given title.
Definition: Revision.php:106
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:980
HTMLCacheUpdate
Class to invalidate the HTML cache of all the pages linking to a given title.
Definition: HTMLCacheUpdate.php:29
Title\$mFragment
$mFragment
Cascade restrictions on this page to included templates and images?
Definition: Title.php:65
Title\getPrefixedURL
getPrefixedURL()
Get a URL-encoded title (not an actual URL) including interwiki.
Definition: Title.php:1541
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:422
Title\__toString
__toString()
Return a string representation of this title.
Definition: Title.php:1383
$title
presenting them properly to the user as errors is done by the caller $title
Definition: hooks.txt:1324
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
User\whoIs
static whoIs( $id)
Get the username corresponding to a given user ID.
Definition: User.php:484
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
Title\countAuthorsBetween
countAuthorsBetween( $old, $new, $limit, $options=array())
Get the number of authors between the given revisions or revision IDs.
Definition: Title.php:4507
Title\areRestrictionsCascading
areRestrictionsCascading()
Returns cascading restrictions for the current article.
Definition: Title.php:2866
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:67
Title\getLatestRevID
getLatestRevID( $flags=0)
What is the page_latest field for this page?
Definition: Title.php:3254
Title\areRestrictionsLoaded
areRestrictionsLoaded()
Accessor for mRestrictionsLoaded.
Definition: Title.php:2813
TitleValue\getFragment
getFragment()
Definition: TitleValue.php:103
Title\getSubpages
getSubpages( $limit=-1)
Get all subpages of this page.
Definition: Title.php:3086
Title\isSingleRevRedirect
isSingleRevRedirect()
Checks if this page is just a one-rev redirect.
Definition: Title.php:4089
Title\$mLatestID
$mLatestID
Cascade restrictions on this page to included templates and images?
Definition: Title.php:67
Title\GAID_FOR_UPDATE
const GAID_FOR_UPDATE
Used to be GAID_FOR_UPDATE define.
Definition: Title.php:50
Title\getPageViewLanguage
getPageViewLanguage()
Get the language in which the content of this page is written when viewed by user.
Definition: Title.php:4943
Title\newFromDBkey
static newFromDBkey( $key)
Create a new Title from a prefixed DB key.
Definition: Title.php:152
TitleValue\getText
getText()
Returns the title in text form, without namespace prefix or fragment.
Definition: TitleValue.php:128
SearchEngine\legalSearchChars
static legalSearchChars()
Definition: SearchEngine.php:256
Title\isSubpage
isSubpage()
Is this a subpage?
Definition: Title.php:1136
Title\getParentCategoryTree
getParentCategoryTree( $children=array())
Get a tree of parent categories.
Definition: Title.php:4218
Title\hasContentModel
hasContentModel( $id)
Convenience method for checking a title's content model name.
Definition: Title.php:913
Title\getRestrictionExpiry
getRestrictionExpiry( $action)
Get the expiry time for the restriction against a given action.
Definition: Title.php:2854
Title\$mCascadeSources
$mCascadeSources
Where are the cascading restrictions coming from on this page?
Definition: Title.php:76
Title\newFromURL
static newFromURL( $url)
THIS IS NOT THE FUNCTION YOU WANT.
Definition: Title.php:241
Title\isValidMoveOperation
isValidMoveOperation(&$nt, $auth=true, $reason='')
Check whether a given move operation would be valid.
Definition: Title.php:3604
PROTO_HTTP
const PROTO_HTTP
Definition: Defines.php:267
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:272
PROTO_RELATIVE
const PROTO_RELATIVE
Definition: Defines.php:269
Title\$mPageLanguage
$mPageLanguage
Cascade restrictions on this page to included templates and images?
Definition: Title.php:89
SpecialPageFactory\getLocalNameFor
static getLocalNameFor( $name, $subpage=false)
Get the local name for a specified canonical name.
Definition: SpecialPageFactory.php:562
Title\isSubpageOf
isSubpageOf(Title $title)
Check if this title is a subpage of another title.
Definition: Title.php:4531
Title\isLocal
isLocal()
Determine whether the object refers to a page within this project.
Definition: Title.php:752
MWNamespace\exists
static exists( $index)
Returns whether the specified namespace exists.
Definition: Namespace.php:171
Title\newFromIDs
static newFromIDs( $ids)
Make an array of titles from an array of IDs.
Definition: Title.php:319
RequestContext\getMain
static getMain()
Static methods.
Definition: RequestContext.php:420
Title\prefix
prefix( $name)
Prefix some arbitrary text with the namespace or interwiki prefix of this object.
Definition: Title.php:1339
$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:237
Title\getNamespaceKey
getNamespaceKey( $prepend='nstab-')
Generate strings used for xml 'id' names in monobook tabs.
Definition: Title.php:4766
Title\getEditURL
getEditURL()
Get the edit URL for this Title.
Definition: Title.php:1855
only
published in in Madrid In the first edition of the Vocabolario for was published In in Rotterdam was the Dictionnaire Universel ! html< p > The first monolingual dictionary written in a Romance language was< i > Sebastián Covarrubias</i >< i > Tesoro de la lengua castellana o published in in Madrid In the first edition of the< i > Vocabolario dell< a href="/index.php?title=Accademia_della_Crusca&amp;action=edit&amp;redlink=1" class="new" title="Accademia della Crusca (page does not exist)"> Accademia della Crusca</a ></i > for was published In in Rotterdam was the< i > Dictionnaire Universel</i ></p > ! end ! test Italics and ! wikitext foo ! html< p >< i > foo</i ></p > !end ! test Italics and ! wikitext foo ! html< p >< i > foo</i ></p > !end ! test Italics and ! wikitext foo ! html< p >< i > foo</i ></p > !end ! test Italics and ! wikitext foo ! html php< p >< i > foo</i ></p > ! html parsoid< p >< i > foo</i >< b ></b ></p > !end ! test Italics and ! wikitext foo ! html< p >< i > foo</i ></p > !end ! test Italics and ! wikitext foo ! html< p >< b > foo</b ></p > !end ! test Italics and ! wikitext foo ! html< p >< b > foo</b ></p > !end ! test Italics and ! wikitext foo ! html php< p >< b > foo</b ></p > ! html parsoid< p >< b > foo</b >< i ></i ></p > !end ! test Italics and ! wikitext foo ! html< p >< i > foo</i ></p > !end ! test Italics and ! wikitext foo ! html< p >< b > foo</b ></p > !end ! test Italics and ! wikitext foo ! html< p >< b > foo</b ></p > !end ! test Italics and ! wikitext foo ! html php< p >< b > foo</b ></p > ! html parsoid< p >< b > foo</b >< i ></i ></p > !end ! test Italics and ! options ! wikitext foo ! html< p >< b >< i > foo</i ></b ></p > !end ! test Italics and ! wikitext foo ! html< p >< i >< b > foo</b ></i ></p > !end ! test Italics and ! wikitext foo ! html< p >< i >< b > foo</b ></i ></p > !end ! test Italics and ! wikitext foo ! html< p >< i >< b > foo</b ></i ></p > !end ! test Italics and ! wikitext foo bar ! html< p >< i > foo< b > bar</b ></i ></p > !end ! test Italics and ! wikitext foo bar ! html< p >< i > foo< b > bar</b ></i ></p > !end ! test Italics and ! wikitext foo bar ! html< p >< i > foo< b > bar</b ></i ></p > !end ! test Italics and ! wikitext foo bar ! html php< p >< b > foo</b > bar</p > ! html parsoid< p >< b > foo</b > bar< i ></i ></p > !end ! test Italics and ! wikitext foo bar ! html php< p >< b > foo</b > bar</p > ! html parsoid< p >< b > foo</b > bar< b ></b ></p > !end ! test Italics and ! wikitext this is about foo s family ! html< p >< i > this is about< b > foo s family</b ></i ></p > !end ! test Italics and ! wikitext this is about foo s family ! html< p >< i > this is about< b > foo s</b > family</i ></p > !end ! test Italics and ! wikitext this is about foo s family ! html< p >< b > this is about< i > foo</i ></b >< i > s family</i ></p > !end ! test Italics and ! options ! wikitext this is about foo s family ! html< p >< i > this is about</i > foo< b > s family</b ></p > !end ! test Italics and ! wikitext this is about foo s family ! html< p >< b > this is about< i > foo s</i > family</b ></p > !end ! test Italicized possessive ! wikitext The s talk page ! html< p > The< i >< a href="/wiki/Main_Page" title="Main Page"> Main Page</a ></i > s talk page</p > ! end ! test Parsoid only
Definition: parserTests.txt:396
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:1598
Title\getTitleParser
static getTitleParser()
B/C kludge: provide a TitleParser for use by Title.
Definition: Title.php:101
Title\isKnown
isKnown()
Does this title refer to a page that can (or might) be meaningfully viewed? In particular,...
Definition: Title.php:4619
$file
if(PHP_SAPI !='cli') $file
Definition: UtfNormalTest2.php:30
$count
$count
Definition: UtfNormalTest2.php:96
$wgArticlePath
$wgArticlePath
Definition: img_auth.php:48
Title\validateFileMoveOperation
validateFileMoveOperation( $nt)
Check if the requested move target is a valid file move target.
Definition: Title.php:3711
$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:1337
TitleValue\getNamespace
getNamespace()
Definition: TitleValue.php:96
Title\$mRedirect
$mRedirect
Cascade restrictions on this page to included templates and images?
Definition: Title.php:86
Title\userCan
userCan( $action, $user=null, $doExpensiveQueries=true)
Can $user perform $action on this page?
Definition: Title.php:1923
Title\getFirstRevision
getFirstRevision( $flags=0)
Get the first revision of the page.
Definition: Title.php:4310
Title\getAllRestrictions
getAllRestrictions()
Accessor/initialisation for mRestrictions.
Definition: Title.php:2840
Title\getEarliestRevTime
getEarliestRevTime( $flags=0)
Get the oldest revision timestamp of this page.
Definition: Title.php:4332
User\isEveryoneAllowed
static isEveryoneAllowed( $right)
Check if all users have the given permission.
Definition: User.php:4179
DB_SLAVE
const DB_SLAVE
Definition: Defines.php:55
Title
Represents a title within MediaWiki.
Definition: Title.php:35
Title\moveTo
moveTo(&$nt, $auth=true, $reason='', $createRedirect=true)
Move a title to a new location.
Definition: Title.php:3756
$namespaces
namespace and then decline to actually register it & $namespaces
Definition: hooks.txt:815
$wgLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as $wgLang
Definition: design.txt:56
Title\$mCascadingRestrictions
$mCascadingRestrictions
Cascade restrictions on this page to included templates and images?
Definition: Title.php:73
Title\canTalk
canTalk()
Could this title have a corresponding talk page?
Definition: Title.php:969
Title\getCascadeProtectionSources
getCascadeProtectionSources( $getPages=true)
Cascading protection: Get the source of any cascading restrictions on this page.
Definition: Title.php:2716
$cache
$cache
Definition: mcc.php:32
Title\getRestrictionTypes
getRestrictionTypes()
Returns restriction types for the current Title.
Definition: Title.php:2512
MalformedTitleException
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Definition: MalformedTitleException.php:31
Title\updateTitleProtection
updateTitleProtection( $create_perm, $reason, $expiry)
Update the title protection status.
Definition: Title.php:2574
Title\loadRestrictionsFromResultWrapper
loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions=null)
Loads a string into mRestrictions array.
Definition: Title.php:2881
Title\isRedirect
isRedirect( $flags=0)
Is this an article that is a redirect page? Uses link cache, adding it if necessary.
Definition: Title.php:3191
MWNamespace\equals
static equals( $ns1, $ns2)
Returns whether the specified namespaces are the same namespace.
Definition: Namespace.php:190
in
Prior to maintenance scripts were a hodgepodge of code that had no cohesion or formal method of action Beginning in
Definition: maintenance.txt:1
Title\isDeleted
isDeleted()
Is there a version of this page in the deletion archive?
Definition: Title.php:3114
Title\makeName
static makeName( $ns, $title, $fragment='', $interwiki='')
Make a prefixed DB key from a DB key and a namespace index.
Definition: Title.php:702
Title\getLinksFrom
getLinksFrom( $options=array(), $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:3440
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:3313
Title\$mTitleProtection
$mTitleProtection
Cached value for getTitleProtection (create protection)
Definition: Title.php:79
Title\hasSubjectNamespace
hasSubjectNamespace( $ns)
Returns true if the title has the same subject namespace as the namespace specified.
Definition: Title.php:1085
Title\isSpecialPage
isSpecialPage()
Returns true if this is a special page.
Definition: Title.php:997
Title\purgeSquid
purgeSquid()
Purge all applicable Squid URLs.
Definition: Title.php:3575
Title\getPreviousRevisionID
getPreviousRevisionID( $revId, $flags=0)
Get the revision ID of the previous revision.
Definition: Title.php:4261
TitleArray
The TitleArray class only exists to provide the newFromResult method at pre- sent.
Definition: TitleArray.php:31
Title\getPageLanguage
getPageLanguage()
Get the language in which the content of this page is written in wikitext.
Definition: Title.php:4912
Title\CACHE_MAX
const CACHE_MAX
Title::newFromText maintains a cache to avoid expensive re-normalization of commonly used titles.
Definition: Title.php:44
Title\getEditNotices
getEditNotices( $oldid=0)
Get a list of rendered edit notices for this page.
Definition: Title.php:4974
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:4715
wfFindFile
wfFindFile( $title, $options=array())
Find a file.
Definition: GlobalFunctions.php:3757
Title\$mUserCaseDBKey
$mUserCaseDBKey
Cascade restrictions on this page to included templates and images?
Definition: Title.php:62
MWNamespace\isCapitalized
static isCapitalized( $index)
Is the namespace first-letter capitalized?
Definition: Namespace.php:378
Title\isCssOrJsPage
isCssOrJsPage()
Could this page contain custom CSS or JavaScript for the global UI.
Definition: Title.php:1174
Title\getFragmentForURL
getFragmentForURL()
Get the fragment in URL form, including the "#" character if there is one.
Definition: Title.php:1309
NS_USER
const NS_USER
Definition: Defines.php:81
Title\$mWatched
$mWatched
Cascade restrictions on this page to included templates and images?
Definition: Title.php:84
Title\$mDbkeyform
$mDbkeyform
Cascade restrictions on this page to included templates and images?
Definition: Title.php:61
ManualLogEntry
Class for creating log entries manually, for example to inject them into the database.
Definition: LogEntry.php:339
$wgNamespaceProtection
if(!isset( $wgVersion)) if( $wgScript===false) if( $wgLoadScript===false) if( $wgArticlePath===false) if(!empty( $wgActionPaths) &&!isset( $wgActionPaths['view'])) if( $wgStylePath===false) if( $wgLocalStylePath===false) if( $wgStyleDirectory===false) if( $wgExtensionAssetsPath===false) if( $wgLogo===false) if( $wgUploadPath===false) if( $wgUploadDirectory===false) if( $wgReadOnlyFile===false) if( $wgFileCacheDirectory===false) if( $wgDeletedDirectory===false) if(isset( $wgFileStore['deleted']['directory'])) if(isset( $wgFooterIcons['copyright']) &&isset( $wgFooterIcons['copyright']['copyright']) && $wgFooterIcons['copyright']['copyright']===array()) if(isset( $wgFooterIcons['poweredby']) &&isset( $wgFooterIcons['poweredby']['mediawiki']) && $wgFooterIcons['poweredby']['mediawiki']['src']===null) $wgNamespaceProtection[NS_MEDIAWIKI]
Unconditional protection for NS_MEDIAWIKI since otherwise it's too easy for a sysadmin to set $wgName...
Definition: Setup.php:135
Title\isAlwaysKnown
isAlwaysKnown()
Should links to this title be shown as potentially viewable (i.e.
Definition: Title.php:4566
Title\isDeletedQuick
isDeletedQuick()
Is there a version of this page in the deletion archive?
Definition: Title.php:3139
Title\getSubpageUrlForm
getSubpageUrlForm()
Get a URL-encoded form of the subpage text.
Definition: Title.php:1530
Language\factory
static factory( $code)
Get a cached or new language object for a given language code.
Definition: Language.php:184
Title\isNewPage
isNewPage()
Check if this is a new page.
Definition: Title.php:4342
Title\moveSubpages
moveSubpages( $nt, $auth=true, $reason='', $createRedirect=true)
Move this page's subpages to be subpages of $nt.
Definition: Title.php:4022
Title\newFromRedirect
static newFromRedirect( $text)
Extract a redirect destination from a string and return the Title, or null if the text doesn't contai...
Definition: Title.php:460
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:87
CONTENT_MODEL_JAVASCRIPT
const CONTENT_MODEL_JAVASCRIPT
Definition: Defines.php:284
Title\getTitleInvalidRegex
static getTitleInvalidRegex()
Returns a simple regex that will match on characters and sequences invalid in titles.
Definition: Title.php:543
$t
$t
Definition: testCompression.php:65
Title\legalChars
static legalChars()
Get a regex character class describing the legal characters in a link.
Definition: Title.php:529
Title\getEscapedText
getEscapedText()
Get the HTML-escaped displayable text form.
Definition: Title.php:1520
MWNamespace\isWatchable
static isWatchable( $index)
Can pages in a namespace be watched?
Definition: Namespace.php:315
Title\getTemplateLinksFrom
getTemplateLinksFrom( $options=array())
Get an array of Title objects used on this Title as a template Also stores the IDs in the link cache.
Definition: Title.php:3501
Title\isTrans
isTrans()
Determine whether the object refers to a page within this project and is transcludable.
Definition: Title.php:788
$error
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead where the first element is the message key and the remaining elements are used as parameters to the message based on mime etc Preferred in most cases over UploadVerification object with all info about the upload string as detected by MediaWiki Handlers will typically only apply for specific mime types object & $error
Definition: hooks.txt:2584
Title\getRestrictions
getRestrictions( $action)
Accessor/initialisation for mRestrictions.
Definition: Title.php:2823
Revision\selectFields
static selectFields()
Return the list of revision fields that should be selected to create a new revision.
Definition: Revision.php:405
MWNamespace\getTalk
static getTalk( $index)
Get the talk namespace index for a given namespace.
Definition: Namespace.php:118
Title\$mNamespace
$mNamespace
Cascade restrictions on this page to included templates and images?
Definition: Title.php:63
$query
return true to allow those checks to and false if checking is done use this to change the tables headers temp or archived zone change it to an object instance and return false override the list derivative used the name of the old file when set the default code will be skipped add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1105
Title\indexTitle
static indexTitle( $ns, $title)
Get a string representation of a title suitable for including in a search index.
Definition: Title.php:673
MWNamespace\getSubject
static getSubject( $index)
Get the subject namespace index for a given namespace Special namespaces (NS_MEDIA,...
Definition: Namespace.php:132
Title\isContentPage
isContentPage()
Is this Title in a namespace which contains content? In other words, is this a content page,...
Definition: Title.php:1096
Title\$mHasSubpage
$mHasSubpage
Cascade restrictions on this page to included templates and images?
Definition: Title.php:88
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:59
wfLocalFile
wfLocalFile( $title)
Get an object referring to a locally registered file.
Definition: GlobalFunctions.php:3768
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:297
Title\invalidateCache
invalidateCache()
Updates page_touched for this page; called from LinksUpdate.php.
Definition: Title.php:4674
Title\getUserCaseDBKey
getUserCaseDBKey()
Get the DB key with the initial letter case as specified by the user.
Definition: Title.php:866
Title\getTemplateLinksTo
getTemplateLinksTo( $options=array())
Get an array of Title objects using this Title as a template Also stores the IDs in the link cache.
Definition: Title.php:3424
$res
$res
Definition: database.txt:21
LinkCache\singleton
static & singleton()
Get an instance of this class.
Definition: LinkCache.php:49
Title\getSelectFields
static getSelectFields()
Returns a list of fields that are to be selected for initializing Title objects or LinkCache entries.
Definition: Title.php:275
Title\hasSubpages
hasSubpages()
Does this have subpages? (Warning, usually requires an extra DB query.)
Definition: Title.php:3058
Title\escapeFragmentForURL
static escapeFragmentForURL( $fragment)
Escape a text fragment, say from a link, for a URL.
Definition: Title.php:722
$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:237
Title\getBaseText
getBaseText()
Get the base page name without a namespace, i.e.
Definition: Title.php:1448
Title\pageCond
pageCond()
Get an associative array for selecting this title from the "page" table.
Definition: Title.php:4245
Title\getText
getText()
Get the text form (spaces not underscores) of the main part.
Definition: Title.php:839
MWNamespace\getCanonicalName
static getCanonicalName( $index)
Returns the canonical (English) name for a given index.
Definition: Namespace.php:237
Title\purgeExpiredRestrictions
static purgeExpiredRestrictions()
Purge expired restrictions from the page_restrictions table.
Definition: Title.php:3032
Title\$mTextform
$mTextform
Cascade restrictions on this page to included templates and images?
Definition: Title.php:59
Title\$mArticleID
$mArticleID
Cascade restrictions on this page to included templates and images?
Definition: Title.php:66
wfExpandUrl
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
Definition: GlobalFunctions.php:544
User\getGroupsWithPermission
static getGroupsWithPermission( $role)
Get all the groups who have a given permission.
Definition: User.php:4143
Title\getSubjectNsText
getSubjectNsText()
Get the namespace text of the subject (rather than talk) page.
Definition: Title.php:949
Title\userIsWatching
userIsWatching()
Is $wgUser watching this page?
Definition: Title.php:1870
Title\$mRestrictionsExpiry
$mRestrictionsExpiry
When do the restrictions on this page expire?
Definition: Title.php:74
Title\loadRestrictions
loadRestrictions( $oldFashionedRestrictions=null)
Load restrictions from the page_restrictions table.
Definition: Title.php:2983
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:36
Title\$mUrlform
$mUrlform
Cascade restrictions on this page to included templates and images?
Definition: Title.php:60
LogFormatter\newFromEntry
static newFromEntry(LogEntry $entry)
Constructs a new formatter suitable for given entry.
Definition: LogFormatter.php:45
wfArrayToCgi
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes two arrays as input, and returns a CGI-style string, e.g.
Definition: GlobalFunctions.php:414
Title\$mPrefixedText
$mPrefixedText
Text form including namespace/interwiki, initialised on demand.
Definition: Title.php:78
Title\getFullUrlForRedirect
getFullUrlForRedirect( $query='', $proto=PROTO_CURRENT)
Get a url appropriate for making redirects based on an untrusted url arg.
Definition: Title.php:1632
Title\getRootTitle
getRootTitle()
Get the root page name title, i.e.
Definition: Title.php:1433
$type
$type
Definition: testCompression.php:46
Title\getLength
getLength( $flags=0)
What is the length of this page? Uses link cache, adding it if necessary.
Definition: Title.php:3226
Title\isWatchable
isWatchable()
Can this title be added to a user's watchlist?
Definition: Title.php:988