From cecb985bee3bdd252e1b8dc0bd500b37cd52be01 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Wed, 16 May 2007 20:58:53 +0000 Subject: Aktualisierung auf MediaWiki 1.10.0 Plugins angepasst und verbessert kleine Korrekturen am Design --- includes/Title.php | 829 ++++++++++++++++++++++++++++------------------------- 1 file changed, 439 insertions(+), 390 deletions(-) (limited to 'includes/Title.php') diff --git a/includes/Title.php b/includes/Title.php index 56414c8a..0ff2e807 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -2,11 +2,12 @@ /** * See title.txt * - * @package MediaWiki */ /** */ -require_once( 'normal/UtfNormal.php' ); +if ( !class_exists( 'UtfNormal' ) ) { + require_once( dirname(__FILE__) . '/normal/UtfNormal.php' ); +} define ( 'GAID_FOR_UPDATE', 1 ); @@ -17,12 +18,14 @@ define ( 'GAID_FOR_UPDATE', 1 ); # reset the cache. define( 'MW_TITLECACHE_MAX', 1000 ); +# Constants for pr_cascade bitfield +define( 'CASCADE', 1 ); + /** * Title class * - Represents a title, which may contain an interwiki designation or namespace * - Can fetch various kinds of data from the database, albeit inefficiently. * - * @package MediaWiki */ class Title { /** @@ -41,21 +44,24 @@ class Title { * @private */ - var $mTextform; # Text form (spaces not underscores) of the main part - var $mUrlform; # URL-encoded form of the main part - var $mDbkeyform; # Main part with underscores - var $mNamespace; # Namespace index, i.e. one of the NS_xxxx constants - var $mInterwiki; # Interwiki prefix (or null string) - var $mFragment; # Title fragment (i.e. the bit after the #) - var $mArticleID; # Article ID, fetched from the link cache on demand - var $mLatestID; # ID of most recent revision - var $mRestrictions; # Array of groups allowed to edit this article - # Only null or "sysop" are supported - var $mRestrictionsLoaded; # Boolean for initialisation on demand - var $mPrefixedText; # Text form including namespace/interwiki, initialised on demand - var $mDefaultNamespace; # Namespace index when there is no namespace - # Zero except in {{transclusion}} tags - var $mWatched; # Is $wgUser watching this page? NULL if unfilled, accessed through userIsWatching() + var $mTextform; # Text form (spaces not underscores) of the main part + var $mUrlform; # URL-encoded form of the main part + var $mDbkeyform; # Main part with underscores + var $mNamespace; # Namespace index, i.e. one of the NS_xxxx constants + var $mInterwiki; # Interwiki prefix (or null string) + var $mFragment; # Title fragment (i.e. the bit after the #) + var $mArticleID; # Article ID, fetched from the link cache on demand + var $mLatestID; # ID of most recent revision + var $mRestrictions; # Array of groups allowed to edit this article + var $mCascadeRestriction; # Cascade restrictions on this page to included templates and images? + var $mRestrictionsExpiry; # When do the restrictions on this page expire? + var $mHasCascadingRestrictions; # Are cascading restrictions in effect on this page? + var $mCascadeRestrictionSources;# Where are the cascading restrictions coming from on this page? + var $mRestrictionsLoaded; # Boolean for initialisation on demand + var $mPrefixedText; # Text form including namespace/interwiki, initialised on demand + var $mDefaultNamespace; # Namespace index when there is no namespace + # Zero except in {{transclusion}} tags + var $mWatched; # Is $wgUser watching this page? NULL if unfilled, accessed through userIsWatching() /**#@-*/ @@ -63,7 +69,7 @@ class Title { * Constructor * @private */ - /* private */ function Title() { + /* private */ function __construct() { $this->mInterwiki = $this->mUrlform = $this->mTextform = $this->mDbkeyform = ''; $this->mArticleID = -1; @@ -75,6 +81,7 @@ class Title { $this->mDefaultNamespace = NS_MAIN; $this->mWatched = NULL; $this->mLatestID = false; + $this->mOldRestrictions = false; } /** @@ -83,10 +90,8 @@ class Title { * instead of spaces, possibly including namespace and * interwiki prefixes * @return Title the new object, or NULL on an error - * @static - * @access public */ - /* static */ function newFromDBkey( $key ) { + public static function newFromDBkey( $key ) { $t = new Title(); $t->mDbkeyform = $key; if( $t->secureAndSplit() ) @@ -105,8 +110,6 @@ class Title { * @param int $defaultNamespace the namespace to use if * none is specified by a prefix * @return Title the new object, or NULL on an error - * @static - * @access public */ public static function newFromText( $text, $defaultNamespace = NS_MAIN ) { if( is_object( $text ) ) { @@ -157,8 +160,6 @@ class Title { * the given title's length does not exceed the maximum. * @param string $url the title, as might be taken from a URL * @return Title the new object, or NULL on an error - * @static - * @access public */ public static function newFromURL( $url ) { global $wgLegalTitleChars; @@ -187,12 +188,10 @@ class Title { * * @param int $id the page_id corresponding to the Title to create * @return Title the new object, or NULL on an error - * @access public - * @static */ public static function newFromID( $id ) { $fname = 'Title::newFromID'; - $dbr =& wfGetDB( DB_SLAVE ); + $dbr = wfGetDB( DB_SLAVE ); $row = $dbr->selectRow( 'page', array( 'page_namespace', 'page_title' ), array( 'page_id' => $id ), $fname ); if ( $row !== false ) { @@ -206,8 +205,8 @@ class Title { /** * Make an array of titles from an array of IDs */ - function newFromIDs( $ids ) { - $dbr =& wfGetDB( DB_SLAVE ); + public static function newFromIDs( $ids ) { + $dbr = wfGetDB( DB_SLAVE ); $res = $dbr->select( 'page', array( 'page_namespace', 'page_title' ), 'page_id IN (' . $dbr->makeList( $ids ) . ')', __METHOD__ ); @@ -228,8 +227,6 @@ class Title { * @param int $ns the namespace of the article * @param string $title the unprefixed database key form * @return Title the new object - * @static - * @access public */ public static function &makeTitle( $ns, $title ) { $t = new Title(); @@ -251,8 +248,6 @@ class Title { * @param int $ns the namespace of the article * @param string $title the database key form * @return Title the new object, or NULL on an error - * @static - * @access public */ public static function makeTitleSafe( $ns, $title ) { $t = new Title(); @@ -266,10 +261,7 @@ class Title { /** * Create a new Title for the Main Page - * - * @static * @return Title the new object - * @access public */ public static function newMainPage() { return Title::newFromText( wfMsgForContent( 'mainpage' ) ); @@ -280,8 +272,6 @@ class Title { * @param string $text the redirect title text * @return Title the new object, or NULL if the text is not a * valid redirect - * @static - * @access public */ public static function newFromRedirect( $text ) { $mwRedir = MagicWord::get( 'redirect' ); @@ -320,7 +310,7 @@ class Title { */ function nameOf( $id ) { $fname = 'Title::nameOf'; - $dbr =& wfGetDB( DB_SLAVE ); + $dbr = wfGetDB( DB_SLAVE ); $s = $dbr->selectRow( 'page', array( 'page_namespace','page_title' ), array( 'page_id' => $id ), $fname ); if ( $s === false ) { return NULL; } @@ -332,8 +322,6 @@ class Title { /** * Get a regex character class describing the legal characters in a link * @return string the list of characters, not delimited - * @static - * @access public */ public static function legalChars() { global $wgLegalTitleChars; @@ -349,7 +337,7 @@ class Title { * @return string a stripped-down title string ready for the * search index */ - /* static */ function indexTitle( $ns, $title ) { + public static function indexTitle( $ns, $title ) { global $wgContLang; $lc = SearchEngine::legalSearchChars() . '&#;'; @@ -388,9 +376,8 @@ class Title { * @return the associated URL, containing "$1", which should be * replaced by an article title * @static (arguably) - * @access public */ - function getInterwikiLink( $key ) { + public function getInterwikiLink( $key ) { global $wgMemc, $wgInterwikiExpiry; global $wgInterwikiCache, $wgContLang; $fname = 'Title::getInterwikiLink'; @@ -413,7 +400,7 @@ class Title { return $s->iw_url; } - $dbr =& wfGetDB( DB_SLAVE ); + $dbr = wfGetDB( DB_SLAVE ); $res = $dbr->select( 'interwiki', array( 'iw_url', 'iw_local', 'iw_trans' ), array( 'iw_prefix' => $key ), $fname ); @@ -441,9 +428,8 @@ class Title { * More logic is explained in DefaultSettings * * @return string URL of interwiki site - * @access public */ - function getInterwikiCached( $key ) { + public static function getInterwikiCached( $key ) { global $wgInterwikiCache, $wgInterwikiScopes, $wgInterwikiFallbackSite; static $db, $site; @@ -484,9 +470,8 @@ class Title { * * @return bool TRUE if this is an in-project interwiki link * or a wikilink, FALSE otherwise - * @access public */ - function isLocal() { + public function isLocal() { if ( $this->mInterwiki != '' ) { # Make sure key is loaded into cache $this->getInterwikiLink( $this->mInterwiki ); @@ -502,9 +487,8 @@ class Title { * this project and is transcludable. * * @return bool TRUE if this is transcludable - * @access public */ - function isTrans() { + public function isTrans() { if ($this->mInterwiki == '') return false; # Make sure key is loaded into cache @@ -513,60 +497,6 @@ class Title { return (bool)(Title::$interwikiCache[$k]->iw_trans); } - /** - * Update the page_touched field for an array of title objects - * @todo Inefficient unless the IDs are already loaded into the - * link cache - * @param array $titles an array of Title objects to be touched - * @param string $timestamp the timestamp to use instead of the - * default current time - * @static - * @access public - */ - function touchArray( $titles, $timestamp = '' ) { - - if ( count( $titles ) == 0 ) { - return; - } - $dbw =& wfGetDB( DB_MASTER ); - if ( $timestamp == '' ) { - $timestamp = $dbw->timestamp(); - } - /* - $page = $dbw->tableName( 'page' ); - $sql = "UPDATE $page SET page_touched='{$timestamp}' WHERE page_id IN ("; - $first = true; - - foreach ( $titles as $title ) { - if ( $wgUseFileCache ) { - $cm = new HTMLFileCache($title); - @unlink($cm->fileCacheName()); - } - - if ( ! $first ) { - $sql .= ','; - } - $first = false; - $sql .= $title->getArticleID(); - } - $sql .= ')'; - if ( ! $first ) { - $dbw->query( $sql, 'Title::touchArray' ); - } - */ - // hack hack hack -- brion 2005-07-11. this was unfriendly to db. - // do them in small chunks: - $fname = 'Title::touchArray'; - foreach( $titles as $title ) { - $dbw->update( 'page', - array( 'page_touched' => $timestamp ), - array( - 'page_namespace' => $title->getNamespace(), - 'page_title' => $title->getDBkey() ), - $fname ); - } - } - /** * Escape a text fragment, say from a link, for a URL */ @@ -588,33 +518,28 @@ class Title { /** * Get the text form (spaces not underscores) of the main part * @return string - * @access public */ - function getText() { return $this->mTextform; } + public function getText() { return $this->mTextform; } /** * Get the URL-encoded form of the main part * @return string - * @access public */ - function getPartialURL() { return $this->mUrlform; } + public function getPartialURL() { return $this->mUrlform; } /** * Get the main part with underscores * @return string - * @access public */ - function getDBkey() { return $this->mDbkeyform; } + public function getDBkey() { return $this->mDbkeyform; } /** * Get the namespace index, i.e. one of the NS_xxxx constants * @return int - * @access public */ - function getNamespace() { return $this->mNamespace; } + public function getNamespace() { return $this->mNamespace; } /** * Get the namespace text * @return string - * @access public */ - function getNsText() { + public function getNsText() { global $wgContLang, $wgCanonicalNamespaceNames; if ( '' != $this->mInterwiki ) { @@ -633,9 +558,8 @@ class Title { /** * Get the namespace text of the subject (rather than talk) page * @return string - * @access public */ - function getSubjectNsText() { + public function getSubjectNsText() { global $wgContLang; return $wgContLang->getNsText( Namespace::getSubject( $this->mNamespace ) ); } @@ -644,38 +568,34 @@ class Title { * Get the namespace text of the talk page * @return string */ - function getTalkNsText() { + public function getTalkNsText() { global $wgContLang; return( $wgContLang->getNsText( Namespace::getTalk( $this->mNamespace ) ) ); } - + /** * Could this title have a corresponding talk page? * @return bool */ - function canTalk() { + public function canTalk() { return( Namespace::canTalk( $this->mNamespace ) ); } - + /** * Get the interwiki prefix (or null string) * @return string - * @access public */ - function getInterwiki() { return $this->mInterwiki; } + public function getInterwiki() { return $this->mInterwiki; } /** * Get the Title fragment (i.e. the bit after the #) in text form * @return string - * @access public */ - function getFragment() { return $this->mFragment; } + public function getFragment() { return $this->mFragment; } /** * Get the fragment in URL form, including the "#" character if there is one - * * @return string - * @access public */ - function getFragmentForURL() { + public function getFragmentForURL() { if ( $this->mFragment == '' ) { return ''; } else { @@ -685,16 +605,15 @@ class Title { /** * Get the default namespace index, for when there is no namespace * @return int - * @access public */ - function getDefaultNamespace() { return $this->mDefaultNamespace; } + public function getDefaultNamespace() { return $this->mDefaultNamespace; } /** * Get title for search index * @return string a stripped-down title string ready for the * search index */ - function getIndexTitle() { + public function getIndexTitle() { return Title::indexTitle( $this->mNamespace, $this->mTextform ); } @@ -702,9 +621,8 @@ class Title { * Get the prefixed database key form * @return string the prefixed title, with underscores and * any interwiki and namespace prefixes - * @access public */ - function getPrefixedDBkey() { + public function getPrefixedDBkey() { $s = $this->prefix( $this->mDbkeyform ); $s = str_replace( ' ', '_', $s ); return $s; @@ -714,9 +632,8 @@ class Title { * Get the prefixed title with spaces. * This is the form usually used for display * @return string the prefixed title, with spaces - * @access public */ - function getPrefixedText() { + public function getPrefixedText() { if ( empty( $this->mPrefixedText ) ) { // FIXME: bad usage of empty() ? $s = $this->prefix( $this->mTextform ); $s = str_replace( '_', ' ', $s ); @@ -730,9 +647,8 @@ class Title { * (part beginning with '#') * @return string the prefixed title, with spaces and * the fragment, including '#' - * @access public */ - function getFullText() { + public function getFullText() { $text = $this->getPrefixedText(); if( '' != $this->mFragment ) { $text .= '#' . $this->mFragment; @@ -744,7 +660,7 @@ class Title { * Get the base name, i.e. the leftmost parts before the / * @return string Base name */ - function getBaseText() { + public function getBaseText() { global $wgNamespacesWithSubpages; if( isset( $wgNamespacesWithSubpages[ $this->mNamespace ] ) && $wgNamespacesWithSubpages[ $this->mNamespace ] ) { $parts = explode( '/', $this->getText() ); @@ -761,7 +677,7 @@ class Title { * Get the lowest-level subpage name, i.e. the rightmost part after / * @return string Subpage name */ - function getSubpageText() { + public function getSubpageText() { global $wgNamespacesWithSubpages; if( isset( $wgNamespacesWithSubpages[ $this->mNamespace ] ) && $wgNamespacesWithSubpages[ $this->mNamespace ] ) { $parts = explode( '/', $this->mTextform ); @@ -770,12 +686,12 @@ class Title { return( $this->mTextform ); } } - + /** * Get a URL-encoded form of the subpage text * @return string URL-encoded subpage name */ - function getSubpageUrlForm() { + public function getSubpageUrlForm() { $text = $this->getSubpageText(); $text = wfUrlencode( str_replace( ' ', '_', $text ) ); $text = str_replace( '%28', '(', str_replace( '%29', ')', $text ) ); # Clean up the URL; per below, this might not be safe @@ -785,9 +701,8 @@ class Title { /** * Get a URL-encoded title (not an actual URL) including interwiki * @return string the URL-encoded form - * @access public */ - function getPrefixedURL() { + public function getPrefixedURL() { $s = $this->prefix( $this->mDbkeyform ); $s = str_replace( ' ', '_', $s ); @@ -808,9 +723,8 @@ class Title { * for interwiki links * @param string $variant language variant of url (for sr, zh..) * @return string the URL - * @access public */ - function getFullURL( $query = '', $variant = false ) { + public function getFullURL( $query = '', $variant = false ) { global $wgContLang, $wgServer, $wgRequest; if ( '' == $this->mInterwiki ) { @@ -831,14 +745,7 @@ class Title { $namespace .= ':'; } $url = str_replace( '$1', $namespace . $this->mUrlform, $baseUrl ); - if( $query != '' ) { - if( false === strpos( $url, '?' ) ) { - $url .= '?'; - } else { - $url .= '&'; - } - $url .= $query; - } + $url = wfAppendQuery( $url, $query ); } # Finally, add the fragment. @@ -855,9 +762,8 @@ class Title { * $wgArticlePath will be used. * @param string $variant language variant of url (for sr, zh..) * @return string the URL - * @access public */ - function getLocalURL( $query = '', $variant = false ) { + public function getLocalURL( $query = '', $variant = false ) { global $wgArticlePath, $wgScript, $wgServer, $wgRequest; global $wgVariantArticlePath, $wgContLang, $wgUser; @@ -881,17 +787,17 @@ class Title { $dbkey = wfUrlencode( $this->getPrefixedDBkey() ); if ( $query == '' ) { if($variant!=false && $wgContLang->hasVariants()){ - if($wgVariantArticlePath==false) + if($wgVariantArticlePath==false) { $variantArticlePath = "$wgScript?title=$1&variant=$2"; // default - else + } else { $variantArticlePath = $wgVariantArticlePath; - + } $url = str_replace( '$2', urlencode( $variant ), $variantArticlePath ); $url = str_replace( '$1', $dbkey, $url ); - } - else + else { $url = str_replace( '$1', $dbkey, $wgArticlePath ); + } } else { global $wgActionPaths; $url = false; @@ -930,9 +836,8 @@ class Title { * using in a link, without a server name or fragment * @param string $query an optional query string * @return string the URL - * @access public */ - function escapeLocalURL( $query = '' ) { + public function escapeLocalURL( $query = '' ) { return htmlspecialchars( $this->getLocalURL( $query ) ); } @@ -942,9 +847,8 @@ class Title { * * @return string the URL * @param string $query an optional query string - * @access public */ - function escapeFullURL( $query = '' ) { + public function escapeFullURL( $query = '' ) { return htmlspecialchars( $this->getFullURL( $query ) ); } @@ -956,9 +860,8 @@ class Title { * @param string $query an optional query string * @param string $variant language variant of url (for sr, zh..) * @return string the URL - * @access public */ - function getInternalURL( $query = '', $variant = false ) { + public function getInternalURL( $query = '', $variant = false ) { global $wgInternalServer; $url = $wgInternalServer . $this->getLocalURL( $query, $variant ); wfRunHooks( 'GetInternalURL', array( &$this, &$url, $query ) ); @@ -969,9 +872,8 @@ class Title { * Get the edit URL for this Title * @return string the URL, or a null string if this is an * interwiki link - * @access public */ - function getEditURL() { + public function getEditURL() { if ( '' != $this->mInterwiki ) { return ''; } $s = $this->getLocalURL( 'action=edit' ); @@ -982,18 +884,16 @@ class Title { * Get the HTML-escaped displayable text form. * Used for the title field in tags. * @return string the text, including any prefixes - * @access public */ - function getEscapedText() { + public function getEscapedText() { return htmlspecialchars( $this->getPrefixedText() ); } /** * Is this Title interwiki? * @return boolean - * @access public */ - function isExternal() { return ( '' != $this->mInterwiki ); } + public function isExternal() { return ( '' != $this->mInterwiki ); } /** * Is this page "semi-protected" - the *only* protection is autoconfirm? @@ -1001,7 +901,7 @@ class Title { * @param string Action to check (default: edit) * @return bool */ - function isSemiProtected( $action = 'edit' ) { + public function isSemiProtected( $action = 'edit' ) { if( $this->exists() ) { $restrictions = $this->getRestrictions( $action ); if( count( $restrictions ) > 0 ) { @@ -1025,12 +925,15 @@ class Title { * @param string $what the action the page is protected from, * by default checks move and edit * @return boolean - * @access public */ - function isProtected( $action = '' ) { + public function isProtected( $action = '' ) { global $wgRestrictionLevels; - if ( NS_SPECIAL == $this->mNamespace ) { return true; } - + + # Special pages have inherent protection + if( $this->getNamespace() == NS_SPECIAL ) + return true; + + # Check regular protection levels if( $action == 'edit' || $action == '' ) { $r = $this->getRestrictions( 'edit' ); foreach( $wgRestrictionLevels as $level ) { @@ -1055,9 +958,8 @@ class Title { /** * Is $wgUser is watching this page? * @return boolean - * @access public */ - function userIsWatching() { + public function userIsWatching() { global $wgUser; if ( is_null( $this->mWatched ) ) { @@ -1071,16 +973,32 @@ class Title { } /** - * Can $wgUser perform $action this page? + * Can $wgUser perform $action on this page? + * This skips potentially expensive cascading permission checks. + * + * Suitable for use for nonessential UI controls in common cases, but + * _not_ for functional access control. + * + * May provide false positives, but should never provide a false negative. + * * @param string $action action that permission needs to be checked for * @return boolean - * @private */ - function userCan($action) { + public function quickUserCan( $action ) { + return $this->userCan( $action, false ); + } + + /** + * Can $wgUser perform $action on this page? + * @param string $action action that permission needs to be checked for + * @param bool $doExpensiveQueries Set this to false to avoid doing unnecessary queries. + * @return boolean + */ + public function userCan( $action, $doExpensiveQueries = true ) { $fname = 'Title::userCan'; wfProfileIn( $fname ); - global $wgUser; + global $wgUser, $wgNamespaceProtection; $result = null; wfRunHooks( 'userCan', array( &$this, &$wgUser, $action, &$result ) ); @@ -1093,12 +1011,16 @@ class Title { wfProfileOut( $fname ); return false; } - // XXX: This is the code that prevents unprotecting a page in NS_MEDIAWIKI - // from taking effect -ævar - if( NS_MEDIAWIKI == $this->mNamespace && - !$wgUser->isAllowed('editinterface') ) { - wfProfileOut( $fname ); - return false; + + if ( array_key_exists( $this->mNamespace, $wgNamespaceProtection ) ) { + $nsProt = $wgNamespaceProtection[ $this->mNamespace ]; + if ( !is_array($nsProt) ) $nsProt = array($nsProt); + foreach( $nsProt as $right ) { + if( '' != $right && !$wgUser->isAllowed( $right ) ) { + wfProfileOut( $fname ); + return false; + } + } } if( $this->mDbkeyform == '_' ) { @@ -1116,7 +1038,28 @@ class Title { wfProfileOut( $fname ); return false; } - + + if ( $doExpensiveQueries && !$this->isCssJsSubpage() ) { + # We /could/ use the protection level on the source page, but it's fairly ugly + # as we have to establish a precedence hierarchy for pages included by multiple + # cascade-protected pages. So just restrict it to people with 'protect' permission, + # as they could remove the protection anyway. + list( $cascadingSources, $restrictions ) = $this->getCascadeProtectionSources(); + # Cascading protection depends on more than this page... + # Several cascading protected pages may include this page... + # Check each cascading level + # This is only for protection restrictions, not for all actions + if( $cascadingSources > 0 && isset($restrictions[$action]) ) { + foreach( $restrictions[$action] as $right ) { + $right = ( $right == 'sysop' ) ? 'protect' : $right; + if( '' != $right && !$wgUser->isAllowed( $right ) ) { + wfProfileOut( $fname ); + return false; + } + } + } + } + foreach( $this->getRestrictions($action) as $right ) { // Backwards compatibility, rewrite sysop -> protect if ( $right == 'sysop' ) { @@ -1149,28 +1092,28 @@ class Title { /** * Can $wgUser edit this page? * @return boolean - * @access public + * @deprecated use userCan('edit') */ - function userCanEdit() { - return $this->userCan('edit'); + public function userCanEdit( $doExpensiveQueries = true ) { + return $this->userCan( 'edit', $doExpensiveQueries ); } /** * Can $wgUser create this page? * @return boolean - * @access public + * @deprecated use userCan('create') */ - function userCanCreate() { - return $this->userCan('create'); + public function userCanCreate( $doExpensiveQueries = true ) { + return $this->userCan( 'create', $doExpensiveQueries ); } /** * Can $wgUser move this page? * @return boolean - * @access public + * @deprecated use userCan('move') */ - function userCanMove() { - return $this->userCan('move'); + public function userCanMove( $doExpensiveQueries = true ) { + return $this->userCan( 'move', $doExpensiveQueries ); } /** @@ -1178,9 +1121,8 @@ class Title { * Some pages just aren't movable. * * @return boolean - * @access public */ - function isMovable() { + public function isMovable() { return Namespace::isMovable( $this->getNamespace() ) && $this->getInterwiki() == ''; } @@ -1188,9 +1130,9 @@ class Title { /** * Can $wgUser read this page? * @return boolean - * @access public + * @todo fold these checks into userCan() */ - function userCanRead() { + public function userCanRead() { global $wgUser; $result = null; @@ -1231,18 +1173,16 @@ class Title { /** * Is this a talk page of some sort? * @return bool - * @access public */ - function isTalkPage() { + public function isTalkPage() { return Namespace::isTalk( $this->getNamespace() ); } /** * Is this a subpage? * @return bool - * @access public */ - function isSubpage() { + public function isSubpage() { global $wgNamespacesWithSubpages; if( isset( $wgNamespacesWithSubpages[ $this->mNamespace ] ) ) { @@ -1255,16 +1195,15 @@ class Title { /** * Is this a .css or .js subpage of a user page? * @return bool - * @access public */ - function isCssJsSubpage() { - return ( NS_USER == $this->mNamespace and preg_match("/\\/.*\\.(css|js)$/", $this->mTextform ) ); + public function isCssJsSubpage() { + return ( NS_USER == $this->mNamespace and preg_match("/\\/.*\\.(?:css|js)$/", $this->mTextform ) ); } /** * Is this a *valid* .css or .js subpage of a user page? * Check that the corresponding skin exists */ - function isValidCssJsSubpage() { + public function isValidCssJsSubpage() { if ( $this->isCssJsSubpage() ) { $skinNames = Skin::getSkinNames(); return array_key_exists( $this->getSkinFromCssJsSubpage(), $skinNames ); @@ -1275,7 +1214,7 @@ class Title { /** * Trim down a .css or .js subpage title to get the corresponding skin name */ - function getSkinFromCssJsSubpage() { + public function getSkinFromCssJsSubpage() { $subpage = explode( '/', $this->mTextform ); $subpage = $subpage[ count( $subpage ) - 1 ]; return( str_replace( array( '.css', '.js' ), array( '', '' ), $subpage ) ); @@ -1283,17 +1222,15 @@ class Title { /** * Is this a .css subpage of a user page? * @return bool - * @access public */ - function isCssSubpage() { + public function isCssSubpage() { return ( NS_USER == $this->mNamespace and preg_match("/\\/.*\\.css$/", $this->mTextform ) ); } /** * Is this a .js subpage of a user page? * @return bool - * @access public */ - function isJsSubpage() { + public function isJsSubpage() { return ( NS_USER == $this->mNamespace and preg_match("/\\/.*\\.js$/", $this->mTextform ) ); } /** @@ -1302,54 +1239,223 @@ class Title { * * @return boolean * @todo XXX: this might be better using restrictions - * @access public */ - function userCanEditCssJsSubpage() { + public function userCanEditCssJsSubpage() { global $wgUser; return ( $wgUser->isAllowed('editinterface') or preg_match('/^'.preg_quote($wgUser->getName(), '/').'\//', $this->mTextform) ); } + /** + * Cascading protection: Return true if cascading restrictions apply to this page, false if not. + * + * @return bool If the page is subject to cascading restrictions. + */ + public function isCascadeProtected() { + list( $sources, $restrictions ) = $this->getCascadeProtectionSources( false ); + return ( $sources > 0 ); + } + + /** + * Cascading protection: Get the source of any cascading restrictions on this page. + * + * @param $get_pages bool Whether or not to retrieve the actual pages that the restrictions have come from. + * @return array( mixed title array, restriction array) + * Array of the Title objects of the pages from which cascading restrictions have come, false for none, or true if such restrictions exist, but $get_pages was not set. + * The restriction array is an array of each type, each of which contains an array of unique groups + */ + public function getCascadeProtectionSources( $get_pages = true ) { + global $wgEnableCascadingProtection, $wgRestrictionTypes; + + # Define our dimension of restrictions types + $pagerestrictions = array(); + foreach( $wgRestrictionTypes as $action ) + $pagerestrictions[$action] = array(); + + if (!$wgEnableCascadingProtection) + return array( false, $pagerestrictions ); + + if ( isset( $this->mCascadeSources ) && $get_pages ) { + return array( $this->mCascadeSources, $this->mCascadingRestrictions ); + } else if ( isset( $this->mHasCascadingRestrictions ) && !$get_pages ) { + return array( $this->mHasCascadingRestrictions, $pagerestrictions ); + } + + wfProfileIn( __METHOD__ ); + + $dbr = wfGetDb( DB_SLAVE ); + + if ( $this->getNamespace() == NS_IMAGE ) { + $tables = array ('imagelinks', 'page_restrictions'); + $where_clauses = array( + 'il_to' => $this->getDBkey(), + 'il_from=pr_page', + 'pr_cascade' => 1 ); + } else { + $tables = array ('templatelinks', 'page_restrictions'); + $where_clauses = array( + 'tl_namespace' => $this->getNamespace(), + 'tl_title' => $this->getDBkey(), + 'tl_from=pr_page', + 'pr_cascade' => 1 ); + } + + if ( $get_pages ) { + $cols = array('pr_page', 'page_namespace', 'page_title', 'pr_expiry', 'pr_type', 'pr_level' ); + $where_clauses[] = 'page_id=pr_page'; + $tables[] = 'page'; + } else { + $cols = array( 'pr_expiry' ); + } + + $res = $dbr->select( $tables, $cols, $where_clauses, __METHOD__ ); + + $sources = $get_pages ? array() : false; + $now = wfTimestampNow(); + $purgeExpired = false; + + while( $row = $dbr->fetchObject( $res ) ) { + $expiry = Block::decodeExpiry( $row->pr_expiry ); + if( $expiry > $now ) { + if ($get_pages) { + $page_id = $row->pr_page; + $page_ns = $row->page_namespace; + $page_title = $row->page_title; + $sources[$page_id] = Title::makeTitle($page_ns, $page_title); + # Add groups needed for each restriction type if its not already there + # Make sure this restriction type still exists + if ( isset($pagerestrictions[$row->pr_type]) && !in_array($row->pr_level, $pagerestrictions[$row->pr_type]) ) { + $pagerestrictions[$row->pr_type][]=$row->pr_level; + } + } else { + $sources = true; + } + } else { + // Trigger lazy purge of expired restrictions from the db + $purgeExpired = true; + } + } + if( $purgeExpired ) { + Title::purgeExpiredRestrictions(); + } + + wfProfileOut( __METHOD__ ); + + if ( $get_pages ) { + $this->mCascadeSources = $sources; + $this->mCascadingRestrictions = $pagerestrictions; + } else { + $this->mHasCascadingRestrictions = $sources; + } + + return array( $sources, $pagerestrictions ); + } + + function areRestrictionsCascading() { + if (!$this->mRestrictionsLoaded) { + $this->loadRestrictions(); + } + + return $this->mCascadeRestriction; + } + /** * Loads a string into mRestrictions array - * @param string $res restrictions in string format - * @access public + * @param resource $res restrictions as an SQL result. */ - function loadRestrictions( $res ) { + private function loadRestrictionsFromRow( $res, $oldFashionedRestrictions = NULL ) { + $dbr = wfGetDb( DB_SLAVE ); + $this->mRestrictions['edit'] = array(); $this->mRestrictions['move'] = array(); - - if( !$res ) { - # No restrictions (page_restrictions blank) - $this->mRestrictionsLoaded = true; - return; + + # Backwards-compatibility: also load the restrictions from the page record (old format). + + if ( $oldFashionedRestrictions == NULL ) { + $oldFashionedRestrictions = $dbr->selectField( 'page', 'page_restrictions', array( 'page_id' => $this->getArticleId() ), __METHOD__ ); } - - foreach( explode( ':', trim( $res ) ) as $restrict ) { - $temp = explode( '=', trim( $restrict ) ); - if(count($temp) == 1) { - // old format should be treated as edit/move restriction - $this->mRestrictions["edit"] = explode( ',', trim( $temp[0] ) ); - $this->mRestrictions["move"] = explode( ',', trim( $temp[0] ) ); - } else { - $this->mRestrictions[$temp[0]] = explode( ',', trim( $temp[1] ) ); + + if ($oldFashionedRestrictions != '') { + + foreach( explode( ':', trim( $oldFashionedRestrictions ) ) as $restrict ) { + $temp = explode( '=', trim( $restrict ) ); + if(count($temp) == 1) { + // old old format should be treated as edit/move restriction + $this->mRestrictions["edit"] = explode( ',', trim( $temp[0] ) ); + $this->mRestrictions["move"] = explode( ',', trim( $temp[0] ) ); + } else { + $this->mRestrictions[$temp[0]] = explode( ',', trim( $temp[1] ) ); + } + } + + $this->mOldRestrictions = true; + $this->mCascadeRestriction = false; + $this->mRestrictionsExpiry = Block::decodeExpiry(''); + + } + + if( $dbr->numRows( $res ) ) { + # Current system - load second to make them override. + $now = wfTimestampNow(); + $purgeExpired = false; + + while ($row = $dbr->fetchObject( $res ) ) { + # Cycle through all the restrictions. + + // This code should be refactored, now that it's being used more generally, + // But I don't really see any harm in leaving it in Block for now -werdna + $expiry = Block::decodeExpiry( $row->pr_expiry ); + + // Only apply the restrictions if they haven't expired! + if ( !$expiry || $expiry > $now ) { + $this->mRestrictionsExpiry = $expiry; + $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) ); + + $this->mCascadeRestriction |= $row->pr_cascade; + } else { + // Trigger a lazy purge of expired restrictions + $purgeExpired = true; + } + } + + if( $purgeExpired ) { + Title::purgeExpiredRestrictions(); } } + $this->mRestrictionsLoaded = true; } + public function loadRestrictions( $oldFashionedRestrictions = NULL ) { + if( !$this->mRestrictionsLoaded ) { + $dbr = wfGetDB( DB_SLAVE ); + + $res = $dbr->select( 'page_restrictions', '*', + array ( 'pr_page' => $this->getArticleId() ), __METHOD__ ); + + $this->loadRestrictionsFromRow( $res, $oldFashionedRestrictions ); + } + } + + /** + * Purge expired restrictions from the page_restrictions table + */ + static function purgeExpiredRestrictions() { + $dbw = wfGetDB( DB_MASTER ); + $dbw->delete( 'page_restrictions', + array( 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), + __METHOD__ ); + } + /** * Accessor/initialisation for mRestrictions * - * @access public * @param string $action action that permission needs to be checked for * @return array the array of groups allowed to edit this article */ - function getRestrictions( $action ) { + public function getRestrictions( $action ) { if( $this->exists() ) { if( !$this->mRestrictionsLoaded ) { - $dbr =& wfGetDB( DB_SLAVE ); - $res = $dbr->selectField( 'page', 'page_restrictions', array( 'page_id' => $this->getArticleId() ) ); - $this->loadRestrictions( $res ); + $this->loadRestrictions(); } return isset( $this->mRestrictions[$action] ) ? $this->mRestrictions[$action] @@ -1362,14 +1468,13 @@ class Title { /** * Is there a version of this page in the deletion archive? * @return int the number of archived revisions - * @access public */ - function isDeleted() { + public function isDeleted() { $fname = 'Title::isDeleted'; if ( $this->getNamespace() < 0 ) { $n = 0; } else { - $dbr =& wfGetDB( DB_SLAVE ); + $dbr = wfGetDB( DB_SLAVE ); $n = $dbr->selectField( 'archive', 'COUNT(*)', array( 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ), $fname ); if( $this->getNamespace() == NS_IMAGE ) { @@ -1386,9 +1491,8 @@ class Title { * @param int $flags a bit field; may be GAID_FOR_UPDATE to select * for update * @return int the ID - * @access public */ - function getArticleID( $flags = 0 ) { + public function getArticleID( $flags = 0 ) { $linkCache =& LinkCache::singleton(); if ( $flags & GAID_FOR_UPDATE ) { $oldUpdate = $linkCache->forUpdate( true ); @@ -1402,11 +1506,11 @@ class Title { return $this->mArticleID; } - function getLatestRevID() { + public function getLatestRevID() { if ($this->mLatestID !== false) return $this->mLatestID; - $db =& wfGetDB(DB_SLAVE); + $db = wfGetDB(DB_SLAVE); return $this->mLatestID = $db->selectField( 'revision', "max(rev_id)", array('rev_page' => $this->getArticleID()), @@ -1422,9 +1526,8 @@ class Title { * Article::doDeleteArticle() * * @param int $newid the new Article ID - * @access public */ - function resetArticleID( $newid ) { + public function resetArticleID( $newid ) { $linkCache =& LinkCache::singleton(); $linkCache->clearBadLink( $this->getPrefixedDBkey() ); @@ -1437,16 +1540,15 @@ class Title { /** * Updates page_touched for this page; called from LinksUpdate.php * @return bool true if the update succeded - * @access public */ - function invalidateCache() { + public function invalidateCache() { global $wgUseFileCache; if ( wfReadOnly() ) { return; } - $dbw =& wfGetDB( DB_MASTER ); + $dbw = wfGetDB( DB_MASTER ); $success = $dbw->update( 'page', array( /* SET */ 'page_touched' => $dbw->timestamp() @@ -1492,9 +1594,8 @@ class Title { * namespace prefixes, sets the other forms, and canonicalizes * everything. * @return bool true on success - * @private */ - /* private */ function secureAndSplit() { + private function secureAndSplit() { global $wgContLang, $wgLocalInterwiki, $wgCapitalLinks; # Initialisation @@ -1536,6 +1637,7 @@ class Title { if ( ':' == $dbkey{0} ) { $this->mNamespace = NS_MAIN; $dbkey = substr( $dbkey, 1 ); # remove the colon but continue processing + $dbkey = trim( $dbkey, '_' ); # remove any subsequent whitespace } # Namespace or interwiki prefix @@ -1544,12 +1646,7 @@ class Title { $m = array(); if ( preg_match( "/^(.+?)_*:_*(.*)$/S", $dbkey, $m ) ) { $p = $m[1]; - $lowerNs = $wgContLang->lc( $p ); - if ( $ns = Namespace::getCanonicalIndex( $lowerNs ) ) { - # Canonical namespace - $dbkey = $m[2]; - $this->mNamespace = $ns; - } elseif ( $ns = $wgContLang->getNsIndex( $lowerNs )) { + if ( $ns = $wgContLang->getNsIndex( $p )) { # Ordinary namespace $dbkey = $m[2]; $this->mNamespace = $ns; @@ -1623,6 +1720,13 @@ class Title { { return false; } + + /** + * Magic tilde sequences? Nu-uh! + */ + if( strpos( $dbkey, '~~~' ) !== false ) { + return false; + } /** * Limit the size of titles to 255 bytes. @@ -1681,18 +1785,17 @@ class Title { * members directly, which is what Linker::formatComment was doing previously. * * @param string $fragment text - * @access kind of public + * @todo clarify whether access is supposed to be public (was marked as "kind of public") */ - function setFragment( $fragment ) { + public function setFragment( $fragment ) { $this->mFragment = str_replace( '_', ' ', substr( $fragment, 1 ) ); } /** * Get a Title object associated with the talk page of this article * @return Title the object for the talk page - * @access public */ - function getTalkPage() { + public function getTalkPage() { return Title::makeTitle( Namespace::getTalk( $this->getNamespace() ), $this->getDBkey() ); } @@ -1701,9 +1804,8 @@ class Title { * talk page * * @return Title the object for the subject page - * @access public */ - function getSubjectPage() { + public function getSubjectPage() { return Title::makeTitle( Namespace::getSubject( $this->getNamespace() ), $this->getDBkey() ); } @@ -1716,15 +1818,14 @@ class Title { * * @param string $options may be FOR UPDATE * @return array the Title objects linking here - * @access public */ - function getLinksTo( $options = '', $table = 'pagelinks', $prefix = 'pl' ) { + public function getLinksTo( $options = '', $table = 'pagelinks', $prefix = 'pl' ) { $linkCache =& LinkCache::singleton(); if ( $options ) { - $db =& wfGetDB( DB_MASTER ); + $db = wfGetDB( DB_MASTER ); } else { - $db =& wfGetDB( DB_SLAVE ); + $db = wfGetDB( DB_SLAVE ); } $res = $db->select( array( 'page', $table ), @@ -1758,9 +1859,8 @@ class Title { * * @param string $options may be FOR UPDATE * @return array the Title objects linking here - * @access public */ - function getTemplateLinksTo( $options = '' ) { + public function getTemplateLinksTo( $options = '' ) { return $this->getLinksTo( $options, 'templatelinks', 'tl' ); } @@ -1769,13 +1869,12 @@ class Title { * * @param string $options may be FOR UPDATE * @return array the Title objects - * @access public */ - function getBrokenLinksFrom( $options = '' ) { + public function getBrokenLinksFrom( $options = '' ) { if ( $options ) { - $db =& wfGetDB( DB_MASTER ); + $db = wfGetDB( DB_MASTER ); } else { - $db =& wfGetDB( DB_SLAVE ); + $db = wfGetDB( DB_SLAVE ); } $res = $db->safeQuery( @@ -1808,9 +1907,8 @@ class Title { * page changes * * @return array the URLs - * @access public */ - function getSquidURLs() { + public function getSquidURLs() { global $wgContLang; $urls = array( @@ -1830,7 +1928,7 @@ class Title { return $urls; } - function purgeSquid() { + public function purgeSquid() { global $wgUseSquid; if ( $wgUseSquid ) { $urls = $this->getSquidURLs(); @@ -1842,9 +1940,8 @@ class Title { /** * Move this page without authentication * @param Title &$nt the new page Title - * @access public */ - function moveNoAuth( &$nt ) { + public function moveNoAuth( &$nt ) { return $this->moveTo( $nt, false ); } @@ -1856,9 +1953,8 @@ class Title { * @param bool $auth indicates whether $wgUser's permissions * should be checked * @return mixed true on success, message name on failure - * @access public */ - function isValidMoveOperation( &$nt, $auth = true ) { + public function isValidMoveOperation( &$nt, $auth = true ) { if( !$this or !$nt ) { return 'badtitletext'; } @@ -1882,8 +1978,8 @@ class Title { } if ( $auth && ( - !$this->userCanEdit() || !$nt->userCanEdit() || - !$this->userCanMove() || !$nt->userCanMove() ) ) { + !$this->userCan( 'edit' ) || !$nt->userCan( 'edit' ) || + !$this->userCan( 'move' ) || !$nt->userCan( 'move' ) ) ) { return 'protectedpage'; } @@ -1905,9 +2001,8 @@ class Title { * @param bool $auth indicates whether $wgUser's permissions * should be checked * @return mixed true on success, message name on failure - * @access public */ - function moveTo( &$nt, $auth = true, $reason = '' ) { + public function moveTo( &$nt, $auth = true, $reason = '' ) { $err = $this->isValidMoveOperation( $nt, $auth ); if( is_string( $err ) ) { return $err; @@ -1924,7 +2019,7 @@ class Title { $redirid = $this->getArticleID(); # Fixing category links (those without piped 'alternate' names) to be sorted under the new title - $dbw =& wfGetDB( DB_MASTER ); + $dbw = wfGetDB( DB_MASTER ); $categorylinks = $dbw->tableName( 'categorylinks' ); $sql = "UPDATE $categorylinks SET cl_sortkey=" . $dbw->addQuotes( $nt->getPrefixedText() ) . " WHERE cl_from=" . $dbw->addQuotes( $pageid ) . @@ -1949,24 +2044,24 @@ class Title { $u->doUpdate(); # Update site_stats - if ( $this->getNamespace() == NS_MAIN and $nt->getNamespace() != NS_MAIN ) { - # Moved out of main namespace - # not viewed, edited, removing - $u = new SiteStatsUpdate( 0, 1, -1, $pageCountChange); - } elseif ( $this->getNamespace() != NS_MAIN and $nt->getNamespace() == NS_MAIN ) { - # Moved into main namespace - # not viewed, edited, adding + if( $this->isContentPage() && !$nt->isContentPage() ) { + # No longer a content page + # Not viewed, edited, removing + $u = new SiteStatsUpdate( 0, 1, -1, $pageCountChange ); + } elseif( !$this->isContentPage() && $nt->isContentPage() ) { + # Now a content page + # Not viewed, edited, adding $u = new SiteStatsUpdate( 0, 1, +1, $pageCountChange ); - } elseif ( $pageCountChange ) { - # Added redirect + } elseif( $pageCountChange ) { + # Redirect added $u = new SiteStatsUpdate( 0, 0, 0, 1 ); - } else{ + } else { + # Nothing special $u = false; } - if ( $u ) { + if( $u ) $u->doUpdate(); - } - + global $wgUser; wfRunHooks( 'TitleMoveComplete', array( &$this, &$nt, &$wgUser, $pageid, $redirid ) ); return true; @@ -1978,12 +2073,11 @@ class Title { * * @param Title &$nt the page to move to, which should currently * be a redirect - * @private */ - function moveOverExistingRedirect( &$nt, $reason = '' ) { + private function moveOverExistingRedirect( &$nt, $reason = '' ) { global $wgUseSquid; $fname = 'Title::moveOverExistingRedirect'; - $comment = wfMsgForContent( '1movedto2', $this->getPrefixedText(), $nt->getPrefixedText() ); + $comment = wfMsgForContent( '1movedto2_redir', $this->getPrefixedText(), $nt->getPrefixedText() ); if ( $reason ) { $comment .= ": $reason"; @@ -1992,7 +2086,7 @@ class Title { $now = wfTimestampNow(); $newid = $nt->getArticleID(); $oldid = $this->getArticleID(); - $dbw =& wfGetDB( DB_MASTER ); + $dbw = wfGetDB( DB_MASTER ); $linkCache =& LinkCache::singleton(); # Delete the old redirect. We don't save it to history since @@ -2056,9 +2150,8 @@ class Title { /** * Move page to non-existing title. * @param Title &$nt the new Title - * @private */ - function moveToNewTitle( &$nt, $reason = '' ) { + private function moveToNewTitle( &$nt, $reason = '' ) { global $wgUseSquid; $fname = 'MovePageForm::moveToNewTitle'; $comment = wfMsgForContent( '1movedto2', $this->getPrefixedText(), $nt->getPrefixedText() ); @@ -2068,7 +2161,7 @@ class Title { $newid = $nt->getArticleID(); $oldid = $this->getArticleID(); - $dbw =& wfGetDB( DB_MASTER ); + $dbw = wfGetDB( DB_MASTER ); $now = $dbw->timestamp(); $linkCache =& LinkCache::singleton(); @@ -2128,12 +2221,11 @@ class Title { * - Selects for update, so don't call it unless you mean business * * @param Title &$nt the new title to check - * @access public */ - function isValidMoveTarget( $nt ) { + public function isValidMoveTarget( $nt ) { $fname = 'Title::isValidMoveTarget'; - $dbw =& wfGetDB( DB_MASTER ); + $dbw = wfGetDB( DB_MASTER ); # Is it a redirect? $id = $nt->getArticleID(); @@ -2179,59 +2271,18 @@ class Title { return $row === false; } - /** - * Create a redirect; fails if the title already exists; does - * not notify RC - * - * @param Title $dest the destination of the redirect - * @param string $comment the comment string describing the move - * @return bool true on success - * @access public - */ - function createRedirect( $dest, $comment ) { - if ( $this->getArticleID() ) { - return false; - } - - $fname = 'Title::createRedirect'; - $dbw =& wfGetDB( DB_MASTER ); - - $article = new Article( $this ); - $newid = $article->insertOn( $dbw ); - $revision = new Revision( array( - 'page' => $newid, - 'comment' => $comment, - 'text' => "#REDIRECT [[" . $dest->getPrefixedText() . "]]\n", - ) ); - $revision->insertOn( $dbw ); - $article->updateRevisionOn( $dbw, $revision, 0 ); - - # Link table - $dbw->insert( 'pagelinks', - array( - 'pl_from' => $newid, - 'pl_namespace' => $dest->getNamespace(), - 'pl_title' => $dest->getDbKey() - ), $fname - ); - - Article::onArticleCreate( $this ); - return true; - } - /** * Get categories to which this Title belongs and return an array of * categories' names. * * @return array an array of parents in the form: * $parent => $currentarticle - * @access public */ - function getParentCategories() { + public function getParentCategories() { global $wgContLang; $titlekey = $this->getArticleId(); - $dbr =& wfGetDB( DB_SLAVE ); + $dbr = wfGetDB( DB_SLAVE ); $categorylinks = $dbr->tableName( 'categorylinks' ); # NEW SQL @@ -2257,9 +2308,8 @@ class Title { * Get a tree of parent categories * @param array $children an array with the children in the keys, to check for circular refs * @return array - * @access public */ - function getParentCategoryTree( $children = array() ) { + public function getParentCategoryTree( $children = array() ) { $parents = $this->getParentCategories(); if($parents != '') { @@ -2286,9 +2336,8 @@ class Title { * the "page" table * * @return array - * @access public */ - function pageCond() { + public function pageCond() { return array( 'page_namespace' => $this->mNamespace, 'page_title' => $this->mDbkeyform ); } @@ -2298,8 +2347,8 @@ class Title { * @param integer $revision Revision ID. Get the revision that was before this one. * @return integer $oldrevision|false */ - function getPreviousRevisionID( $revision ) { - $dbr =& wfGetDB( DB_SLAVE ); + public function getPreviousRevisionID( $revision ) { + $dbr = wfGetDB( DB_SLAVE ); return $dbr->selectField( 'revision', 'rev_id', 'rev_page=' . intval( $this->getArticleId() ) . ' AND rev_id<' . intval( $revision ) . ' ORDER BY rev_id DESC' ); @@ -2311,8 +2360,8 @@ class Title { * @param integer $revision Revision ID. Get the revision that was after this one. * @return integer $oldrevision|false */ - function getNextRevisionID( $revision ) { - $dbr =& wfGetDB( DB_SLAVE ); + public function getNextRevisionID( $revision ) { + $dbr = wfGetDB( DB_SLAVE ); return $dbr->selectField( 'revision', 'rev_id', 'rev_page=' . intval( $this->getArticleId() ) . ' AND rev_id>' . intval( $revision ) . ' ORDER BY rev_id' ); @@ -2325,8 +2374,8 @@ class Title { * @param integer $new Revision ID. * @return integer Number of revisions between these IDs. */ - function countRevisionsBetween( $old, $new ) { - $dbr =& wfGetDB( DB_SLAVE ); + public function countRevisionsBetween( $old, $new ) { + $dbr = wfGetDB( DB_SLAVE ); return $dbr->selectField( 'revision', 'count(*)', 'rev_page = ' . intval( $this->getArticleId() ) . ' AND rev_id > ' . intval( $old ) . @@ -2339,7 +2388,7 @@ class Title { * @param Title $title * @return bool */ - function equals( $title ) { + public function equals( $title ) { // Note: === is necessary for proper matching of number-like titles. return $this->getInterwiki() === $title->getInterwiki() && $this->getNamespace() == $title->getNamespace() @@ -2350,7 +2399,7 @@ class Title { * Check if page exists * @return bool */ - function exists() { + public function exists() { return $this->getArticleId() != 0; } @@ -2360,7 +2409,7 @@ class Title { * Currently, a self-link with a fragment and special pages are in * this category. Special pages never exist in the database. */ - function isAlwaysKnown() { + public function isAlwaysKnown() { return $this->isExternal() || ( 0 == $this->mNamespace && "" == $this->mDbkeyform ) || NS_SPECIAL == $this->mNamespace; } @@ -2370,7 +2419,7 @@ class Title { * pages linking to this title. May be sent to the job queue depending * on the number of links. Typically called on create and delete. */ - function touchLinks() { + public function touchLinks() { $u = new HTMLCacheUpdate( $this, 'pagelinks' ); $u->doUpdate(); @@ -2383,8 +2432,8 @@ class Title { /** * Get the last touched timestamp */ - function getTouched() { - $dbr =& wfGetDB( DB_SLAVE ); + public function getTouched() { + $dbr = wfGetDB( DB_SLAVE ); $touched = $dbr->selectField( 'page', 'page_touched', array( 'page_namespace' => $this->getNamespace(), @@ -2394,26 +2443,14 @@ class Title { return $touched; } - /** - * Get a cached value from a global cache that is invalidated when this page changes - * @param string $key the key - * @param callback $callback A callback function which generates the value on cache miss - * - * @deprecated use DependencyWrapper - */ - function getRelatedCache( $memc, $key, $expiry, $callback, $params = array() ) { - return DependencyWrapper::getValueFromCache( $memc, $key, $expiry, $callback, - $params, new TitleDependency( $this ) ); - } - - function trackbackURL() { + public function trackbackURL() { global $wgTitle, $wgScriptPath, $wgServer; return "$wgServer$wgScriptPath/trackback.php?article=" . htmlspecialchars(urlencode($wgTitle->getPrefixedDBkey())); } - function trackbackRDF() { + public function trackbackRDF() { $url = htmlspecialchars($this->getFullURL()); $title = htmlspecialchars($this->getText()); $tburl = $this->trackbackURL(); @@ -2434,7 +2471,7 @@ class Title { * Generate strings used for xml 'id' names in monobook tabs * @return string */ - function getNamespaceKey() { + public function getNamespaceKey() { global $wgContLang; switch ($this->getNamespace()) { case NS_MAIN: @@ -2473,9 +2510,8 @@ class Title { /** * Returns true if this title resolves to the named special page * @param string $name The special page name - * @access public */ - function isSpecial( $name ) { + public function isSpecial( $name ) { if ( $this->getNamespace() == NS_SPECIAL ) { list( $thisName, /* $subpage */ ) = SpecialPage::resolveAliasWithSubpage( $this->getDBkey() ); if ( $name == $thisName ) { @@ -2489,7 +2525,7 @@ class Title { * If the Title refers to a special page alias which is not the local default, * returns a new Title which points to the local default. Otherwise, returns $this. */ - function fixSpecialName() { + public function fixSpecialName() { if ( $this->getNamespace() == NS_SPECIAL ) { $canonicalName = SpecialPage::resolveAlias( $this->mDbkeyform ); if ( $canonicalName ) { @@ -2501,5 +2537,18 @@ class Title { } return $this; } + + /** + * Is this Title in a namespace which contains content? + * In other words, is this a content page, for the purposes of calculating + * statistics, etc? + * + * @return bool + */ + public function isContentPage() { + return Namespace::isContent( $this->getNamespace() ); + } + } + ?> -- cgit v1.2.2