Large refactoring of rollback code:
authorRoan Kattouw <catrope@users.mediawiki.org>
Tue, 15 Jan 2008 17:31:07 +0000 (17:31 +0000)
committerRoan Kattouw <catrope@users.mediawiki.org>
Tue, 15 Jan 2008 17:31:07 +0000 (17:31 +0000)
* DB code is now in Article::commitRollback(), permissions checks in Article::doRollback()
* doRollback() no longer returns named constants but an OutputPage::showPermissionsErrorPage() compatible array

includes/Article.php

index 9ca021c..050d57a 100644 (file)
@@ -36,19 +36,6 @@ class Article {
        var $mUserText;                 //!<
        /**@}}*/
 
-       /**
-        * Constants used by internal components to get rollback results
-        */
-       const SUCCESS = 0;                      // Operation successful
-       const PERM_DENIED = 1;          // Permission denied
-       const BLOCKED = 2;                      // User has been blocked
-       const READONLY = 3;                     // Wiki is in read-only mode
-       const BAD_TOKEN = 4;            // Invalid token specified
-       const BAD_TITLE = 5;            // $this is not a valid Article
-       const ALREADY_ROLLED = 6;       // Someone else already rolled this back. $from and $summary will be set
-       const ONLY_AUTHOR = 7;          // User is the only author of the page
-       const RATE_LIMITED = 8;
        /**
         * Constructor and clear the article
         * @param $title Reference to a Title object.
@@ -2273,57 +2260,73 @@ class Article {
        /**
         * Roll back the most recent consecutive set of edits to a page
         * from the same user; fails if there are no eligible edits to
-        * roll back to, e.g. user is the sole contributor
+        * roll back to, e.g. user is the sole contributor. This function
+        * performs permissions checks on $wgUser, then calls commitRollback()
+        * to do the dirty work
         *
         * @param string $fromP - Name of the user whose edits to rollback. 
         * @param string $summary - Custom summary. Set to default summary if empty.
         * @param string $token - Rollback token.
         * @param bool $bot - If true, mark all reverted edits as bot.
         * 
-        * @param array $resultDetails contains result-specific dict of additional values
-        *    ALREADY_ROLLED : 'current' (rev)
-        *    SUCCESS        : 'summary' (str), 'current' (rev), 'target' (rev)
+        * @param array $resultDetails contains result-specific array of additional values
+        *    'alreadyrolled' : 'current' (rev)
+        *    success        : 'summary' (str), 'current' (rev), 'target' (rev)
         * 
-        * @return self::SUCCESS on succes, self::* on failure
+        * @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().
+        * NOTE: If the user is blocked, 'blocked' is passed as a message, but it doesn't exist. Be sure to check
+        *       it before calling showPermissionsErrorPage(). The same is true for 'actionthrottledtext', which
+        *       is passed if the rate limit is passed.
         */
        public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails ) {
-               global $wgUser, $wgUseRCPatrol;
+               global $wgUser;
                $resultDetails = null;
 
-               # Just in case it's being called from elsewhere         
-
-               if( $wgUser->isAllowed( 'rollback' ) && $this->mTitle->userCan( 'edit' ) ) {
-                       if( $wgUser->isBlocked() ) {
-                               return self::BLOCKED;
-                       }
-               } else {
-                       return self::PERM_DENIED;
-               }
-                       
-               if ( wfReadOnly() ) {
-                       return self::READONLY;
-               }
-
+               # Check permissions
+               $errors = array_merge( $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser ),
+                                               $this->mTitle->getUserPermissionsErrors( 'rollback', $wgUser ) );
                if( !$wgUser->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) )
-                       return self::BAD_TOKEN;
+                       $errors[] = array( 'sessionfailure' );
 
                if ( $wgUser->pingLimiter('rollback') || $wgUser->pingLimiter() ) {
-                       return self::RATE_LIMITED;
+                       $errors[] = array( 'actionthrottledtext' );
                }
+               if ( $wgUser->isBlocked() )
+                       $errors[] = array( 'blocked' );
 
+               # If there were errors, bail out now
+               if(!empty($errors))
+                       return $errors;
+               
+               return $this->commitRollback($fromP, $summary, $bot, $resultDetails);
+       }
+       
+       /**
+        * Backend implementation of doRollback(), please refer there for parameter and return value documentation
+        *
+        * NOTE: This function does NOT check ANY permissions, it just commits the rollback to the DB.
+        *       Therefore, you should only call this function directly if you really know what you're doing. If you don't, use doRollback() instead
+        */     
+       public function commitRollback($fromP, $summary, $bot, &$resultDetails) {
+               global $wgUseRCPatrol;
                $dbw = wfGetDB( DB_MASTER );
 
                # Get the last editor
                $current = Revision::newFromTitle( $this->mTitle );
                if( is_null( $current ) ) {
                        # Something wrong... no page?
-                       return self::BAD_TITLE;
+                       return array(array('notanarticle'));
                }
 
                $from = str_replace( '_', ' ', $fromP );
                if( $from != $current->getUserText() ) {
                        $resultDetails = array( 'current' => $current );
-                       return self::ALREADY_ROLLED;
+                       return array(array('alreadyrolled',
+                               htmlspecialchars($this->mTitle->getPrefixedText()),
+                               htmlspecialchars($fromP),
+                               htmlspecialchars($current->getUserText())
+                       ));
                }
 
                # Get the last edit not by this guy
@@ -2331,17 +2334,15 @@ class Article {
                $user_text = $dbw->addQuotes( $current->getUserText() );
                $s = $dbw->selectRow( 'revision',
                        array( 'rev_id', 'rev_timestamp' ),
-                       array(
-                               'rev_page' => $current->getPage(),
+                       array(  'rev_page' => $current->getPage(),
                                "rev_user <> {$user} OR rev_user_text <> {$user_text}"
                        ), __METHOD__,
-                       array(
-                               'USE INDEX' => 'page_timestamp',
+                       array(  'USE INDEX' => 'page_timestamp',
                                'ORDER BY'  => 'rev_timestamp DESC' )
                        );
                if( $s === false ) {
-                       # Something wrong
-                       return self::ONLY_AUTHOR;
+                       # No one else ever edited this page
+                       return array(array('cantrollback'));
                }
        
                $set = array();
@@ -2364,7 +2365,7 @@ class Article {
                                );
                }
 
-               # Get the edit summary
+               # Generate the edit summary if necessary
                $target = Revision::newFromId( $s->rev_id );
                if( empty( $summary ) )
                {
@@ -2393,7 +2394,7 @@ class Article {
                        'current' => $current,
                        'target' => $target,
                );
-               return self::SUCCESS;
+               return array();
        }
 
        /**
@@ -2401,19 +2402,8 @@ class Article {
         */
        function rollback() {
                global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
-
                $details = null;
 
-               # Skip the permissions-checking in doRollback() itself, by checking permissions here.
-
-               $perm_errors = array_merge( $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser ),
-                                               $this->mTitle->getUserPermissionsErrors( 'rollback', $wgUser ) );
-
-               if (count($perm_errors)) {
-                       $wgOut->showPermissionsErrorPage( $perm_errors );
-                       return;
-               }
-
                $result = $this->doRollback(
                        $wgRequest->getVal( 'from' ),
                        $wgRequest->getText( 'summary' ),
@@ -2421,62 +2411,30 @@ class Article {
                        $wgRequest->getBool( 'bot' ),
                        $details
                );
-
-               switch( $result ) {
-                       case self::BLOCKED:
-                               $wgOut->blockedPage();
-                               break;
-                       case self::PERM_DENIED:
-                               $wgOut->permissionRequired( 'rollback' );
-                               break;
-                       case self::READONLY:
-                               $wgOut->readOnlyPage( $this->getContent() );
-                               break;
-                       case self::BAD_TOKEN:
-                               $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
-                               $wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
-                               break;
-                       case self::BAD_TITLE:
-                               $wgOut->addHtml( wfMsg( 'notanarticle' ) );
-                               break;
-                       case self::ALREADY_ROLLED:
-                               $current = $details['current'];
-                               $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
-                               $wgOut->addWikiText(
-                                       wfMsg( 'alreadyrolled',
-                                               htmlspecialchars( $this->mTitle->getPrefixedText() ),
-                                               htmlspecialchars( $wgRequest->getVal( 'from' ) ),
-                                               htmlspecialchars( $current->getUserText() )
-                                       )
-                               );
-                               if( $current->getComment() != '' ) {
-                                       $wgOut->addHtml( wfMsg( 'editcomment',
-                                               $wgUser->getSkin()->formatComment( $current->getComment() ) ) );
-                               }
-                               break;
-                       case self::ONLY_AUTHOR:
-                               $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
-                               $wgOut->addHtml( wfMsg( 'cantrollback' ) );
-                               break;
-                       case self::RATE_LIMITED:
-                               $wgOut->rateLimited();
-                               break;
-                       case self::SUCCESS:
-                               $current = $details['current'];
-                               $target = $details['target'];
-                               $wgOut->setPageTitle( wfMsg( 'actioncomplete' ) );
-                               $wgOut->setRobotPolicy( 'noindex,nofollow' );
-                               $old = $wgUser->getSkin()->userLink( $current->getUser(), $current->getUserText() )
-                                       . $wgUser->getSkin()->userToolLinks( $current->getUser(), $current->getUserText() );
-                               $new = $wgUser->getSkin()->userLink( $target->getUser(), $target->getUserText() )
-                                       . $wgUser->getSkin()->userToolLinks( $target->getUser(), $target->getUserText() );
-                               $wgOut->addHtml( wfMsgExt( 'rollback-success', array( 'parse', 'replaceafter' ), $old, $new ) );
-                               $wgOut->returnToMain( false, $this->mTitle );
-                               break;
-                       default:
-                               throw new MWException( __METHOD__ . ": Unknown return value `{$result}`" );
+               
+               if(in_array(array('blocked'), $result)) {
+                       $wgOut->blockedPage();
+                       return;
+               }
+               if(in_array(array('actionthrottledtext'), $result)) {
+                       $wgOut->rateLimited();
+                       return;
+               }
+               if(!empty($result)) {
+                       $wgOut->showPermissionsErrorPage( $result );
+                       return;
                }
 
+               $current = $details['current'];
+               $target = $details['target'];
+               $wgOut->setPageTitle( wfMsg( 'actioncomplete' ) );
+               $wgOut->setRobotPolicy( 'noindex,nofollow' );
+               $old = $wgUser->getSkin()->userLink( $current->getUser(), $current->getUserText() )
+                       . $wgUser->getSkin()->userToolLinks( $current->getUser(), $current->getUserText() );
+               $new = $wgUser->getSkin()->userLink( $target->getUser(), $target->getUserText() )
+                       . $wgUser->getSkin()->userToolLinks( $target->getUser(), $target->getUserText() );
+               $wgOut->addHtml( wfMsgExt( 'rollback-success', array( 'parse', 'replaceafter' ), $old, $new ) );
+               $wgOut->returnToMain( false, $this->mTitle );
        }