8 if (!class_exists(
'UtfNormal')) {
9 require_once(
'include/Unicode/UtfNormal.php');
15 define(
'NS_MAIN',
"nsmain");
16 define(
'NS_SPECIAL',
"nsspecial");
18 define(
'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, 25 define(
'MW_TITLECACHE_MAX', 1000);
27 # Constants for pr_cascade bitfield 59 public
$mFragment;
# Title fragment (i.e. the bit after the #) 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;
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';
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 1040 foreach ($wgRestrictionLevels as $level) {
1041 if (in_array($level,
$r) && $level !=
'') {
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);
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);
1181 !($this->isMovable() && $wgUser->isAllowed(
'move'))) {
1182 wfProfileOut($fname);
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,
'_');
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));
1973 $linkCache = &LinkCache::singleton();
1976 $db = wfGetDB(DB_MASTER);
1978 $db = wfGetDB(DB_SLAVE);
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)) {
1996 $linkCache->addGoodLinkObj(
$row->page_id, $titleObj);
1997 $retVal[] = $titleObj;
2001 $db->freeResult(
$res);
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() ),
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) ||
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)) {
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)
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()));
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) {
isAlwaysKnown()
Should a link should be displayed as a known link, just based on its title?
static getInterwikiCached($key)
Fetch interwiki prefix data from local cache in constant database.
static purgeExpiredRestrictions()
Purge expired restrictions from the page_restrictions table.
if($err=$client->getError()) $namespace
setFragment($fragment)
Set the fragment for this title This is kind of bad, since except for this rarely-used function...
loadRestrictionsFromRow($res, $oldFashionedRestrictions=null)
Loads a string into mRestrictions array.
touchLinks()
Update page_touched timestamps and send squid purge messages for pages linking to this title...
userCanEditCssJsSubpage()
Protect css/js subpages of user pages: can $wgUser edit this page?
getFragment()
Get the Title fragment (i.e.
countRevisionsBetween($old, $new)
Get the number of revisions between the given revision IDs.
getLinksTo($options='', $table='pagelinks', $prefix='pl')
Get a Title object associated with the talk page of this article.
isJsSubpage()
Is this a .js subpage of a user page?
getSquidURLs()
Get a list of URLs to purge from the Squid cache when this page changes.
invalidateCache()
Updates page_touched for this page; called from LinksUpdate.php.
isSpecial($name)
Returns true if this title resolves to the named special page.
getArticleID($flags=0)
Get the article ID for this Title from the link cache, adding it if necessary.
getText()
Simple accessors.
getSubpageText()
Get the lowest-level subpage name, i.e.
getBaseText()
Get the base name, i.e.
nameOf($id)
Get the prefixed DB key associated with an ID.
static newMainPage()
Create a new Title for the Main Page.
isCssJsSubpage()
Is this a .css or .js subpage of a user page?
secureAndSplit()
Secure and split - main initialisation function for this object.
moveNoAuth(&$nt)
Move this page without authentication.
getInterwikiLink($key)
Returns the URL associated with an interwiki prefix.
prefix($name)
Prefix some arbitrary text with the namespace or interwiki prefix of this object. ...
isProtected($action='')
Does the title correspond to a protected article?
static escapeFragmentForURL($fragment)
Escape a text fragment, say from a link, for a URL.
isValidMoveTarget($nt)
Checks if $this can be moved to a given Title.
getLocalURL($query='', $variant=false)
Get a URL with no fragment or server name.
escapeLocalURL($query='')
Get an HTML-escaped version of the URL form, suitable for using in a link, without a server name or f...
if(!array_key_exists('StateId', $_REQUEST)) $id
$mHasCascadingRestrictions
exists()
Check if page exists.
getPrefixedText()
Get the prefixed title with spaces.
on($eventName, callable $callBack, $priority=100)
Subscribe to an event.
static newFromText($text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
getIndexTitle()
Get title for search index.
getParentCategories()
Get categories to which this Title belongs and return an array of categories' names.
getSkinFromCssJsSubpage()
Trim down a .css or .js subpage title to get the corresponding skin name.
static makeName($ns, $title)
getNamespaceKey()
Generate strings used for xml 'id' names in monobook tabs.
fixSpecialName()
If the Title refers to a special page alias which is not the local default, returns a new Title which...
userCanCreate($doExpensiveQueries=true)
Can $wgUser create this page?
userCanMove($doExpensiveQueries=true)
Can $wgUser move this page?
static decodeCharReferences($text)
Decode any character references, numeric or named entities, in the text and return a UTF-8 string...
userCanRead()
Would anybody with sufficient privileges be able to move this page? Some pages just aren't movable...
getNsText()
Get the namespace text.
isExternal()
Is this Title interwiki?
static newFromIDs($ids)
Make an array of titles from an array of IDs.
escapeFullURL($query='')
Get an HTML-escaped version of the URL form, suitable for using in a link, including the server name ...
getDBkey()
Get the main part with underscores.
getPrefixedURL()
Get a URL-encoded title (not an actual URL) including interwiki.
areRestrictionsCascading()
static newFromDBkey($key)
Create a new Title from a prefixed DB key.
getFullURL($query='', $variant=false)
Get a real URL referring to this title, with interwiki link and fragment.
getBrokenLinksFrom($options='')
Get an array of Title objects referring to non-existent articles linked from this page...
foreach($_POST as $key=> $value) $res
isCascadeProtected()
Cascading protection: Return true if cascading restrictions apply to this page, false if not...
getDefaultNamespace()
Get the default namespace index, for when there is no namespace.
getNextRevisionID($revision)
Get the revision ID of the next revision.
getFullText()
Get the prefixed title with spaces, plus any fragment (part beginning with '#')
isCssSubpage()
Is this a .css subpage of a user page?
static $titleCache
Static cache variables.
static indexTitle($ns, $title)
Get a string representation of a title suitable for including in a search index.
getTouched()
Get the last touched timestamp.
getNamespace()
Get the namespace index, i.e.
getCascadeProtectionSources($get_pages=true)
Cascading protection: Get the source of any cascading restrictions on this page.
static newFromRedirect($text)
Create a new Title for a redirect.
getPreviousRevisionID($revision)
Get the revision ID of the previous revision.
isSubpage()
Is this a talk page of some sort?
getInterwiki()
Get the namespace text of the subject (rather than talk) page.
static newFromID($id)
Create a new Title from an article ID.
getRestrictions($action)
Accessor/initialisation for mRestrictions.
quickUserCan($action)
Can $wgUser perform $action on this page? This skips potentially expensive cascading permission check...
getEditURL()
Get the edit URL for this Title.
static makeTitleSafe($ns, $title)
Create a new Title from a namespace index and a DB key.
loadRestrictions($oldFashionedRestrictions=null)
getSubpageUrlForm()
Get a URL-encoded form of the subpage text.
moveTo(&$nt, $auth=true, $reason='')
Move a title to a new location.
getEscapedText()
Get the HTML-escaped displayable text form.
moveToNewTitle(&$nt, $reason='')
Move page to non-existing title.
isTrans()
Determine whether the object refers to a page within this project and is transcludable.
static newFromURL($url)
Create a new Title from URL-encoded text.
getTemplateLinksTo($options='')
Get an array of Title objects using this Title as a template Also stores the IDs in the link cache...
moveOverExistingRedirect(&$nt, $reason='')
Move page to a title which is at present a redirect to the source page.
getFragmentForURL()
Get the fragment in URL form, including the "#" character if there is one.
pageCond()
Get an associative array for selecting this title from the "page" table.
$mTextform
All member variables should be considered private Please use the accessor functions.
isValidMoveOperation(&$nt, $auth=true)
Check whether a given move operation would be valid.
static legalChars()
Get a regex character class describing the legal characters in a link.
static wfUrlencode($s)
From GlobalFunctions.php.
isDeleted()
Is there a version of this page in the deletion archive?
isLocal()
Determine whether the object refers to a page within this project.
static & makeTitle($ns, $title)
Create a new Title from a namespace index and a DB key.
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?
getParentCategoryTree($children=array())
Get a tree of parent categories.
if(empty($password)) $table
getInternalURL($query='', $variant=false)
Get the URL form for an internal link.
userCan($action, $doExpensiveQueries=true)
Can $wgUser perform $action on this page?
isSemiProtected($action='edit')
Is this page "semi-protected" - the only protection is autoconfirm?
userIsWatching()
Is $wgUser is watching this page?
$mCascadeRestrictionSources
equals($title)
Compare with another title.
resetArticleID($newid)
This clears some fields in this object, and clears any associated keys in the "bad links" section of ...
getPartialURL()
Get the URL-encoded form of the main part.
getPrefixedDBkey()
Get the prefixed database key form.