<?php
/**
* See title.txt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
*/
-/**
- * @deprecated This used to be a define, but was moved to
- * Title::GAID_FOR_UPDATE in 1.17. This will probably be removed in 1.18
- */
-define( 'GAID_FOR_UPDATE', Title::GAID_FOR_UPDATE );
-
/**
* Represents a title within MediaWiki.
* Optionally may contain an interwiki designation or namespace.
/**
* Constructor
- * @private
*/
- /* private */ function __construct() { }
+ /*protected*/ function __construct() { }
/**
* Create a new Title from a prefixed DB key
* @return String the prefixed title, with spaces
*/
public function getPrefixedText() {
- if ( empty( $this->mPrefixedText ) ) { // FIXME: bad usage of empty() ?
+ // @todo FIXME: Bad usage of empty() ?
+ if ( empty( $this->mPrefixedText ) ) {
$s = $this->prefix( $this->mTextform );
$s = str_replace( '_', ' ', $s );
$this->mPrefixedText = $s;
}
}
}
+
if ( $url === false ) {
if ( $query == '-' ) {
$query = '';
}
}
- // FIXME: this causes breakage in various places when we
+ // @todo FIXME: This causes breakage in various places when we
// actually expected a local URL and end up with dupe prefixes.
if ( $wgRequest->getVal( 'action' ) == 'render' ) {
$url = $wgServer . $url;
/**
* Can $user perform $action on this page?
*
- * FIXME: This *does not* check throttles (User::pingLimiter()).
+ * @todo FIXME: This *does not* check throttles (User::pingLimiter()).
*
* @param $action String action that permission needs to be checked for
* @param $user User to check
- * @param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary queries.
+ * @param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary queries by
+ * skipping checks for cascading protections and user blocks.
* @param $ignoreErrors Array of Strings Set this to a list of message keys whose corresponding errors may be ignored.
* @return Array of arguments to wfMsg to explain permissions problems.
*/
if ( is_array( $result ) && count( $result ) && !is_array( $result[0] ) ) {
// A single array representing an error
$errors[] = $result;
- } else if ( is_array( $result ) && is_array( $result[0] ) ) {
+ } elseif ( is_array( $result ) && is_array( $result[0] ) ) {
// A nested array representing multiple errors
$errors = array_merge( $errors, $result );
- } else if ( $result !== '' && is_string( $result ) ) {
+ } elseif ( $result !== '' && is_string( $result ) ) {
// A string representing a message-id
$errors[] = array( $result );
- } else if ( $result === false ) {
+ } elseif ( $result === false ) {
// a generic "We don't want them to do that"
$errors[] = array( 'badaccess-group0' );
}
if ( $action != 'patrol' && !$user->isAllowed( 'editusercssjs' )
&& !preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
if ( $this->isCssSubpage() && !$user->isAllowed( 'editusercss' ) ) {
- $errors[] = array( 'customcssjsprotected' );
- } else if ( $this->isJsSubpage() && !$user->isAllowed( 'edituserjs' ) ) {
- $errors[] = array( 'customcssjsprotected' );
+ $errors[] = array( 'customcssprotected' );
+ } elseif ( $this->isJsSubpage() && !$user->isAllowed( 'edituserjs' ) ) {
+ $errors[] = array( 'customjsprotected' );
}
}
* @return Array list of errors
*/
private function checkUserBlock( $action, $user, $errors, $doExpensiveQueries, $short ) {
- if( $short && count( $errors ) > 0 ) {
+ if( !$doExpensiveQueries ) {
return $errors;
}
$blockExpiry = $user->mBlock->mExpiry;
$blockTimestamp = $wgLang->timeanddate( wfTimestamp( TS_MW, $user->mBlock->mTimestamp ), true );
if ( $blockExpiry == 'infinity' ) {
- $blockExpiry = wfMessage( 'infiniteblock' );
+ $blockExpiry = wfMessage( 'infiniteblock' )->text();
} else {
$blockExpiry = $wgLang->timeanddate( wfTimestamp( TS_MW, $blockExpiry ), true );
}
- $intended = $user->mBlock->getTarget();
+ $intended = strval( $user->mBlock->getTarget() );
$errors[] = array( ( $block->mAuto ? 'autoblockedtext' : 'blockedtext' ), $link, $reason, $ip, $name,
$blockid, $blockExpiry, $intended, $blockTimestamp );
# Not a public wiki, so no shortcut
$useShortcut = false;
} elseif ( !empty( $wgRevokePermissions ) ) {
- /*
+ /**
* Iterate through each group with permissions being revoked (key not included since we don't care
* what the group name is), then check if the read permission is being revoked. If it is, then
* we don't use the shortcut below since the user might not be able to read, even though anon
# Always grant access to the login page.
# Even anons need to be able to log in.
- if ( $this->isSpecial( 'Userlogin' ) || $this->isSpecial( 'Resetpass' ) ) {
+ if ( $this->isSpecial( 'Userlogin' ) || $this->isSpecial( 'ChangePassword' ) ) {
return true;
}
# If it's a special page, ditch the subpage bit and check again
if ( $this->getNamespace() == NS_SPECIAL ) {
$name = $this->getDBkey();
- list( $name, /* $subpage */ ) = SpecialPage::resolveAliasWithSubpage( $name );
+ list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name );
if ( $name === false ) {
# Invalid special page, but we show standard login required message
return false;
if ( isset( $this->mCascadeSources ) && $getPages ) {
return array( $this->mCascadeSources, $this->mCascadingRestrictions );
- } else if ( isset( $this->mHasCascadingRestrictions ) && !$getPages ) {
+ } elseif ( isset( $this->mHasCascadingRestrictions ) && !$getPages ) {
return array( $this->mHasCascadingRestrictions, $pagerestrictions );
}
* @return String the prefixed text
* @private
*/
- /* private */ function prefix( $name ) {
+ private function prefix( $name ) {
$p = '';
if ( $this->mInterwiki != '' ) {
$p = $this->mInterwiki . ':';
global $wgContLang, $wgLocalInterwiki;
# Initialisation
- $rxTc = self::getTitleInvalidRegex();
-
$this->mInterwiki = $this->mFragment = '';
$this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
# Initial colon indicates main namespace rather than specified default
# but should not create invalid {ns,title} pairs such as {0,Project:Foo}
- if ( ':' == $dbkey { 0 } ) {
+ if ( ':' == $dbkey[0] ) {
$this->mNamespace = NS_MAIN;
$dbkey = substr( $dbkey, 1 ); # remove the colon but continue processing
$dbkey = trim( $dbkey, '_' ); # remove any subsequent whitespace
if ( $wgContLang->getNsIndex( $x[1] ) ) {
# Disallow Talk:File:x type titles...
return false;
- } else if ( Interwiki::isValidInterwiki( $x[1] ) ) {
+ } elseif ( Interwiki::isValidInterwiki( $x[1] ) ) {
# Disallow Talk:Interwiki:x type titles...
return false;
}
}
$fragment = strstr( $dbkey, '#' );
if ( false !== $fragment ) {
- $this->setFragment( preg_replace( '/^#_*/', '#', $fragment ) );
+ $this->setFragment( $fragment );
$dbkey = substr( $dbkey, 0, strlen( $dbkey ) - strlen( $fragment ) );
# remove whitespace again: prevents "Foo_bar_#"
# becoming "Foo_bar_"
}
# Reject illegal characters.
+ $rxTc = self::getTitleInvalidRegex();
if ( preg_match( $rxTc, $dbkey ) ) {
return false;
}
}
$dbw->begin(); # If $file was a LocalFile, its transaction would have closed our own.
- $pageid = $this->getArticleID( GAID_FOR_UPDATE );
+ $pageid = $this->getArticleID( self::GAID_FOR_UPDATE );
$protected = $this->isProtected();
$pageCountChange = ( $createRedirect ? 1 : 0 ) - ( $nt->exists() ? 1 : 0 );
// Do the actual move
$err = $this->moveToInternal( $nt, $reason, $createRedirect );
if ( is_array( $err ) ) {
- # FIXME: What about the File we have already moved?
+ # @todo FIXME: What about the File we have already moved?
$dbw->rollback();
return $err;
}
if ( $reason ) {
$comment .= wfMsgForContent( 'colon-separator' ) . $reason;
}
- $log->addEntry( 'move_prot', $nt, $comment, array( $this->getPrefixedText() ) ); // FIXME: $params?
+ // @todo FIXME: $params?
+ $log->addEntry( 'move_prot', $nt, $comment, array( $this->getPrefixedText() ) );
}
# Update watchlists
$u->doUpdate();
$dbw->commit();
-
+
# Update site_stats
if ( $this->isContentPage() && !$nt->isContentPage() ) {
# No longer a content page
}
$nullRevId = $nullRevision->insertOn( $dbw );
- $article = new Article( $this );
- wfRunHooks( 'NewRevisionFromEditComplete', array( $article, $nullRevision, $latest, $wgUser ) );
-
# Change the name of the target page:
$dbw->update( 'page',
/* SET */ array(
);
$nt->resetArticleID( $oldid );
+ $article = new Article( $nt );
+ wfRunHooks( 'NewRevisionFromEditComplete', array( $article, $nullRevision, $latest, $wgUser ) );
+
# Recreate the redirect, this time in the other direction.
if ( $createRedirect || !$wgUser->isAllowed( 'suppressredirect' ) ) {
$mwRedir = MagicWord::get( 'redirect' );
* @return Revision|Null if page doesn't exist
*/
public function getFirstRevision( $flags = 0 ) {
- $db = ( $flags & self::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 );
+ if ( $pageId ) {
+ $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+ $row = $db->selectRow( 'revision', '*',
+ array( 'rev_page' => $pageId ),
+ __METHOD__,
+ array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 )
+ );
+ if ( $row ) {
+ return new Revision( $row );
+ }
}
+ return null;
}
/**
- * Check if this is a new page
+ * Get the oldest revision timestamp of this page
*
- * @return bool
+ * @param $flags Int Title::GAID_FOR_UPDATE
+ * @return String: MW timestamp
*/
- public function isNewPage() {
- $dbr = wfGetDB( DB_SLAVE );
- return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
+ public function getEarliestRevTime( $flags = 0 ) {
+ $rev = $this->getFirstRevision( $flags );
+ return $rev ? $rev->getTimestamp() : null;
}
/**
- * Get the oldest revision timestamp of this page
+ * Check if this is a new page
*
- * @return String: MW timestamp
+ * @return bool
*/
- public function getEarliestRevTime() {
+ public function isNewPage() {
$dbr = wfGetDB( DB_SLAVE );
- if ( $this->exists() ) {
- $min = $dbr->selectField( 'revision',
- 'MIN(rev_timestamp)',
- array( 'rev_page' => $this->getArticleId() ),
- __METHOD__ );
- return wfTimestampOrNull( TS_MW, $min );
- }
- return null;
+ return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
}
/**
- * Get the number of revisions between the given revision IDs.
+ * Get the number of revisions between the given revision.
* Used for diffs and other things that really need it.
*
- * @param $old Int Revision ID.
- * @param $new Int Revision ID.
- * @return Int Number of revisions between these IDs.
+ * @param $old int|Revision Old revision or rev ID (first before range)
+ * @param $new int|Revision New revision or rev ID (first after range)
+ * @return Int Number of revisions between these revisions.
*/
public function countRevisionsBetween( $old, $new ) {
+ if ( !( $old instanceof Revision ) ) {
+ $old = Revision::newFromTitle( $this, (int)$old );
+ }
+ if ( !( $new instanceof Revision ) ) {
+ $new = Revision::newFromTitle( $this, (int)$new );
+ }
+ if ( !$old || !$new ) {
+ return 0; // nothing to compare
+ }
$dbr = wfGetDB( DB_SLAVE );
- return (int)$dbr->selectField( 'revision', 'count(*)', array(
- 'rev_page' => intval( $this->getArticleId() ),
- 'rev_id > ' . intval( $old ),
- 'rev_id < ' . intval( $new )
- ), __METHOD__
+ return (int)$dbr->selectField( 'revision', 'count(*)',
+ array(
+ 'rev_page' => $this->getArticleId(),
+ 'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
+ 'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
+ ),
+ __METHOD__
);
}
* Get the number of authors between the given revision IDs.
* Used for diffs and other things that really need it.
*
- * @param $fromRevId Int Revision ID (first before range)
- * @param $toRevId Int Revision ID (first after range)
+ * @param $old int|Revision Old revision or rev ID (first before range)
+ * @param $new int|Revision New revision or rev ID (first after range)
* @param $limit Int Maximum number of authors
- * @param $flags Int Title::GAID_FOR_UPDATE
- * @return Int
+ * @return Int Number of revision authors between these revisions.
*/
- public function countAuthorsBetween( $fromRevId, $toRevId, $limit, $flags = 0 ) {
- $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
- $res = $db->select( 'revision', 'DISTINCT rev_user_text',
+ public function countAuthorsBetween( $old, $new, $limit ) {
+ if ( !( $old instanceof Revision ) ) {
+ $old = Revision::newFromTitle( $this, (int)$old );
+ }
+ if ( !( $new instanceof Revision ) ) {
+ $new = Revision::newFromTitle( $this, (int)$new );
+ }
+ if ( !$old || !$new ) {
+ return 0; // nothing to compare
+ }
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
array(
'rev_page' => $this->getArticleID(),
- 'rev_id > ' . (int)$fromRevId,
- 'rev_id < ' . (int)$toRevId
+ 'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
+ 'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
), __METHOD__,
- array( 'LIMIT' => $limit )
+ array( 'LIMIT' => $limit + 1 ) // add one so caller knows it was truncated
);
- return (int)$db->numRows( $res );
+ return (int)$dbr->numRows( $res );
}
/**
return (bool)wfFindFile( $this );
case NS_SPECIAL:
// valid special page
- return SpecialPage::exists( $this->getDBkey() );
+ return SpecialPageFactory::exists( $this->getDBkey() );
case NS_MAIN:
// selflink, possibly with fragment
return $this->mDbkeyform == '';
*/
public function isSpecial( $name ) {
if ( $this->getNamespace() == NS_SPECIAL ) {
- list( $thisName, /* $subpage */ ) = SpecialPage::resolveAliasWithSubpage( $this->getDBkey() );
+ list( $thisName, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $this->getDBkey() );
if ( $name == $thisName ) {
return true;
}
*/
public function fixSpecialName() {
if ( $this->getNamespace() == NS_SPECIAL ) {
- $canonicalName = SpecialPage::resolveAlias( $this->mDbkeyform );
+ list( $canonicalName, /*...*/ ) = SpecialPageFactory::resolveAlias( $this->mDbkeyform );
if ( $canonicalName ) {
- $localName = SpecialPage::getLocalNameFor( $canonicalName );
+ $localName = SpecialPageFactory::getLocalNameFor( $canonicalName );
if ( $localName != $this->mDbkeyform ) {
return Title::makeTitle( NS_SPECIAL, $localName );
}
}
return $unprefixed;
}
+
+ /**
+ * Get the language in which the content of this page is written.
+ * Defaults to $wgContLang, but in certain cases it can be e.g.
+ * $wgLang (such as special pages, which are in the user language).
+ *
+ * @return object Language
+ */
+ public function getPageLanguage() {
+ global $wgLang;
+ if ( $this->getNamespace() == NS_SPECIAL ) {
+ // special pages are in the user language
+ return $wgLang;
+ } elseif ( $this->isRedirect() ) {
+ // the arrow on a redirect page is aligned according to the user language
+ return $wgLang;
+ } elseif ( $this->isCssOrJsPage() ) {
+ // css/js should always be LTR and is, in fact, English
+ return wfGetLangObj( 'en' );
+ } elseif ( $this->getNamespace() == NS_MEDIAWIKI ) {
+ // Parse mediawiki messages with correct target language
+ list( /* $unused */, $lang ) = MessageCache::singleton()->figureMessage( $this->getText() );
+ return wfGetLangObj( $lang );
+ }
+ global $wgContLang;
+ // If nothing special, it should be in the wiki content language
+ $pageLang = $wgContLang;
+ // Hook at the end because we don't want to override the above stuff
+ wfRunHooks( 'PageContentLanguage', array( $this, &$pageLang, $wgLang ) );
+ return wfGetLangObj( $pageLang );
+ }
+}
+
+/**
+ * A BadTitle is generated in MediaWiki::parseTitle() if the title is invalid; the
+ * software uses this to display an error page. Internally it's basically a Title
+ * for an empty special page
+ */
+class BadTitle extends Title {
+ public function __construct(){
+ $this->mTextform = '';
+ $this->mUrlform = '';
+ $this->mDbkeyform = '';
+ $this->mNamespace = NS_SPECIAL; // Stops talk page link, etc, being shown
+ }
+
+ public function exists(){
+ return false;
+ }
+
+ public function getPrefixedText(){
+ return '';
+ }
+
+ public function getText(){
+ return '';
+ }
+
+ public function getPrefixedURL(){
+ return '';
+ }
+
+ public function getPrefixedDBKey(){
+ return '';
+ }
}