MediaWiki  master
ApiQueryUserContribs.php
Go to the documentation of this file.
1 <?php
33 
40 
42  private $commentStore;
43 
46 
48  private $userNameUtils;
49 
51  private $revisionStore;
52 
55 
57  private $actorMigration;
58 
69  public function __construct(
70  ApiQuery $query,
71  $moduleName,
78  ) {
79  parent::__construct( $query, $moduleName, 'uc' );
80  $this->commentStore = $commentStore;
81  $this->userIdentityLookup = $userIdentityLookup;
82  $this->userNameUtils = $userNameUtils;
83  $this->revisionStore = $revisionStore;
84  $this->changeTagDefStore = $changeTagDefStore;
85  $this->actorMigration = $actorMigration;
86  }
87 
89 
90  private $fld_ids = false, $fld_title = false, $fld_timestamp = false,
91  $fld_comment = false, $fld_parsedcomment = false, $fld_flags = false,
92  $fld_patrolled = false, $fld_tags = false, $fld_size = false, $fld_sizediff = false;
93 
94  public function execute() {
95  // Parse some parameters
96  $this->params = $this->extractRequestParams();
97 
98  $prop = array_fill_keys( $this->params['prop'], true );
99  $this->fld_ids = isset( $prop['ids'] );
100  $this->fld_title = isset( $prop['title'] );
101  $this->fld_comment = isset( $prop['comment'] );
102  $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
103  $this->fld_size = isset( $prop['size'] );
104  $this->fld_sizediff = isset( $prop['sizediff'] );
105  $this->fld_flags = isset( $prop['flags'] );
106  $this->fld_timestamp = isset( $prop['timestamp'] );
107  $this->fld_patrolled = isset( $prop['patrolled'] );
108  $this->fld_tags = isset( $prop['tags'] );
109 
110  // The main query may use the 'contributions' group DB, which can map to replica DBs
111  // with extra user based indexes or partitioning by user. The additional metadata
112  // queries should use a regular replica DB since the lookup pattern is not all by user.
113  $dbSecondary = $this->getDB(); // any random replica DB
114 
115  $sort = ( $this->params['dir'] == 'newer' ?
116  SelectQueryBuilder::SORT_ASC : SelectQueryBuilder::SORT_DESC );
117  $op = ( $this->params['dir'] == 'older' ? '<' : '>' );
118 
119  // Create an Iterator that produces the UserIdentity objects we need, depending
120  // on which of the 'userprefix', 'userids', or 'user' params was
121  // specified.
122  $this->requireOnlyOneParameter( $this->params, 'userprefix', 'userids', 'user' );
123  if ( isset( $this->params['userprefix'] ) ) {
124  $this->multiUserMode = true;
125  $this->orderBy = 'name';
126  $fname = __METHOD__;
127 
128  // Because 'userprefix' might produce a huge number of users (e.g.
129  // a wiki with users "Test00000001" to "Test99999999"), use a
130  // generator with batched lookup and continuation.
131  $userIter = call_user_func( function () use ( $dbSecondary, $sort, $op, $fname ) {
132  $fromName = false;
133  if ( $this->params['continue'] !== null ) {
134  $continue = explode( '|', $this->params['continue'] );
135  $this->dieContinueUsageIf( count( $continue ) != 4 );
136  $this->dieContinueUsageIf( $continue[0] !== 'name' );
137  $fromName = $continue[1];
138  }
139 
140  $limit = 501;
141  do {
142  $from = $fromName ? "$op= " . $dbSecondary->addQuotes( $fromName ) : false;
143  $usersBatch = $this->userIdentityLookup
144  ->newSelectQueryBuilder()
145  ->caller( $fname )
146  ->limit( $limit )
147  ->whereUserNamePrefix( $this->params['userprefix'] )
148  ->where( $from ? [ "actor_name $from" ] : [] )
149  ->orderByName( $sort )
150  ->fetchUserIdentities();
151 
152  $count = 0;
153  $fromName = false;
154  foreach ( $usersBatch as $user ) {
155  if ( ++$count >= $limit ) {
156  $fromName = $user->getName();
157  break;
158  }
159  yield $user;
160  }
161  } while ( $fromName !== false );
162  } );
163  // Do the actual sorting client-side, because otherwise
164  // prepareQuery might try to sort by actor and confuse everything.
165  $batchSize = 1;
166  } elseif ( isset( $this->params['userids'] ) ) {
167  if ( $this->params['userids'] === [] ) {
168  $encParamName = $this->encodeParamName( 'userids' );
169  $this->dieWithError( [ 'apierror-paramempty', $encParamName ], "paramempty_$encParamName" );
170  }
171 
172  $ids = [];
173  foreach ( $this->params['userids'] as $uid ) {
174  if ( $uid <= 0 ) {
175  $this->dieWithError( [ 'apierror-invaliduserid', $uid ], 'invaliduserid' );
176  }
177  $ids[] = $uid;
178  }
179 
180  $this->orderBy = 'id';
181  $this->multiUserMode = count( $ids ) > 1;
182 
183  $from = $fromId = false;
184  if ( $this->multiUserMode && $this->params['continue'] !== null ) {
185  $continue = explode( '|', $this->params['continue'] );
186  $this->dieContinueUsageIf( count( $continue ) != 4 );
187  $this->dieContinueUsageIf( $continue[0] !== 'id' && $continue[0] !== 'actor' );
188  $fromId = (int)$continue[1];
189  $this->dieContinueUsageIf( $continue[1] !== (string)$fromId );
190  $from = "$op= $fromId";
191  }
192 
193  $userIter = $this->userIdentityLookup
194  ->newSelectQueryBuilder()
195  ->caller( __METHOD__ )
196  ->whereUserIds( $ids )
197  ->orderByUserId( $sort )
198  ->where( $from ? [ "actor_id $from" ] : [] )
199  ->fetchUserIdentities();
200  $batchSize = count( $ids );
201  } else {
202  $names = [];
203  if ( !count( $this->params['user'] ) ) {
204  $encParamName = $this->encodeParamName( 'user' );
205  $this->dieWithError(
206  [ 'apierror-paramempty', $encParamName ], "paramempty_$encParamName"
207  );
208  }
209  foreach ( $this->params['user'] as $u ) {
210  if ( $u === '' ) {
211  $encParamName = $this->encodeParamName( 'user' );
212  $this->dieWithError(
213  [ 'apierror-paramempty', $encParamName ], "paramempty_$encParamName"
214  );
215  }
216 
217  if ( $this->userNameUtils->isIP( $u ) || ExternalUserNames::isExternal( $u ) ) {
218  $names[$u] = null;
219  } else {
220  $name = $this->userNameUtils->getCanonical( $u );
221  if ( $name === false ) {
222  $encParamName = $this->encodeParamName( 'user' );
223  $this->dieWithError(
224  [ 'apierror-baduser', $encParamName, wfEscapeWikiText( $u ) ], "baduser_$encParamName"
225  );
226  }
227  $names[$name] = null;
228  }
229  }
230 
231  $this->orderBy = 'name';
232  $this->multiUserMode = count( $names ) > 1;
233 
234  $from = $fromName = false;
235  if ( $this->multiUserMode && $this->params['continue'] !== null ) {
236  $continue = explode( '|', $this->params['continue'] );
237  $this->dieContinueUsageIf( count( $continue ) != 4 );
238  $this->dieContinueUsageIf( $continue[0] !== 'name' && $continue[0] !== 'actor' );
239  $fromName = $continue[1];
240  $from = "$op= " . $dbSecondary->addQuotes( $fromName );
241  }
242 
243  $userIter = $this->userIdentityLookup
244  ->newSelectQueryBuilder()
245  ->caller( __METHOD__ )
246  ->whereUserNames( array_keys( $names ) )
247  ->where( $from ? [ "actor_id $from" ] : [] )
248  ->orderByName( $sort )
249  ->fetchUserIdentities();
250  $batchSize = count( $names );
251  }
252 
253  // The DB query will order by actor so update $this->orderBy to match.
254  if ( $batchSize > 1 ) {
255  $this->orderBy = 'actor';
256  }
257 
258  $count = 0;
259  $limit = $this->params['limit'];
260  $userIter->rewind();
261  while ( $userIter->valid() ) {
262  $users = [];
263  while ( count( $users ) < $batchSize && $userIter->valid() ) {
264  $users[] = $userIter->current();
265  $userIter->next();
266  }
267 
268  $hookData = [];
269  $this->prepareQuery( $users, $limit - $count );
270  $res = $this->select( __METHOD__, [], $hookData );
271 
272  if ( $this->fld_title ) {
273  $this->executeGenderCacheFromResultWrapper( $res, __METHOD__ );
274  }
275 
276  if ( $this->fld_sizediff ) {
277  $revIds = [];
278  foreach ( $res as $row ) {
279  if ( $row->rev_parent_id ) {
280  $revIds[] = (int)$row->rev_parent_id;
281  }
282  }
283  $this->parentLens = $this->revisionStore->getRevisionSizes( $revIds );
284  }
285 
286  foreach ( $res as $row ) {
287  if ( ++$count > $limit ) {
288  // We've reached the one extra which shows that there are
289  // additional pages to be had. Stop here...
290  $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) );
291  break 2;
292  }
293 
294  $vals = $this->extractRowInfo( $row );
295  $fit = $this->processRow( $row, $vals, $hookData ) &&
296  $this->getResult()->addValue( [ 'query', $this->getModuleName() ], null, $vals );
297  if ( !$fit ) {
298  $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) );
299  break 2;
300  }
301  }
302  }
303 
304  $this->getResult()->addIndexedTagName( [ 'query', $this->getModuleName() ], 'item' );
305  }
306 
312  private function prepareQuery( array $users, $limit ) {
313  $this->resetQueryParams();
314  $db = $this->getDB();
315 
316  $revQuery = $this->revisionStore->getQueryInfo( [ 'page' ] );
317  $revWhere = $this->actorMigration->getWhere( $db, 'rev_user', $users );
318 
319  $orderUserField = 'rev_actor';
320  $userField = $this->orderBy === 'actor' ? 'rev_actor' : 'actor_name';
321  $tsField = 'rev_timestamp';
322  $idField = 'rev_id';
323 
324  $this->addTables( $revQuery['tables'] );
325  $this->addJoinConds( $revQuery['joins'] );
326  $this->addFields( $revQuery['fields'] );
327  $this->addWhere( $revWhere['conds'] );
328 
329  // Handle continue parameter
330  if ( $this->params['continue'] !== null ) {
331  $continue = explode( '|', $this->params['continue'] );
332  if ( $this->multiUserMode ) {
333  $this->dieContinueUsageIf( count( $continue ) != 4 );
334  $modeFlag = array_shift( $continue );
335  $this->dieContinueUsageIf( $modeFlag !== $this->orderBy );
336  $encUser = $db->addQuotes( array_shift( $continue ) );
337  } else {
338  $this->dieContinueUsageIf( count( $continue ) != 2 );
339  }
340  $encTS = $db->addQuotes( $db->timestamp( $continue[0] ) );
341  $encId = (int)$continue[1];
342  $this->dieContinueUsageIf( $encId != $continue[1] );
343  $op = ( $this->params['dir'] == 'older' ? '<' : '>' );
344  if ( $this->multiUserMode ) {
345  $this->addWhere(
346  // @phan-suppress-next-line PhanPossiblyUndeclaredVariable encUser is set when used
347  "$userField $op $encUser OR " .
348  // @phan-suppress-next-line PhanPossiblyUndeclaredVariable encUser is set when used
349  "($userField = $encUser AND " .
350  "($tsField $op $encTS OR " .
351  "($tsField = $encTS AND " .
352  "$idField $op= $encId)))"
353  );
354  } else {
355  $this->addWhere(
356  "$tsField $op $encTS OR " .
357  "($tsField = $encTS AND " .
358  "$idField $op= $encId)"
359  );
360  }
361  }
362 
363  // Don't include any revisions where we're not supposed to be able to
364  // see the username.
365  if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
366  $bitmask = RevisionRecord::DELETED_USER;
367  } elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
368  $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
369  } else {
370  $bitmask = 0;
371  }
372  if ( $bitmask ) {
373  $this->addWhere( $db->bitAnd( 'rev_deleted', $bitmask ) . " != $bitmask" );
374  }
375 
376  // Add the user field to ORDER BY if there are multiple users
377  if ( count( $users ) > 1 ) {
378  $this->addWhereRange( $orderUserField, $this->params['dir'], null, null );
379  }
380 
381  // Then timestamp
382  $this->addTimestampWhereRange( $tsField,
383  $this->params['dir'], $this->params['start'], $this->params['end'] );
384 
385  // Then rev_id for a total ordering
386  $this->addWhereRange( $idField, $this->params['dir'], null, null );
387 
388  $this->addWhereFld( 'page_namespace', $this->params['namespace'] );
389 
390  $show = $this->params['show'];
391  if ( $this->params['toponly'] ) { // deprecated/old param
392  $show[] = 'top';
393  }
394  if ( $show !== null ) {
395  $show = array_fill_keys( $show, true );
396 
397  if ( ( isset( $show['minor'] ) && isset( $show['!minor'] ) )
398  || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) )
399  || ( isset( $show['autopatrolled'] ) && isset( $show['!autopatrolled'] ) )
400  || ( isset( $show['autopatrolled'] ) && isset( $show['!patrolled'] ) )
401  || ( isset( $show['top'] ) && isset( $show['!top'] ) )
402  || ( isset( $show['new'] ) && isset( $show['!new'] ) )
403  ) {
404  $this->dieWithError( 'apierror-show' );
405  }
406 
407  $this->addWhereIf( 'rev_minor_edit = 0', isset( $show['!minor'] ) );
408  $this->addWhereIf( 'rev_minor_edit != 0', isset( $show['minor'] ) );
409  $this->addWhereIf(
410  'rc_patrolled = ' . RecentChange::PRC_UNPATROLLED,
411  isset( $show['!patrolled'] )
412  );
413  $this->addWhereIf(
414  'rc_patrolled != ' . RecentChange::PRC_UNPATROLLED,
415  isset( $show['patrolled'] )
416  );
417  $this->addWhereIf(
418  'rc_patrolled != ' . RecentChange::PRC_AUTOPATROLLED,
419  isset( $show['!autopatrolled'] )
420  );
421  $this->addWhereIf(
422  'rc_patrolled = ' . RecentChange::PRC_AUTOPATROLLED,
423  isset( $show['autopatrolled'] )
424  );
425  $this->addWhereIf( $idField . ' != page_latest', isset( $show['!top'] ) );
426  $this->addWhereIf( $idField . ' = page_latest', isset( $show['top'] ) );
427  $this->addWhereIf( 'rev_parent_id != 0', isset( $show['!new'] ) );
428  $this->addWhereIf( 'rev_parent_id = 0', isset( $show['new'] ) );
429  }
430  $this->addOption( 'LIMIT', $limit + 1 );
431 
432  if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ||
433  isset( $show['autopatrolled'] ) || isset( $show['!autopatrolled'] ) || $this->fld_patrolled
434  ) {
435  $user = $this->getUser();
436  if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) {
437  $this->dieWithError( 'apierror-permissiondenied-patrolflag', 'permissiondenied' );
438  }
439 
440  $isFilterset = isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ||
441  isset( $show['autopatrolled'] ) || isset( $show['!autopatrolled'] );
442  $this->addTables( 'recentchanges' );
443  $this->addJoinConds( [ 'recentchanges' => [
444  $isFilterset ? 'JOIN' : 'LEFT JOIN',
445  [ 'rc_this_oldid = ' . $idField ]
446  ] ] );
447  }
448 
449  $this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
450 
451  if ( $this->fld_tags ) {
452  $this->addFields( [ 'ts_tags' => ChangeTags::makeTagSummarySubquery( 'revision' ) ] );
453  }
454 
455  if ( isset( $this->params['tag'] ) ) {
456  $this->addTables( 'change_tag' );
457  $this->addJoinConds(
458  [ 'change_tag' => [ 'JOIN', [ $idField . ' = ct_rev_id' ] ] ]
459  );
460  try {
461  $this->addWhereFld( 'ct_tag_id', $this->changeTagDefStore->getId( $this->params['tag'] ) );
462  } catch ( NameTableAccessException $exception ) {
463  // Return nothing.
464  $this->addWhere( '1=0' );
465  }
466  }
467  $this->addOption(
468  'MAX_EXECUTION_TIME',
469  $this->getConfig()->get( MainConfigNames::MaxExecutionTimeForExpensiveQueries )
470  );
471  }
472 
479  private function extractRowInfo( $row ) {
480  $vals = [];
481  $anyHidden = false;
482 
483  if ( $row->rev_deleted & RevisionRecord::DELETED_TEXT ) {
484  $vals['texthidden'] = true;
485  $anyHidden = true;
486  }
487 
488  // Any rows where we can't view the user were filtered out in the query.
489  $vals['userid'] = (int)$row->rev_user;
490  $vals['user'] = $row->rev_user_text;
491  if ( $row->rev_deleted & RevisionRecord::DELETED_USER ) {
492  $vals['userhidden'] = true;
493  $anyHidden = true;
494  }
495  if ( $this->fld_ids ) {
496  $vals['pageid'] = (int)$row->rev_page;
497  $vals['revid'] = (int)$row->rev_id;
498 
499  if ( $row->rev_parent_id !== null ) {
500  $vals['parentid'] = (int)$row->rev_parent_id;
501  }
502  }
503 
504  $title = Title::makeTitle( $row->page_namespace, $row->page_title );
505 
506  if ( $this->fld_title ) {
508  }
509 
510  if ( $this->fld_timestamp ) {
511  $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rev_timestamp );
512  }
513 
514  if ( $this->fld_flags ) {
515  $vals['new'] = $row->rev_parent_id == 0 && $row->rev_parent_id !== null;
516  $vals['minor'] = (bool)$row->rev_minor_edit;
517  $vals['top'] = $row->page_latest == $row->rev_id;
518  }
519 
520  if ( $this->fld_comment || $this->fld_parsedcomment ) {
521  if ( $row->rev_deleted & RevisionRecord::DELETED_COMMENT ) {
522  $vals['commenthidden'] = true;
523  $anyHidden = true;
524  }
525 
526  $userCanView = RevisionRecord::userCanBitfield(
527  $row->rev_deleted,
528  RevisionRecord::DELETED_COMMENT, $this->getUser()
529  );
530 
531  if ( $userCanView ) {
532  $comment = $this->commentStore->getComment( 'rev_comment', $row )->text;
533  if ( $this->fld_comment ) {
534  $vals['comment'] = $comment;
535  }
536 
537  if ( $this->fld_parsedcomment ) {
538  $vals['parsedcomment'] = Linker::formatComment( $comment, $title );
539  }
540  }
541  }
542 
543  if ( $this->fld_patrolled ) {
544  $vals['patrolled'] = $row->rc_patrolled != RecentChange::PRC_UNPATROLLED;
545  $vals['autopatrolled'] = $row->rc_patrolled == RecentChange::PRC_AUTOPATROLLED;
546  }
547 
548  if ( $this->fld_size && $row->rev_len !== null ) {
549  $vals['size'] = (int)$row->rev_len;
550  }
551 
552  if ( $this->fld_sizediff
553  && $row->rev_len !== null
554  && $row->rev_parent_id !== null
555  ) {
556  $parentLen = $this->parentLens[$row->rev_parent_id] ?? 0;
557  $vals['sizediff'] = (int)$row->rev_len - $parentLen;
558  }
559 
560  if ( $this->fld_tags ) {
561  if ( $row->ts_tags ) {
562  $tags = explode( ',', $row->ts_tags );
563  ApiResult::setIndexedTagName( $tags, 'tag' );
564  $vals['tags'] = $tags;
565  } else {
566  $vals['tags'] = [];
567  }
568  }
569 
570  if ( $anyHidden && ( $row->rev_deleted & RevisionRecord::DELETED_RESTRICTED ) ) {
571  $vals['suppressed'] = true;
572  }
573 
574  return $vals;
575  }
576 
577  private function continueStr( $row ) {
578  if ( $this->multiUserMode ) {
579  switch ( $this->orderBy ) {
580  case 'id':
581  return "id|$row->rev_user|$row->rev_timestamp|$row->rev_id";
582  case 'name':
583  return "name|$row->rev_user_text|$row->rev_timestamp|$row->rev_id";
584  case 'actor':
585  return "actor|$row->rev_actor|$row->rev_timestamp|$row->rev_id";
586  }
587  } else {
588  return "$row->rev_timestamp|$row->rev_id";
589  }
590  }
591 
592  public function getCacheMode( $params ) {
593  // This module provides access to deleted revisions and patrol flags if
594  // the requester is logged in
595  return 'anon-public-user-private';
596  }
597 
598  public function getAllowedParams() {
599  return [
600  'limit' => [
601  ApiBase::PARAM_DFLT => 10,
602  ApiBase::PARAM_TYPE => 'limit',
603  ApiBase::PARAM_MIN => 1,
606  ],
607  'start' => [
608  ApiBase::PARAM_TYPE => 'timestamp'
609  ],
610  'end' => [
611  ApiBase::PARAM_TYPE => 'timestamp'
612  ],
613  'continue' => [
614  ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
615  ],
616  'user' => [
617  ApiBase::PARAM_TYPE => 'user',
618  UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'interwiki' ],
620  ],
621  'userids' => [
622  ApiBase::PARAM_TYPE => 'integer',
624  ],
625  'userprefix' => null,
626  'dir' => [
627  ApiBase::PARAM_DFLT => 'older',
629  'newer',
630  'older'
631  ],
632  ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
633  ],
634  'namespace' => [
635  ApiBase::PARAM_ISMULTI => true,
636  ApiBase::PARAM_TYPE => 'namespace'
637  ],
638  'prop' => [
639  ApiBase::PARAM_ISMULTI => true,
640  ApiBase::PARAM_DFLT => 'ids|title|timestamp|comment|size|flags',
642  'ids',
643  'title',
644  'timestamp',
645  'comment',
646  'parsedcomment',
647  'size',
648  'sizediff',
649  'flags',
650  'patrolled',
651  'tags'
652  ],
654  ],
655  'show' => [
656  ApiBase::PARAM_ISMULTI => true,
658  'minor',
659  '!minor',
660  'patrolled',
661  '!patrolled',
662  'autopatrolled',
663  '!autopatrolled',
664  'top',
665  '!top',
666  'new',
667  '!new',
668  ],
670  'apihelp-query+usercontribs-param-show',
671  $this->getConfig()->get( MainConfigNames::RCMaxAge )
672  ],
673  ],
674  'tag' => null,
675  'toponly' => [
676  ApiBase::PARAM_DFLT => false,
678  ],
679  ];
680  }
681 
682  protected function getExamplesMessages() {
683  return [
684  'action=query&list=usercontribs&ucuser=Example'
685  => 'apihelp-query+usercontribs-example-user',
686  'action=query&list=usercontribs&ucuserprefix=192.0.2.'
687  => 'apihelp-query+usercontribs-example-ipprefix',
688  ];
689  }
690 
691  public function getHelpUrls() {
692  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Usercontribs';
693  }
694 }
695 
700 class_alias( ApiQueryUserContribs::class, 'ApiQueryContributions' );
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Definition: WebStart.php:82
This is not intended to be a long-term part of MediaWiki; it will be deprecated and removed once acto...
const PARAM_MAX2
Definition: ApiBase.php:90
const PARAM_DEPRECATED
Definition: ApiBase.php:102
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition: ApiBase.php:743
const PARAM_MAX
Definition: ApiBase.php:86
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:1446
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition: ApiBase.php:1638
const PARAM_TYPE
Definition: ApiBase.php:82
const PARAM_DFLT
Definition: ApiBase.php:74
requireOnlyOneParameter( $params,... $required)
Die if none or more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:903
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition: ApiBase.php:196
const PARAM_MIN
Definition: ApiBase.php:94
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:221
getResult()
Get the result object.
Definition: ApiBase.php:629
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:765
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:163
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:223
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:498
const PARAM_ISMULTI
Definition: ApiBase.php:78
This is a base class for all Query modules.
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
processRow( $row, array &$data, array &$hookData)
Call the ApiQueryBaseProcessRow hook.
resetQueryParams()
Blank the internal arrays with query parameters.
addWhereIf( $value, $condition)
Same as addWhere(), but add the WHERE clauses only if a condition is met.
addWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, and an ORDER BY clause to sort in the right direction.
addFields( $value)
Add a set of fields to select to the internal array.
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
addTimestampWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, similar to addWhereRange, but converts $start and $end t...
getDB()
Get the Query database connection (read-only)
executeGenderCacheFromResultWrapper(IResultWrapper $res, $fname=__METHOD__, $fieldPrefix='page')
Preprocess the result set to fill the GenderCache with the necessary information before using self::a...
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
addFieldsIf( $value, $condition)
Same as addFields(), but add the fields only if a condition is met.
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
addWhere( $value)
Add a set of WHERE clauses to the internal array.
This query action adds a list of a specified user's contributions to the output.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
ActorMigration $actorMigration
getCacheMode( $params)
Get the cache mode for the data generated by this module.
getHelpUrls()
Return links to more detailed help pages about the module.
__construct(ApiQuery $query, $moduleName, CommentStore $commentStore, UserIdentityLookup $userIdentityLookup, UserNameUtils $userNameUtils, RevisionStore $revisionStore, NameTableStore $changeTagDefStore, ActorMigration $actorMigration)
getExamplesMessages()
Returns usage examples for this module.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
prepareQuery(array $users, $limit)
Prepares the query and returns the limit of rows requested.
extractRowInfo( $row)
Extract fields from the database row and append them to a result array.
UserIdentityLookup $userIdentityLookup
NameTableStore $changeTagDefStore
This is the main query class.
Definition: ApiQuery.php:40
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:604
static makeTagSummarySubquery( $tables)
Make the tag summary subquery based on the given tables and return it.
Handle database storage of comments such as edit summaries and log reasons.
static isExternal( $username)
Tells whether the username is external or not.
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...
Definition: Linker.php:1424
A class containing constants representing the names of configuration variables.
Page revision base class.
Service for looking up page revisions.
Exception representing a failure to look up a row from a name table.
UserNameUtils service.
const PRC_UNPATROLLED
const PRC_AUTOPATROLLED
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:637
Interface for objects representing user identity.
return true
Definition: router.php:90
$revQuery