Index: trunk/phase3/includes/Article.php |
— | — | @@ -261,6 +261,28 @@ |
262 | 262 | global $wgParser; |
263 | 263 | return $wgParser->getSection( $text, $section ); |
264 | 264 | } |
| 265 | + |
| 266 | + /** |
| 267 | + * Get the text that needs to be saved in order to undo all revisions |
| 268 | + * between $undo and $undoafter. Revisions must belong to the same page, |
| 269 | + * must exist and must not be deleted |
| 270 | + * @param $undo Revision |
| 271 | + * @param $undoafter Revision Must be an earlier revision than $undo |
| 272 | + * @return mixed string on success, false on failure |
| 273 | + */ |
| 274 | + public function getUndoText( Revision $undo, Revision $undoafter = null ) { |
| 275 | + $undo_text = $undo->getText(); |
| 276 | + $undoafter_text = $undoafter->getText(); |
| 277 | + $cur_text = $this->getContent(); |
| 278 | + if ( $cur_text == $undo_text ) { |
| 279 | + # No use doing a merge if it's just a straight revert. |
| 280 | + return $undoafter_text; |
| 281 | + } |
| 282 | + $undone_text = ''; |
| 283 | + if ( !wfMerge( $undo_text, $undoafter_text, $cur_text, $undone_text ) ) |
| 284 | + return false; |
| 285 | + return $undone_text; |
| 286 | + } |
265 | 287 | |
266 | 288 | /** |
267 | 289 | * @return int The oldid of the article that is to be shown, 0 for the |
Index: trunk/phase3/includes/EditPage.php |
— | — | @@ -165,18 +165,13 @@ |
166 | 166 | $undorev->getPage() == $this->mArticle->getID() && |
167 | 167 | !$undorev->isDeleted( Revision::DELETED_TEXT ) && |
168 | 168 | !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) { |
169 | | - $undorev_text = $undorev->getText(); |
170 | | - $oldrev_text = $oldrev->getText(); |
171 | | - $currev_text = $text; |
172 | | - |
173 | | - if ( $currev_text != $undorev_text ) { |
174 | | - $result = wfMerge( $undorev_text, $oldrev_text, $currev_text, $text ); |
| 169 | + |
| 170 | + $undotext = $this->mArticle->getUndoText( $undorev, $oldrev ); |
| 171 | + if ( $undotext === false ) { |
| 172 | + # Warn the user that something went wrong |
| 173 | + $this->editFormPageTop .= $wgOut->parse( '<div class="error mw-undo-failure">' . wfMsgNoTrans( 'undo-failure' ) . '</div>' ); |
175 | 174 | } else { |
176 | | - # No use doing a merge if it's just a straight revert. |
177 | | - $text = $oldrev_text; |
178 | | - $result = true; |
179 | | - } |
180 | | - if ( $result ) { |
| 175 | + $text = $undotext; |
181 | 176 | # Inform the user of our success and set an automatic edit summary |
182 | 177 | $this->editFormPageTop .= $wgOut->parse( '<div class="mw-undo-success">' . wfMsgNoTrans( 'undo-success' ) . '</div>' ); |
183 | 178 | $firstrev = $oldrev->getNext(); |
— | — | @@ -186,9 +181,6 @@ |
187 | 182 | $this->undidRev = $undo; |
188 | 183 | } |
189 | 184 | $this->formtype = 'diff'; |
190 | | - } else { |
191 | | - # Warn the user that something went wrong |
192 | | - $this->editFormPageTop .= $wgOut->parse( '<div class="error mw-undo-failure">' . wfMsgNoTrans( 'undo-failure' ) . '</div>' ); |
193 | 185 | } |
194 | 186 | } else { |
195 | 187 | // Failed basic sanity checks. |
Index: trunk/phase3/includes/api/ApiEditPage.php |
— | — | @@ -48,7 +48,9 @@ |
49 | 49 | $params = $this->extractRequestParams(); |
50 | 50 | if(is_null($params['title'])) |
51 | 51 | $this->dieUsageMsg(array('missingparam', 'title')); |
52 | | - if(is_null($params['text']) && is_null($params['appendtext']) && is_null($params['prependtext'])) |
| 52 | + if(is_null($params['text']) && is_null($params['appendtext']) && |
| 53 | + is_null($params['prependtext']) && |
| 54 | + $params['undo'] == 0) |
53 | 55 | $this->dieUsageMsg(array('missingtext')); |
54 | 56 | if(is_null($params['token'])) |
55 | 57 | $this->dieUsageMsg(array('missingparam', 'token')); |
— | — | @@ -79,9 +81,39 @@ |
80 | 82 | $params['text'] = $params['prependtext'] . $content . $params['appendtext']; |
81 | 83 | $toMD5 = $params['prependtext'] . $params['appendtext']; |
82 | 84 | } |
| 85 | + |
| 86 | + if($params['undo'] > 0) |
| 87 | + { |
| 88 | + if($params['undoafter'] > 0) |
| 89 | + { |
| 90 | + if($params['undo'] < $params['undoafter']) |
| 91 | + list($params['undo'], $params['undoafter']) = |
| 92 | + array($params['undoafter'], $params['undo']); |
| 93 | + $undoafterRev = Revision::newFromID($params['undoafter']); |
| 94 | + } |
| 95 | + $undoRev = Revision::newFromID($params['undo']); |
| 96 | + if(is_null($undoRev) || $undoRev->isDeleted(Revision::DELETED_TEXT)) |
| 97 | + $this->dieUsageMsg(array('nosuchrevid', $params['undo'])); |
| 98 | + if($params['undoafter'] == 0) |
| 99 | + $undoafterRev = $undoRev->getPrevious(); |
| 100 | + if(is_null($undoafterRev) || $undoafterRev->isDeleted(Revision::DELETED_TEXT)) |
| 101 | + $this->dieUsageMsg(array('nosuchrevid', $params['undoafter'])); |
| 102 | + if($undoRev->getPage() != $articleObj->getID()) |
| 103 | + $this->dieUsageMsg(array('revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText())); |
| 104 | + if($undoafterRev->getPage() != $articleObj->getID()) |
| 105 | + $this->dieUsageMsg(array('revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText())); |
| 106 | + $newtext = $articleObj->getUndoText($undoRev, $undoafterRev); |
| 107 | + if($newtext === false) |
| 108 | + $this->dieUsageMsg(array('undo-failure')); |
| 109 | + $params['text'] = $newtext; |
| 110 | + // If no summary was given and we only undid one rev, |
| 111 | + // use an autosummary |
| 112 | + if(is_null($params['summary']) && $titleObj->getNextRevisionID($undoafterRev->getID()) == $params['undo']) |
| 113 | + $params['summary'] = wfMsgForContent('undo-summary', $params['undo'], $undoRev->getUserText()); |
| 114 | + } |
83 | 115 | |
84 | 116 | # See if the MD5 hash checks out |
85 | | - if(isset($params['md5'])) |
| 117 | + if(!is_null($params['md5'])) |
86 | 118 | if(md5($toMD5) !== $params['md5']) |
87 | 119 | $this->dieUsageMsg(array('hashcheckfailed')); |
88 | 120 | |
— | — | @@ -140,9 +172,9 @@ |
141 | 173 | # Run hooks |
142 | 174 | # Handle CAPTCHA parameters |
143 | 175 | global $wgRequest; |
144 | | - if(isset($params['captchaid'])) |
| 176 | + if(!is_null($params['captchaid'])) |
145 | 177 | $wgRequest->setVal( 'wpCaptchaId', $params['captchaid'] ); |
146 | | - if(isset($params['captchaword'])) |
| 178 | + if(!is_null($params['captchaword'])) |
147 | 179 | $wgRequest->setVal( 'wpCaptchaWord', $params['captchaword'] ); |
148 | 180 | $r = array(); |
149 | 181 | if(!wfRunHooks('APIEditBeforeSave', array(&$ep, $ep->textbox1, &$r))) |
— | — | @@ -269,6 +301,12 @@ |
270 | 302 | 'md5' => null, |
271 | 303 | 'prependtext' => null, |
272 | 304 | 'appendtext' => null, |
| 305 | + 'undo' => array( |
| 306 | + ApiBase :: PARAM_TYPE => 'integer' |
| 307 | + ), |
| 308 | + 'undoafter' => array( |
| 309 | + ApiBase :: PARAM_TYPE => 'integer' |
| 310 | + ), |
273 | 311 | ); |
274 | 312 | } |
275 | 313 | |
— | — | @@ -300,13 +338,19 @@ |
301 | 339 | 'prependtext' => array( 'Add this text to the beginning of the page. Overrides text.', |
302 | 340 | 'Don\'t use together with section: that won\'t do what you expect.'), |
303 | 341 | 'appendtext' => 'Add this text to the end of the page. Overrides text', |
| 342 | + 'undo' => 'Undo this revision. Overrides text, prependtext and appendtext', |
| 343 | + 'undoafter' => 'Undo all revisions from undo to this one. If not set, just undo one revision', |
304 | 344 | ); |
305 | 345 | } |
306 | 346 | |
307 | 347 | protected function getExamples() { |
308 | 348 | return array ( |
309 | 349 | "Edit a page (anonymous user):", |
310 | | - " api.php?action=edit&title=Test&summary=test%20summary&text=article%20content&basetimestamp=20070824123454&token=%2B\\" |
| 350 | + " api.php?action=edit&title=Test&summary=test%20summary&text=article%20content&basetimestamp=20070824123454&token=%2B\\", |
| 351 | + "Prepend __NOTOC__ to a page (anonymous user):", |
| 352 | + " api.php?action=edit&title=Test&summary=NOTOC&minor&prependtext=__NOTOC__%0A&basetimestamp=20070824123454&token=%2B\\", |
| 353 | + "Undo r13579 through r13585 with autosummary(anonymous user):", |
| 354 | + " api.php?action=edit&title=Test&undo=13585&undoafter=13579&basetimestamp=20070824123454&token=%2B\\", |
311 | 355 | ); |
312 | 356 | } |
313 | 357 | |
Index: trunk/phase3/includes/api/ApiBase.php |
— | — | @@ -705,6 +705,7 @@ |
706 | 706 | 'missingparam' => array('code' => 'no$1', 'info' => "The \$1 parameter must be set"), |
707 | 707 | 'invalidtitle' => array('code' => 'invalidtitle', 'info' => "Bad title ``\$1''"), |
708 | 708 | 'nosuchpageid' => array('code' => 'nosuchpageid', 'info' => "There is no page with ID \$1"), |
| 709 | + 'nosuchrevid' => array('code' => 'nosuchrevid', 'info' => "There is no revision with ID \$1"), |
709 | 710 | 'invaliduser' => array('code' => 'invaliduser', 'info' => "Invalid username ``\$1''"), |
710 | 711 | 'invalidexpiry' => array('code' => 'invalidexpiry', 'info' => "Invalid expiry time ``\$1''"), |
711 | 712 | 'pastexpiry' => array('code' => 'pastexpiry', 'info' => "Expiry time ``\$1'' is in the past"), |
— | — | @@ -739,8 +740,10 @@ |
740 | 741 | 'blankpage' => array('code' => 'emptypage', 'info' => "Creating new, empty pages is not allowed"), |
741 | 742 | 'editconflict' => array('code' => 'editconflict', 'info' => "Edit conflict detected"), |
742 | 743 | 'hashcheckfailed' => array('code' => 'badmd5', 'info' => "The supplied MD5 hash was incorrect"), |
743 | | - 'missingtext' => array('code' => 'notext', 'info' => "One of the text, appendtext and prependtext parameters must be set"), |
| 744 | + 'missingtext' => array('code' => 'notext', 'info' => "One of the text, appendtext, prependtext and undo parameters must be set"), |
744 | 745 | 'emptynewsection' => array('code' => 'emptynewsection', 'info' => 'Creating empty new sections is not possible.'), |
| 746 | + 'revwrongpage' => array('code' => 'revwrongpage', 'info' => "r\$1 is not a revision of ``\$2''"), |
| 747 | + 'undo-failure' => array('code' => 'undofailure', 'info' => 'Undo failed due to conflicting intermediate edits'), |
745 | 748 | ); |
746 | 749 | |
747 | 750 | /** |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -97,6 +97,7 @@ |
98 | 98 | * (bug 17069) Added ucshow=patrolled|!patrolled to list=usercontribs |
99 | 99 | * action=delete respects $wgDeleteRevisionsLimit and the bigdelete user right |
100 | 100 | * (bug 17024) Added legaltitlechars field to meta=siteinfo&siprop=general output |
| 101 | +* (bug 15949) Add undo functionality to action=edit |
101 | 102 | |
102 | 103 | === Languages updated in 1.15 === |
103 | 104 | |