* API: (bug 15949) Add undo functionality to action=edit
authorRoan Kattouw <catrope@users.mediawiki.org>
Mon, 26 Jan 2009 13:51:03 +0000 (13:51 +0000)
committerRoan Kattouw <catrope@users.mediawiki.org>
Mon, 26 Jan 2009 13:51:03 +0000 (13:51 +0000)
* 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
includes/Article.php
includes/EditPage.php
includes/api/ApiBase.php
includes/api/ApiEditPage.php

index 1524277..0aa7fae 100644 (file)
@@ -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 ===
 
index f37538d..4f3125e 100644 (file)
@@ -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
index d1c41d8..3060d4c 100644 (file)
@@ -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( '<div class="error mw-undo-failure">' . wfMsgNoTrans( 'undo-failure' ) . '</div>' );
                                        } 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( '<div class="mw-undo-success">' . wfMsgNoTrans( 'undo-success' ) . '</div>' );
                                                $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( '<div class="error mw-undo-failure">' . wfMsgNoTrans( 'undo-failure' ) . '</div>' );
                                        }
                                } else {
                                        // Failed basic sanity checks.
index 8afaae1..ea18c32 100644 (file)
@@ -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'),
        );
 
        /**
index c69d506..d092755 100644 (file)
@@ -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\\",
                );
        }