// 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;
}
return false;
}
- return $wgUser->getEditToken(
- array( $title->getPrefixedText(), $rev->getUserText() ) );
+ return $wgUser->getEditToken( 'rollback' );
}
protected function run( ApiPageSet $resultPageSet = null ) {
// 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();
}
$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 ) {
}
} 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'] );
}
$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() );
// 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;
}
}
} 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,
// 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] );
"rev_id >= $revid)"
);
}
- $this->addOption( 'ORDER BY', array(
+ $this->addOption( 'ORDER BY', [
'rev_page',
'rev_id'
- ) );
+ ] );
} else {
ApiBase::dieDebug( __METHOD__, 'param validation?' );
}
$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 {
}
}
- $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 {
}
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',
'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';
}
}