8if (!class_exists(
'UtfNormal')) {
9 require_once(
'include/Unicode/UtfNormal.php');
15define(
'NS_MAIN',
"nsmain");
16define(
'NS_SPECIAL',
"nsspecial");
18define(
'GAID_FOR_UPDATE', 1);
20# Title::newFromTitle maintains a cache to avoid
21# expensive re-normalization of commonly used titles.
22# On a batch operation this can become a memory leak
23# if not bounded. After hitting this many titles,
25define(
'MW_TITLECACHE_MAX', 1000);
27# Constants for pr_cascade bitfield
54 public $mTextform; # Text form (spaces not underscores) of the main part
55 public $mUrlform; # URL-encoded form of the main part
57 public $mNamespace; # Namespace index, i.e. one of the NS_xxxx constants
59 public
$mFragment;
# Title fragment (i.e. the bit after the #)
60 public $mArticleID; # Article ID, fetched from the link cache on demand
68 public $mPrefixedText; # Text form including
namespace/interwiki, initialised on demand
70 # Zero except in {{transclusion}} tags
81 $this->mInterwiki = $this->mUrlform =
82 $this->mTextform = $this->mDbkeyform =
'';
83 $this->mArticleID = -1;
85 $this->mRestrictionsLoaded =
false;
86 $this->mRestrictions = array();
87 # Dont change the following, NS_MAIN is hardcoded in several place
89 $this->mDefaultNamespace =
NS_MAIN;
90 $this->mWatched =
null;
91 $this->mLatestID =
false;
92 $this->mOldRestrictions =
false;
105 $t->mDbkeyform = $key;
106 if ($t->secureAndSplit()) {
126 if (is_object($text)) {
127 throw new MWException(
'Title::newFromText given an object');
148 $t->mDbkeyform = str_replace(
' ',
'_', $filteredText);
149 $t->mDefaultNamespace = $defaultNamespace;
151 static $cachedcount = 0 ;
152 if ($t->secureAndSplit()) {
153 if ($defaultNamespace ==
NS_MAIN) {
155 # Avoid memory leaks on mass operations...
177 global $wgLegalTitleChars;
180 # For compatibility with old buggy URLs. "+" is usually not valid in titles,
181 # but some URLs used it as a space replacement and they still come
182 # from some external search tools.
183 if (strpos($wgLegalTitleChars,
'+') ===
false) {
187 $t->mDbkeyform = str_replace(
' ',
'_',
$url);
188 if ($t->secureAndSplit()) {
206 $fname =
'Title::newFromID';
207 $dbr = wfGetDB(DB_SLAVE);
208 $row = $dbr->selectRow(
210 array(
'page_namespace',
'page_title' ),
211 array(
'page_id' => $id ),
214 if ($row !==
false) {
227 $dbr = wfGetDB(DB_SLAVE);
230 array(
'page_namespace',
'page_title' ),
231 'page_id IN (' . $dbr->makeList($ids) .
')',
236 while ($row = $dbr->fetchObject(
$res)) {
258 $t->mNamespace = intval($ns);
259 $t->mDbkeyform = str_replace(
' ',
'_', $title);
260 $t->mArticleID = ($ns >= 0) ? -1 : 0;
261 $t->mUrlform = wfUrlencode($t->mDbkeyform);
262 $t->mTextform = str_replace(
'_',
' ', $title);
279 if ($t->secureAndSplit()) {
305 if ($mwRedir->matchStart($text)) {
307 if (preg_match(
'/\[{2}(.*?)(?:\||\]{2})/', $text, $m)) {
308 # categories are escaped using : for example one can enter:
309 # #REDIRECT [[:Category:Music]]. Need to remove it.
310 if (substr($m[1], 0, 1) ==
':') {
311 # We don't want to keep the ':'
312 $m[1] = substr($m[1], 1);
316 # Disallow redirects to Special:Userlogout
317 if (!is_null($rt) && $rt->isSpecial(
'Userlogout')) {
325 #----------------------------------------------------------------------------
327 #----------------------------------------------------------------------------
339 $fname =
'Title::nameOf';
340 $dbr = wfGetDB(DB_SLAVE);
342 $s = $dbr->selectRow(
'page', array(
'page_namespace',
'page_title' ), array(
'page_id' => $id ), $fname);
357 global $wgLegalTitleChars;
359 $wgLegalTitleChars =
" %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+";
361 return $wgLegalTitleChars;
377 $lc = SearchEngine::legalSearchChars() .
'&#;';
378 $t = $wgContLang->stripForSearch($title);
379 $t = preg_replace(
"/[^{$lc}]+/",
' ', $t);
380 $t = $wgContLang->lc($t);
383 $t = preg_replace(
"/([{$lc}]+)'s( |$)/",
"\\1 \\1's ", $t);
384 $t = preg_replace(
"/([{$lc}]+)s'( |$)/",
"\\1s ", $t);
386 $t = preg_replace(
"/\\s+/",
' ', $t);
388 if ($ns == NS_IMAGE) {
389 $t = preg_replace(
"/ (png|gif|jpg|jpeg|ogg)$/",
"", $t);
404 $n = $wgContLang->getNsText($ns);
405 return $n ==
'' ? $title :
"$n:$title";
417 global $wgMemc, $wgInterwikiExpiry;
418 global $wgInterwikiCache, $wgContLang;
422 $fname =
'Title::getInterwikiLink';
424 $key = $wgContLang->lc($key);
426 $k = wfMemcKey(
'interwiki', $key);
431 if ($wgInterwikiCache) {
435 $s = $wgMemc->get($k);
436 # Ignore old keys with no iw_local
437 if ($s && isset($s->iw_local) && isset($s->iw_trans)) {
442 $dbr = wfGetDB(DB_SLAVE);
445 array(
'iw_url',
'iw_local',
'iw_trans' ),
446 array(
'iw_prefix' => $key ),
453 $s = $dbr->fetchObject(
$res);
455 # Cache non-existence: create a blank object and save it to memcached
461 $wgMemc->set($k, $s, $wgInterwikiExpiry);
476 global $wgInterwikiCache, $wgInterwikiScopes, $wgInterwikiFallbackSite;
480 $db = dba_open($wgInterwikiCache,
'r',
'cdb');
483 if ($wgInterwikiScopes >= 3 and !$site) {
484 $site = dba_fetch(
'__sites:' . wfWikiID(), $db);
486 $site = $wgInterwikiFallbackSite;
489 $value = dba_fetch(wfMemcKey($key), $db);
490 if ($value ==
'' and $wgInterwikiScopes >= 3) {
492 $value = dba_fetch(
"_{$site}:{$key}", $db);
494 if ($value ==
'' and $wgInterwikiScopes >= 2) {
496 $value = dba_fetch(
"__global:{$key}", $db);
498 if ($value ==
'undef') {
506 list($local,
$url) = explode(
' ', $value, 2);
508 $s->iw_local = (int) $local;
522 if ($this->mInterwiki !=
'') {
523 # Make sure key is loaded into cache
525 $k = wfMemcKey(
'interwiki', $this->mInterwiki);
540 if ($this->mInterwiki ==
'') {
543 # Make sure key is loaded into cache
545 $k = wfMemcKey(
'interwiki', $this->mInterwiki);
554 $fragment = str_replace(
' ',
'_', $fragment);
556 $replaceArray = array(
560 return strtr($fragment, $replaceArray);
563 #----------------------------------------------------------------------------
565 #----------------------------------------------------------------------------
606 global $wgContLang, $wgCanonicalNamespaceNames;
608 if (
'' != $this->mInterwiki) {
615 if (isset($wgCanonicalNamespaceNames[$this->mNamespace])) {
619 return $wgContLang->getNsText($this->mNamespace);
669 if ($this->mFragment ==
'') {
701 $s = $this->
prefix($this->mDbkeyform);
702 $s = str_replace(
' ',
'_', $s);
713 if (empty($this->mPrefixedText)) {
714 $s = $this->
prefix($this->mTextform);
715 $s = str_replace(
'_',
' ', $s);
716 $this->mPrefixedText = $s;
730 if (
'' != $this->mFragment) {
742 global $wgNamespacesWithSubpages;
743 if (isset($wgNamespacesWithSubpages[ $this->mNamespace ]) && $wgNamespacesWithSubpages[ $this->mNamespace ]) {
744 $parts = explode(
'/', $this->
getText());
745 # Don't discard the real title if there's no subpage involved
746 if (count($parts) > 1) {
747 unset($parts[ count($parts) - 1 ]);
749 return implode(
'/', $parts);
761 global $wgNamespacesWithSubpages;
762 if (isset($wgNamespacesWithSubpages[ $this->mNamespace ]) && $wgNamespacesWithSubpages[ $this->mNamespace ]) {
763 $parts = explode(
'/', $this->mTextform);
764 return($parts[ count($parts) - 1 ]);
766 return($this->mTextform);
777 $text = wfUrlencode(str_replace(
' ',
'_', $text));
778 $text = str_replace(
'%28',
'(', str_replace(
'%29',
')', $text)); # Clean
up the URL; per below,
this might not be safe
788 $s = $this->
prefix($this->mDbkeyform);
789 $s = str_replace(
' ',
'_', $s);
791 $s = wfUrlencode($s) ;
793 # Cleaning up URL to make it look nice -- is this safe?
794 $s = str_replace(
'%28',
'(', $s);
795 $s = str_replace(
'%29',
')', $s);
811 global $wgContLang, $wgServer, $wgRequest;
813 if (
'' == $this->mInterwiki) {
818 if ($wgRequest->getVal(
'action') !=
'render') {
826 # Can this actually happen? Interwikis shouldn't be parsed.
827 # Yes! It can in interwiki transclusion. But... it probably shouldn't.
834 # Finally, add the fragment.
837 wfRunHooks(
'GetFullURL', array( &$this, &
$url,
$query ));
851 global $wgArticlePath, $wgScript, $wgServer, $wgRequest;
852 global $wgVariantArticlePath, $wgContLang, $wgUser;
855 if ($variant ==
false && $wgContLang->hasVariants() && !$wgUser->isLoggedIn()) {
856 $pref = $wgContLang->getPreferredVariant(
false);
857 if ($pref != $wgContLang->getCode()) {
874 if ($variant !=
false && $wgContLang->hasVariants()) {
875 if ($wgVariantArticlePath ==
false) {
876 $variantArticlePath =
"$wgScript?title=$1&variant=$2";
878 $variantArticlePath = $wgVariantArticlePath;
880 $url = str_replace(
'$2', urlencode($variant), $variantArticlePath);
881 $url = str_replace(
'$1', $dbkey,
$url);
883 $url = str_replace(
'$1', $dbkey, $wgArticlePath);
886 global $wgActionPaths;
889 if (!empty($wgActionPaths) &&
890 preg_match(
'/^(.*&|)action=([^&]*)(&(.*)|)$/',
$query, $matches)) {
891 $action = urldecode($matches[2]);
892 if (isset($wgActionPaths[$action])) {
894 if (isset($matches[4])) {
897 $url = str_replace(
'$1', $dbkey, $wgActionPaths[$action]);
903 if (
$url ===
false) {
907 $url =
"{$wgScript}?title={$dbkey}&{$query}";
913 if ($wgRequest->getVal(
'action') ==
'render') {
917 wfRunHooks(
'GetLocalURL', array( &$this, &
$url,
$query ));
955 global $wgInternalServer;
957 wfRunHooks(
'GetInternalURL', array( &$this, &
$url,
$query ));
968 if (
'' != $this->mInterwiki) {
992 return (
'' != $this->mInterwiki);
1005 if (count($restrictions) > 0) {
1006 foreach ($restrictions as $restriction) {
1007 if (strtolower($restriction) !=
'autoconfirmed') {
1017 # If it doesn't exist, it can't be protected
1030 global $wgRestrictionLevels;
1032 # Special pages have inherent protection
1037 # Check regular protection levels
1038 if ($action ==
'edit' || $action ==
'') {
1040 foreach ($wgRestrictionLevels as $level) {
1041 if (in_array($level, $r) && $level !=
'') {
1047 if ($action ==
'move' || $action ==
'') {
1049 foreach ($wgRestrictionLevels as $level) {
1050 if (in_array($level, $r) && $level !=
'') {
1067 if (is_null($this->mWatched)) {
1068 if (
NS_SPECIAL == $this->mNamespace || !$wgUser->isLoggedIn()) {
1069 $this->mWatched =
false;
1071 $this->mWatched = $wgUser->isWatched($this);
1091 return $this->
userCan($action,
false);
1100 public function userCan($action, $doExpensiveQueries =
true)
1102 $fname =
'Title::userCan';
1103 wfProfileIn($fname);
1105 global $wgUser, $wgNamespaceProtection;
1108 wfRunHooks(
'userCan', array( &$this, &$wgUser, $action, &
$result ));
1110 wfProfileOut($fname);
1115 wfProfileOut($fname);
1119 if (array_key_exists($this->mNamespace, $wgNamespaceProtection)) {
1121 if (!is_array($nsProt)) {
1122 $nsProt = array($nsProt);
1124 foreach ($nsProt as $right) {
1125 if (
'' != $right && !$wgUser->isAllowed($right)) {
1126 wfProfileOut($fname);
1132 if ($this->mDbkeyform ==
'_') {
1133 # FIXME: Is this necessary? Shouldn't be allowed anyway...
1134 wfProfileOut($fname);
1138 # protect css/js subpages of user pages
1139 # XXX: this might be better using restrictions
1140 # XXX: Find a way to work around the php bug that prevents using $this->userCanEditCssJsSubpage() from working
1142 && !$wgUser->isAllowed(
'editinterface')
1143 && !preg_match(
'/^' . preg_quote($wgUser->getName(),
'/') .
'\//', $this->mTextform)) {
1144 wfProfileOut($fname);
1149 # We /could/ use the protection level on the source page, but it's fairly ugly
1150 # as we have to establish a precedence hierarchy for pages included by multiple
1151 # cascade-protected pages. So just restrict it to people with 'protect' permission,
1152 # as they could remove the protection anyway.
1154 # Cascading protection depends on more than this page...
1155 # Several cascading protected pages may include this page...
1156 # Check each cascading level
1157 # This is only for protection restrictions, not for all actions
1158 if ($cascadingSources > 0 && isset($restrictions[$action])) {
1159 foreach ($restrictions[$action] as $right) {
1160 $right = ($right ==
'sysop') ?
'protect' : $right;
1161 if (
'' != $right && !$wgUser->isAllowed($right)) {
1162 wfProfileOut($fname);
1171 if ($right ==
'sysop') {
1174 if (
'' != $right && !$wgUser->isAllowed($right)) {
1175 wfProfileOut($fname);
1180 if ($action ==
'move' &&
1181 !($this->isMovable() && $wgUser->isAllowed(
'move'))) {
1182 wfProfileOut($fname);
1186 if ($action ==
'create') {
1187 if (($this->isTalkPage() && !$wgUser->isAllowed(
'createtalk')) ||
1188 (!$this->isTalkPage() && !$wgUser->isAllowed(
'createpage'))) {
1189 wfProfileOut($fname);
1194 wfProfileOut($fname);
1205 return $this->
userCan(
'edit', $doExpensiveQueries);
1215 return $this->
userCan(
'create', $doExpensiveQueries);
1225 return $this->
userCan(
'move', $doExpensiveQueries);
1249 wfRunHooks(
'userCan', array( &$this, &$wgUser,
'read', &
$result ));
1254 if ($wgUser->isAllowed(
'read')) {
1257 global $wgWhitelistRead;
1269 if ($wgWhitelistRead && in_array(
$name, $wgWhitelistRead)) {
1273 # Compatibility with old settings
1275 if (in_array(
':' .
$name, $wgWhitelistRead)) {
1297 global $wgNamespacesWithSubpages;
1299 if (isset($wgNamespacesWithSubpages[ $this->mNamespace ])) {
1300 return (strpos($this->
getText(),
'/') !==
false && $wgNamespacesWithSubpages[ $this->mNamespace ] ==
true);
1312 return (NS_USER == $this->mNamespace and preg_match(
"/\\/.*\\.(?:css|js)$/", $this->mTextform));
1321 $skinNames = Skin::getSkinNames();
1332 $subpage = explode(
'/', $this->mTextform);
1333 $subpage = $subpage[ count($subpage) - 1 ];
1334 return(str_replace(array(
'.css',
'.js' ), array(
'',
'' ), $subpage));
1342 return (NS_USER == $this->mNamespace and preg_match(
"/\\/.*\\.css$/", $this->mTextform));
1350 return (NS_USER == $this->mNamespace and preg_match(
"/\\/.*\\.js$/", $this->mTextform));
1362 return ($wgUser->isAllowed(
'editinterface') or preg_match(
'/^' . preg_quote($wgUser->getName(),
'/') .
'\//', $this->mTextform));
1373 return ($sources > 0);
1386 global $wgEnableCascadingProtection, $wgRestrictionTypes;
1388 # Define our dimension of restrictions types
1389 $pagerestrictions = array();
1390 foreach ($wgRestrictionTypes as $action) {
1391 $pagerestrictions[$action] = array();
1394 if (!$wgEnableCascadingProtection) {
1395 return array(
false, $pagerestrictions );
1398 if (isset($this->mCascadeSources) && $get_pages) {
1399 return array( $this->mCascadeSources, $this->mCascadingRestrictions );
1400 } elseif (isset($this->mHasCascadingRestrictions) && !$get_pages) {
1401 return array( $this->mHasCascadingRestrictions, $pagerestrictions );
1404 wfProfileIn(__METHOD__);
1406 $dbr = wfGetDb(DB_SLAVE);
1409 $tables = array(
'imagelinks',
'page_restrictions');
1410 $where_clauses = array(
1413 'pr_cascade' => 1 );
1415 $tables = array(
'templatelinks',
'page_restrictions');
1416 $where_clauses = array(
1420 'pr_cascade' => 1 );
1424 $cols = array(
'pr_page',
'page_namespace',
'page_title',
'pr_expiry',
'pr_type',
'pr_level' );
1425 $where_clauses[] =
'page_id=pr_page';
1428 $cols = array(
'pr_expiry' );
1431 $res = $dbr->select($tables,
$cols, $where_clauses, __METHOD__);
1433 $sources = $get_pages ? array() :
false;
1434 $now = wfTimestampNow();
1435 $purgeExpired =
false;
1437 while ($row = $dbr->fetchObject(
$res)) {
1438 $expiry = Block::decodeExpiry($row->pr_expiry);
1439 if ($expiry > $now) {
1441 $page_id = $row->pr_page;
1442 $page_ns = $row->page_namespace;
1443 $page_title = $row->page_title;
1445 # Add groups needed for each restriction type if its not already there
1446 # Make sure this restriction type still exists
1447 if (isset($pagerestrictions[$row->pr_type]) && !in_array($row->pr_level, $pagerestrictions[$row->pr_type])) {
1448 $pagerestrictions[$row->pr_type][] = $row->pr_level;
1455 $purgeExpired =
true;
1458 if ($purgeExpired) {
1462 wfProfileOut(__METHOD__);
1465 $this->mCascadeSources = $sources;
1466 $this->mCascadingRestrictions = $pagerestrictions;
1468 $this->mHasCascadingRestrictions = $sources;
1471 return array( $sources, $pagerestrictions );
1476 if (!$this->mRestrictionsLoaded) {
1489 $dbr = wfGetDb(DB_SLAVE);
1491 $this->mRestrictions[
'edit'] = array();
1492 $this->mRestrictions[
'move'] = array();
1494 # Backwards-compatibility: also load the restrictions from the page record (old format).
1496 if ($oldFashionedRestrictions ==
null) {
1497 $oldFashionedRestrictions = $dbr->selectField(
'page',
'page_restrictions', array(
'page_id' => $this->getArticleId() ), __METHOD__);
1500 if ($oldFashionedRestrictions !=
'') {
1501 foreach (explode(
':', trim($oldFashionedRestrictions)) as $restrict) {
1502 $temp = explode(
'=', trim($restrict));
1503 if (count($temp) == 1) {
1505 $this->mRestrictions[
"edit"] = explode(
',', trim($temp[0]));
1506 $this->mRestrictions[
"move"] = explode(
',', trim($temp[0]));
1508 $this->mRestrictions[$temp[0]] = explode(
',', trim($temp[1]));
1512 $this->mOldRestrictions =
true;
1513 $this->mCascadeRestriction =
false;
1514 $this->mRestrictionsExpiry = Block::decodeExpiry(
'');
1517 if ($dbr->numRows(
$res)) {
1518 # Current system - load second to make them override.
1519 $now = wfTimestampNow();
1520 $purgeExpired =
false;
1522 while ($row = $dbr->fetchObject(
$res)) {
1523 # Cycle through all the restrictions.
1527 $expiry = Block::decodeExpiry($row->pr_expiry);
1530 if (!$expiry || $expiry > $now) {
1531 $this->mRestrictionsExpiry = $expiry;
1532 $this->mRestrictions[$row->pr_type] = explode(
',', trim($row->pr_level));
1534 $this->mCascadeRestriction |= $row->pr_cascade;
1537 $purgeExpired =
true;
1541 if ($purgeExpired) {
1546 $this->mRestrictionsLoaded =
true;
1551 if (!$this->mRestrictionsLoaded) {
1552 $dbr = wfGetDB(DB_SLAVE);
1554 $res = $dbr->select(
1555 'page_restrictions',
1557 array(
'pr_page' => $this->getArticleId() ),
1570 $dbw = wfGetDB(DB_MASTER);
1572 'page_restrictions',
1573 array(
'pr_expiry < ' . $dbw->addQuotes($dbw->timestamp()) ),
1587 if (!$this->mRestrictionsLoaded) {
1590 return isset($this->mRestrictions[$action])
1591 ? $this->mRestrictions[$action]
1604 $fname =
'Title::isDeleted';
1608 $dbr = wfGetDB(DB_SLAVE);
1609 $n = $dbr->selectField(
'archive',
'COUNT(*)', array(
'ar_namespace' => $this->
getNamespace(),
1610 'ar_title' => $this->
getDBkey() ), $fname);
1612 $n += $dbr->selectField(
1615 array(
'fa_name' => $this->
getDBkey() ),
1632 $linkCache = &LinkCache::singleton();
1634 $oldUpdate = $linkCache->forUpdate(
true);
1635 $this->mArticleID = $linkCache->addLinkObj($this);
1636 $linkCache->forUpdate($oldUpdate);
1638 if (-1 == $this->mArticleID) {
1639 $this->mArticleID = $linkCache->addLinkObj($this);
1647 if ($this->mLatestID !==
false) {
1651 $db = wfGetDB(DB_SLAVE);
1652 return $this->mLatestID = $db->selectField(
1656 'Title::getLatestRevID'
1672 $linkCache = &LinkCache::singleton();
1676 $this->mArticleID = -1;
1678 $this->mArticleID = $newid;
1680 $this->mRestrictionsLoaded =
false;
1681 $this->mRestrictions = array();
1690 global $wgUseFileCache;
1696 $dbw = wfGetDB(DB_MASTER);
1700 'page_touched' => $dbw->timestamp()
1706 'Title::invalidateCache'
1709 if ($wgUseFileCache) {
1710 $cache =
new HTMLFileCache($this);
1711 @unlink($cache->fileCacheName());
1728 if (
'' != $this->mInterwiki) {
1729 $p = $this->mInterwiki .
':';
1731 if (0 != $this->mNamespace) {
1749 global $wgContLang, $wgLocalInterwiki, $wgCapitalLinks;
1752 static $rxTc =
false;
1754 # % is needed as well
1758 $this->mInterwiki = $this->mFragment =
'';
1763 # Strip Unicode bidi override characters.
1764 # Sometimes they slip into cut-n-pasted page titles, where the
1765 # override chars get included in list displays.
1766 $dbkey = str_replace(
"\xE2\x80\x8E",
'', $dbkey);
1767 $dbkey = str_replace(
"\xE2\x80\x8F",
'', $dbkey);
1769 # Clean up whitespace
1771 $dbkey = preg_replace(
'/[ _]+/',
'_', $dbkey);
1772 $dbkey = trim($dbkey,
'_');
1778 if (
false !== strpos($dbkey, UTF8_REPLACEMENT)) {
1779 # Contained illegal UTF-8 sequences or forbidden Unicode chars.
1783 $this->mDbkeyform = $dbkey;
1785 # Initial colon indicates main namespace rather than specified default
1786 # but should not create invalid {ns,title} pairs such as {0,Project:Foo}
1787 if (
':' == $dbkey[0]) {
1789 $dbkey = substr($dbkey, 1); #
remove the colon but
continue processing
1790 $dbkey = trim($dbkey,
'_'); #
remove any subsequent whitespace
1793 # Namespace or interwiki prefix
1797 if (preg_match(
"/^(.+?)_*:_*(.*)$/S", $dbkey, $m)) {
1799 if ($ns = $wgContLang->getNsIndex($p)) {
1800 # Ordinary namespace
1802 $this->mNamespace = $ns;
1805 # Can't make a local interwiki link to an interwiki link.
1806 # That's just crazy!
1812 $this->mInterwiki = $wgContLang->lc($p);
1814 # Redundant interwiki prefix to the local wiki
1815 if (0 == strcasecmp($this->mInterwiki, $wgLocalInterwiki)) {
1817 # Can't have an empty self-link
1820 $this->mInterwiki =
'';
1822 # Do another namespace split...
1826 # If there's an initial colon after the interwiki, that also
1827 # resets the default namespace
1828 if ($dbkey !==
'' && $dbkey[0] ==
':') {
1830 $dbkey = substr($dbkey, 1);
1833 # If there's no recognized interwiki or namespace,
1834 # then let the colon expression be part of the title.
1839 # We already know that some pages won't be in the database!
1841 if (
'' != $this->mInterwiki ||
NS_SPECIAL == $this->mNamespace) {
1842 $this->mArticleID = 0;
1844 $fragment = strstr($dbkey,
'#');
1845 if (
false !== $fragment) {
1847 $dbkey = substr($dbkey, 0, strlen($dbkey) - strlen($fragment));
1848 # remove whitespace again: prevents "Foo_bar_#"
1849 # becoming "Foo_bar_"
1850 $dbkey = preg_replace(
'/_*$/',
'', $dbkey);
1853 # Reject illegal characters.
1855 if (preg_match($rxTc, $dbkey)) {
1864 if (strpos($dbkey,
'.') !==
false &&
1865 ($dbkey ===
'.' || $dbkey ===
'..' ||
1866 strpos($dbkey,
'./') === 0 ||
1867 strpos($dbkey,
'../') === 0 ||
1868 strpos($dbkey,
'/./') !==
false ||
1869 strpos($dbkey,
'/../') !==
false)) {
1876 if (strpos($dbkey,
'~~~') !==
false) {
1887 if (($this->mNamespace !=
NS_SPECIAL && strlen($dbkey) > 255) ||
1888 strlen($dbkey) > 512) {
1900 if ($wgCapitalLinks && $this->mInterwiki ==
'') {
1901 $dbkey = $wgContLang->ucfirst($dbkey);
1910 $this->mInterwiki ==
'' &&
1911 $this->mNamespace !=
NS_MAIN) {
1916 if ($dbkey !==
'' &&
':' == $dbkey[0]) {
1921 $this->mDbkeyform = $dbkey;
1924 $this->mTextform = str_replace(
'_',
' ', $dbkey);
1940 $this->mFragment = str_replace(
'_',
' ', substr($fragment, 1));
1971 public function getLinksTo($options =
'', $table =
'pagelinks', $prefix =
'pl')
1973 $linkCache = &LinkCache::singleton();
1976 $db = wfGetDB(DB_MASTER);
1978 $db = wfGetDB(DB_SLAVE);
1982 array(
'page', $table ),
1983 array(
'page_namespace',
'page_title',
'page_id' ),
1985 "{$prefix}_from=page_id",
1987 "{$prefix}_title" => $this->getDbKey() ),
1988 'Title::getLinksTo',
1993 if ($db->numRows(
$res)) {
1994 while ($row = $db->fetchObject(
$res)) {
1995 if ($titleObj =
Title::makeTitle($row->page_namespace, $row->page_title)) {
1996 $linkCache->addGoodLinkObj($row->page_id, $titleObj);
1997 $retVal[] = $titleObj;
2001 $db->freeResult(
$res);
2017 return $this->
getLinksTo($options,
'templatelinks',
'tl');
2029 $db = wfGetDB(DB_MASTER);
2031 $db = wfGetDB(DB_SLAVE);
2034 $res = $db->safeQuery(
2035 "SELECT pl_namespace, pl_title
2038 ON pl_namespace=page_namespace
2039 AND pl_title=page_title
2041 AND page_namespace IS NULL
2043 $db->tableName(
'pagelinks'),
2044 $db->tableName(
'page'),
2045 $this->getArticleId(),
2050 if ($db->numRows(
$res)) {
2051 while ($row = $db->fetchObject(
$res)) {
2055 $db->freeResult(
$res);
2076 if ($wgContLang->hasVariants()) {
2077 $variants = $wgContLang->getVariants();
2078 foreach ($variants as $vCode) {
2079 if ($vCode == $wgContLang->getCode()) {
2094 $u =
new SquidUpdate($urls);
2105 return $this->
moveTo($nt,
false);
2119 if (!$this or !$nt) {
2120 return 'badtitletext';
2122 if ($this->
equals($nt)) {
2125 if (!$this->isMovable() || !$nt->isMovable()) {
2126 return 'immobile_namespace';
2130 $newid = $nt->getArticleID();
2132 if (strlen($nt->getDBkey()) < 1) {
2133 return 'articleexists';
2137 (
'' == $nt->getDBkey())) {
2138 return 'badarticleerror';
2142 !$this->
userCan(
'edit') || !$nt->userCan(
'edit') ||
2143 !$this->userCan(
'move') || !$nt->userCan(
'move')
2145 return 'protectedpage';
2148 # The move is allowed only if (1) the target doesn't exist, or
2149 # (2) the target is a redirect to the source, and has no history
2150 # (so we can undo bad moves right after they're done).
2152 if (0 != $newid) { # Target
exists; check
for validity
2154 return 'articleexists';
2170 if (is_string($err)) {
2175 if ($nt->exists()) {
2177 $pageCountChange = 0;
2178 }
else { # Target didn
't exist, do normal move.
2179 $this->moveToNewTitle($nt, $reason);
2180 $pageCountChange = 1;
2182 $redirid = $this->getArticleID();
2184 # Fixing category links (those without piped 'alternate
' names) to be sorted under the new title
2185 $dbw = wfGetDB(DB_MASTER);
2186 $categorylinks = $dbw->tableName('categorylinks
');
2187 $sql = "UPDATE $categorylinks SET cl_sortkey=" . $dbw->addQuotes($nt->getPrefixedText()) .
2188 " WHERE cl_from=" . $dbw->addQuotes($pageid) .
2189 " AND cl_sortkey=" . $dbw->addQuotes($this->getPrefixedText());
2190 $dbw->query($sql, 'SpecialMovepage::doSubmit
');
2194 $oldnamespace = $this->getNamespace() & ~1;
2195 $newnamespace = $nt->getNamespace() & ~1;
2196 $oldtitle = $this->getDBkey();
2197 $newtitle = $nt->getDBkey();
2199 if ($oldnamespace != $newnamespace || $oldtitle != $newtitle) {
2200 WatchedItem::duplicateEntries($this, $nt);
2203 # Update search engine
2204 $u = new SearchUpdate($pageid, $nt->getPrefixedDBkey());
2206 $u = new SearchUpdate($redirid, $this->getPrefixedDBkey(), '');
2210 if ($this->isContentPage() && !$nt->isContentPage()) {
2211 # No longer a content page
2212 # Not viewed, edited, removing
2213 $u = new SiteStatsUpdate(0, 1, -1, $pageCountChange);
2214 } elseif (!$this->isContentPage() && $nt->isContentPage()) {
2215 # Now a content page
2216 # Not viewed, edited, adding
2217 $u = new SiteStatsUpdate(0, 1, +1, $pageCountChange);
2218 } elseif ($pageCountChange) {
2220 $u = new SiteStatsUpdate(0, 0, 0, 1);
2230 wfRunHooks('TitleMoveComplete
', array( &$this, &$nt, &$wgUser, $pageid, $redirid ));
2241 private function moveOverExistingRedirect(&$nt, $reason = '')
2245 $comment = wfMsgForContent('1movedto2_redir
', $this->getPrefixedText(), $nt->getPrefixedText());
2248 $comment .= ": $reason";
2251 $now = wfTimestampNow();
2252 $newid = $nt->getArticleID();
2253 $oldid = $this->getArticleID();
2254 $dbw = wfGetDB(DB_MASTER);
2255 $linkCache = &LinkCache::singleton();
2257 # Delete the old redirect. We don't save it to history since
2258 # by definition if we've got here it's rather uninteresting.
2259 # We have to remove it so that the next step doesn't trigger
2260 # a conflict on the unique namespace+title index...
2261 $dbw->delete(
'page', array(
'page_id' => $newid ), $fname);
2263 # Save a null revision in the page's history notifying of the move
2264 $nullRevision = Revision::newNullRevision($dbw, $oldid,
$comment,
true);
2265 $nullRevId = $nullRevision->insertOn($dbw);
2267 # Change the name of the target page:
2272 'page_touched' => $dbw->timestamp($now),
2273 'page_namespace' => $nt->getNamespace(),
2274 'page_title' => $nt->getDBkey(),
2275 'page_latest' => $nullRevId,
2278 array(
'page_id' => $oldid ),
2281 $linkCache->clearLink($nt->getPrefixedDBkey());
2283 # Recreate the redirect, this time in the other direction.
2285 $redirectText = $mwRedir->getSynonym(0) .
' [[' . $nt->getPrefixedText() .
"]]\n";
2286 $redirectArticle =
new Article($this);
2287 $newid = $redirectArticle->insertOn($dbw);
2288 $redirectRevision =
new Revision(array(
2291 'text' => $redirectText ));
2292 $redirectRevision->insertOn($dbw);
2293 $redirectArticle->updateRevisionOn($dbw, $redirectRevision, 0);
2297 $log =
new LogPage(
'move');
2298 $log->addEntry(
'move_redir', $this, $reason, array( 1 => $nt->getPrefixedText() ));
2300 # Now, we record the link from the redirect to the new title.
2301 # It should have no other outgoing links...
2302 $dbw->delete(
'pagelinks', array(
'pl_from' => $newid ), $fname);
2306 'pl_from' => $newid,
2307 'pl_namespace' => $nt->getNamespace(),
2308 'pl_title' => $nt->getDbKey() ),
2314 $urls = array_merge($nt->getSquidURLs(), $this->getSquidURLs());
2315 $u =
new SquidUpdate($urls);
2327 $fname =
'MovePageForm::moveToNewTitle';
2333 $newid = $nt->getArticleID();
2335 $dbw = wfGetDB(DB_MASTER);
2336 $now = $dbw->timestamp();
2337 $linkCache = &LinkCache::singleton();
2339 # Save a null revision in the page's history notifying of the move
2340 $nullRevision = Revision::newNullRevision($dbw, $oldid,
$comment,
true);
2341 $nullRevId = $nullRevision->insertOn($dbw);
2348 'page_touched' => $now,
2349 'page_namespace' => $nt->getNamespace(),
2350 'page_title' => $nt->getDBkey(),
2351 'page_latest' => $nullRevId,
2354 array(
'page_id' => $oldid ),
2358 $linkCache->clearLink($nt->getPrefixedDBkey());
2362 $redirectText = $mwRedir->getSynonym(0) .
' [[' . $nt->getPrefixedText() .
"]]\n";
2363 $redirectArticle =
new Article($this);
2364 $newid = $redirectArticle->insertOn($dbw);
2365 $redirectRevision =
new Revision(array(
2368 'text' => $redirectText ));
2369 $redirectRevision->insertOn($dbw);
2370 $redirectArticle->updateRevisionOn($dbw, $redirectRevision, 0);
2374 $log =
new LogPage(
'move');
2375 $log->addEntry(
'move', $this, $reason, array( 1 => $nt->getPrefixedText()));
2377 # Purge caches as per article creation
2378 Article::onArticleCreate($nt);
2380 # Record the just-created redirect's linking to the page
2384 'pl_from' => $newid,
2385 'pl_namespace' => $nt->getNamespace(),
2386 'pl_title' => $nt->getDBkey() ),
2390 # Purge old title from squid
2391 # The new title, and links to the new title, are purged in Article::onArticleCreate()
2403 $fname =
'Title::isValidMoveTarget';
2404 $dbw = wfGetDB(DB_MASTER);
2407 $id = $nt->getArticleID();
2408 $obj = $dbw->selectRow(
2409 array(
'page',
'revision',
'text'),
2410 array(
'page_is_redirect',
'old_text',
'old_flags' ),
2411 array(
'page_id' => $id,
'page_latest=rev_id',
'rev_text_id=old_id' ),
2416 if (!$obj || 0 == $obj->page_is_redirect) {
2418 wfDebug(__METHOD__ .
": not a redirect\n");
2421 $text = Revision::getRevisionText($obj);
2423 # Does the redirect point to the source?
2424 # Or is it a broken self-redirect, usually caused by namespace collisions?
2426 if (preg_match(
"/\\[\\[\\s*([^\\]\\|]*)]]/", $text, $m)) {
2428 if (!is_object($redirTitle) ||
2429 ($redirTitle->getPrefixedDBkey() != $this->getPrefixedDBkey() &&
2430 $redirTitle->getPrefixedDBkey() != $nt->getPrefixedDBkey())) {
2431 wfDebug(__METHOD__ .
": redirect points to other page\n");
2436 wfDebug(__METHOD__ .
": failsafe\n");
2440 # Does the article have a history?
2441 $row = $dbw->selectRow(
2442 array(
'page',
'revision'),
2444 array(
'page_namespace' => $nt->getNamespace(),
2445 'page_title' => $nt->getDBkey(),
2446 'page_id=rev_page AND page_latest != rev_id'
2452 # Return true if there was no history
2453 return $row ===
false;
2467 $titlekey = $this->getArticleId();
2468 $dbr = wfGetDB(DB_SLAVE);
2469 $categorylinks = $dbr->tableName(
'categorylinks');
2472 $sql =
"SELECT * FROM $categorylinks"
2473 .
" WHERE cl_from='$titlekey'"
2474 .
" AND cl_from <> '0'"
2475 .
" ORDER BY cl_sortkey";
2477 $res = $dbr->query($sql) ;
2479 if ($dbr->numRows(
$res) > 0) {
2480 while ($x = $dbr->fetchObject(
$res)) {
2482 $data[$wgContLang->getNSText(NS_CATEGORY) .
':' . $x->cl_to] = $this->
getFullText();
2484 $dbr->freeResult(
$res) ;
2500 if ($parents !=
'') {
2501 foreach ($parents as $parent => $current) {
2502 if (array_key_exists($parent, $children)) {
2503 # Circular reference
2504 $stack[$parent] = array();
2508 $stack[$parent] = $nt->getParentCategoryTree($children + array($parent => 1));
2527 return array(
'page_namespace' => $this->mNamespace,
'page_title' => $this->mDbkeyform );
2538 $dbr = wfGetDB(DB_SLAVE);
2539 return $dbr->selectField(
2542 'rev_page=' . intval($this->getArticleId()) .
2543 ' AND rev_id<' . intval($revision) .
' ORDER BY rev_id DESC'
2555 $dbr = wfGetDB(DB_SLAVE);
2556 return $dbr->selectField(
2559 'rev_page=' . intval($this->getArticleId()) .
2560 ' AND rev_id>' . intval($revision) .
' ORDER BY rev_id'
2573 $dbr = wfGetDB(DB_SLAVE);
2574 return $dbr->selectField(
2577 'rev_page = ' . intval($this->getArticleId()) .
2578 ' AND rev_id > ' . intval($old) .
2579 ' AND rev_id < ' . intval($new)
2592 return $this->
getInterwiki() === $title->getInterwiki()
2594 && $this->getDbkey() === $title->getDbkey();
2603 return $this->getArticleId() != 0;
2625 $u =
new HTMLCacheUpdate($this,
'pagelinks');
2629 $u =
new HTMLCacheUpdate($this,
'categorylinks');
2639 $dbr = wfGetDB(DB_SLAVE);
2640 $touched = $dbr->selectField(
2654 global $wgTitle, $wgScriptPath, $wgServer;
2656 return "$wgServer$wgScriptPath/trackback.php?article="
2657 . htmlspecialchars(urlencode($wgTitle->getPrefixedDBkey()));
2663 $title = htmlspecialchars($this->
getText());
2667<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"
2668 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
2669 xmlns:trackback=\"http://madskills.com/public/xml/rss/module/trackback/\">
2672 dc:identifier=\"$url\"
2674 trackback:ping=\"$tburl\" />
2688 return 'nstab-main';
2691 return 'nstab-user';
2693 return 'nstab-media';
2695 return 'nstab-special';
2697 case NS_PROJECT_TALK:
2698 return 'nstab-project';
2701 return 'nstab-image';
2703 case NS_MEDIAWIKI_TALK:
2704 return 'nstab-mediawiki';
2706 case NS_TEMPLATE_TALK:
2707 return 'nstab-template';
2710 return 'nstab-help';
2712 case NS_CATEGORY_TALK:
2713 return 'nstab-category';
2715 return 'nstab-' . $wgContLang->lc($this->getSubjectNsText());
2726 list($thisName, ) = SpecialPage::resolveAliasWithSubpage($this->
getDBkey());
2727 if (
$name == $thisName) {
2741 $canonicalName = SpecialPage::resolveAlias($this->mDbkeyform);
2742 if ($canonicalName) {
2743 $localName = SpecialPage::getLocalNameFor($canonicalName);
2744 if ($localName != $this->mDbkeyform) {
const NS_MAIN(!class_exists('UtfNormal'))
See title.txt.
An exception for terminatinating execution or to throw for unit testing.
static decodeCharReferences($text)
Decode any character references, numeric or named entities, in the text and return a UTF-8 string.
getInternalURL($query='', $variant=false)
Get the URL form for an internal link.
static newFromRedirect($text)
Create a new Title for a redirect.
escapeFullURL($query='')
Get an HTML-escaped version of the URL form, suitable for using in a link, including the server name ...
getTouched()
Get the last touched timestamp.
getNamespace()
Get the namespace index, i.e.
static getInterwikiCached($key)
Fetch interwiki prefix data from local cache in constant database.
userCanRead()
Would anybody with sufficient privileges be able to move this page? Some pages just aren't movable.
static $titleCache
Static cache variables.
invalidateCache()
Updates page_touched for this page; called from LinksUpdate.php.
isValidMoveOperation(&$nt, $auth=true)
Check whether a given move operation would be valid.
$mHasCascadingRestrictions
moveTo(&$nt, $auth=true, $reason='')
Move a title to a new location.
$mTextform
All member variables should be considered private Please use the accessor functions.
nameOf($id)
Get the prefixed DB key associated with an ID.
getNextRevisionID($revision)
Get the revision ID of the next revision.
secureAndSplit()
Secure and split - main initialisation function for this object.
getSkinFromCssJsSubpage()
Trim down a .css or .js subpage title to get the corresponding skin name.
getDefaultNamespace()
Get the default namespace index, for when there is no namespace.
resetArticleID($newid)
This clears some fields in this object, and clears any associated keys in the "bad links" section of ...
getFragment()
Get the Title fragment (i.e.
isCascadeProtected()
Cascading protection: Return true if cascading restrictions apply to this page, false if not.
isProtected($action='')
Does the title correspond to a protected article?
getIndexTitle()
Get title for search index.
getPrefixedURL()
Get a URL-encoded title (not an actual URL) including interwiki.
getLinksTo($options='', $table='pagelinks', $prefix='pl')
Get a Title object associated with the talk page of this article.
prefix($name)
Prefix some arbitrary text with the namespace or interwiki prefix of this object.
static newFromText($text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
areRestrictionsCascading()
getEscapedText()
Get the HTML-escaped displayable text form.
getFullText()
Get the prefixed title with spaces, plus any fragment (part beginning with '#')
getFullURL($query='', $variant=false)
Get a real URL referring to this title, with interwiki link and fragment.
isLocal()
Determine whether the object refers to a page within this project.
moveNoAuth(&$nt)
Move this page without authentication.
$mCascadeRestrictionSources
static makeTitleSafe($ns, $title)
Create a new Title from a namespace index and a DB key.
quickUserCan($action)
Can $wgUser perform $action on this page? This skips potentially expensive cascading permission check...
fixSpecialName()
If the Title refers to a special page alias which is not the local default, returns a new Title which...
static legalChars()
Get a regex character class describing the legal characters in a link.
isCssJsSubpage()
Is this a .css or .js subpage of a user page?
getPrefixedDBkey()
Get the prefixed database key form.
getNsText()
Get the namespace text.
static purgeExpiredRestrictions()
Purge expired restrictions from the page_restrictions table.
equals($title)
Compare with another title.
userCanCreate($doExpensiveQueries=true)
Can $wgUser create this page?
isTrans()
Determine whether the object refers to a page within this project and is transcludable.
static makeName($ns, $title)
touchLinks()
Update page_touched timestamps and send squid purge messages for pages linking to this title.
userIsWatching()
Is $wgUser is watching this page?
isExternal()
Is this Title interwiki?
getFragmentForURL()
Get the fragment in URL form, including the "#" character if there is one.
static newFromID($id)
Create a new Title from an article ID.
getArticleID($flags=0)
Get the article ID for this Title from the link cache, adding it if necessary.
static & makeTitle($ns, $title)
Create a new Title from a namespace index and a DB key.
getSubpageUrlForm()
Get a URL-encoded form of the subpage text.
getDBkey()
Get the main part with underscores.
countRevisionsBetween($old, $new)
Get the number of revisions between the given revision IDs.
userCanEditCssJsSubpage()
Protect css/js subpages of user pages: can $wgUser edit this page?
isValidCssJsSubpage()
Is this a valid .css or .js subpage of a user page? Check that the corresponding skin exists.
userCanEdit($doExpensiveQueries=true)
Can $wgUser edit this page?
static indexTitle($ns, $title)
Get a string representation of a title suitable for including in a search index.
getBrokenLinksFrom($options='')
Get an array of Title objects referring to non-existent articles linked from this page.
getCascadeProtectionSources($get_pages=true)
Cascading protection: Get the source of any cascading restrictions on this page.
getPartialURL()
Get the URL-encoded form of the main part.
getBaseText()
Get the base name, i.e.
getText()
Simple accessors.
pageCond()
Get an associative array for selecting this title from the "page" table.
isSemiProtected($action='edit')
Is this page "semi-protected" - the only protection is autoconfirm?
isJsSubpage()
Is this a .js subpage of a user page?
userCanMove($doExpensiveQueries=true)
Can $wgUser move this page?
isSpecial($name)
Returns true if this title resolves to the named special page.
static newMainPage()
Create a new Title for the Main Page.
isCssSubpage()
Is this a .css subpage of a user page?
exists()
Check if page exists.
getSquidURLs()
Get a list of URLs to purge from the Squid cache when this page changes.
getLocalURL($query='', $variant=false)
Get a URL with no fragment or server name.
static newFromURL($url)
Create a new Title from URL-encoded text.
isDeleted()
Is there a version of this page in the deletion archive?
moveOverExistingRedirect(&$nt, $reason='')
Move page to a title which is at present a redirect to the source page.
static newFromDBkey($key)
Create a new Title from a prefixed DB key.
setFragment($fragment)
Set the fragment for this title This is kind of bad, since except for this rarely-used function,...
loadRestrictions($oldFashionedRestrictions=null)
getParentCategoryTree($children=array())
Get a tree of parent categories.
getEditURL()
Get the edit URL for this Title.
getParentCategories()
Get categories to which this Title belongs and return an array of categories' names.
getNamespaceKey()
Generate strings used for xml 'id' names in monobook tabs.
static newFromIDs($ids)
Make an array of titles from an array of IDs.
getInterwikiLink($key)
Returns the URL associated with an interwiki prefix.
isSubpage()
Is this a talk page of some sort?
moveToNewTitle(&$nt, $reason='')
Move page to non-existing title.
getInterwiki()
Get the namespace text of the subject (rather than talk) page.
isValidMoveTarget($nt)
Checks if $this can be moved to a given Title.
userCan($action, $doExpensiveQueries=true)
Can $wgUser perform $action on this page?
getTemplateLinksTo($options='')
Get an array of Title objects using this Title as a template Also stores the IDs in the link cache.
getSubpageText()
Get the lowest-level subpage name, i.e.
getPreviousRevisionID($revision)
Get the revision ID of the previous revision.
getPrefixedText()
Get the prefixed title with spaces.
getRestrictions($action)
Accessor/initialisation for mRestrictions.
loadRestrictionsFromRow($res, $oldFashionedRestrictions=null)
Loads a string into mRestrictions array.
isAlwaysKnown()
Should a link should be displayed as a known link, just based on its title?
static escapeFragmentForURL($fragment)
Escape a text fragment, say from a link, for a URL.
escapeLocalURL($query='')
Get an HTML-escaped version of the URL form, suitable for using in a link, without a server name or f...
static wfUrlencode($s)
From GlobalFunctions.php.
if($err=$client->getError()) $namespace
foreach($_POST as $key=> $value) $res