Merge "Don't fallback from uk to ru"
[lhc/web/wiklou.git] / includes / api / ApiQueryRevisions.php
index 552ca3b..3259927 100644 (file)
@@ -56,13 +56,13 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                // If we're in a mode that breaks the same-origin policy, no tokens can
                // be obtained
                if ( $this->lacksSameOriginSecurity() ) {
-                       return array();
+                       return [];
                }
 
-               $this->tokenFunctions = array(
-                       'rollback' => array( 'ApiQueryRevisions', 'getRollbackToken' )
-               );
-               Hooks::run( 'APIQueryRevisionsTokens', array( &$this->tokenFunctions ) );
+               $this->tokenFunctions = [
+                       'rollback' => [ 'ApiQueryRevisions', 'getRollbackToken' ]
+               ];
+               Hooks::run( 'APIQueryRevisionsTokens', [ &$this->tokenFunctions ] );
 
                return $this->tokenFunctions;
        }
@@ -80,8 +80,7 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                        return false;
                }
 
-               return $wgUser->getEditToken(
-                       array( $title->getPrefixedText(), $rev->getUserText() ) );
+               return $wgUser->getEditToken( 'rollback' );
        }
 
        protected function run( ApiPageSet $resultPageSet = null ) {
@@ -91,10 +90,10 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                // Enum mode can only be used when exactly one page is provided.
                // Enumerating revisions on multiple pages make it extremely
                // difficult to manage continuations and require additional SQL indexes
-               $enumRevMode = ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ||
-                       !is_null( $params['limit'] ) || !is_null( $params['startid'] ) ||
-                       !is_null( $params['endid'] ) || $params['dir'] === 'newer' ||
-                       !is_null( $params['start'] ) || !is_null( $params['end'] ) );
+               $enumRevMode = ( $params['user'] !== null || $params['excludeuser'] !== null ||
+                       $params['limit'] !== null || $params['startid'] !== null ||
+                       $params['endid'] !== null || $params['dir'] === 'newer' ||
+                       $params['start'] !== null || $params['end'] !== null );
 
                $pageSet = $this->getPageSet();
                $pageCount = $pageSet->getGoodTitleCount();
@@ -135,9 +134,9 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                }
 
                $db = $this->getDB();
-               $this->addTables( array( 'revision', 'page' ) );
+               $this->addTables( [ 'revision', 'page' ] );
                $this->addJoinConds(
-                       array( 'page' => array( 'INNER JOIN', array( 'page_id = rev_page' ) ) )
+                       [ 'page' => [ 'INNER JOIN', [ 'page_id = rev_page' ] ] ]
                );
 
                if ( $resultPageSet === null ) {
@@ -149,21 +148,21 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                        }
                } else {
                        $this->limit = $this->getParameter( 'limit' ) ?: 10;
-                       $this->addFields( array( 'rev_id', 'rev_page' ) );
+                       $this->addFields( [ 'rev_id', 'rev_timestamp', 'rev_page' ] );
                }
 
                if ( $this->fld_tags ) {
                        $this->addTables( 'tag_summary' );
                        $this->addJoinConds(
-                               array( 'tag_summary' => array( 'LEFT JOIN', array( 'rev_id=ts_rev_id' ) ) )
+                               [ 'tag_summary' => [ 'LEFT JOIN', [ 'rev_id=ts_rev_id' ] ] ]
                        );
                        $this->addFields( 'ts_tags' );
                }
 
-               if ( !is_null( $params['tag'] ) ) {
+               if ( $params['tag'] !== null ) {
                        $this->addTables( 'change_tag' );
                        $this->addJoinConds(
-                               array( 'change_tag' => array( 'INNER JOIN', array( 'rev_id=ct_rev_id' ) ) )
+                               [ 'change_tag' => [ 'INNER JOIN', [ 'rev_id=ct_rev_id' ] ] ]
                        );
                        $this->addWhereFld( 'ct_tag', $params['tag'] );
                }
@@ -182,7 +181,7 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
 
                        $this->addTables( 'text' );
                        $this->addJoinConds(
-                               array( 'text' => array( 'INNER JOIN', array( 'rev_text_id=old_id' ) ) )
+                               [ 'text' => [ 'INNER JOIN', [ 'rev_text_id=old_id' ] ] ]
                        );
                        $this->addFields( 'old_id' );
                        $this->addFields( Revision::selectTextFields() );
@@ -191,63 +190,68 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                // add user name, if needed
                if ( $this->fld_user ) {
                        $this->addTables( 'user' );
-                       $this->addJoinConds( array( 'user' => Revision::userJoinCond() ) );
+                       $this->addJoinConds( [ 'user' => Revision::userJoinCond() ] );
                        $this->addFields( Revision::selectUserFields() );
                }
 
                if ( $enumRevMode ) {
+                       // Indexes targeted:
+                       //  page_timestamp if we don't have rvuser
+                       //  page_user_timestamp if we have a logged-in rvuser
+                       //  page_timestamp or usertext_timestamp if we have an IP rvuser
+
                        // This is mostly to prevent parameter errors (and optimize SQL?)
-                       if ( !is_null( $params['startid'] ) && !is_null( $params['start'] ) ) {
+                       if ( $params['startid'] !== null && $params['start'] !== null ) {
                                $this->dieUsage( 'start and startid cannot be used together', 'badparams' );
                        }
 
-                       if ( !is_null( $params['endid'] ) && !is_null( $params['end'] ) ) {
+                       if ( $params['endid'] !== null && $params['end'] !== null ) {
                                $this->dieUsage( 'end and endid cannot be used together', 'badparams' );
                        }
 
-                       if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) {
+                       if ( $params['user'] !== null && $params['excludeuser'] !== null ) {
                                $this->dieUsage( 'user and excludeuser cannot be used together', 'badparams' );
                        }
 
-                       // Continuing effectively uses startid. But we can't use rvstartid
-                       // directly, because there is no way to tell the client to ''not''
-                       // send rvstart if it sent it in the original query. So instead we
-                       // send the continuation startid as rvcontinue, and ignore both
-                       // rvstart and rvstartid when that is supplied.
-                       if ( !is_null( $params['continue'] ) ) {
-                               $params['startid'] = $params['continue'];
-                               $params['start'] = null;
+                       if ( $params['continue'] !== null ) {
+                               $cont = explode( '|', $params['continue'] );
+                               $this->dieContinueUsageIf( count( $cont ) != 2 );
+                               $op = ( $params['dir'] === 'newer' ? '>' : '<' );
+                               $continueTimestamp = $db->addQuotes( $db->timestamp( $cont[0] ) );
+                               $continueId = (int)$cont[1];
+                               $this->dieContinueUsageIf( $continueId != $cont[1] );
+                               $this->addWhere( "rev_timestamp $op $continueTimestamp OR " .
+                                       "(rev_timestamp = $continueTimestamp AND " .
+                                       "rev_id $op= $continueId)"
+                               );
                        }
 
-                       // This code makes an assumption that sorting by rev_id and rev_timestamp produces
-                       // the same result. This way users may request revisions starting at a given time,
-                       // but to page through results use the rev_id returned after each page.
-                       // Switching to rev_id removes the potential problem of having more than
-                       // one row with the same timestamp for the same page.
-                       // The order needs to be the same as start parameter to avoid SQL filesort.
-                       if ( is_null( $params['startid'] ) && is_null( $params['endid'] ) ) {
-                               $this->addTimestampWhereRange( 'rev_timestamp', $params['dir'],
-                                       $params['start'], $params['end'] );
-                       } else {
-                               $this->addWhereRange( 'rev_id', $params['dir'],
-                                       $params['startid'], $params['endid'] );
-                               // One of start and end can be set
-                               // If neither is set, this does nothing
-                               $this->addTimestampWhereRange( 'rev_timestamp', $params['dir'],
-                                       $params['start'], $params['end'], false );
-                       }
+                       $this->addTimestampWhereRange( 'rev_timestamp', $params['dir'],
+                               $params['start'], $params['end'] );
+                       $this->addWhereRange( 'rev_id', $params['dir'],
+                               $params['startid'], $params['endid'] );
 
                        // There is only one ID, use it
                        $ids = array_keys( $pageSet->getGoodTitles() );
                        $this->addWhereFld( 'rev_page', reset( $ids ) );
 
-                       if ( !is_null( $params['user'] ) ) {
-                               $this->addWhereFld( 'rev_user_text', $params['user'] );
-                       } elseif ( !is_null( $params['excludeuser'] ) ) {
-                               $this->addWhere( 'rev_user_text != ' .
-                                       $db->addQuotes( $params['excludeuser'] ) );
+                       if ( $params['user'] !== null ) {
+                               $user = User::newFromName( $params['user'] );
+                               if ( $user && $user->getId() > 0 ) {
+                                       $this->addWhereFld( 'rev_user', $user->getId() );
+                               } else {
+                                       $this->addWhereFld( 'rev_user_text', $params['user'] );
+                               }
+                       } elseif ( $params['excludeuser'] !== null ) {
+                               $user = User::newFromName( $params['excludeuser'] );
+                               if ( $user && $user->getId() > 0 ) {
+                                       $this->addWhere( 'rev_user != ' . $user->getId() );
+                               } else {
+                                       $this->addWhere( 'rev_user_text != ' .
+                                               $db->addQuotes( $params['excludeuser'] ) );
+                               }
                        }
-                       if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
+                       if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
                                // Paranoia: avoid brute force searches (bug 17342)
                                if ( !$this->getUser()->isAllowed( 'deletedhistory' ) ) {
                                        $bitmask = Revision::DELETED_USER;
@@ -261,16 +265,20 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                                }
                        }
                } elseif ( $revCount > 0 ) {
+                       // Always targets the PRIMARY index
+
                        $revs = $pageSet->getLiveRevisionIDs();
 
                        // Get all revision IDs
                        $this->addWhereFld( 'rev_id', array_keys( $revs ) );
 
-                       if ( !is_null( $params['continue'] ) ) {
+                       if ( $params['continue'] !== null ) {
                                $this->addWhere( 'rev_id >= ' . intval( $params['continue'] ) );
                        }
                        $this->addOption( 'ORDER BY', 'rev_id' );
                } elseif ( $pageCount > 0 ) {
+                       // Always targets the rev_page_id index
+
                        $titles = $pageSet->getGoodTitles();
 
                        // When working in multi-page non-enumeration mode,
@@ -282,7 +290,7 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                        // Every time someone relies on equality propagation, god kills a kitten :)
                        $this->addWhereFld( 'rev_page', array_keys( $titles ) );
 
-                       if ( !is_null( $params['continue'] ) ) {
+                       if ( $params['continue'] !== null ) {
                                $cont = explode( '|', $params['continue'] );
                                $this->dieContinueUsageIf( count( $cont ) != 2 );
                                $pageid = intval( $cont[0] );
@@ -293,10 +301,10 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                                        "rev_id >= $revid)"
                                );
                        }
-                       $this->addOption( 'ORDER BY', array(
+                       $this->addOption( 'ORDER BY', [
                                'rev_page',
                                'rev_id'
-                       ) );
+                       ] );
                } else {
                        ApiBase::dieDebug( __METHOD__, 'param validation?' );
                }
@@ -304,15 +312,17 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                $this->addOption( 'LIMIT', $this->limit + 1 );
 
                $count = 0;
-               $generated = array();
-               $res = $this->select( __METHOD__ );
+               $generated = [];
+               $hookData = [];
+               $res = $this->select( __METHOD__, [], $hookData );
 
                foreach ( $res as $row ) {
                        if ( ++$count > $this->limit ) {
                                // We've reached the one extra which shows that there are
                                // additional pages to be had. Stop here...
                                if ( $enumRevMode ) {
-                                       $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
+                                       $this->setContinueEnumParameter( 'continue',
+                                               $row->rev_timestamp . '|' . intval( $row->rev_id ) );
                                } elseif ( $revCount > 0 ) {
                                        $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
                                } else {
@@ -341,10 +351,12 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                                        }
                                }
 
-                               $fit = $this->addPageSubItem( $row->rev_page, $rev, 'rev' );
+                               $fit = $this->processRow( $row, $rev, $hookData ) &&
+                                       $this->addPageSubItem( $row->rev_page, $rev, 'rev' );
                                if ( !$fit ) {
                                        if ( $enumRevMode ) {
-                                               $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
+                                               $this->setContinueEnumParameter( 'continue',
+                                                       $row->rev_timestamp . '|' . intval( $row->rev_id ) );
                                        } elseif ( $revCount > 0 ) {
                                                $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
                                        } else {
@@ -369,58 +381,58 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
        }
 
        public function getAllowedParams() {
-               $ret = parent::getAllowedParams() + array(
-                       'startid' => array(
+               $ret = parent::getAllowedParams() + [
+                       'startid' => [
                                ApiBase::PARAM_TYPE => 'integer',
-                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'singlepageonly' ) ),
-                       ),
-                       'endid' => array(
+                               ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ],
+                       ],
+                       'endid' => [
                                ApiBase::PARAM_TYPE => 'integer',
-                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'singlepageonly' ) ),
-                       ),
-                       'start' => array(
+                               ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ],
+                       ],
+                       'start' => [
                                ApiBase::PARAM_TYPE => 'timestamp',
-                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'singlepageonly' ) ),
-                       ),
-                       'end' => array(
+                               ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ],
+                       ],
+                       'end' => [
                                ApiBase::PARAM_TYPE => 'timestamp',
-                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'singlepageonly' ) ),
-                       ),
-                       'dir' => array(
+                               ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ],
+                       ],
+                       'dir' => [
                                ApiBase::PARAM_DFLT => 'older',
-                               ApiBase::PARAM_TYPE => array(
+                               ApiBase::PARAM_TYPE => [
                                        'newer',
                                        'older'
-                               ),
+                               ],
                                ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
-                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'singlepageonly' ) ),
-                       ),
-                       'user' => array(
+                               ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ],
+                       ],
+                       'user' => [
                                ApiBase::PARAM_TYPE => 'user',
-                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'singlepageonly' ) ),
-                       ),
-                       'excludeuser' => array(
+                               ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ],
+                       ],
+                       'excludeuser' => [
                                ApiBase::PARAM_TYPE => 'user',
-                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'singlepageonly' ) ),
-                       ),
+                               ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ],
+                       ],
                        'tag' => null,
-                       'token' => array(
+                       'token' => [
                                ApiBase::PARAM_DEPRECATED => true,
                                ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ),
                                ApiBase::PARAM_ISMULTI => true
-                       ),
-                       'continue' => array(
+                       ],
+                       'continue' => [
                                ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
-                       ),
-               );
+                       ],
+               ];
 
-               $ret['limit'][ApiBase::PARAM_HELP_MSG_INFO] = array( array( 'singlepageonly' ) );
+               $ret['limit'][ApiBase::PARAM_HELP_MSG_INFO] = [ [ 'singlepageonly' ] ];
 
                return $ret;
        }
 
        protected function getExamplesMessages() {
-               return array(
+               return [
                        'action=query&prop=revisions&titles=API|Main%20Page&' .
                                'rvprop=timestamp|user|comment|content'
                                => 'apihelp-query+revisions-example-content',
@@ -439,10 +451,10 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                        'action=query&prop=revisions&titles=Main%20Page&rvlimit=5&' .
                                'rvprop=timestamp|user|comment&rvuser=MediaWiki%20default'
                                => 'apihelp-query+revisions-example-first5-user',
-               );
+               ];
        }
 
        public function getHelpUrls() {
-               return 'https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv';
+               return 'https://www.mediawiki.org/wiki/API:Revisions';
        }
 }