*
*/
class Article {
- /**
+ /**@{{
* @private
*/
var $mComment; //!<
var $mTouched; //!<
var $mUser; //!<
var $mUserText; //!<
- /** end private properties */
+ /**@}}*/
+ /**
+ * Constants used by internal components to get rollback results
+ */
+ const SUCCESS = 0; // Operation successful
+ const PERM_DENIED = 1; // Permission denied
+ const BLOCKED = 2; // User has been blocked
+ const READONLY = 3; // Wiki is in read-only mode
+ const BAD_TOKEN = 4; // Invalid token specified
+ const BAD_TITLE = 5; // $this is not a valid Article
+ const ALREADY_ROLLED = 6; // Someone else already rolled this back. $from and $summary will be set
+ const ONLY_AUTHOR = 7; // User is the only author of the page
+
/**
* Constructor and clear the article
* @param $title Reference to a Title object.
* @return bool
*/
function isCurrent() {
+ # If no oldid, this is the current version.
+ if ($this->getOldID() == 0)
+ return true;
+
return $this->exists() &&
isset( $this->mRevision ) &&
$this->mRevision->isCurrent();
function view() {
global $wgUser, $wgOut, $wgRequest, $wgContLang;
global $wgEnableParserCache, $wgStylePath, $wgUseRCPatrol, $wgParser;
- global $wgUseTrackbacks, $wgNamespaceRobotPolicies;
+ global $wgUseTrackbacks, $wgNamespaceRobotPolicies, $wgArticleRobotPolicies;
$sk = $wgUser->getSkin();
wfProfileIn( __METHOD__ );
# Discourage indexing of printable versions, but encourage following
if( $wgOut->isPrintable() ) {
$policy = 'noindex,follow';
+ } elseif ( isset( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) ) {
+ $policy = $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()];
} elseif( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
# Honour customised robot policies for this namespace
$policy = $wgNamespaceRobotPolicies[$ns];
}
# Should the parser cache be used?
- $pcache = $wgEnableParserCache &&
- intval( $wgUser->getOption( 'stubthreshold' ) ) == 0 &&
- $this->exists() &&
- empty( $oldid );
+ $pcache = $wgEnableParserCache
+ && intval( $wgUser->getOption( 'stubthreshold' ) ) == 0
+ && $this->exists()
+ && empty( $oldid )
+ && !$this->mTitle->isCssOrJsPage()
+ && !$this->mTitle->isCssJsSubpage();
wfDebug( 'Article::view using parser cache: ' . ($pcache ? 'yes' : 'no' ) . "\n" );
if ( $wgUser->getOption( 'stubthreshold' ) ) {
wfIncrStats( 'pcache_miss_stub' );
}
$outputDone = false;
- wfRunHooks( 'ArticleViewHeader', array( &$this ) );
+ wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$pcache ) );
if ( $pcache ) {
if ( $wgOut->tryParserCache( $this, $wgUser ) ) {
+ // Ensure that UI elements requiring revision ID have
+ // the correct version information.
+ $wgOut->setRevisionId( $this->mLatest );
$outputDone = true;
}
}
}
if( !$outputDone ) {
$wgOut->setRevisionId( $this->getRevIdFetched() );
- # wrap user css and user js in pre and don't parse
- # XXX: use $this->mTitle->usCssJsSubpage() when php is fixed/ a workaround is found
- if (
- $ns == NS_USER &&
- preg_match('/\\/[\\w]+\\.(css|js)$/', $this->mTitle->getDBkey())
- ) {
- $wgOut->addWikiText( wfMsg('clearyourcache'));
- $wgOut->addHTML( '<pre>'.htmlspecialchars($this->mContent)."\n</pre>" );
- } else if ( $rt = Title::newFromRedirect( $text ) ) {
+
+ // Pages containing custom CSS or JavaScript get special treatment
+ if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
+ $wgOut->addHtml( wfMsgExt( 'clearyourcache', 'parse' ) );
+
+ // Give hooks a chance to customise the output
+ if( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->mTitle, $wgOut ) ) ) {
+ // Wrap the whole lot in a <pre> and don't parse
+ preg_match( '!\.(css|js)$!u', $this->mTitle->getText(), $m );
+ $wgOut->addHtml( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
+ $wgOut->addHtml( htmlspecialchars( $this->mContent ) );
+ $wgOut->addHtml( "\n</pre>\n" );
+ }
+
+ }
+
+ elseif ( $rt = Title::newFromRedirect( $text ) ) {
# Display redirect
$imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
$imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png';
$rmvtxt = "";
if ($wgUser->isAllowed( 'trackback' )) {
$delurl = $this->mTitle->getFullURL("action=deletetrackback&tbid="
- . $o->tb_id . "&token=" . $wgUser->editToken());
- $rmvtxt = wfMsg('trackbackremove', $delurl);
+ . $o->tb_id . "&token=" . urlencode( $wgUser->editToken() ) );
+ $rmvtxt = wfMsg( 'trackbackremove', htmlspecialchars( $delurl ) );
}
$tbtext .= wfMsg(strlen($o->tb_ex) ? 'trackbackexcerpt' : 'trackback',
$o->tb_title,
# If nothing's changed, do nothing
if( $changed ) {
+ global $wgGroupPermissions;
if( wfRunHooks( 'ArticleProtect', array( &$this, &$wgUser, $limit, $reason ) ) ) {
$dbw = wfGetDB( DB_MASTER );
$encodedExpiry = Block::encodeExpiry($expiry, $dbw );
$expiry_description = '';
-
if ( $encodedExpiry != 'infinity' ) {
$expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ) ).')';
}
# Prepare a null revision to be added to the history
- $comment = $wgContLang->ucfirst( wfMsgForContent( $protect ? 'protectedarticle' : 'unprotectedarticle', $this->mTitle->getPrefixedText() ) );
+ $modified = $current != '' && $protect;
+ if ( $protect ) {
+ $comment_type = $modified ? 'modifiedarticleprotection' : 'protectedarticle';
+ } else {
+ $comment_type = 'unprotectedarticle';
+ }
+ $comment = $wgContLang->ucfirst( wfMsgForContent( $comment_type, $this->mTitle->getPrefixedText() ) );
+
+ foreach( $limit as $action => $restrictions ) {
+ # Check if the group level required to edit also can protect pages
+ # Otherwise, people who cannot normally protect can "protect" pages via transclusion
+ $cascade = ( $cascade && isset($wgGroupPermissions[$restrictions]['protect']) && $wgGroupPermissions[$restrictions]['protect'] );
+ }
+
+ $cascade_description = '';
+ if ($cascade) {
+ $cascade_description = ' ['.wfMsg('protect-summary-cascade').']';
+ }
if( $reason )
$comment .= ": $reason";
$comment .= " [$updated]";
if ( $expiry_description && $protect )
$comment .= "$expiry_description";
+ if ( $cascade )
+ $comment .= "$cascade_description";
$nullRevision = Revision::newNullRevision( $dbw, $id, $comment, true );
$nullRevId = $nullRevision->insertOn( $dbw );
# Update the protection log
$log = new LogPage( 'protect' );
- $cascade_description = '';
-
- if ($cascade) {
- $cascade_description = ' ['.wfMsg('protect-summary-cascade').']';
- }
-
if( $protect ) {
- $log->addEntry( 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
+ $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
} else {
$log->addEntry( 'unprotect', $this->mTitle, $reason );
}
$reason = wfMsgForContent( 'exblank' );
}
- if( $length < 500 && $reason === '' ) {
+ if( $reason === '' ) {
# comment field=255, let's grep the first 150 to have some user
# space left
global $wgContLang;
/**
- * Fetch deletion log
+ * Show relevant lines from the deletion log
*/
- function showLogExtract( &$out ) {
- # Show relevant lines from the deletion log:
- $out->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
+ function showLogExtract( $out ) {
+ $out->addHtml( '<h2>' . htmlspecialchars( LogPage::logName( 'delete' ) ) . '</h2>' );
$logViewer = new LogViewer(
new LogReader(
new FauxRequest(
}
/**
- * Revert a modification
- */
- function rollback() {
- global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
-
+ * Roll back the most recent consecutive set of edits to a page
+ * from the same user; fails if there are no eligible edits to
+ * roll back to, e.g. user is the sole contributor
+ *
+ * @param string $fromP - Name of the user whose edits to rollback.
+ * @param string $summary - Custom summary. Set to default summary if empty.
+ * @param string $token - Rollback token.
+ * @param bool $bot - If true, mark all reverted edits as bot.
+ *
+ * @param array $resultDetails contains result-specific dict of additional values
+ * ALREADY_ROLLED : 'current' (rev)
+ * SUCCESS : 'summary' (str), 'current' (rev), 'target' (rev)
+ *
+ * @return self::SUCCESS on succes, self::* on failure
+ */
+ public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails ) {
+ global $wgUser, $wgUseRCPatrol;
+ $resultDetails = null;
+
if( $wgUser->isAllowed( 'rollback' ) ) {
if( $wgUser->isBlocked() ) {
- $wgOut->blockedPage();
- return;
+ return self::BLOCKED;
}
} else {
- $wgOut->permissionRequired( 'rollback' );
- return;
+ return self::PERM_DENIED;
}
-
+
if ( wfReadOnly() ) {
- $wgOut->readOnlyPage( $this->getContent() );
- return;
+ return self::READONLY;
}
- if( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ),
- array( $this->mTitle->getPrefixedText(),
- $wgRequest->getVal( 'from' ) ) ) ) {
- $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
- $wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
- return;
- }
- $dbw = wfGetDB( DB_MASTER );
-
- # Enhanced rollback, marks edits rc_bot=1
- $bot = $wgRequest->getBool( 'bot' );
+ if( !$wgUser->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) )
+ return self::BAD_TOKEN;
- # Replace all this user's current edits with the next one down
+ $dbw = wfGetDB( DB_MASTER );
# Get the last editor
$current = Revision::newFromTitle( $this->mTitle );
if( is_null( $current ) ) {
# Something wrong... no page?
- $wgOut->addHTML( wfMsg( 'notanarticle' ) );
- return;
+ return self::BAD_TITLE;
}
- $from = str_replace( '_', ' ', $wgRequest->getVal( 'from' ) );
+ $from = str_replace( '_', ' ', $fromP );
if( $from != $current->getUserText() ) {
- $wgOut->setPageTitle( wfMsg('rollbackfailed') );
- $wgOut->addWikiText( wfMsg( 'alreadyrolled',
- htmlspecialchars( $this->mTitle->getPrefixedText()),
- htmlspecialchars( $from ),
- htmlspecialchars( $current->getUserText() ) ) );
- if( $current->getComment() != '') {
- $wgOut->addHTML(
- wfMsg( 'editcomment',
- htmlspecialchars( $current->getComment() ) ) );
- }
- return;
+ $resultDetails = array( 'current' => $current );
+ return self::ALREADY_ROLLED;
}
# Get the last edit not by this guy
);
if( $s === false ) {
# Something wrong
- $wgOut->setPageTitle(wfMsg('rollbackfailed'));
- $wgOut->addHTML( wfMsg( 'cantrollback' ) );
- return;
+ return self::ONLY_AUTHOR;
}
-
+
$set = array();
if ( $bot ) {
# Mark all reverted edits as bot
if ( $set ) {
$dbw->update( 'recentchanges', $set,
- array( /* WHERE */
- 'rc_cur_id' => $current->getPage(),
- 'rc_user_text' => $current->getUserText(),
- "rc_timestamp > '{$s->rev_timestamp}'",
- ), __METHOD__
- );
+ array( /* WHERE */
+ 'rc_cur_id' => $current->getPage(),
+ 'rc_user_text' => $current->getUserText(),
+ "rc_timestamp > '{$s->rev_timestamp}'",
+ ), __METHOD__
+ );
}
# Get the edit summary
$target = Revision::newFromId( $s->rev_id );
- $newComment = wfMsgForContent( 'revertpage', $target->getUserText(), $from );
- $newComment = $wgRequest->getText( 'summary', $newComment );
+ if( empty( $summary ) )
+ $summary = wfMsgForContent( 'revertpage', $target->getUserText(), $from );
+
+ # Save
+ $flags = EDIT_UPDATE | EDIT_MINOR;
+ if( $bot )
+ $flags |= EDIT_FORCE_BOT;
+ $this->doEdit( $target->getText(), $summary, $flags );
+
+ $resultDetails = array(
+ 'summary' => $summary,
+ 'current' => $current,
+ 'target' => $target,
+ );
+ return self::SUCCESS;
+ }
- # Save it!
- $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
- $wgOut->addHTML( '<h2>' . htmlspecialchars( $newComment ) . "</h2>\n<hr />\n" );
+ /**
+ * User interface for rollback operations
+ */
+ function rollback() {
+ global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
- $this->updateArticle( $target->getText(), $newComment, 1, $this->mTitle->userIsWatching(), $bot );
+ $details = null;
+ $result = $this->doRollback(
+ $wgRequest->getVal( 'from' ),
+ $wgRequest->getText( 'summary' ),
+ $wgRequest->getVal( 'token' ),
+ $wgRequest->getBool( 'bot' ),
+ $details
+ );
+
+ switch( $result ) {
+ case self::BLOCKED:
+ $wgOut->blockedPage();
+ break;
+ case self::PERM_DENIED:
+ $wgOut->permissionRequired( 'rollback' );
+ break;
+ case self::READONLY:
+ $wgOut->readOnlyPage( $this->getContent() );
+ break;
+ case self::BAD_TOKEN:
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
+ break;
+ case self::BAD_TITLE:
+ $wgOut->addHtml( wfMsg( 'notanarticle' ) );
+ break;
+ case self::ALREADY_ROLLED:
+ $current = $details['current'];
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $wgOut->addWikiText(
+ wfMsg( 'alreadyrolled',
+ htmlspecialchars( $this->mTitle->getPrefixedText() ),
+ htmlspecialchars( $wgRequest->getVal( 'from' ) ),
+ htmlspecialchars( $current->getUserText() )
+ )
+ );
+ if( $current->getComment() != '' ) {
+ $wgOut->addHtml( wfMsg( 'editcomment',
+ $wgUser->getSkin()->formatComment( $current->getComment() ) ) );
+ }
+ break;
+ case self::ONLY_AUTHOR:
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $wgOut->addHtml( wfMsg( 'cantrollback' ) );
+ break;
+ case self::SUCCESS:
+ $current = $details['current'];
+ $target = $details['target'];
+ $wgOut->setPageTitle( wfMsg( 'actioncomplete' ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $old = $wgUser->getSkin()->userLink( $current->getUser(), $current->getUserText() )
+ . $wgUser->getSkin()->userToolLinks( $current->getUser(), $current->getUserText() );
+ $new = $wgUser->getSkin()->userLink( $target->getUser(), $target->getUserText() )
+ . $wgUser->getSkin()->userToolLinks( $target->getUser(), $target->getUserText() );
+ $wgOut->addHtml( wfMsgExt( 'rollback-success', array( 'parse', 'replaceafter' ), $old, $new ) );
+ $wgOut->returnToMain( false, $this->mTitle );
+ break;
+ default:
+ throw new MWException( __METHOD__ . ": Unknown return value `{$retval}`" );
+ }
- $wgOut->returnToMain( false );
}
if ( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
wfSeedRandom();
- if ( 0 == mt_rand( 0, 999 ) ) {
+ if ( 0 == mt_rand( 0, 99 ) ) {
# Periodically flush old entries from the recentchanges table.
global $wgRCMaxAge;
$userlinks = $sk->userLink( $revision->getUser(), $revision->getUserText() )
. $sk->userToolLinks( $revision->getUser(), $revision->getUserText() );
- $r = "\n\t\t\t\t<div id=\"mw-revision-info\">" . wfMsg( 'revision-info', $td, $userlinks ) . "</div>\n" .
+ $m = wfMsg( 'revision-info-current' );
+ $infomsg = $current && !wfEmptyMsg( 'revision-info-current', $m ) && $m != '-'
+ ? 'revision-info-current'
+ : 'revision-info';
+
+ $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . wfMsg( $infomsg, $td, $userlinks ) . "</div>\n" .
"\n\t\t\t\t<div id=\"mw-revision-nav\">" . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
$wgOut->setSubtitle( $r );
}
* @return bool
*/
function isFileCacheable() {
- global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest;
+ global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest, $wgLang, $wgContLang;
$action = $wgRequest->getVal( 'action' );
$oldid = $wgRequest->getVal( 'oldid' );
$diff = $wgRequest->getVal( 'diff' );
$redirect = $wgRequest->getVal( 'redirect' );
$printable = $wgRequest->getVal( 'printable' );
+ $page = $wgRequest->getVal( 'page' );
+
+ //check for non-standard user language; this covers uselang,
+ //and extensions for auto-detecting user language.
+ $ulang = $wgLang->getCode();
+ $clang = $wgContLang->getCode();
+
+ $cacheable = $wgUseFileCache
+ && (!$wgShowIPinHeader)
+ && ($this->getID() != 0)
+ && ($wgUser->isAnon())
+ && (!$wgUser->getNewtalk())
+ && ($this->mTitle->getNamespace() != NS_SPECIAL )
+ && (empty( $action ) || $action == 'view')
+ && (!isset($oldid))
+ && (!isset($diff))
+ && (!isset($redirect))
+ && (!isset($printable))
+ && !isset($page)
+ && (!$this->mRedirectedFrom)
+ && ($ulang === $clang);
- return $wgUseFileCache
- and (!$wgShowIPinHeader)
- and ($this->getID() != 0)
- and ($wgUser->isAnon())
- and (!$wgUser->getNewtalk())
- and ($this->mTitle->getNamespace() != NS_SPECIAL )
- and (empty( $action ) || $action == 'view')
- and (!isset($oldid))
- and (!isset($diff))
- and (!isset($redirect))
- and (!isset($printable))
- and (!$this->mRedirectedFrom);
+ if ( $cacheable ) {
+ //extension may have reason to disable file caching on some pages.
+ $cacheable = wfRunHooks( 'IsFileCacheable', array( $this ) );
+ }
+
+ return $cacheable;
}
/**
$wgOut->addParserOutput( $parserOutput );
}
-}
-
-?>
+}
\ No newline at end of file