MediaWiki REL1_35
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
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 ->getRevisionSizes( $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 $this->addOption(
449 'MAX_EXECUTION_TIME',
450 $this->getConfig()->get( 'MaxExecutionTimeForExpensiveQueries' )
451 );
452 }
453
460 private function extractRowInfo( $row ) {
461 $vals = [];
462 $anyHidden = false;
463
464 if ( $row->rev_deleted & RevisionRecord::DELETED_TEXT ) {
465 $vals['texthidden'] = true;
466 $anyHidden = true;
467 }
468
469 // Any rows where we can't view the user were filtered out in the query.
470 $vals['userid'] = (int)$row->rev_user;
471 $vals['user'] = $row->rev_user_text;
472 if ( $row->rev_deleted & RevisionRecord::DELETED_USER ) {
473 $vals['userhidden'] = true;
474 $anyHidden = true;
475 }
476 if ( $this->fld_ids ) {
477 $vals['pageid'] = (int)$row->rev_page;
478 $vals['revid'] = (int)$row->rev_id;
479
480 if ( $row->rev_parent_id !== null ) {
481 $vals['parentid'] = (int)$row->rev_parent_id;
482 }
483 }
484
485 $title = Title::makeTitle( $row->page_namespace, $row->page_title );
486
487 if ( $this->fld_title ) {
489 }
490
491 if ( $this->fld_timestamp ) {
492 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rev_timestamp );
493 }
494
495 if ( $this->fld_flags ) {
496 $vals['new'] = $row->rev_parent_id == 0 && $row->rev_parent_id !== null;
497 $vals['minor'] = (bool)$row->rev_minor_edit;
498 $vals['top'] = $row->page_latest == $row->rev_id;
499 }
500
501 if ( $this->fld_comment || $this->fld_parsedcomment ) {
502 if ( $row->rev_deleted & RevisionRecord::DELETED_COMMENT ) {
503 $vals['commenthidden'] = true;
504 $anyHidden = true;
505 }
506
507 $userCanView = RevisionRecord::userCanBitfield(
508 $row->rev_deleted,
509 RevisionRecord::DELETED_COMMENT, $this->getUser()
510 );
511
512 if ( $userCanView ) {
513 $comment = $this->commentStore->getComment( 'rev_comment', $row )->text;
514 if ( $this->fld_comment ) {
515 $vals['comment'] = $comment;
516 }
517
518 if ( $this->fld_parsedcomment ) {
519 $vals['parsedcomment'] = Linker::formatComment( $comment, $title );
520 }
521 }
522 }
523
524 if ( $this->fld_patrolled ) {
525 $vals['patrolled'] = $row->rc_patrolled != RecentChange::PRC_UNPATROLLED;
526 $vals['autopatrolled'] = $row->rc_patrolled == RecentChange::PRC_AUTOPATROLLED;
527 }
528
529 if ( $this->fld_size && $row->rev_len !== null ) {
530 $vals['size'] = (int)$row->rev_len;
531 }
532
533 if ( $this->fld_sizediff
534 && $row->rev_len !== null
535 && $row->rev_parent_id !== null
536 ) {
537 $parentLen = $this->parentLens[$row->rev_parent_id] ?? 0;
538 $vals['sizediff'] = (int)$row->rev_len - $parentLen;
539 }
540
541 if ( $this->fld_tags ) {
542 if ( $row->ts_tags ) {
543 $tags = explode( ',', $row->ts_tags );
544 ApiResult::setIndexedTagName( $tags, 'tag' );
545 $vals['tags'] = $tags;
546 } else {
547 $vals['tags'] = [];
548 }
549 }
550
551 if ( $anyHidden && ( $row->rev_deleted & RevisionRecord::DELETED_RESTRICTED ) ) {
552 $vals['suppressed'] = true;
553 }
554
555 return $vals;
556 }
557
558 private function continueStr( $row ) {
559 if ( $this->multiUserMode ) {
560 switch ( $this->orderBy ) {
561 case 'id':
562 return "id|$row->rev_user|$row->rev_timestamp|$row->rev_id";
563 case 'name':
564 return "name|$row->rev_user_text|$row->rev_timestamp|$row->rev_id";
565 case 'actor':
566 return "actor|$row->rev_actor|$row->rev_timestamp|$row->rev_id";
567 }
568 } else {
569 return "$row->rev_timestamp|$row->rev_id";
570 }
571 }
572
573 public function getCacheMode( $params ) {
574 // This module provides access to deleted revisions and patrol flags if
575 // the requester is logged in
576 return 'anon-public-user-private';
577 }
578
579 public function getAllowedParams() {
580 return [
581 'limit' => [
583 ApiBase::PARAM_TYPE => 'limit',
587 ],
588 'start' => [
589 ApiBase::PARAM_TYPE => 'timestamp'
590 ],
591 'end' => [
592 ApiBase::PARAM_TYPE => 'timestamp'
593 ],
594 'continue' => [
595 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
596 ],
597 'user' => [
598 ApiBase::PARAM_TYPE => 'user',
599 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'interwiki' ],
601 ],
602 'userids' => [
603 ApiBase::PARAM_TYPE => 'integer',
605 ],
606 'userprefix' => null,
607 'dir' => [
608 ApiBase::PARAM_DFLT => 'older',
610 'newer',
611 'older'
612 ],
613 ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
614 ],
615 'namespace' => [
617 ApiBase::PARAM_TYPE => 'namespace'
618 ],
619 'prop' => [
621 ApiBase::PARAM_DFLT => 'ids|title|timestamp|comment|size|flags',
623 'ids',
624 'title',
625 'timestamp',
626 'comment',
627 'parsedcomment',
628 'size',
629 'sizediff',
630 'flags',
631 'patrolled',
632 'tags'
633 ],
635 ],
636 'show' => [
639 'minor',
640 '!minor',
641 'patrolled',
642 '!patrolled',
643 'autopatrolled',
644 '!autopatrolled',
645 'top',
646 '!top',
647 'new',
648 '!new',
649 ],
651 'apihelp-query+usercontribs-param-show',
652 $this->getConfig()->get( 'RCMaxAge' )
653 ],
654 ],
655 'tag' => null,
656 'toponly' => [
657 ApiBase::PARAM_DFLT => false,
659 ],
660 ];
661 }
662
663 protected function getExamplesMessages() {
664 return [
665 'action=query&list=usercontribs&ucuser=Example'
666 => 'apihelp-query+usercontribs-example-user',
667 'action=query&list=usercontribs&ucuserprefix=192.0.2.'
668 => 'apihelp-query+usercontribs-example-ipprefix',
669 ];
670 }
671
672 public function getHelpUrls() {
673 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Usercontribs';
674 }
675}
676
681class_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,...
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1437
const PARAM_MAX2
Definition ApiBase.php:86
const PARAM_DEPRECATED
Definition ApiBase.php:98
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition ApiBase.php:750
const PARAM_MAX
Definition ApiBase.php:82
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition ApiBase.php:1617
const PARAM_TYPE
Definition ApiBase.php:78
const PARAM_DFLT
Definition ApiBase.php:70
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition ApiBase.php:692
requireOnlyOneParameter( $params,... $required)
Die if none or more than one of a certain set of parameters is set and not false.
Definition ApiBase.php:909
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:195
const PARAM_MIN
Definition ApiBase.php:90
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:220
getResult()
Get the result object.
Definition ApiBase.php:620
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:772
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:162
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition ApiBase.php:222
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:499
const PARAM_ISMULTI
Definition ApiBase.php:74
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) Stable to override.
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...
getCacheMode( $params)
Get the cache mode for the data generated by this module.
getHelpUrls()
Return links to more detailed help pages about the module.
getExamplesMessages()
Returns usage examples for this module.
__construct(ApiQuery $query, $moduleName)
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.
This is the main query class.
Definition ApiQuery.php:37
static makeTagSummarySubquery( $tables)
Make the tag summary subquery based on the given tables and return it.
CommentStore handles storage of comments (edit summaries, log reasons, etc) in the database.
getUser()
Stable to override.
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:1209
MediaWikiServices is the service locator for the application scope of MediaWiki.
Type definition for user types.
Definition UserDef.php:23
Page revision base class.
Exception representing a failure to look up a row from a name table.
static newFromResult( $res)
Definition UserArray.php:30
static getCanonicalName( $name, $validate='valid')
Given unvalidated user input, return a canonical username, or false if the username is invalid.
Definition User.php:1130
static newFromRow( $row, $data=null)
Create a new user object from a user row.
Definition User.php:717
static isIP( $name)
Does the string match an anonymous IP address?
Definition User.php:951
return true
Definition router.php:92