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