From 64498675086985654b0cc2499380256295850a19 Mon Sep 17 00:00:00 2001 From: Roan Kattouw Date: Mon, 26 Jan 2009 13:51:03 +0000 Subject: [PATCH] * API: (bug 15949) Add undo functionality to action=edit * Move undo text generation from EditPage::getContent() to Article::getUndoText() * Add some more examples for action=edit * ApiEditPage.php: don't mix !is_null() and isset(), be consistent --- RELEASE-NOTES | 1 + includes/Article.php | 22 +++++++++++++++ includes/EditPage.php | 20 ++++--------- includes/api/ApiBase.php | 5 +++- includes/api/ApiEditPage.php | 54 ++++++++++++++++++++++++++++++++---- 5 files changed, 82 insertions(+), 20 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 1524277fae..0aa7fae70c 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -97,6 +97,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN * (bug 17069) Added ucshow=patrolled|!patrolled to list=usercontribs * action=delete respects $wgDeleteRevisionsLimit and the bigdelete user right * (bug 17024) Added legaltitlechars field to meta=siteinfo&siprop=general output +* (bug 15949) Add undo functionality to action=edit === Languages updated in 1.15 === diff --git a/includes/Article.php b/includes/Article.php index f37538d970..4f3125ea7c 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -261,6 +261,28 @@ class Article { global $wgParser; return $wgParser->getSection( $text, $section ); } + + /** + * Get the text that needs to be saved in order to undo all revisions + * between $undo and $undoafter. Revisions must belong to the same page, + * must exist and must not be deleted + * @param $undo Revision + * @param $undoafter Revision Must be an earlier revision than $undo + * @return mixed string on success, false on failure + */ + public function getUndoText( Revision $undo, Revision $undoafter = null ) { + $undo_text = $undo->getText(); + $undoafter_text = $undoafter->getText(); + $cur_text = $this->getContent(); + if ( $cur_text == $undo_text ) { + # No use doing a merge if it's just a straight revert. + return $undoafter_text; + } + $undone_text = ''; + if ( !wfMerge( $undo_text, $undoafter_text, $cur_text, $undone_text ) ) + return false; + return $undone_text; + } /** * @return int The oldid of the article that is to be shown, 0 for the diff --git a/includes/EditPage.php b/includes/EditPage.php index d1c41d8efa..3060d4c491 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -165,18 +165,13 @@ class EditPage { $undorev->getPage() == $this->mArticle->getID() && !$undorev->isDeleted( Revision::DELETED_TEXT ) && !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) { - $undorev_text = $undorev->getText(); - $oldrev_text = $oldrev->getText(); - $currev_text = $text; - - if ( $currev_text != $undorev_text ) { - $result = wfMerge( $undorev_text, $oldrev_text, $currev_text, $text ); + + $undotext = $this->mArticle->getUndoText( $undorev, $oldrev ); + if ( $undotext === false ) { + # Warn the user that something went wrong + $this->editFormPageTop .= $wgOut->parse( '
' . wfMsgNoTrans( 'undo-failure' ) . '
' ); } else { - # No use doing a merge if it's just a straight revert. - $text = $oldrev_text; - $result = true; - } - if ( $result ) { + $text = $undotext; # Inform the user of our success and set an automatic edit summary $this->editFormPageTop .= $wgOut->parse( '
' . wfMsgNoTrans( 'undo-success' ) . '
' ); $firstrev = $oldrev->getNext(); @@ -186,9 +181,6 @@ class EditPage { $this->undidRev = $undo; } $this->formtype = 'diff'; - } else { - # Warn the user that something went wrong - $this->editFormPageTop .= $wgOut->parse( '
' . wfMsgNoTrans( 'undo-failure' ) . '
' ); } } else { // Failed basic sanity checks. diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index 8afaae1b1d..ea18c3246b 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -705,6 +705,7 @@ abstract class ApiBase { 'missingparam' => array('code' => 'no$1', 'info' => "The \$1 parameter must be set"), 'invalidtitle' => array('code' => 'invalidtitle', 'info' => "Bad title ``\$1''"), 'nosuchpageid' => array('code' => 'nosuchpageid', 'info' => "There is no page with ID \$1"), + 'nosuchrevid' => array('code' => 'nosuchrevid', 'info' => "There is no revision with ID \$1"), 'invaliduser' => array('code' => 'invaliduser', 'info' => "Invalid username ``\$1''"), 'invalidexpiry' => array('code' => 'invalidexpiry', 'info' => "Invalid expiry time ``\$1''"), 'pastexpiry' => array('code' => 'pastexpiry', 'info' => "Expiry time ``\$1'' is in the past"), @@ -739,8 +740,10 @@ abstract class ApiBase { 'blankpage' => array('code' => 'emptypage', 'info' => "Creating new, empty pages is not allowed"), 'editconflict' => array('code' => 'editconflict', 'info' => "Edit conflict detected"), 'hashcheckfailed' => array('code' => 'badmd5', 'info' => "The supplied MD5 hash was incorrect"), - 'missingtext' => array('code' => 'notext', 'info' => "One of the text, appendtext and prependtext parameters must be set"), + 'missingtext' => array('code' => 'notext', 'info' => "One of the text, appendtext, prependtext and undo parameters must be set"), 'emptynewsection' => array('code' => 'emptynewsection', 'info' => 'Creating empty new sections is not possible.'), + 'revwrongpage' => array('code' => 'revwrongpage', 'info' => "r\$1 is not a revision of ``\$2''"), + 'undo-failure' => array('code' => 'undofailure', 'info' => 'Undo failed due to conflicting intermediate edits'), ); /** diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php index c69d506b53..d09275508d 100644 --- a/includes/api/ApiEditPage.php +++ b/includes/api/ApiEditPage.php @@ -48,7 +48,9 @@ class ApiEditPage extends ApiBase { $params = $this->extractRequestParams(); if(is_null($params['title'])) $this->dieUsageMsg(array('missingparam', 'title')); - if(is_null($params['text']) && is_null($params['appendtext']) && is_null($params['prependtext'])) + if(is_null($params['text']) && is_null($params['appendtext']) && + is_null($params['prependtext']) && + $params['undo'] == 0) $this->dieUsageMsg(array('missingtext')); if(is_null($params['token'])) $this->dieUsageMsg(array('missingparam', 'token')); @@ -79,9 +81,39 @@ class ApiEditPage extends ApiBase { $params['text'] = $params['prependtext'] . $content . $params['appendtext']; $toMD5 = $params['prependtext'] . $params['appendtext']; } + + if($params['undo'] > 0) + { + if($params['undoafter'] > 0) + { + if($params['undo'] < $params['undoafter']) + list($params['undo'], $params['undoafter']) = + array($params['undoafter'], $params['undo']); + $undoafterRev = Revision::newFromID($params['undoafter']); + } + $undoRev = Revision::newFromID($params['undo']); + if(is_null($undoRev) || $undoRev->isDeleted(Revision::DELETED_TEXT)) + $this->dieUsageMsg(array('nosuchrevid', $params['undo'])); + if($params['undoafter'] == 0) + $undoafterRev = $undoRev->getPrevious(); + if(is_null($undoafterRev) || $undoafterRev->isDeleted(Revision::DELETED_TEXT)) + $this->dieUsageMsg(array('nosuchrevid', $params['undoafter'])); + if($undoRev->getPage() != $articleObj->getID()) + $this->dieUsageMsg(array('revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText())); + if($undoafterRev->getPage() != $articleObj->getID()) + $this->dieUsageMsg(array('revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText())); + $newtext = $articleObj->getUndoText($undoRev, $undoafterRev); + if($newtext === false) + $this->dieUsageMsg(array('undo-failure')); + $params['text'] = $newtext; + // If no summary was given and we only undid one rev, + // use an autosummary + if(is_null($params['summary']) && $titleObj->getNextRevisionID($undoafterRev->getID()) == $params['undo']) + $params['summary'] = wfMsgForContent('undo-summary', $params['undo'], $undoRev->getUserText()); + } # See if the MD5 hash checks out - if(isset($params['md5'])) + if(!is_null($params['md5'])) if(md5($toMD5) !== $params['md5']) $this->dieUsageMsg(array('hashcheckfailed')); @@ -140,9 +172,9 @@ class ApiEditPage extends ApiBase { # Run hooks # Handle CAPTCHA parameters global $wgRequest; - if(isset($params['captchaid'])) + if(!is_null($params['captchaid'])) $wgRequest->setVal( 'wpCaptchaId', $params['captchaid'] ); - if(isset($params['captchaword'])) + if(!is_null($params['captchaword'])) $wgRequest->setVal( 'wpCaptchaWord', $params['captchaword'] ); $r = array(); if(!wfRunHooks('APIEditBeforeSave', array(&$ep, $ep->textbox1, &$r))) @@ -269,6 +301,12 @@ class ApiEditPage extends ApiBase { 'md5' => null, 'prependtext' => null, 'appendtext' => null, + 'undo' => array( + ApiBase :: PARAM_TYPE => 'integer' + ), + 'undoafter' => array( + ApiBase :: PARAM_TYPE => 'integer' + ), ); } @@ -300,13 +338,19 @@ class ApiEditPage extends ApiBase { 'prependtext' => array( 'Add this text to the beginning of the page. Overrides text.', 'Don\'t use together with section: that won\'t do what you expect.'), 'appendtext' => 'Add this text to the end of the page. Overrides text', + 'undo' => 'Undo this revision. Overrides text, prependtext and appendtext', + 'undoafter' => 'Undo all revisions from undo to this one. If not set, just undo one revision', ); } protected function getExamples() { return array ( "Edit a page (anonymous user):", - " api.php?action=edit&title=Test&summary=test%20summary&text=article%20content&basetimestamp=20070824123454&token=%2B\\" + " api.php?action=edit&title=Test&summary=test%20summary&text=article%20content&basetimestamp=20070824123454&token=%2B\\", + "Prepend __NOTOC__ to a page (anonymous user):", + " api.php?action=edit&title=Test&summary=NOTOC&minor&prependtext=__NOTOC__%0A&basetimestamp=20070824123454&token=%2B\\", + "Undo r13579 through r13585 with autosummary(anonymous user):", + " api.php?action=edit&title=Test&undo=13585&undoafter=13579&basetimestamp=20070824123454&token=%2B\\", ); } -- 2.20.1