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