var $mLength = -1; ///< The page length, 0 for special pages
var $mRedirect = null; ///< Is the article at this title a redirect?
var $mNotificationTimestamp = array(); ///< Associative array of user ID -> timestamp/false
+ var $mBacklinkCache = null; ///< Cache of links to this title
//@}
/**
* Extract a redirect destination from a string and return the
* Title, or null if the text doesn't contain a valid redirect
+ * This will only return the very next target, useful for
+ * the redirect table and other checks that don't need full recursion
*
- * @param $text \type{String} Text with possible redirect
+ * @param $text \type{\string} Text with possible redirect
* @return \type{Title} The corresponding Title
*/
public static function newFromRedirect( $text ) {
+ return self::newFromRedirectInternal( $text );
+ }
+
+ /**
+ * Extract a redirect destination from a string and return the
+ * Title, or null if the text doesn't contain a valid redirect
+ * This will recurse down $wgMaxRedirects times or until a non-redirect target is hit
+ * in order to provide (hopefully) the Title of the final destination instead of another redirect
+ *
+ * @param $text \type{\string} Text with possible redirect
+ * @return \type{Title} The corresponding Title
+ */
+ public static function newFromRedirectRecurse( $text ) {
+ $titles = self::newFromRedirectArray( $text );
+ return $titles ? array_pop( $titles ) : null;
+ }
+
+ /**
+ * Extract a redirect destination from a string and return an
+ * array of Titles, or null if the text doesn't contain a valid redirect
+ * The last element in the array is the final destination after all redirects
+ * have been resolved (up to $wgMaxRedirects times)
+ *
+ * @param $text \type{\string} Text with possible redirect
+ * @return \type{\array} Array of Titles, with the destination last
+ */
+ public static function newFromRedirectArray( $text ) {
+ global $wgMaxRedirects;
+ // are redirects disabled?
+ if( $wgMaxRedirects < 1 )
+ return null;
+ $title = self::newFromRedirectInternal( $text );
+ if( is_null( $title ) )
+ return null;
+ // recursive check to follow double redirects
+ $recurse = $wgMaxRedirects;
+ $titles = array( $title );
+ while( --$recurse > 0 ) {
+ if( $title->isRedirect() ) {
+ $article = new Article( $title, 0 );
+ $newtitle = $article->getRedirectTarget();
+ } else {
+ break;
+ }
+ // Redirects to some special pages are not permitted
+ if( $newtitle instanceOf Title && $newtitle->isValidRedirectTarget() ) {
+ // the new title passes the checks, so make that our current title so that further recursion can be checked
+ $title = $newtitle;
+ $titles[] = $newtitle;
+ } else {
+ break;
+ }
+ }
+ return $titles;
+ }
+
+ /**
+ * Really extract the redirect destination
+ * Do not call this function directly, use one of the newFromRedirect* functions above
+ *
+ * @param $text \type{\string} Text with possible redirect
+ * @return \type{Title} The corresponding Title
+ */
+ protected static function newFromRedirectInternal( $text ) {
$redir = MagicWord::get( 'redirect' );
$text = trim($text);
if( $redir->matchStartAndRemove( $text ) ) {
$m[1] = urldecode( ltrim( $m[1], ':' ) );
}
$title = Title::newFromText( $m[1] );
- // Redirects to some special pages are not permitted
- if( $title instanceof Title
- && !$title->isSpecial( 'Userlogout' )
- && !$title->isSpecial( 'Filepath' ) )
- {
- return $title;
+ // If the title is a redirect to bad special pages or is invalid, return null
+ if( !$title instanceof Title || !$title->isValidRedirectTarget() ) {
+ return null;
}
+ return $title;
}
}
return null;
*/
static function escapeFragmentForURL( $fragment ) {
global $wgEnforceHtmlIds;
- return wfUrlencode( Sanitizer::escapeId( $fragment,
- $wgEnforceHtmlIds ? array() : 'xml' ) );
+ # Note that we don't urlencode the fragment. urlencoded Unicode
+ # fragments appear not to work in IE (at least up to 7) or in at least
+ # one version of Opera 9.x. The W3C validator, for one, doesn't seem
+ # to care if they aren't encoded.
+ return Sanitizer::escapeId( $fragment,
+ $wgEnforceHtmlIds ? 'noninitial' : 'xml' );
}
#----------------------------------------------------------------------------
$query = $matches[1];
if( isset( $matches[4] ) ) $query .= $matches[4];
$url = str_replace( '$1', $dbkey, $wgActionPaths[$action] );
- if( $query != '' ) $url .= '?' . $query;
+ if( $query != '' ) {
+ $url = wfAppendQuery( $url, $query );
+ }
}
}
if ( $url === false ) {
*/
public function userCan( $action, $doExpensiveQueries = true ) {
global $wgUser;
- return ( $this->getUserPermissionsErrorsInternal( $action, $wgUser, $doExpensiveQueries ) === array());
+ return ($this->getUserPermissionsErrorsInternal( $action, $wgUser, $doExpensiveQueries, true ) === array());
}
/**
}
// Edit blocks should not affect reading. Account creation blocks handled at userlogin.
- if ( $user->isBlockedFrom( $this ) && $action != 'read' && $action != 'createaccount' ) {
+ if ( $action != 'read' && $action != 'createaccount' && $user->isBlockedFrom( $this ) ) {
$block = $user->mBlock;
// This is from OutputPage::blockedPage
* @param $action \type{\string} action that permission needs to be checked for
* @param $user \type{User} user to check
* @param $doExpensiveQueries \type{\bool} Set this to false to avoid doing unnecessary queries.
+ * @param $short \type{\bool} Set this to true to stop after the first permission error.
* @return \type{\array} Array of arrays of the arguments to wfMsg to explain permissions problems.
*/
- private function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries = true ) {
+ private function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries=true, $short=false ) {
wfProfileIn( __METHOD__ );
$errors = array();
+ // First stop is permissions checks, which fail most often, and which are easiest to test.
+ if ( $action == 'move' ) {
+ if( !$user->isAllowed( 'move-rootuserpages' )
+ && $this->getNamespace() == NS_USER && !$this->isSubpage() )
+ {
+ // Show user page-specific message only if the user can move other pages
+ $errors[] = array( 'cant-move-user-page' );
+ }
+
+ // Check if user is allowed to move files if it's a file
+ if( $this->getNamespace() == NS_FILE && !$user->isAllowed( 'movefile' ) ) {
+ $errors[] = array( 'movenotallowedfile' );
+ }
+
+ if( !$user->isAllowed( 'move' ) ) {
+ // User can't move anything
+ $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
+ }
+ } elseif ( $action == 'create' ) {
+ if( ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
+ ( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) ) )
+ {
+ $errors[] = $user->isAnon() ? array ('nocreatetext') : array ('nocreate-loggedin');
+ }
+ } elseif( $action == 'move-target' ) {
+ if( !$user->isAllowed( 'move' ) ) {
+ // User can't move anything
+ $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
+ } elseif( !$user->isAllowed( 'move-rootuserpages' )
+ && $this->getNamespace() == NS_USER && !$this->isSubpage() )
+ {
+ // Show user page-specific message only if the user can move other pages
+ $errors[] = array( 'cant-move-to-user-page' );
+ }
+ } elseif( !$user->isAllowed( $action ) ) {
+ $return = null;
+ $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
+ User::getGroupsWithPermission( $action ) );
+ if( $groups ) {
+ $return = array( 'badaccess-groups',
+ array( implode( ', ', $groups ), count( $groups ) ) );
+ } else {
+ $return = array( "badaccess-group0" );
+ }
+ $errors[] = $return;
+ }
+
+ # Short-circuit point
+ if( $short && count($errors) > 0 ) {
+ wfProfileOut( __METHOD__ );
+ return $errors;
+ }
+
// Use getUserPermissionsErrors instead
if( !wfRunHooks( 'userCan', array( &$this, &$user, $action, &$result ) ) ) {
wfProfileOut( __METHOD__ );
return $result ? array() : array( array( 'badaccess-group0' ) );
}
-
+ // Check getUserPermissionsErrors hook
if( !wfRunHooks( 'getUserPermissionsErrors', array(&$this,&$user,$action,&$result) ) ) {
if( is_array($result) && count($result) && !is_array($result[0]) )
$errors[] = $result; # A single array representing an error
else if( $result === false )
$errors[] = array('badaccess-group0'); # a generic "We don't want them to do that"
}
+ # Short-circuit point
+ if( $short && count($errors) > 0 ) {
+ wfProfileOut( __METHOD__ );
+ return $errors;
+ }
+ // Check getUserPermissionsErrorsExpensive hook
if( $doExpensiveQueries && !wfRunHooks( 'getUserPermissionsErrorsExpensive', array(&$this,&$user,$action,&$result) ) ) {
if( is_array($result) && count($result) && !is_array($result[0]) )
$errors[] = $result; # A single array representing an error
else if( $result === false )
$errors[] = array('badaccess-group0'); # a generic "We don't want them to do that"
}
+ # Short-circuit point
+ if( $short && count($errors) > 0 ) {
+ wfProfileOut( __METHOD__ );
+ return $errors;
+ }
- // TODO: document
+ # Only 'createaccount' and 'execute' can be performed on
+ # special pages, which don't actually exist in the DB.
$specialOKActions = array( 'createaccount', 'execute' );
if( NS_SPECIAL == $this->mNamespace && !in_array( $action, $specialOKActions) ) {
$errors[] = array('ns-specialprotected');
}
+ # Check $wgNamespaceProtection for restricted namespaces
if( $this->isNamespaceProtected() ) {
$ns = $this->getNamespace() == NS_MAIN ?
wfMsg( 'nstab-main' ) : $this->getNsText();
array('protectedinterface') : array( 'namespaceprotected', $ns );
}
- # protect css/js subpages of user pages
+ # Protect css/js subpages of user pages
# XXX: this might be better using restrictions
# XXX: Find a way to work around the php bug that prevents using $this->userCanEditCssJsSubpage() from working
if( $this->isCssJsSubpage() && !$user->isAllowed('editusercssjs')
$errors[] = array('customcssjsprotected');
}
+ # Check against page_restrictions table requirements on this
+ # page. The user must possess all required rights for this action.
+ foreach( $this->getRestrictions($action) as $right ) {
+ // Backwards compatibility, rewrite sysop -> protect
+ if( $right == 'sysop' ) {
+ $right = 'protect';
+ }
+ if( '' != $right && !$user->isAllowed( $right ) ) {
+ // Users with 'editprotected' permission can edit protected pages
+ if( $action=='edit' && $user->isAllowed( 'editprotected' ) ) {
+ // Users with 'editprotected' permission cannot edit protected pages
+ // with cascading option turned on.
+ if( $this->mCascadeRestriction ) {
+ $errors[] = array( 'protectedpagetext', $right );
+ }
+ } else {
+ $errors[] = array( 'protectedpagetext', $right );
+ }
+ }
+ }
+ # Short-circuit point
+ if( $short && count($errors) > 0 ) {
+ wfProfileOut( __METHOD__ );
+ return $errors;
+ }
+
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
}
}
}
-
- foreach( $this->getRestrictions($action) as $right ) {
- // Backwards compatibility, rewrite sysop -> protect
- if( $right == 'sysop' ) {
- $right = 'protect';
- }
- if( '' != $right && !$user->isAllowed( $right ) ) {
- // Users with 'editprotected' permission can edit protected pages
- if( $action=='edit' && $user->isAllowed( 'editprotected' ) ) {
- // Users with 'editprotected' permission cannot edit protected pages
- // with cascading option turned on.
- if( $this->mCascadeRestriction ) {
- $errors[] = array( 'protectedpagetext', $right );
- } else {
- // Nothing, user can edit!
- }
- } else {
- $errors[] = array( 'protectedpagetext', $right );
- }
- }
+ # Short-circuit point
+ if( $short && count($errors) > 0 ) {
+ wfProfileOut( __METHOD__ );
+ return $errors;
}
if( $action == 'protect' ) {
$errors[] = array( 'titleprotected', User::whoIs($pt_user), $pt_reason );
}
}
-
- if( ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
- ( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) ) )
- {
- $errors[] = $user->isAnon() ? array ('nocreatetext') : array ('nocreate-loggedin');
- }
} elseif( $action == 'move' ) {
- if( !$user->isAllowed( 'move' ) ) {
- // User can't move anything
- $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
- } elseif( !$user->isAllowed( 'move-rootuserpages' )
- && $this->getNamespace() == NS_USER && !$this->isSubpage() )
- {
- // Show user page-specific message only if the user can move other pages
- $errors[] = array( 'cant-move-user-page' );
- }
// Check for immobile pages
if( !MWNamespace::isMovable( $this->getNamespace() ) ) {
// Specific message for this case
$errors[] = array( 'immobile-page' );
}
} elseif( $action == 'move-target' ) {
- if( !$user->isAllowed( 'move' ) ) {
- // User can't move anything
- $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
- } elseif( !$user->isAllowed( 'move-rootuserpages' )
- && $this->getNamespace() == NS_USER && !$this->isSubpage() )
- {
- // Show user page-specific message only if the user can move other pages
- $errors[] = array( 'cant-move-to-user-page' );
- }
if( !MWNamespace::isMovable( $this->getNamespace() ) ) {
$errors[] = array( 'immobile-target-namespace', $this->getNsText() );
} elseif( !$this->isMovable() ) {
$errors[] = array( 'immobile-target-page' );
}
- } elseif( !$user->isAllowed( $action ) ) {
- $return = null;
- $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
- User::getGroupsWithPermission( $action ) );
- if( $groups ) {
- $return = array( 'badaccess-groups',
- array( implode( ', ', $groups ), count( $groups ) ) );
- } else {
- $return = array( "badaccess-group0" );
- }
- $errors[] = $return;
}
wfProfileOut( __METHOD__ );
$expiry_description = '';
if ( $encodedExpiry != 'infinity' ) {
- $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ) ).')';
+ $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ) , $wgContLang->date( $expiry ) , $wgContLang->time( $expiry ) ).')';
}
else {
$expiry_description .= ' (' . wfMsgForContent( 'protect-expiry-indefinite' ).')';
}
# Shortcut for public wikis, allows skipping quite a bit of code
- if ($wgGroupPermissions['*']['read'])
+ if ( !empty( $wgGroupPermissions['*']['read'] ) )
return true;
if( $wgUser->isAllowed( 'read' ) ) {
return $this->mHasSubpages;
}
- $db = wfGetDB( DB_SLAVE );
- return $this->mHasSubpages = (bool)$db->selectField( 'page', '1',
- "page_namespace = {$this->mNamespace} AND page_title LIKE '"
- . $db->escapeLike( $this->mDbkeyform ) . "/%'",
- __METHOD__
+ $subpages = $this->getSubpages( 1 );
+ if( $subpages instanceof TitleArray )
+ return $this->mHasSubpages = (bool)$subpages->count();
+ return $this->mHasSubpages = false;
+ }
+
+ /**
+ * Get all subpages of this page.
+ * @param $limit Maximum number of subpages to fetch; -1 for no limit
+ * @return mixed TitleArray, or empty array if this page's namespace
+ * doesn't allow subpages
+ */
+ public function getSubpages( $limit = -1 ) {
+ if( !MWNamespace::hasSubpages( $this->getNamespace() ) )
+ return array();
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $conds['page_namespace'] = $this->getNamespace();
+ $conds[] = 'page_title LIKE ' . $dbr->addQuotes(
+ $dbr->escapeLike( $this->getDBkey() ) . '/%' );
+ $options = array();
+ if( $limit > -1 )
+ $options['LIMIT'] = $limit;
+ return $this->mSubpages = TitleArray::newFromResult(
+ $dbr->select( 'page',
+ array( 'page_id', 'page_namespace', 'page_title' ),
+ $conds,
+ __METHOD__,
+ $options
+ )
);
}
# Namespace or interwiki prefix
$firstPass = true;
+ $prefixRegexp = "/^(.+?)_*:_*(.*)$/S";
do {
$m = array();
- if ( preg_match( "/^(.+?)_*:_*(.*)$/S", $dbkey, $m ) ) {
+ if ( preg_match( $prefixRegexp, $dbkey, $m ) ) {
$p = $m[1];
- if ( $ns = $wgContLang->getNsIndex( $p )) {
+ if ( $ns = $wgContLang->getNsIndex( $p ) ) {
# Ordinary namespace
$dbkey = $m[2];
$this->mNamespace = $ns;
+ # For Talk:X pages, check if X has a "namespace" prefix
+ if( $ns == NS_TALK && preg_match( $prefixRegexp, $dbkey, $x ) ) {
+ if( $wgContLang->getNsIndex( $x[1] ) )
+ return false; # Disallow Talk:File:x type titles...
+ else if( Interwiki::isValidInterwiki( $x[1] ) )
+ return false; # Disallow Talk:Interwiki:x type titles...
+ }
} elseif( Interwiki::isValidInterwiki( $p ) ) {
if( !$firstPass ) {
# Can't make a local interwiki link to an interwiki link.
* WARNING: do not use this function on arbitrary user-supplied titles!
* On heavily-used templates it will max out the memory.
*
- * @param $options \type{\string} may be FOR UPDATE
+ * @param array $options may be FOR UPDATE
* @return \type{\arrayof{Title}} the Title objects linking here
*/
- public function getLinksTo( $options = '', $table = 'pagelinks', $prefix = 'pl' ) {
+ public function getLinksTo( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
$linkCache = LinkCache::singleton();
- if ( $options ) {
+ if ( count( $options ) > 0 ) {
$db = wfGetDB( DB_MASTER );
} else {
$db = wfGetDB( DB_SLAVE );
* WARNING: do not use this function on arbitrary user-supplied titles!
* On heavily-used templates it will max out the memory.
*
- * @param $options \type{\string} may be FOR UPDATE
+ * @param array $options may be FOR UPDATE
* @return \type{\arrayof{Title}} the Title objects linking here
*/
- public function getTemplateLinksTo( $options = '' ) {
+ public function getTemplateLinksTo( $options = array() ) {
return $this->getLinksTo( $options, 'templatelinks', 'tl' );
}
* Get an array of Title objects referring to non-existent articles linked from this page
*
* @todo check if needed (used only in SpecialBrokenRedirects.php, and should use redirect table in this case)
- * @param $options \type{\string} may be FOR UPDATE
* @return \type{\arrayof{Title}} the Title objects
*/
- public function getBrokenLinksFrom( $options = '' ) {
+ public function getBrokenLinksFrom() {
if ( $this->getArticleId() == 0 ) {
# All links from article ID 0 are false positives
return array();
}
- if ( $options ) {
- $db = wfGetDB( DB_MASTER );
- } else {
- $db = wfGetDB( DB_SLAVE );
- }
-
- $res = $db->safeQuery(
- "SELECT pl_namespace, pl_title
- FROM !
- LEFT JOIN !
- ON pl_namespace=page_namespace
- AND pl_title=page_title
- WHERE pl_from=?
- AND page_namespace IS NULL
- !",
- $db->tableName( 'pagelinks' ),
- $db->tableName( 'page' ),
- $this->getArticleId(),
- $options );
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select(
+ array( 'page', 'pagelinks' ),
+ array( 'pl_namespace', 'pl_title' ),
+ array(
+ 'pl_from' => $this->getArticleId(),
+ 'page_namespace IS NULL'
+ ),
+ __METHOD__, array(),
+ array(
+ 'page' => array(
+ 'LEFT JOIN',
+ array( 'pl_namespace=page_namespace', 'pl_title=page_title' )
+ )
+ )
+ );
$retVal = array();
- if ( $db->numRows( $res ) ) {
- foreach( $res as $row ) {
- $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
- }
+ foreach( $res as $row ) {
+ $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
}
- $db->freeResult( $res );
return $retVal;
}
);
# Update the protection log
$log = new LogPage( 'protect' );
- $comment = wfMsgForContent('prot_1movedto2',$this->getPrefixedText(), $nt->getPrefixedText() );
- if( $reason ) $comment .= ': ' . $reason;
+ $comment = wfMsgForContent( 'prot_1movedto2', $this->getPrefixedText(), $nt->getPrefixedText() );
+ if( $reason ) $comment .= wfMsgForContent( 'colon-separator' ) . $reason;
$log->addEntry( 'move_prot', $nt, $comment, array($this->getPrefixedText()) ); // FIXME: $params?
}
}
+ /**
+ * Move this page's subpages to be subpages of $nt
+ * @param $nt Title Move target
+ * @param $auth bool Whether $wgUser's permissions should be checked
+ * @param $reason string The reason for the move
+ * @param $createRedirect bool Whether to create redirects from the old subpages to the new ones
+ * Ignored if the user doesn't have the 'suppressredirect' right
+ * @return mixed array with old page titles as keys, and strings (new page titles) or
+ * arrays (errors) as values, or an error array with numeric indices if no pages were moved
+ */
+ public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true ) {
+ global $wgUser, $wgMaximumMovedPages;
+ // Check permissions
+ if( !$this->userCan( 'move-subpages' ) )
+ return array( 'cant-move-subpages' );
+ // Do the source and target namespaces support subpages?
+ if( !MWNamespace::hasSubpages( $this->getNamespace() ) )
+ return array( 'namespace-nosubpages',
+ MWNamespace::getCanonicalName( $this->getNamespace() ) );
+ if( !MWNamespace::hasSubpages( $nt->getNamespace() ) )
+ return array( 'namespace-nosubpages',
+ MWNamespace::getCanonicalName( $nt->getNamespace() ) );
+
+ $subpages = $this->getSubpages($wgMaximumMovedPages + 1);
+ $retval = array();
+ $count = 0;
+ foreach( $subpages as $oldSubpage ) {
+ $count++;
+ if( $count > $wgMaximumMovedPages ) {
+ $retval[$oldSubpage->getPrefixedTitle()] =
+ array( 'movepage-max-pages',
+ $wgMaximumMovedPages );
+ break;
+ }
+
+ if( $oldSubpage->getArticleId() == $this->getArticleId() )
+ // When moving a page to a subpage of itself,
+ // don't move it twice
+ continue;
+ $newPageName = preg_replace(
+ '#^'.preg_quote( $this->getDBKey(), '#' ).'#',
+ $nt->getDBKey(), $oldSubpage->getDBKey() );
+ if( $oldSubpage->isTalkPage() ) {
+ $newNs = $nt->getTalkPage()->getNamespace();
+ } else {
+ $newNs = $nt->getSubjectPage()->getNamespace();
+ }
+ # Bug 14385: we need makeTitleSafe because the new page names may
+ # be longer than 255 characters.
+ $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
+
+ $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect );
+ if( $success === true ) {
+ $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
+ } else {
+ $retval[$oldSubpage->getPrefixedText()] = $success;
+ }
+ }
+ return $retval;
+ }
+
/**
* Checks if this page is just a one-rev redirect.
* Adds lock, so don't use just for light purposes.
array( 'page_is_redirect', 'page_latest', 'page_id' ),
$this->pageCond(),
__METHOD__,
- 'FOR UPDATE'
+ array( 'FOR UPDATE' )
);
# Cache some fields we may want
$this->mArticleID = $row ? intval($row->page_id) : 0;
'page_latest != rev_id'
),
__METHOD__,
- 'FOR UPDATE'
+ array( 'FOR UPDATE' )
);
# Return true if there was no history
return ($row === false);
);
}
+ /**
+ * Get the first revision of the page
+ *
+ * @param $flags \type{\int} GAID_FOR_UPDATE
+ * @return Revision (or NULL if page doesn't exist)
+ */
+ public function getFirstRevision( $flags=0 ) {
+ $db = ($flags & GAID_FOR_UPDATE) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+ $pageId = $this->getArticleId($flags);
+ if( !$pageId ) return NULL;
+ $row = $db->selectRow( 'revision', '*',
+ array( 'rev_page' => $pageId ),
+ __METHOD__,
+ array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 )
+ );
+ if( !$row ) {
+ return NULL;
+ } else {
+ return new Revision( $row );
+ }
+ }
+
/**
* Check if this is a new page
*
public function isKnown() {
return $this->exists() || $this->isAlwaysKnown();
}
+
+ /**
+ * Is this in a namespace that allows actual pages?
+ *
+ * @return \type{\bool} TRUE or FALSE
+ */
+ public function canExist() {
+ return $this->mNamespace >= 0 && $this->mNamespace != NS_MEDIA;
+ }
/**
* Update page_touched timestamps and send squid purge messages for
}
return $redirs;
}
+
+ /**
+ * Check if this Title is a valid redirect target
+ *
+ * @return \type{\bool} TRUE or FALSE
+ */
+ public function isValidRedirectTarget() {
+ global $wgInvalidRedirectTargets;
+
+ // invalid redirect targets are stored in a global array, but explicity disallow Userlogout here
+ if( $this->isSpecial( 'Userlogout' ) ) {
+ return false;
+ }
+
+ foreach( $wgInvalidRedirectTargets as $target ) {
+ if( $this->isSpecial( $target ) ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Get a backlink cache object
+ */
+ function getBacklinkCache() {
+ if ( is_null( $this->mBacklinkCache ) ) {
+ $this->mBacklinkCache = new BacklinkCache( $this );
+ }
+ return $this->mBacklinkCache;
+ }
}