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