Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F1841
revertpatch.patch
Public
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Authored By
•
bzimport
Nov 21 2014, 8:12 PM
2014-11-21 20:12:32 (UTC+0)
Size
15 KB
Referenced Files
None
Subscribers
None
revertpatch.patch
View Options
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 ) .
+ " <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 = " <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
Details
Attached
Mime Type
text/x-diff
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1420
Default Alt Text
revertpatch.patch (15 KB)
Attached To
Mode
T3511: "Revert to this version" link for admins
Attached
Detach File
Event Timeline
Log In to Comment