Go to the documentation of this file.
36 parent::__construct( $query, $moduleName,
'rc' );
57 if ( isset( $this->tokenFunctions ) ) {
67 $this->tokenFunctions = [
68 'patrol' => [ self::class,
'getPatrolToken' ]
70 Hooks::run(
'APIQueryRecentChangesTokens', [ &$this->tokenFunctions ] );
85 $validTokenUser =
false;
88 if ( ( $wgUser->useRCPatrol() && $rc->getAttribute(
'rc_type' ) ==
RC_EDIT ) ||
89 ( $wgUser->useNPPatrol() && $rc->getAttribute(
'rc_type' ) ==
RC_NEW )
91 $validTokenUser =
true;
93 } elseif ( $wgUser->useRCPatrol() || $wgUser->useNPPatrol() ) {
94 $validTokenUser =
true;
97 if ( $validTokenUser ) {
99 static $cachedPatrolToken =
null;
101 if ( is_null( $cachedPatrolToken ) ) {
102 $cachedPatrolToken = $wgUser->getEditToken(
'patrol' );
105 return $cachedPatrolToken;
116 $this->fld_comment = isset( $prop[
'comment'] );
117 $this->fld_parsedcomment = isset( $prop[
'parsedcomment'] );
118 $this->fld_user = isset( $prop[
'user'] );
119 $this->fld_userid = isset( $prop[
'userid'] );
120 $this->fld_flags = isset( $prop[
'flags'] );
121 $this->fld_timestamp = isset( $prop[
'timestamp'] );
122 $this->fld_title = isset( $prop[
'title'] );
123 $this->fld_ids = isset( $prop[
'ids'] );
124 $this->fld_sizes = isset( $prop[
'sizes'] );
125 $this->fld_redirect = isset( $prop[
'redirect'] );
126 $this->fld_patrolled = isset( $prop[
'patrolled'] );
127 $this->fld_loginfo = isset( $prop[
'loginfo'] );
128 $this->fld_tags = isset( $prop[
'tags'] );
129 $this->fld_sha1 = isset( $prop[
'sha1'] );
137 $this->
run( $resultPageSet );
145 public function run( $resultPageSet =
null ) {
157 if ( !is_null( $params[
'continue'] ) ) {
158 $cont = explode(
'|', $params[
'continue'] );
160 $db = $this->
getDB();
161 $timestamp = $db->addQuotes( $db->timestamp( $cont[0] ) );
164 $op = $params[
'dir'] ===
'older' ?
'<' :
'>';
166 "rc_timestamp $op $timestamp OR " .
167 "(rc_timestamp = $timestamp AND " .
172 $order = $params[
'dir'] ===
'older' ?
'DESC' :
'ASC';
174 "rc_timestamp $order",
178 $this->
addWhereFld(
'rc_namespace', $params[
'namespace'] );
180 if ( !is_null( $params[
'type'] ) ) {
183 }
catch ( Exception $e ) {
188 $title = $params[
'title'];
189 if ( !is_null(
$title ) ) {
191 if ( is_null( $titleObj ) ) {
194 $this->
addWhereFld(
'rc_namespace', $titleObj->getNamespace() );
195 $this->
addWhereFld(
'rc_title', $titleObj->getDBkey() );
198 if ( !is_null( $params[
'show'] ) ) {
199 $show = array_flip( $params[
'show'] );
202 if ( ( isset( $show[
'minor'] ) && isset( $show[
'!minor'] ) )
203 || ( isset( $show[
'bot'] ) && isset( $show[
'!bot'] ) )
204 || ( isset( $show[
'anon'] ) && isset( $show[
'!anon'] ) )
205 || ( isset( $show[
'redirect'] ) && isset( $show[
'!redirect'] ) )
206 || ( isset( $show[
'patrolled'] ) && isset( $show[
'!patrolled'] ) )
207 || ( isset( $show[
'patrolled'] ) && isset( $show[
'unpatrolled'] ) )
208 || ( isset( $show[
'!patrolled'] ) && isset( $show[
'unpatrolled'] ) )
209 || ( isset( $show[
'autopatrolled'] ) && isset( $show[
'!autopatrolled'] ) )
210 || ( isset( $show[
'autopatrolled'] ) && isset( $show[
'unpatrolled'] ) )
211 || ( isset( $show[
'autopatrolled'] ) && isset( $show[
'!patrolled'] ) )
218 if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) {
219 $this->
dieWithError(
'apierror-permissiondenied-patrolflag',
'permissiondenied' );
224 $this->
addWhereIf(
'rc_minor = 0', isset( $show[
'!minor'] ) );
225 $this->
addWhereIf(
'rc_minor != 0', isset( $show[
'minor'] ) );
226 $this->
addWhereIf(
'rc_bot = 0', isset( $show[
'!bot'] ) );
227 $this->
addWhereIf(
'rc_bot != 0', isset( $show[
'bot'] ) );
228 if ( isset( $show[
'anon'] ) || isset( $show[
'!anon'] ) ) {
230 $actorQuery = $actorMigration->getJoin(
'rc_user' );
231 $this->
addTables( $actorQuery[
'tables'] );
234 $actorMigration->isAnon( $actorQuery[
'fields'][
'rc_user'] ), isset( $show[
'anon'] )
237 $actorMigration->isNotAnon( $actorQuery[
'fields'][
'rc_user'] ), isset( $show[
'!anon'] )
240 $this->
addWhereIf(
'rc_patrolled = 0', isset( $show[
'!patrolled'] ) );
241 $this->
addWhereIf(
'rc_patrolled != 0', isset( $show[
'patrolled'] ) );
242 $this->
addWhereIf(
'page_is_redirect = 1', isset( $show[
'redirect'] ) );
244 if ( isset( $show[
'unpatrolled'] ) ) {
246 if ( $user->useRCPatrol() ) {
248 } elseif ( $user->useNPPatrol() ) {
256 isset( $show[
'!autopatrolled'] )
260 isset( $show[
'autopatrolled'] )
265 'page_is_redirect = 0 OR page_is_redirect IS NULL',
266 isset( $show[
'!redirect'] )
272 if ( !is_null( $params[
'user'] ) ) {
276 $this->
addTables( $actorQuery[
'tables'] );
278 $this->
addWhere( $actorQuery[
'conds'] );
281 if ( !is_null( $params[
'excludeuser'] ) ) {
285 $this->
addTables( $actorQuery[
'tables'] );
287 $this->
addWhere(
'NOT(' . $actorQuery[
'conds'] .
')' );
301 $showRedirects =
false;
303 if ( !is_null( $params[
'prop'] ) ) {
304 $prop = array_flip( $params[
'prop'] );
309 if ( $this->fld_patrolled && !$user->useRCPatrol() && !$user->useNPPatrol() ) {
310 $this->
dieWithError(
'apierror-permissiondenied-patrolflag',
'permissiondenied' );
314 $this->
addFieldsIf( [
'rc_this_oldid',
'rc_last_oldid' ], $this->fld_ids );
315 $this->
addFieldsIf( [
'rc_minor',
'rc_type',
'rc_bot' ], $this->fld_flags );
316 $this->
addFieldsIf( [
'rc_old_len',
'rc_new_len' ], $this->fld_sizes );
317 $this->
addFieldsIf( [
'rc_patrolled',
'rc_log_type' ], $this->fld_patrolled );
319 [
'rc_logid',
'rc_log_type',
'rc_log_action',
'rc_params' ],
322 $showRedirects = $this->fld_redirect || isset( $show[
'redirect'] )
323 || isset( $show[
'!redirect'] );
326 $resultPageSet && $params[
'generaterevisions'] );
328 if ( $this->fld_tags ) {
332 if ( $this->fld_sha1 ) {
335 [
'rc_this_oldid=rev_id' ] ] ] );
336 $this->
addFields( [
'rev_sha1',
'rev_deleted' ] );
339 if ( $params[
'toponly'] || $showRedirects ) {
342 [
'rc_namespace=page_namespace',
'rc_title=page_title' ] ] ] );
345 if ( $params[
'toponly'] ) {
346 $this->
addWhere(
'rc_this_oldid = page_latest' );
350 if ( !is_null( $params[
'tag'] ) ) {
352 $this->
addJoinConds( [
'change_tag' => [
'JOIN', [
'rc_id=ct_rc_id' ] ] ] );
353 $changeTagDefStore = MediaWikiServices::getInstance()->getChangeTagDefStore();
355 $this->
addWhereFld(
'ct_tag_id', $changeTagDefStore->getId( $params[
'tag'] ) );
363 if ( !is_null( $params[
'user'] ) || !is_null( $params[
'excludeuser'] ) ) {
365 $bitmask = RevisionRecord::DELETED_USER;
367 ->userHasAnyRight( $user,
'suppressrevision',
'viewsuppressed' )
369 $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
374 $this->
addWhere( $this->
getDB()->bitAnd(
'rc_deleted', $bitmask ) .
" != $bitmask" );
377 if ( $this->
getRequest()->getCheck(
'namespace' ) ) {
382 ->userHasAnyRight( $user,
'suppressrevision',
'viewsuppressed' )
391 $this->
getDB()->bitAnd(
'rc_deleted', $bitmask ) .
" != $bitmask",
396 $this->token = $params[
'token'];
398 if ( $this->fld_comment || $this->fld_parsedcomment || $this->token ) {
400 $commentQuery = $this->commentStore->getJoin(
'rc_comment' );
401 $this->
addTables( $commentQuery[
'tables'] );
402 $this->
addFields( $commentQuery[
'fields'] );
406 if ( $this->fld_user || $this->fld_userid || !is_null( $this->token ) ) {
409 $this->
addTables( $actorQuery[
'tables'] );
410 $this->
addFields( $actorQuery[
'fields'] );
414 $this->
addOption(
'LIMIT', $params[
'limit'] + 1 );
419 $res = $this->
select( __METHOD__, [], $hookData );
427 foreach (
$res as $row ) {
428 if ( $count === 0 && $resultPageSet !==
null ) {
432 $this,
'continue',
"$row->rc_timestamp|$row->rc_id"
435 if ( ++$count > $params[
'limit'] ) {
442 if ( is_null( $resultPageSet ) ) {
447 $fit = $this->
processRow( $row, $vals, $hookData ) &&
448 $result->addValue( [
'query', $this->
getModuleName() ],
null, $vals );
453 } elseif ( $params[
'generaterevisions'] ) {
454 $revid = (int)$row->rc_this_oldid;
463 if ( is_null( $resultPageSet ) ) {
465 $result->addIndexedTagName( [
'query', $this->
getModuleName() ],
'rc' );
466 } elseif ( $params[
'generaterevisions'] ) {
467 $resultPageSet->populateFromRevisionIDs( $revids );
469 $resultPageSet->populateFromTitles( $titles );
487 $type = (int)$row->rc_type;
493 if ( $this->fld_title || $this->fld_ids ) {
495 $vals[
'actionhidden'] =
true;
501 if ( $this->fld_title ) {
504 if ( $this->fld_ids ) {
505 $vals[
'pageid'] = (int)$row->rc_cur_id;
506 $vals[
'revid'] = (
int)$row->rc_this_oldid;
507 $vals[
'old_revid'] = (int)$row->rc_last_oldid;
512 if ( $this->fld_ids ) {
513 $vals[
'rcid'] = (int)$row->rc_id;
517 if ( $this->fld_user || $this->fld_userid ) {
518 if ( $row->rc_deleted & RevisionRecord::DELETED_USER ) {
519 $vals[
'userhidden'] =
true;
522 if ( RevisionRecord::userCanBitfield( $row->rc_deleted, RevisionRecord::DELETED_USER, $user ) ) {
523 if ( $this->fld_user ) {
524 $vals[
'user'] = $row->rc_user_text;
527 if ( $this->fld_userid ) {
528 $vals[
'userid'] = (int)$row->rc_user;
531 if ( !$row->rc_user ) {
532 $vals[
'anon'] =
true;
538 if ( $this->fld_flags ) {
539 $vals[
'bot'] = (bool)$row->rc_bot;
540 $vals[
'new'] = $row->rc_type ==
RC_NEW;
541 $vals[
'minor'] = (
bool)$row->rc_minor;
545 if ( $this->fld_sizes ) {
546 $vals[
'oldlen'] = (int)$row->rc_old_len;
547 $vals[
'newlen'] = (
int)$row->rc_new_len;
551 if ( $this->fld_timestamp ) {
552 $vals[
'timestamp'] =
wfTimestamp( TS_ISO_8601, $row->rc_timestamp );
556 if ( $this->fld_comment || $this->fld_parsedcomment ) {
557 if ( $row->rc_deleted & RevisionRecord::DELETED_COMMENT ) {
558 $vals[
'commenthidden'] =
true;
561 if ( RevisionRecord::userCanBitfield(
562 $row->rc_deleted, RevisionRecord::DELETED_COMMENT, $user
564 $comment = $this->commentStore->getComment(
'rc_comment', $row )->text;
565 if ( $this->fld_comment ) {
566 $vals[
'comment'] = $comment;
569 if ( $this->fld_parsedcomment ) {
575 if ( $this->fld_redirect ) {
576 $vals[
'redirect'] = (bool)$row->page_is_redirect;
580 if ( $this->fld_patrolled ) {
586 if ( $this->fld_loginfo && $row->rc_type ==
RC_LOG ) {
588 $vals[
'actionhidden'] =
true;
592 $vals[
'logid'] = (int)$row->rc_logid;
593 $vals[
'logtype'] = $row->rc_log_type;
594 $vals[
'logaction'] = $row->rc_log_action;
599 if ( $this->fld_tags ) {
600 if ( $row->ts_tags ) {
601 $tags = explode(
',', $row->ts_tags );
603 $vals[
'tags'] = $tags;
609 if ( $this->fld_sha1 && $row->rev_sha1 !==
null ) {
610 if ( $row->rev_deleted & RevisionRecord::DELETED_TEXT ) {
611 $vals[
'sha1hidden'] =
true;
614 if ( RevisionRecord::userCanBitfield(
615 $row->rev_deleted, RevisionRecord::DELETED_TEXT, $user
617 if ( $row->rev_sha1 !==
'' ) {
618 $vals[
'sha1'] = Wikimedia\base_convert( $row->rev_sha1, 36, 16, 40 );
625 if ( !is_null( $this->token ) ) {
627 foreach ( $this->token as
$t ) {
630 if ( $val ===
false ) {
631 $this->
addWarning( [
'apiwarn-tokennotallowed', $t ] );
633 $vals[
$t .
'token'] = $val;
638 if ( $anyHidden && ( $row->rc_deleted & RevisionRecord::DELETED_RESTRICTED ) ) {
639 $vals[
'suppressed'] =
true;
650 return isset( $flagsArray[
'patrolled'] ) ||
651 isset( $flagsArray[
'!patrolled'] ) ||
652 isset( $flagsArray[
'unpatrolled'] ) ||
653 isset( $flagsArray[
'autopatrolled'] ) ||
654 isset( $flagsArray[
'!autopatrolled'] );
658 if ( isset( $params[
'show'] ) &&
663 if ( isset( $params[
'token'] ) ) {
669 if ( !is_null( $params[
'prop'] ) && in_array(
'parsedcomment', $params[
'prop'] ) ) {
671 return 'anon-public-user-private';
766 'generaterevisions' =>
false,
772 'action=query&list=recentchanges'
773 =>
'apihelp-query+recentchanges-example-simple',
774 'action=query&generator=recentchanges&grcshow=!patrolled&prop=info'
775 =>
'apihelp-query+recentchanges-example-generator',
780 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Recentchanges';
static getPatrolToken( $pageid, $title, $rc=null)
run( $resultPageSet=null)
Generates and outputs the result of this query based upon the provided parameters.
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
processRow( $row, array &$data, array &$hookData)
Call the ApiQueryBaseProcessRow hook.
addFields( $value)
Add a set of fields to select to the internal array.
This is the main query class.
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
A query action to enumerate the recent changes that were done to the wiki.
extractRowInfo( $row)
Extracts from a single sql row the data needed to describe one recent change.
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
addTimestampWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, similar to addWhereRange, but converts $start and $end t...
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
getTokenFunctions()
Get an array mapping token names to their handler functions.
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
getResult()
Get the result object.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
static getChangeTypes()
Get an array of all change types.
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
static parseToRCType( $type)
Parsing text to RC_* constants.
lacksSameOriginSecurity()
Returns true if the current request breaks the same-origin policy.
static newMigration()
Static constructor.
addFieldsIf( $value, $condition)
Same as addFields(), but add the fields only if a condition is met.
includesPatrollingFlags(array $flagsArray)
setContinueEnumParameter( $paramName, $paramValue)
Overridden to set the generator param if in generator mode.
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
executeGenerator( $resultPageSet)
Execute this module as a generator.
const LIMIT_BIG1
Fast query, standard limit.
getDB()
Get the Query database connection (read-only)
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
const PARAM_EXTRA_NAMESPACES
(int[]) When PARAM_TYPE is 'namespace', include these as additional possible values.
__construct(ApiQuery $query, $moduleName)
getContinuationManager()
Get the continuation manager.
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
initProperties( $prop)
Sets internal state to include the desired properties in the output.
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
requireMaxOneParameter( $params, $required)
Die if more than one of a certain set of parameters is set and not false.
static formatComment( $comment, $title=null, $local=false, $wikiId=null)
This function is called by all recent changes variants, by the page history, and by the user contribu...
getHelpUrls()
Return links to more detailed help pages about the module.
getExamplesMessages()
Returns usage examples for this module.
const LIMIT_BIG2
Fast query, apihighlimits limit.
static userCanBitfield( $bitfield, $field, User $user=null)
Determine if the current user is allowed to view a particular field of this log row,...
static parseFromRCType( $rcType)
Parsing RC_* constants to human-readable test.
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
getModuleName()
Get the name of the module being executed by this instance.
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
const PARAM_MAX2
(integer) Max value allowed for the parameter for users with the apihighlimits right,...
addWhere( $value)
Add a set of WHERE clauses to the internal array.
static isUnpatrolled( $rc, User $user)
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
userCanSeeRevDel()
Check whether the current user has permission to view revision-deleted fields.
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
getCacheMode( $params)
Get the cache mode for the data generated by this module.
addWhereIf( $value, $condition)
Same as addWhere(), but add the WHERE clauses only if a condition is met.
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.