MediaWiki  master
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 setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:604
static setContentValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name and mark as META_CONTENT.
Definition: ApiResult.php:467
static makeTagSummarySubquery( $tables)
Make the tag summary subquery based on the given tables and return it.
Definition: ChangeTags.php:664
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.
Page revision base class.
Service for looking up page revisions.
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40
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.
Definition: IntegerDef.php:23