MediaWiki  1.23.15
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  global $wgEmailConfirmToEdit, $wgBlockDisablesLogin;
2270  // Account creation blocks handled at userlogin.
2271  // Unblocking handled in SpecialUnblock
2272  if ( !$doExpensiveQueries || in_array( $action, array( 'createaccount', 'unblock' ) ) ) {
2273  return $errors;
2274  }
2275 
2276  // Optimize for a very common case
2277  if ( $action === 'read' && !$wgBlockDisablesLogin ) {
2278  return $errors;
2279  }
2280 
2281 
2282  if ( $wgEmailConfirmToEdit && !$user->isEmailConfirmed() ) {
2283  $errors[] = array( 'confirmedittext' );
2284  }
2285 
2286  if ( ( $action == 'edit' || $action == 'create' ) && !$user->isBlockedFrom( $this ) ) {
2287  // Don't block the user from editing their own talk page unless they've been
2288  // explicitly blocked from that too.
2289  } elseif ( $user->isBlocked() && $user->mBlock->prevents( $action ) !== false ) {
2290  // @todo FIXME: Pass the relevant context into this function.
2291  $errors[] = $user->getBlock()->getPermissionsError( RequestContext::getMain() );
2292  }
2293 
2294  return $errors;
2295  }
2296 
2308  private function checkReadPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
2309  global $wgWhitelistRead, $wgWhitelistReadRegexp;
2310 
2311  $whitelisted = false;
2312  if ( User::isEveryoneAllowed( 'read' ) ) {
2313  # Shortcut for public wikis, allows skipping quite a bit of code
2314  $whitelisted = true;
2315  } elseif ( $user->isAllowed( 'read' ) ) {
2316  # If the user is allowed to read pages, he is allowed to read all pages
2317  $whitelisted = true;
2318  } elseif ( $this->isSpecial( 'Userlogin' )
2319  || $this->isSpecial( 'ChangePassword' )
2320  || $this->isSpecial( 'PasswordReset' )
2321  ) {
2322  # Always grant access to the login page.
2323  # Even anons need to be able to log in.
2324  $whitelisted = true;
2325  } elseif ( is_array( $wgWhitelistRead ) && count( $wgWhitelistRead ) ) {
2326  # Time to check the whitelist
2327  # Only do these checks is there's something to check against
2328  $name = $this->getPrefixedText();
2329  $dbName = $this->getPrefixedDBkey();
2330 
2331  // Check for explicit whitelisting with and without underscores
2332  if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) {
2333  $whitelisted = true;
2334  } elseif ( $this->getNamespace() == NS_MAIN ) {
2335  # Old settings might have the title prefixed with
2336  # a colon for main-namespace pages
2337  if ( in_array( ':' . $name, $wgWhitelistRead ) ) {
2338  $whitelisted = true;
2339  }
2340  } elseif ( $this->isSpecialPage() ) {
2341  # If it's a special page, ditch the subpage bit and check again
2342  $name = $this->getDBkey();
2343  list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name );
2344  if ( $name ) {
2345  $pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
2346  if ( in_array( $pure, $wgWhitelistRead, true ) ) {
2347  $whitelisted = true;
2348  }
2349  }
2350  }
2351  }
2352 
2353  if ( !$whitelisted && is_array( $wgWhitelistReadRegexp ) && !empty( $wgWhitelistReadRegexp ) ) {
2354  $name = $this->getPrefixedText();
2355  // Check for regex whitelisting
2356  foreach ( $wgWhitelistReadRegexp as $listItem ) {
2357  if ( preg_match( $listItem, $name ) ) {
2358  $whitelisted = true;
2359  break;
2360  }
2361  }
2362  }
2363 
2364  if ( !$whitelisted ) {
2365  # If the title is not whitelisted, give extensions a chance to do so...
2366  wfRunHooks( 'TitleReadWhitelist', array( $this, $user, &$whitelisted ) );
2367  if ( !$whitelisted ) {
2368  $errors[] = $this->missingPermissionError( $action, $short );
2369  }
2370  }
2371 
2372  return $errors;
2373  }
2374 
2383  private function missingPermissionError( $action, $short ) {
2384  // We avoid expensive display logic for quickUserCan's and such
2385  if ( $short ) {
2386  return array( 'badaccess-group0' );
2387  }
2388 
2389  $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
2390  User::getGroupsWithPermission( $action ) );
2391 
2392  if ( count( $groups ) ) {
2393  global $wgLang;
2394  return array(
2395  'badaccess-groups',
2396  $wgLang->commaList( $groups ),
2397  count( $groups )
2398  );
2399  } else {
2400  return array( 'badaccess-group0' );
2401  }
2402  }
2403 
2415  protected function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries = true, $short = false ) {
2416  wfProfileIn( __METHOD__ );
2417 
2418  # Read has special handling
2419  if ( $action == 'read' ) {
2420  $checks = array(
2421  'checkPermissionHooks',
2422  'checkReadPermissions',
2423  'checkUserBlock', // for wgBlockDisablesLogin
2424  );
2425  } else {
2426  $checks = array(
2427  'checkQuickPermissions',
2428  'checkPermissionHooks',
2429  'checkSpecialsAndNSPermissions',
2430  'checkCSSandJSPermissions',
2431  'checkPageRestrictions',
2432  'checkCascadingSourcesRestrictions',
2433  'checkActionPermissions',
2434  'checkUserBlock'
2435  );
2436  }
2437 
2438  $errors = array();
2439  while ( count( $checks ) > 0 &&
2440  !( $short && count( $errors ) > 0 ) ) {
2441  $method = array_shift( $checks );
2442  $errors = $this->$method( $action, $user, $errors, $doExpensiveQueries, $short );
2443  }
2444 
2445  wfProfileOut( __METHOD__ );
2446  return $errors;
2447  }
2448 
2456  public static function getFilteredRestrictionTypes( $exists = true ) {
2457  global $wgRestrictionTypes;
2458  $types = $wgRestrictionTypes;
2459  if ( $exists ) {
2460  # Remove the create restriction for existing titles
2461  $types = array_diff( $types, array( 'create' ) );
2462  } else {
2463  # Only the create and upload restrictions apply to non-existing titles
2464  $types = array_intersect( $types, array( 'create', 'upload' ) );
2465  }
2466  return $types;
2467  }
2468 
2474  public function getRestrictionTypes() {
2475  if ( $this->isSpecialPage() ) {
2476  return array();
2477  }
2478 
2479  $types = self::getFilteredRestrictionTypes( $this->exists() );
2480 
2481  if ( $this->getNamespace() != NS_FILE ) {
2482  # Remove the upload restriction for non-file titles
2483  $types = array_diff( $types, array( 'upload' ) );
2484  }
2485 
2486  wfRunHooks( 'TitleGetRestrictionTypes', array( $this, &$types ) );
2487 
2488  wfDebug( __METHOD__ . ': applicable restrictions to [[' .
2489  $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" );
2490 
2491  return $types;
2492  }
2493 
2501  private function getTitleProtection() {
2502  // Can't protect pages in special namespaces
2503  if ( $this->getNamespace() < 0 ) {
2504  return false;
2505  }
2506 
2507  // Can't protect pages that exist.
2508  if ( $this->exists() ) {
2509  return false;
2510  }
2511 
2512  if ( !isset( $this->mTitleProtection ) ) {
2513  $dbr = wfGetDB( DB_SLAVE );
2514  $res = $dbr->select(
2515  'protected_titles',
2516  array( 'pt_user', 'pt_reason', 'pt_expiry', 'pt_create_perm' ),
2517  array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ),
2518  __METHOD__
2519  );
2520 
2521  // fetchRow returns false if there are no rows.
2522  $this->mTitleProtection = $dbr->fetchRow( $res );
2523  }
2524  return $this->mTitleProtection;
2525  }
2526 
2536  public function updateTitleProtection( $create_perm, $reason, $expiry ) {
2537  wfDeprecated( __METHOD__, '1.19' );
2538 
2539  global $wgUser;
2540 
2541  $limit = array( 'create' => $create_perm );
2542  $expiry = array( 'create' => $expiry );
2543 
2544  $page = WikiPage::factory( $this );
2545  $cascade = false;
2546  $status = $page->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $wgUser );
2547 
2548  return $status->isOK();
2549  }
2550 
2554  public function deleteTitleProtection() {
2555  $dbw = wfGetDB( DB_MASTER );
2556 
2557  $dbw->delete(
2558  'protected_titles',
2559  array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ),
2560  __METHOD__
2561  );
2562  $this->mTitleProtection = false;
2563  }
2564 
2572  public function isSemiProtected( $action = 'edit' ) {
2573  global $wgSemiprotectedRestrictionLevels;
2574 
2575  $restrictions = $this->getRestrictions( $action );
2576  $semi = $wgSemiprotectedRestrictionLevels;
2577  if ( !$restrictions || !$semi ) {
2578  // Not protected, or all protection is full protection
2579  return false;
2580  }
2581 
2582  // Remap autoconfirmed to editsemiprotected for BC
2583  foreach ( array_keys( $semi, 'autoconfirmed' ) as $key ) {
2584  $semi[$key] = 'editsemiprotected';
2585  }
2586  foreach ( array_keys( $restrictions, 'autoconfirmed' ) as $key ) {
2587  $restrictions[$key] = 'editsemiprotected';
2588  }
2589 
2590  return !array_diff( $restrictions, $semi );
2591  }
2592 
2600  public function isProtected( $action = '' ) {
2601  global $wgRestrictionLevels;
2602 
2603  $restrictionTypes = $this->getRestrictionTypes();
2604 
2605  # Special pages have inherent protection
2606  if ( $this->isSpecialPage() ) {
2607  return true;
2608  }
2609 
2610  # Check regular protection levels
2611  foreach ( $restrictionTypes as $type ) {
2612  if ( $action == $type || $action == '' ) {
2613  $r = $this->getRestrictions( $type );
2614  foreach ( $wgRestrictionLevels as $level ) {
2615  if ( in_array( $level, $r ) && $level != '' ) {
2616  return true;
2617  }
2618  }
2619  }
2620  }
2621 
2622  return false;
2623  }
2624 
2632  public function isNamespaceProtected( User $user ) {
2634 
2635  if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) {
2636  foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) {
2637  if ( $right != '' && !$user->isAllowed( $right ) ) {
2638  return true;
2639  }
2640  }
2641  }
2642  return false;
2643  }
2644 
2650  public function isCascadeProtected() {
2651  list( $sources, /* $restrictions */ ) = $this->getCascadeProtectionSources( false );
2652  return ( $sources > 0 );
2653  }
2654 
2664  public function areCascadeProtectionSourcesLoaded( $getPages = true ) {
2665  return $getPages ? isset( $this->mCascadeSources ) : isset( $this->mHasCascadingRestrictions );
2666  }
2667 
2678  public function getCascadeProtectionSources( $getPages = true ) {
2680  $pagerestrictions = array();
2681 
2682  if ( isset( $this->mCascadeSources ) && $getPages ) {
2683  return array( $this->mCascadeSources, $this->mCascadingRestrictions );
2684  } elseif ( isset( $this->mHasCascadingRestrictions ) && !$getPages ) {
2685  return array( $this->mHasCascadingRestrictions, $pagerestrictions );
2686  }
2687 
2688  wfProfileIn( __METHOD__ );
2689 
2690  $dbr = wfGetDB( DB_SLAVE );
2691 
2692  if ( $this->getNamespace() == NS_FILE ) {
2693  $tables = array( 'imagelinks', 'page_restrictions' );
2694  $where_clauses = array(
2695  'il_to' => $this->getDBkey(),
2696  'il_from=pr_page',
2697  'pr_cascade' => 1
2698  );
2699  } else {
2700  $tables = array( 'templatelinks', 'page_restrictions' );
2701  $where_clauses = array(
2702  'tl_namespace' => $this->getNamespace(),
2703  'tl_title' => $this->getDBkey(),
2704  'tl_from=pr_page',
2705  'pr_cascade' => 1
2706  );
2707  }
2708 
2709  if ( $getPages ) {
2710  $cols = array( 'pr_page', 'page_namespace', 'page_title',
2711  'pr_expiry', 'pr_type', 'pr_level' );
2712  $where_clauses[] = 'page_id=pr_page';
2713  $tables[] = 'page';
2714  } else {
2715  $cols = array( 'pr_expiry' );
2716  }
2717 
2718  $res = $dbr->select( $tables, $cols, $where_clauses, __METHOD__ );
2719 
2720  $sources = $getPages ? array() : false;
2721  $now = wfTimestampNow();
2722  $purgeExpired = false;
2723 
2724  foreach ( $res as $row ) {
2725  $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW );
2726  if ( $expiry > $now ) {
2727  if ( $getPages ) {
2728  $page_id = $row->pr_page;
2729  $page_ns = $row->page_namespace;
2730  $page_title = $row->page_title;
2731  $sources[$page_id] = Title::makeTitle( $page_ns, $page_title );
2732  # Add groups needed for each restriction type if its not already there
2733  # Make sure this restriction type still exists
2734 
2735  if ( !isset( $pagerestrictions[$row->pr_type] ) ) {
2736  $pagerestrictions[$row->pr_type] = array();
2737  }
2738 
2739  if (
2740  isset( $pagerestrictions[$row->pr_type] )
2741  && !in_array( $row->pr_level, $pagerestrictions[$row->pr_type] )
2742  ) {
2743  $pagerestrictions[$row->pr_type][] = $row->pr_level;
2744  }
2745  } else {
2746  $sources = true;
2747  }
2748  } else {
2749  // Trigger lazy purge of expired restrictions from the db
2750  $purgeExpired = true;
2751  }
2752  }
2753  if ( $purgeExpired ) {
2755  }
2756 
2757  if ( $getPages ) {
2758  $this->mCascadeSources = $sources;
2759  $this->mCascadingRestrictions = $pagerestrictions;
2760  } else {
2761  $this->mHasCascadingRestrictions = $sources;
2762  }
2763 
2764  wfProfileOut( __METHOD__ );
2765  return array( $sources, $pagerestrictions );
2766  }
2767 
2775  public function areRestrictionsLoaded() {
2777  }
2778 
2785  public function getRestrictions( $action ) {
2786  if ( !$this->mRestrictionsLoaded ) {
2787  $this->loadRestrictions();
2788  }
2789  return isset( $this->mRestrictions[$action] )
2790  ? $this->mRestrictions[$action]
2791  : array();
2792  }
2793 
2802  public function getAllRestrictions() {
2803  if ( !$this->mRestrictionsLoaded ) {
2804  $this->loadRestrictions();
2805  }
2806  return $this->mRestrictions;
2807  }
2808 
2816  public function getRestrictionExpiry( $action ) {
2817  if ( !$this->mRestrictionsLoaded ) {
2818  $this->loadRestrictions();
2819  }
2820  return isset( $this->mRestrictionsExpiry[$action] ) ? $this->mRestrictionsExpiry[$action] : false;
2821  }
2822 
2829  if ( !$this->mRestrictionsLoaded ) {
2830  $this->loadRestrictions();
2831  }
2832 
2834  }
2835 
2843  private function loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions = null ) {
2844  $rows = array();
2845 
2846  foreach ( $res as $row ) {
2847  $rows[] = $row;
2848  }
2849 
2850  $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
2851  }
2852 
2862  public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
2864  $dbr = wfGetDB( DB_SLAVE );
2865 
2866  $restrictionTypes = $this->getRestrictionTypes();
2867 
2868  foreach ( $restrictionTypes as $type ) {
2869  $this->mRestrictions[$type] = array();
2870  $this->mRestrictionsExpiry[$type] = $wgContLang->formatExpiry( '', TS_MW );
2871  }
2872 
2873  $this->mCascadeRestriction = false;
2874 
2875  # Backwards-compatibility: also load the restrictions from the page record (old format).
2876 
2877  if ( $oldFashionedRestrictions === null ) {
2878  $oldFashionedRestrictions = $dbr->selectField( 'page', 'page_restrictions',
2879  array( 'page_id' => $this->getArticleID() ), __METHOD__ );
2880  }
2881 
2882  if ( $oldFashionedRestrictions != '' ) {
2883 
2884  foreach ( explode( ':', trim( $oldFashionedRestrictions ) ) as $restrict ) {
2885  $temp = explode( '=', trim( $restrict ) );
2886  if ( count( $temp ) == 1 ) {
2887  // old old format should be treated as edit/move restriction
2888  $this->mRestrictions['edit'] = explode( ',', trim( $temp[0] ) );
2889  $this->mRestrictions['move'] = explode( ',', trim( $temp[0] ) );
2890  } else {
2891  $restriction = trim( $temp[1] );
2892  if ( $restriction != '' ) { //some old entries are empty
2893  $this->mRestrictions[$temp[0]] = explode( ',', $restriction );
2894  }
2895  }
2896  }
2897 
2898  $this->mOldRestrictions = true;
2899 
2900  }
2901 
2902  if ( count( $rows ) ) {
2903  # Current system - load second to make them override.
2904  $now = wfTimestampNow();
2905  $purgeExpired = false;
2906 
2907  # Cycle through all the restrictions.
2908  foreach ( $rows as $row ) {
2909 
2910  // Don't take care of restrictions types that aren't allowed
2911  if ( !in_array( $row->pr_type, $restrictionTypes ) ) {
2912  continue;
2913  }
2914 
2915  // This code should be refactored, now that it's being used more generally,
2916  // But I don't really see any harm in leaving it in Block for now -werdna
2917  $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW );
2918 
2919  // Only apply the restrictions if they haven't expired!
2920  if ( !$expiry || $expiry > $now ) {
2921  $this->mRestrictionsExpiry[$row->pr_type] = $expiry;
2922  $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) );
2923 
2924  $this->mCascadeRestriction |= $row->pr_cascade;
2925  } else {
2926  // Trigger a lazy purge of expired restrictions
2927  $purgeExpired = true;
2928  }
2929  }
2930 
2931  if ( $purgeExpired ) {
2933  }
2934  }
2935 
2936  $this->mRestrictionsLoaded = true;
2937  }
2938 
2945  public function loadRestrictions( $oldFashionedRestrictions = null ) {
2947  if ( !$this->mRestrictionsLoaded ) {
2948  if ( $this->exists() ) {
2949  $dbr = wfGetDB( DB_SLAVE );
2950 
2951  $res = $dbr->select(
2952  'page_restrictions',
2953  array( 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ),
2954  array( 'pr_page' => $this->getArticleID() ),
2955  __METHOD__
2956  );
2957 
2958  $this->loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions );
2959  } else {
2960  $title_protection = $this->getTitleProtection();
2961 
2962  if ( $title_protection ) {
2963  $now = wfTimestampNow();
2964  $expiry = $wgContLang->formatExpiry( $title_protection['pt_expiry'], TS_MW );
2965 
2966  if ( !$expiry || $expiry > $now ) {
2967  // Apply the restrictions
2968  $this->mRestrictionsExpiry['create'] = $expiry;
2969  $this->mRestrictions['create'] = explode( ',', trim( $title_protection['pt_create_perm'] ) );
2970  } else { // Get rid of the old restrictions
2972  $this->mTitleProtection = false;
2973  }
2974  } else {
2975  $this->mRestrictionsExpiry['create'] = $wgContLang->formatExpiry( '', TS_MW );
2976  }
2977  $this->mRestrictionsLoaded = true;
2978  }
2979  }
2980  }
2981 
2986  public function flushRestrictions() {
2987  $this->mRestrictionsLoaded = false;
2988  $this->mTitleProtection = null;
2989  }
2990 
2994  static function purgeExpiredRestrictions() {
2995  if ( wfReadOnly() ) {
2996  return;
2997  }
2998 
2999  $method = __METHOD__;
3000  $dbw = wfGetDB( DB_MASTER );
3001  $dbw->onTransactionIdle( function() use ( $dbw, $method ) {
3002  $dbw->delete(
3003  'page_restrictions',
3004  array( 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
3005  $method
3006  );
3007  $dbw->delete(
3008  'protected_titles',
3009  array( 'pt_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
3010  $method
3011  );
3012  } );
3013  }
3014 
3020  public function hasSubpages() {
3021  if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
3022  # Duh
3023  return false;
3024  }
3025 
3026  # We dynamically add a member variable for the purpose of this method
3027  # alone to cache the result. There's no point in having it hanging
3028  # around uninitialized in every Title object; therefore we only add it
3029  # if needed and don't declare it statically.
3030  if ( !isset( $this->mHasSubpages ) ) {
3031  $this->mHasSubpages = false;
3032  $subpages = $this->getSubpages( 1 );
3033  if ( $subpages instanceof TitleArray ) {
3034  $this->mHasSubpages = (bool)$subpages->count();
3035  }
3036  }
3037 
3038  return $this->mHasSubpages;
3039  }
3040 
3048  public function getSubpages( $limit = -1 ) {
3049  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
3050  return array();
3051  }
3052 
3053  $dbr = wfGetDB( DB_SLAVE );
3054  $conds['page_namespace'] = $this->getNamespace();
3055  $conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() );
3056  $options = array();
3057  if ( $limit > -1 ) {
3058  $options['LIMIT'] = $limit;
3059  }
3060  $this->mSubpages = TitleArray::newFromResult(
3061  $dbr->select( 'page',
3062  array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ),
3063  $conds,
3064  __METHOD__,
3065  $options
3066  )
3067  );
3068  return $this->mSubpages;
3069  }
3070 
3076  public function isDeleted() {
3077  if ( $this->getNamespace() < 0 ) {
3078  $n = 0;
3079  } else {
3080  $dbr = wfGetDB( DB_SLAVE );
3081 
3082  $n = $dbr->selectField( 'archive', 'COUNT(*)',
3083  array( 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ),
3084  __METHOD__
3085  );
3086  if ( $this->getNamespace() == NS_FILE ) {
3087  $n += $dbr->selectField( 'filearchive', 'COUNT(*)',
3088  array( 'fa_name' => $this->getDBkey() ),
3089  __METHOD__
3090  );
3091  }
3092  }
3093  return (int)$n;
3094  }
3095 
3101  public function isDeletedQuick() {
3102  if ( $this->getNamespace() < 0 ) {
3103  return false;
3104  }
3105  $dbr = wfGetDB( DB_SLAVE );
3106  $deleted = (bool)$dbr->selectField( 'archive', '1',
3107  array( 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ),
3108  __METHOD__
3109  );
3110  if ( !$deleted && $this->getNamespace() == NS_FILE ) {
3111  $deleted = (bool)$dbr->selectField( 'filearchive', '1',
3112  array( 'fa_name' => $this->getDBkey() ),
3113  __METHOD__
3114  );
3115  }
3116  return $deleted;
3117  }
3118 
3127  public function getArticleID( $flags = 0 ) {
3128  if ( $this->getNamespace() < 0 ) {
3129  $this->mArticleID = 0;
3130  return $this->mArticleID;
3131  }
3132  $linkCache = LinkCache::singleton();
3133  if ( $flags & self::GAID_FOR_UPDATE ) {
3134  $oldUpdate = $linkCache->forUpdate( true );
3135  $linkCache->clearLink( $this );
3136  $this->mArticleID = $linkCache->addLinkObj( $this );
3137  $linkCache->forUpdate( $oldUpdate );
3138  } else {
3139  if ( -1 == $this->mArticleID ) {
3140  $this->mArticleID = $linkCache->addLinkObj( $this );
3141  }
3142  }
3143  return $this->mArticleID;
3144  }
3145 
3153  public function isRedirect( $flags = 0 ) {
3154  if ( !is_null( $this->mRedirect ) ) {
3155  return $this->mRedirect;
3156  }
3157  # Calling getArticleID() loads the field from cache as needed
3158  if ( !$this->getArticleID( $flags ) ) {
3159  $this->mRedirect = false;
3160  return $this->mRedirect;
3161  }
3162 
3163  $linkCache = LinkCache::singleton();
3164  $cached = $linkCache->getGoodLinkFieldObj( $this, 'redirect' );
3165  if ( $cached === null ) {
3166  # Trust LinkCache's state over our own
3167  # LinkCache is telling us that the page doesn't exist, despite there being cached
3168  # data relating to an existing page in $this->mArticleID. Updaters should clear
3169  # LinkCache as appropriate, or use $flags = Title::GAID_FOR_UPDATE. If that flag is
3170  # set, then LinkCache will definitely be up to date here, since getArticleID() forces
3171  # LinkCache to refresh its data from the master.
3172  $this->mRedirect = false;
3173  return $this->mRedirect;
3174  }
3175 
3176  $this->mRedirect = (bool)$cached;
3177 
3178  return $this->mRedirect;
3179  }
3180 
3188  public function getLength( $flags = 0 ) {
3189  if ( $this->mLength != -1 ) {
3190  return $this->mLength;
3191  }
3192  # Calling getArticleID() loads the field from cache as needed
3193  if ( !$this->getArticleID( $flags ) ) {
3194  $this->mLength = 0;
3195  return $this->mLength;
3196  }
3197  $linkCache = LinkCache::singleton();
3198  $cached = $linkCache->getGoodLinkFieldObj( $this, 'length' );
3199  if ( $cached === null ) {
3200  # Trust LinkCache's state over our own, as for isRedirect()
3201  $this->mLength = 0;
3202  return $this->mLength;
3203  }
3204 
3205  $this->mLength = intval( $cached );
3206 
3207  return $this->mLength;
3208  }
3209 
3216  public function getLatestRevID( $flags = 0 ) {
3217  if ( !( $flags & Title::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
3218  return intval( $this->mLatestID );
3219  }
3220  # Calling getArticleID() loads the field from cache as needed
3221  if ( !$this->getArticleID( $flags ) ) {
3222  $this->mLatestID = 0;
3223  return $this->mLatestID;
3224  }
3225  $linkCache = LinkCache::singleton();
3226  $linkCache->addLinkObj( $this );
3227  $cached = $linkCache->getGoodLinkFieldObj( $this, 'revision' );
3228  if ( $cached === null ) {
3229  # Trust LinkCache's state over our own, as for isRedirect()
3230  $this->mLatestID = 0;
3231  return $this->mLatestID;
3232  }
3233 
3234  $this->mLatestID = intval( $cached );
3235 
3236  return $this->mLatestID;
3237  }
3238 
3249  public function resetArticleID( $newid ) {
3250  $linkCache = LinkCache::singleton();
3251  $linkCache->clearLink( $this );
3252 
3253  if ( $newid === false ) {
3254  $this->mArticleID = -1;
3255  } else {
3256  $this->mArticleID = intval( $newid );
3257  }
3258  $this->mRestrictionsLoaded = false;
3259  $this->mRestrictions = array();
3260  $this->mRedirect = null;
3261  $this->mLength = -1;
3262  $this->mLatestID = false;
3263  $this->mContentModel = false;
3264  $this->mEstimateRevisions = null;
3265  $this->mPageLanguage = false;
3266  }
3267 
3275  public static function capitalize( $text, $ns = NS_MAIN ) {
3277 
3278  if ( MWNamespace::isCapitalized( $ns ) ) {
3279  return $wgContLang->ucfirst( $text );
3280  } else {
3281  return $text;
3282  }
3283  }
3284 
3296  private function secureAndSplit() {
3297  # Initialisation
3298  $this->mInterwiki = '';
3299  $this->mFragment = '';
3300  $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
3301 
3302  $dbkey = $this->mDbkeyform;
3303 
3304  try {
3305  // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
3306  // the parsing code with Title, while avoiding massive refactoring.
3307  // @todo: get rid of secureAndSplit, refactor parsing code.
3308  $parser = $this->getTitleParser();
3309  $parts = $parser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
3310  } catch ( MalformedTitleException $ex ) {
3311  return false;
3312  }
3313 
3314  # Fill fields
3315  $this->setFragment( '#' . $parts['fragment'] );
3316  $this->mInterwiki = $parts['interwiki'];
3317  $this->mNamespace = $parts['namespace'];
3318  $this->mUserCaseDBKey = $parts['user_case_dbkey'];
3319 
3320  $this->mDbkeyform = $parts['dbkey'];
3321  $this->mUrlform = wfUrlencode( $this->mDbkeyform );
3322  $this->mTextform = str_replace( '_', ' ', $this->mDbkeyform );
3323 
3324  # We already know that some pages won't be in the database!
3325  if ( $this->isExternal() || $this->mNamespace == NS_SPECIAL ) {
3326  $this->mArticleID = 0;
3327  }
3328 
3329  return true;
3330  }
3331 
3344  public function getLinksTo( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
3345  if ( count( $options ) > 0 ) {
3346  $db = wfGetDB( DB_MASTER );
3347  } else {
3348  $db = wfGetDB( DB_SLAVE );
3349  }
3350 
3351  $res = $db->select(
3352  array( 'page', $table ),
3353  self::getSelectFields(),
3354  array(
3355  "{$prefix}_from=page_id",
3356  "{$prefix}_namespace" => $this->getNamespace(),
3357  "{$prefix}_title" => $this->getDBkey() ),
3358  __METHOD__,
3359  $options
3360  );
3361 
3362  $retVal = array();
3363  if ( $res->numRows() ) {
3364  $linkCache = LinkCache::singleton();
3365  foreach ( $res as $row ) {
3366  $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title );
3367  if ( $titleObj ) {
3368  $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
3369  $retVal[] = $titleObj;
3370  }
3371  }
3372  }
3373  return $retVal;
3374  }
3375 
3386  public function getTemplateLinksTo( $options = array() ) {
3387  return $this->getLinksTo( $options, 'templatelinks', 'tl' );
3388  }
3389 
3402  public function getLinksFrom( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
3403  global $wgContentHandlerUseDB;
3404 
3405  $id = $this->getArticleID();
3406 
3407  # If the page doesn't exist; there can't be any link from this page
3408  if ( !$id ) {
3409  return array();
3410  }
3411 
3412  if ( count( $options ) > 0 ) {
3413  $db = wfGetDB( DB_MASTER );
3414  } else {
3415  $db = wfGetDB( DB_SLAVE );
3416  }
3417 
3418  $namespaceFiled = "{$prefix}_namespace";
3419  $titleField = "{$prefix}_title";
3420 
3421  $fields = array( $namespaceFiled, $titleField, 'page_id', 'page_len', 'page_is_redirect', 'page_latest' );
3422  if ( $wgContentHandlerUseDB ) {
3423  $fields[] = 'page_content_model';
3424  }
3425 
3426  $res = $db->select(
3427  array( $table, 'page' ),
3428  $fields,
3429  array( "{$prefix}_from" => $id ),
3430  __METHOD__,
3431  $options,
3432  array( 'page' => array( 'LEFT JOIN', array( "page_namespace=$namespaceFiled", "page_title=$titleField" ) ) )
3433  );
3434 
3435  $retVal = array();
3436  if ( $res->numRows() ) {
3437  $linkCache = LinkCache::singleton();
3438  foreach ( $res as $row ) {
3439  $titleObj = Title::makeTitle( $row->$namespaceFiled, $row->$titleField );
3440  if ( $titleObj ) {
3441  if ( $row->page_id ) {
3442  $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
3443  } else {
3444  $linkCache->addBadLinkObj( $titleObj );
3445  }
3446  $retVal[] = $titleObj;
3447  }
3448  }
3449  }
3450  return $retVal;
3451  }
3452 
3463  public function getTemplateLinksFrom( $options = array() ) {
3464  return $this->getLinksFrom( $options, 'templatelinks', 'tl' );
3465  }
3466 
3473  public function getBrokenLinksFrom() {
3474  if ( $this->getArticleID() == 0 ) {
3475  # All links from article ID 0 are false positives
3476  return array();
3477  }
3478 
3479  $dbr = wfGetDB( DB_SLAVE );
3480  $res = $dbr->select(
3481  array( 'page', 'pagelinks' ),
3482  array( 'pl_namespace', 'pl_title' ),
3483  array(
3484  'pl_from' => $this->getArticleID(),
3485  'page_namespace IS NULL'
3486  ),
3487  __METHOD__, array(),
3488  array(
3489  'page' => array(
3490  'LEFT JOIN',
3491  array( 'pl_namespace=page_namespace', 'pl_title=page_title' )
3492  )
3493  )
3494  );
3495 
3496  $retVal = array();
3497  foreach ( $res as $row ) {
3498  $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
3499  }
3500  return $retVal;
3501  }
3502 
3509  public function getSquidURLs() {
3510  $urls = array(
3511  $this->getInternalURL(),
3512  $this->getInternalURL( 'action=history' )
3513  );
3514 
3515  $pageLang = $this->getPageLanguage();
3516  if ( $pageLang->hasVariants() ) {
3517  $variants = $pageLang->getVariants();
3518  foreach ( $variants as $vCode ) {
3519  $urls[] = $this->getInternalURL( '', $vCode );
3520  }
3521  }
3522 
3523  // If we are looking at a css/js user subpage, purge the action=raw.
3524  if ( $this->isJsSubpage() ) {
3525  $urls[] = $this->getInternalUrl( 'action=raw&ctype=text/javascript' );
3526  } elseif ( $this->isCssSubpage() ) {
3527  $urls[] = $this->getInternalUrl( 'action=raw&ctype=text/css' );
3528  }
3529 
3530  wfRunHooks( 'TitleSquidURLs', array( $this, &$urls ) );
3531  return $urls;
3532  }
3533 
3537  public function purgeSquid() {
3538  global $wgUseSquid;
3539  if ( $wgUseSquid ) {
3540  $urls = $this->getSquidURLs();
3541  $u = new SquidUpdate( $urls );
3542  $u->doUpdate();
3543  }
3544  }
3545 
3552  public function moveNoAuth( &$nt ) {
3553  return $this->moveTo( $nt, false );
3554  }
3555 
3566  public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) {
3567  global $wgUser, $wgContentHandlerUseDB;
3568 
3569  $errors = array();
3570  if ( !$nt ) {
3571  // Normally we'd add this to $errors, but we'll get
3572  // lots of syntax errors if $nt is not an object
3573  return array( array( 'badtitletext' ) );
3574  }
3575  if ( $this->equals( $nt ) ) {
3576  $errors[] = array( 'selfmove' );
3577  }
3578  if ( !$this->isMovable() ) {
3579  $errors[] = array( 'immobile-source-namespace', $this->getNsText() );
3580  }
3581  if ( $nt->isExternal() ) {
3582  $errors[] = array( 'immobile-target-namespace-iw' );
3583  }
3584  if ( !$nt->isMovable() ) {
3585  $errors[] = array( 'immobile-target-namespace', $nt->getNsText() );
3586  }
3587 
3588  $oldid = $this->getArticleID();
3589  $newid = $nt->getArticleID();
3590 
3591  if ( strlen( $nt->getDBkey() ) < 1 ) {
3592  $errors[] = array( 'articleexists' );
3593  }
3594  if (
3595  ( $this->getDBkey() == '' ) ||
3596  ( !$oldid ) ||
3597  ( $nt->getDBkey() == '' )
3598  ) {
3599  $errors[] = array( 'badarticleerror' );
3600  }
3601 
3602  // Content model checks
3603  if ( !$wgContentHandlerUseDB &&
3604  $this->getContentModel() !== $nt->getContentModel() ) {
3605  // can't move a page if that would change the page's content model
3606  $errors[] = array(
3607  'bad-target-model',
3609  ContentHandler::getLocalizedName( $nt->getContentModel() )
3610  );
3611  }
3612 
3613  // Image-specific checks
3614  if ( $this->getNamespace() == NS_FILE ) {
3615  $errors = array_merge( $errors, $this->validateFileMoveOperation( $nt ) );
3616  }
3617 
3618  if ( $nt->getNamespace() == NS_FILE && $this->getNamespace() != NS_FILE ) {
3619  $errors[] = array( 'nonfile-cannot-move-to-file' );
3620  }
3621 
3622  if ( $auth ) {
3623  $errors = wfMergeErrorArrays( $errors,
3624  $this->getUserPermissionsErrors( 'move', $wgUser ),
3625  $this->getUserPermissionsErrors( 'edit', $wgUser ),
3626  $nt->getUserPermissionsErrors( 'move-target', $wgUser ),
3627  $nt->getUserPermissionsErrors( 'edit', $wgUser ) );
3628  }
3629 
3630  $match = EditPage::matchSummarySpamRegex( $reason );
3631  if ( $match !== false ) {
3632  // This is kind of lame, won't display nice
3633  $errors[] = array( 'spamprotectiontext' );
3634  }
3635 
3636  $err = null;
3637  if ( !wfRunHooks( 'AbortMove', array( $this, $nt, $wgUser, &$err, $reason ) ) ) {
3638  $errors[] = array( 'hookaborted', $err );
3639  }
3640 
3641  # The move is allowed only if (1) the target doesn't exist, or
3642  # (2) the target is a redirect to the source, and has no history
3643  # (so we can undo bad moves right after they're done).
3644 
3645  if ( 0 != $newid ) { # Target exists; check for validity
3646  if ( !$this->isValidMoveTarget( $nt ) ) {
3647  $errors[] = array( 'articleexists' );
3648  }
3649  } else {
3650  $tp = $nt->getTitleProtection();
3651  $right = $tp['pt_create_perm'];
3652  if ( $right == 'sysop' ) {
3653  $right = 'editprotected'; // B/C
3654  }
3655  if ( $right == 'autoconfirmed' ) {
3656  $right = 'editsemiprotected'; // B/C
3657  }
3658  if ( $tp and !$wgUser->isAllowed( $right ) ) {
3659  $errors[] = array( 'cantmove-titleprotected' );
3660  }
3661  }
3662  if ( empty( $errors ) ) {
3663  return true;
3664  }
3665  return $errors;
3666  }
3667 
3673  protected function validateFileMoveOperation( $nt ) {
3674  global $wgUser;
3675 
3676  $errors = array();
3677 
3678  // wfFindFile( $nt ) / wfLocalFile( $nt ) is not allowed until below
3679 
3680  $file = wfLocalFile( $this );
3681  if ( $file->exists() ) {
3682  if ( $nt->getText() != wfStripIllegalFilenameChars( $nt->getText() ) ) {
3683  $errors[] = array( 'imageinvalidfilename' );
3684  }
3685  if ( !File::checkExtensionCompatibility( $file, $nt->getDBkey() ) ) {
3686  $errors[] = array( 'imagetypemismatch' );
3687  }
3688  }
3689 
3690  if ( $nt->getNamespace() != NS_FILE ) {
3691  $errors[] = array( 'imagenocrossnamespace' );
3692  // From here we want to do checks on a file object, so if we can't
3693  // create one, we must return.
3694  return $errors;
3695  }
3696 
3697  // wfFindFile( $nt ) / wfLocalFile( $nt ) is allowed below here
3698 
3699  $destFile = wfLocalFile( $nt );
3700  if ( !$wgUser->isAllowed( 'reupload-shared' ) && !$destFile->exists() && wfFindFile( $nt ) ) {
3701  $errors[] = array( 'file-exists-sharedrepo' );
3702  }
3703 
3704  return $errors;
3705  }
3706 
3718  public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true ) {
3719  global $wgUser;
3720  $err = $this->isValidMoveOperation( $nt, $auth, $reason );
3721  if ( is_array( $err ) ) {
3722  // Auto-block user's IP if the account was "hard" blocked
3723  $wgUser->spreadAnyEditBlock();
3724  return $err;
3725  }
3726  // Check suppressredirect permission
3727  if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
3728  $createRedirect = true;
3729  }
3730 
3731  wfRunHooks( 'TitleMove', array( $this, $nt, $wgUser ) );
3732 
3733  // If it is a file, move it first.
3734  // It is done before all other moving stuff is done because it's hard to revert.
3735  $dbw = wfGetDB( DB_MASTER );
3736  if ( $this->getNamespace() == NS_FILE ) {
3737  $file = wfLocalFile( $this );
3738  if ( $file->exists() ) {
3739  $status = $file->move( $nt );
3740  if ( !$status->isOk() ) {
3741  return $status->getErrorsArray();
3742  }
3743  }
3744  // Clear RepoGroup process cache
3745  RepoGroup::singleton()->clearCache( $this );
3746  RepoGroup::singleton()->clearCache( $nt ); # clear false negative cache
3747  }
3748 
3749  $dbw->begin( __METHOD__ ); # If $file was a LocalFile, its transaction would have closed our own.
3750  $pageid = $this->getArticleID( self::GAID_FOR_UPDATE );
3751  $protected = $this->isProtected();
3752 
3753  // Do the actual move
3754  $this->moveToInternal( $nt, $reason, $createRedirect );
3755 
3756  // Refresh the sortkey for this row. Be careful to avoid resetting
3757  // cl_timestamp, which may disturb time-based lists on some sites.
3758  $prefixes = $dbw->select(
3759  'categorylinks',
3760  array( 'cl_sortkey_prefix', 'cl_to' ),
3761  array( 'cl_from' => $pageid ),
3762  __METHOD__
3763  );
3764  foreach ( $prefixes as $prefixRow ) {
3765  $prefix = $prefixRow->cl_sortkey_prefix;
3766  $catTo = $prefixRow->cl_to;
3767  $dbw->update( 'categorylinks',
3768  array(
3769  'cl_sortkey' => Collation::singleton()->getSortKey(
3770  $nt->getCategorySortkey( $prefix ) ),
3771  'cl_timestamp=cl_timestamp' ),
3772  array(
3773  'cl_from' => $pageid,
3774  'cl_to' => $catTo ),
3775  __METHOD__
3776  );
3777  }
3778 
3779  $redirid = $this->getArticleID();
3780 
3781  if ( $protected ) {
3782  # Protect the redirect title as the title used to be...
3783  $dbw->insertSelect( 'page_restrictions', 'page_restrictions',
3784  array(
3785  'pr_page' => $redirid,
3786  'pr_type' => 'pr_type',
3787  'pr_level' => 'pr_level',
3788  'pr_cascade' => 'pr_cascade',
3789  'pr_user' => 'pr_user',
3790  'pr_expiry' => 'pr_expiry'
3791  ),
3792  array( 'pr_page' => $pageid ),
3793  __METHOD__,
3794  array( 'IGNORE' )
3795  );
3796  # Update the protection log
3797  $log = new LogPage( 'protect' );
3798  $comment = wfMessage(
3799  'prot_1movedto2',
3800  $this->getPrefixedText(),
3801  $nt->getPrefixedText()
3802  )->inContentLanguage()->text();
3803  if ( $reason ) {
3804  $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
3805  }
3806  // @todo FIXME: $params?
3807  $logId = $log->addEntry( 'move_prot', $nt, $comment, array( $this->getPrefixedText() ), $wgUser );
3808 
3809  // reread inserted pr_ids for log relation
3810  $insertedPrIds = $dbw->select(
3811  'page_restrictions',
3812  'pr_id',
3813  array( 'pr_page' => $redirid ),
3814  __METHOD__
3815  );
3816  $logRelationsValues = array();
3817  foreach ( $insertedPrIds as $prid ) {
3818  $logRelationsValues[] = $prid->pr_id;
3819  }
3820  $log->addRelations( 'pr_id', $logRelationsValues, $logId );
3821  }
3822 
3823  # Update watchlists
3824  $oldnamespace = MWNamespace::getSubject( $this->getNamespace() );
3825  $newnamespace = MWNamespace::getSubject( $nt->getNamespace() );
3826  $oldtitle = $this->getDBkey();
3827  $newtitle = $nt->getDBkey();
3828 
3829  if ( $oldnamespace != $newnamespace || $oldtitle != $newtitle ) {
3830  WatchedItem::duplicateEntries( $this, $nt );
3831  }
3832 
3833  $dbw->commit( __METHOD__ );
3834 
3835  wfRunHooks( 'TitleMoveComplete', array( &$this, &$nt, &$wgUser, $pageid, $redirid, $reason ) );
3836  return true;
3837  }
3838 
3849  private function moveToInternal( &$nt, $reason = '', $createRedirect = true ) {
3851 
3852  if ( $nt->exists() ) {
3853  $moveOverRedirect = true;
3854  $logType = 'move_redir';
3855  } else {
3856  $moveOverRedirect = false;
3857  $logType = 'move';
3858  }
3859 
3860  if ( $createRedirect ) {
3861  $contentHandler = ContentHandler::getForTitle( $this );
3862  $redirectContent = $contentHandler->makeRedirectContent( $nt,
3863  wfMessage( 'move-redirect-text' )->inContentLanguage()->plain() );
3864 
3865  // NOTE: If this page's content model does not support redirects, $redirectContent will be null.
3866  } else {
3867  $redirectContent = null;
3868  }
3869 
3870  $logEntry = new ManualLogEntry( 'move', $logType );
3871  $logEntry->setPerformer( $wgUser );
3872  $logEntry->setTarget( $this );
3873  $logEntry->setComment( $reason );
3874  $logEntry->setParameters( array(
3875  '4::target' => $nt->getPrefixedText(),
3876  '5::noredir' => $redirectContent ? '0': '1',
3877  ) );
3878 
3879  $formatter = LogFormatter::newFromEntry( $logEntry );
3880  $formatter->setContext( RequestContext::newExtraneousContext( $this ) );
3881  $comment = $formatter->getPlainActionText();
3882  if ( $reason ) {
3883  $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
3884  }
3885  # Truncate for whole multibyte characters.
3886  $comment = $wgContLang->truncate( $comment, 255 );
3887 
3888  $oldid = $this->getArticleID();
3889 
3890  $dbw = wfGetDB( DB_MASTER );
3891 
3892  $newpage = WikiPage::factory( $nt );
3893 
3894  if ( $moveOverRedirect ) {
3895  $newid = $nt->getArticleID();
3896  $newcontent = $newpage->getContent();
3897 
3898  # Delete the old redirect. We don't save it to history since
3899  # by definition if we've got here it's rather uninteresting.
3900  # We have to remove it so that the next step doesn't trigger
3901  # a conflict on the unique namespace+title index...
3902  $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ );
3903 
3904  $newpage->doDeleteUpdates( $newid, $newcontent );
3905  }
3906 
3907  # Save a null revision in the page's history notifying of the move
3908  $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true );
3909  if ( !is_object( $nullRevision ) ) {
3910  throw new MWException( 'No valid null revision produced in ' . __METHOD__ );
3911  }
3912 
3913  $nullRevision->insertOn( $dbw );
3914 
3915  # Change the name of the target page:
3916  $dbw->update( 'page',
3917  /* SET */ array(
3918  'page_namespace' => $nt->getNamespace(),
3919  'page_title' => $nt->getDBkey(),
3920  ),
3921  /* WHERE */ array( 'page_id' => $oldid ),
3922  __METHOD__
3923  );
3924 
3925  // clean up the old title before reset article id - bug 45348
3926  if ( !$redirectContent ) {
3927  WikiPage::onArticleDelete( $this );
3928  }
3929 
3930  $this->resetArticleID( 0 ); // 0 == non existing
3931  $nt->resetArticleID( $oldid );
3932  $newpage->loadPageData( WikiPage::READ_LOCKING ); // bug 46397
3933 
3934  $newpage->updateRevisionOn( $dbw, $nullRevision );
3935 
3936  wfRunHooks( 'NewRevisionFromEditComplete',
3937  array( $newpage, $nullRevision, $nullRevision->getParentId(), $wgUser ) );
3938 
3939  $newpage->doEditUpdates( $nullRevision, $wgUser, array( 'changed' => false ) );
3940 
3941  if ( !$moveOverRedirect ) {
3943  }
3944 
3945  # Recreate the redirect, this time in the other direction.
3946  if ( $redirectContent ) {
3947  $redirectArticle = WikiPage::factory( $this );
3948  $redirectArticle->loadFromRow( false, WikiPage::READ_LOCKING ); // bug 46397
3949  $newid = $redirectArticle->insertOn( $dbw );
3950  if ( $newid ) { // sanity
3951  $this->resetArticleID( $newid );
3952  $redirectRevision = new Revision( array(
3953  'title' => $this, // for determining the default content model
3954  'page' => $newid,
3955  'comment' => $comment,
3956  'content' => $redirectContent ) );
3957  $redirectRevision->insertOn( $dbw );
3958  $redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 );
3959 
3960  wfRunHooks( 'NewRevisionFromEditComplete',
3961  array( $redirectArticle, $redirectRevision, false, $wgUser ) );
3962 
3963  $redirectArticle->doEditUpdates( $redirectRevision, $wgUser, array( 'created' => true ) );
3964  }
3965  }
3966 
3967  # Log the move
3968  $logid = $logEntry->insert();
3969  $logEntry->publish( $logid );
3970  }
3971 
3984  public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true ) {
3985  global $wgMaximumMovedPages;
3986  // Check permissions
3987  if ( !$this->userCan( 'move-subpages' ) ) {
3988  return array( 'cant-move-subpages' );
3989  }
3990  // Do the source and target namespaces support subpages?
3991  if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
3992  return array( 'namespace-nosubpages',
3994  }
3995  if ( !MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
3996  return array( 'namespace-nosubpages',
3997  MWNamespace::getCanonicalName( $nt->getNamespace() ) );
3998  }
3999 
4000  $subpages = $this->getSubpages( $wgMaximumMovedPages + 1 );
4001  $retval = array();
4002  $count = 0;
4003  foreach ( $subpages as $oldSubpage ) {
4004  $count++;
4005  if ( $count > $wgMaximumMovedPages ) {
4006  $retval[$oldSubpage->getPrefixedText()] =
4007  array( 'movepage-max-pages',
4008  $wgMaximumMovedPages );
4009  break;
4010  }
4011 
4012  // We don't know whether this function was called before
4013  // or after moving the root page, so check both
4014  // $this and $nt
4015  if ( $oldSubpage->getArticleID() == $this->getArticleID()
4016  || $oldSubpage->getArticleID() == $nt->getArticleID()
4017  ) {
4018  // When moving a page to a subpage of itself,
4019  // don't move it twice
4020  continue;
4021  }
4022  $newPageName = preg_replace(
4023  '#^' . preg_quote( $this->getDBkey(), '#' ) . '#',
4024  StringUtils::escapeRegexReplacement( $nt->getDBkey() ), # bug 21234
4025  $oldSubpage->getDBkey() );
4026  if ( $oldSubpage->isTalkPage() ) {
4027  $newNs = $nt->getTalkPage()->getNamespace();
4028  } else {
4029  $newNs = $nt->getSubjectPage()->getNamespace();
4030  }
4031  # Bug 14385: we need makeTitleSafe because the new page names may
4032  # be longer than 255 characters.
4033  $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
4034 
4035  $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect );
4036  if ( $success === true ) {
4037  $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
4038  } else {
4039  $retval[$oldSubpage->getPrefixedText()] = $success;
4040  }
4041  }
4042  return $retval;
4043  }
4044 
4051  public function isSingleRevRedirect() {
4052  global $wgContentHandlerUseDB;
4053 
4054  $dbw = wfGetDB( DB_MASTER );
4055 
4056  # Is it a redirect?
4057  $fields = array( 'page_is_redirect', 'page_latest', 'page_id' );
4058  if ( $wgContentHandlerUseDB ) {
4059  $fields[] = 'page_content_model';
4060  }
4061 
4062  $row = $dbw->selectRow( 'page',
4063  $fields,
4064  $this->pageCond(),
4065  __METHOD__,
4066  array( 'FOR UPDATE' )
4067  );
4068  # Cache some fields we may want
4069  $this->mArticleID = $row ? intval( $row->page_id ) : 0;
4070  $this->mRedirect = $row ? (bool)$row->page_is_redirect : false;
4071  $this->mLatestID = $row ? intval( $row->page_latest ) : false;
4072  $this->mContentModel = $row && isset( $row->page_content_model ) ? strval( $row->page_content_model ) : false;
4073  if ( !$this->mRedirect ) {
4074  return false;
4075  }
4076  # Does the article have a history?
4077  $row = $dbw->selectField( array( 'page', 'revision' ),
4078  'rev_id',
4079  array( 'page_namespace' => $this->getNamespace(),
4080  'page_title' => $this->getDBkey(),
4081  'page_id=rev_page',
4082  'page_latest != rev_id'
4083  ),
4084  __METHOD__,
4085  array( 'FOR UPDATE' )
4086  );
4087  # Return true if there was no history
4088  return ( $row === false );
4089  }
4090 
4098  public function isValidMoveTarget( $nt ) {
4099  # Is it an existing file?
4100  if ( $nt->getNamespace() == NS_FILE ) {
4101  $file = wfLocalFile( $nt );
4102  if ( $file->exists() ) {
4103  wfDebug( __METHOD__ . ": file exists\n" );
4104  return false;
4105  }
4106  }
4107  # Is it a redirect with no history?
4108  if ( !$nt->isSingleRevRedirect() ) {
4109  wfDebug( __METHOD__ . ": not a one-rev redirect\n" );
4110  return false;
4111  }
4112  # Get the article text
4114  if ( !is_object( $rev ) ) {
4115  return false;
4116  }
4117  $content = $rev->getContent();
4118  # Does the redirect point to the source?
4119  # Or is it a broken self-redirect, usually caused by namespace collisions?
4120  $redirTitle = $content ? $content->getRedirectTarget() : null;
4121 
4122  if ( $redirTitle ) {
4123  if ( $redirTitle->getPrefixedDBkey() != $this->getPrefixedDBkey() &&
4124  $redirTitle->getPrefixedDBkey() != $nt->getPrefixedDBkey() ) {
4125  wfDebug( __METHOD__ . ": redirect points to other page\n" );
4126  return false;
4127  } else {
4128  return true;
4129  }
4130  } else {
4131  # Fail safe (not a redirect after all. strange.)
4132  wfDebug( __METHOD__ . ": failsafe: database sais " . $nt->getPrefixedDBkey() .
4133  " is a redirect, but it doesn't contain a valid redirect.\n" );
4134  return false;
4135  }
4136  }
4137 
4145  public function getParentCategories() {
4147 
4148  $data = array();
4149 
4150  $titleKey = $this->getArticleID();
4151 
4152  if ( $titleKey === 0 ) {
4153  return $data;
4154  }
4155 
4156  $dbr = wfGetDB( DB_SLAVE );
4157 
4158  $res = $dbr->select(
4159  'categorylinks',
4160  'cl_to',
4161  array( 'cl_from' => $titleKey ),
4162  __METHOD__
4163  );
4164 
4165  if ( $res->numRows() > 0 ) {
4166  foreach ( $res as $row ) {
4167  // $data[] = Title::newFromText($wgContLang->getNsText ( NS_CATEGORY ).':'.$row->cl_to);
4168  $data[$wgContLang->getNsText( NS_CATEGORY ) . ':' . $row->cl_to] = $this->getFullText();
4169  }
4170  }
4171  return $data;
4172  }
4173 
4180  public function getParentCategoryTree( $children = array() ) {
4181  $stack = array();
4182  $parents = $this->getParentCategories();
4183 
4184  if ( $parents ) {
4185  foreach ( $parents as $parent => $current ) {
4186  if ( array_key_exists( $parent, $children ) ) {
4187  # Circular reference
4188  $stack[$parent] = array();
4189  } else {
4190  $nt = Title::newFromText( $parent );
4191  if ( $nt ) {
4192  $stack[$parent] = $nt->getParentCategoryTree( $children + array( $parent => 1 ) );
4193  }
4194  }
4195  }
4196  }
4197 
4198  return $stack;
4199  }
4200 
4207  public function pageCond() {
4208  if ( $this->mArticleID > 0 ) {
4209  // PK avoids secondary lookups in InnoDB, shouldn't hurt other DBs
4210  return array( 'page_id' => $this->mArticleID );
4211  } else {
4212  return array( 'page_namespace' => $this->mNamespace, 'page_title' => $this->mDbkeyform );
4213  }
4214  }
4215 
4223  public function getPreviousRevisionID( $revId, $flags = 0 ) {
4225  $revId = $db->selectField( 'revision', 'rev_id',
4226  array(
4227  'rev_page' => $this->getArticleID( $flags ),
4228  'rev_id < ' . intval( $revId )
4229  ),
4230  __METHOD__,
4231  array( 'ORDER BY' => 'rev_id DESC' )
4232  );
4233 
4234  if ( $revId === false ) {
4235  return false;
4236  } else {
4237  return intval( $revId );
4238  }
4239  }
4240 
4248  public function getNextRevisionID( $revId, $flags = 0 ) {
4250  $revId = $db->selectField( 'revision', 'rev_id',
4251  array(
4252  'rev_page' => $this->getArticleID( $flags ),
4253  'rev_id > ' . intval( $revId )
4254  ),
4255  __METHOD__,
4256  array( 'ORDER BY' => 'rev_id' )
4257  );
4258 
4259  if ( $revId === false ) {
4260  return false;
4261  } else {
4262  return intval( $revId );
4263  }
4264  }
4265 
4272  public function getFirstRevision( $flags = 0 ) {
4273  $pageId = $this->getArticleID( $flags );
4274  if ( $pageId ) {
4276  $row = $db->selectRow( 'revision', Revision::selectFields(),
4277  array( 'rev_page' => $pageId ),
4278  __METHOD__,
4279  array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 )
4280  );
4281  if ( $row ) {
4282  return new Revision( $row );
4283  }
4284  }
4285  return null;
4286  }
4287 
4294  public function getEarliestRevTime( $flags = 0 ) {
4295  $rev = $this->getFirstRevision( $flags );
4296  return $rev ? $rev->getTimestamp() : null;
4297  }
4298 
4304  public function isNewPage() {
4305  $dbr = wfGetDB( DB_SLAVE );
4306  return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
4307  }
4308 
4314  public function isBigDeletion() {
4315  global $wgDeleteRevisionsLimit;
4316 
4317  if ( !$wgDeleteRevisionsLimit ) {
4318  return false;
4319  }
4320 
4321  $revCount = $this->estimateRevisionCount();
4322  return $revCount > $wgDeleteRevisionsLimit;
4323  }
4324 
4330  public function estimateRevisionCount() {
4331  if ( !$this->exists() ) {
4332  return 0;
4333  }
4334 
4335  if ( $this->mEstimateRevisions === null ) {
4336  $dbr = wfGetDB( DB_SLAVE );
4337  $this->mEstimateRevisions = $dbr->estimateRowCount( 'revision', '*',
4338  array( 'rev_page' => $this->getArticleID() ), __METHOD__ );
4339  }
4340 
4342  }
4343 
4353  public function countRevisionsBetween( $old, $new, $max = null ) {
4354  if ( !( $old instanceof Revision ) ) {
4355  $old = Revision::newFromTitle( $this, (int)$old );
4356  }
4357  if ( !( $new instanceof Revision ) ) {
4358  $new = Revision::newFromTitle( $this, (int)$new );
4359  }
4360  if ( !$old || !$new ) {
4361  return 0; // nothing to compare
4362  }
4363  $dbr = wfGetDB( DB_SLAVE );
4364  $conds = array(
4365  'rev_page' => $this->getArticleID(),
4366  'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4367  'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4368  );
4369  if ( $max !== null ) {
4370  $res = $dbr->select( 'revision', '1',
4371  $conds,
4372  __METHOD__,
4373  array( 'LIMIT' => $max + 1 ) // extra to detect truncation
4374  );
4375  return $res->numRows();
4376  } else {
4377  return (int)$dbr->selectField( 'revision', 'count(*)', $conds, __METHOD__ );
4378  }
4379  }
4380 
4397  public function getAuthorsBetween( $old, $new, $limit, $options = array() ) {
4398  if ( !( $old instanceof Revision ) ) {
4399  $old = Revision::newFromTitle( $this, (int)$old );
4400  }
4401  if ( !( $new instanceof Revision ) ) {
4402  $new = Revision::newFromTitle( $this, (int)$new );
4403  }
4404  // XXX: what if Revision objects are passed in, but they don't refer to this title?
4405  // Add $old->getPage() != $new->getPage() || $old->getPage() != $this->getArticleID()
4406  // in the sanity check below?
4407  if ( !$old || !$new ) {
4408  return null; // nothing to compare
4409  }
4410  $authors = array();
4411  $old_cmp = '>';
4412  $new_cmp = '<';
4413  $options = (array)$options;
4414  if ( in_array( 'include_old', $options ) ) {
4415  $old_cmp = '>=';
4416  }
4417  if ( in_array( 'include_new', $options ) ) {
4418  $new_cmp = '<=';
4419  }
4420  if ( in_array( 'include_both', $options ) ) {
4421  $old_cmp = '>=';
4422  $new_cmp = '<=';
4423  }
4424  // No DB query needed if $old and $new are the same or successive revisions:
4425  if ( $old->getId() === $new->getId() ) {
4426  return ( $old_cmp === '>' && $new_cmp === '<' ) ? array() : array( $old->getRawUserText() );
4427  } elseif ( $old->getId() === $new->getParentId() ) {
4428  if ( $old_cmp === '>=' && $new_cmp === '<=' ) {
4429  $authors[] = $old->getRawUserText();
4430  if ( $old->getRawUserText() != $new->getRawUserText() ) {
4431  $authors[] = $new->getRawUserText();
4432  }
4433  } elseif ( $old_cmp === '>=' ) {
4434  $authors[] = $old->getRawUserText();
4435  } elseif ( $new_cmp === '<=' ) {
4436  $authors[] = $new->getRawUserText();
4437  }
4438  return $authors;
4439  }
4440  $dbr = wfGetDB( DB_SLAVE );
4441  $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
4442  array(
4443  'rev_page' => $this->getArticleID(),
4444  "rev_timestamp $old_cmp " . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
4445  "rev_timestamp $new_cmp " . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
4446  ), __METHOD__,
4447  array( 'LIMIT' => $limit + 1 ) // add one so caller knows it was truncated
4448  );
4449  foreach ( $res as $row ) {
4450  $authors[] = $row->rev_user_text;
4451  }
4452  return $authors;
4453  }
4454 
4469  public function countAuthorsBetween( $old, $new, $limit, $options = array() ) {
4470  $authors = $this->getAuthorsBetween( $old, $new, $limit, $options );
4471  return $authors ? count( $authors ) : 0;
4472  }
4473 
4480  public function equals( Title $title ) {
4481  // Note: === is necessary for proper matching of number-like titles.
4482  return $this->getInterwiki() === $title->getInterwiki()
4483  && $this->getNamespace() == $title->getNamespace()
4484  && $this->getDBkey() === $title->getDBkey();
4485  }
4486 
4493  public function isSubpageOf( Title $title ) {
4494  return $this->getInterwiki() === $title->getInterwiki()
4495  && $this->getNamespace() == $title->getNamespace()
4496  && strpos( $this->getDBkey(), $title->getDBkey() . '/' ) === 0;
4497  }
4498 
4508  public function exists() {
4509  return $this->getArticleID() != 0;
4510  }
4511 
4528  public function isAlwaysKnown() {
4529  $isKnown = null;
4530 
4541  wfRunHooks( 'TitleIsAlwaysKnown', array( $this, &$isKnown ) );
4542 
4543  if ( !is_null( $isKnown ) ) {
4544  return $isKnown;
4545  }
4546 
4547  if ( $this->isExternal() ) {
4548  return true; // any interwiki link might be viewable, for all we know
4549  }
4550 
4551  switch ( $this->mNamespace ) {
4552  case NS_MEDIA:
4553  case NS_FILE:
4554  // file exists, possibly in a foreign repo
4555  return (bool)wfFindFile( $this );
4556  case NS_SPECIAL:
4557  // valid special page
4558  return SpecialPageFactory::exists( $this->getDBkey() );
4559  case NS_MAIN:
4560  // selflink, possibly with fragment
4561  return $this->mDbkeyform == '';
4562  case NS_MEDIAWIKI:
4563  // known system message
4564  return $this->hasSourceText() !== false;
4565  default:
4566  return false;
4567  }
4568  }
4569 
4581  public function isKnown() {
4582  return $this->isAlwaysKnown() || $this->exists();
4583  }
4584 
4590  public function hasSourceText() {
4591  if ( $this->exists() ) {
4592  return true;
4593  }
4594 
4595  if ( $this->mNamespace == NS_MEDIAWIKI ) {
4596  // If the page doesn't exist but is a known system message, default
4597  // message content will be displayed, same for language subpages-
4598  // Use always content language to avoid loading hundreds of languages
4599  // to get the link color.
4601  list( $name, ) = MessageCache::singleton()->figureMessage( $wgContLang->lcfirst( $this->getText() ) );
4602  $message = wfMessage( $name )->inLanguage( $wgContLang )->useDatabase( false );
4603  return $message->exists();
4604  }
4605 
4606  return false;
4607  }
4608 
4614  public function getDefaultMessageText() {
4616 
4617  if ( $this->getNamespace() != NS_MEDIAWIKI ) { // Just in case
4618  return false;
4619  }
4620 
4621  list( $name, $lang ) = MessageCache::singleton()->figureMessage( $wgContLang->lcfirst( $this->getText() ) );
4622  $message = wfMessage( $name )->inLanguage( $lang )->useDatabase( false );
4623 
4624  if ( $message->exists() ) {
4625  return $message->plain();
4626  } else {
4627  return false;
4628  }
4629  }
4630 
4636  public function invalidateCache() {
4637  if ( wfReadOnly() ) {
4638  return false;
4639  }
4640 
4641  $method = __METHOD__;
4642  $dbw = wfGetDB( DB_MASTER );
4643  $conds = $this->pageCond();
4644  $dbw->onTransactionIdle( function() use ( $dbw, $conds, $method ) {
4645  $dbw->update(
4646  'page',
4647  array( 'page_touched' => $dbw->timestamp() ),
4648  $conds,
4649  $method
4650  );
4651  } );
4652 
4653  return true;
4654  }
4655 
4661  public function touchLinks() {
4662  $u = new HTMLCacheUpdate( $this, 'pagelinks' );
4663  $u->doUpdate();
4664 
4665  if ( $this->getNamespace() == NS_CATEGORY ) {
4666  $u = new HTMLCacheUpdate( $this, 'categorylinks' );
4667  $u->doUpdate();
4668  }
4669  }
4670 
4677  public function getTouched( $db = null ) {
4678  $db = isset( $db ) ? $db : wfGetDB( DB_SLAVE );
4679  $touched = $db->selectField( 'page', 'page_touched', $this->pageCond(), __METHOD__ );
4680  return $touched;
4681  }
4682 
4689  public function getNotificationTimestamp( $user = null ) {
4690  global $wgUser, $wgShowUpdatedMarker;
4691  // Assume current user if none given
4692  if ( !$user ) {
4693  $user = $wgUser;
4694  }
4695  // Check cache first
4696  $uid = $user->getId();
4697  // avoid isset here, as it'll return false for null entries
4698  if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) {
4699  return $this->mNotificationTimestamp[$uid];
4700  }
4701  if ( !$uid || !$wgShowUpdatedMarker || !$user->isAllowed( 'viewmywatchlist' ) ) {
4702  $this->mNotificationTimestamp[$uid] = false;
4703  return $this->mNotificationTimestamp[$uid];
4704  }
4705  // Don't cache too much!
4706  if ( count( $this->mNotificationTimestamp ) >= self::CACHE_MAX ) {
4707  $this->mNotificationTimestamp = array();
4708  }
4709  $dbr = wfGetDB( DB_SLAVE );
4710  $this->mNotificationTimestamp[$uid] = $dbr->selectField( 'watchlist',
4711  'wl_notificationtimestamp',
4712  array(
4713  'wl_user' => $user->getId(),
4714  'wl_namespace' => $this->getNamespace(),
4715  'wl_title' => $this->getDBkey(),
4716  ),
4717  __METHOD__
4718  );
4719  return $this->mNotificationTimestamp[$uid];
4720  }
4721 
4728  public function getNamespaceKey( $prepend = 'nstab-' ) {
4730  // Gets the subject namespace if this title
4731  $namespace = MWNamespace::getSubject( $this->getNamespace() );
4732  // Checks if canonical namespace name exists for namespace
4733  if ( MWNamespace::exists( $this->getNamespace() ) ) {
4734  // Uses canonical namespace name
4735  $namespaceKey = MWNamespace::getCanonicalName( $namespace );
4736  } else {
4737  // Uses text of namespace
4738  $namespaceKey = $this->getSubjectNsText();
4739  }
4740  // Makes namespace key lowercase
4741  $namespaceKey = $wgContLang->lc( $namespaceKey );
4742  // Uses main
4743  if ( $namespaceKey == '' ) {
4744  $namespaceKey = 'main';
4745  }
4746  // Changes file to image for backwards compatibility
4747  if ( $namespaceKey == 'file' ) {
4748  $namespaceKey = 'image';
4749  }
4750  return $prepend . $namespaceKey;
4751  }
4752 
4759  public function getRedirectsHere( $ns = null ) {
4760  $redirs = array();
4761 
4762  $dbr = wfGetDB( DB_SLAVE );
4763  $where = array(
4764  'rd_namespace' => $this->getNamespace(),
4765  'rd_title' => $this->getDBkey(),
4766  'rd_from = page_id'
4767  );
4768  if ( $this->isExternal() ) {
4769  $where['rd_interwiki'] = $this->getInterwiki();
4770  } else {
4771  $where[] = 'rd_interwiki = ' . $dbr->addQuotes( '' ) . ' OR rd_interwiki IS NULL';
4772  }
4773  if ( !is_null( $ns ) ) {
4774  $where['page_namespace'] = $ns;
4775  }
4776 
4777  $res = $dbr->select(
4778  array( 'redirect', 'page' ),
4779  array( 'page_namespace', 'page_title' ),
4780  $where,
4781  __METHOD__
4782  );
4783 
4784  foreach ( $res as $row ) {
4785  $redirs[] = self::newFromRow( $row );
4786  }
4787  return $redirs;
4788  }
4789 
4795  public function isValidRedirectTarget() {
4796  global $wgInvalidRedirectTargets;
4797 
4798  // invalid redirect targets are stored in a global array, but explicitly disallow Userlogout here
4799  if ( $this->isSpecial( 'Userlogout' ) ) {
4800  return false;
4801  }
4802 
4803  foreach ( $wgInvalidRedirectTargets as $target ) {
4804  if ( $this->isSpecial( $target ) ) {
4805  return false;
4806  }
4807  }
4808 
4809  return true;
4810  }
4811 
4817  public function getBacklinkCache() {
4818  return BacklinkCache::get( $this );
4819  }
4820 
4826  public function canUseNoindex() {
4827  global $wgContentNamespaces, $wgExemptFromUserRobotsControl;
4828 
4829  $bannedNamespaces = is_null( $wgExemptFromUserRobotsControl )
4830  ? $wgContentNamespaces
4831  : $wgExemptFromUserRobotsControl;
4832 
4833  return !in_array( $this->mNamespace, $bannedNamespaces );
4834 
4835  }
4836 
4847  public function getCategorySortkey( $prefix = '' ) {
4848  $unprefixed = $this->getText();
4849 
4850  // Anything that uses this hook should only depend
4851  // on the Title object passed in, and should probably
4852  // tell the users to run updateCollations.php --force
4853  // in order to re-sort existing category relations.
4854  wfRunHooks( 'GetDefaultSortkey', array( $this, &$unprefixed ) );
4855  if ( $prefix !== '' ) {
4856  # Separate with a line feed, so the unprefixed part is only used as
4857  # a tiebreaker when two pages have the exact same prefix.
4858  # In UCA, tab is the only character that can sort above LF
4859  # so we strip both of them from the original prefix.
4860  $prefix = strtr( $prefix, "\n\t", ' ' );
4861  return "$prefix\n$unprefixed";
4862  }
4863  return $unprefixed;
4864  }
4865 
4874  public function getPageLanguage() {
4875  global $wgLang, $wgLanguageCode;
4876  wfProfileIn( __METHOD__ );
4877  if ( $this->isSpecialPage() ) {
4878  // special pages are in the user language
4879  wfProfileOut( __METHOD__ );
4880  return $wgLang;
4881  }
4882 
4883  if ( !$this->mPageLanguage || $this->mPageLanguage[1] !== $wgLanguageCode ) {
4884  // Note that this may depend on user settings, so the cache should be only per-request.
4885  // NOTE: ContentHandler::getPageLanguage() may need to load the content to determine the page language!
4886  // Checking $wgLanguageCode hasn't changed for the benefit of unit tests.
4887  $contentHandler = ContentHandler::getForTitle( $this );
4888  $langObj = wfGetLangObj( $contentHandler->getPageLanguage( $this ) );
4889  $this->mPageLanguage = array( $langObj->getCode(), $wgLanguageCode );
4890  } else {
4891  $langObj = wfGetLangObj( $this->mPageLanguage[0] );
4892  }
4893  wfProfileOut( __METHOD__ );
4894  return $langObj;
4895  }
4896 
4905  public function getPageViewLanguage() {
4906  global $wgLang;
4907 
4908  if ( $this->isSpecialPage() ) {
4909  // If the user chooses a variant, the content is actually
4910  // in a language whose code is the variant code.
4911  $variant = $wgLang->getPreferredVariant();
4912  if ( $wgLang->getCode() !== $variant ) {
4913  return Language::factory( $variant );
4914  }
4915 
4916  return $wgLang;
4917  }
4918 
4919  //NOTE: can't be cached persistently, depends on user settings
4920  //NOTE: ContentHandler::getPageViewLanguage() may need to load the content to determine the page language!
4921  $contentHandler = ContentHandler::getForTitle( $this );
4922  $pageLang = $contentHandler->getPageViewLanguage( $this );
4923  return $pageLang;
4924  }
4925 
4936  public function getEditNotices( $oldid = 0 ) {
4937  $notices = array();
4938 
4939  # Optional notices on a per-namespace and per-page basis
4940  $editnotice_ns = 'editnotice-' . $this->getNamespace();
4941  $editnotice_ns_message = wfMessage( $editnotice_ns );
4942  if ( $editnotice_ns_message->exists() ) {
4943  $notices[$editnotice_ns] = $editnotice_ns_message->parseAsBlock();
4944  }
4945  if ( MWNamespace::hasSubpages( $this->getNamespace() ) ) {
4946  $parts = explode( '/', $this->getDBkey() );
4947  $editnotice_base = $editnotice_ns;
4948  while ( count( $parts ) > 0 ) {
4949  $editnotice_base .= '-' . array_shift( $parts );
4950  $editnotice_base_msg = wfMessage( $editnotice_base );
4951  if ( $editnotice_base_msg->exists() ) {
4952  $notices[$editnotice_base] = $editnotice_base_msg->parseAsBlock();
4953  }
4954  }
4955  } else {
4956  # Even if there are no subpages in namespace, we still don't want / in MW ns.
4957  $editnoticeText = $editnotice_ns . '-' . str_replace( '/', '-', $this->getDBkey() );
4958  $editnoticeMsg = wfMessage( $editnoticeText );
4959  if ( $editnoticeMsg->exists() ) {
4960  $notices[$editnoticeText] = $editnoticeMsg->parseAsBlock();
4961  }
4962  }
4963 
4964  wfRunHooks( 'TitleGetEditNotices', array( $this, $oldid, &$notices ) );
4965  return $notices;
4966  }
4967 }
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:2501
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:4826
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:2572
$wgUser
$wgUser
Definition: Setup.php:572
Title\isNamespaceProtected
isNamespaceProtected(User $user)
Determines if $user is unable to edit this page because it has been protected by $wgNamespaceProtecti...
Definition: Title.php:2632
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:4248
Title\isBigDeletion
isBigDeletion()
Check whether the number of revisions of this page surpasses $wgDeleteRevisionsLimit.
Definition: Title.php:4314
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:2664
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:4397
Title\getFilteredRestrictionTypes
static getFilteredRestrictionTypes( $exists=true)
Get a filtered list of all restriction types supported by this wiki.
Definition: Title.php:2456
wfMergeErrorArrays
wfMergeErrorArrays()
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
Definition: GlobalFunctions.php:260
Title\getInternalURL
getInternalURL( $query='', $query2=false)
Get the URL form for an internal link.
Definition: Title.php: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:2308
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:4817
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:4330
Title\countRevisionsBetween
countRevisionsBetween( $old, $new, $max=null)
Get the number of revisions between the given revision.
Definition: Title.php:4353
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:3344
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3714
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
Title\$mNotificationTimestamp
$mNotificationTimestamp
Cascade restrictions on this page to included templates and images?
Definition: Title.php:87
Title\checkActionPermissions
checkActionPermissions( $action, $user, $errors, $doExpensiveQueries, $short)
Check action permissions not already checked in checkQuickPermissions.
Definition: Title.php: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:4689
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:4661
$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:377
Title\getPrefixedText
getPrefixedText()
Get the prefixed title with spaces.
Definition: Title.php:1369
Title\getTransWikiID
getTransWikiID()
Returns the DB name of the distant wiki which owns the object.
Definition: Title.php:801
Title\resultToError
resultToError( $errors, $result)
Add the resulting error code to the errors array.
Definition: Title.php: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:3127
$right
return false if a UserGetRights hook might remove the named right $right
Definition: hooks.txt:2809
StringUtils\escapeRegexReplacement
static escapeRegexReplacement( $string)
Escape a string to make it suitable for inclusion in a preg_replace() replacement parameter.
Definition: StringUtils.php:296
Title\$mContentModel
$mContentModel
Cascade restrictions on this page to included templates and images?
Definition: Title.php:68
Title\getSquidURLs
getSquidURLs()
Get a list of URLs to purge from the Squid cache when this page changes.
Definition: Title.php:3509
Title\secureAndSplit
secureAndSplit()
Secure and split - main initialisation function for this object.
Definition: Title.php:3296
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:3552
Title\isTalkPage
isTalkPage()
Is this a talk page of some sort?
Definition: Title.php:1237
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1360
Interwiki\fetch
static fetch( $prefix)
Fetch an Interwiki object.
Definition: Interwiki.php:77
Sanitizer\decodeCharReferencesAndNormalize
static decodeCharReferencesAndNormalize( $text)
Decode any character references, numeric or named entities, in the next and normalize the resulting s...
Definition: Sanitizer.php: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:2862
$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:4614
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:283
Title\hasSourceText
hasSourceText()
Does this page have source text?
Definition: Title.php:4590
Title\$mCascadeRestriction
$mCascadeRestriction
Cascade restrictions on this page to included templates and images?
Definition: Title.php:72
Title\loadFromRow
loadFromRow( $row)
Load Title object fields from a DB row.
Definition: Title.php:357
User\groupHasPermission
static groupHasPermission( $group, $role)
Check, if the given group has the given permission.
Definition: User.php:4166
Title\resetArticleID
resetArticleID( $newid)
This clears some fields in this object, and clears any associated keys in the "bad links" section of ...
Definition: Title.php:3249
IDBAccessObject\READ_LATEST
const READ_LATEST
Definition: IDBAccessObject.php:49
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2124
Title\setFragment
setFragment( $fragment)
Set the fragment for this title.
Definition: Title.php:1327
Title\convertByteClassToUnicodeClass
static convertByteClassToUnicodeClass( $byteClass)
Utility method for converting a character sequence from bytes to Unicode.
Definition: Title.php:572
cache
you have access to all of the normal MediaWiki so you can get a DB use the cache
Definition: maintenance.txt:52
Title\getUserPermissionsErrorsInternal
getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries=true, $short=false)
Can $user perform $action on this page? This is an internal function, which checks ONLY that previous...
Definition: Title.php:2415
Title\exists
exists()
Check if page exists.
Definition: Title.php:4508
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:4145
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:506
Revision
Definition: Revision.php:26
NS_MAIN
const NS_MAIN
Definition: Defines.php:79
Title\checkCascadingSourcesRestrictions
checkCascadingSourcesRestrictions( $action, $user, $errors, $doExpensiveQueries, $short)
Check restrictions on cascading pages.
Definition: Title.php:2162
Title\isCascadeProtected
isCascadeProtected()
Cascading protection: Return true if cascading restrictions apply to this page, false if not.
Definition: Title.php:2650
Title\isValidMoveTarget
isValidMoveTarget( $nt)
Checks if $this can be moved to a given Title.
Definition: Title.php:4098
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:3908
Title\getTitleFormatter
static getTitleFormatter()
B/C kludge: provide a TitleParser for use by Title.
Definition: Title.php:133
$out
$out
Definition: UtfNormalGenerate.php:167
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:103
Title\isMainPage
isMainPage()
Is this the mainpage?
Definition: Title.php:1127
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1174
$titles
linkcache txt The LinkCache class maintains a list of article titles and the information about whether or not the article exists in the database This is used to mark up links when displaying a page If the same link appears more than once on any page then it only has to be looked up once In most cases link lookups are done in batches with the LinkBatch class or the equivalent in so the link cache is mostly useful for short snippets of parsed and for links in the navigation areas of the skin The link cache was formerly used to track links used in a document for the purposes of updating the link tables This application is now deprecated To create a you can use the following $titles
Definition: linkcache.txt:17
Title\escapeLocalURL
escapeLocalURL( $query='', $query2=false)
Get an HTML-escaped version of the URL form, suitable for using in a link, without a server name or f...
Definition: Title.php: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:2600
Title\flushRestrictions
flushRestrictions()
Flush the protection cache in this object and force reload from the database.
Definition: Title.php:2986
Title\getCategorySortkey
getCategorySortkey( $prefix='')
Returns the raw sort key to be used for categories, with the specified prefix.
Definition: Title.php:4847
Title\getContentModel
getContentModel()
Get the page's content model id, see the CONTENT_MODEL_XXX constants.
Definition: Title.php:890
Title\getInterwiki
getInterwiki()
Get the interwiki prefix.
Definition: Title.php:778
$parser
do that in ParserLimitReportFormat instead $parser
Definition: hooks.txt:1961
WikiPage\onArticleDelete
static onArticleDelete( $title)
Clears caches when article is deleted.
Definition: WikiPage.php:3087
MWNamespace\canTalk
static canTalk( $index)
Can this namespace ever have a talk namespace?
Definition: Namespace.php:293
Title\isValidRedirectTarget
isValidRedirectTarget()
Check if this Title is a valid redirect target.
Definition: Title.php:4795
Title\deleteTitleProtection
deleteTitleProtection()
Remove any title protection due to page existing.
Definition: Title.php:2554
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:3473
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:3849
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:2383
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:4066
Title\getSubjectPage
getSubjectPage()
Get a title object associated with the subject page of this talk page.
Definition: Title.php:1256
MapCacheLRU
Handles a simple LRU key/value map with a maximum number of entries.
Definition: MapCacheLRU.php:33
wfGetLangObj
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
Definition: GlobalFunctions.php:1399
Title\getFullText
getFullText()
Get the prefixed title with spaces, plus any fragment (part beginning with '#')
Definition: Title.php:1393
MWNamespace\hasSubpages
static hasSubpages( $index)
Does the namespace allow subpages?
Definition: Namespace.php:325
Title\getTitleCache
static getTitleCache()
Definition: Title.php:262
Title\hasFragment
hasFragment()
Check if a Title fragment is set.
Definition: Title.php:1301
Title\isCssSubpage
isCssSubpage()
Is this a .css subpage of a user page?
Definition: Title.php:1217
Title\$titleCache
static $titleCache
Definition: Title.php:37
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
Title\getRedirectsHere
getRedirectsHere( $ns=null)
Get all extant redirects to this Title.
Definition: Title.php:4759
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:4480
Title\$mLength
$mLength
Cascade restrictions on this page to included templates and images?
Definition: Title.php:85
$comment
$comment
Definition: importImages.php:107
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:2561
Title\getRootText
getRootText()
Get the root page name text without a namespace, i.e.
Definition: Title.php:1413
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:93
Title\escapeFullURL
escapeFullURL( $query='', $query2=false)
Get an HTML-escaped version of the URL form, suitable for using in a link, including the server name ...
Definition: Title.php: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:2478
Revision\newFromTitle
static newFromTitle( $title, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given title.
Definition: Revision.php:106
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:980
HTMLCacheUpdate
Class to invalidate the HTML cache of all the pages linking to a given title.
Definition: HTMLCacheUpdate.php:29
Title\$mFragment
$mFragment
Cascade restrictions on this page to included templates and images?
Definition: Title.php:65
Title\getPrefixedURL
getPrefixedURL()
Get a URL-encoded title (not an actual URL) including interwiki.
Definition: Title.php:1541
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:422
Title\__toString
__toString()
Return a string representation of this title.
Definition: Title.php:1383
$title
presenting them properly to the user as errors is done by the caller $title
Definition: hooks.txt:1324
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
User\whoIs
static whoIs( $id)
Get the username corresponding to a given user ID.
Definition: User.php:484
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
Title\countAuthorsBetween
countAuthorsBetween( $old, $new, $limit, $options=array())
Get the number of authors between the given revisions or revision IDs.
Definition: Title.php:4469
Title\areRestrictionsCascading
areRestrictionsCascading()
Returns cascading restrictions for the current article.
Definition: Title.php:2828
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:3216
Title\areRestrictionsLoaded
areRestrictionsLoaded()
Accessor for mRestrictionsLoaded.
Definition: Title.php:2775
TitleValue\getFragment
getFragment()
Definition: TitleValue.php:103
Title\getSubpages
getSubpages( $limit=-1)
Get all subpages of this page.
Definition: Title.php:3048
Title\isSingleRevRedirect
isSingleRevRedirect()
Checks if this page is just a one-rev redirect.
Definition: Title.php:4051
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:4905
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:4180
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:2816
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:3566
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:4493
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:4728
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:4581
$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:3673
$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:4272
Title\getAllRestrictions
getAllRestrictions()
Accessor/initialisation for mRestrictions.
Definition: Title.php:2802
Title\getEarliestRevTime
getEarliestRevTime( $flags=0)
Get the oldest revision timestamp of this page.
Definition: Title.php:4294
User\isEveryoneAllowed
static isEveryoneAllowed( $right)
Check if all users have the given permission.
Definition: User.php:4179
DB_SLAVE
const DB_SLAVE
Definition: Defines.php:55
Title
Represents a title within MediaWiki.
Definition: Title.php:35
Title\moveTo
moveTo(&$nt, $auth=true, $reason='', $createRedirect=true)
Move a title to a new location.
Definition: Title.php:3718
$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:2678
$cache
$cache
Definition: mcc.php:32
Title\getRestrictionTypes
getRestrictionTypes()
Returns restriction types for the current Title.
Definition: Title.php:2474
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:2536
Title\loadRestrictionsFromResultWrapper
loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions=null)
Loads a string into mRestrictions array.
Definition: Title.php:2843
Title\isRedirect
isRedirect( $flags=0)
Is this an article that is a redirect page? Uses link cache, adding it if necessary.
Definition: Title.php:3153
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:3076
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:3402
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:3275
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:3537
Title\getPreviousRevisionID
getPreviousRevisionID( $revId, $flags=0)
Get the revision ID of the previous revision.
Definition: Title.php:4223
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:4874
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:4936
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:4677
wfFindFile
wfFindFile( $title, $options=array())
Find a file.
Definition: GlobalFunctions.php:3757
Title\$mUserCaseDBKey
$mUserCaseDBKey
Cascade restrictions on this page to included templates and images?
Definition: Title.php:62
MWNamespace\isCapitalized
static isCapitalized( $index)
Is the namespace first-letter capitalized?
Definition: Namespace.php:378
Title\isCssOrJsPage
isCssOrJsPage()
Could this page contain custom CSS or JavaScript for the global UI.
Definition: Title.php:1174
Title\getFragmentForURL
getFragmentForURL()
Get the fragment in URL form, including the "#" character if there is one.
Definition: Title.php:1309
NS_USER
const NS_USER
Definition: Defines.php:81
Title\$mWatched
$mWatched
Cascade restrictions on this page to included templates and images?
Definition: Title.php:84
Title\$mDbkeyform
$mDbkeyform
Cascade restrictions on this page to included templates and images?
Definition: Title.php:61
ManualLogEntry
Class for creating log entries manually, for example to inject them into the database.
Definition: LogEntry.php:339
$wgNamespaceProtection
if(!isset( $wgVersion)) if( $wgScript===false) if( $wgLoadScript===false) if( $wgArticlePath===false) if(!empty( $wgActionPaths) &&!isset( $wgActionPaths['view'])) if( $wgStylePath===false) if( $wgLocalStylePath===false) if( $wgStyleDirectory===false) if( $wgExtensionAssetsPath===false) if( $wgLogo===false) if( $wgUploadPath===false) if( $wgUploadDirectory===false) if( $wgReadOnlyFile===false) if( $wgFileCacheDirectory===false) if( $wgDeletedDirectory===false) if(isset( $wgFileStore['deleted']['directory'])) if(isset( $wgFooterIcons['copyright']) &&isset( $wgFooterIcons['copyright']['copyright']) && $wgFooterIcons['copyright']['copyright']===array()) if(isset( $wgFooterIcons['poweredby']) &&isset( $wgFooterIcons['poweredby']['mediawiki']) && $wgFooterIcons['poweredby']['mediawiki']['src']===null) $wgNamespaceProtection[NS_MEDIAWIKI]
Unconditional protection for NS_MEDIAWIKI since otherwise it's too easy for a sysadmin to set $wgName...
Definition: Setup.php:135
Title\isAlwaysKnown
isAlwaysKnown()
Should links to this title be shown as potentially viewable (i.e.
Definition: Title.php:4528
Title\isDeletedQuick
isDeletedQuick()
Is there a version of this page in the deletion archive?
Definition: Title.php:3101
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:4304
Title\moveSubpages
moveSubpages( $nt, $auth=true, $reason='', $createRedirect=true)
Move this page's subpages to be subpages of $nt.
Definition: Title.php:3984
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:3463
Title\isTrans
isTrans()
Determine whether the object refers to a page within this project and is transcludable.
Definition: Title.php:788
$error
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead where the first element is the message key and the remaining elements are used as parameters to the message based on mime etc Preferred in most cases over UploadVerification object with all info about the upload string as detected by MediaWiki Handlers will typically only apply for specific mime types object & $error
Definition: hooks.txt:2584
Title\getRestrictions
getRestrictions( $action)
Accessor/initialisation for mRestrictions.
Definition: Title.php:2785
Revision\selectFields
static selectFields()
Return the list of revision fields that should be selected to create a new revision.
Definition: Revision.php:405
MWNamespace\getTalk
static getTalk( $index)
Get the talk namespace index for a given namespace.
Definition: Namespace.php:118
Title\$mNamespace
$mNamespace
Cascade restrictions on this page to included templates and images?
Definition: Title.php:63
$query
return true to allow those checks to and false if checking is done use this to change the tables headers temp or archived zone change it to an object instance and return false override the list derivative used the name of the old file when set the default code will be skipped add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1105
Title\indexTitle
static indexTitle( $ns, $title)
Get a string representation of a title suitable for including in a search index.
Definition: Title.php:673
MWNamespace\getSubject
static getSubject( $index)
Get the subject namespace index for a given namespace Special namespaces (NS_MEDIA,...
Definition: Namespace.php:132
Title\isContentPage
isContentPage()
Is this Title in a namespace which contains content? In other words, is this a content page,...
Definition: Title.php:1096
Title\$mHasSubpage
$mHasSubpage
Cascade restrictions on this page to included templates and images?
Definition: Title.php:88
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:59
wfLocalFile
wfLocalFile( $title)
Get an object referring to a locally registered file.
Definition: GlobalFunctions.php:3768
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:297
Title\invalidateCache
invalidateCache()
Updates page_touched for this page; called from LinksUpdate.php.
Definition: Title.php:4636
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:3386
$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:3020
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:4207
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:2994
Title\$mTextform
$mTextform
Cascade restrictions on this page to included templates and images?
Definition: Title.php:59
Title\$mArticleID
$mArticleID
Cascade restrictions on this page to included templates and images?
Definition: Title.php:66
wfExpandUrl
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
Definition: GlobalFunctions.php:544
User\getGroupsWithPermission
static getGroupsWithPermission( $role)
Get all the groups who have a given permission.
Definition: User.php:4143
Title\getSubjectNsText
getSubjectNsText()
Get the namespace text of the subject (rather than talk) page.
Definition: Title.php:949
Title\userIsWatching
userIsWatching()
Is $wgUser watching this page?
Definition: Title.php: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:2945
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:36
Title\$mUrlform
$mUrlform
Cascade restrictions on this page to included templates and images?
Definition: Title.php:60
LogFormatter\newFromEntry
static newFromEntry(LogEntry $entry)
Constructs a new formatter suitable for given entry.
Definition: LogFormatter.php:45
wfArrayToCgi
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes two arrays as input, and returns a CGI-style string, e.g.
Definition: GlobalFunctions.php:414
Title\$mPrefixedText
$mPrefixedText
Text form including namespace/interwiki, initialised on demand.
Definition: Title.php:78
Title\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:3188
Title\isWatchable
isWatchable()
Can this title be added to a user's watchlist?
Definition: Title.php:988