var $mUserText; //!<
/**@}}*/
- /**
- * 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.
* @param $oldId Integer revision ID, null to fetch from request, zero for current
*/
- function __construct( &$title, $oldId = null ) {
+ function __construct( Title $title, $oldId = null ) {
$this->mTitle =& $title;
$this->mOldId = $oldId;
$this->clear();
$this->mRevIdFetched = 0;
$this->mRedirectUrl = false;
$this->mLatest = false;
+ $this->mPreparedEdit = false;
}
/**
* @return Return the text of this revision
*/
function getContent() {
- global $wgUser, $wgOut;
+ global $wgUser, $wgOut, $wgMessageCache;
wfProfileIn( __METHOD__ );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ $wgMessageCache->loadAllMessages();
$ret = wfMsgWeirdKey ( $this->mTitle->getText() ) ;
} else {
$ret = wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' );
// FIXME: Horrible, horrible! This content-loading interface just plain sucks.
// We should instead work with the Revision object when we need it...
- $this->mContent = $revision->userCan( Revision::DELETED_TEXT ) ? $revision->getRawText() : "";
- //$this->mContent = $revision->getText();
+ $this->mContent = $revision->revText(); // Loads if user is allowed
$this->mUser = $revision->getUser();
$this->mUserText = $revision->getUserText();
global $wgUser, $wgOut, $wgRequest, $wgContLang;
global $wgEnableParserCache, $wgStylePath, $wgParser;
global $wgUseTrackbacks, $wgNamespaceRobotPolicies, $wgArticleRobotPolicies;
+ global $wgDefaultRobotPolicy;
$sk = $wgUser->getSkin();
wfProfileIn( __METHOD__ );
# Honour customised robot policies for this namespace
$policy = $wgNamespaceRobotPolicies[$ns];
} else {
- # Default to encourage indexing and following links
- $policy = 'index,follow';
+ $policy = $wgDefaultRobotPolicy;
}
$wgOut->setRobotPolicy( $policy );
$this->setOldSubtitle( isset($this->mOldId) ? $this->mOldId : $oldid );
if( $this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
if( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) {
- $wgOut->addWikiText( wfMsg( 'rev-deleted-text-permission' ) );
+ $wgOut->addWikiMsg( 'rev-deleted-text-permission' );
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
return;
} else {
- $wgOut->addWikiText( wfMsg( 'rev-deleted-text-view' ) );
+ $wgOut->addWikiMsg( 'rev-deleted-text-view' );
// and we are allowed to see...
}
}
# check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
if( $ns == NS_USER_TALK &&
User::isIP( $this->mTitle->getText() ) ) {
- $wgOut->addWikiText( wfMsg('anontalkpagetext') );
+ $wgOut->addWikiMsg('anontalkpagetext');
}
# If we have been passed an &rcid= parameter, we want to give the user a
# chance to mark this new article as patrolled.
- if (!is_null( $rcid ) && $rcid != 0 && $wgUser->isAllowed( 'patrol' ) ) {
+ if( !is_null( $rcid ) && $rcid != 0 && $wgUser->isAllowed( 'patrol' ) && $this->mTitle->exists() ) {
$wgOut->addHTML(
"<div class='patrollink'>" .
wfMsgHtml( 'markaspatrolledlink',
$o->tb_name,
$rmvtxt);
}
- $wgOut->addWikitext(wfMsg('trackbackbox', $tbtext));
+ $wgOut->addWikiMsg( 'trackbackbox', $tbtext );
}
function deletetrackback() {
global $wgUser, $wgRequest, $wgOut, $wgTitle;
if (!$wgUser->matchEditToken($wgRequest->getVal('token'))) {
- $wgOut->addWikitext(wfMsg('sessionfailure'));
+ $wgOut->addWikiMsg( 'sessionfailure' );
return;
}
$db = wfGetDB(DB_MASTER);
$db->delete('trackbacks', array('tb_id' => $wgRequest->getInt('tbid')));
$wgTitle->invalidateCache();
- $wgOut->addWikiText(wfMsg('trackbackdeleteok'));
+ $wgOut->addWikiMsg('trackbackdeleteok');
}
function render() {
$update = SquidUpdate::newSimplePurge( $this->mTitle );
$update->doUpdate();
}
+ if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ global $wgMessageCache;
+ if ( $this->getID() == 0 ) {
+ $text = false;
+ } else {
+ $text = $this->getContent();
+ }
+ $wgMessageCache->replace( $this->mTitle->getDBkey(), $text );
+ }
$this->view();
}
$result = $dbw->affectedRows() != 0;
if ($result) {
- // FIXME: Should the result from updateRedirectOn() be returned instead?
$this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
}
/**
* @deprecated use Article::doEdit()
*/
- function insertNewArticle( $text, $summary, $isminor, $watchthis, $suppressRC=false, $comment=false ) {
+ function insertNewArticle( $text, $summary, $isminor, $watchthis, $suppressRC=false, $comment=false, $bot=false ) {
$flags = EDIT_NEW | EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY |
( $isminor ? EDIT_MINOR : 0 ) |
- ( $suppressRC ? EDIT_SUPPRESS_RC : 0 );
+ ( $suppressRC ? EDIT_SUPPRESS_RC : 0 ) |
+ ( $bot ? EDIT_FORCE_BOT : 0 );
# If this is a comment, add the summary as headline
if ( $comment && $summary != "" ) {
# Silently ignore EDIT_MINOR if not allowed
$isminor = ( $flags & EDIT_MINOR ) && $wgUser->isAllowed('minoredit');
- $bot = $wgUser->isAllowed( 'bot' ) || ( $flags & EDIT_FORCE_BOT );
+ $bot = $flags & EDIT_FORCE_BOT;
$oldtext = $this->getContent();
$oldsize = strlen( $oldtext );
if ($flags & EDIT_AUTOSUMMARY && $summary == '')
$summary = $this->getAutosummary( $oldtext, $text, $flags );
- $text = $this->preSaveTransform( $text );
+ $editInfo = $this->prepareTextForEdit( $text );
+ $text = $editInfo->pst;
$newsize = strlen( $text );
$dbw = wfGetDB( DB_MASTER );
$lastRevision = 0;
$revisionId = 0;
+
+ $changed = ( strcmp( $text, $oldtext ) != 0 );
- if ( 0 != strcmp( $text, $oldtext ) ) {
+ if ( $changed ) {
$this->mGoodAdjustment = (int)$this->isCountable( $text )
- (int)$this->isCountable( $oldtext );
$this->mTotalAdjustment = 0;
# Invalidate cache of this article and all pages using this article
# as a template. Partly deferred.
Article::onArticleEdit( $this->mTitle );
-
+
# Update links tables, site stats, etc.
- $changed = ( strcmp( $oldtext, $text ) != 0 );
$this->editUpdates( $text, $summary, $isminor, $now, $revisionId, $changed );
}
} else {
*
* @param boolean $noRedir Add redirect=no
* @param string $sectionAnchor section to redirect to, including "#"
+ * @param string $extraq, extra query params
*/
function doRedirect( $noRedir = false, $sectionAnchor = '', $extraq = '' ) {
global $wgOut;
# Check patrol config options
if ( !($wgUseNPPatrol || $wgUseRCPatrol)) {
- $wgOut->errorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
+ $wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
return;
}
$rc = $rcid ? RecentChange::newFromId($rcid) : null;
if ( is_null ( $rc ) )
{
- $wgOut->errorPage( 'markedaspatrollederror', 'markedaspatrollederrortext' );
+ $wgOut->showErrorPage( 'markedaspatrollederror', 'markedaspatrollederrortext' );
return;
}
- if ( $rc->mAttribs['rc_type'] == RC_NEW && !$wgUseNPPatrol ) {
- $wgOut->errorpage( 'nppatroldisabled', 'nppatroldisabledtext' );
- return;
- }
-
- if ( !$wgUseRCPatrol && $rc->mAttribs['rc_type'] != RC_NEW) {
- $wgOut->errorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
+ if ( !$wgUseRCPatrol && $rc->getAttribute( 'rc_type' ) != RC_NEW) {
+ // Only new pages can be patrolled if the general patrolling is off....???
+ // @fixme -- is this necessary? Shouldn't we only bother controlling the
+ // front end here?
+ $wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
return;
}
}
#It would be nice to see where the user had actually come from, but for now just guess
- $returnto = $rc->mAttribs['rc_type'] == RC_NEW ? 'Newpages' : 'Recentchanges';
+ $returnto = $rc->getAttribute( 'rc_type' ) == RC_NEW ? 'Newpages' : 'Recentchanges';
$return = Title::makeTitle( NS_SPECIAL, $returnto );
# If it's left up to us, check that the user is allowed to patrol this edit
# The user made this edit, and can't patrol it
# Tell them so, and then back off
$wgOut->setPageTitle( wfMsg( 'markedaspatrollederror' ) );
- $wgOut->addWikiText( wfMsgNoTrans( 'markedaspatrollederror-noautopatrol' ) );
+ $wgOut->addWikiMsg( 'markedaspatrollederror-noautopatrol' );
$wgOut->returnToMain( false, $return );
return;
}
}
- # Mark the edit as patrolled
- RecentChange::markPatrolled( $rcid );
- PatrolLog::record( $rcid );
- wfRunHooks( 'MarkPatrolledComplete', array( &$rcid, &$wgUser, false ) );
+ # Check that the revision isn't patrolled already
+ # Prevents duplicate log entries
+ if( !$rc->getAttribute( 'rc_patrolled' ) ) {
+ # Mark the edit as patrolled
+ RecentChange::markPatrolled( $rcid );
+ PatrolLog::record( $rcid );
+ wfRunHooks( 'MarkPatrolledComplete', array( &$rcid, &$wgUser, false ) );
+ }
# Inform the user
$wgOut->setPageTitle( wfMsg( 'markedaspatrolled' ) );
- $wgOut->addWikiText( wfMsgNoTrans( 'markedaspatrolledtext' ) );
+ $wgOut->addWikiMsg( 'markedaspatrolledtext' );
$wgOut->returnToMain( false, $return );
}
$wgOut->setPagetitle( wfMsg( 'addedwatch' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
- $link = wfEscapeWikiText( $this->mTitle->getPrefixedText() );
- $text = wfMsg( 'addedwatchtext', $link );
- $wgOut->addWikiText( $text );
+ $wgOut->addWikiMsg( 'addedwatchtext', $this->mTitle->getPrefixedText() );
}
$wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
$wgOut->setPagetitle( wfMsg( 'removedwatch' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
- $link = wfEscapeWikiText( $this->mTitle->getPrefixedText() );
- $text = wfMsg( 'removedwatchtext', $link );
- $wgOut->addWikiText( $text );
+ $wgOut->addWikiMsg( 'removedwatchtext', $this->mTitle->getPrefixedText() );
}
$wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
global $wgUser, $wgRestrictionTypes, $wgContLang;
$id = $this->mTitle->getArticleID();
- if( !$wgUser->isAllowed( 'protect' ) || wfReadOnly() || $id == 0 ) {
+ if( array() != $this->mTitle->getUserPermissionsErrors( 'protect', $wgUser ) || wfReadOnly() || $id == 0 ) {
return false;
}
$expiry_description = '';
if ( $encodedExpiry != 'infinity' ) {
- $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ) ).')';
+ $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry, false, false ) ).')';
}
# Prepare a null revision to be added to the history
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 = ( $cascade && isset($wgGroupPermissions[$restrictions]['protect']) &&
+ $wgGroupPermissions[$restrictions]['protect'] );
}
$cascade_description = '';
$comment .= "$expiry_description";
if ( $cascade )
$comment .= "$cascade_description";
-
- $nullRevision = Revision::newNullRevision( $dbw, $id, $comment, true );
- $nullRevId = $nullRevision->insertOn( $dbw );
-
+
# Update restrictions table
foreach( $limit as $action => $restrictions ) {
if ($restrictions != '' ) {
}
}
+ # Insert a null revision
+ $nullRevision = Revision::newNullRevision( $dbw, $id, $comment, true );
+ $nullRevId = $nullRevision->insertOn( $dbw );
+
# Update page record
$dbw->update( 'page',
array( /* SET */
# Update the protection log
$log = new LogPage( 'protect' );
+
+
if( $protect ) {
$log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
}
return implode( ':', $bits );
}
+
+ /**
+ * Auto-generates a deletion reason
+ * @param bool &$hasHistory Whether the page has a history
+ */
+ public function generateReason(&$hasHistory)
+ {
+ global $wgContLang;
+ $dbw = wfGetDB(DB_MASTER);
+ // Get the last revision
+ $rev = Revision::newFromTitle($this->mTitle);
+ if(is_null($rev))
+ return false;
+ // Get the article's contents
+ $contents = $rev->getText();
+ $blank = false;
+ // If the page is blank, use the text from the previous revision,
+ // which can only be blank if there's a move/import/protect dummy revision involved
+ if($contents == '')
+ {
+ $prev = $rev->getPrevious();
+ if($prev)
+ {
+ $contents = $prev->getText();
+ $blank = true;
+ }
+ }
+
+ // Find out if there was only one contributor
+ // Only scan the last 20 revisions
+ $limit = 20;
+ $res = $dbw->select('revision', 'rev_user_text', array('rev_page' => $this->getID()), __METHOD__,
+ array('LIMIT' => $limit));
+ if($res === false)
+ // This page has no revisions, which is very weird
+ return false;
+ if($res->numRows() > 1)
+ $hasHistory = true;
+ else
+ $hasHistory = false;
+ $row = $dbw->fetchObject($res);
+ $onlyAuthor = $row->rev_user_text;
+ // Try to find a second contributor
+ while( $row = $dbw->fetchObject($res) ) {
+ if($row->rev_user_text != $onlyAuthor) {
+ $onlyAuthor = false;
+ break;
+ }
+ }
+ $dbw->freeResult($res);
+
+ // Generate the summary with a '$1' placeholder
+ if($blank) {
+ // The current revision is blank and the one before is also
+ // blank. It's just not our lucky day
+ $reason = wfMsgForContent('exbeforeblank', '$1');
+ } else {
+ if($onlyAuthor)
+ $reason = wfMsgForContent('excontentauthor', '$1', $onlyAuthor);
+ else
+ $reason = wfMsgForContent('excontent', '$1');
+ }
+
+ // Replace newlines with spaces to prevent uglyness
+ $contents = preg_replace("/[\n\r]/", ' ', $contents);
+ // Calculate the maximum amount of chars to get
+ // Max content length = max comment length - length of the comment (excl. $1) - '...'
+ $maxLength = 255 - (strlen($reason) - 2) - 3;
+ $contents = $wgContLang->truncate($contents, $maxLength, '...');
+ // Remove possible unfinished links
+ $contents = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $contents );
+ // Now replace the '$1' placeholder
+ $reason = str_replace( '$1', $contents, $reason );
+ return $reason;
+ }
+
/*
* UI entry point for page deletion
*/
function delete() {
global $wgUser, $wgOut, $wgRequest;
+
$confirm = $wgRequest->wasPosted() &&
- $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
- $reason = $wgRequest->getText( 'wpReason' );
+ $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
+
+ $this->DeleteReasonList = $wgRequest->getText( 'wpDeleteReasonList', 'other' );
+ $this->DeleteReason = $wgRequest->getText( 'wpReason' );
+
+ $reason = $this->DeleteReasonList;
+
+ if ( $reason != 'other' && $this->DeleteReason != '') {
+ // Entry from drop down menu + additional comment
+ $reason .= ': ' . $this->DeleteReason;
+ } elseif ( $reason == 'other' ) {
+ $reason = $this->DeleteReason;
+ }
+ # Flag to hide all contents of the archived revisions
+ $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed('deleterevision');
# This code desperately needs to be totally rewritten
+ # Read-only check...
+ if ( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ }
+
# Check permissions
$permission_errors = $this->mTitle->getUserPermissionsErrors( 'delete', $wgUser );
- if (count($permission_errors)>0)
- {
+ if (count($permission_errors)>0) {
$wgOut->showPermissionsErrorPage( $permission_errors );
return;
}
- $wgOut->setPagetitle( wfMsg( 'confirmdelete' ) );
+ $wgOut->setPagetitle( wfMsg( 'delete-confirm', $this->mTitle->getPrefixedText() ) );
# Better double-check that it hasn't been deleted yet!
$dbw = wfGetDB( DB_MASTER );
return;
}
+ # Hack for big sites
+ $bigHistory = $this->isBigDeletion();
+ if( $bigHistory && !$this->mTitle->userCan( 'bigdelete' ) ) {
+ global $wgLang, $wgDeleteRevisionsLimit;
+ $wgOut->wrapWikiMsg( "<div class='error'>\n$1</div>\n",
+ array( 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
+ return;
+ }
+
if( $confirm ) {
- $this->doDelete( $reason );
+ $this->doDelete( $reason, $suppress );
if( $wgRequest->getCheck( 'wpWatch' ) ) {
$this->doWatch();
} elseif( $this->mTitle->userIsWatching() ) {
return;
}
- # determine whether this page has earlier revisions
- # and insert a warning if it does
- $maxRevisions = 20;
- $authors = $this->getLastNAuthors( $maxRevisions, $latest );
+ // Generate deletion reason
+ $hasHistory = false;
+ if ( !$reason ) $reason = $this->generateReason($hasHistory);
- if( count( $authors ) > 1 && !$confirm ) {
+ // If the page has a history, insert a warning
+ if( $hasHistory && !$confirm ) {
$skin=$wgUser->getSkin();
$wgOut->addHTML( '<strong>' . wfMsg( 'historywarning' ) . ' ' . $skin->historyLink() . '</strong>' );
- }
-
- # If a single user is responsible for all revisions, find out who they are
- if ( count( $authors ) == $maxRevisions ) {
- // Query bailed out, too many revisions to find out if they're all the same
- $authorOfAll = false;
- } else {
- $authorOfAll = reset( $authors );
- foreach ( $authors as $author ) {
- if ( $authorOfAll != $author ) {
- $authorOfAll = false;
- break;
- }
- }
- }
- # Fetch article text
- $rev = Revision::newFromTitle( $this->mTitle );
-
- if( !is_null( $rev ) ) {
- # if this is a mini-text, we can paste part of it into the deletion reason
- $text = $rev->getText();
-
- #if this is empty, an earlier revision may contain "useful" text
- $blanked = false;
- if( $text == '' ) {
- $prev = $rev->getPrevious();
- if( $prev ) {
- $text = $prev->getText();
- $blanked = true;
- }
- }
-
- $length = strlen( $text );
-
- # this should not happen, since it is not possible to store an empty, new
- # page. Let's insert a standard text in case it does, though
- if( $length == 0 && $reason === '' ) {
- $reason = wfMsgForContent( 'exblank' );
- }
-
- if( $reason === '' ) {
- # comment field=255, let's grep the first 150 to have some user
- # space left
- global $wgContLang;
- $text = $wgContLang->truncate( $text, 150, '...' );
-
- # let's strip out newlines
- $text = preg_replace( "/[\n\r]/", '', $text );
-
- if( !$blanked ) {
- if( $authorOfAll === false ) {
- $reason = wfMsgForContent( 'excontent', $text );
- } else {
- $reason = wfMsgForContent( 'excontentauthor', $text, $authorOfAll );
- }
- } else {
- $reason = wfMsgForContent( 'exbeforeblank', $text );
- }
+ if( $bigHistory ) {
+ global $wgLang, $wgDeleteRevisionsLimit;
+ $wgOut->wrapWikiMsg( "<div class='error'>\n$1</div>\n",
+ array( 'delete-warning-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
}
}
-
+
return $this->confirmDelete( '', $reason );
}
+
+ /**
+ * @return bool whether or not the page surpasses $wgDeleteRevisionsLimit revisions
+ */
+ function isBigDeletion() {
+ global $wgDeleteRevisionsLimit;
+ if( $wgDeleteRevisionsLimit ) {
+ $revCount = $this->estimateRevisionCount();
+ return $revCount > $wgDeleteRevisionsLimit;
+ }
+ return false;
+ }
+
+ /**
+ * @return int approximate revision count
+ */
+ function estimateRevisionCount() {
+ $dbr = wfGetDB();
+ // For an exact count...
+ //return $dbr->selectField( 'revision', 'COUNT(*)',
+ // array( 'rev_page' => $this->getId() ), __METHOD__ );
+ return $dbr->estimateRowCount( 'revision', '*',
+ array( 'rev_page' => $this->getId() ), __METHOD__ );
+ }
/**
* Get the last N authors
/**
* Output deletion confirmation dialog
+ * @param $par string FIXME: do we need this parameter? One Call from Article::delete with '' only.
+ * @param $reason string Prefilled reason
*/
function confirmDelete( $par, $reason ) {
- global $wgOut, $wgUser;
+ global $wgOut, $wgUser, $wgContLang;
+ $align = $wgContLang->isRtl() ? 'left' : 'right';
wfDebug( "Article::confirmDelete\n" );
- $sub = htmlspecialchars( $this->mTitle->getPrefixedText() );
- $wgOut->setSubtitle( wfMsg( 'deletesub', $sub ) );
+ $wgOut->setSubtitle( wfMsg( 'delete-backlink', $wgUser->getSkin()->makeKnownLinkObj( $this->mTitle ) ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
- $wgOut->addWikiText( wfMsg( 'confirmdeletetext' ) );
-
- $formaction = $this->mTitle->escapeLocalURL( 'action=delete' . $par );
-
- $confirm = htmlspecialchars( wfMsg( 'deletepage' ) );
- $delcom = htmlspecialchars( wfMsg( 'deletecomment' ) );
- $token = htmlspecialchars( $wgUser->editToken() );
- $watch = Xml::checkLabel( wfMsg( 'watchthis' ), 'wpWatch', 'wpWatch', $wgUser->getBoolOption( 'watchdeletion' ) || $this->mTitle->userIsWatching(), array( 'tabindex' => '2' ) );
-
- $wgOut->addHTML( "
-<form id='deleteconfirm' method='post' action=\"{$formaction}\">
- <table border='0'>
- <tr>
- <td align='right'>
- <label for='wpReason'>{$delcom}:</label>
- </td>
- <td align='left'>
- <input type='text' size='60' name='wpReason' id='wpReason' value=\"" . htmlspecialchars( $reason ) . "\" tabindex=\"1\" />
- </td>
- </tr>
- <tr>
- <td> </td>
- <td>$watch</td>
- </tr>
- <tr>
- <td> </td>
- <td>
- <input type='submit' name='wpConfirmB' id='wpConfirmB' value=\"{$confirm}\" tabindex=\"3\" />
- </td>
- </tr>
- </table>
- <input type='hidden' name='wpEditToken' value=\"{$token}\" />
-</form>\n" );
+ $wgOut->addWikiMsg( 'confirmdeletetext' );
- $wgOut->returnToMain( false, $this->mTitle );
+ if( $wgUser->isAllowed( 'deleterevision' ) ) {
+ $suppress = "<tr id=\"wpDeleteSuppressRow\" name=\"wpDeleteSuppressRow\"><td></td><td>";
+ $suppress .= Xml::checkLabel( wfMsg( 'revdelete-suppress' ), 'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '2' ) );
+ $suppress .= "</td></tr>";
+ } else {
+ $suppress = '';
+ }
+
+ $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->mTitle->getLocalURL( 'action=delete' . $par ), 'id' => 'deleteconfirm' ) ) .
+ Xml::openElement( 'fieldset', array( 'id' => 'mw-delete-table' ) ) .
+ Xml::element( 'legend', null, wfMsg( 'delete-legend' ) ) .
+ Xml::openElement( 'table' ) .
+ "<tr id=\"wpDeleteReasonListRow\">
+ <td align='$align'>" .
+ Xml::label( wfMsg( 'deletecomment' ), 'wpDeleteReasonList' ) .
+ "</td>
+ <td>" .
+ Xml::listDropDown( 'wpDeleteReasonList',
+ wfMsgForContent( 'deletereason-dropdown' ),
+ wfMsgForContent( 'deletereasonotherlist' ), '', 'wpReasonDropDown', 1 ) .
+ "</td>
+ </tr>
+ <tr id=\"wpDeleteReasonRow\">
+ <td align='$align'>" .
+ Xml::label( wfMsg( 'deleteotherreason' ), 'wpReason' ) .
+ "</td>
+ <td>" .
+ Xml::input( 'wpReason', 60, $reason, array( 'type' => 'text', 'maxlength' => '255', 'tabindex' => '2', 'id' => 'wpReason' ) ) .
+ "</td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>" .
+ Xml::checkLabel( wfMsg( 'watchthis' ), 'wpWatch', 'wpWatch', $wgUser->getBoolOption( 'watchdeletion' ) || $this->mTitle->userIsWatching(), array( 'tabindex' => '3' ) ) .
+ "</td>
+ </tr>
+ $suppress
+ <tr>
+ <td></td>
+ <td>" .
+ Xml::submitButton( wfMsg( 'deletepage' ), array( 'name' => 'wpConfirmB', 'id' => 'wpConfirmB', 'tabindex' => '4' ) ) .
+ "</td>
+ </tr>" .
+ Xml::closeElement( 'table' ) .
+ Xml::closeElement( 'fieldset' ) .
+ Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
+ Xml::closeElement( 'form' );
+
+ if ( $wgUser->isAllowed( 'editinterface' ) ) {
+ $skin = $wgUser->getSkin();
+ $link = $skin->makeLink ( 'MediaWiki:Deletereason-dropdown', wfMsgHtml( 'delete-edit-reasonlist' ) );
+ $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
+ }
+ $wgOut->addHTML( $form );
$this->showLogExtract( $wgOut );
}
* Show relevant lines from the deletion log
*/
function showLogExtract( $out ) {
- $out->addHtml( '<h2>' . htmlspecialchars( LogPage::logName( 'delete' ) ) . '</h2>' );
+ $out->addHtml( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
$logViewer = new LogViewer(
new LogReader(
new FauxRequest(
/**
* Perform a deletion and output success or failure messages
*/
- function doDelete( $reason ) {
+ function doDelete( $reason, $suppress = false ) {
global $wgOut, $wgUser;
wfDebug( __METHOD__."\n" );
if (wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason))) {
- if ( $this->doDeleteArticle( $reason ) ) {
- $deleted = wfEscapeWikiText( $this->mTitle->getPrefixedText() );
+ if ( $this->doDeleteArticle( $reason, $suppress ) ) {
+ $deleted = $this->mTitle->getPrefixedText();
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
- $loglink = '[[Special:Log/delete|' . wfMsg( 'deletionlog' ) . ']]';
- $text = wfMsg( 'deletedtext', $deleted, $loglink );
+ $loglink = '[[Special:Log/delete|' . wfMsgNoTrans( 'deletionlog' ) . ']]';
- $wgOut->addWikiText( $text );
+ $wgOut->addWikiMsg( 'deletedtext', $deleted, $loglink );
$wgOut->returnToMain( false );
wfRunHooks('ArticleDeleteComplete', array(&$this, &$wgUser, $reason));
} else {
- $wgOut->showFatalError( wfMsg( 'cannotdelete' ) );
+ $wgOut->showFatalError( wfMsg( 'cannotdelete' ).'<br/>'.wfMsg('cannotdelete-merge') );
}
}
}
* Deletes the article with database consistency, writes logs, purges caches
* Returns success
*/
- function doDeleteArticle( $reason ) {
+ function doDeleteArticle( $reason, $suppress = false ) {
global $wgUseSquid, $wgDeferredUpdateList;
global $wgUseTrackbacks;
$u = new SiteStatsUpdate( 0, 1, -(int)$this->isCountable( $this->getContent() ), -1 );
array_push( $wgDeferredUpdateList, $u );
+ // Bitfields to further suppress the content
+ if ( $suppress ) {
+ $bitfield = 0;
+ // This should be 15...
+ $bitfield |= Revision::DELETED_TEXT;
+ $bitfield |= Revision::DELETED_COMMENT;
+ $bitfield |= Revision::DELETED_USER;
+ $bitfield |= Revision::DELETED_RESTRICTED;
+ } else {
+ $bitfield = 'rev_deleted';
+ }
+
// For now, shunt the revision data into the archive table.
// Text is *not* removed from the text table; bulk storage
// is left intact to avoid breaking block-compression or
'ar_text_id' => 'rev_text_id',
'ar_text' => '\'\'', // Be explicit to appease
'ar_flags' => '\'\'', // MySQL's "strict mode"...
- 'ar_len' => 'rev_len',
+ 'ar_len' => 'rev_len',
'ar_page_id' => 'page_id',
+ 'ar_deleted' => $bitfield
), array(
'page_id' => $id,
'page_id = rev_page'
# Delete restrictions for it
$dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ );
+ # Fix category table counts
+ $cats = array();
+ $res = $dbw->select( 'categorylinks', 'cl_to',
+ array( 'cl_from' => $id ), __METHOD__ );
+ foreach( $res as $row ) {
+ $cats []= $row->cl_to;
+ }
+ $this->updateCategoryCounts( array(), $cats );
+
# Now that it's safely backed up, delete it
$dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__);
# If using cascading deletes, we can skip some explicit deletes
if ( !$dbw->cascadingDeletes() ) {
-
$dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
if ($wgUseTrackbacks)
# Clear caches
Article::onArticleDelete( $this->mTitle );
- # Log the deletion
- $log = new LogPage( 'delete' );
+ # Log the deletion, if the page was suppressed, log it at Oversight instead
+ $logtype = $suppress ? 'oversight' : 'delete';
+ $log = new LogPage( $logtype );
$log->addEntry( 'delete', $this->mTitle, $reason );
# Clear the cached article id so the interface doesn't act like we exist
/**
* 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
+ * roll back to, e.g. user is the sole contributor. This function
+ * performs permissions checks on $wgUser, then calls commitRollback()
+ * to do the dirty work
*
* @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 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)
+ * @param array $resultDetails contains result-specific array of additional values
+ * 'alreadyrolled' : 'current' (rev)
+ * success : 'summary' (str), 'current' (rev), 'target' (rev)
*
- * @return self::SUCCESS on succes, self::* on failure
+ * @return array of errors, each error formatted as
+ * array(messagekey, param1, param2, ...).
+ * On success, the array is empty. This array can also be passed to
+ * OutputPage::showPermissionsErrorPage().
*/
public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails ) {
- global $wgUser, $wgUseRCPatrol;
+ global $wgUser;
$resultDetails = null;
- # Just in case it's being called from elsewhere
-
- if( $wgUser->isAllowed( 'rollback' ) && $this->mTitle->userCan( 'edit' ) ) {
- if( $wgUser->isBlocked() ) {
- return self::BLOCKED;
- }
- } else {
- return self::PERM_DENIED;
- }
-
- if ( wfReadOnly() ) {
- return self::READONLY;
- }
-
+ # Check permissions
+ $errors = array_merge( $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser ),
+ $this->mTitle->getUserPermissionsErrors( 'rollback', $wgUser ) );
if( !$wgUser->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) )
- return self::BAD_TOKEN;
+ $errors[] = array( 'sessionfailure' );
+ if ( $wgUser->pingLimiter('rollback') || $wgUser->pingLimiter() ) {
+ $errors[] = array( 'actionthrottledtext' );
+ }
+ # If there were errors, bail out now
+ if(!empty($errors))
+ return $errors;
+
+ return $this->commitRollback($fromP, $summary, $bot, $resultDetails);
+ }
+
+ /**
+ * Backend implementation of doRollback(), please refer there for parameter
+ * and return value documentation
+ *
+ * NOTE: This function does NOT check ANY permissions, it just commits the
+ * rollback to the DB Therefore, you should only call this function direct-
+ * ly if you want to use custom permissions checks. If you don't, use
+ * doRollback() instead.
+ */
+ public function commitRollback($fromP, $summary, $bot, &$resultDetails) {
+ global $wgUseRCPatrol, $wgUser;
$dbw = wfGetDB( DB_MASTER );
+ if( wfReadOnly() ) {
+ return array( array( 'readonlytext' ) );
+ }
+
# Get the last editor
$current = Revision::newFromTitle( $this->mTitle );
if( is_null( $current ) ) {
# Something wrong... no page?
- return self::BAD_TITLE;
+ return array(array('notanarticle'));
}
$from = str_replace( '_', ' ', $fromP );
if( $from != $current->getUserText() ) {
$resultDetails = array( 'current' => $current );
- return self::ALREADY_ROLLED;
+ return array(array('alreadyrolled',
+ htmlspecialchars($this->mTitle->getPrefixedText()),
+ htmlspecialchars($fromP),
+ htmlspecialchars($current->getUserText())
+ ));
}
# Get the last edit not by this guy
$user = intval( $current->getUser() );
$user_text = $dbw->addQuotes( $current->getUserText() );
$s = $dbw->selectRow( 'revision',
- array( 'rev_id', 'rev_timestamp' ),
- array(
- 'rev_page' => $current->getPage(),
+ array( 'rev_id', 'rev_timestamp', 'rev_deleted' ),
+ array( 'rev_page' => $current->getPage(),
"rev_user <> {$user} OR rev_user_text <> {$user_text}"
), __METHOD__,
- array(
- 'USE INDEX' => 'page_timestamp',
+ array( 'USE INDEX' => 'page_timestamp',
'ORDER BY' => 'rev_timestamp DESC' )
);
if( $s === false ) {
- # Something wrong
- return self::ONLY_AUTHOR;
+ # No one else ever edited this page
+ return array(array('cantrollback'));
+ } else if( $s->rev_deleted & REVISION::DELETED_TEXT || $s->rev_deleted & REVISION::DELETED_USER ) {
+ # Only admins can see this text
+ return array(array('notvisiblerev'));
}
$set = array();
- if ( $bot ) {
+ if ( $bot && $wgUser->isAllowed('markbotedits') ) {
# Mark all reverted edits as bot
$set['rc_bot'] = 1;
}
);
}
- # Get the edit summary
+ # Generate the edit summary if necessary
$target = Revision::newFromId( $s->rev_id );
if( empty( $summary ) )
- $summary = wfMsgForContent( 'revertpage', $target->getUserText(), $from );
+ {
+ global $wgLang;
+ $summary = wfMsgForContent( 'revertpage',
+ $target->getUserText(), $from,
+ $s->rev_id, $wgLang->timeanddate(wfTimestamp(TS_MW, $s->rev_timestamp), true),
+ $current->getId(), $wgLang->timeanddate($current->getTimestamp())
+ );
+ }
# Save
- $flags = EDIT_UPDATE | EDIT_MINOR;
- if( $bot )
+ $flags = EDIT_UPDATE;
+
+ if ($wgUser->isAllowed('minoredit'))
+ $flags |= EDIT_MINOR;
+
+ if( $bot && ($wgUser->isAllowed('markbotedits') || $wgUser->isAllowed('bot')) )
$flags |= EDIT_FORCE_BOT;
$this->doEdit( $target->getText(), $summary, $flags );
'current' => $current,
'target' => $target,
);
- return self::SUCCESS;
+ return array();
}
/**
*/
function rollback() {
global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
-
$details = null;
- # Skip the permissions-checking in doRollback() itself, by checking permissions here.
-
- $perm_errors = array_merge( $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser ),
- $this->mTitle->getUserPermissionsErrors( 'rollback', $wgUser ) );
-
- if (count($perm_errors)) {
- $wgOut->showPermissionsErrorPage( $perm_errors );
- return;
- }
-
$result = $this->doRollback(
$wgRequest->getVal( 'from' ),
$wgRequest->getText( 'summary' ),
$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:
+ if( in_array( array( 'blocked' ), $result ) ) {
+ $wgOut->blockedPage();
+ return;
+ }
+ if( in_array( array( 'actionthrottledtext' ), $result ) ) {
+ $wgOut->rateLimited();
+ return;
+ }
+ if( isset( $result[0][0] ) && ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' ) ){
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $errArray = $result[0];
+ $errMsg = array_shift( $errArray );
+ $wgOut->addWikiMsgArray( $errMsg, $errArray );
+ if( isset( $details['current'] ) ){
$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() ) ) );
+ $wgOut->addWikiMsgArray( 'editcomment', array( $wgUser->getSkin()->formatComment( $current->getComment() ) ), array( 'replaceafter' ) );
}
- 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 `{$result}`" );
+ }
+ return;
+ }
+ # Display permissions errors before read-only message -- there's no
+ # point in misleading the user into thinking the inability to rollback
+ # is only temporary.
+ if( !empty($result) && $result !== array( array('readonlytext') ) ) {
+ # array_diff is completely broken for arrays of arrays, sigh. Re-
+ # move any 'readonlytext' error manually.
+ $out = array();
+ foreach( $result as $error ) {
+ if( $error != array( 'readonlytext' ) ) {
+ $out []= $error;
+ }
+ }
+ $wgOut->showPermissionsErrorPage( $out );
+ return;
+ }
+ if( $result == array( array('readonlytext') ) ) {
+ $wgOut->readOnlyPage();
+ return;
}
+ $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 );
}
$wgUser->clearNotification( $this->mTitle );
}
+ /**
+ * Prepare text which is about to be saved.
+ * Returns a stdclass with source, pst and output members
+ */
+ function prepareTextForEdit( $text, $revid=null ) {
+ if ( $this->mPreparedEdit && $this->mPreparedEdit->newText == $text && $this->mPreparedEdit->revid == $revid) {
+ // Already prepared
+ return $this->mPreparedEdit;
+ }
+ global $wgParser;
+ $edit = (object)array();
+ $edit->revid = $revid;
+ $edit->newText = $text;
+ $edit->pst = $this->preSaveTransform( $text );
+ $options = new ParserOptions;
+ $options->setTidy( true );
+ $options->enableLimitReport();
+ $edit->output = $wgParser->parse( $edit->pst, $this->mTitle, $options, true, true, $revid );
+ $edit->oldText = $this->getContent();
+ $this->mPreparedEdit = $edit;
+ return $edit;
+ }
+
/**
* Do standard deferred updates after page edit.
* Update links tables, site stats, search index and message cache.
* @param $changed Whether or not the content actually changed
*/
function editUpdates( $text, $summary, $minoredit, $timestamp_of_pagechange, $newid, $changed = true ) {
- global $wgDeferredUpdateList, $wgMessageCache, $wgUser, $wgParser;
+ global $wgDeferredUpdateList, $wgMessageCache, $wgUser, $wgParser, $wgEnableParserCache;
wfProfileIn( __METHOD__ );
# Parse the text
- $options = new ParserOptions;
- $options->setTidy(true);
- $poutput = $wgParser->parse( $text, $this->mTitle, $options, true, true, $newid );
+ # Be careful not to double-PST: $text is usually already PST-ed once
+ if ( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
+ wfDebug( __METHOD__ . ": No prepared edit or vary-revision is set...\n" );
+ $editInfo = $this->prepareTextForEdit( $text, $newid );
+ } else {
+ wfDebug( __METHOD__ . ": No vary-revision, using prepared edit...\n" );
+ $editInfo = $this->mPreparedEdit;
+ }
# Save it to the parser cache
- $parserCache =& ParserCache::singleton();
- $parserCache->save( $poutput, $this, $wgUser );
+ if ( $wgEnableParserCache ) {
+ $parserCache =& ParserCache::singleton();
+ $parserCache->save( $editInfo->output, $this, $wgUser );
+ }
# Update the links tables
- $u = new LinksUpdate( $this->mTitle, $poutput );
+ $u = new LinksUpdate( $this->mTitle, $editInfo->output );
$u->doUpdate();
if( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
$sk = $wgUser->getSkin();
$lnk = $current
? wfMsg( 'currentrevisionlink' )
- : $lnk = $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'currentrevisionlink' ) );
+ : $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'currentrevisionlink' ) );
$curdiff = $current
? wfMsg( 'diff' )
: $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=cur&oldid='.$oldid );
? wfMsg( 'diff' )
: $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=next&oldid='.$oldid );
- $userlinks = $sk->userLink( $revision->getUser(), $revision->getUserText() )
- . $sk->userToolLinks( $revision->getUser(), $revision->getUserText() );
+ $cdel='';
+ if( $wgUser->isAllowed( 'deleterevision' ) ) {
+ $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+ if( $revision->isCurrent() ) {
+ // We don't handle top deleted edits too well
+ $cdel = wfMsgHtml('rev-delundel');
+ } else if( !$revision->userCan( Revision::DELETED_RESTRICTED ) ) {
+ // If revision was hidden from sysops
+ $cdel = wfMsgHtml('rev-delundel');
+ } else {
+ $cdel = $sk->makeKnownLinkObj( $revdel,
+ wfMsgHtml('rev-delundel'),
+ 'target=' . urlencode( $this->mTitle->getPrefixedDbkey() ) .
+ '&oldid=' . urlencode( $oldid ) );
+ // Bolden oversighted content
+ if( $revision->isDeleted( Revision::DELETED_RESTRICTED ) )
+ $cdel = "<strong>$cdel</strong>";
+ }
+ $cdel = "(<small>$cdel</small>) ";
+ }
+ # Show user links if allowed to see them. Normally they
+ # are hidden regardless, but since we can already see the text here...
+ $userlinks = $sk->revUserTools( $revision, false );
$m = wfMsg( 'revision-info-current' );
$infomsg = $current && !wfEmptyMsg( 'revision-info-current', $m ) && $m != '-'
: '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";
+
+ "\n\t\t\t\t<div id=\"mw-revision-nav\">" . $cdel . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
$wgOut->setSubtitle( $r );
}
$title->touchLinks();
$title->purgeSquid();
+ $title->deleteTitleProtection();
}
static function onArticleDelete( $title ) {
if( $title->getNamespace() == NS_MEDIAWIKI) {
$wgMessageCache->replace( $title->getDBkey(), false );
}
+ if( $title->getNamespace() == NS_IMAGE ) {
+ $update = new HTMLCacheUpdate( $title, 'imagelinks' );
+ $update->doUpdate();
+ }
}
/**
global $wgDeferredUpdateList, $wgUseFileCache;
// Invalidate caches of articles which include this page
- $update = new HTMLCacheUpdate( $title, 'templatelinks' );
- $wgDeferredUpdateList[] = $update;
+ $wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'templatelinks' );
+ // Invalidate the caches of all pages which redirect here
+ $wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'redirect' );
+
# Purge squid for this page only
$title->purgeSquid();
/**#@-*/
+ /**
+ * Overriden by ImagePage class, only present here to avoid a fatal error
+ * Called for ?action=revert
+ */
+ public function revert(){
+ global $wgOut;
+ $wgOut->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
+ }
+
/**
* Info about this page
* Called for ?action=info when $wgAllowPageInfo is on.
return $result;
}
+ /**
+ * Returns a list of hidden categories this page is a member of.
+ * Uses the page_props and categorylinks tables.
+ *
+ * @return array Array of Title objects
+ */
+ function getHiddenCategories() {
+ $result = array();
+ $id = $this->mTitle->getArticleID();
+ if( $id == 0 ) {
+ return array();
+ }
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( array( 'categorylinks', 'page_props', 'page' ),
+ array( 'cl_to' ),
+ array( 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat',
+ 'page_namespace' => NS_CATEGORY, 'page_title=cl_to'),
+ 'Article:getHiddenCategories' );
+ if ( false !== $res ) {
+ if ( $dbr->numRows( $res ) ) {
+ while ( $row = $dbr->fetchObject( $res ) ) {
+ $result[] = Title::makeTitle( NS_CATEGORY, $row->cl_to );
+ }
+ }
+ }
+ $dbr->freeResult( $res );
+ return $result;
+ }
+
/**
* Return an auto-generated summary if the text provided is a redirect.
*
* @param bool $cache
*/
public function outputWikiText( $text, $cache = true ) {
- global $wgParser, $wgUser, $wgOut;
+ global $wgParser, $wgUser, $wgOut, $wgEnableParserCache;
$popts = $wgOut->parserOptions();
$popts->setTidy(true);
+ $popts->enableLimitReport();
$parserOutput = $wgParser->parse( $text, $this->mTitle,
$popts, true, true, $this->getRevIdFetched() );
$popts->setTidy(false);
- if ( $cache && $this && $parserOutput->getCacheTime() != -1 ) {
+ $popts->enableLimitReport( false );
+ if ( $wgEnableParserCache && $cache && $this && $parserOutput->getCacheTime() != -1 ) {
$parserCache =& ParserCache::singleton();
$parserCache->save( $parserOutput, $this, $wgUser );
}
$wgOut->addParserOutput( $parserOutput );
}
+ /**
+ * Update all the appropriate counts in the category table, given that
+ * we've added the categories $added and deleted the categories $deleted.
+ *
+ * @param $added array The names of categories that were added
+ * @param $deleted array The names of categories that were deleted
+ * @return null
+ */
+ public function updateCategoryCounts( $added, $deleted ) {
+ $ns = $this->mTitle->getNamespace();
+ $dbw = wfGetDB( DB_MASTER );
+
+ # First make sure the rows exist. If one of the "deleted" ones didn't
+ # exist, we might legitimately not create it, but it's simpler to just
+ # create it and then give it a negative value, since the value is bogus
+ # anyway.
+ #
+ # Sometimes I wish we had INSERT ... ON DUPLICATE KEY UPDATE.
+ $insertCats = array_merge( $added, $deleted );
+ if( !$insertCats ) {
+ # Okay, nothing to do
+ return;
+ }
+ $insertRows = array();
+ foreach( $insertCats as $cat ) {
+ $insertRows[] = array( 'cat_title' => $cat );
+ }
+ $dbw->insert( 'category', $insertRows, __METHOD__, 'IGNORE' );
+
+ $addFields = array( 'cat_pages = cat_pages + 1' );
+ $removeFields = array( 'cat_pages = cat_pages - 1' );
+ if( $ns == NS_CATEGORY ) {
+ $addFields[] = 'cat_subcats = cat_subcats + 1';
+ $removeFields[] = 'cat_subcats = cat_subcats - 1';
+ } elseif( $ns == NS_IMAGE ) {
+ $addFields[] = 'cat_files = cat_files + 1';
+ $removeFields[] = 'cat_files = cat_files - 1';
+ }
+
+ if ( $added ) {
+ $dbw->update(
+ 'category',
+ $addFields,
+ array( 'cat_title' => $added ),
+ __METHOD__
+ );
+ }
+ if ( $deleted ) {
+ $dbw->update(
+ 'category',
+ $removeFields,
+ array( 'cat_title' => $deleted ),
+ __METHOD__
+ );
+ }
+ }
}