RollbackAction: Implement AJAX interface and require POST
[lhc/web/wiklou.git] / includes / actions / RollbackAction.php
index cc94fd3..e32582e 100644 (file)
@@ -25,7 +25,7 @@
  *
  * @ingroup Actions
  */
-class RollbackAction extends FormlessAction {
+class RollbackAction extends FormAction {
 
        public function getName() {
                return 'rollback';
@@ -35,39 +35,79 @@ class RollbackAction extends FormlessAction {
                return 'rollback';
        }
 
-       public function onView() {
-               // TODO: use $this->useTransactionalTimeLimit(); when POST only
-               wfTransactionalTimeLimit();
+       protected function preText() {
+               return $this->msg( 'confirm-rollback-top' )->parse();
+       }
+
+       protected function alterForm( HTMLForm $form ) {
+               $form->setSubmitTextMsg( 'confirm-rollback-button' );
+               $form->setTokenSalt( 'rollback' );
+
+               // Copy parameters from GET to confirmation form
+               $from = $this->getRequest()->getVal( 'from' );
+               if ( $from === null ) {
+                       throw new BadRequestError( 'rollbackfailed', 'rollback-missingparam' );
+               }
+               foreach ( [ 'from', 'bot', 'hidediff', 'summary' ] as $param ) {
+                       $val = $this->getRequest()->getVal( $param );
+                       if ( $val !== null ) {
+                               $form->addHiddenField( $param, $val );
+                       }
+               }
+       }
 
-               $details = null;
+       /**
+        * This must return true so that HTMLForm::show() will not display the form again after
+        * submission. For rollback, display either the form or the result (success/error)
+        * not both.
+        *
+        * @return bool
+        * @throws ErrorPageError
+        */
+       public function onSubmit( $data ) {
+               $this->useTransactionalTimeLimit();
 
                $request = $this->getRequest();
                $user = $this->getUser();
+               $from = $request->getVal( 'from' );
+               $rev = $this->page->getRevision();
+               if ( $from === null || $from === '' ) {
+                       throw new ErrorPageError( 'rollbackfailed', 'rollback-missingparam' );
+               }
+               if ( $from !== $rev->getUserText() ) {
+                       throw new ErrorPageError( 'rollbackfailed', 'alreadyrolled', [
+                               $this->getTitle()->getPrefixedText(),
+                               $from,
+                               $rev->getUserText()
+                       ] );
+               }
 
-               $result = $this->page->doRollback(
-                       $request->getVal( 'from' ),
+               $data = null;
+               $errors = $this->page->doRollback(
+                       $from,
                        $request->getText( 'summary' ),
-                       $request->getVal( 'token' ),
+                       // Provided by HTMLForm
+                       $request->getVal( 'wpEditToken' ),
                        $request->getBool( 'bot' ),
-                       $details,
+                       $data,
                        $this->getUser()
                );
 
-               if ( in_array( array( 'actionthrottledtext' ), $result ) ) {
+               if ( in_array( [ 'actionthrottledtext' ], $errors ) ) {
                        throw new ThrottledError;
                }
 
-               if ( isset( $result[0][0] ) &&
-                       ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' )
+               if ( isset( $errors[0][0] ) &&
+                       ( $errors[0][0] == 'alreadyrolled' || $errors[0][0] == 'cantrollback' )
                ) {
                        $this->getOutput()->setPageTitle( $this->msg( 'rollbackfailed' ) );
-                       $errArray = $result[0];
+                       $errArray = $errors[0];
                        $errMsg = array_shift( $errArray );
                        $this->getOutput()->addWikiMsgArray( $errMsg, $errArray );
 
-                       if ( isset( $details['current'] ) ) {
+                       if ( isset( $data['current'] ) ) {
                                /** @var Revision $current */
-                               $current = $details['current'];
+                               $current = $data['current'];
 
                                if ( $current->getComment() != '' ) {
                                        $this->getOutput()->addHTML( $this->msg( 'editcomment' )->rawParams(
@@ -75,25 +115,24 @@ class RollbackAction extends FormlessAction {
                                }
                        }
 
-                       return;
+                       return true;
                }
 
                # NOTE: Permission errors already handled by Action::checkExecute.
-
-               if ( $result == array( array( 'readonlytext' ) ) ) {
+               if ( $errors == [ [ 'readonlytext' ] ] ) {
                        throw new ReadOnlyError;
                }
 
                # XXX: Would be nice if ErrorPageError could take multiple errors, and/or a status object.
-               #     Right now, we only show the first error
-               foreach ( $result as $error ) {
+               #      Right now, we only show the first error
+               foreach ( $errors as $error ) {
                        throw new ErrorPageError( 'rollbackfailed', $error[0], array_slice( $error, 1 ) );
                }
 
                /** @var Revision $current */
-               $current = $details['current'];
-               $target = $details['target'];
-               $newId = $details['newid'];
+               $current = $data['current'];
+               $target = $data['target'];
+               $newId = $data['newid'];
                $this->getOutput()->setPageTitle( $this->msg( 'actioncomplete' ) );
                $this->getOutput()->setRobotPolicy( 'noindex,nofollow' );
 
@@ -103,7 +142,7 @@ class RollbackAction extends FormlessAction {
                        ->parseAsBlock() );
 
                if ( $user->getBoolOption( 'watchrollback' ) ) {
-                       $user->addWatch( $this->page->getTitle(), WatchedItem::IGNORE_USER_RIGHTS );
+                       $user->addWatch( $this->page->getTitle(), User::IGNORE_USER_RIGHTS );
                }
 
                $this->getOutput()->returnToMain( false, $this->getTitle() );
@@ -121,6 +160,12 @@ class RollbackAction extends FormlessAction {
                        );
                        $de->showDiff( '', '' );
                }
+               return true;
+       }
+
+       public function onSuccess() {
+               // Required by parent class, but redundant because onSubmit already shows
+               // the success message when needed.
        }
 
        protected function getDescription() {