Merge "Vector: Update comments in vector.js"
[lhc/web/wiklou.git] / includes / api / ApiQueryRecentChanges.php
index b298003..1fb2a69 100644 (file)
@@ -32,7 +32,7 @@
  */
 class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
 
-       public function __construct( $query, $moduleName ) {
+       public function __construct( ApiQuery $query, $moduleName ) {
                parent::__construct( $query, $moduleName, 'rc' );
        }
 
@@ -69,9 +69,9 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
        }
 
        /**
-        * @param  $pageid
-        * @param  $title
-        * @param $rc RecentChange (optional)
+        * @param int $pageid
+        * @param Title $title
+        * @param RecentChange|null $rc
         * @return bool|string
         */
        public static function getPatrolToken( $pageid, $title, $rc = null ) {
@@ -135,7 +135,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
        /**
         * Generates and outputs the result of this query based upon the provided parameters.
         *
-        * @param $resultPageSet ApiPageSet
+        * @param ApiPageSet $resultPageSet
         */
        public function run( $resultPageSet = null ) {
                $user = $this->getUser();
@@ -145,7 +145,6 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                /* Build our basic query. Namely, something along the lines of:
                 * SELECT * FROM recentchanges WHERE rc_timestamp > $start
                 *              AND rc_timestamp < $end AND rc_namespace = $namespace
-                *              AND rc_deleted = 0
                 */
                $this->addTables( 'recentchanges' );
                $index = array( 'recentchanges' => 'rc_timestamp' ); // May change
@@ -153,15 +152,12 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
 
                if ( !is_null( $params['continue'] ) ) {
                        $cont = explode( '|', $params['continue'] );
-                       if ( count( $cont ) != 2 ) {
-                               $this->dieUsage( 'Invalid continue param. You should pass the ' .
-                                       'original value returned by the previous query', '_badcontinue' );
-                       }
-
-                       $timestamp = $this->getDB()->addQuotes( wfTimestamp( TS_MW, $cont[0] ) );
+                       $this->dieContinueUsageIf( count( $cont ) != 2 );
+                       $db = $this->getDB();
+                       $timestamp = $db->addQuotes( $db->timestamp( $cont[0] ) );
                        $id = intval( $cont[1] );
+                       $this->dieContinueUsageIf( $id != $cont[1] );
                        $op = $params['dir'] === 'older' ? '<' : '>';
-
                        $this->addWhere(
                                "rc_timestamp $op $timestamp OR " .
                                "(rc_timestamp = $timestamp AND " .
@@ -176,10 +172,13 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                ) );
 
                $this->addWhereFld( 'rc_namespace', $params['namespace'] );
-               $this->addWhereFld( 'rc_deleted', 0 );
 
                if ( !is_null( $params['type'] ) ) {
-                       $this->addWhereFld( 'rc_type', $this->parseRCType( $params['type'] ) );
+                       try {
+                               $this->addWhereFld( 'rc_type', RecentChange::parseToRCType( $params['type'] ) );
+                       } catch ( MWException $e ) {
+                               ApiBase::dieDebug( __METHOD__, $e->getMessage() );
+                       }
                }
 
                if ( !is_null( $params['show'] ) ) {
@@ -256,6 +255,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
 
                /* Add the fields we're concerned with to our query. */
                $this->addFields( array(
+                       'rc_id',
                        'rc_timestamp',
                        'rc_namespace',
                        'rc_title',
@@ -279,12 +279,11 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                                );
                        }
 
-                       $this->addFields( 'rc_id' );
                        /* Add fields to our query if they are specified as a needed parameter. */
                        $this->addFieldsIf( array( 'rc_this_oldid', 'rc_last_oldid' ), $this->fld_ids );
                        $this->addFieldsIf( 'rc_comment', $this->fld_comment || $this->fld_parsedcomment );
-                       $this->addFieldsIf( 'rc_user', $this->fld_user );
-                       $this->addFieldsIf( 'rc_user_text', $this->fld_user || $this->fld_userid );
+                       $this->addFieldsIf( 'rc_user', $this->fld_user || $this->fld_userid );
+                       $this->addFieldsIf( 'rc_user_text', $this->fld_user );
                        $this->addFieldsIf( array( 'rc_minor', 'rc_type', 'rc_bot' ), $this->fld_flags );
                        $this->addFieldsIf( array( 'rc_old_len', 'rc_new_len' ), $this->fld_sizes );
                        $this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
@@ -326,6 +325,36 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                        $this->addWhereFld( 'ct_tag', $params['tag'] );
                }
 
+               // Paranoia: avoid brute force searches (bug 17342)
+               if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
+                       if ( !$user->isAllowed( 'deletedhistory' ) ) {
+                               $bitmask = Revision::DELETED_USER;
+                       } elseif ( !$user->isAllowed( 'suppressrevision' ) ) {
+                               $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
+                       } else {
+                               $bitmask = 0;
+                       }
+                       if ( $bitmask ) {
+                               $this->addWhere( $this->getDB()->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask" );
+                       }
+               }
+               if ( $this->getRequest()->getCheck( 'namespace' ) ) {
+                       // LogPage::DELETED_ACTION hides the affected page, too.
+                       if ( !$user->isAllowed( 'deletedhistory' ) ) {
+                               $bitmask = LogPage::DELETED_ACTION;
+                       } elseif ( !$user->isAllowed( 'suppressrevision' ) ) {
+                               $bitmask = LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED;
+                       } else {
+                               $bitmask = 0;
+                       }
+                       if ( $bitmask ) {
+                               $this->addWhere( $this->getDB()->makeList( array(
+                                       'rc_type != ' . RC_LOG,
+                                       $this->getDB()->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask",
+                               ), LIST_OR ) );
+                       }
+               }
+
                $this->token = $params['token'];
                $this->addOption( 'LIMIT', $params['limit'] + 1 );
                $this->addOption( 'USE INDEX', $index );
@@ -343,10 +372,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                        if ( ++$count > $params['limit'] ) {
                                // We've reached the one extra which shows that there are
                                // additional pages to be had. Stop here...
-                               $this->setContinueEnumParameter(
-                                       'continue',
-                                       wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) . '|' . $row->rc_id
-                               );
+                               $this->setContinueEnumParameter( 'continue', "$row->rc_timestamp|$row->rc_id" );
                                break;
                        }
 
@@ -360,10 +386,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                                }
                                $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
                                if ( !$fit ) {
-                                       $this->setContinueEnumParameter(
-                                               'continue',
-                                               wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) . '|' . $row->rc_id
-                                       );
+                                       $this->setContinueEnumParameter( 'continue', "$row->rc_timestamp|$row->rc_id" );
                                        break;
                                }
                        } else {
@@ -389,62 +412,58 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
        public function extractRowInfo( $row ) {
                /* Determine the title of the page that has been changed. */
                $title = Title::makeTitle( $row->rc_namespace, $row->rc_title );
+               $user = $this->getUser();
 
                /* Our output data. */
                $vals = array();
 
                $type = intval( $row->rc_type );
+               $vals['type'] = RecentChange::parseFromRCType( $type );
 
-               /* Determine what kind of change this was. */
-               switch ( $type ) {
-                       case RC_EDIT:
-                               $vals['type'] = 'edit';
-                               break;
-                       case RC_NEW:
-                               $vals['type'] = 'new';
-                               break;
-                       case RC_MOVE:
-                               $vals['type'] = 'move';
-                               break;
-                       case RC_LOG:
-                               $vals['type'] = 'log';
-                               break;
-                       case RC_EXTERNAL:
-                               $vals['type'] = 'external';
-                               break;
-                       case RC_MOVE_OVER_REDIRECT:
-                               $vals['type'] = 'move over redirect';
-                               break;
-                       default:
-                               $vals['type'] = $type;
-               }
+               $anyHidden = false;
 
                /* Create a new entry in the result for the title. */
-               if ( $this->fld_title ) {
-                       ApiQueryBase::addTitleInfo( $vals, $title );
+               if ( $this->fld_title || $this->fld_ids ) {
+                       if ( $type === RC_LOG && ( $row->rc_deleted & LogPage::DELETED_ACTION ) ) {
+                               $vals['actionhidden'] = '';
+                               $anyHidden = true;
+                       }
+                       if ( $type !== RC_LOG ||
+                               LogEventsList::userCanBitfield( $row->rc_deleted, LogPage::DELETED_ACTION, $user )
+                       ) {
+                               if ( $this->fld_title ) {
+                                       ApiQueryBase::addTitleInfo( $vals, $title );
+                               }
+                               if ( $this->fld_ids ) {
+                                       $vals['pageid'] = intval( $row->rc_cur_id );
+                                       $vals['revid'] = intval( $row->rc_this_oldid );
+                                       $vals['old_revid'] = intval( $row->rc_last_oldid );
+                               }
+                       }
                }
 
-               /* Add ids, such as rcid, pageid, revid, and oldid to the change's info. */
                if ( $this->fld_ids ) {
                        $vals['rcid'] = intval( $row->rc_id );
-                       $vals['pageid'] = intval( $row->rc_cur_id );
-                       $vals['revid'] = intval( $row->rc_this_oldid );
-                       $vals['old_revid'] = intval( $row->rc_last_oldid );
                }
 
-               /* Add user data and 'anon' flag, if use is anonymous. */
+               /* Add user data and 'anon' flag, if user is anonymous. */
                if ( $this->fld_user || $this->fld_userid ) {
-
-                       if ( $this->fld_user ) {
-                               $vals['user'] = $row->rc_user_text;
+                       if ( $row->rc_deleted & Revision::DELETED_USER ) {
+                               $vals['userhidden'] = '';
+                               $anyHidden = true;
                        }
+                       if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_USER, $user ) ) {
+                               if ( $this->fld_user ) {
+                                       $vals['user'] = $row->rc_user_text;
+                               }
 
-                       if ( $this->fld_userid ) {
-                               $vals['userid'] = $row->rc_user;
-                       }
+                               if ( $this->fld_userid ) {
+                                       $vals['userid'] = $row->rc_user;
+                               }
 
-                       if ( !$row->rc_user ) {
-                               $vals['anon'] = '';
+                               if ( !$row->rc_user ) {
+                                       $vals['anon'] = '';
+                               }
                        }
                }
 
@@ -473,12 +492,20 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                }
 
                /* Add edit summary / log summary. */
-               if ( $this->fld_comment && isset( $row->rc_comment ) ) {
-                       $vals['comment'] = $row->rc_comment;
-               }
+               if ( $this->fld_comment || $this->fld_parsedcomment ) {
+                       if ( $row->rc_deleted & Revision::DELETED_COMMENT ) {
+                               $vals['commenthidden'] = '';
+                               $anyHidden = true;
+                       }
+                       if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_COMMENT, $user ) ) {
+                               if ( $this->fld_comment && isset( $row->rc_comment ) ) {
+                                       $vals['comment'] = $row->rc_comment;
+                               }
 
-               if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) {
-                       $vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title );
+                               if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) {
+                                       $vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title );
+                               }
+                       }
                }
 
                if ( $this->fld_redirect ) {
@@ -492,23 +519,29 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                        $vals['patrolled'] = '';
                }
 
-               if ( $this->fld_patrolled && ChangesList::isUnpatrolled( $row, $this->getUser() ) ) {
+               if ( $this->fld_patrolled && ChangesList::isUnpatrolled( $row, $user ) ) {
                        $vals['unpatrolled'] = '';
                }
 
                if ( $this->fld_loginfo && $row->rc_type == RC_LOG ) {
-                       $vals['logid'] = intval( $row->rc_logid );
-                       $vals['logtype'] = $row->rc_log_type;
-                       $vals['logaction'] = $row->rc_log_action;
-                       $logEntry = DatabaseLogEntry::newFromRow( (array)$row );
-                       ApiQueryLogEvents::addLogParams(
-                               $this->getResult(),
-                               $vals,
-                               $logEntry->getParameters(),
-                               $logEntry->getType(),
-                               $logEntry->getSubtype(),
-                               $logEntry->getTimestamp()
-                       );
+                       if ( $row->rc_deleted & LogPage::DELETED_ACTION ) {
+                               $vals['actionhidden'] = '';
+                               $anyHidden = true;
+                       }
+                       if ( LogEventsList::userCanBitfield( $row->rc_deleted, LogPage::DELETED_ACTION, $user ) ) {
+                               $vals['logid'] = intval( $row->rc_logid );
+                               $vals['logtype'] = $row->rc_log_type;
+                               $vals['logaction'] = $row->rc_log_action;
+                               $logEntry = DatabaseLogEntry::newFromRow( (array)$row );
+                               ApiQueryLogEvents::addLogParams(
+                                       $this->getResult(),
+                                       $vals,
+                                       $logEntry->getParameters(),
+                                       $logEntry->getType(),
+                                       $logEntry->getSubtype(),
+                                       $logEntry->getTimestamp()
+                               );
+                       }
                }
 
                if ( $this->fld_tags ) {
@@ -522,15 +555,16 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                }
 
                if ( $this->fld_sha1 && $row->rev_sha1 !== null ) {
-                       // The RevDel check should currently never pass due to the
-                       // rc_deleted = 0 condition in the WHERE clause, but in case that
-                       // ever changes we check it here too.
                        if ( $row->rev_deleted & Revision::DELETED_TEXT ) {
                                $vals['sha1hidden'] = '';
-                       } elseif ( $row->rev_sha1 !== '' ) {
-                               $vals['sha1'] = wfBaseConvert( $row->rev_sha1, 36, 16, 40 );
-                       } else {
-                               $vals['sha1'] = '';
+                               $anyHidden = true;
+                       }
+                       if ( Revision::userCanBitfield( $row->rev_deleted, Revision::DELETED_TEXT, $user ) ) {
+                               if ( $row->rev_sha1 !== '' ) {
+                                       $vals['sha1'] = wfBaseConvert( $row->rev_sha1, 36, 16, 40 );
+                               } else {
+                                       $vals['sha1'] = '';
+                               }
                        }
                }
 
@@ -547,31 +581,11 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                        }
                }
 
-               return $vals;
-       }
-
-       private function parseRCType( $type ) {
-               if ( is_array( $type ) ) {
-                       $retval = array();
-                       foreach ( $type as $t ) {
-                               $retval[] = $this->parseRCType( $t );
-                       }
-
-                       return $retval;
+               if ( $anyHidden && ( $row->rc_deleted & Revision::DELETED_RESTRICTED ) ) {
+                       $vals['suppressed'] = '';
                }
 
-               switch ( $type ) {
-                       case 'edit':
-                               return RC_EDIT;
-                       case 'new':
-                               return RC_NEW;
-                       case 'log':
-                               return RC_LOG;
-                       case 'external':
-                               return RC_EXTERNAL;
-                       default:
-                               ApiBase::dieDebug( __METHOD__, "Unknown type '$type'" );
-               }
+               return $vals;
        }
 
        public function getCacheMode( $params ) {
@@ -585,6 +599,9 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                if ( isset( $params['token'] ) ) {
                        return 'private';
                }
+               if ( $this->userCanSeeRevDel() ) {
+                       return 'private';
+               }
                if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) {
                        // formatComment() calls wfMessage() among other things
                        return 'anon-public-user-private';
@@ -823,7 +840,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
        }
 
        public function getDescription() {
-               return 'Enumerate recent changes';
+               return 'Enumerate recent changes.';
        }
 
        public function getPossibleErrors() {