MediaWiki master
ApiQueryDeletedrevs.php
Go to the documentation of this file.
1<?php
9namespace MediaWiki\Api;
10
28use Wikimedia\Timestamp\TimestampFormat as TS;
29
37
38 public function __construct(
39 ApiQuery $query,
40 string $moduleName,
41 private readonly CommentStore $commentStore,
42 private readonly RowCommentFormatter $commentFormatter,
43 private readonly RevisionStore $revisionStore,
44 private readonly NameTableStore $changeTagDefStore,
45 private readonly ChangeTagsStore $changeTagsStore,
46 private readonly LinkBatchFactory $linkBatchFactory,
47 ) {
48 parent::__construct( $query, $moduleName, 'dr' );
49 }
50
51 public function execute() {
52 // Before doing anything at all, let's check permissions
53 $this->checkUserRightsAny( 'deletedhistory' );
54
55 $this->addDeprecation( 'apiwarn-deprecation-deletedrevs', 'action=query&list=deletedrevs' );
56
57 $user = $this->getUser();
58 $db = $this->getDB();
59 $params = $this->extractRequestParams( false );
60 $prop = array_fill_keys( $params['prop'], true );
61 $fld_parentid = isset( $prop['parentid'] );
62 $fld_revid = isset( $prop['revid'] );
63 $fld_user = isset( $prop['user'] );
64 $fld_userid = isset( $prop['userid'] );
65 $fld_comment = isset( $prop['comment'] );
66 $fld_parsedcomment = isset( $prop['parsedcomment'] );
67 $fld_minor = isset( $prop['minor'] );
68 $fld_len = isset( $prop['len'] );
69 $fld_sha1 = isset( $prop['sha1'] );
70 $fld_content = isset( $prop['content'] );
71 $fld_token = isset( $prop['token'] );
72 $fld_tags = isset( $prop['tags'] );
73
74 // If we're in a mode that breaks the same-origin policy, no tokens can
75 // be obtained
76 if ( $this->lacksSameOriginSecurity() ||
77 // If user can't undelete, no tokens
78 !$this->getAuthority()->isAllowed( 'undelete' )
79 ) {
80 $fld_token = false;
81 }
82
83 $result = $this->getResult();
84 $pageSet = $this->getPageSet();
85 $titles = $pageSet->getPages();
86
87 // This module operates in three modes:
88 // 'revs': List deleted revs for certain titles (1)
89 // 'user': List deleted revs by a certain user (2)
90 // 'all': List all deleted revs in NS (3)
91 $mode = 'all';
92 if ( count( $titles ) > 0 ) {
93 $mode = 'revs';
94 } elseif ( $params['user'] !== null ) {
95 $mode = 'user';
96 }
97
98 if ( $mode == 'revs' || $mode == 'user' ) {
99 // Ignore namespace and unique due to inability to know whether they were purposely set
100 foreach ( [ 'from', 'to', 'prefix', /*'namespace', 'unique'*/ ] as $p ) {
101 if ( $params[$p] !== null ) {
102 $this->dieWithError( [ 'apierror-deletedrevs-param-not-1-2', $p ], 'badparams' );
103 }
104 }
105 } else {
106 foreach ( [ 'start', 'end' ] as $p ) {
107 if ( $params[$p] !== null ) {
108 $this->dieWithError( [ 'apierror-deletedrevs-param-not-3', $p ], 'badparams' );
109 }
110 }
111 }
112
113 if ( $params['user'] !== null && $params['excludeuser'] !== null ) {
114 $this->dieWithError( 'user and excludeuser cannot be used together', 'badparams' );
115 }
116
117 $arQuery = $this->revisionStore->getArchiveQueryInfo();
118 $this->addTables( $arQuery['tables'] );
119 $this->addFields( $arQuery['fields'] );
120 $this->addJoinConds( $arQuery['joins'] );
121 $this->addFields( [ 'ar_title', 'ar_namespace' ] );
122
123 if ( $fld_tags ) {
124 $this->addFields( [
125 'ts_tags' => $this->changeTagsStore->makeTagSummarySubquery( 'archive' )
126 ] );
127 }
128
129 if ( $fld_sha1 ) {
130 $pairExpr = $db->buildGroupConcat(
131 $db->buildConcat( [ 'sr.role_name', $db->addQuotes( ':' ), 'c.content_sha1' ] ),
132 ','
133 );
134 $sha1Subquery = $db->newSelectQueryBuilder()
135 ->select( [
136 'ar_rev_id',
137 'ar_deleted',
138 'ar_slot_pairs' => $pairExpr,
139 ] )
140 ->from( 'archive' )
141 ->join( 'slots', 's', [ 'ar_rev_id = s.slot_revision_id' ] )
142 ->join( 'content', 'c', [ 's.slot_content_id = c.content_id' ] )
143 ->join( 'slot_roles', 'sr', [ 's.slot_role_id = sr.role_id' ] )
144 ->groupBy( [ 'ar_rev_id', 'ar_deleted' ] )
145 ->caller( __METHOD__ )
146 ->getSQL();
147
148 $this->addTables( [ 'arsha1' => new Subquery( $sha1Subquery ) ] );
149 $this->addFields( [
150 'ar_deleted' => 'arsha1.ar_deleted',
151 'ar_slot_pairs' => 'arsha1.ar_slot_pairs'
152 ] );
153 $this->addJoinConds( [ 'arsha1' => [ 'LEFT JOIN', [ 'ar_rev_id = arsha1.ar_rev_id' ] ] ] );
154 }
155
156 if ( $params['tag'] !== null ) {
157 $this->addTables( 'change_tag' );
158 $this->addJoinConds(
159 [ 'change_tag' => [ 'JOIN', [ 'ar_rev_id=ct_rev_id' ] ] ]
160 );
161 try {
162 $this->addWhereFld( 'ct_tag_id', $this->changeTagDefStore->getId( $params['tag'] ) );
163 } catch ( NameTableAccessException ) {
164 // Return nothing.
165 $this->addWhere( '1=0' );
166 }
167 }
168
169 // This means stricter restrictions
170 if ( $fld_content ) {
171 $this->checkUserRightsAny( [ 'deletedtext', 'undelete' ] );
172 }
173 // Check limits
174 $userMax = $fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1;
175 $botMax = $fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2;
176
177 $limit = $params['limit'];
178
179 if ( $limit == 'max' ) {
180 $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
181 $this->getResult()->addParsedLimit( $this->getModuleName(), $limit );
182 }
183
184 $limit = $this->getMain()->getParamValidator()->validateValue(
185 $this, 'limit', $limit, [
186 ParamValidator::PARAM_TYPE => 'limit',
187 IntegerDef::PARAM_MIN => 1,
188 IntegerDef::PARAM_MAX => $userMax,
189 IntegerDef::PARAM_MAX2 => $botMax,
190 IntegerDef::PARAM_IGNORE_RANGE => true,
191 ]
192 );
193
194 if ( $fld_token ) {
195 // Undelete tokens are identical for all pages, so we cache one here
196 $token = $user->getEditToken( '', $this->getMain()->getRequest() );
197 }
198
199 $dir = $params['dir'];
200
201 // We need a custom WHERE clause that matches all titles.
202 if ( $mode == 'revs' ) {
203 $lb = $this->linkBatchFactory->newLinkBatch( $titles );
204 $where = $lb->constructSet( 'ar', $db );
205 $this->addWhere( $where );
206 } elseif ( $mode == 'all' ) {
207 $this->addWhereFld( 'ar_namespace', $params['namespace'] );
208
209 $from = $params['from'] === null
210 ? null
211 : $this->titlePartToKey( $params['from'], $params['namespace'] );
212 $to = $params['to'] === null
213 ? null
214 : $this->titlePartToKey( $params['to'], $params['namespace'] );
215 $this->addWhereRange( 'ar_title', $dir, $from, $to );
216
217 if ( isset( $params['prefix'] ) ) {
218 $this->addWhere(
219 $db->expr(
220 'ar_title',
221 IExpression::LIKE,
222 new LikeValue(
223 $this->titlePartToKey( $params['prefix'], $params['namespace'] ),
224 $db->anyString()
225 )
226 )
227 );
228 }
229 }
230
231 if ( $params['user'] !== null ) {
232 // We already join on actor due to getArchiveQueryInfo()
233 $this->addWhereFld( 'actor_name', $params['user'] );
234 } elseif ( $params['excludeuser'] !== null ) {
235 $this->addWhere( $db->expr( 'actor_name', '!=', $params['excludeuser'] ) );
236 }
237
238 if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
239 // Paranoia: avoid brute force searches (T19342)
240 // (shouldn't be able to get here without 'deletedhistory', but
241 // check it again just in case)
242 if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
243 $bitmask = RevisionRecord::DELETED_USER;
244 } elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
245 $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
246 } else {
247 $bitmask = 0;
248 }
249 if ( $bitmask ) {
250 $this->addWhere( $db->bitAnd( 'ar_deleted', $bitmask ) . " != $bitmask" );
251 }
252 }
253
254 if ( $params['continue'] !== null ) {
255 $op = ( $dir == 'newer' ? '>=' : '<=' );
256 if ( $mode == 'all' || $mode == 'revs' ) {
257 $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'int', 'string', 'timestamp', 'int' ] );
258 $this->addWhere( $db->buildComparison( $op, [
259 'ar_namespace' => $cont[0],
260 'ar_title' => $cont[1],
261 'ar_timestamp' => $db->timestamp( $cont[2] ),
262 'ar_id' => $cont[3],
263 ] ) );
264 } else {
265 $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'timestamp', 'int' ] );
266 $this->addWhere( $db->buildComparison( $op, [
267 'ar_timestamp' => $db->timestamp( $cont[0] ),
268 'ar_id' => $cont[1],
269 ] ) );
270 }
271 }
272
273 $this->addOption( 'LIMIT', $limit + 1 );
274 if ( $mode == 'all' ) {
275 if ( $params['unique'] ) {
276 // @todo Does this work on non-MySQL?
277 $this->addOption( 'GROUP BY', 'ar_title' );
278 } else {
279 $sort = ( $dir == 'newer' ? '' : ' DESC' );
280 $this->addOption( 'ORDER BY', [
281 'ar_title' . $sort,
282 'ar_timestamp' . $sort,
283 'ar_id' . $sort,
284 ] );
285 }
286 } else {
287 if ( $mode == 'revs' ) {
288 // Sort by ns and title in the same order as timestamp for efficiency
289 $this->addWhereRange( 'ar_namespace', $dir, null, null );
290 $this->addWhereRange( 'ar_title', $dir, null, null );
291 }
292 $this->addTimestampWhereRange( 'ar_timestamp', $dir, $params['start'], $params['end'] );
293 // Include in ORDER BY for uniqueness
294 $this->addWhereRange( 'ar_id', $dir, null, null );
295 }
296 $res = $this->select( __METHOD__ );
297
298 $formattedComments = [];
299 if ( $fld_parsedcomment ) {
300 $formattedComments = $this->commentFormatter->formatItems(
301 $this->commentFormatter->rows( $res )
302 ->indexField( 'ar_id' )
303 ->commentKey( 'ar_comment' )
304 ->namespaceField( 'ar_namespace' )
305 ->titleField( 'ar_title' )
306 );
307 }
308
309 $pageMap = []; // Maps ns&title to (fake) pageid
310 $count = 0;
311 $newPageID = 0;
312 foreach ( $res as $row ) {
313 if ( ++$count > $limit ) {
314 // We've had enough
315 if ( $mode == 'all' || $mode == 'revs' ) {
316 $this->setContinueEnumParameter( 'continue',
317 "$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id"
318 );
319 } else {
320 $this->setContinueEnumParameter( 'continue', "$row->ar_timestamp|$row->ar_id" );
321 }
322 break;
323 }
324
325 $rev = [];
326 $anyHidden = false;
327
328 $rev['timestamp'] = wfTimestamp( TS::ISO_8601, $row->ar_timestamp );
329 if ( $fld_revid ) {
330 $rev['revid'] = (int)$row->ar_rev_id;
331 }
332 if ( $fld_parentid && $row->ar_parent_id !== null ) {
333 $rev['parentid'] = (int)$row->ar_parent_id;
334 }
335 if ( $fld_user || $fld_userid ) {
336 if ( $row->ar_deleted & RevisionRecord::DELETED_USER ) {
337 $rev['userhidden'] = true;
338 $anyHidden = true;
339 }
340 if ( RevisionRecord::userCanBitfield(
341 $row->ar_deleted,
342 RevisionRecord::DELETED_USER,
343 $user
344 ) ) {
345 if ( $fld_user ) {
346 $rev['user'] = $row->ar_user_text;
347 }
348 if ( $fld_userid ) {
349 $rev['userid'] = (int)$row->ar_user;
350 }
351 }
352 }
353
354 if ( $fld_comment || $fld_parsedcomment ) {
355 if ( $row->ar_deleted & RevisionRecord::DELETED_COMMENT ) {
356 $rev['commenthidden'] = true;
357 $anyHidden = true;
358 }
359 if ( RevisionRecord::userCanBitfield(
360 $row->ar_deleted,
361 RevisionRecord::DELETED_COMMENT,
362 $user
363 ) ) {
364 $comment = $this->commentStore->getComment( 'ar_comment', $row )->text;
365 if ( $fld_comment ) {
366 $rev['comment'] = $comment;
367 }
368 if ( $fld_parsedcomment ) {
369 $rev['parsedcomment'] = $formattedComments[$row->ar_id];
370 }
371 }
372 }
373
374 if ( $fld_minor ) {
375 $rev['minor'] = $row->ar_minor_edit == 1;
376 }
377 if ( $fld_len ) {
378 $rev['len'] = $row->ar_len;
379 }
380 if ( $fld_sha1 ) {
381 if ( $row->ar_deleted & RevisionRecord::DELETED_TEXT ) {
382 $rev['sha1hidden'] = true;
383 $anyHidden = true;
384 }
385 if ( RevisionRecord::userCanBitfield(
386 $row->ar_deleted,
387 RevisionRecord::DELETED_TEXT,
388 $user
389 ) ) {
390 if ( $row->ar_slot_pairs !== null ) {
391 $combinedBase36 = '';
392 if ( $row->ar_slot_pairs !== '' ) {
393 $items = explode( ',', $row->ar_slot_pairs );
394 $slotHashes = [];
395 foreach ( $items as $item ) {
396 $parts = explode( ':', $item );
397 $slotHashes[$parts[0]] = $parts[1];
398 }
399 ksort( $slotHashes );
400
401 $accu = null;
402 foreach ( $slotHashes as $slotHash ) {
403 $accu = $accu === null
404 ? $slotHash
405 : SlotRecord::base36Sha1( $accu . $slotHash );
406 }
407 $combinedBase36 = $accu ?? SlotRecord::base36Sha1( '' );
408 }
409
410 $rev['sha1'] = $combinedBase36 !== ''
411 ? \Wikimedia\base_convert( $combinedBase36, 36, 16, 40 )
412 : '';
413 }
414 }
415 }
416 if ( $fld_content ) {
417 if ( $row->ar_deleted & RevisionRecord::DELETED_TEXT ) {
418 $rev['texthidden'] = true;
419 $anyHidden = true;
420 }
421 if ( RevisionRecord::userCanBitfield(
422 $row->ar_deleted,
423 RevisionRecord::DELETED_TEXT,
424 $user
425 ) ) {
426 ApiResult::setContentValue( $rev, 'text',
427 $this->revisionStore->newRevisionFromArchiveRow( $row )
428 ->getContent( SlotRecord::MAIN )->serialize() );
429 }
430 }
431
432 if ( $fld_tags ) {
433 if ( $row->ts_tags ) {
434 $tags = explode( ',', $row->ts_tags );
435 ApiResult::setIndexedTagName( $tags, 'tag' );
436 $rev['tags'] = $tags;
437 } else {
438 $rev['tags'] = [];
439 }
440 }
441
442 if ( $anyHidden && ( $row->ar_deleted & RevisionRecord::DELETED_RESTRICTED ) ) {
443 $rev['suppressed'] = true;
444 }
445
446 if ( !isset( $pageMap[$row->ar_namespace][$row->ar_title] ) ) {
447 $pageID = $newPageID++;
448 $pageMap[$row->ar_namespace][$row->ar_title] = $pageID;
449 $a = [ 'revisions' => [ $rev ] ];
450 ApiResult::setIndexedTagName( $a['revisions'], 'rev' );
451 $title = Title::makeTitle( $row->ar_namespace, $row->ar_title );
452 ApiQueryBase::addTitleInfo( $a, $title );
453 if ( $fld_token ) {
454 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable token is set when used
455 $a['token'] = $token;
456 }
457 $fit = $result->addValue( [ 'query', $this->getModuleName() ], $pageID, $a );
458 } else {
459 $pageID = $pageMap[$row->ar_namespace][$row->ar_title];
460 $fit = $result->addValue(
461 [ 'query', $this->getModuleName(), $pageID, 'revisions' ],
462 null, $rev );
463 }
464 if ( !$fit ) {
465 if ( $mode == 'all' || $mode == 'revs' ) {
466 $this->setContinueEnumParameter( 'continue',
467 "$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id"
468 );
469 } else {
470 $this->setContinueEnumParameter( 'continue', "$row->ar_timestamp|$row->ar_id" );
471 }
472 break;
473 }
474 }
475 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'page' );
476 }
477
479 public function isDeprecated() {
480 return true;
481 }
482
484 public function getAllowedParams() {
485 $smallLimit = $this->getMain()->canApiHighLimits() ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_SML1;
486 return [
487 'start' => [
488 ParamValidator::PARAM_TYPE => 'timestamp',
489 ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 1, 2 ] ],
490 ],
491 'end' => [
492 ParamValidator::PARAM_TYPE => 'timestamp',
493 ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 1, 2 ] ],
494 ],
495 'dir' => [
496 ParamValidator::PARAM_TYPE => [
497 'newer',
498 'older'
499 ],
500 ParamValidator::PARAM_DEFAULT => 'older',
501 ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
503 'newer' => 'api-help-paramvalue-direction-newer',
504 'older' => 'api-help-paramvalue-direction-older',
505 ],
506 ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 1, 3 ] ],
507 ],
508 'from' => [
509 ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ],
510 ],
511 'to' => [
512 ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ],
513 ],
514 'prefix' => [
515 ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ],
516 ],
517 'unique' => [
518 ParamValidator::PARAM_DEFAULT => false,
519 ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ],
520 ],
521 'namespace' => [
522 ParamValidator::PARAM_TYPE => 'namespace',
523 ParamValidator::PARAM_DEFAULT => NS_MAIN,
524 ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ],
525 ],
526 'tag' => null,
527 'user' => [
528 ParamValidator::PARAM_TYPE => 'user',
529 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'temp', 'id', 'interwiki' ],
530 ],
531 'excludeuser' => [
532 ParamValidator::PARAM_TYPE => 'user',
533 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'temp', 'id', 'interwiki' ],
534 ],
535 'prop' => [
536 ParamValidator::PARAM_DEFAULT => 'user|comment',
537 ParamValidator::PARAM_TYPE => [
538 'revid',
539 'parentid',
540 'user',
541 'userid',
542 'comment',
543 'parsedcomment',
544 'minor',
545 'len',
546 'sha1',
547 'content',
548 'token',
549 'tags'
550 ],
551 ParamValidator::PARAM_ISMULTI => true,
553 'content' => [ 'apihelp-query+deletedrevs-paramvalue-prop-content', $smallLimit ],
554 ],
555 EnumDef::PARAM_DEPRECATED_VALUES => [
556 'token' => true,
557 ],
558 ],
559 'limit' => [
560 ParamValidator::PARAM_DEFAULT => 10,
561 ParamValidator::PARAM_TYPE => 'limit',
562 IntegerDef::PARAM_MIN => 1,
563 IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
564 IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2,
565 ApiBase::PARAM_HELP_MSG => [ 'apihelp-query+deletedrevs-param-limit', $smallLimit ],
566 ],
567 'continue' => [
568 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
569 ],
570 ];
571 }
572
574 protected function getExamplesMessages() {
575 $title = Title::newMainPage();
576 $talkTitle = $title->getTalkPageIfDefined();
577 $examples = [];
578
579 if ( $talkTitle ) {
580 $title = rawurlencode( $title->getPrefixedText() );
581 $talkTitle = rawurlencode( $talkTitle->getPrefixedText() );
582 $examples = [
583 "action=query&list=deletedrevs&titles={$title}|{$talkTitle}&" .
584 'drprop=user|comment|content'
585 => 'apihelp-query+deletedrevs-example-mode1',
586 ];
587 }
588
589 return array_merge( $examples, [
590 'action=query&list=deletedrevs&druser=Bob&drlimit=50'
591 => 'apihelp-query+deletedrevs-example-mode2',
592 'action=query&list=deletedrevs&drdir=newer&drlimit=50'
593 => 'apihelp-query+deletedrevs-example-mode3-main',
594 'action=query&list=deletedrevs&drdir=newer&drlimit=50&drnamespace=1&drunique='
595 => 'apihelp-query+deletedrevs-example-mode3-talk',
596 ] );
597 }
598
600 public function getHelpUrls() {
601 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Deletedrevs';
602 }
603}
604
606class_alias( ApiQueryDeletedrevs::class, 'ApiQueryDeletedrevs' );
const NS_MAIN
Definition Defines.php:51
wfTimestamp( $outputtype=TS::UNIX, $ts=0)
Get a timestamp string in one of various formats.
const LIMIT_SML1
Slow query, standard limit.
Definition ApiBase.php:235
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1506
checkUserRightsAny( $rights)
Helper function for permission-denied errors.
Definition ApiBase.php:1615
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:542
const PARAM_HELP_MSG_INFO
(array) Specify additional information tags for the parameter.
Definition ApiBase.php:184
getMain()
Get the main module.
Definition ApiBase.php:560
parseContinueParamOrDie(string $continue, array $types)
Parse the 'continue' parameter in the usual format and validate the types of each part,...
Definition ApiBase.php:1691
getResult()
Get the result object.
Definition ApiBase.php:681
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, or 'string' with PARAM_ISMULTI,...
Definition ApiBase.php:206
const LIMIT_SML2
Slow query, apihighlimits limit.
Definition ApiBase.php:237
lacksSameOriginSecurity()
Returns true if the current request breaks the same-origin policy.
Definition ApiBase.php:608
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:166
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition ApiBase.php:233
addDeprecation( $msg, $feature, $data=[])
Add a deprecation warning for this module.
Definition ApiBase.php:1438
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:822
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:231
This is a base class for all Query modules.
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
getDB()
Get the Query database connection (read-only).
select( $method, $extraQuery=[], ?array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
titlePartToKey( $titlePart, $namespace=NS_MAIN)
Convert an input title or title prefix into a dbkey.
addWhere( $value)
Add a set of WHERE clauses to the internal array.
getPageSet()
Get the PageSet object to work on.
addTimestampWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, similar to addWhereRange, but converts $start and $end t...
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
addFields( $value)
Add a set of fields to select to the internal array.
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.
Query module to enumerate all deleted revisions.
isDeprecated()
Indicates whether this module is deprecated.1.25 to override bool
getHelpUrls()
Return links to more detailed help pages about the module.1.25, returning boolean false is deprecated...
__construct(ApiQuery $query, string $moduleName, private readonly CommentStore $commentStore, private readonly RowCommentFormatter $commentFormatter, private readonly RevisionStore $revisionStore, private readonly NameTableStore $changeTagDefStore, private readonly ChangeTagsStore $changeTagsStore, private readonly LinkBatchFactory $linkBatchFactory,)
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
getExamplesMessages()
Returns usage examples for this module.Return value has query strings as keys, with values being eith...
This is the main query class.
Definition ApiQuery.php:36
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
static setContentValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name and mark as META_CONTENT.
Read-write access to the change_tags table.
This is basically a CommentFormatter with a CommentStore dependency, allowing it to retrieve comment ...
Handle database storage of comments such as edit summaries and log reasons.
makeTitle( $linkId)
Convert a link ID to a Title.to override Title
Factory for LinkBatch objects to batch query page metadata.
Type definition for user types.
Definition UserDef.php:27
Page revision base class.
Service for looking up page revisions.
Value object representing a content slot associated with a page revision.
Exception representing a failure to look up a row from a name table.
Represents a title within MediaWiki.
Definition Title.php:69
Service for formatting and validating API parameters.
Type definition for enumeration types.
Definition EnumDef.php:32
Type definition for integer types.
Content of like value.
Definition LikeValue.php:14