MediaWiki  1.23.8
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 
1637  public function getLocalURL( $query = '', $query2 = false ) {
1638  global $wgArticlePath, $wgScript, $wgServer, $wgRequest;
1639 
1640  $query = self::fixUrlQueryArgs( $query, $query2 );
1641 
1642  $interwiki = Interwiki::fetch( $this->mInterwiki );
1643  if ( $interwiki ) {
1644  $namespace = $this->getNsText();
1645  if ( $namespace != '' ) {
1646  # Can this actually happen? Interwikis shouldn't be parsed.
1647  # Yes! It can in interwiki transclusion. But... it probably shouldn't.
1648  $namespace .= ':';
1649  }
1650  $url = $interwiki->getURL( $namespace . $this->getDBkey() );
1651  $url = wfAppendQuery( $url, $query );
1652  } else {
1653  $dbkey = wfUrlencode( $this->getPrefixedDBkey() );
1654  if ( $query == '' ) {
1655  $url = str_replace( '$1', $dbkey, $wgArticlePath );
1656  wfRunHooks( 'GetLocalURL::Article', array( &$this, &$url ) );
1657  } else {
1658  global $wgVariantArticlePath, $wgActionPaths, $wgContLang;
1659  $url = false;
1660  $matches = array();
1661 
1662  if ( !empty( $wgActionPaths )
1663  && preg_match( '/^(.*&|)action=([^&]*)(&(.*)|)$/', $query, $matches )
1664  ) {
1665  $action = urldecode( $matches[2] );
1666  if ( isset( $wgActionPaths[$action] ) ) {
1667  $query = $matches[1];
1668  if ( isset( $matches[4] ) ) {
1669  $query .= $matches[4];
1670  }
1671  $url = str_replace( '$1', $dbkey, $wgActionPaths[$action] );
1672  if ( $query != '' ) {
1673  $url = wfAppendQuery( $url, $query );
1674  }
1675  }
1676  }
1677 
1678  if ( $url === false
1679  && $wgVariantArticlePath
1680  && $wgContLang->getCode() === $this->getPageLanguage()->getCode()
1681  && $this->getPageLanguage()->hasVariants()
1682  && preg_match( '/^variant=([^&]*)$/', $query, $matches )
1683  ) {
1684  $variant = urldecode( $matches[1] );
1685  if ( $this->getPageLanguage()->hasVariant( $variant ) ) {
1686  // Only do the variant replacement if the given variant is a valid
1687  // variant for the page's language.
1688  $url = str_replace( '$2', urlencode( $variant ), $wgVariantArticlePath );
1689  $url = str_replace( '$1', $dbkey, $url );
1690  }
1691  }
1692 
1693  if ( $url === false ) {
1694  if ( $query == '-' ) {
1695  $query = '';
1696  }
1697  $url = "{$wgScript}?title={$dbkey}&{$query}";
1698  }
1699  }
1700 
1701  wfRunHooks( 'GetLocalURL::Internal', array( &$this, &$url, $query ) );
1702 
1703  // @todo FIXME: This causes breakage in various places when we
1704  // actually expected a local URL and end up with dupe prefixes.
1705  if ( $wgRequest->getVal( 'action' ) == 'render' ) {
1706  $url = $wgServer . $url;
1707  }
1708  }
1709  wfRunHooks( 'GetLocalURL', array( &$this, &$url, $query ) );
1710  return $url;
1711  }
1712 
1729  public function getLinkURL( $query = '', $query2 = false, $proto = PROTO_RELATIVE ) {
1730  wfProfileIn( __METHOD__ );
1731  if ( $this->isExternal() || $proto !== PROTO_RELATIVE ) {
1732  $ret = $this->getFullURL( $query, $query2, $proto );
1733  } elseif ( $this->getPrefixedText() === '' && $this->hasFragment() ) {
1734  $ret = $this->getFragmentForURL();
1735  } else {
1736  $ret = $this->getLocalURL( $query, $query2 ) . $this->getFragmentForURL();
1737  }
1738  wfProfileOut( __METHOD__ );
1739  return $ret;
1740  }
1741 
1752  public function escapeLocalURL( $query = '', $query2 = false ) {
1753  wfDeprecated( __METHOD__, '1.19' );
1754  return htmlspecialchars( $this->getLocalURL( $query, $query2 ) );
1755  }
1756 
1765  public function escapeFullURL( $query = '', $query2 = false ) {
1766  wfDeprecated( __METHOD__, '1.19' );
1767  return htmlspecialchars( $this->getFullURL( $query, $query2 ) );
1768  }
1769 
1782  public function getInternalURL( $query = '', $query2 = false ) {
1783  global $wgInternalServer, $wgServer;
1784  $query = self::fixUrlQueryArgs( $query, $query2 );
1785  $server = $wgInternalServer !== false ? $wgInternalServer : $wgServer;
1786  $url = wfExpandUrl( $server . $this->getLocalURL( $query ), PROTO_HTTP );
1787  wfRunHooks( 'GetInternalURL', array( &$this, &$url, $query ) );
1788  return $url;
1789  }
1790 
1802  public function getCanonicalURL( $query = '', $query2 = false ) {
1803  $query = self::fixUrlQueryArgs( $query, $query2 );
1804  $url = wfExpandUrl( $this->getLocalURL( $query ) . $this->getFragmentForURL(), PROTO_CANONICAL );
1805  wfRunHooks( 'GetCanonicalURL', array( &$this, &$url, $query ) );
1806  return $url;
1807  }
1808 
1817  public function escapeCanonicalURL( $query = '', $query2 = false ) {
1818  wfDeprecated( __METHOD__, '1.19' );
1819  return htmlspecialchars( $this->getCanonicalURL( $query, $query2 ) );
1820  }
1821 
1828  public function getEditURL() {
1829  if ( $this->isExternal() ) {
1830  return '';
1831  }
1832  $s = $this->getLocalURL( 'action=edit' );
1833 
1834  return $s;
1835  }
1836 
1843  public function userIsWatching() {
1844  global $wgUser;
1845 
1846  if ( is_null( $this->mWatched ) ) {
1847  if ( NS_SPECIAL == $this->mNamespace || !$wgUser->isLoggedIn() ) {
1848  $this->mWatched = false;
1849  } else {
1850  $this->mWatched = $wgUser->isWatched( $this );
1851  }
1852  }
1853  return $this->mWatched;
1854  }
1855 
1862  public function userCanRead() {
1863  wfDeprecated( __METHOD__, '1.19' );
1864  return $this->userCan( 'read' );
1865  }
1866 
1882  public function quickUserCan( $action, $user = null ) {
1883  return $this->userCan( $action, $user, false );
1884  }
1885 
1896  public function userCan( $action, $user = null, $doExpensiveQueries = true ) {
1897  if ( !$user instanceof User ) {
1898  global $wgUser;
1899  $user = $wgUser;
1900  }
1901  return !count( $this->getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries, true ) );
1902  }
1903 
1917  public function getUserPermissionsErrors( $action, $user, $doExpensiveQueries = true, $ignoreErrors = array() ) {
1918  $errors = $this->getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries );
1919 
1920  // Remove the errors being ignored.
1921  foreach ( $errors as $index => $error ) {
1922  $error_key = is_array( $error ) ? $error[0] : $error;
1923 
1924  if ( in_array( $error_key, $ignoreErrors ) ) {
1925  unset( $errors[$index] );
1926  }
1927  }
1928 
1929  return $errors;
1930  }
1931 
1943  private function checkQuickPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
1944  if ( !wfRunHooks( 'TitleQuickPermissions', array( $this, $user, $action, &$errors, $doExpensiveQueries, $short ) ) ) {
1945  return $errors;
1946  }
1947 
1948  if ( $action == 'create' ) {
1949  if (
1950  ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
1951  ( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) )
1952  ) {
1953  $errors[] = $user->isAnon() ? array( 'nocreatetext' ) : array( 'nocreate-loggedin' );
1954  }
1955  } elseif ( $action == 'move' ) {
1956  if ( !$user->isAllowed( 'move-rootuserpages' )
1957  && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
1958  // Show user page-specific message only if the user can move other pages
1959  $errors[] = array( 'cant-move-user-page' );
1960  }
1961 
1962  // Check if user is allowed to move files if it's a file
1963  if ( $this->mNamespace == NS_FILE && !$user->isAllowed( 'movefile' ) ) {
1964  $errors[] = array( 'movenotallowedfile' );
1965  }
1966 
1967  if ( !$user->isAllowed( 'move' ) ) {
1968  // User can't move anything
1969  $userCanMove = User::groupHasPermission( 'user', 'move' );
1970  $autoconfirmedCanMove = User::groupHasPermission( 'autoconfirmed', 'move' );
1971  if ( $user->isAnon() && ( $userCanMove || $autoconfirmedCanMove ) ) {
1972  // custom message if logged-in users without any special rights can move
1973  $errors[] = array( 'movenologintext' );
1974  } else {
1975  $errors[] = array( 'movenotallowed' );
1976  }
1977  }
1978  } elseif ( $action == 'move-target' ) {
1979  if ( !$user->isAllowed( 'move' ) ) {
1980  // User can't move anything
1981  $errors[] = array( 'movenotallowed' );
1982  } elseif ( !$user->isAllowed( 'move-rootuserpages' )
1983  && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
1984  // Show user page-specific message only if the user can move other pages
1985  $errors[] = array( 'cant-move-to-user-page' );
1986  }
1987  } elseif ( !$user->isAllowed( $action ) ) {
1988  $errors[] = $this->missingPermissionError( $action, $short );
1989  }
1990 
1991  return $errors;
1992  }
1993 
2002  private function resultToError( $errors, $result ) {
2003  if ( is_array( $result ) && count( $result ) && !is_array( $result[0] ) ) {
2004  // A single array representing an error
2005  $errors[] = $result;
2006  } elseif ( is_array( $result ) && is_array( $result[0] ) ) {
2007  // A nested array representing multiple errors
2008  $errors = array_merge( $errors, $result );
2009  } elseif ( $result !== '' && is_string( $result ) ) {
2010  // A string representing a message-id
2011  $errors[] = array( $result );
2012  } elseif ( $result === false ) {
2013  // a generic "We don't want them to do that"
2014  $errors[] = array( 'badaccess-group0' );
2015  }
2016  return $errors;
2017  }
2018 
2030  private function checkPermissionHooks( $action, $user, $errors, $doExpensiveQueries, $short ) {
2031  // Use getUserPermissionsErrors instead
2032  $result = '';
2033  if ( !wfRunHooks( 'userCan', array( &$this, &$user, $action, &$result ) ) ) {
2034  return $result ? array() : array( array( 'badaccess-group0' ) );
2035  }
2036  // Check getUserPermissionsErrors hook
2037  if ( !wfRunHooks( 'getUserPermissionsErrors', array( &$this, &$user, $action, &$result ) ) ) {
2038  $errors = $this->resultToError( $errors, $result );
2039  }
2040  // Check getUserPermissionsErrorsExpensive hook
2041  if (
2042  $doExpensiveQueries
2043  && !( $short && count( $errors ) > 0 )
2044  && !wfRunHooks( 'getUserPermissionsErrorsExpensive', array( &$this, &$user, $action, &$result ) )
2045  ) {
2046  $errors = $this->resultToError( $errors, $result );
2047  }
2048 
2049  return $errors;
2050  }
2051 
2063  private function checkSpecialsAndNSPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2064  # Only 'createaccount' can be performed on special pages,
2065  # which don't actually exist in the DB.
2066  if ( NS_SPECIAL == $this->mNamespace && $action !== 'createaccount' ) {
2067  $errors[] = array( 'ns-specialprotected' );
2068  }
2069 
2070  # Check $wgNamespaceProtection for restricted namespaces
2071  if ( $this->isNamespaceProtected( $user ) ) {
2072  $ns = $this->mNamespace == NS_MAIN ?
2073  wfMessage( 'nstab-main' )->text() : $this->getNsText();
2074  $errors[] = $this->mNamespace == NS_MEDIAWIKI ?
2075  array( 'protectedinterface' ) : array( 'namespaceprotected', $ns );
2076  }
2077 
2078  return $errors;
2079  }
2080 
2092  private function checkCSSandJSPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2093  # Protect css/js subpages of user pages
2094  # XXX: this might be better using restrictions
2095  # XXX: right 'editusercssjs' is deprecated, for backward compatibility only
2096  if ( $action != 'patrol' && !$user->isAllowed( 'editusercssjs' ) ) {
2097  if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
2098  if ( $this->isCssSubpage() && !$user->isAllowedAny( 'editmyusercss', 'editusercss' ) ) {
2099  $errors[] = array( 'mycustomcssprotected' );
2100  } elseif ( $this->isJsSubpage() && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' ) ) {
2101  $errors[] = array( 'mycustomjsprotected' );
2102  }
2103  } else {
2104  if ( $this->isCssSubpage() && !$user->isAllowed( 'editusercss' ) ) {
2105  $errors[] = array( 'customcssprotected' );
2106  } elseif ( $this->isJsSubpage() && !$user->isAllowed( 'edituserjs' ) ) {
2107  $errors[] = array( 'customjsprotected' );
2108  }
2109  }
2110  }
2111 
2112  return $errors;
2113  }
2114 
2128  private function checkPageRestrictions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2129  foreach ( $this->getRestrictions( $action ) as $right ) {
2130  // Backwards compatibility, rewrite sysop -> editprotected
2131  if ( $right == 'sysop' ) {
2132  $right = 'editprotected';
2133  }
2134  // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
2135  if ( $right == 'autoconfirmed' ) {
2136  $right = 'editsemiprotected';
2137  }
2138  if ( $right == '' ) {
2139  continue;
2140  }
2141  if ( !$user->isAllowed( $right ) ) {
2142  $errors[] = array( 'protectedpagetext', $right );
2143  } elseif ( $this->mCascadeRestriction && !$user->isAllowed( 'protect' ) ) {
2144  $errors[] = array( 'protectedpagetext', 'protect' );
2145  }
2146  }
2147 
2148  return $errors;
2149  }
2150 
2162  private function checkCascadingSourcesRestrictions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2163  if ( $doExpensiveQueries && !$this->isCssJsSubpage() ) {
2164  # We /could/ use the protection level on the source page, but it's
2165  # fairly ugly as we have to establish a precedence hierarchy for pages
2166  # included by multiple cascade-protected pages. So just restrict
2167  # it to people with 'protect' permission, as they could remove the
2168  # protection anyway.
2169  list( $cascadingSources, $restrictions ) = $this->getCascadeProtectionSources();
2170  # Cascading protection depends on more than this page...
2171  # Several cascading protected pages may include this page...
2172  # Check each cascading level
2173  # This is only for protection restrictions, not for all actions
2174  if ( isset( $restrictions[$action] ) ) {
2175  foreach ( $restrictions[$action] as $right ) {
2176  // Backwards compatibility, rewrite sysop -> editprotected
2177  if ( $right == 'sysop' ) {
2178  $right = 'editprotected';
2179  }
2180  // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
2181  if ( $right == 'autoconfirmed' ) {
2182  $right = 'editsemiprotected';
2183  }
2184  if ( $right != '' && !$user->isAllowedAll( 'protect', $right ) ) {
2185  $pages = '';
2186  foreach ( $cascadingSources as $page ) {
2187  $pages .= '* [[:' . $page->getPrefixedText() . "]]\n";
2188  }
2189  $errors[] = array( 'cascadeprotected', count( $cascadingSources ), $pages );
2190  }
2191  }
2192  }
2193  }
2194 
2195  return $errors;
2196  }
2197 
2209  private function checkActionPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2210  global $wgDeleteRevisionsLimit, $wgLang;
2211 
2212  if ( $action == 'protect' ) {
2213  if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $doExpensiveQueries, true ) ) ) {
2214  // If they can't edit, they shouldn't protect.
2215  $errors[] = array( 'protect-cantedit' );
2216  }
2217  } elseif ( $action == 'create' ) {
2218  $title_protection = $this->getTitleProtection();
2219  if ( $title_protection ) {
2220  if ( $title_protection['pt_create_perm'] == 'sysop' ) {
2221  $title_protection['pt_create_perm'] = 'editprotected'; // B/C
2222  }
2223  if ( $title_protection['pt_create_perm'] == 'autoconfirmed' ) {
2224  $title_protection['pt_create_perm'] = 'editsemiprotected'; // B/C
2225  }
2226  if ( $title_protection['pt_create_perm'] == ''
2227  || !$user->isAllowed( $title_protection['pt_create_perm'] )
2228  ) {
2229  $errors[] = array( 'titleprotected', User::whoIs( $title_protection['pt_user'] ), $title_protection['pt_reason'] );
2230  }
2231  }
2232  } elseif ( $action == 'move' ) {
2233  // Check for immobile pages
2234  if ( !MWNamespace::isMovable( $this->mNamespace ) ) {
2235  // Specific message for this case
2236  $errors[] = array( 'immobile-source-namespace', $this->getNsText() );
2237  } elseif ( !$this->isMovable() ) {
2238  // Less specific message for rarer cases
2239  $errors[] = array( 'immobile-source-page' );
2240  }
2241  } elseif ( $action == 'move-target' ) {
2242  if ( !MWNamespace::isMovable( $this->mNamespace ) ) {
2243  $errors[] = array( 'immobile-target-namespace', $this->getNsText() );
2244  } elseif ( !$this->isMovable() ) {
2245  $errors[] = array( 'immobile-target-page' );
2246  }
2247  } elseif ( $action == 'delete' ) {
2248  if ( $doExpensiveQueries && $wgDeleteRevisionsLimit
2249  && !$this->userCan( 'bigdelete', $user ) && $this->isBigDeletion()
2250  ) {
2251  $errors[] = array( 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) );
2252  }
2253  }
2254  return $errors;
2255  }
2256 
2268  private function checkUserBlock( $action, $user, $errors, $doExpensiveQueries, $short ) {
2269  // Account creation blocks handled at userlogin.
2270  // Unblocking handled in SpecialUnblock
2271  if ( !$doExpensiveQueries || in_array( $action, array( 'createaccount', 'unblock' ) ) ) {
2272  return $errors;
2273  }
2274 
2275  global $wgEmailConfirmToEdit;
2276 
2277  if ( $wgEmailConfirmToEdit && !$user->isEmailConfirmed() ) {
2278  $errors[] = array( 'confirmedittext' );
2279  }
2280 
2281  if ( ( $action == 'edit' || $action == 'create' ) && !$user->isBlockedFrom( $this ) ) {
2282  // Don't block the user from editing their own talk page unless they've been
2283  // explicitly blocked from that too.
2284  } elseif ( $user->isBlocked() && $user->mBlock->prevents( $action ) !== false ) {
2285  // @todo FIXME: Pass the relevant context into this function.
2286  $errors[] = $user->getBlock()->getPermissionsError( RequestContext::getMain() );
2287  }
2288 
2289  return $errors;
2290  }
2291 
2303  private function checkReadPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2304  global $wgWhitelistRead, $wgWhitelistReadRegexp;
2305 
2306  $whitelisted = false;
2307  if ( User::isEveryoneAllowed( 'read' ) ) {
2308  # Shortcut for public wikis, allows skipping quite a bit of code
2309  $whitelisted = true;
2310  } elseif ( $user->isAllowed( 'read' ) ) {
2311  # If the user is allowed to read pages, he is allowed to read all pages
2312  $whitelisted = true;
2313  } elseif ( $this->isSpecial( 'Userlogin' )
2314  || $this->isSpecial( 'ChangePassword' )
2315  || $this->isSpecial( 'PasswordReset' )
2316  ) {
2317  # Always grant access to the login page.
2318  # Even anons need to be able to log in.
2319  $whitelisted = true;
2320  } elseif ( is_array( $wgWhitelistRead ) && count( $wgWhitelistRead ) ) {
2321  # Time to check the whitelist
2322  # Only do these checks is there's something to check against
2323  $name = $this->getPrefixedText();
2324  $dbName = $this->getPrefixedDBkey();
2325 
2326  // Check for explicit whitelisting with and without underscores
2327  if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) {
2328  $whitelisted = true;
2329  } elseif ( $this->getNamespace() == NS_MAIN ) {
2330  # Old settings might have the title prefixed with
2331  # a colon for main-namespace pages
2332  if ( in_array( ':' . $name, $wgWhitelistRead ) ) {
2333  $whitelisted = true;
2334  }
2335  } elseif ( $this->isSpecialPage() ) {
2336  # If it's a special page, ditch the subpage bit and check again
2337  $name = $this->getDBkey();
2338  list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name );
2339  if ( $name ) {
2340  $pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
2341  if ( in_array( $pure, $wgWhitelistRead, true ) ) {
2342  $whitelisted = true;
2343  }
2344  }
2345  }
2346  }
2347 
2348  if ( !$whitelisted && is_array( $wgWhitelistReadRegexp ) && !empty( $wgWhitelistReadRegexp ) ) {
2349  $name = $this->getPrefixedText();
2350  // Check for regex whitelisting
2351  foreach ( $wgWhitelistReadRegexp as $listItem ) {
2352  if ( preg_match( $listItem, $name ) ) {
2353  $whitelisted = true;
2354  break;
2355  }
2356  }
2357  }
2358 
2359  if ( !$whitelisted ) {
2360  # If the title is not whitelisted, give extensions a chance to do so...
2361  wfRunHooks( 'TitleReadWhitelist', array( $this, $user, &$whitelisted ) );
2362  if ( !$whitelisted ) {
2363  $errors[] = $this->missingPermissionError( $action, $short );
2364  }
2365  }
2366 
2367  return $errors;
2368  }
2369 
2378  private function missingPermissionError( $action, $short ) {
2379  // We avoid expensive display logic for quickUserCan's and such
2380  if ( $short ) {
2381  return array( 'badaccess-group0' );
2382  }
2383 
2384  $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
2385  User::getGroupsWithPermission( $action ) );
2386 
2387  if ( count( $groups ) ) {
2388  global $wgLang;
2389  return array(
2390  'badaccess-groups',
2391  $wgLang->commaList( $groups ),
2392  count( $groups )
2393  );
2394  } else {
2395  return array( 'badaccess-group0' );
2396  }
2397  }
2398 
2410  protected function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries = true, $short = false ) {
2411  wfProfileIn( __METHOD__ );
2412 
2413  # Read has special handling
2414  if ( $action == 'read' ) {
2415  $checks = array(
2416  'checkPermissionHooks',
2417  'checkReadPermissions',
2418  );
2419  } else {
2420  $checks = array(
2421  'checkQuickPermissions',
2422  'checkPermissionHooks',
2423  'checkSpecialsAndNSPermissions',
2424  'checkCSSandJSPermissions',
2425  'checkPageRestrictions',
2426  'checkCascadingSourcesRestrictions',
2427  'checkActionPermissions',
2428  'checkUserBlock'
2429  );
2430  }
2431 
2432  $errors = array();
2433  while ( count( $checks ) > 0 &&
2434  !( $short && count( $errors ) > 0 ) ) {
2435  $method = array_shift( $checks );
2436  $errors = $this->$method( $action, $user, $errors, $doExpensiveQueries, $short );
2437  }
2438 
2439  wfProfileOut( __METHOD__ );
2440  return $errors;
2441  }
2442 
2450  public static function getFilteredRestrictionTypes( $exists = true ) {
2451  global $wgRestrictionTypes;
2452  $types = $wgRestrictionTypes;
2453  if ( $exists ) {
2454  # Remove the create restriction for existing titles
2455  $types = array_diff( $types, array( 'create' ) );
2456  } else {
2457  # Only the create and upload restrictions apply to non-existing titles
2458  $types = array_intersect( $types, array( 'create', 'upload' ) );
2459  }
2460  return $types;
2461  }
2462 
2468  public function getRestrictionTypes() {
2469  if ( $this->isSpecialPage() ) {
2470  return array();
2471  }
2472 
2473  $types = self::getFilteredRestrictionTypes( $this->exists() );
2474 
2475  if ( $this->getNamespace() != NS_FILE ) {
2476  # Remove the upload restriction for non-file titles
2477  $types = array_diff( $types, array( 'upload' ) );
2478  }
2479 
2480  wfRunHooks( 'TitleGetRestrictionTypes', array( $this, &$types ) );
2481 
2482  wfDebug( __METHOD__ . ': applicable restrictions to [[' .
2483  $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" );
2484 
2485  return $types;
2486  }
2487 
2495  private function getTitleProtection() {
2496  // Can't protect pages in special namespaces
2497  if ( $this->getNamespace() < 0 ) {
2498  return false;
2499  }
2500 
2501  // Can't protect pages that exist.
2502  if ( $this->exists() ) {
2503  return false;
2504  }
2505 
2506  if ( !isset( $this->mTitleProtection ) ) {
2507  $dbr = wfGetDB( DB_SLAVE );
2508  $res = $dbr->select(
2509  'protected_titles',
2510  array( 'pt_user', 'pt_reason', 'pt_expiry', 'pt_create_perm' ),
2511  array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ),
2512  __METHOD__
2513  );
2514 
2515  // fetchRow returns false if there are no rows.
2516  $this->mTitleProtection = $dbr->fetchRow( $res );
2517  }
2518  return $this->mTitleProtection;
2519  }
2520 
2530  public function updateTitleProtection( $create_perm, $reason, $expiry ) {
2531  wfDeprecated( __METHOD__, '1.19' );
2532 
2533  global $wgUser;
2534 
2535  $limit = array( 'create' => $create_perm );
2536  $expiry = array( 'create' => $expiry );
2537 
2538  $page = WikiPage::factory( $this );
2539  $cascade = false;
2540  $status = $page->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $wgUser );
2541 
2542  return $status->isOK();
2543  }
2544 
2548  public function deleteTitleProtection() {
2549  $dbw = wfGetDB( DB_MASTER );
2550 
2551  $dbw->delete(
2552  'protected_titles',
2553  array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ),
2554  __METHOD__
2555  );
2556  $this->mTitleProtection = false;
2557  }
2558 
2566  public function isSemiProtected( $action = 'edit' ) {
2567  global $wgSemiprotectedRestrictionLevels;
2568 
2569  $restrictions = $this->getRestrictions( $action );
2570  $semi = $wgSemiprotectedRestrictionLevels;
2571  if ( !$restrictions || !$semi ) {
2572  // Not protected, or all protection is full protection
2573  return false;
2574  }
2575 
2576  // Remap autoconfirmed to editsemiprotected for BC
2577  foreach ( array_keys( $semi, 'autoconfirmed' ) as $key ) {
2578  $semi[$key] = 'editsemiprotected';
2579  }
2580  foreach ( array_keys( $restrictions, 'autoconfirmed' ) as $key ) {
2581  $restrictions[$key] = 'editsemiprotected';
2582  }
2583 
2584  return !array_diff( $restrictions, $semi );
2585  }
2586 
2594  public function isProtected( $action = '' ) {
2595  global $wgRestrictionLevels;
2596 
2597  $restrictionTypes = $this->getRestrictionTypes();
2598 
2599  # Special pages have inherent protection
2600  if ( $this->isSpecialPage() ) {
2601  return true;
2602  }
2603 
2604  # Check regular protection levels
2605  foreach ( $restrictionTypes as $type ) {
2606  if ( $action == $type || $action == '' ) {
2607  $r = $this->getRestrictions( $type );
2608  foreach ( $wgRestrictionLevels as $level ) {
2609  if ( in_array( $level, $r ) && $level != '' ) {
2610  return true;
2611  }
2612  }
2613  }
2614  }
2615 
2616  return false;
2617  }
2618 
2626  public function isNamespaceProtected( User $user ) {
2628 
2629  if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) {
2630  foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) {
2631  if ( $right != '' && !$user->isAllowed( $right ) ) {
2632  return true;
2633  }
2634  }
2635  }
2636  return false;
2637  }
2638 
2644  public function isCascadeProtected() {
2645  list( $sources, /* $restrictions */ ) = $this->getCascadeProtectionSources( false );
2646  return ( $sources > 0 );
2647  }
2648 
2658  public function areCascadeProtectionSourcesLoaded( $getPages = true ) {
2659  return $getPages ? isset( $this->mCascadeSources ) : isset( $this->mHasCascadingRestrictions );
2660  }
2661 
2672  public function getCascadeProtectionSources( $getPages = true ) {
2674  $pagerestrictions = array();
2675 
2676  if ( isset( $this->mCascadeSources ) && $getPages ) {
2677  return array( $this->mCascadeSources, $this->mCascadingRestrictions );
2678  } elseif ( isset( $this->mHasCascadingRestrictions ) && !$getPages ) {
2679  return array( $this->mHasCascadingRestrictions, $pagerestrictions );
2680  }
2681 
2682  wfProfileIn( __METHOD__ );
2683 
2684  $dbr = wfGetDB( DB_SLAVE );
2685 
2686  if ( $this->getNamespace() == NS_FILE ) {
2687  $tables = array( 'imagelinks', 'page_restrictions' );
2688  $where_clauses = array(
2689  'il_to' => $this->getDBkey(),
2690  'il_from=pr_page',
2691  'pr_cascade' => 1
2692  );
2693  } else {
2694  $tables = array( 'templatelinks', 'page_restrictions' );
2695  $where_clauses = array(
2696  'tl_namespace' => $this->getNamespace(),
2697  'tl_title' => $this->getDBkey(),
2698  'tl_from=pr_page',
2699  'pr_cascade' => 1
2700  );
2701  }
2702 
2703  if ( $getPages ) {
2704  $cols = array( 'pr_page', 'page_namespace', 'page_title',
2705  'pr_expiry', 'pr_type', 'pr_level' );
2706  $where_clauses[] = 'page_id=pr_page';
2707  $tables[] = 'page';
2708  } else {
2709  $cols = array( 'pr_expiry' );
2710  }
2711 
2712  $res = $dbr->select( $tables, $cols, $where_clauses, __METHOD__ );
2713 
2714  $sources = $getPages ? array() : false;
2715  $now = wfTimestampNow();
2716  $purgeExpired = false;
2717 
2718  foreach ( $res as $row ) {
2719  $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW );
2720  if ( $expiry > $now ) {
2721  if ( $getPages ) {
2722  $page_id = $row->pr_page;
2723  $page_ns = $row->page_namespace;
2724  $page_title = $row->page_title;
2725  $sources[$page_id] = Title::makeTitle( $page_ns, $page_title );
2726  # Add groups needed for each restriction type if its not already there
2727  # Make sure this restriction type still exists
2728 
2729  if ( !isset( $pagerestrictions[$row->pr_type] ) ) {
2730  $pagerestrictions[$row->pr_type] = array();
2731  }
2732 
2733  if (
2734  isset( $pagerestrictions[$row->pr_type] )
2735  && !in_array( $row->pr_level, $pagerestrictions[$row->pr_type] )
2736  ) {
2737  $pagerestrictions[$row->pr_type][] = $row->pr_level;
2738  }
2739  } else {
2740  $sources = true;
2741  }
2742  } else {
2743  // Trigger lazy purge of expired restrictions from the db
2744  $purgeExpired = true;
2745  }
2746  }
2747  if ( $purgeExpired ) {
2749  }
2750 
2751  if ( $getPages ) {
2752  $this->mCascadeSources = $sources;
2753  $this->mCascadingRestrictions = $pagerestrictions;
2754  } else {
2755  $this->mHasCascadingRestrictions = $sources;
2756  }
2757 
2758  wfProfileOut( __METHOD__ );
2759  return array( $sources, $pagerestrictions );
2760  }
2761 
2769  public function areRestrictionsLoaded() {
2771  }
2772 
2779  public function getRestrictions( $action ) {
2780  if ( !$this->mRestrictionsLoaded ) {
2781  $this->loadRestrictions();
2782  }
2783  return isset( $this->mRestrictions[$action] )
2784  ? $this->mRestrictions[$action]
2785  : array();
2786  }
2787 
2796  public function getAllRestrictions() {
2797  if ( !$this->mRestrictionsLoaded ) {
2798  $this->loadRestrictions();
2799  }
2800  return $this->mRestrictions;
2801  }
2802 
2810  public function getRestrictionExpiry( $action ) {
2811  if ( !$this->mRestrictionsLoaded ) {
2812  $this->loadRestrictions();
2813  }
2814  return isset( $this->mRestrictionsExpiry[$action] ) ? $this->mRestrictionsExpiry[$action] : false;
2815  }
2816 
2823  if ( !$this->mRestrictionsLoaded ) {
2824  $this->loadRestrictions();
2825  }
2826 
2828  }
2829 
2837  private function loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions = null ) {
2838  $rows = array();
2839 
2840  foreach ( $res as $row ) {
2841  $rows[] = $row;
2842  }
2843 
2844  $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
2845  }
2846 
2856  public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
2858  $dbr = wfGetDB( DB_SLAVE );
2859 
2860  $restrictionTypes = $this->getRestrictionTypes();
2861 
2862  foreach ( $restrictionTypes as $type ) {
2863  $this->mRestrictions[$type] = array();
2864  $this->mRestrictionsExpiry[$type] = $wgContLang->formatExpiry( '', TS_MW );
2865  }
2866 
2867  $this->mCascadeRestriction = false;
2868 
2869  # Backwards-compatibility: also load the restrictions from the page record (old format).
2870 
2871  if ( $oldFashionedRestrictions === null ) {
2872  $oldFashionedRestrictions = $dbr->selectField( 'page', 'page_restrictions',
2873  array( 'page_id' => $this->getArticleID() ), __METHOD__ );
2874  }
2875 
2876  if ( $oldFashionedRestrictions != '' ) {
2877 
2878  foreach ( explode( ':', trim( $oldFashionedRestrictions ) ) as $restrict ) {
2879  $temp = explode( '=', trim( $restrict ) );
2880  if ( count( $temp ) == 1 ) {
2881  // old old format should be treated as edit/move restriction
2882  $this->mRestrictions['edit'] = explode( ',', trim( $temp[0] ) );
2883  $this->mRestrictions['move'] = explode( ',', trim( $temp[0] ) );
2884  } else {
2885  $restriction = trim( $temp[1] );
2886  if ( $restriction != '' ) { //some old entries are empty
2887  $this->mRestrictions[$temp[0]] = explode( ',', $restriction );
2888  }
2889  }
2890  }
2891 
2892  $this->mOldRestrictions = true;
2893 
2894  }
2895 
2896  if ( count( $rows ) ) {
2897  # Current system - load second to make them override.
2898  $now = wfTimestampNow();
2899  $purgeExpired = false;
2900 
2901  # Cycle through all the restrictions.
2902  foreach ( $rows as $row ) {
2903 
2904  // Don't take care of restrictions types that aren't allowed
2905  if ( !in_array( $row->pr_type, $restrictionTypes ) ) {
2906  continue;
2907  }
2908 
2909  // This code should be refactored, now that it's being used more generally,
2910  // But I don't really see any harm in leaving it in Block for now -werdna
2911  $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW );
2912 
2913  // Only apply the restrictions if they haven't expired!
2914  if ( !$expiry || $expiry > $now ) {
2915  $this->mRestrictionsExpiry[$row->pr_type] = $expiry;
2916  $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) );
2917 
2918  $this->mCascadeRestriction |= $row->pr_cascade;
2919  } else {
2920  // Trigger a lazy purge of expired restrictions
2921  $purgeExpired = true;
2922  }
2923  }
2924 
2925  if ( $purgeExpired ) {
2927  }
2928  }
2929 
2930  $this->mRestrictionsLoaded = true;
2931  }
2932 
2939  public function loadRestrictions( $oldFashionedRestrictions = null ) {
2941  if ( !$this->mRestrictionsLoaded ) {
2942  if ( $this->exists() ) {
2943  $dbr = wfGetDB( DB_SLAVE );
2944 
2945  $res = $dbr->select(
2946  'page_restrictions',
2947  array( 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ),
2948  array( 'pr_page' => $this->getArticleID() ),
2949  __METHOD__
2950  );
2951 
2952  $this->loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions );
2953  } else {
2954  $title_protection = $this->getTitleProtection();
2955 
2956  if ( $title_protection ) {
2957  $now = wfTimestampNow();
2958  $expiry = $wgContLang->formatExpiry( $title_protection['pt_expiry'], TS_MW );
2959 
2960  if ( !$expiry || $expiry > $now ) {
2961  // Apply the restrictions
2962  $this->mRestrictionsExpiry['create'] = $expiry;
2963  $this->mRestrictions['create'] = explode( ',', trim( $title_protection['pt_create_perm'] ) );
2964  } else { // Get rid of the old restrictions
2966  $this->mTitleProtection = false;
2967  }
2968  } else {
2969  $this->mRestrictionsExpiry['create'] = $wgContLang->formatExpiry( '', TS_MW );
2970  }
2971  $this->mRestrictionsLoaded = true;
2972  }
2973  }
2974  }
2975 
2980  public function flushRestrictions() {
2981  $this->mRestrictionsLoaded = false;
2982  $this->mTitleProtection = null;
2983  }
2984 
2988  static function purgeExpiredRestrictions() {
2989  if ( wfReadOnly() ) {
2990  return;
2991  }
2992 
2993  $method = __METHOD__;
2994  $dbw = wfGetDB( DB_MASTER );
2995  $dbw->onTransactionIdle( function() use ( $dbw, $method ) {
2996  $dbw->delete(
2997  'page_restrictions',
2998  array( 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
2999  $method
3000  );
3001  $dbw->delete(
3002  'protected_titles',
3003  array( 'pt_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
3004  $method
3005  );
3006  } );
3007  }
3008 
3014  public function hasSubpages() {
3015  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
3016  # Duh
3017  return false;
3018  }
3019 
3020  # We dynamically add a member variable for the purpose of this method
3021  # alone to cache the result. There's no point in having it hanging
3022  # around uninitialized in every Title object; therefore we only add it
3023  # if needed and don't declare it statically.
3024  if ( !isset( $this->mHasSubpages ) ) {
3025  $this->mHasSubpages = false;
3026  $subpages = $this->getSubpages( 1 );
3027  if ( $subpages instanceof TitleArray ) {
3028  $this->mHasSubpages = (bool)$subpages->count();
3029  }
3030  }
3031 
3032  return $this->mHasSubpages;
3033  }
3034 
3042  public function getSubpages( $limit = -1 ) {
3043  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
3044  return array();
3045  }
3046 
3047  $dbr = wfGetDB( DB_SLAVE );
3048  $conds['page_namespace'] = $this->getNamespace();
3049  $conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() );
3050  $options = array();
3051  if ( $limit > -1 ) {
3052  $options['LIMIT'] = $limit;
3053  }
3054  $this->mSubpages = TitleArray::newFromResult(
3055  $dbr->select( 'page',
3056  array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ),
3057  $conds,
3058  __METHOD__,
3059  $options
3060  )
3061  );
3062  return $this->mSubpages;
3063  }
3064 
3070  public function isDeleted() {
3071  if ( $this->getNamespace() < 0 ) {
3072  $n = 0;
3073  } else {
3074  $dbr = wfGetDB( DB_SLAVE );
3075 
3076  $n = $dbr->selectField( 'archive', 'COUNT(*)',
3077  array( 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ),
3078  __METHOD__
3079  );
3080  if ( $this->getNamespace() == NS_FILE ) {
3081  $n += $dbr->selectField( 'filearchive', 'COUNT(*)',
3082  array( 'fa_name' => $this->getDBkey() ),
3083  __METHOD__
3084  );
3085  }
3086  }
3087  return (int)$n;
3088  }
3089 
3095  public function isDeletedQuick() {
3096  if ( $this->getNamespace() < 0 ) {
3097  return false;
3098  }
3099  $dbr = wfGetDB( DB_SLAVE );
3100  $deleted = (bool)$dbr->selectField( 'archive', '1',
3101  array( 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ),
3102  __METHOD__
3103  );
3104  if ( !$deleted && $this->getNamespace() == NS_FILE ) {
3105  $deleted = (bool)$dbr->selectField( 'filearchive', '1',
3106  array( 'fa_name' => $this->getDBkey() ),
3107  __METHOD__
3108  );
3109  }
3110  return $deleted;
3111  }
3112 
3121  public function getArticleID( $flags = 0 ) {
3122  if ( $this->getNamespace() < 0 ) {
3123  $this->mArticleID = 0;
3124  return $this->mArticleID;
3125  }
3126  $linkCache = LinkCache::singleton();
3127  if ( $flags & self::GAID_FOR_UPDATE ) {
3128  $oldUpdate = $linkCache->forUpdate( true );
3129  $linkCache->clearLink( $this );
3130  $this->mArticleID = $linkCache->addLinkObj( $this );
3131  $linkCache->forUpdate( $oldUpdate );
3132  } else {
3133  if ( -1 == $this->mArticleID ) {
3134  $this->mArticleID = $linkCache->addLinkObj( $this );
3135  }
3136  }
3137  return $this->mArticleID;
3138  }
3139 
3147  public function isRedirect( $flags = 0 ) {
3148  if ( !is_null( $this->mRedirect ) ) {
3149  return $this->mRedirect;
3150  }
3151  # Calling getArticleID() loads the field from cache as needed
3152  if ( !$this->getArticleID( $flags ) ) {
3153  $this->mRedirect = false;
3154  return $this->mRedirect;
3155  }
3156 
3157  $linkCache = LinkCache::singleton();
3158  $cached = $linkCache->getGoodLinkFieldObj( $this, 'redirect' );
3159  if ( $cached === null ) {
3160  # Trust LinkCache's state over our own
3161  # LinkCache is telling us that the page doesn't exist, despite there being cached
3162  # data relating to an existing page in $this->mArticleID. Updaters should clear
3163  # LinkCache as appropriate, or use $flags = Title::GAID_FOR_UPDATE. If that flag is
3164  # set, then LinkCache will definitely be up to date here, since getArticleID() forces
3165  # LinkCache to refresh its data from the master.
3166  $this->mRedirect = false;
3167  return $this->mRedirect;
3168  }
3169 
3170  $this->mRedirect = (bool)$cached;
3171 
3172  return $this->mRedirect;
3173  }
3174 
3182  public function getLength( $flags = 0 ) {
3183  if ( $this->mLength != -1 ) {
3184  return $this->mLength;
3185  }
3186  # Calling getArticleID() loads the field from cache as needed
3187  if ( !$this->getArticleID( $flags ) ) {
3188  $this->mLength = 0;
3189  return $this->mLength;
3190  }
3191  $linkCache = LinkCache::singleton();
3192  $cached = $linkCache->getGoodLinkFieldObj( $this, 'length' );
3193  if ( $cached === null ) {
3194  # Trust LinkCache's state over our own, as for isRedirect()
3195  $this->mLength = 0;
3196  return $this->mLength;
3197  }
3198 
3199  $this->mLength = intval( $cached );
3200 
3201  return $this->mLength;
3202  }
3203 
3210  public function getLatestRevID( $flags = 0 ) {
3211  if ( !( $flags & Title::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
3212  return intval( $this->mLatestID );
3213  }
3214  # Calling getArticleID() loads the field from cache as needed
3215  if ( !$this->getArticleID( $flags ) ) {
3216  $this->mLatestID = 0;
3217  return $this->mLatestID;
3218  }
3219  $linkCache = LinkCache::singleton();
3220  $linkCache->addLinkObj( $this );
3221  $cached = $linkCache->getGoodLinkFieldObj( $this, 'revision' );
3222  if ( $cached === null ) {
3223  # Trust LinkCache's state over our own, as for isRedirect()
3224  $this->mLatestID = 0;
3225  return $this->mLatestID;
3226  }
3227 
3228  $this->mLatestID = intval( $cached );
3229 
3230  return $this->mLatestID;
3231  }
3232 
3243  public function resetArticleID( $newid ) {
3244  $linkCache = LinkCache::singleton();
3245  $linkCache->clearLink( $this );
3246 
3247  if ( $newid === false ) {
3248  $this->mArticleID = -1;
3249  } else {
3250  $this->mArticleID = intval( $newid );
3251  }
3252  $this->mRestrictionsLoaded = false;
3253  $this->mRestrictions = array();
3254  $this->mRedirect = null;
3255  $this->mLength = -1;
3256  $this->mLatestID = false;
3257  $this->mContentModel = false;
3258  $this->mEstimateRevisions = null;
3259  $this->mPageLanguage = false;
3260  }
3261 
3269  public static function capitalize( $text, $ns = NS_MAIN ) {
3271 
3272  if ( MWNamespace::isCapitalized( $ns ) ) {
3273  return $wgContLang->ucfirst( $text );
3274  } else {
3275  return $text;
3276  }
3277  }
3278 
3290  private function secureAndSplit() {
3291  # Initialisation
3292  $this->mInterwiki = '';
3293  $this->mFragment = '';
3294  $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
3295 
3296  $dbkey = $this->mDbkeyform;
3297 
3298  try {
3299  // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
3300  // the parsing code with Title, while avoiding massive refactoring.
3301  // @todo: get rid of secureAndSplit, refactor parsing code.
3302  $parser = $this->getTitleParser();
3303  $parts = $parser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
3304  } catch ( MalformedTitleException $ex ) {
3305  return false;
3306  }
3307 
3308  # Fill fields
3309  $this->setFragment( '#' . $parts['fragment'] );
3310  $this->mInterwiki = $parts['interwiki'];
3311  $this->mNamespace = $parts['namespace'];
3312  $this->mUserCaseDBKey = $parts['user_case_dbkey'];
3313 
3314  $this->mDbkeyform = $parts['dbkey'];
3315  $this->mUrlform = wfUrlencode( $this->mDbkeyform );
3316  $this->mTextform = str_replace( '_', ' ', $this->mDbkeyform );
3317 
3318  # We already know that some pages won't be in the database!
3319  if ( $this->isExternal() || $this->mNamespace == NS_SPECIAL ) {
3320  $this->mArticleID = 0;
3321  }
3322 
3323  return true;
3324  }
3325 
3338  public function getLinksTo( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
3339  if ( count( $options ) > 0 ) {
3340  $db = wfGetDB( DB_MASTER );
3341  } else {
3342  $db = wfGetDB( DB_SLAVE );
3343  }
3344 
3345  $res = $db->select(
3346  array( 'page', $table ),
3347  self::getSelectFields(),
3348  array(
3349  "{$prefix}_from=page_id",
3350  "{$prefix}_namespace" => $this->getNamespace(),
3351  "{$prefix}_title" => $this->getDBkey() ),
3352  __METHOD__,
3353  $options
3354  );
3355 
3356  $retVal = array();
3357  if ( $res->numRows() ) {
3358  $linkCache = LinkCache::singleton();
3359  foreach ( $res as $row ) {
3360  $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title );
3361  if ( $titleObj ) {
3362  $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
3363  $retVal[] = $titleObj;
3364  }
3365  }
3366  }
3367  return $retVal;
3368  }
3369 
3380  public function getTemplateLinksTo( $options = array() ) {
3381  return $this->getLinksTo( $options, 'templatelinks', 'tl' );
3382  }
3383 
3396  public function getLinksFrom( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
3397  global $wgContentHandlerUseDB;
3398 
3399  $id = $this->getArticleID();
3400 
3401  # If the page doesn't exist; there can't be any link from this page
3402  if ( !$id ) {
3403  return array();
3404  }
3405 
3406  if ( count( $options ) > 0 ) {
3407  $db = wfGetDB( DB_MASTER );
3408  } else {
3409  $db = wfGetDB( DB_SLAVE );
3410  }
3411 
3412  $namespaceFiled = "{$prefix}_namespace";
3413  $titleField = "{$prefix}_title";
3414 
3415  $fields = array( $namespaceFiled, $titleField, 'page_id', 'page_len', 'page_is_redirect', 'page_latest' );
3416  if ( $wgContentHandlerUseDB ) {
3417  $fields[] = 'page_content_model';
3418  }
3419 
3420  $res = $db->select(
3421  array( $table, 'page' ),
3422  $fields,
3423  array( "{$prefix}_from" => $id ),
3424  __METHOD__,
3425  $options,
3426  array( 'page' => array( 'LEFT JOIN', array( "page_namespace=$namespaceFiled", "page_title=$titleField" ) ) )
3427  );
3428 
3429  $retVal = array();
3430  if ( $res->numRows() ) {
3431  $linkCache = LinkCache::singleton();
3432  foreach ( $res as $row ) {
3433  $titleObj = Title::makeTitle( $row->$namespaceFiled, $row->$titleField );
3434  if ( $titleObj ) {
3435  if ( $row->page_id ) {
3436  $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
3437  } else {
3438  $linkCache->addBadLinkObj( $titleObj );
3439  }
3440  $retVal[] = $titleObj;
3441  }
3442  }
3443  }
3444  return $retVal;
3445  }
3446 
3457  public function getTemplateLinksFrom( $options = array() ) {
3458  return $this->getLinksFrom( $options, 'templatelinks', 'tl' );
3459  }
3460 
3467  public function getBrokenLinksFrom() {
3468  if ( $this->getArticleID() == 0 ) {
3469  # All links from article ID 0 are false positives
3470  return array();
3471  }
3472 
3473  $dbr = wfGetDB( DB_SLAVE );
3474  $res = $dbr->select(
3475  array( 'page', 'pagelinks' ),
3476  array( 'pl_namespace', 'pl_title' ),
3477  array(
3478  'pl_from' => $this->getArticleID(),
3479  'page_namespace IS NULL'
3480  ),
3481  __METHOD__, array(),
3482  array(
3483  'page' => array(
3484  'LEFT JOIN',
3485  array( 'pl_namespace=page_namespace', 'pl_title=page_title' )
3486  )
3487  )
3488  );
3489 
3490  $retVal = array();
3491  foreach ( $res as $row ) {
3492  $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
3493  }
3494  return $retVal;
3495  }
3496 
3503  public function getSquidURLs() {
3504  $urls = array(
3505  $this->getInternalURL(),
3506  $this->getInternalURL( 'action=history' )
3507  );
3508 
3509  $pageLang = $this->getPageLanguage();
3510  if ( $pageLang->hasVariants() ) {
3511  $variants = $pageLang->getVariants();
3512  foreach ( $variants as $vCode ) {
3513  $urls[] = $this->getInternalURL( '', $vCode );
3514  }
3515  }
3516 
3517  // If we are looking at a css/js user subpage, purge the action=raw.
3518  if ( $this->isJsSubpage() ) {
3519  $urls[] = $this->getInternalUrl( 'action=raw&ctype=text/javascript' );
3520  } elseif ( $this->isCssSubpage() ) {
3521  $urls[] = $this->getInternalUrl( 'action=raw&ctype=text/css' );
3522  }
3523 
3524  wfRunHooks( 'TitleSquidURLs', array( $this, &$urls ) );
3525  return $urls;
3526  }
3527 
3531  public function purgeSquid() {
3532  global $wgUseSquid;
3533  if ( $wgUseSquid ) {
3534  $urls = $this->getSquidURLs();
3535  $u = new SquidUpdate( $urls );
3536  $u->doUpdate();
3537  }
3538  }
3539 
3546  public function moveNoAuth( &$nt ) {
3547  return $this->moveTo( $nt, false );
3548  }
3549 
3560  public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) {
3561  global $wgUser, $wgContentHandlerUseDB;
3562 
3563  $errors = array();
3564  if ( !$nt ) {
3565  // Normally we'd add this to $errors, but we'll get
3566  // lots of syntax errors if $nt is not an object
3567  return array( array( 'badtitletext' ) );
3568  }
3569  if ( $this->equals( $nt ) ) {
3570  $errors[] = array( 'selfmove' );
3571  }
3572  if ( !$this->isMovable() ) {
3573  $errors[] = array( 'immobile-source-namespace', $this->getNsText() );
3574  }
3575  if ( $nt->isExternal() ) {
3576  $errors[] = array( 'immobile-target-namespace-iw' );
3577  }
3578  if ( !$nt->isMovable() ) {
3579  $errors[] = array( 'immobile-target-namespace', $nt->getNsText() );
3580  }
3581 
3582  $oldid = $this->getArticleID();
3583  $newid = $nt->getArticleID();
3584 
3585  if ( strlen( $nt->getDBkey() ) < 1 ) {
3586  $errors[] = array( 'articleexists' );
3587  }
3588  if (
3589  ( $this->getDBkey() == '' ) ||
3590  ( !$oldid ) ||
3591  ( $nt->getDBkey() == '' )
3592  ) {
3593  $errors[] = array( 'badarticleerror' );
3594  }
3595 
3596  // Content model checks
3597  if ( !$wgContentHandlerUseDB &&
3598  $this->getContentModel() !== $nt->getContentModel() ) {
3599  // can't move a page if that would change the page's content model
3600  $errors[] = array(
3601  'bad-target-model',
3603  ContentHandler::getLocalizedName( $nt->getContentModel() )
3604  );
3605  }
3606 
3607  // Image-specific checks
3608  if ( $this->getNamespace() == NS_FILE ) {
3609  $errors = array_merge( $errors, $this->validateFileMoveOperation( $nt ) );
3610  }
3611 
3612  if ( $nt->getNamespace() == NS_FILE && $this->getNamespace() != NS_FILE ) {
3613  $errors[] = array( 'nonfile-cannot-move-to-file' );
3614  }
3615 
3616  if ( $auth ) {
3617  $errors = wfMergeErrorArrays( $errors,
3618  $this->getUserPermissionsErrors( 'move', $wgUser ),
3619  $this->getUserPermissionsErrors( 'edit', $wgUser ),
3620  $nt->getUserPermissionsErrors( 'move-target', $wgUser ),
3621  $nt->getUserPermissionsErrors( 'edit', $wgUser ) );
3622  }
3623 
3624  $match = EditPage::matchSummarySpamRegex( $reason );
3625  if ( $match !== false ) {
3626  // This is kind of lame, won't display nice
3627  $errors[] = array( 'spamprotectiontext' );
3628  }
3629 
3630  $err = null;
3631  if ( !wfRunHooks( 'AbortMove', array( $this, $nt, $wgUser, &$err, $reason ) ) ) {
3632  $errors[] = array( 'hookaborted', $err );
3633  }
3634 
3635  # The move is allowed only if (1) the target doesn't exist, or
3636  # (2) the target is a redirect to the source, and has no history
3637  # (so we can undo bad moves right after they're done).
3638 
3639  if ( 0 != $newid ) { # Target exists; check for validity
3640  if ( !$this->isValidMoveTarget( $nt ) ) {
3641  $errors[] = array( 'articleexists' );
3642  }
3643  } else {
3644  $tp = $nt->getTitleProtection();
3645  $right = $tp['pt_create_perm'];
3646  if ( $right == 'sysop' ) {
3647  $right = 'editprotected'; // B/C
3648  }
3649  if ( $right == 'autoconfirmed' ) {
3650  $right = 'editsemiprotected'; // B/C
3651  }
3652  if ( $tp and !$wgUser->isAllowed( $right ) ) {
3653  $errors[] = array( 'cantmove-titleprotected' );
3654  }
3655  }
3656  if ( empty( $errors ) ) {
3657  return true;
3658  }
3659  return $errors;
3660  }
3661 
3667  protected function validateFileMoveOperation( $nt ) {
3668  global $wgUser;
3669 
3670  $errors = array();
3671 
3672  // wfFindFile( $nt ) / wfLocalFile( $nt ) is not allowed until below
3673 
3674  $file = wfLocalFile( $this );
3675  if ( $file->exists() ) {
3676  if ( $nt->getText() != wfStripIllegalFilenameChars( $nt->getText() ) ) {
3677  $errors[] = array( 'imageinvalidfilename' );
3678  }
3679  if ( !File::checkExtensionCompatibility( $file, $nt->getDBkey() ) ) {
3680  $errors[] = array( 'imagetypemismatch' );
3681  }
3682  }
3683 
3684  if ( $nt->getNamespace() != NS_FILE ) {
3685  $errors[] = array( 'imagenocrossnamespace' );
3686  // From here we want to do checks on a file object, so if we can't
3687  // create one, we must return.
3688  return $errors;
3689  }
3690 
3691  // wfFindFile( $nt ) / wfLocalFile( $nt ) is allowed below here
3692 
3693  $destFile = wfLocalFile( $nt );
3694  if ( !$wgUser->isAllowed( 'reupload-shared' ) && !$destFile->exists() && wfFindFile( $nt ) ) {
3695  $errors[] = array( 'file-exists-sharedrepo' );
3696  }
3697 
3698  return $errors;
3699  }
3700 
3712  public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true ) {
3713  global $wgUser;
3714  $err = $this->isValidMoveOperation( $nt, $auth, $reason );
3715  if ( is_array( $err ) ) {
3716  // Auto-block user's IP if the account was "hard" blocked
3717  $wgUser->spreadAnyEditBlock();
3718  return $err;
3719  }
3720  // Check suppressredirect permission
3721  if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
3722  $createRedirect = true;
3723  }
3724 
3725  wfRunHooks( 'TitleMove', array( $this, $nt, $wgUser ) );
3726 
3727  // If it is a file, move it first.
3728  // It is done before all other moving stuff is done because it's hard to revert.
3729  $dbw = wfGetDB( DB_MASTER );
3730  if ( $this->getNamespace() == NS_FILE ) {
3731  $file = wfLocalFile( $this );
3732  if ( $file->exists() ) {
3733  $status = $file->move( $nt );
3734  if ( !$status->isOk() ) {
3735  return $status->getErrorsArray();
3736  }
3737  }
3738  // Clear RepoGroup process cache
3739  RepoGroup::singleton()->clearCache( $this );
3740  RepoGroup::singleton()->clearCache( $nt ); # clear false negative cache
3741  }
3742 
3743  $dbw->begin( __METHOD__ ); # If $file was a LocalFile, its transaction would have closed our own.
3744  $pageid = $this->getArticleID( self::GAID_FOR_UPDATE );
3745  $protected = $this->isProtected();
3746 
3747  // Do the actual move
3748  $this->moveToInternal( $nt, $reason, $createRedirect );
3749 
3750  // Refresh the sortkey for this row. Be careful to avoid resetting
3751  // cl_timestamp, which may disturb time-based lists on some sites.
3752  $prefixes = $dbw->select(
3753  'categorylinks',
3754  array( 'cl_sortkey_prefix', 'cl_to' ),
3755  array( 'cl_from' => $pageid ),
3756  __METHOD__
3757  );
3758  foreach ( $prefixes as $prefixRow ) {
3759  $prefix = $prefixRow->cl_sortkey_prefix;
3760  $catTo = $prefixRow->cl_to;
3761  $dbw->update( 'categorylinks',
3762  array(
3763  'cl_sortkey' => Collation::singleton()->getSortKey(
3764  $nt->getCategorySortkey( $prefix ) ),
3765  'cl_timestamp=cl_timestamp' ),
3766  array(
3767  'cl_from' => $pageid,
3768  'cl_to' => $catTo ),
3769  __METHOD__
3770  );
3771  }
3772 
3773  $redirid = $this->getArticleID();
3774 
3775  if ( $protected ) {
3776  # Protect the redirect title as the title used to be...
3777  $dbw->insertSelect( 'page_restrictions', 'page_restrictions',
3778  array(
3779  'pr_page' => $redirid,
3780  'pr_type' => 'pr_type',
3781  'pr_level' => 'pr_level',
3782  'pr_cascade' => 'pr_cascade',
3783  'pr_user' => 'pr_user',
3784  'pr_expiry' => 'pr_expiry'
3785  ),
3786  array( 'pr_page' => $pageid ),
3787  __METHOD__,
3788  array( 'IGNORE' )
3789  );
3790  # Update the protection log
3791  $log = new LogPage( 'protect' );
3792  $comment = wfMessage(
3793  'prot_1movedto2',
3794  $this->getPrefixedText(),
3795  $nt->getPrefixedText()
3796  )->inContentLanguage()->text();
3797  if ( $reason ) {
3798  $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
3799  }
3800  // @todo FIXME: $params?
3801  $logId = $log->addEntry( 'move_prot', $nt, $comment, array( $this->getPrefixedText() ), $wgUser );
3802 
3803  // reread inserted pr_ids for log relation
3804  $insertedPrIds = $dbw->select(
3805  'page_restrictions',
3806  'pr_id',
3807  array( 'pr_page' => $redirid ),
3808  __METHOD__
3809  );
3810  $logRelationsValues = array();
3811  foreach ( $insertedPrIds as $prid ) {
3812  $logRelationsValues[] = $prid->pr_id;
3813  }
3814  $log->addRelations( 'pr_id', $logRelationsValues, $logId );
3815  }
3816 
3817  # Update watchlists
3818  $oldnamespace = MWNamespace::getSubject( $this->getNamespace() );
3819  $newnamespace = MWNamespace::getSubject( $nt->getNamespace() );
3820  $oldtitle = $this->getDBkey();
3821  $newtitle = $nt->getDBkey();
3822 
3823  if ( $oldnamespace != $newnamespace || $oldtitle != $newtitle ) {
3824  WatchedItem::duplicateEntries( $this, $nt );
3825  }
3826 
3827  $dbw->commit( __METHOD__ );
3828 
3829  wfRunHooks( 'TitleMoveComplete', array( &$this, &$nt, &$wgUser, $pageid, $redirid, $reason ) );
3830  return true;
3831  }
3832 
3843  private function moveToInternal( &$nt, $reason = '', $createRedirect = true ) {
3845 
3846  if ( $nt->exists() ) {
3847  $moveOverRedirect = true;
3848  $logType = 'move_redir';
3849  } else {
3850  $moveOverRedirect = false;
3851  $logType = 'move';
3852  }
3853 
3854  if ( $createRedirect ) {
3855  $contentHandler = ContentHandler::getForTitle( $this );
3856  $redirectContent = $contentHandler->makeRedirectContent( $nt,
3857  wfMessage( 'move-redirect-text' )->inContentLanguage()->plain() );
3858 
3859  // NOTE: If this page's content model does not support redirects, $redirectContent will be null.
3860  } else {
3861  $redirectContent = null;
3862  }
3863 
3864  $logEntry = new ManualLogEntry( 'move', $logType );
3865  $logEntry->setPerformer( $wgUser );
3866  $logEntry->setTarget( $this );
3867  $logEntry->setComment( $reason );
3868  $logEntry->setParameters( array(
3869  '4::target' => $nt->getPrefixedText(),
3870  '5::noredir' => $redirectContent ? '0': '1',
3871  ) );
3872 
3873  $formatter = LogFormatter::newFromEntry( $logEntry );
3874  $formatter->setContext( RequestContext::newExtraneousContext( $this ) );
3875  $comment = $formatter->getPlainActionText();
3876  if ( $reason ) {
3877  $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
3878  }
3879  # Truncate for whole multibyte characters.
3880  $comment = $wgContLang->truncate( $comment, 255 );
3881 
3882  $oldid = $this->getArticleID();
3883 
3884  $dbw = wfGetDB( DB_MASTER );
3885 
3886  $newpage = WikiPage::factory( $nt );
3887 
3888  if ( $moveOverRedirect ) {
3889  $newid = $nt->getArticleID();
3890  $newcontent = $newpage->getContent();
3891 
3892  # Delete the old redirect. We don't save it to history since
3893  # by definition if we've got here it's rather uninteresting.
3894  # We have to remove it so that the next step doesn't trigger
3895  # a conflict on the unique namespace+title index...
3896  $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ );
3897 
3898  $newpage->doDeleteUpdates( $newid, $newcontent );
3899  }
3900 
3901  # Save a null revision in the page's history notifying of the move
3902  $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true );
3903  if ( !is_object( $nullRevision ) ) {
3904  throw new MWException( 'No valid null revision produced in ' . __METHOD__ );
3905  }
3906 
3907  $nullRevision->insertOn( $dbw );
3908 
3909  # Change the name of the target page:
3910  $dbw->update( 'page',
3911  /* SET */ array(
3912  'page_namespace' => $nt->getNamespace(),
3913  'page_title' => $nt->getDBkey(),
3914  ),
3915  /* WHERE */ array( 'page_id' => $oldid ),
3916  __METHOD__
3917  );
3918 
3919  // clean up the old title before reset article id - bug 45348
3920  if ( !$redirectContent ) {
3921  WikiPage::onArticleDelete( $this );
3922  }
3923 
3924  $this->resetArticleID( 0 ); // 0 == non existing
3925  $nt->resetArticleID( $oldid );
3926  $newpage->loadPageData( WikiPage::READ_LOCKING ); // bug 46397
3927 
3928  $newpage->updateRevisionOn( $dbw, $nullRevision );
3929 
3930  wfRunHooks( 'NewRevisionFromEditComplete',
3931  array( $newpage, $nullRevision, $nullRevision->getParentId(), $wgUser ) );
3932 
3933  $newpage->doEditUpdates( $nullRevision, $wgUser, array( 'changed' => false ) );
3934 
3935  if ( !$moveOverRedirect ) {
3937  }
3938 
3939  # Recreate the redirect, this time in the other direction.
3940  if ( $redirectContent ) {
3941  $redirectArticle = WikiPage::factory( $this );
3942  $redirectArticle->loadFromRow( false, WikiPage::READ_LOCKING ); // bug 46397
3943  $newid = $redirectArticle->insertOn( $dbw );
3944  if ( $newid ) { // sanity
3945  $this->resetArticleID( $newid );
3946  $redirectRevision = new Revision( array(
3947  'title' => $this, // for determining the default content model
3948  'page' => $newid,
3949  'comment' => $comment,
3950  'content' => $redirectContent ) );
3951  $redirectRevision->insertOn( $dbw );
3952  $redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 );
3953 
3954  wfRunHooks( 'NewRevisionFromEditComplete',
3955  array( $redirectArticle, $redirectRevision, false, $wgUser ) );
3956 
3957  $redirectArticle->doEditUpdates( $redirectRevision, $wgUser, array( 'created' => true ) );
3958  }
3959  }
3960 
3961  # Log the move
3962  $logid = $logEntry->insert();
3963  $logEntry->publish( $logid );
3964  }
3965 
3978  public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true ) {
3979  global $wgMaximumMovedPages;
3980  // Check permissions
3981  if ( !$this->userCan( 'move-subpages' ) ) {
3982  return array( 'cant-move-subpages' );
3983  }
3984  // Do the source and target namespaces support subpages?
3985  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
3986  return array( 'namespace-nosubpages',
3988  }
3989  if ( !MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
3990  return array( 'namespace-nosubpages',
3991  MWNamespace::getCanonicalName( $nt->getNamespace() ) );
3992  }
3993 
3994  $subpages = $this->getSubpages( $wgMaximumMovedPages + 1 );
3995  $retval = array();
3996  $count = 0;
3997  foreach ( $subpages as $oldSubpage ) {
3998  $count++;
3999  if ( $count > $wgMaximumMovedPages ) {
4000  $retval[$oldSubpage->getPrefixedText()] =
4001  array( 'movepage-max-pages',
4002  $wgMaximumMovedPages );
4003  break;
4004  }
4005 
4006  // We don't know whether this function was called before
4007  // or after moving the root page, so check both
4008  // $this and $nt
4009  if ( $oldSubpage->getArticleID() == $this->getArticleID()
4010  || $oldSubpage->getArticleID() == $nt->getArticleID()
4011  ) {
4012  // When moving a page to a subpage of itself,
4013  // don't move it twice
4014  continue;
4015  }
4016  $newPageName = preg_replace(
4017  '#^' . preg_quote( $this->getDBkey(), '#' ) . '#',
4018  StringUtils::escapeRegexReplacement( $nt->getDBkey() ), # bug 21234
4019  $oldSubpage->getDBkey() );
4020  if ( $oldSubpage->isTalkPage() ) {
4021  $newNs = $nt->getTalkPage()->getNamespace();
4022  } else {
4023  $newNs = $nt->getSubjectPage()->getNamespace();
4024  }
4025  # Bug 14385: we need makeTitleSafe because the new page names may
4026  # be longer than 255 characters.
4027  $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
4028 
4029  $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect );
4030  if ( $success === true ) {
4031  $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
4032  } else {
4033  $retval[$oldSubpage->getPrefixedText()] = $success;
4034  }
4035  }
4036  return $retval;
4037  }
4038 
4045  public function isSingleRevRedirect() {
4046  global $wgContentHandlerUseDB;
4047 
4048  $dbw = wfGetDB( DB_MASTER );
4049 
4050  # Is it a redirect?
4051  $fields = array( 'page_is_redirect', 'page_latest', 'page_id' );
4052  if ( $wgContentHandlerUseDB ) {
4053  $fields[] = 'page_content_model';
4054  }
4055 
4056  $row = $dbw->selectRow( 'page',
4057  $fields,
4058  $this->pageCond(),
4059  __METHOD__,
4060  array( 'FOR UPDATE' )
4061  );
4062  # Cache some fields we may want
4063  $this->mArticleID = $row ? intval( $row->page_id ) : 0;
4064  $this->mRedirect = $row ? (bool)$row->page_is_redirect : false;
4065  $this->mLatestID = $row ? intval( $row->page_latest ) : false;
4066  $this->mContentModel = $row && isset( $row->page_content_model ) ? strval( $row->page_content_model ) : false;
4067  if ( !$this->mRedirect ) {
4068  return false;
4069  }
4070  # Does the article have a history?
4071  $row = $dbw->selectField( array( 'page', 'revision' ),
4072  'rev_id',
4073  array( 'page_namespace' => $this->getNamespace(),
4074  'page_title' => $this->getDBkey(),
4075  'page_id=rev_page',
4076  'page_latest != rev_id'
4077  ),
4078  __METHOD__,
4079  array( 'FOR UPDATE' )
4080  );
4081  # Return true if there was no history
4082  return ( $row === false );
4083  }
4084 
4092  public function isValidMoveTarget( $nt ) {
4093  # Is it an existing file?
4094  if ( $nt->getNamespace() == NS_FILE ) {
4095  $file = wfLocalFile( $nt );
4096  if ( $file->exists() ) {
4097  wfDebug( __METHOD__ . ": file exists\n" );
4098  return false;
4099  }
4100  }
4101  # Is it a redirect with no history?
4102  if ( !$nt->isSingleRevRedirect() ) {
4103  wfDebug( __METHOD__ . ": not a one-rev redirect\n" );
4104  return false;
4105  }
4106  # Get the article text
4108  if ( !is_object( $rev ) ) {
4109  return false;
4110  }
4111  $content = $rev->getContent();
4112  # Does the redirect point to the source?
4113  # Or is it a broken self-redirect, usually caused by namespace collisions?
4114  $redirTitle = $content ? $content->getRedirectTarget() : null;
4115 
4116  if ( $redirTitle ) {
4117  if ( $redirTitle->getPrefixedDBkey() != $this->getPrefixedDBkey() &&
4118  $redirTitle->getPrefixedDBkey() != $nt->getPrefixedDBkey() ) {
4119  wfDebug( __METHOD__ . ": redirect points to other page\n" );
4120  return false;
4121  } else {
4122  return true;
4123  }
4124  } else {
4125  # Fail safe (not a redirect after all. strange.)
4126  wfDebug( __METHOD__ . ": failsafe: database sais " . $nt->getPrefixedDBkey() .
4127  " is a redirect, but it doesn't contain a valid redirect.\n" );
4128  return false;
4129  }
4130  }
4131 
4139  public function getParentCategories() {
4141 
4142  $data = array();
4143 
4144  $titleKey = $this->getArticleID();
4145 
4146  if ( $titleKey === 0 ) {
4147  return $data;
4148  }
4149 
4150  $dbr = wfGetDB( DB_SLAVE );
4151 
4152  $res = $dbr->select(
4153  'categorylinks',
4154  'cl_to',
4155  array( 'cl_from' => $titleKey ),
4156  __METHOD__
4157  );
4158 
4159  if ( $res->numRows() > 0 ) {
4160  foreach ( $res as $row ) {
4161  // $data[] = Title::newFromText($wgContLang->getNsText ( NS_CATEGORY ).':'.$row->cl_to);
4162  $data[$wgContLang->getNsText( NS_CATEGORY ) . ':' . $row->cl_to] = $this->getFullText();
4163  }
4164  }
4165  return $data;
4166  }
4167 
4174  public function getParentCategoryTree( $children = array() ) {
4175  $stack = array();
4176  $parents = $this->getParentCategories();
4177 
4178  if ( $parents ) {
4179  foreach ( $parents as $parent => $current ) {
4180  if ( array_key_exists( $parent, $children ) ) {
4181  # Circular reference
4182  $stack[$parent] = array();
4183  } else {
4184  $nt = Title::newFromText( $parent );
4185  if ( $nt ) {
4186  $stack[$parent] = $nt->getParentCategoryTree( $children + array( $parent => 1 ) );
4187  }
4188  }
4189  }
4190  }
4191 
4192  return $stack;
4193  }
4194 
4201  public function pageCond() {
4202  if ( $this->mArticleID > 0 ) {
4203  // PK avoids secondary lookups in InnoDB, shouldn't hurt other DBs
4204  return array( 'page_id' => $this->mArticleID );
4205  } else {
4206  return array( 'page_namespace' => $this->mNamespace, 'page_title' => $this->mDbkeyform );
4207  }
4208  }
4209 
4217  public function getPreviousRevisionID( $revId, $flags = 0 ) {
4219  $revId = $db->selectField( 'revision', 'rev_id',
4220  array(
4221  'rev_page' => $this->getArticleID( $flags ),
4222  'rev_id < ' . intval( $revId )
4223  ),
4224  __METHOD__,
4225  array( 'ORDER BY' => 'rev_id DESC' )
4226  );
4227 
4228  if ( $revId === false ) {
4229  return false;
4230  } else {
4231  return intval( $revId );
4232  }
4233  }
4234 
4242  public function getNextRevisionID( $revId, $flags = 0 ) {
4244  $revId = $db->selectField( 'revision', 'rev_id',
4245  array(
4246  'rev_page' => $this->getArticleID( $flags ),
4247  'rev_id > ' . intval( $revId )
4248  ),
4249  __METHOD__,
4250  array( 'ORDER BY' => 'rev_id' )
4251  );
4252 
4253  if ( $revId === false ) {
4254  return false;
4255  } else {
4256  return intval( $revId );
4257  }
4258  }
4259 
4266  public function getFirstRevision( $flags = 0 ) {
4267  $pageId = $this->getArticleID( $flags );
4268  if ( $pageId ) {
4270  $row = $db->selectRow( 'revision', Revision::selectFields(),
4271  array( 'rev_page' => $pageId ),
4272  __METHOD__,
4273  array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 )
4274  );
4275  if ( $row ) {
4276  return new Revision( $row );
4277  }
4278  }
4279  return null;
4280  }
4281 
4288  public function getEarliestRevTime( $flags = 0 ) {
4289  $rev = $this->getFirstRevision( $flags );
4290  return $rev ? $rev->getTimestamp() : null;
4291  }
4292 
4298  public function isNewPage() {
4299  $dbr = wfGetDB( DB_SLAVE );
4300  return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
4301  }
4302 
4308  public function isBigDeletion() {
4309  global $wgDeleteRevisionsLimit;
4310 
4311  if ( !$wgDeleteRevisionsLimit ) {
4312  return false;
4313  }
4314 
4315  $revCount = $this->estimateRevisionCount();
4316  return $revCount > $wgDeleteRevisionsLimit;
4317  }
4318 
4324  public function estimateRevisionCount() {
4325  if ( !$this->exists() ) {
4326  return 0;
4327  }
4328 
4329  if ( $this->mEstimateRevisions === null ) {
4330  $dbr = wfGetDB( DB_SLAVE );
4331  $this->mEstimateRevisions = $dbr->estimateRowCount( 'revision', '*',
4332  array( 'rev_page' => $this->getArticleID() ), __METHOD__ );
4333  }
4334 
4336  }
4337 
4347  public function countRevisionsBetween( $old, $new, $max = null ) {
4348  if ( !( $old instanceof Revision ) ) {
4349  $old = Revision::newFromTitle( $this, (int)$old );
4350  }
4351  if ( !( $new instanceof Revision ) ) {
4352  $new = Revision::newFromTitle( $this, (int)$new );
4353  }
4354  if ( !$old || !$new ) {
4355  return 0; // nothing to compare
4356  }
4357  $dbr = wfGetDB( DB_SLAVE );
4358  $conds = array(
4359  'rev_page' => $this->getArticleID(),
4360  'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4361  'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4362  );
4363  if ( $max !== null ) {
4364  $res = $dbr->select( 'revision', '1',
4365  $conds,
4366  __METHOD__,
4367  array( 'LIMIT' => $max + 1 ) // extra to detect truncation
4368  );
4369  return $res->numRows();
4370  } else {
4371  return (int)$dbr->selectField( 'revision', 'count(*)', $conds, __METHOD__ );
4372  }
4373  }
4374 
4391  public function getAuthorsBetween( $old, $new, $limit, $options = array() ) {
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  // XXX: what if Revision objects are passed in, but they don't refer to this title?
4399  // Add $old->getPage() != $new->getPage() || $old->getPage() != $this->getArticleID()
4400  // in the sanity check below?
4401  if ( !$old || !$new ) {
4402  return null; // nothing to compare
4403  }
4404  $authors = array();
4405  $old_cmp = '>';
4406  $new_cmp = '<';
4407  $options = (array)$options;
4408  if ( in_array( 'include_old', $options ) ) {
4409  $old_cmp = '>=';
4410  }
4411  if ( in_array( 'include_new', $options ) ) {
4412  $new_cmp = '<=';
4413  }
4414  if ( in_array( 'include_both', $options ) ) {
4415  $old_cmp = '>=';
4416  $new_cmp = '<=';
4417  }
4418  // No DB query needed if $old and $new are the same or successive revisions:
4419  if ( $old->getId() === $new->getId() ) {
4420  return ( $old_cmp === '>' && $new_cmp === '<' ) ? array() : array( $old->getRawUserText() );
4421  } elseif ( $old->getId() === $new->getParentId() ) {
4422  if ( $old_cmp === '>=' && $new_cmp === '<=' ) {
4423  $authors[] = $old->getRawUserText();
4424  if ( $old->getRawUserText() != $new->getRawUserText() ) {
4425  $authors[] = $new->getRawUserText();
4426  }
4427  } elseif ( $old_cmp === '>=' ) {
4428  $authors[] = $old->getRawUserText();
4429  } elseif ( $new_cmp === '<=' ) {
4430  $authors[] = $new->getRawUserText();
4431  }
4432  return $authors;
4433  }
4434  $dbr = wfGetDB( DB_SLAVE );
4435  $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
4436  array(
4437  'rev_page' => $this->getArticleID(),
4438  "rev_timestamp $old_cmp " . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4439  "rev_timestamp $new_cmp " . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4440  ), __METHOD__,
4441  array( 'LIMIT' => $limit + 1 ) // add one so caller knows it was truncated
4442  );
4443  foreach ( $res as $row ) {
4444  $authors[] = $row->rev_user_text;
4445  }
4446  return $authors;
4447  }
4448 
4463  public function countAuthorsBetween( $old, $new, $limit, $options = array() ) {
4464  $authors = $this->getAuthorsBetween( $old, $new, $limit, $options );
4465  return $authors ? count( $authors ) : 0;
4466  }
4467 
4474  public function equals( Title $title ) {
4475  // Note: === is necessary for proper matching of number-like titles.
4476  return $this->getInterwiki() === $title->getInterwiki()
4477  && $this->getNamespace() == $title->getNamespace()
4478  && $this->getDBkey() === $title->getDBkey();
4479  }
4480 
4487  public function isSubpageOf( Title $title ) {
4488  return $this->getInterwiki() === $title->getInterwiki()
4489  && $this->getNamespace() == $title->getNamespace()
4490  && strpos( $this->getDBkey(), $title->getDBkey() . '/' ) === 0;
4491  }
4492 
4502  public function exists() {
4503  return $this->getArticleID() != 0;
4504  }
4505 
4522  public function isAlwaysKnown() {
4523  $isKnown = null;
4524 
4535  wfRunHooks( 'TitleIsAlwaysKnown', array( $this, &$isKnown ) );
4536 
4537  if ( !is_null( $isKnown ) ) {
4538  return $isKnown;
4539  }
4540 
4541  if ( $this->isExternal() ) {
4542  return true; // any interwiki link might be viewable, for all we know
4543  }
4544 
4545  switch ( $this->mNamespace ) {
4546  case NS_MEDIA:
4547  case NS_FILE:
4548  // file exists, possibly in a foreign repo
4549  return (bool)wfFindFile( $this );
4550  case NS_SPECIAL:
4551  // valid special page
4552  return SpecialPageFactory::exists( $this->getDBkey() );
4553  case NS_MAIN:
4554  // selflink, possibly with fragment
4555  return $this->mDbkeyform == '';
4556  case NS_MEDIAWIKI:
4557  // known system message
4558  return $this->hasSourceText() !== false;
4559  default:
4560  return false;
4561  }
4562  }
4563 
4575  public function isKnown() {
4576  return $this->isAlwaysKnown() || $this->exists();
4577  }
4578 
4584  public function hasSourceText() {
4585  if ( $this->exists() ) {
4586  return true;
4587  }
4588 
4589  if ( $this->mNamespace == NS_MEDIAWIKI ) {
4590  // If the page doesn't exist but is a known system message, default
4591  // message content will be displayed, same for language subpages-
4592  // Use always content language to avoid loading hundreds of languages
4593  // to get the link color.
4595  list( $name, ) = MessageCache::singleton()->figureMessage( $wgContLang->lcfirst( $this->getText() ) );
4596  $message = wfMessage( $name )->inLanguage( $wgContLang )->useDatabase( false );
4597  return $message->exists();
4598  }
4599 
4600  return false;
4601  }
4602 
4608  public function getDefaultMessageText() {
4610 
4611  if ( $this->getNamespace() != NS_MEDIAWIKI ) { // Just in case
4612  return false;
4613  }
4614 
4615  list( $name, $lang ) = MessageCache::singleton()->figureMessage( $wgContLang->lcfirst( $this->getText() ) );
4616  $message = wfMessage( $name )->inLanguage( $lang )->useDatabase( false );
4617 
4618  if ( $message->exists() ) {
4619  return $message->plain();
4620  } else {
4621  return false;
4622  }
4623  }
4624 
4630  public function invalidateCache() {
4631  if ( wfReadOnly() ) {
4632  return false;
4633  }
4634 
4635  $method = __METHOD__;
4636  $dbw = wfGetDB( DB_MASTER );
4637  $conds = $this->pageCond();
4638  $dbw->onTransactionIdle( function() use ( $dbw, $conds, $method ) {
4639  $dbw->update(
4640  'page',
4641  array( 'page_touched' => $dbw->timestamp() ),
4642  $conds,
4643  $method
4644  );
4645  } );
4646 
4647  return true;
4648  }
4649 
4655  public function touchLinks() {
4656  $u = new HTMLCacheUpdate( $this, 'pagelinks' );
4657  $u->doUpdate();
4658 
4659  if ( $this->getNamespace() == NS_CATEGORY ) {
4660  $u = new HTMLCacheUpdate( $this, 'categorylinks' );
4661  $u->doUpdate();
4662  }
4663  }
4664 
4671  public function getTouched( $db = null ) {
4672  $db = isset( $db ) ? $db : wfGetDB( DB_SLAVE );
4673  $touched = $db->selectField( 'page', 'page_touched', $this->pageCond(), __METHOD__ );
4674  return $touched;
4675  }
4676 
4683  public function getNotificationTimestamp( $user = null ) {
4684  global $wgUser, $wgShowUpdatedMarker;
4685  // Assume current user if none given
4686  if ( !$user ) {
4687  $user = $wgUser;
4688  }
4689  // Check cache first
4690  $uid = $user->getId();
4691  // avoid isset here, as it'll return false for null entries
4692  if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) {
4693  return $this->mNotificationTimestamp[$uid];
4694  }
4695  if ( !$uid || !$wgShowUpdatedMarker || !$user->isAllowed( 'viewmywatchlist' ) ) {
4696  $this->mNotificationTimestamp[$uid] = false;
4697  return $this->mNotificationTimestamp[$uid];
4698  }
4699  // Don't cache too much!
4700  if ( count( $this->mNotificationTimestamp ) >= self::CACHE_MAX ) {
4701  $this->mNotificationTimestamp = array();
4702  }
4703  $dbr = wfGetDB( DB_SLAVE );
4704  $this->mNotificationTimestamp[$uid] = $dbr->selectField( 'watchlist',
4705  'wl_notificationtimestamp',
4706  array(
4707  'wl_user' => $user->getId(),
4708  'wl_namespace' => $this->getNamespace(),
4709  'wl_title' => $this->getDBkey(),
4710  ),
4711  __METHOD__
4712  );
4713  return $this->mNotificationTimestamp[$uid];
4714  }
4715 
4722  public function getNamespaceKey( $prepend = 'nstab-' ) {
4724  // Gets the subject namespace if this title
4725  $namespace = MWNamespace::getSubject( $this->getNamespace() );
4726  // Checks if canonical namespace name exists for namespace
4727  if ( MWNamespace::exists( $this->getNamespace() ) ) {
4728  // Uses canonical namespace name
4729  $namespaceKey = MWNamespace::getCanonicalName( $namespace );
4730  } else {
4731  // Uses text of namespace
4732  $namespaceKey = $this->getSubjectNsText();
4733  }
4734  // Makes namespace key lowercase
4735  $namespaceKey = $wgContLang->lc( $namespaceKey );
4736  // Uses main
4737  if ( $namespaceKey == '' ) {
4738  $namespaceKey = 'main';
4739  }
4740  // Changes file to image for backwards compatibility
4741  if ( $namespaceKey == 'file' ) {
4742  $namespaceKey = 'image';
4743  }
4744  return $prepend . $namespaceKey;
4745  }
4746 
4753  public function getRedirectsHere( $ns = null ) {
4754  $redirs = array();
4755 
4756  $dbr = wfGetDB( DB_SLAVE );
4757  $where = array(
4758  'rd_namespace' => $this->getNamespace(),
4759  'rd_title' => $this->getDBkey(),
4760  'rd_from = page_id'
4761  );
4762  if ( $this->isExternal() ) {
4763  $where['rd_interwiki'] = $this->getInterwiki();
4764  } else {
4765  $where[] = 'rd_interwiki = ' . $dbr->addQuotes( '' ) . ' OR rd_interwiki IS NULL';
4766  }
4767  if ( !is_null( $ns ) ) {
4768  $where['page_namespace'] = $ns;
4769  }
4770 
4771  $res = $dbr->select(
4772  array( 'redirect', 'page' ),
4773  array( 'page_namespace', 'page_title' ),
4774  $where,
4775  __METHOD__
4776  );
4777 
4778  foreach ( $res as $row ) {
4779  $redirs[] = self::newFromRow( $row );
4780  }
4781  return $redirs;
4782  }
4783 
4789  public function isValidRedirectTarget() {
4790  global $wgInvalidRedirectTargets;
4791 
4792  // invalid redirect targets are stored in a global array, but explicitly disallow Userlogout here
4793  if ( $this->isSpecial( 'Userlogout' ) ) {
4794  return false;
4795  }
4796 
4797  foreach ( $wgInvalidRedirectTargets as $target ) {
4798  if ( $this->isSpecial( $target ) ) {
4799  return false;
4800  }
4801  }
4802 
4803  return true;
4804  }
4805 
4811  public function getBacklinkCache() {
4812  return BacklinkCache::get( $this );
4813  }
4814 
4820  public function canUseNoindex() {
4821  global $wgContentNamespaces, $wgExemptFromUserRobotsControl;
4822 
4823  $bannedNamespaces = is_null( $wgExemptFromUserRobotsControl )
4824  ? $wgContentNamespaces
4825  : $wgExemptFromUserRobotsControl;
4826 
4827  return !in_array( $this->mNamespace, $bannedNamespaces );
4828 
4829  }
4830 
4841  public function getCategorySortkey( $prefix = '' ) {
4842  $unprefixed = $this->getText();
4843 
4844  // Anything that uses this hook should only depend
4845  // on the Title object passed in, and should probably
4846  // tell the users to run updateCollations.php --force
4847  // in order to re-sort existing category relations.
4848  wfRunHooks( 'GetDefaultSortkey', array( $this, &$unprefixed ) );
4849  if ( $prefix !== '' ) {
4850  # Separate with a line feed, so the unprefixed part is only used as
4851  # a tiebreaker when two pages have the exact same prefix.
4852  # In UCA, tab is the only character that can sort above LF
4853  # so we strip both of them from the original prefix.
4854  $prefix = strtr( $prefix, "\n\t", ' ' );
4855  return "$prefix\n$unprefixed";
4856  }
4857  return $unprefixed;
4858  }
4859 
4868  public function getPageLanguage() {
4869  global $wgLang, $wgLanguageCode;
4870  wfProfileIn( __METHOD__ );
4871  if ( $this->isSpecialPage() ) {
4872  // special pages are in the user language
4873  wfProfileOut( __METHOD__ );
4874  return $wgLang;
4875  }
4876 
4877  if ( !$this->mPageLanguage || $this->mPageLanguage[1] !== $wgLanguageCode ) {
4878  // Note that this may depend on user settings, so the cache should be only per-request.
4879  // NOTE: ContentHandler::getPageLanguage() may need to load the content to determine the page language!
4880  // Checking $wgLanguageCode hasn't changed for the benefit of unit tests.
4881  $contentHandler = ContentHandler::getForTitle( $this );
4882  $langObj = wfGetLangObj( $contentHandler->getPageLanguage( $this ) );
4883  $this->mPageLanguage = array( $langObj->getCode(), $wgLanguageCode );
4884  } else {
4885  $langObj = wfGetLangObj( $this->mPageLanguage[0] );
4886  }
4887  wfProfileOut( __METHOD__ );
4888  return $langObj;
4889  }
4890 
4899  public function getPageViewLanguage() {
4900  global $wgLang;
4901 
4902  if ( $this->isSpecialPage() ) {
4903  // If the user chooses a variant, the content is actually
4904  // in a language whose code is the variant code.
4905  $variant = $wgLang->getPreferredVariant();
4906  if ( $wgLang->getCode() !== $variant ) {
4907  return Language::factory( $variant );
4908  }
4909 
4910  return $wgLang;
4911  }
4912 
4913  //NOTE: can't be cached persistently, depends on user settings
4914  //NOTE: ContentHandler::getPageViewLanguage() may need to load the content to determine the page language!
4915  $contentHandler = ContentHandler::getForTitle( $this );
4916  $pageLang = $contentHandler->getPageViewLanguage( $this );
4917  return $pageLang;
4918  }
4919 
4930  public function getEditNotices( $oldid = 0 ) {
4931  $notices = array();
4932 
4933  # Optional notices on a per-namespace and per-page basis
4934  $editnotice_ns = 'editnotice-' . $this->getNamespace();
4935  $editnotice_ns_message = wfMessage( $editnotice_ns );
4936  if ( $editnotice_ns_message->exists() ) {
4937  $notices[$editnotice_ns] = $editnotice_ns_message->parseAsBlock();
4938  }
4939  if ( MWNamespace::hasSubpages( $this->getNamespace() ) ) {
4940  $parts = explode( '/', $this->getDBkey() );
4941  $editnotice_base = $editnotice_ns;
4942  while ( count( $parts ) > 0 ) {
4943  $editnotice_base .= '-' . array_shift( $parts );
4944  $editnotice_base_msg = wfMessage( $editnotice_base );
4945  if ( $editnotice_base_msg->exists() ) {
4946  $notices[$editnotice_base] = $editnotice_base_msg->parseAsBlock();
4947  }
4948  }
4949  } else {
4950  # Even if there are no subpages in namespace, we still don't want / in MW ns.
4951  $editnoticeText = $editnotice_ns . '-' . str_replace( '/', '-', $this->getDBkey() );
4952  $editnoticeMsg = wfMessage( $editnoticeText );
4953  if ( $editnoticeMsg->exists() ) {
4954  $notices[$editnoticeText] = $editnoticeMsg->parseAsBlock();
4955  }
4956  }
4957 
4958  wfRunHooks( 'TitleGetEditNotices', array( $this, $oldid, &$notices ) );
4959  return $notices;
4960  }
4961 }
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:2495
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:4820
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:1637
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:2566
$wgUser
$wgUser
Definition: Setup.php:552
Title\isNamespaceProtected
isNamespaceProtected(User $user)
Determines if $user is unable to edit this page because it has been protected by $wgNamespaceProtecti...
Definition: Title.php:2626
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:4242
Title\isBigDeletion
isBigDeletion()
Check whether the number of revisions of this page surpasses $wgDeleteRevisionsLimit.
Definition: Title.php:4308
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:2658
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:4391
Title\getFilteredRestrictionTypes
static getFilteredRestrictionTypes( $exists=true)
Get a filtered list of all restriction types supported by this wiki.
Definition: Title.php:2450
wfMergeErrorArrays
wfMergeErrorArrays()
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
Definition: GlobalFunctions.php:213
Title\getInternalURL
getInternalURL( $query='', $query2=false)
Get the URL form for an internal link.
Definition: Title.php:1782
$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:2303
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:4811
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:4324
Title\countRevisionsBetween
countRevisionsBetween( $old, $new, $max=null)
Get the number of revisions between the given revision.
Definition: Title.php:4347
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:3338
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3659
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:2209
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:4683
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:4655
$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:2128
wfUrlencode
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
Definition: GlobalFunctions.php:330
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:2002
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:1729
Title\getArticleID
getArticleID( $flags=0)
Get the article ID for this Title from the link cache, adding it if necessary.
Definition: Title.php:3121
$right
return false if a UserGetRights hook might remove the named right $right
Definition: hooks.txt:2798
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:3503
Title\secureAndSplit
secureAndSplit()
Secure and split - main initialisation function for this object.
Definition: Title.php:3290
Title\quickUserCan
quickUserCan( $action, $user=null)
Can $user perform $action on this page? This skips potentially expensive cascading permission checks ...
Definition: Title.php:1882
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:2063
$limit
if( $sleep) $limit
Definition: importImages.php:99
Title\moveNoAuth
moveNoAuth(&$nt)
Move this page without authentication.
Definition: Title.php:3546
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:1313
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:1430
$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:1817
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:2856
$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:4608
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:283
Title\hasSourceText
hasSourceText()
Does this page have source text?
Definition: Title.php:4584
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:4147
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:3243
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:2113
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:2410
Title\exists
exists()
Check if page exists.
Definition: Title.php:4502
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:4139
Title\userCanRead
userCanRead()
Can $wgUser read this page?
Definition: Title.php:1862
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:459
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:2162
Title\isCascadeProtected
isCascadeProtected()
Cascading protection: Return true if cascading restrictions apply to this page, false if not.
Definition: Title.php:2644
Title\isValidMoveTarget
isValidMoveTarget( $nt)
Checks if $this can be moved to a given Title.
Definition: Title.php:4092
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:1917
$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:3853
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:1127
$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:1752
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:2030
Title\isProtected
isProtected( $action='')
Does the title correspond to a protected article?
Definition: Title.php:2594
Title\flushRestrictions
flushRestrictions()
Flush the protection cache in this object and force reload from the database.
Definition: Title.php:2980
Title\getCategorySortkey
getCategorySortkey( $prefix='')
Returns the raw sort key to be used for categories, with the specified prefix.
Definition: Title.php:4841
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:1956
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:4789
Title\deleteTitleProtection
deleteTitleProtection()
Remove any title protection due to page existing.
Definition: Title.php:2548
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:3467
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:3843
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
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:2378
Title\checkCSSandJSPermissions
checkCSSandJSPermissions( $action, $user, $errors, $doExpensiveQueries, $short)
Check CSS/JS sub-page permissions.
Definition: Title.php:2092
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:4010
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:1352
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:4753
Title\checkQuickPermissions
checkQuickPermissions( $action, $user, $errors, $doExpensiveQueries, $short)
Permissions checks that fail most often, and which are easiest to test.
Definition: Title.php:1943
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:326
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:4474
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:2514
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:1765
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:2268
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:1802
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:1099
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:2431
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:933
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:4463
Title\areRestrictionsCascading
areRestrictionsCascading()
Returns cascading restrictions for the current article.
Definition: Title.php:2822
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:3210
Title\areRestrictionsLoaded
areRestrictionsLoaded()
Accessor for mRestrictionsLoaded.
Definition: Title.php:2769
TitleValue\getFragment
getFragment()
Definition: TitleValue.php:103
Title\getSubpages
getSubpages( $limit=-1)
Get all subpages of this page.
Definition: Title.php:3042
Title\isSingleRevRedirect
isSingleRevRedirect()
Checks if this page is just a one-rev redirect.
Definition: Title.php:4045
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:4899
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:4174
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:2810
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:3560
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:271
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:561
Title\isSubpageOf
isSubpageOf(Title $title)
Check if this title is a subpage of another title.
Definition: Title.php:4487
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:4722
Title\getEditURL
getEditURL()
Get the edit URL for this Title.
Definition: Title.php:1828
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:4575
$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:3667
$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:1896
Title\getFirstRevision
getFirstRevision( $flags=0)
Get the first revision of the page.
Definition: Title.php:4266
Title\getAllRestrictions
getAllRestrictions()
Accessor/initialisation for mRestrictions.
Definition: Title.php:2796
Title\getEarliestRevTime
getEarliestRevTime( $flags=0)
Get the oldest revision timestamp of this page.
Definition: Title.php:4288
User\isEveryoneAllowed
static isEveryoneAllowed( $right)
Check if all users have the given permission.
Definition: User.php:4160
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:3712
$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:2672
$cache
$cache
Definition: mcc.php:32
Title\getRestrictionTypes
getRestrictionTypes()
Returns restriction types for the current Title.
Definition: Title.php:2468
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:2530
Title\loadRestrictionsFromResultWrapper
loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions=null)
Loads a string into mRestrictions array.
Definition: Title.php:2837
Title\isRedirect
isRedirect( $flags=0)
Is this an article that is a redirect page? Uses link cache, adding it if necessary.
Definition: Title.php:3147
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:3070
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:3396
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:3269
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:3531
Title\getPreviousRevisionID
getPreviousRevisionID( $revId, $flags=0)
Get the revision ID of the previous revision.
Definition: Title.php:4217
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:4868
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:4930
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:4671
wfFindFile
wfFindFile( $title, $options=array())
Find a file.
Definition: GlobalFunctions.php:3702
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:4522
Title\isDeletedQuick
isDeletedQuick()
Is there a version of this page in the deletion archive?
Definition: Title.php:3095
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:4298
Title\moveSubpages
moveSubpages( $nt, $auth=true, $reason='', $createRedirect=true)
Move this page's subpages to be subpages of $nt.
Definition: Title.php:3978
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:3457
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:2573
Title\getRestrictions
getRestrictions( $action)
Accessor/initialisation for mRestrictions.
Definition: Title.php:2779
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:3713
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:4630
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:3380
$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:3014
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:4201
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:2988
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:497
User\getGroupsWithPermission
static getGroupsWithPermission( $role)
Get all the groups who have a given permission.
Definition: User.php:4124
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:1843
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:2939
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:367
Title\$mPrefixedText
$mPrefixedText
Text form including namespace/interwiki, initialised on demand.
Definition: Title.php:78
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:3182
Title\isWatchable
isWatchable()
Can this title be added to a user's watchlist?
Definition: Title.php:988