MediaWiki  master
ApiQueryRevisionsBase.php
Go to the documentation of this file.
1 <?php
37 
46 
47  // region Constants for internal use. Don't use externally.
50  // Bits to indicate the results of the revdel permission check on a revision,
51  // see self::checkRevDel()
52  private const IS_DELETED = 1; // Whether the field is revision-deleted
53  private const CANNOT_VIEW = 2; // Whether the user cannot view the field due to revdel
54 
55  // endregion
56 
60 
61  protected $fld_ids = false, $fld_flags = false, $fld_timestamp = false,
62  $fld_size = false, $fld_slotsize = false, $fld_sha1 = false, $fld_slotsha1 = false,
63  $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_userid = false,
64  $fld_content = false, $fld_tags = false, $fld_contentmodel = false, $fld_roles = false,
65  $fld_parsetree = false;
66 
68  private $revisionStore;
69 
72 
74  private $parserFactory;
75 
78 
81 
84 
98  public function __construct(
99  ApiQuery $queryModule,
100  $moduleName,
101  $paramPrefix = '',
108  ) {
109  parent::__construct( $queryModule, $moduleName, $paramPrefix );
110  // This class is part of the stable interface and
111  // therefor fallback to global state, if services are not provided
112  $services = MediaWikiServices::getInstance();
113  $this->revisionStore = $revisionStore ?? $services->getRevisionStore();
114  $this->contentHandlerFactory = $contentHandlerFactory ?? $services->getContentHandlerFactory();
115  $this->parserFactory = $parserFactory ?? $services->getParserFactory();
116  $this->slotRoleRegistry = $slotRoleRegistry ?? $services->getSlotRoleRegistry();
117  $this->contentRenderer = $contentRenderer ?? $services->getContentRenderer();
118  $this->contentTransformer = $contentTransformer ?? $services->getContentTransformer();
119  }
120 
121  public function execute() {
122  $this->run();
123  }
124 
125  public function executeGenerator( $resultPageSet ) {
126  $this->run( $resultPageSet );
127  }
128 
133  abstract protected function run( ApiPageSet $resultPageSet = null );
134 
140  protected function parseParameters( $params ) {
141  $prop = array_fill_keys( $params['prop'], true );
142 
143  $this->fld_ids = isset( $prop['ids'] );
144  $this->fld_flags = isset( $prop['flags'] );
145  $this->fld_timestamp = isset( $prop['timestamp'] );
146  $this->fld_comment = isset( $prop['comment'] );
147  $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
148  $this->fld_size = isset( $prop['size'] );
149  $this->fld_slotsize = isset( $prop['slotsize'] );
150  $this->fld_sha1 = isset( $prop['sha1'] );
151  $this->fld_slotsha1 = isset( $prop['slotsha1'] );
152  $this->fld_content = isset( $prop['content'] );
153  $this->fld_contentmodel = isset( $prop['contentmodel'] );
154  $this->fld_userid = isset( $prop['userid'] );
155  $this->fld_user = isset( $prop['user'] );
156  $this->fld_tags = isset( $prop['tags'] );
157  $this->fld_roles = isset( $prop['roles'] );
158  $this->fld_parsetree = isset( $prop['parsetree'] );
159 
160  $this->slotRoles = $params['slots'];
161 
162  if ( $this->slotRoles !== null ) {
163  if ( $this->fld_parsetree ) {
164  $this->dieWithError( [
165  'apierror-invalidparammix-cannotusewith',
166  $this->encodeParamName( 'prop=parsetree' ),
167  $this->encodeParamName( 'slots' ),
168  ], 'invalidparammix' );
169  }
170  foreach ( [
171  'expandtemplates', 'generatexml', 'parse', 'diffto', 'difftotext', 'difftotextpst',
172  'contentformat'
173  ] as $p ) {
174  if ( $params[$p] !== null && $params[$p] !== false ) {
175  $this->dieWithError( [
176  'apierror-invalidparammix-cannotusewith',
177  $this->encodeParamName( $p ),
178  $this->encodeParamName( 'slots' ),
179  ], 'invalidparammix' );
180  }
181  }
182  }
183 
184  if ( !empty( $params['contentformat'] ) ) {
185  $this->contentFormat = $params['contentformat'];
186  }
187 
188  $this->limit = $params['limit'];
189 
190  if ( $params['difftotext'] !== null ) {
191  $this->difftotext = $params['difftotext'];
192  $this->difftotextpst = $params['difftotextpst'];
193  } elseif ( $params['diffto'] !== null ) {
194  if ( $params['diffto'] == 'cur' ) {
195  $params['diffto'] = 0;
196  }
197  if ( ( !ctype_digit( $params['diffto'] ) || $params['diffto'] < 0 )
198  && $params['diffto'] != 'prev' && $params['diffto'] != 'next'
199  ) {
200  $p = $this->getModulePrefix();
201  $this->dieWithError( [ 'apierror-baddiffto', $p ], 'diffto' );
202  }
203  // Check whether the revision exists and is readable,
204  // DifferenceEngine returns a rather ambiguous empty
205  // string if that's not the case
206  if ( $params['diffto'] != 0 ) {
207  $difftoRev = $this->revisionStore->getRevisionById( $params['diffto'] );
208  if ( !$difftoRev ) {
209  $this->dieWithError( [ 'apierror-nosuchrevid', $params['diffto'] ] );
210  }
211  // @phan-suppress-next-line PhanTypeMismatchArgumentNullable T240141
212  $revDel = $this->checkRevDel( $difftoRev, RevisionRecord::DELETED_TEXT );
213  if ( $revDel & self::CANNOT_VIEW ) {
214  $this->addWarning( [ 'apiwarn-difftohidden', $difftoRev->getId() ] );
215  $params['diffto'] = null;
216  }
217  }
218  $this->diffto = $params['diffto'];
219  }
220 
221  $this->fetchContent = $this->fld_content || $this->diffto !== null
222  || $this->difftotext !== null || $this->fld_parsetree;
223 
224  $smallLimit = false;
225  if ( $this->fetchContent ) {
226  $smallLimit = true;
227  $this->expandTemplates = $params['expandtemplates'];
228  $this->generateXML = $params['generatexml'];
229  $this->parseContent = $params['parse'];
230  if ( $this->parseContent ) {
231  // Must manually initialize unset limit
232  if ( $this->limit === null ) {
233  $this->limit = 1;
234  }
235  }
236  $this->section = $params['section'] ?? false;
237  }
238 
239  $userMax = $this->parseContent ? 1 : ( $smallLimit ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 );
240  $botMax = $this->parseContent ? 1 : ( $smallLimit ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 );
241  if ( $this->limit == 'max' ) {
242  $this->limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
243  if ( $this->setParsedLimit ) {
244  $this->getResult()->addParsedLimit( $this->getModuleName(), $this->limit );
245  }
246  }
247 
248  $this->limit = $this->getMain()->getParamValidator()->validateValue(
249  $this, 'limit', $this->limit ?? 10, [
250  ParamValidator::PARAM_TYPE => 'limit',
251  IntegerDef::PARAM_MIN => 1,
252  IntegerDef::PARAM_MAX => $userMax,
253  IntegerDef::PARAM_MAX2 => $botMax,
254  IntegerDef::PARAM_IGNORE_RANGE => true,
255  ]
256  );
257 
258  $this->needSlots = $this->fetchContent || $this->fld_contentmodel ||
259  $this->fld_slotsize || $this->fld_slotsha1;
260  if ( $this->needSlots && $this->slotRoles === null ) {
261  $encParam = $this->encodeParamName( 'slots' );
262  $name = $this->getModuleName();
263  $parent = $this->getParent();
264  $parentParam = $parent->encodeParamName( $parent->getModuleManager()->getModuleGroup( $name ) );
265  $this->addDeprecation(
266  [ 'apiwarn-deprecation-missingparam', $encParam ],
267  "action=query&{$parentParam}={$name}&!{$encParam}"
268  );
269  }
270  }
271 
279  private function checkRevDel( RevisionRecord $revision, $field ) {
280  $ret = $revision->isDeleted( $field ) ? self::IS_DELETED : 0;
281  if ( $ret ) {
282  $canSee = $revision->userCan( $field, $this->getAuthority() );
283  $ret |= ( $canSee ? 0 : self::CANNOT_VIEW );
284  }
285  return $ret;
286  }
287 
296  protected function extractRevisionInfo( RevisionRecord $revision, $row ) {
297  $vals = [];
298  $anyHidden = false;
299 
300  if ( $this->fld_ids ) {
301  $vals['revid'] = (int)$revision->getId();
302  if ( $revision->getParentId() !== null ) {
303  $vals['parentid'] = (int)$revision->getParentId();
304  }
305  }
306 
307  if ( $this->fld_flags ) {
308  $vals['minor'] = $revision->isMinor();
309  }
310 
311  if ( $this->fld_user || $this->fld_userid ) {
312  $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_USER );
313  if ( $revDel & self::IS_DELETED ) {
314  $vals['userhidden'] = true;
315  $anyHidden = true;
316  }
317  if ( !( $revDel & self::CANNOT_VIEW ) ) {
318  $u = $revision->getUser( RevisionRecord::RAW );
319  if ( $this->fld_user ) {
320  $vals['user'] = $u->getName();
321  }
322  if ( !$u->isRegistered() ) {
323  $vals['anon'] = true;
324  }
325 
326  if ( $this->fld_userid ) {
327  $vals['userid'] = $u->getId();
328  }
329  }
330  }
331 
332  if ( $this->fld_timestamp ) {
333  $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $revision->getTimestamp() );
334  }
335 
336  if ( $this->fld_size ) {
337  try {
338  $vals['size'] = (int)$revision->getSize();
339  } catch ( RevisionAccessException $e ) {
340  // Back compat: If there's no size, return 0.
341  // @todo: Gergő says to mention T198099 as a "todo" here.
342  $vals['size'] = 0;
343  }
344  }
345 
346  if ( $this->fld_sha1 ) {
347  $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
348  if ( $revDel & self::IS_DELETED ) {
349  $vals['sha1hidden'] = true;
350  $anyHidden = true;
351  }
352  if ( !( $revDel & self::CANNOT_VIEW ) ) {
353  try {
354  $vals['sha1'] = Wikimedia\base_convert( $revision->getSha1(), 36, 16, 40 );
355  } catch ( RevisionAccessException $e ) {
356  // Back compat: If there's no sha1, return empty string.
357  // @todo: Gergő says to mention T198099 as a "todo" here.
358  $vals['sha1'] = '';
359  }
360  }
361  }
362 
363  try {
364  if ( $this->fld_roles ) {
365  $vals['roles'] = $revision->getSlotRoles();
366  }
367 
368  if ( $this->needSlots ) {
369  $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
370  if ( ( $this->fld_slotsha1 || $this->fetchContent ) && ( $revDel & self::IS_DELETED ) ) {
371  $anyHidden = true;
372  }
373  $vals = array_merge( $vals, $this->extractAllSlotInfo( $revision, $revDel ) );
374  }
375  } catch ( RevisionAccessException $ex ) {
376  // This is here so T212428 doesn't spam the log.
377  // TODO: find out why T212428 happens in the first place!
378  $vals['slotsmissing'] = true;
379 
380  LoggerFactory::getInstance( 'api-warning' )->error(
381  'Failed to access revision slots',
382  [ 'revision' => $revision->getId(), 'exception' => $ex, ]
383  );
384  }
385 
386  if ( $this->fld_comment || $this->fld_parsedcomment ) {
387  $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_COMMENT );
388  if ( $revDel & self::IS_DELETED ) {
389  $vals['commenthidden'] = true;
390  $anyHidden = true;
391  }
392  if ( !( $revDel & self::CANNOT_VIEW ) ) {
393  $comment = $revision->getComment( RevisionRecord::RAW );
394  $comment = $comment ? $comment->text : '';
395 
396  if ( $this->fld_comment ) {
397  $vals['comment'] = $comment;
398  }
399 
400  if ( $this->fld_parsedcomment ) {
401  $vals['parsedcomment'] = Linker::formatComment(
402  $comment, Title::newFromLinkTarget( $revision->getPageAsLinkTarget() )
403  );
404  }
405  }
406  }
407 
408  if ( $this->fld_tags ) {
409  if ( $row->ts_tags ) {
410  $tags = explode( ',', $row->ts_tags );
411  ApiResult::setIndexedTagName( $tags, 'tag' );
412  $vals['tags'] = $tags;
413  } else {
414  $vals['tags'] = [];
415  }
416  }
417 
418  if ( $anyHidden && $revision->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
419  $vals['suppressed'] = true;
420  }
421 
422  return $vals;
423  }
424 
434  private function extractAllSlotInfo( RevisionRecord $revision, $revDel ): array {
435  $vals = [];
436 
437  if ( $this->slotRoles === null ) {
438  try {
439  $slot = $revision->getSlot( SlotRecord::MAIN, RevisionRecord::RAW );
440  } catch ( RevisionAccessException $e ) {
441  // Back compat: If there's no slot, there's no content, so set 'textmissing'
442  // @todo: Gergő says to mention T198099 as a "todo" here.
443  $vals['textmissing'] = true;
444  $slot = null;
445  }
446 
447  if ( $slot ) {
448  $content = null;
449  $vals += $this->extractSlotInfo( $slot, $revDel, $content );
450  if ( !empty( $vals['nosuchsection'] ) ) {
451  $this->dieWithError(
452  [
453  'apierror-nosuchsection-what',
454  wfEscapeWikiText( $this->section ),
455  $this->msg( 'revid', $revision->getId() )
456  ],
457  'nosuchsection'
458  );
459  }
460  if ( $content ) {
461  $vals += $this->extractDeprecatedContent( $content, $revision );
462  }
463  }
464  } else {
465  $roles = array_intersect( $this->slotRoles, $revision->getSlotRoles() );
466  $vals['slots'] = [
468  ];
469  foreach ( $roles as $role ) {
470  try {
471  $slot = $revision->getSlot( $role, RevisionRecord::RAW );
472  } catch ( RevisionAccessException $e ) {
473  // Don't error out here so the client can still process other slots/revisions.
474  // @todo: Gergő says to mention T198099 as a "todo" here.
475  $vals['slots'][$role]['missing'] = true;
476  continue;
477  }
478  $content = null;
479  $vals['slots'][$role] = $this->extractSlotInfo( $slot, $revDel, $content );
480  // @todo Move this into extractSlotInfo() (and remove its $content parameter)
481  // when extractDeprecatedContent() is no more.
482  if ( $content ) {
484  $vals['slots'][$role]['contentmodel'] = $content->getModel();
485  $vals['slots'][$role]['contentformat'] = $content->getDefaultFormat();
487  $vals['slots'][$role],
488  'content',
489  $content->serialize()
490  );
491  }
492  }
493  ApiResult::setArrayType( $vals['slots'], 'kvp', 'role' );
494  ApiResult::setIndexedTagName( $vals['slots'], 'slot' );
495  }
496  return $vals;
497  }
498 
508  private function extractSlotInfo( SlotRecord $slot, $revDel, &$content = null ) {
509  $vals = [];
510  ApiResult::setArrayType( $vals, 'assoc' );
511 
512  if ( $this->fld_slotsize ) {
513  $vals['size'] = (int)$slot->getSize();
514  }
515 
516  if ( $this->fld_slotsha1 ) {
517  if ( $revDel & self::IS_DELETED ) {
518  $vals['sha1hidden'] = true;
519  }
520  if ( !( $revDel & self::CANNOT_VIEW ) ) {
521  if ( $slot->getSha1() != '' ) {
522  $vals['sha1'] = Wikimedia\base_convert( $slot->getSha1(), 36, 16, 40 );
523  } else {
524  $vals['sha1'] = '';
525  }
526  }
527  }
528 
529  if ( $this->fld_contentmodel ) {
530  $vals['contentmodel'] = $slot->getModel();
531  }
532 
533  $content = null;
534  if ( $this->fetchContent ) {
535  if ( $revDel & self::IS_DELETED ) {
536  $vals['texthidden'] = true;
537  }
538  if ( !( $revDel & self::CANNOT_VIEW ) ) {
539  try {
540  $content = $slot->getContent();
541  } catch ( RevisionAccessException $e ) {
542  // @todo: Gergő says to mention T198099 as a "todo" here.
543  $vals['textmissing'] = true;
544  }
545  // Expand templates after getting section content because
546  // template-added sections don't count and Parser::preprocess()
547  // will have less input
548  if ( $content && $this->section !== false ) {
549  $content = $content->getSection( $this->section );
550  if ( !$content ) {
551  $vals['nosuchsection'] = true;
552  }
553  }
554  }
555  }
556 
557  return $vals;
558  }
559 
566  private function extractDeprecatedContent( Content $content, RevisionRecord $revision ) {
567  $vals = [];
569 
570  if ( $this->fld_parsetree || ( $this->fld_content && $this->generateXML ) ) {
571  if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
573  '@phan-var WikitextContent $content';
574  $t = $content->getText(); # note: don't set $text
575 
576  $parser = $this->parserFactory->create();
577  $parser->startExternalParse(
578  $title,
579  ParserOptions::newFromContext( $this->getContext() ),
580  Parser::OT_PREPROCESS
581  );
582  $dom = $parser->preprocessToDom( $t );
583  if ( is_callable( [ $dom, 'saveXML' ] ) ) {
584  // @phan-suppress-next-line PhanUndeclaredMethod
585  $xml = $dom->saveXML();
586  } else {
587  // @phan-suppress-next-line PhanUndeclaredMethod
588  $xml = $dom->__toString();
589  }
590  $vals['parsetree'] = $xml;
591  } else {
592  $vals['badcontentformatforparsetree'] = true;
593  $this->addWarning(
594  [
595  'apierror-parsetree-notwikitext-title',
596  wfEscapeWikiText( $title->getPrefixedText() ),
597  $content->getModel()
598  ],
599  'parsetree-notwikitext'
600  );
601  }
602  }
603 
604  if ( $this->fld_content ) {
605  $text = null;
606 
607  if ( $this->expandTemplates && !$this->parseContent ) {
608  if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
610  '@phan-var WikitextContent $content';
611  $text = $content->getText();
612 
613  $text = $this->parserFactory->create()->preprocess(
614  $text,
615  $title,
616  ParserOptions::newFromContext( $this->getContext() )
617  );
618  } else {
619  $this->addWarning( [
620  'apierror-templateexpansion-notwikitext',
621  wfEscapeWikiText( $title->getPrefixedText() ),
622  $content->getModel()
623  ] );
624  $vals['badcontentformat'] = true;
625  $text = false;
626  }
627  }
628  if ( $this->parseContent ) {
629  $po = $this->contentRenderer->getParserOutput(
630  $content,
631  $title,
632  $revision->getId(),
633  ParserOptions::newFromContext( $this->getContext() )
634  );
635  $text = $po->getText();
636  }
637 
638  if ( $text === null ) {
639  $format = $this->contentFormat ?: $content->getDefaultFormat();
640  $model = $content->getModel();
641 
642  if ( !$content->isSupportedFormat( $format ) ) {
643  $name = wfEscapeWikiText( $title->getPrefixedText() );
644  $this->addWarning( [ 'apierror-badformat', $this->contentFormat, $model, $name ] );
645  $vals['badcontentformat'] = true;
646  $text = false;
647  } else {
648  $text = $content->serialize( $format );
649  // always include format and model.
650  // Format is needed to deserialize, model is needed to interpret.
651  $vals['contentformat'] = $format;
652  $vals['contentmodel'] = $model;
653  }
654  }
655 
656  if ( $text !== false ) {
657  ApiResult::setContentValue( $vals, 'content', $text );
658  }
659  }
660 
661  if ( $content && ( $this->diffto !== null || $this->difftotext !== null ) ) {
662  static $n = 0; // Number of uncached diffs we've had
663 
664  if ( $n < $this->getConfig()->get( MainConfigNames::APIMaxUncachedDiffs ) ) {
665  $vals['diff'] = [];
666  $context = new DerivativeContext( $this->getContext() );
667  $context->setTitle( $title );
668  $handler = $content->getContentHandler();
669 
670  if ( $this->difftotext !== null ) {
671  $model = $title->getContentModel();
672 
673  if ( $this->contentFormat
674  && !$this->contentHandlerFactory->getContentHandler( $model )
675  ->isSupportedFormat( $this->contentFormat )
676  ) {
677  $name = wfEscapeWikiText( $title->getPrefixedText() );
678  $this->addWarning( [ 'apierror-badformat', $this->contentFormat, $model, $name ] );
679  $vals['diff']['badcontentformat'] = true;
680  $engine = null;
681  } else {
682  $difftocontent = $this->contentHandlerFactory->getContentHandler( $model )
683  ->unserializeContent( $this->difftotext, $this->contentFormat );
684 
685  if ( $this->difftotextpst ) {
686  $popts = ParserOptions::newFromContext( $this->getContext() );
687  $difftocontent = $this->contentTransformer->preSaveTransform(
688  $difftocontent,
689  $title,
690  $this->getUser(),
691  $popts
692  );
693  }
694 
695  $engine = $handler->createDifferenceEngine( $context );
696  $engine->setContent( $content, $difftocontent );
697  }
698  } else {
699  $engine = $handler->createDifferenceEngine( $context, $revision->getId(), $this->diffto );
700  $vals['diff']['from'] = $engine->getOldid();
701  $vals['diff']['to'] = $engine->getNewid();
702  }
703  if ( $engine ) {
704  $difftext = $engine->getDiffBody();
705  ApiResult::setContentValue( $vals['diff'], 'body', $difftext );
706  if ( !$engine->wasCacheHit() ) {
707  $n++;
708  }
709  }
710  } else {
711  $vals['diff']['notcached'] = true;
712  }
713  }
714 
715  return $vals;
716  }
717 
724  public function getCacheMode( $params ) {
725  if ( $this->userCanSeeRevDel() ) {
726  return 'private';
727  }
728 
729  return 'public';
730  }
731 
737  public function getAllowedParams() {
738  $slotRoles = $this->slotRoleRegistry->getKnownRoles();
739  sort( $slotRoles, SORT_STRING );
740 
741  return [
742  'prop' => [
743  ParamValidator::PARAM_ISMULTI => true,
744  ParamValidator::PARAM_DEFAULT => 'ids|timestamp|flags|comment|user',
745  ParamValidator::PARAM_TYPE => [
746  'ids',
747  'flags',
748  'timestamp',
749  'user',
750  'userid',
751  'size',
752  'slotsize',
753  'sha1',
754  'slotsha1',
755  'contentmodel',
756  'comment',
757  'parsedcomment',
758  'content',
759  'tags',
760  'roles',
761  'parsetree',
762  ],
763  ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-prop',
765  'ids' => 'apihelp-query+revisions+base-paramvalue-prop-ids',
766  'flags' => 'apihelp-query+revisions+base-paramvalue-prop-flags',
767  'timestamp' => 'apihelp-query+revisions+base-paramvalue-prop-timestamp',
768  'user' => 'apihelp-query+revisions+base-paramvalue-prop-user',
769  'userid' => 'apihelp-query+revisions+base-paramvalue-prop-userid',
770  'size' => 'apihelp-query+revisions+base-paramvalue-prop-size',
771  'slotsize' => 'apihelp-query+revisions+base-paramvalue-prop-slotsize',
772  'sha1' => 'apihelp-query+revisions+base-paramvalue-prop-sha1',
773  'slotsha1' => 'apihelp-query+revisions+base-paramvalue-prop-slotsha1',
774  'contentmodel' => 'apihelp-query+revisions+base-paramvalue-prop-contentmodel',
775  'comment' => 'apihelp-query+revisions+base-paramvalue-prop-comment',
776  'parsedcomment' => 'apihelp-query+revisions+base-paramvalue-prop-parsedcomment',
777  'content' => 'apihelp-query+revisions+base-paramvalue-prop-content',
778  'tags' => 'apihelp-query+revisions+base-paramvalue-prop-tags',
779  'roles' => 'apihelp-query+revisions+base-paramvalue-prop-roles',
780  'parsetree' => [ 'apihelp-query+revisions+base-paramvalue-prop-parsetree',
782  ],
783  EnumDef::PARAM_DEPRECATED_VALUES => [
784  'parsetree' => true,
785  ],
786  ],
787  'slots' => [
788  ParamValidator::PARAM_TYPE => $slotRoles,
789  ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-slots',
790  ParamValidator::PARAM_ISMULTI => true,
791  ParamValidator::PARAM_ALL => true,
792  ],
793  'limit' => [
794  ParamValidator::PARAM_TYPE => 'limit',
795  IntegerDef::PARAM_MIN => 1,
796  IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
797  IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2,
798  ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-limit',
799  ],
800  'expandtemplates' => [
801  ParamValidator::PARAM_DEFAULT => false,
802  ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-expandtemplates',
803  ParamValidator::PARAM_DEPRECATED => true,
804  ],
805  'generatexml' => [
806  ParamValidator::PARAM_DEFAULT => false,
807  ParamValidator::PARAM_DEPRECATED => true,
808  ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-generatexml',
809  ],
810  'parse' => [
811  ParamValidator::PARAM_DEFAULT => false,
812  ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-parse',
813  ParamValidator::PARAM_DEPRECATED => true,
814  ],
815  'section' => [
816  ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-section',
817  ],
818  'diffto' => [
819  ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-diffto',
820  ParamValidator::PARAM_DEPRECATED => true,
821  ],
822  'difftotext' => [
823  ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-difftotext',
824  ParamValidator::PARAM_DEPRECATED => true,
825  ],
826  'difftotextpst' => [
827  ParamValidator::PARAM_DEFAULT => false,
828  ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-difftotextpst',
829  ParamValidator::PARAM_DEPRECATED => true,
830  ],
831  'contentformat' => [
832  ParamValidator::PARAM_TYPE => $this->contentHandlerFactory->getAllContentFormats(),
833  ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-contentformat',
834  ParamValidator::PARAM_DEPRECATED => true,
835  ],
836  ];
837  }
838 }
getUser()
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:210
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
getContext()
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition: ApiBase.php:506
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:1458
getMain()
Get the main module.
Definition: ApiBase.php:514
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition: ApiBase.php:196
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
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
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition: ApiBase.php:1376
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
This class contains a list of pages that the client has requested.
Definition: ApiPageSet.php:50
getParent()
Get the parent of this module.Stability: stableto override 1.25 ApiBase|null
encodeParamName( $paramName)
Overrides ApiBase to prepend 'g' to every generator parameter.
A base class for functions common to producing a list of revisions.
executeGenerator( $resultPageSet)
extractAllSlotInfo(RevisionRecord $revision, $revDel)
Extracts information about all relevant slots.
ContentRenderer $contentRenderer
parseParameters( $params)
Parse the parameters into the various instance fields.
IContentHandlerFactory $contentHandlerFactory
extractDeprecatedContent(Content $content, RevisionRecord $revision)
Format a Content using deprecated options.
ContentTransformer $contentTransformer
SlotRoleRegistry $slotRoleRegistry
extractSlotInfo(SlotRecord $slot, $revDel, &$content=null)
Extract information from the SlotRecord.
__construct(ApiQuery $queryModule, $moduleName, $paramPrefix='', RevisionStore $revisionStore=null, IContentHandlerFactory $contentHandlerFactory=null, ParserFactory $parserFactory=null, SlotRoleRegistry $slotRoleRegistry=null, ContentRenderer $contentRenderer=null, ContentTransformer $contentTransformer=null)
run(ApiPageSet $resultPageSet=null)
checkRevDel(RevisionRecord $revision, $field)
Test revision deletion status.
extractRevisionInfo(RevisionRecord $revision, $row)
Extract information from the RevisionRecord.
This is the main query class.
Definition: ApiQuery.php:41
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
Definition: ApiResult.php:716
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:604
const META_KVP_MERGE
Key for the metadata item that indicates that the KVP key should be added into an assoc value,...
Definition: ApiResult.php:129
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
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
An IContextSource implementation which will inherit context from another source but allow individual ...
static formatComment( $comment, $title=null, $local=false, $wikiId=null)
This function is called by all recent changes variants, by the page history, and by the user contribu...
Definition: Linker.php:1402
PSR-3 logger instance factory.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
Exception representing a failure to look up a revision.
Page revision base class.
getUser( $audience=self::FOR_PUBLIC, Authority $performer=null)
Fetch revision's author's user identity, if it's available to the specified audience.
getSize()
Returns the nominal size of this revision, in bogo-bytes.
getSlotRoles()
Returns the slot names (roles) of all slots present in this revision.
getParentId( $wikiId=self::LOCAL)
Get parent revision ID (the original previous page revision).
getTimestamp()
MCR migration note: this replaced Revision::getTimestamp.
getComment( $audience=self::FOR_PUBLIC, Authority $performer=null)
Fetch revision comment, if it's available to the specified audience.
getSlot( $role, $audience=self::FOR_PUBLIC, Authority $performer=null)
Returns meta-data for the given slot.
getSha1()
Returns the base36 sha1 of this revision.
isMinor()
MCR migration note: this replaced Revision::isMinor.
getPageAsLinkTarget()
Returns the title of the page this revision is associated with as a LinkTarget object.
userCan( $field, Authority $performer)
Determine if the give authority is allowed to view a particular field of this revision,...
isDeleted( $field)
MCR migration note: this replaced Revision::isDeleted.
getId( $wikiId=self::LOCAL)
Get revision ID.
Service for looking up page revisions.
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40
getContent()
Returns the Content of the given slot.
Definition: SlotRecord.php:317
getSha1()
Returns the content size.
Definition: SlotRecord.php:556
getSize()
Returns the content size.
Definition: SlotRecord.php:540
getModel()
Returns the content model.
Definition: SlotRecord.php:584
A registry service for SlotRoleHandlers, used to define which slot roles are available on which page.
static newFromContext(IContextSource $context)
Get a ParserOptions object from a IContextSource object.
static newFromLinkTarget(LinkTarget $linkTarget, $forceClone='')
Returns a Title given a LinkTarget.
Definition: Title.php:282
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
Content object for wiki text pages.
Base interface for content objects.
Definition: Content.php:35
$content
Definition: router.php:76