MediaWiki  master
ApiQueryUserContribs.php
Go to the documentation of this file.
1 <?php
27 
34 
35  public function __construct( ApiQuery $query, $moduleName ) {
36  parent::__construct( $query, $moduleName, 'uc' );
37  }
38 
40 
42  private $commentStore;
43 
44  private $fld_ids = false, $fld_title = false, $fld_timestamp = false,
45  $fld_comment = false, $fld_parsedcomment = false, $fld_flags = false,
46  $fld_patrolled = false, $fld_tags = false, $fld_size = false, $fld_sizediff = false;
47 
48  public function execute() {
49  // Parse some parameters
50  $this->params = $this->extractRequestParams();
51 
52  $this->commentStore = CommentStore::getStore();
53 
54  $prop = array_flip( $this->params['prop'] );
55  $this->fld_ids = isset( $prop['ids'] );
56  $this->fld_title = isset( $prop['title'] );
57  $this->fld_comment = isset( $prop['comment'] );
58  $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
59  $this->fld_size = isset( $prop['size'] );
60  $this->fld_sizediff = isset( $prop['sizediff'] );
61  $this->fld_flags = isset( $prop['flags'] );
62  $this->fld_timestamp = isset( $prop['timestamp'] );
63  $this->fld_patrolled = isset( $prop['patrolled'] );
64  $this->fld_tags = isset( $prop['tags'] );
65 
66  // The main query may use the 'contributions' group DB, which can map to replica DBs
67  // with extra user based indexes or partioning by user. The additional metadata
68  // queries should use a regular replica DB since the lookup pattern is not all by user.
69  $dbSecondary = $this->getDB(); // any random replica DB
70 
71  $sort = ( $this->params['dir'] == 'newer' ? '' : ' DESC' );
72  $op = ( $this->params['dir'] == 'older' ? '<' : '>' );
73 
74  // Create an Iterator that produces the UserIdentity objects we need, depending
75  // on which of the 'userprefix', 'userids', or 'user' params was
76  // specified.
77  $this->requireOnlyOneParameter( $this->params, 'userprefix', 'userids', 'user' );
78  if ( isset( $this->params['userprefix'] ) ) {
79  $this->multiUserMode = true;
80  $this->orderBy = 'name';
81  $fname = __METHOD__;
82 
83  // Because 'userprefix' might produce a huge number of users (e.g.
84  // a wiki with users "Test00000001" to "Test99999999"), use a
85  // generator with batched lookup and continuation.
86  $userIter = call_user_func( function () use ( $dbSecondary, $sort, $op, $fname ) {
87  $fromName = false;
88  if ( $this->params['continue'] !== null ) {
89  $continue = explode( '|', $this->params['continue'] );
90  $this->dieContinueUsageIf( count( $continue ) != 4 );
91  $this->dieContinueUsageIf( $continue[0] !== 'name' );
92  $fromName = $continue[1];
93  }
94  $like = $dbSecondary->buildLike( $this->params['userprefix'], $dbSecondary->anyString() );
95 
96  $limit = 501;
97 
98  do {
99  $from = $fromName ? "$op= " . $dbSecondary->addQuotes( $fromName ) : false;
100  $res = $dbSecondary->select(
101  'actor',
102  [ 'actor_id', 'user_id' => 'COALESCE(actor_user,0)', 'user_name' => 'actor_name' ],
103  array_merge( [ "actor_name$like" ], $from ? [ "actor_name $from" ] : [] ),
104  $fname,
105  [ 'ORDER BY' => [ "user_name $sort" ], 'LIMIT' => $limit ]
106  );
107 
108  $count = 0;
109  $fromName = false;
110  foreach ( $res as $row ) {
111  if ( ++$count >= $limit ) {
112  $fromName = $row->user_name;
113  break;
114  }
115  yield User::newFromRow( $row );
116  }
117  } while ( $fromName !== false );
118  } );
119  // Do the actual sorting client-side, because otherwise
120  // prepareQuery might try to sort by actor and confuse everything.
121  $batchSize = 1;
122  } elseif ( isset( $this->params['userids'] ) ) {
123  if ( $this->params['userids'] === [] ) {
124  $encParamName = $this->encodeParamName( 'userids' );
125  $this->dieWithError( [ 'apierror-paramempty', $encParamName ], "paramempty_$encParamName" );
126  }
127 
128  $ids = [];
129  foreach ( $this->params['userids'] as $uid ) {
130  if ( $uid <= 0 ) {
131  $this->dieWithError( [ 'apierror-invaliduserid', $uid ], 'invaliduserid' );
132  }
133  $ids[] = $uid;
134  }
135 
136  $this->orderBy = 'id';
137  $this->multiUserMode = count( $ids ) > 1;
138 
139  $from = $fromId = false;
140  if ( $this->multiUserMode && $this->params['continue'] !== null ) {
141  $continue = explode( '|', $this->params['continue'] );
142  $this->dieContinueUsageIf( count( $continue ) != 4 );
143  $this->dieContinueUsageIf( $continue[0] !== 'id' && $continue[0] !== 'actor' );
144  $fromId = (int)$continue[1];
145  $this->dieContinueUsageIf( $continue[1] !== (string)$fromId );
146  $from = "$op= $fromId";
147  }
148 
149  $res = $dbSecondary->select(
150  'actor',
151  [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
152  array_merge( [ 'actor_user' => $ids ], $from ? [ "actor_id $from" ] : [] ),
153  __METHOD__,
154  [ 'ORDER BY' => "user_id $sort" ]
155  );
156  $userIter = UserArray::newFromResult( $res );
157  $batchSize = count( $ids );
158  } else {
159  $names = [];
160  if ( !count( $this->params['user'] ) ) {
161  $encParamName = $this->encodeParamName( 'user' );
162  $this->dieWithError(
163  [ 'apierror-paramempty', $encParamName ], "paramempty_$encParamName"
164  );
165  }
166  foreach ( $this->params['user'] as $u ) {
167  if ( $u === '' ) {
168  $encParamName = $this->encodeParamName( 'user' );
169  $this->dieWithError(
170  [ 'apierror-paramempty', $encParamName ], "paramempty_$encParamName"
171  );
172  }
173 
174  if ( User::isIP( $u ) || ExternalUserNames::isExternal( $u ) ) {
175  $names[$u] = null;
176  } else {
177  $name = User::getCanonicalName( $u, 'valid' );
178  if ( $name === false ) {
179  $encParamName = $this->encodeParamName( 'user' );
180  $this->dieWithError(
181  [ 'apierror-baduser', $encParamName, wfEscapeWikiText( $u ) ], "baduser_$encParamName"
182  );
183  }
184  $names[$name] = null;
185  }
186  }
187 
188  $this->orderBy = 'name';
189  $this->multiUserMode = count( $names ) > 1;
190 
191  $from = $fromName = false;
192  if ( $this->multiUserMode && $this->params['continue'] !== null ) {
193  $continue = explode( '|', $this->params['continue'] );
194  $this->dieContinueUsageIf( count( $continue ) != 4 );
195  $this->dieContinueUsageIf( $continue[0] !== 'name' && $continue[0] !== 'actor' );
196  $fromName = $continue[1];
197  $from = "$op= " . $dbSecondary->addQuotes( $fromName );
198  }
199 
200  $res = $dbSecondary->select(
201  'actor',
202  [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
203  array_merge(
204  [ 'actor_name' => array_map( 'strval', array_keys( $names ) ) ],
205  $from ? [ "actor_id $from" ] : []
206  ),
207  __METHOD__,
208  [ 'ORDER BY' => "actor_name $sort" ]
209  );
210  $userIter = UserArray::newFromResult( $res );
211  $batchSize = count( $names );
212  }
213 
214  // The DB query will order by actor so update $this->orderBy to match.
215  if ( $batchSize > 1 ) {
216  $this->orderBy = 'actor';
217  }
218 
219  $count = 0;
220  $limit = $this->params['limit'];
221  $userIter->rewind();
222  while ( $userIter->valid() ) {
223  $users = [];
224  while ( count( $users ) < $batchSize && $userIter->valid() ) {
225  $users[] = $userIter->current();
226  $userIter->next();
227  }
228 
229  $hookData = [];
230  $this->prepareQuery( $users, $limit - $count );
231  $res = $this->select( __METHOD__, [], $hookData );
232 
233  if ( $this->fld_title ) {
234  $this->executeGenderCacheFromResultWrapper( $res, __METHOD__ );
235  }
236 
237  if ( $this->fld_sizediff ) {
238  $revIds = [];
239  foreach ( $res as $row ) {
240  if ( $row->rev_parent_id ) {
241  $revIds[] = $row->rev_parent_id;
242  }
243  }
244  $this->parentLens = MediaWikiServices::getInstance()->getRevisionStore()
245  ->listRevisionSizes( $dbSecondary, $revIds );
246  }
247 
248  foreach ( $res as $row ) {
249  if ( ++$count > $limit ) {
250  // We've reached the one extra which shows that there are
251  // additional pages to be had. Stop here...
252  $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) );
253  break 2;
254  }
255 
256  $vals = $this->extractRowInfo( $row );
257  $fit = $this->processRow( $row, $vals, $hookData ) &&
258  $this->getResult()->addValue( [ 'query', $this->getModuleName() ], null, $vals );
259  if ( !$fit ) {
260  $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) );
261  break 2;
262  }
263  }
264  }
265 
266  $this->getResult()->addIndexedTagName( [ 'query', $this->getModuleName() ], 'item' );
267  }
268 
274  private function prepareQuery( array $users, $limit ) {
275  $this->resetQueryParams();
276  $db = $this->getDB();
277 
278  $revQuery = MediaWikiServices::getInstance()->getRevisionStore()->getQueryInfo( [ 'page' ] );
279 
280  $revWhere = ActorMigration::newMigration()->getWhere( $db, 'rev_user', $users );
281  $orderUserField = 'rev_actor';
282  $userField = $this->orderBy === 'actor' ? 'revactor_actor' : 'actor_name';
283  $tsField = 'revactor_timestamp';
284  $idField = 'revactor_rev';
285 
286  // T221511: MySQL/MariaDB (10.1.37) can sometimes irrationally decide that querying `actor`
287  // before `revision_actor_temp` and filesorting is somehow better than querying $limit+1 rows
288  // from `revision_actor_temp`. Tell it not to reorder the query (and also reorder it ourselves
289  // because as generated by RevisionStore it'll have `revision` first rather than
290  // `revision_actor_temp`). But not when uctag is used, as it seems as likely to be harmed as
291  // helped in that case, and not when there's only one User because in that case it fetches
292  // the one `actor` row as a constant and doesn't filesort.
293  if ( count( $users ) > 1 && !isset( $this->params['tag'] ) ) {
294  $revQuery['joins']['revision'] = $revQuery['joins']['temp_rev_user'];
295  unset( $revQuery['joins']['temp_rev_user'] );
296  $this->addOption( 'STRAIGHT_JOIN' );
297  // It isn't actually necesssary to reorder $revQuery['tables'] as Database does the right thing
298  // when join conditions are given for all joins, but GergÅ‘ is wary of relying on that so pull
299  // `revision_actor_temp` to the start.
300  $revQuery['tables'] =
301  [ 'temp_rev_user' => $revQuery['tables']['temp_rev_user'] ] + $revQuery['tables'];
302  }
303 
304  $this->addTables( $revQuery['tables'] );
305  $this->addJoinConds( $revQuery['joins'] );
306  $this->addFields( $revQuery['fields'] );
307  $this->addWhere( $revWhere['conds'] );
308 
309  // Handle continue parameter
310  if ( $this->params['continue'] !== null ) {
311  $continue = explode( '|', $this->params['continue'] );
312  if ( $this->multiUserMode ) {
313  $this->dieContinueUsageIf( count( $continue ) != 4 );
314  $modeFlag = array_shift( $continue );
315  $this->dieContinueUsageIf( $modeFlag !== $this->orderBy );
316  $encUser = $db->addQuotes( array_shift( $continue ) );
317  } else {
318  $this->dieContinueUsageIf( count( $continue ) != 2 );
319  }
320  $encTS = $db->addQuotes( $db->timestamp( $continue[0] ) );
321  $encId = (int)$continue[1];
322  $this->dieContinueUsageIf( $encId != $continue[1] );
323  $op = ( $this->params['dir'] == 'older' ? '<' : '>' );
324  if ( $this->multiUserMode ) {
325  $this->addWhere(
326  "$userField $op $encUser OR " .
327  "($userField = $encUser AND " .
328  "($tsField $op $encTS OR " .
329  "($tsField = $encTS AND " .
330  "$idField $op= $encId)))"
331  );
332  } else {
333  $this->addWhere(
334  "$tsField $op $encTS OR " .
335  "($tsField = $encTS AND " .
336  "$idField $op= $encId)"
337  );
338  }
339  }
340 
341  // Don't include any revisions where we're not supposed to be able to
342  // see the username.
343  $user = $this->getUser();
344  if ( !$this->getPermissionManager()->userHasRight( $user, 'deletedhistory' ) ) {
345  $bitmask = RevisionRecord::DELETED_USER;
346  } elseif ( !$this->getPermissionManager()
347  ->userHasAnyRight( $user, 'suppressrevision', 'viewsuppressed' )
348  ) {
349  $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
350  } else {
351  $bitmask = 0;
352  }
353  if ( $bitmask ) {
354  $this->addWhere( $db->bitAnd( 'rev_deleted', $bitmask ) . " != $bitmask" );
355  }
356 
357  // Add the user field to ORDER BY if there are multiple users
358  if ( count( $users ) > 1 ) {
359  $this->addWhereRange( $orderUserField, $this->params['dir'], null, null );
360  }
361 
362  // Then timestamp
363  $this->addTimestampWhereRange( $tsField,
364  $this->params['dir'], $this->params['start'], $this->params['end'] );
365 
366  // Then rev_id for a total ordering
367  $this->addWhereRange( $idField, $this->params['dir'], null, null );
368 
369  $this->addWhereFld( 'page_namespace', $this->params['namespace'] );
370 
371  $show = $this->params['show'];
372  if ( $this->params['toponly'] ) { // deprecated/old param
373  $show[] = 'top';
374  }
375  if ( $show !== null ) {
376  $show = array_flip( $show );
377 
378  if ( ( isset( $show['minor'] ) && isset( $show['!minor'] ) )
379  || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) )
380  || ( isset( $show['autopatrolled'] ) && isset( $show['!autopatrolled'] ) )
381  || ( isset( $show['autopatrolled'] ) && isset( $show['!patrolled'] ) )
382  || ( isset( $show['top'] ) && isset( $show['!top'] ) )
383  || ( isset( $show['new'] ) && isset( $show['!new'] ) )
384  ) {
385  $this->dieWithError( 'apierror-show' );
386  }
387 
388  $this->addWhereIf( 'rev_minor_edit = 0', isset( $show['!minor'] ) );
389  $this->addWhereIf( 'rev_minor_edit != 0', isset( $show['minor'] ) );
390  $this->addWhereIf(
391  'rc_patrolled = ' . RecentChange::PRC_UNPATROLLED,
392  isset( $show['!patrolled'] )
393  );
394  $this->addWhereIf(
395  'rc_patrolled != ' . RecentChange::PRC_UNPATROLLED,
396  isset( $show['patrolled'] )
397  );
398  $this->addWhereIf(
399  'rc_patrolled != ' . RecentChange::PRC_AUTOPATROLLED,
400  isset( $show['!autopatrolled'] )
401  );
402  $this->addWhereIf(
403  'rc_patrolled = ' . RecentChange::PRC_AUTOPATROLLED,
404  isset( $show['autopatrolled'] )
405  );
406  $this->addWhereIf( $idField . ' != page_latest', isset( $show['!top'] ) );
407  $this->addWhereIf( $idField . ' = page_latest', isset( $show['top'] ) );
408  $this->addWhereIf( 'rev_parent_id != 0', isset( $show['!new'] ) );
409  $this->addWhereIf( 'rev_parent_id = 0', isset( $show['new'] ) );
410  }
411  $this->addOption( 'LIMIT', $limit + 1 );
412 
413  if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ||
414  isset( $show['autopatrolled'] ) || isset( $show['!autopatrolled'] ) || $this->fld_patrolled
415  ) {
416  if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) {
417  $this->dieWithError( 'apierror-permissiondenied-patrolflag', 'permissiondenied' );
418  }
419 
420  $isFilterset = isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ||
421  isset( $show['autopatrolled'] ) || isset( $show['!autopatrolled'] );
422  $this->addTables( 'recentchanges' );
423  $this->addJoinConds( [ 'recentchanges' => [
424  $isFilterset ? 'JOIN' : 'LEFT JOIN',
425  [ 'rc_this_oldid = ' . $idField ]
426  ] ] );
427  }
428 
429  $this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
430 
431  if ( $this->fld_tags ) {
432  $this->addFields( [ 'ts_tags' => ChangeTags::makeTagSummarySubquery( 'revision' ) ] );
433  }
434 
435  if ( isset( $this->params['tag'] ) ) {
436  $this->addTables( 'change_tag' );
437  $this->addJoinConds(
438  [ 'change_tag' => [ 'JOIN', [ $idField . ' = ct_rev_id' ] ] ]
439  );
440  $changeTagDefStore = MediaWikiServices::getInstance()->getChangeTagDefStore();
441  try {
442  $this->addWhereFld( 'ct_tag_id', $changeTagDefStore->getId( $this->params['tag'] ) );
443  } catch ( NameTableAccessException $exception ) {
444  // Return nothing.
445  $this->addWhere( '1=0' );
446  }
447  }
448  }
449 
456  private function extractRowInfo( $row ) {
457  $vals = [];
458  $anyHidden = false;
459 
460  if ( $row->rev_deleted & RevisionRecord::DELETED_TEXT ) {
461  $vals['texthidden'] = true;
462  $anyHidden = true;
463  }
464 
465  // Any rows where we can't view the user were filtered out in the query.
466  $vals['userid'] = (int)$row->rev_user;
467  $vals['user'] = $row->rev_user_text;
468  if ( $row->rev_deleted & RevisionRecord::DELETED_USER ) {
469  $vals['userhidden'] = true;
470  $anyHidden = true;
471  }
472  if ( $this->fld_ids ) {
473  $vals['pageid'] = (int)$row->rev_page;
474  $vals['revid'] = (int)$row->rev_id;
475 
476  if ( $row->rev_parent_id !== null ) {
477  $vals['parentid'] = (int)$row->rev_parent_id;
478  }
479  }
480 
481  $title = Title::makeTitle( $row->page_namespace, $row->page_title );
482 
483  if ( $this->fld_title ) {
485  }
486 
487  if ( $this->fld_timestamp ) {
488  $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rev_timestamp );
489  }
490 
491  if ( $this->fld_flags ) {
492  $vals['new'] = $row->rev_parent_id == 0 && $row->rev_parent_id !== null;
493  $vals['minor'] = (bool)$row->rev_minor_edit;
494  $vals['top'] = $row->page_latest == $row->rev_id;
495  }
496 
497  if ( $this->fld_comment || $this->fld_parsedcomment ) {
498  if ( $row->rev_deleted & RevisionRecord::DELETED_COMMENT ) {
499  $vals['commenthidden'] = true;
500  $anyHidden = true;
501  }
502 
503  $userCanView = RevisionRecord::userCanBitfield(
504  $row->rev_deleted,
505  RevisionRecord::DELETED_COMMENT, $this->getUser()
506  );
507 
508  if ( $userCanView ) {
509  $comment = $this->commentStore->getComment( 'rev_comment', $row )->text;
510  if ( $this->fld_comment ) {
511  $vals['comment'] = $comment;
512  }
513 
514  if ( $this->fld_parsedcomment ) {
515  $vals['parsedcomment'] = Linker::formatComment( $comment, $title );
516  }
517  }
518  }
519 
520  if ( $this->fld_patrolled ) {
521  $vals['patrolled'] = $row->rc_patrolled != RecentChange::PRC_UNPATROLLED;
522  $vals['autopatrolled'] = $row->rc_patrolled == RecentChange::PRC_AUTOPATROLLED;
523  }
524 
525  if ( $this->fld_size && $row->rev_len !== null ) {
526  $vals['size'] = (int)$row->rev_len;
527  }
528 
529  if ( $this->fld_sizediff
530  && $row->rev_len !== null
531  && $row->rev_parent_id !== null
532  ) {
533  $parentLen = $this->parentLens[$row->rev_parent_id] ?? 0;
534  $vals['sizediff'] = (int)$row->rev_len - $parentLen;
535  }
536 
537  if ( $this->fld_tags ) {
538  if ( $row->ts_tags ) {
539  $tags = explode( ',', $row->ts_tags );
540  ApiResult::setIndexedTagName( $tags, 'tag' );
541  $vals['tags'] = $tags;
542  } else {
543  $vals['tags'] = [];
544  }
545  }
546 
547  if ( $anyHidden && ( $row->rev_deleted & RevisionRecord::DELETED_RESTRICTED ) ) {
548  $vals['suppressed'] = true;
549  }
550 
551  return $vals;
552  }
553 
554  private function continueStr( $row ) {
555  if ( $this->multiUserMode ) {
556  switch ( $this->orderBy ) {
557  case 'id':
558  return "id|$row->rev_user|$row->rev_timestamp|$row->rev_id";
559  case 'name':
560  return "name|$row->rev_user_text|$row->rev_timestamp|$row->rev_id";
561  case 'actor':
562  return "actor|$row->rev_actor|$row->rev_timestamp|$row->rev_id";
563  }
564  } else {
565  return "$row->rev_timestamp|$row->rev_id";
566  }
567  }
568 
569  public function getCacheMode( $params ) {
570  // This module provides access to deleted revisions and patrol flags if
571  // the requester is logged in
572  return 'anon-public-user-private';
573  }
574 
575  public function getAllowedParams() {
576  return [
577  'limit' => [
578  ApiBase::PARAM_DFLT => 10,
579  ApiBase::PARAM_TYPE => 'limit',
580  ApiBase::PARAM_MIN => 1,
583  ],
584  'start' => [
585  ApiBase::PARAM_TYPE => 'timestamp'
586  ],
587  'end' => [
588  ApiBase::PARAM_TYPE => 'timestamp'
589  ],
590  'continue' => [
591  ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
592  ],
593  'user' => [
594  ApiBase::PARAM_TYPE => 'user',
595  UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'interwiki' ],
597  ],
598  'userids' => [
599  ApiBase::PARAM_TYPE => 'integer',
601  ],
602  'userprefix' => null,
603  'dir' => [
604  ApiBase::PARAM_DFLT => 'older',
606  'newer',
607  'older'
608  ],
609  ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
610  ],
611  'namespace' => [
612  ApiBase::PARAM_ISMULTI => true,
613  ApiBase::PARAM_TYPE => 'namespace'
614  ],
615  'prop' => [
616  ApiBase::PARAM_ISMULTI => true,
617  ApiBase::PARAM_DFLT => 'ids|title|timestamp|comment|size|flags',
619  'ids',
620  'title',
621  'timestamp',
622  'comment',
623  'parsedcomment',
624  'size',
625  'sizediff',
626  'flags',
627  'patrolled',
628  'tags'
629  ],
631  ],
632  'show' => [
633  ApiBase::PARAM_ISMULTI => true,
635  'minor',
636  '!minor',
637  'patrolled',
638  '!patrolled',
639  'autopatrolled',
640  '!autopatrolled',
641  'top',
642  '!top',
643  'new',
644  '!new',
645  ],
647  'apihelp-query+usercontribs-param-show',
648  $this->getConfig()->get( 'RCMaxAge' )
649  ],
650  ],
651  'tag' => null,
652  'toponly' => [
653  ApiBase::PARAM_DFLT => false,
655  ],
656  ];
657  }
658 
659  protected function getExamplesMessages() {
660  return [
661  'action=query&list=usercontribs&ucuser=Example'
662  => 'apihelp-query+usercontribs-example-user',
663  'action=query&list=usercontribs&ucuserprefix=192.0.2.'
664  => 'apihelp-query+usercontribs-example-ipprefix',
665  ];
666  }
667 
668  public function getHelpUrls() {
669  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Usercontribs';
670  }
671 }
672 
677 class_alias( ApiQueryUserContribs::class, 'ApiQueryContributions' );
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:63
ChangeTags\makeTagSummarySubquery
static makeTagSummarySubquery( $tables)
Make the tag summary subquery based on the given tables and return it.
Definition: ChangeTags.php:844
ApiQueryBase\processRow
processRow( $row, array &$data, array &$hookData)
Call the ApiQueryBaseProcessRow hook.
Definition: ApiQueryBase.php:444
ApiQueryBase\addFields
addFields( $value)
Add a set of fields to select to the internal array.
Definition: ApiQueryBase.php:193
ApiQueryUserContribs\$fld_timestamp
$fld_timestamp
Definition: ApiQueryUserContribs.php:44
ApiQuery
This is the main query class.
Definition: ApiQuery.php:37
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
ApiQueryUserContribs\$fld_title
$fld_title
Definition: ApiQueryUserContribs.php:44
ApiQueryBase\resetQueryParams
resetQueryParams()
Blank the internal arrays with query parameters.
Definition: ApiQueryBase.php:146
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:134
if
if(ini_get( 'mbstring.func_overload')) if(!defined('MW_ENTRY_POINT'))
Pre-config setup: Before loading LocalSettings.php.
Definition: Setup.php:58
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:1375
ApiQueryBase\addTimestampWhereRange
addTimestampWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, similar to addWhereRange, but converts $start and $end t...
Definition: ApiQueryBase.php:357
ApiBase\PARAM_HELP_MSG
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:96
true
return true
Definition: router.php:92
ApiQueryUserContribs\$fld_patrolled
$fld_patrolled
Definition: ApiQueryUserContribs.php:46
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1873
ApiBase\PARAM_TYPE
const PARAM_TYPE
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:60
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:538
CommentStore
CommentStore handles storage of comments (edit summaries, log reasons, etc) in the database.
Definition: CommentStore.php:31
ApiQueryUserContribs\$fld_size
$fld_size
Definition: ApiQueryUserContribs.php:46
ApiQueryBase\addOption
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
Definition: ApiQueryBase.php:369
$res
$res
Definition: testCompression.php:54
ContextSource\getUser
getUser()
Definition: ContextSource.php:120
User\newFromRow
static newFromRow( $row, $data=null)
Create a new user object from a user row.
Definition: User.php:718
ApiQueryUserContribs\$fld_sizediff
$fld_sizediff
Definition: ApiQueryUserContribs.php:46
$revQuery
$revQuery
Definition: testCompression.php:53
ActorMigration\newMigration
static newMigration()
Static constructor.
Definition: ActorMigration.php:139
ApiQueryBase\addFieldsIf
addFieldsIf( $value, $condition)
Same as addFields(), but add the fields only if a condition is met.
Definition: ApiQueryBase.php:207
ApiQueryUserContribs\__construct
__construct(ApiQuery $query, $moduleName)
Definition: ApiQueryUserContribs.php:35
ApiQueryUserContribs\$orderBy
$orderBy
Definition: ApiQueryUserContribs.php:39
Wikimedia\ParamValidator\ParamValidator::TypeDef\UserDef
Type definition for user types.
Definition: UserDef.php:23
ApiBase\PARAM_DEPRECATED
const PARAM_DEPRECATED
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:65
ApiBase\PARAM_MIN
const PARAM_MIN
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:63
ApiQueryBase\executeGenderCacheFromResultWrapper
executeGenderCacheFromResultWrapper(IResultWrapper $res, $fname=__METHOD__, $fieldPrefix='page')
Preprocess the result set to fill the GenderCache with the necessary information before using self::a...
Definition: ApiQueryBase.php:622
ApiQueryBase
This is a base class for all Query modules.
Definition: ApiQueryBase.php:34
ApiBase\LIMIT_BIG1
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:154
ApiQueryUserContribs
This query action adds a list of a specified user's contributions to the output.
Definition: ApiQueryUserContribs.php:33
ApiQueryBase\getDB
getDB()
Get the Query database connection (read-only)
Definition: ApiQueryBase.php:107
ApiQueryUserContribs\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiQueryUserContribs.php:48
UserArray\newFromResult
static newFromResult( $res)
Definition: UserArray.php:30
ApiBase\PARAM_MAX
const PARAM_MAX
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:61
ApiQueryUserContribs\$parentLens
$parentLens
Definition: ApiQueryUserContribs.php:39
User\isIP
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:948
ApiQueryBase\addTables
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
Definition: ApiQueryBase.php:161
ApiQueryBase\select
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
Definition: ApiQueryBase.php:394
ApiQueryUserContribs\$fld_parsedcomment
$fld_parsedcomment
Definition: ApiQueryUserContribs.php:45
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:659
$title
$title
Definition: testCompression.php:36
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:602
ApiQueryUserContribs\extractRowInfo
extractRowInfo( $row)
Extract fields from the database row and append them to a result array.
Definition: ApiQueryUserContribs.php:456
ApiQueryBase\addWhereRange
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.
Definition: ApiQueryBase.php:322
ApiQueryUserContribs\continueStr
continueStr( $row)
Definition: ApiQueryUserContribs.php:554
ApiQueryUserContribs\prepareQuery
prepareQuery(array $users, $limit)
Prepares the query and returns the limit of rows requested.
Definition: ApiQueryUserContribs.php:274
ApiResult\setIndexedTagName
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:616
ApiBase\requireOnlyOneParameter
requireOnlyOneParameter( $params,... $required)
Die if none or more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:792
ApiQueryUserContribs\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiQueryUserContribs.php:659
ApiBase\dieContinueUsageIf
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition: ApiBase.php:1555
ApiQueryUserContribs\$fld_comment
$fld_comment
Definition: ApiQueryUserContribs.php:45
ApiBase\encodeParamName
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition: ApiBase.php:637
ApiBase\getPermissionManager
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition: ApiBase.php:608
ApiQueryUserContribs\$params
$params
Definition: ApiQueryUserContribs.php:39
ApiQueryUserContribs\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiQueryUserContribs.php:668
ApiQueryUserContribs\getCacheMode
getCacheMode( $params)
Get the cache mode for the data generated by this module.
Definition: ApiQueryUserContribs.php:569
ApiQueryBase\addJoinConds
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
Definition: ApiQueryBase.php:182
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1552
ApiQueryBase\addWhereFld
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
Definition: ApiQueryBase.php:267
ApiQueryUserContribs\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiQueryUserContribs.php:575
RecentChange\PRC_AUTOPATROLLED
const PRC_AUTOPATROLLED
Definition: RecentChange.php:82
Linker\formatComment
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:1167
ApiQueryUserContribs\$commentStore
CommentStore $commentStore
Definition: ApiQueryUserContribs.php:42
ApiQueryUserContribs\$multiUserMode
$multiUserMode
Definition: ApiQueryUserContribs.php:39
ApiBase\LIMIT_BIG2
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:156
User\getCanonicalName
static getCanonicalName( $name, $validate='valid')
Given unvalidated user input, return a canonical username, or false if the username is invalid.
Definition: User.php:1195
ApiBase\PARAM_DFLT
const PARAM_DFLT
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:58
RecentChange\PRC_UNPATROLLED
const PRC_UNPATROLLED
Definition: RecentChange.php:80
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:418
MediaWiki\Storage\NameTableAccessException
Exception representing a failure to look up a row from a name table.
Definition: NameTableAccessException.php:32
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:59
ApiQueryUserContribs\$fld_ids
$fld_ids
Definition: ApiQueryUserContribs.php:44
ApiBase\PARAM_MAX2
const PARAM_MAX2
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:62
ApiQueryBase\addWhere
addWhere( $value)
Add a set of WHERE clauses to the internal array.
Definition: ApiQueryBase.php:230
ApiQueryBase\setContinueEnumParameter
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
Definition: ApiQueryBase.php:511
ApiBase\PARAM_HELP_MSG_PER_VALUE
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:129
CommentStore\getStore
static getStore()
Definition: CommentStore.php:116
ApiQueryUserContribs\$fld_flags
$fld_flags
Definition: ApiQueryUserContribs.php:45
ExternalUserNames\isExternal
static isExternal( $username)
Tells whether the username is external or not.
Definition: ExternalUserNames.php:137
ApiQueryBase\addWhereIf
addWhereIf( $value, $condition)
Same as addWhere(), but add the WHERE clauses only if a condition is met.
Definition: ApiQueryBase.php:248
ApiQueryBase\addTitleInfo
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
Definition: ApiQueryBase.php:462
ApiQueryUserContribs\$fld_tags
$fld_tags
Definition: ApiQueryUserContribs.php:46