Page MenuHomePhabricator

revertpatch.patch

Authored By
bzimport
Nov 21 2014, 8:12 PM
Size
15 KB
Referenced Files
None
Subscribers
None

revertpatch.patch

Index: includes/Article.php
===================================================================
--- includes/Article.php (revision 33212)
+++ includes/Article.php (working copy)
@@ -2571,8 +2571,232 @@
$wgOut->returnToMain( false, $this->mTitle );
}
+ /**
+ * Revert to a specified reveision of a page.
+ * Fails if the target revision doesn't match the page we're on
+ * or someone edits the page between the time the link
+ * is generated and used, else it could get messy.
+ * This function performs permissions checks on $wgUser,
+ * then calls commitRevisionRevert() to do the dirty work
+ *
+ * @param int $toRevision - Revision to revert to.
+ * @param string $summary - Custom summary. Set to default summary if empty.
+ * @param string $token - Revert token.
+ * @param bool $minor - Minor edit
+ *
+ * @param array $resultDetails contains result-specific array of additional values
+ *
+ * @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 doRevisionRevert ( $toRevision, $summary, $token, $minor, &$resultDetails ) {
+ global $wgUser;
+ $resultDetails = null;
+ # Check permissions
+ $errors = array_merge( $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser ),
+ $this->mTitle->getUserPermissionsErrors( 'revisionrevert', $wgUser ) );
+ $current = Revision::newFromTitle( $this->mTitle );
+ if( !$wgUser->matchEditToken( $token, array( $current->getId(), $toRevision ) ) )
+ $errors[] = array( 'revisionrevert-failure' );
+
+ if ( $wgUser->pingLimiter('revisionrevert') || $wgUser->pingLimiter() ) {
+ $errors[] = array( 'actionthrottledtext' );
+ }
+ # If there were errors, bail out now
+ if(!empty($errors))
+ return $errors;
+
+ return $this->commitRevisionRevert( $toRevision, $summary, $minor, $resultDetails);
+ }
+
/**
+ * Backend implementation of doRevisionRevert(), please refer there for parameter
+ * and return value documentation
+ *
+ * NOTE: This function does NOT check ANY permissions, it just commits the
+ * revert 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
+ * doRevisionRevert() instead.
+ */
+ public function commitRevisionRevert( $toRevision, $summary, $minor, &$resultDetails) {
+ global $wgUser;
+
+ if( wfReadOnly() ) {
+ return array( array( 'readonlytext' ) );
+ }
+
+ # Load the target revision
+ $targetRev = Revision::newFromTitle( $this->mTitle, $toRevision );
+
+ # make sure both revisions are from the same page
+ $current = Revision::newFromTitle( $this->mTitle );
+ if ( $targetRev->getPage() != $current->getPage() ) {
+ return array( array('revisionrevert-title-mismatch'));
+ }
+ # Is the target revision text or user deleted?
+ if( $targetRev->isDeleted( REVISION::DELETED_TEXT ) || $targetRev->isDeleted( REVISION::DELETED_USER ) ) {
+ return array( array('notvisiblerev'));
+ }
+
+ # Generate the edit summary if necessary
+ if( empty( $summary ) ) {
+ global $wgLang;
+ $summary = wfMsgForContent( 'revisionrevert-summary',
+ $targetRev->getId(),
+ $wgLang->timeanddate(wfTimestamp(TS_MW, $targetRev->mTimestamp), true),
+ $targetRev->getUserText()
+ );
+ }
+
+ # Save
+ $flags = EDIT_UPDATE;
+
+ if ($wgUser->isAllowed('minoredit') && $minor )
+ $flags |= EDIT_MINOR;
+
+ if( $wgUser->isAllowed('bot') )
+ $flags |= EDIT_FORCE_BOT;
+ $this->doEdit( $targetRev->getText(), $summary, $flags );
+
+ wfRunHooks( 'ArticleRevertComplete', array( $this, $wgUser, $targetRev ) );
+
+ $resultDetails = array(
+ 'target' => $targetRev,
+ );
+ return array();
+ }
+
+ /**
+ * User interface for revision-revert operations
+ */
+ function revisionRevert() {
+ global $wgOut, $wgRequest, $wgUser;
+ // Let people bypass this form by providing a summary as a URL parameter
+ if ( $wgRequest->getText( 'skip' ) || $wgRequest->wasPosted() ) {
+ $this->revisionRevertContinue();
+ return;
+ }
+ $target = Revision::newFromId( $wgRequest->getVal( 'wpRevertTarget' ) );
+ if( $target->isDeleted( REVISION::DELETED_TEXT ) || $target->isDeleted( REVISION::DELETED_USER ) ) {
+ $out []= 'notvisiblerev';
+ $wgOut->showPermissionsErrorPage( $out );
+ return;
+ }
+ // Form for submitting a
+ $wgOut->addWikiMsg( 'revisionrevert-text' );
+ $wgOut->setPagetitle( $this->mTitle );
+ $revertSummary = Xml::label( wfMsg( 'revisionrevert-summary-label') , 'wpRevertSummary' );
+ $wgOut->addHTML(
+ Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->mTitle->getLocalURL( "action=revisionrevert" ), 'id' => 'revisionrevert' ) ) .
+ Xml::openElement( 'fieldset' ) .
+ Xml::element( 'legend', null, wfMsg( 'revisionrevert-legend' ) ) .
+ Xml::openElement( 'table', array ( 'id' => 'mw-revisionrevert-table' ) ) . "
+ <tr>
+ <td class='mw-label'>
+ {$revertSummary}
+ </td>
+ <td class='mw-input'> " .
+ Xml::input( 'wpRevertSummary', 45, '',
+ array( 'tabindex' => '3', 'id' => 'mw-revert-summary' ) ) .
+ Xml::hidden( 'wpRevertToken', $wgRequest->getVal( 'wpRevertToken' ) ) .
+ Xml::hidden( 'wpRevertTarget', $wgRequest->getVal( 'wpRevertTarget' ) ) .
+ "</td>
+ </tr> ");
+ if ( $wgUser->isAllowed('minoredit') ) {
+ $minorLabel = wfMsgExt('minoredit', array('parseinline'));
+ $attribs = array(
+ 'accesskey' => wfMsg( 'accesskey-minoredit' ),
+ 'id' => 'wpRevertMinor',
+ );
+ $wgOut->addHTML(
+ "<tr>
+ <td>" .
+ Xml::check( 'wpMinoredit', true, $attribs ) .
+ "&nbsp;<label for='wpMinoredit'".$wgUser->getSkin()->tooltipAndAccesskey('minoredit').">{$minorLabel}</label>
+ </td>
+ </tr>"
+ );
+ }
+ $wgOut->addHTML(
+ "<tr>
+ <td class='mw-submit' >" .
+ Xml::submitButton( wfMsg( 'revisionrevert-submit' ),
+ array( 'name' => 'wpRevertSubmit', 'tabindex' => '11' ) ) .
+ "</td>
+ </tr>" .
+ Xml::closeElement( 'table' ) .
+ Xml::closeElement( 'fieldset' ) .
+ Xml::closeElement( 'form' )
+ );
+ $oldid = $target->getId();
+ $diff = $this->mOldId;
+ $de = new DifferenceEngine( $this->mTitle );
+ $de->setText( $this->fetchContent() , $target->getText() );
+ $de->showDiff( wfMsg( 'revisionrevert-diff-cur' ), wfMsg( 'revisionrevert-diff-changes' ) );
+ }
+
+ function revisionRevertContinue() {
+ global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
+ $details = null;
+ $result = $this->doRevisionRevert(
+ $wgRequest->getVal( 'wpRevertTarget' ),
+ $wgRequest->getText( 'wpRevertSummary' ),
+ $wgRequest->getVal( 'wpRevertToken' ),
+ $wgRequest->getVal( 'wpMinoredit' ),
+ $details
+ );
+
+ 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] == 'revisionrevert-title-mismatch' ){
+ $wgOut->setPageTitle( wfMsg( 'revisionrevert-failed' ) );
+ $errArray = $result[0];
+ $errMsg = array_shift( $errArray );
+ $wgOut->addWikiMsgArray( $errMsg, $errArray );
+ return;
+ }
+ # Display permissions errors before read-only message -- there's no
+ # point in misleading the user into thinking the inability to revert
+ # 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;
+ }
+ global $wgLang;
+ $target = $details['target'];
+ $wgOut->setPageTitle( wfMsg( 'actioncomplete' ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $targetRev = $target->getId();
+ $targetUser = $wgUser->getSkin()->userLink( $target->getUser(), $target->getUserText() )
+ . $wgUser->getSkin()->userToolLinks( $target->getUser(), $target->getUserText() );
+ $targetTime = $wgLang->timeanddate($target->getTimestamp(), true );
+ $wgOut->addHtml( wfMsgExt( 'revisionrevert-success', array( 'parse', 'replaceafter' ), $targetRev, $targetUser, $targetTime ) );
+ $wgOut->returnToMain( false, $this->mTitle );
+ }
+
+ /**
* Do standard deferred updates after page view
* @private
*/
@@ -2792,10 +3016,15 @@
$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\">" . $cdel . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
+ if ($wgUser->isAllowed('revisionrevert') && !$current && !( $revision->isDeleted( REVISION::DELETED_TEXT ) || $revision->isDeleted( REVISION::DELETED_USER ) ) ) {
+ $cur = Revision::newFromTitle( $this->mTitle );
+ $revertlink = $sk->generateRevisionRevert( $revision, $cur );
+ $r = $r . "\n\t\t\t\t" . $revertlink;
+ }
$wgOut->setSubtitle( $r );
}
Index: includes/DefaultSettings.php
===================================================================
--- includes/DefaultSettings.php (revision 33212)
+++ includes/DefaultSettings.php (working copy)
@@ -1150,6 +1150,7 @@
$wgGroupPermissions['sysop']['suppressredirect'] = true;
$wgGroupPermissions['sysop']['apihighlimits'] = true;
$wgGroupPermissions['sysop']['browsearchive'] = true;
+$wgGroupPermissions['sysop']['revisionrevert'] = true;
#$wgGroupPermissions['sysop']['mergehistory'] = true;
// Permission to change users' group assignments
Index: includes/DifferenceEngine.php
===================================================================
--- includes/DifferenceEngine.php (revision 33212)
+++ includes/DifferenceEngine.php (working copy)
@@ -251,14 +251,25 @@
}
$rdel = "&nbsp;&nbsp;&nbsp;<tt>(<small>$rdel</small>)</tt> ";
}
+ $rrev = ''; $lrev = '';
+ if( $wgUser->isAllowed( 'revisionrevert' ) && ($this->mNewRev->getPage() == $this->mOldRev->getPage() ) ) {
+
+ $cur = Revision::newFromTitle( $this->mTitle );
+ if (!( $this->mNewRev->isDeleted( REVISION::DELETED_TEXT ) || $this->mNewRev->isDeleted( REVISION::DELETED_USER ) ) && !$this->mNewRev->isCurrent() ) {
+ $rrev = $sk->generateRevisionRevert( $this->mNewRev, $cur );
+ }
+ if (!( $this->mOldRev->isDeleted( REVISION::DELETED_TEXT ) || $this->mOldRev->isDeleted( REVISION::DELETED_USER ) ) && !$this->mOldRev->isCurrent() ) {
+ $lrev = $sk->generateRevisionRevert( $this->mOldRev, $cur );
+ }
+ }
$oldHeader = '<div id="mw-diff-otitle1"><strong>'.$this->mOldtitle.'</strong></div>' .
'<div id="mw-diff-otitle2">' . $sk->revUserTools( $this->mOldRev, true ) . "</div>" .
- '<div id="mw-diff-otitle3">' . $oldminor . $sk->revComment( $this->mOldRev, !$diffOnly, true ) . $ldel . "</div>" .
+ '<div id="mw-diff-otitle3">' . $oldminor . $sk->revComment( $this->mOldRev, !$diffOnly, true ) . $ldel . $lrev . "</div>" .
'<div id="mw-diff-otitle4">' . $prevlink .'</div>';
$newHeader = '<div id="mw-diff-ntitle1"><strong>'.$this->mNewtitle.'</strong></div>' .
'<div id="mw-diff-ntitle2">' . $sk->revUserTools( $this->mNewRev, true ) . " $rollback</div>" .
- '<div id="mw-diff-ntitle3">' . $newminor . $sk->revComment( $this->mNewRev, !$diffOnly, true ) . $rdel . "</div>" .
+ '<div id="mw-diff-ntitle3">' . $newminor . $sk->revComment( $this->mNewRev, !$diffOnly, true ) . $rdel . $rrev . "</div>" .
'<div id="mw-diff-ntitle4">' . $nextlink . $patrol . '</div>';
$this->showDiff( $oldHeader, $newHeader );
Index: includes/Linker.php
===================================================================
--- includes/Linker.php (revision 33212)
+++ includes/Linker.php (working copy)
@@ -1320,6 +1320,36 @@
'action=rollback&from=' . urlencode( $rev->getUserText() ) . $extra
);
}
+ /**
+ * Generate a revision-revert link based on given revisions
+ *
+ * @param Revision $toRev - revision being reverted to
+ * @param Revision $fromRev - the current revision
+ */
+ function generateRevisionRevert( $toRev, $fromRev ) {
+ return '<span class="mw-revert-link">['
+ . $this->buildRevisionRevertLink( $toRev, $fromRev )
+ . ']</span>';
+ }
+
+ /**
+ * Build a raw revision-revert link, useful for collections of "tool" links
+ *
+ * @param Revision $toRev - revision being reverted to
+ * @param Revision $fromRev - the current revision
+ * @return string
+ */
+ public function buildRevisionRevertLink( $toRev, $fromRev ) {
+ global $wgUser;
+ $title = $fromRev->getTitle();
+ $token = '&wpRevertToken=' . urlencode( $wgUser->editToken( array( $fromRev->getId(),
+ $toRev->getId() ) ) );
+ return $this->makeKnownLinkObj(
+ $title,
+ wfMsgHtml( 'revisionrevert-link' ),
+ 'action=revisionrevert&wpRevertTarget=' . urlencode($toRev->getId()) . $token
+ );
+ }
/**
* Returns HTML for the "templates used on this page" list.
Index: includes/PageHistory.php
===================================================================
--- includes/PageHistory.php (revision 33212)
+++ includes/PageHistory.php (working copy)
@@ -264,6 +264,14 @@
$tools = array();
+ if( !$this->mTitle->getUserPermissionsErrors( 'revisionrevert', $wgUser )
+ && !$this->mTitle->getUserPermissionsErrors( 'edit', $wgUser ) && !$latest ) {
+ $from = Revision::newFromTitle( $this->mTitle );
+ $tools[] = '<span class="mw-revert-link">'
+ . $this->mSkin->buildRevisionRevertLink( $rev, $from )
+ . '</span>';
+ }
+
if ( !is_null( $next ) && is_object( $next ) ) {
if( !$this->mTitle->getUserPermissionsErrors( 'rollback', $wgUser )
&& !$this->mTitle->getUserPermissionsErrors( 'edit', $wgUser )
Index: includes/Wiki.php
===================================================================
--- includes/Wiki.php (revision 33212)
+++ includes/Wiki.php (working copy)
@@ -434,6 +434,7 @@
case 'delete':
case 'revert':
case 'rollback':
+ case 'revisionrevert':
case 'protect':
case 'unprotect':
case 'info':
Index: languages/messages/MessagesEn.php
===================================================================
--- languages/messages/MessagesEn.php (revision 33211)
+++ languages/messages/MessagesEn.php (working copy)
@@ -2074,7 +2074,22 @@
'minimum-size' => 'Min size',
'maximum-size' => 'Max size',
'pagesize' => '(bytes)',
+'revisionrevert-summary' => 'Reverted to revision $1 from $2 by [[Special:Contributions/$3|$3]]',
+'revisionrevert-link' => 'revert to revision',
+'revisionrevert-failure' => 'Someone may have edited the page since the link was made or there may be a problem with your login session',
+'revisionrevert-success' => 'Reverted to revision $1 by $2 from $3',
+'revisionrevert-title-mismatch'=>'Both revisions must be of the same page.',
+'revisionrevert-failed' => 'Revert failed',
+'revisionrevert-summary-label'=> 'Revert summary:',
+'revisionrevert-legend' => 'Revert',
+'revisionrevert-submit' => 'Submit',
+'revisionrevert-diff-cur' => 'Current version',
+'revisionrevert-diff-changes' => 'New text',
+'revisionrevert-text' => 'This will revert the page to the content of the selected revision. Please review the changes below before submitting.
+You can set the edit summary in the box below or leave it blank to use the default summary.',
+
+
# Restrictions (nouns)
'restriction-edit' => 'Edit',
'restriction-move' => 'Move',

File Metadata

Mime Type
text/x-diff
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1420
Default Alt Text
revertpatch.patch (15 KB)

Event Timeline