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