MediaWiki master
ApiQueryRevisionsBase.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\Api;
24
47use stdClass;
51
60
61 // region Constants for internal use. Don't use externally.
64 // Bits to indicate the results of the revdel permission check on a revision,
65 // see self::checkRevDel()
66 private const IS_DELETED = 1; // Whether the field is revision-deleted
67 private const CANNOT_VIEW = 2; // Whether the user cannot view the field due to revdel
68
69 private const LIMIT_PARSE = 1;
70
71 // endregion
72
74 protected $limit;
76 protected $diffto;
78 protected $difftotext;
80 protected $difftotextpst;
84 protected $generateXML;
86 protected $section;
88 protected $parseContent;
90 protected $fetchContent;
92 protected $contentFormat;
93 protected bool $setParsedLimit = true;
94 protected ?array $slotRoles = null;
98 protected $needSlots;
99
100 protected bool $fld_ids = false;
101 protected bool $fld_flags = false;
102 protected bool $fld_timestamp = false;
103 protected bool $fld_size = false;
104 protected bool $fld_slotsize = false;
105 protected bool $fld_sha1 = false;
106 protected bool $fld_slotsha1 = false;
107 protected bool $fld_comment = false;
108 protected bool $fld_parsedcomment = false;
109 protected bool $fld_user = false;
110 protected bool $fld_userid = false;
111 protected bool $fld_content = false;
112 protected bool $fld_tags = false;
113 protected bool $fld_contentmodel = false;
114 protected bool $fld_roles = false;
115 protected bool $fld_parsetree = false;
116
121 private $numUncachedDiffs = 0;
122
123 private RevisionStore $revisionStore;
124 private IContentHandlerFactory $contentHandlerFactory;
125 private ParserFactory $parserFactory;
126 private SlotRoleRegistry $slotRoleRegistry;
127 private ContentRenderer $contentRenderer;
128 private ContentTransformer $contentTransformer;
129 private CommentFormatter $commentFormatter;
130 private TempUserCreator $tempUserCreator;
131 private UserFactory $userFactory;
132 private UserNameUtils $userNameUtils;
133
138 public function __construct(
139 ApiQuery $queryModule,
140 string $moduleName,
141 string $paramPrefix = '',
142 ?RevisionStore $revisionStore = null,
143 ?IContentHandlerFactory $contentHandlerFactory = null,
144 ?ParserFactory $parserFactory = null,
145 ?SlotRoleRegistry $slotRoleRegistry = null,
146 ?ContentRenderer $contentRenderer = null,
147 ?ContentTransformer $contentTransformer = null,
148 ?CommentFormatter $commentFormatter = null,
149 ?TempUserCreator $tempUserCreator = null,
150 ?UserFactory $userFactory = null,
151 ?UserNameUtils $userNameUtils = null
152 ) {
153 parent::__construct( $queryModule, $moduleName, $paramPrefix );
154 // This class is part of the stable interface and
155 // therefor fallback to global state, if services are not provided
156 $services = MediaWikiServices::getInstance();
157 $this->revisionStore = $revisionStore ?? $services->getRevisionStore();
158 $this->contentHandlerFactory = $contentHandlerFactory ?? $services->getContentHandlerFactory();
159 $this->parserFactory = $parserFactory ?? $services->getParserFactory();
160 $this->slotRoleRegistry = $slotRoleRegistry ?? $services->getSlotRoleRegistry();
161 $this->contentRenderer = $contentRenderer ?? $services->getContentRenderer();
162 $this->contentTransformer = $contentTransformer ?? $services->getContentTransformer();
163 $this->commentFormatter = $commentFormatter ?? $services->getCommentFormatter();
164 $this->tempUserCreator = $tempUserCreator ?? $services->getTempUserCreator();
165 $this->userFactory = $userFactory ?? $services->getUserFactory();
166 $this->userNameUtils = $userNameUtils ?? $services->getUserNameUtils();
167 }
168
169 public function execute() {
170 $this->run();
171 }
172
173 public function executeGenerator( $resultPageSet ) {
174 $this->run( $resultPageSet );
175 }
176
181 abstract protected function run( ?ApiPageSet $resultPageSet = null );
182
188 protected function parseParameters( $params ) {
189 $prop = array_fill_keys( $params['prop'], true );
190
191 $this->fld_ids = isset( $prop['ids'] );
192 $this->fld_flags = isset( $prop['flags'] );
193 $this->fld_timestamp = isset( $prop['timestamp'] );
194 $this->fld_comment = isset( $prop['comment'] );
195 $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
196 $this->fld_size = isset( $prop['size'] );
197 $this->fld_slotsize = isset( $prop['slotsize'] );
198 $this->fld_sha1 = isset( $prop['sha1'] );
199 $this->fld_slotsha1 = isset( $prop['slotsha1'] );
200 $this->fld_content = isset( $prop['content'] );
201 $this->fld_contentmodel = isset( $prop['contentmodel'] );
202 $this->fld_userid = isset( $prop['userid'] );
203 $this->fld_user = isset( $prop['user'] );
204 $this->fld_tags = isset( $prop['tags'] );
205 $this->fld_roles = isset( $prop['roles'] );
206 $this->fld_parsetree = isset( $prop['parsetree'] );
207
208 $this->slotRoles = $params['slots'];
209
210 if ( $this->slotRoles !== null ) {
211 if ( $this->fld_parsetree ) {
212 $this->dieWithError( [
213 'apierror-invalidparammix-cannotusewith',
214 $this->encodeParamName( 'prop=parsetree' ),
215 $this->encodeParamName( 'slots' ),
216 ], 'invalidparammix' );
217 }
218 foreach ( [
219 'expandtemplates', 'generatexml', 'parse', 'diffto', 'difftotext', 'difftotextpst',
220 'contentformat'
221 ] as $p ) {
222 if ( $params[$p] !== null && $params[$p] !== false ) {
223 $this->dieWithError( [
224 'apierror-invalidparammix-cannotusewith',
225 $this->encodeParamName( $p ),
226 $this->encodeParamName( 'slots' ),
227 ], 'invalidparammix' );
228 }
229 }
230 $this->slotContentFormats = [];
231 foreach ( $this->slotRoles as $slotRole ) {
232 if ( isset( $params['contentformat-' . $slotRole] ) ) {
233 $this->slotContentFormats[$slotRole] = $params['contentformat-' . $slotRole];
234 }
235 }
236 }
237
238 if ( !empty( $params['contentformat'] ) ) {
239 $this->contentFormat = $params['contentformat'];
240 }
241
242 $this->limit = $params['limit'];
243
244 if ( $params['difftotext'] !== null ) {
245 $this->difftotext = $params['difftotext'];
246 $this->difftotextpst = $params['difftotextpst'];
247 } elseif ( $params['diffto'] !== null ) {
248 if ( $params['diffto'] == 'cur' ) {
249 $params['diffto'] = 0;
250 }
251 if ( ( !ctype_digit( $params['diffto'] ) || $params['diffto'] < 0 )
252 && $params['diffto'] != 'prev' && $params['diffto'] != 'next'
253 ) {
254 $p = $this->getModulePrefix();
255 $this->dieWithError( [ 'apierror-baddiffto', $p ], 'diffto' );
256 }
257 // Check whether the revision exists and is readable,
258 // DifferenceEngine returns a rather ambiguous empty
259 // string if that's not the case
260 if ( is_numeric( $params['diffto'] ) && $params['diffto'] != 0 ) {
261 $difftoRev = $this->revisionStore->getRevisionById( $params['diffto'] );
262 if ( !$difftoRev ) {
263 $this->dieWithError( [ 'apierror-nosuchrevid', $params['diffto'] ] );
264 }
265 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable T240141
266 $revDel = $this->checkRevDel( $difftoRev, RevisionRecord::DELETED_TEXT );
267 if ( $revDel & self::CANNOT_VIEW ) {
268 $this->addWarning( [ 'apiwarn-difftohidden', $difftoRev->getId() ] );
269 $params['diffto'] = null;
270 }
271 }
272 $this->diffto = $params['diffto'];
273 }
274
275 $this->fetchContent = $this->fld_content || $this->diffto !== null
276 || $this->difftotext !== null || $this->fld_parsetree;
277
278 $smallLimit = false;
279 if ( $this->fetchContent ) {
280 $smallLimit = true;
281 $this->expandTemplates = $params['expandtemplates'];
282 $this->generateXML = $params['generatexml'];
283 $this->parseContent = $params['parse'];
284 if ( $this->parseContent ) {
285 // Must manually initialize unset limit
286 $this->limit ??= self::LIMIT_PARSE;
287 }
288 $this->section = $params['section'] ?? false;
289 }
290
291 $userMax = $this->parseContent ? self::LIMIT_PARSE :
292 ( $smallLimit ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 );
293 $botMax = $this->parseContent ? self::LIMIT_PARSE :
294 ( $smallLimit ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 );
295 if ( $this->limit == 'max' ) {
296 $this->limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
297 if ( $this->setParsedLimit ) {
298 $this->getResult()->addParsedLimit( $this->getModuleName(), $this->limit );
299 }
300 }
301
302 $this->limit = $this->getMain()->getParamValidator()->validateValue(
303 $this, 'limit', $this->limit ?? 10, [
304 ParamValidator::PARAM_TYPE => 'limit',
305 IntegerDef::PARAM_MIN => 1,
306 IntegerDef::PARAM_MAX => $userMax,
307 IntegerDef::PARAM_MAX2 => $botMax,
308 IntegerDef::PARAM_IGNORE_RANGE => true,
309 ]
310 );
311
312 $this->needSlots = $this->fetchContent || $this->fld_contentmodel ||
313 $this->fld_slotsize || $this->fld_slotsha1;
314 if ( $this->needSlots && $this->slotRoles === null ) {
315 $encParam = $this->encodeParamName( 'slots' );
316 $name = $this->getModuleName();
317 $parent = $this->getParent();
318 $parentParam = $parent->encodeParamName( $parent->getModuleManager()->getModuleGroup( $name ) );
319 $this->addDeprecation(
320 [ 'apiwarn-deprecation-missingparam', $encParam ],
321 "action=query&{$parentParam}={$name}&!{$encParam}"
322 );
323 }
324 }
325
333 private function checkRevDel( RevisionRecord $revision, $field ) {
334 $ret = $revision->isDeleted( $field ) ? self::IS_DELETED : 0;
335 if ( $ret ) {
336 $canSee = $revision->userCan( $field, $this->getAuthority() );
337 $ret |= ( $canSee ? 0 : self::CANNOT_VIEW );
338 }
339 return $ret;
340 }
341
350 protected function extractRevisionInfo( RevisionRecord $revision, $row ) {
351 $vals = [];
352 $anyHidden = false;
353
354 if ( $this->fld_ids ) {
355 $vals['revid'] = (int)$revision->getId();
356 if ( $revision->getParentId() !== null ) {
357 $vals['parentid'] = (int)$revision->getParentId();
358 }
359 }
360
361 if ( $this->fld_flags ) {
362 $vals['minor'] = $revision->isMinor();
363 }
364
365 if ( $this->fld_user || $this->fld_userid ) {
366 $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_USER );
367 if ( $revDel & self::IS_DELETED ) {
368 $vals['userhidden'] = true;
369 $anyHidden = true;
370 }
371 if ( !( $revDel & self::CANNOT_VIEW ) ) {
372 $u = $revision->getUser( RevisionRecord::RAW );
373 if ( $this->fld_user ) {
374 $vals['user'] = $u->getName();
375 }
376 if ( $this->userNameUtils->isTemp( $u->getName() ) ) {
377 $vals['temp'] = true;
378 }
379 if ( !$u->isRegistered() ) {
380 $vals['anon'] = true;
381 }
382
383 if ( $this->fld_userid ) {
384 $vals['userid'] = $u->getId();
385 }
386 }
387 }
388
389 if ( $this->fld_timestamp ) {
390 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $revision->getTimestamp() );
391 }
392
393 if ( $this->fld_size ) {
394 try {
395 $vals['size'] = (int)$revision->getSize();
396 } catch ( RevisionAccessException $e ) {
397 // Back compat: If there's no size, return 0.
398 // @todo: Gergő says to mention T198099 as a "todo" here.
399 $vals['size'] = 0;
400 }
401 }
402
403 if ( $this->fld_sha1 ) {
404 $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
405 if ( $revDel & self::IS_DELETED ) {
406 $vals['sha1hidden'] = true;
407 $anyHidden = true;
408 }
409 if ( !( $revDel & self::CANNOT_VIEW ) ) {
410 try {
411 $vals['sha1'] = \Wikimedia\base_convert( $revision->getSha1(), 36, 16, 40 );
412 } catch ( RevisionAccessException $e ) {
413 // Back compat: If there's no sha1, return empty string.
414 // @todo: Gergő says to mention T198099 as a "todo" here.
415 $vals['sha1'] = '';
416 }
417 }
418 }
419
420 try {
421 if ( $this->fld_roles ) {
422 $vals['roles'] = $revision->getSlotRoles();
423 }
424
425 if ( $this->needSlots ) {
426 $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
427 if ( ( $this->fld_slotsha1 || $this->fetchContent ) && ( $revDel & self::IS_DELETED ) ) {
428 $anyHidden = true;
429 }
430 $vals = array_merge( $vals, $this->extractAllSlotInfo( $revision, $revDel ) );
431 }
432 } catch ( RevisionAccessException $ex ) {
433 // This is here so T212428 doesn't spam the log.
434 // TODO: find out why T212428 happens in the first place!
435 $vals['slotsmissing'] = true;
436
437 LoggerFactory::getInstance( 'api-warning' )->error(
438 'Failed to access revision slots',
439 [ 'revision' => $revision->getId(), 'exception' => $ex, ]
440 );
441 }
442
443 if ( $this->fld_comment || $this->fld_parsedcomment ) {
444 $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_COMMENT );
445 if ( $revDel & self::IS_DELETED ) {
446 $vals['commenthidden'] = true;
447 $anyHidden = true;
448 }
449 if ( !( $revDel & self::CANNOT_VIEW ) ) {
450 $comment = $revision->getComment( RevisionRecord::RAW );
451 $comment = $comment->text ?? '';
452
453 if ( $this->fld_comment ) {
454 $vals['comment'] = $comment;
455 }
456
457 if ( $this->fld_parsedcomment ) {
458 $vals['parsedcomment'] = $this->commentFormatter->format(
459 $comment, Title::newFromLinkTarget( $revision->getPageAsLinkTarget() )
460 );
461 }
462 }
463 }
464
465 if ( $this->fld_tags ) {
466 if ( $row->ts_tags ) {
467 $tags = explode( ',', $row->ts_tags );
468 ApiResult::setIndexedTagName( $tags, 'tag' );
469 $vals['tags'] = $tags;
470 } else {
471 $vals['tags'] = [];
472 }
473 }
474
475 if ( $anyHidden && $revision->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
476 $vals['suppressed'] = true;
477 }
478
479 return $vals;
480 }
481
491 private function extractAllSlotInfo( RevisionRecord $revision, $revDel ): array {
492 $vals = [];
493
494 if ( $this->slotRoles === null ) {
495 try {
496 $slot = $revision->getSlot( SlotRecord::MAIN, RevisionRecord::RAW );
497 } catch ( RevisionAccessException $e ) {
498 // Back compat: If there's no slot, there's no content, so set 'textmissing'
499 // @todo: Gergő says to mention T198099 as a "todo" here.
500 $vals['textmissing'] = true;
501 $slot = null;
502 }
503
504 if ( $slot ) {
505 $content = null;
506 $vals += $this->extractSlotInfo( $slot, $revDel, $content );
507 if ( !empty( $vals['nosuchsection'] ) ) {
508 $this->dieWithError(
509 [
510 'apierror-nosuchsection-what',
511 wfEscapeWikiText( $this->section ),
512 $this->msg( 'revid', $revision->getId() )
513 ],
514 'nosuchsection'
515 );
516 }
517 if ( $content ) {
518 $vals += $this->extractDeprecatedContent( $content, $revision );
519 }
520 }
521 } else {
522 $roles = array_intersect( $this->slotRoles, $revision->getSlotRoles() );
523 $vals['slots'] = [
525 ];
526 foreach ( $roles as $role ) {
527 try {
528 $slot = $revision->getSlot( $role, RevisionRecord::RAW );
529 } catch ( RevisionAccessException $e ) {
530 // Don't error out here so the client can still process other slots/revisions.
531 // @todo: Gergő says to mention T198099 as a "todo" here.
532 $vals['slots'][$role]['missing'] = true;
533 continue;
534 }
535 $content = null;
536 $vals['slots'][$role] = $this->extractSlotInfo( $slot, $revDel, $content );
537 // @todo Move this into extractSlotInfo() (and remove its $content parameter)
538 // when extractDeprecatedContent() is no more.
539 if ( $content ) {
541 $model = $content->getModel();
542 $format = $this->slotContentFormats[$role] ?? $content->getDefaultFormat();
543 if ( !$content->isSupportedFormat( $format ) ) {
544 $this->addWarning( [
545 'apierror-badformat',
546 $format,
547 $model,
548 $this->msg( 'revid', $revision->getId() )
549 ] );
550 $vals['slots'][$role]['badcontentformat'] = true;
551 } else {
552 $vals['slots'][$role]['contentmodel'] = $model;
553 $vals['slots'][$role]['contentformat'] = $format;
555 $vals['slots'][$role],
556 'content',
557 $content->serialize( $format )
558 );
559 }
560 }
561 }
562 ApiResult::setArrayType( $vals['slots'], 'kvp', 'role' );
563 ApiResult::setIndexedTagName( $vals['slots'], 'slot' );
564 }
565 return $vals;
566 }
567
577 private function extractSlotInfo( SlotRecord $slot, $revDel, &$content = null ) {
578 $vals = [];
579 ApiResult::setArrayType( $vals, 'assoc' );
580
581 if ( $this->fld_slotsize ) {
582 $vals['size'] = (int)$slot->getSize();
583 }
584
585 if ( $this->fld_slotsha1 ) {
586 if ( $revDel & self::IS_DELETED ) {
587 $vals['sha1hidden'] = true;
588 }
589 if ( !( $revDel & self::CANNOT_VIEW ) ) {
590 if ( $slot->getSha1() != '' ) {
591 $vals['sha1'] = \Wikimedia\base_convert( $slot->getSha1(), 36, 16, 40 );
592 } else {
593 $vals['sha1'] = '';
594 }
595 }
596 }
597
598 if ( $this->fld_contentmodel ) {
599 $vals['contentmodel'] = $slot->getModel();
600 }
601
602 $content = null;
603 if ( $this->fetchContent ) {
604 if ( $revDel & self::IS_DELETED ) {
605 $vals['texthidden'] = true;
606 }
607 if ( !( $revDel & self::CANNOT_VIEW ) ) {
608 try {
609 $content = $slot->getContent();
610 } catch ( RevisionAccessException $e ) {
611 // @todo: Gergő says to mention T198099 as a "todo" here.
612 $vals['textmissing'] = true;
613 }
614 // Expand templates after getting section content because
615 // template-added sections don't count and Parser::preprocess()
616 // will have less input
617 if ( $content && $this->section !== false ) {
618 $content = $content->getSection( $this->section );
619 if ( !$content ) {
620 $vals['nosuchsection'] = true;
621 }
622 }
623 }
624 }
625
626 return $vals;
627 }
628
635 private function extractDeprecatedContent( Content $content, RevisionRecord $revision ) {
636 $vals = [];
637 $title = Title::newFromLinkTarget( $revision->getPageAsLinkTarget() );
638
639 if ( $this->fld_parsetree || ( $this->fld_content && $this->generateXML ) ) {
640 if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
642 '@phan-var WikitextContent $content';
643 $t = $content->getText(); # note: don't set $text
644
645 $parser = $this->parserFactory->create();
646 $parser->startExternalParse(
647 $title,
648 ParserOptions::newFromContext( $this->getContext() ),
649 Parser::OT_PREPROCESS
650 );
651 $dom = $parser->preprocessToDom( $t );
652 // @phan-suppress-next-line PhanUndeclaredMethodInCallable
653 if ( is_callable( [ $dom, 'saveXML' ] ) ) {
654 // @phan-suppress-next-line PhanUndeclaredMethod
655 $xml = $dom->saveXML();
656 } else {
657 // @phan-suppress-next-line PhanUndeclaredMethod
658 $xml = $dom->__toString();
659 }
660 $vals['parsetree'] = $xml;
661 } else {
662 $vals['badcontentformatforparsetree'] = true;
663 $this->addWarning(
664 [
665 'apierror-parsetree-notwikitext-title',
666 wfEscapeWikiText( $title->getPrefixedText() ),
667 $content->getModel()
668 ],
669 'parsetree-notwikitext'
670 );
671 }
672 }
673
674 if ( $this->fld_content ) {
675 $text = null;
676
677 if ( $this->expandTemplates && !$this->parseContent ) {
678 if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
680 '@phan-var WikitextContent $content';
681 $text = $content->getText();
682
683 $text = $this->parserFactory->create()->preprocess(
684 $text,
685 $title,
686 ParserOptions::newFromContext( $this->getContext() )
687 );
688 } else {
689 $this->addWarning( [
690 'apierror-templateexpansion-notwikitext',
691 wfEscapeWikiText( $title->getPrefixedText() ),
692 $content->getModel()
693 ] );
694 $vals['badcontentformat'] = true;
695 $text = false;
696 }
697 }
698 if ( $this->parseContent ) {
699 $popts = ParserOptions::newFromContext( $this->getContext() );
700 $po = $this->contentRenderer->getParserOutput(
701 $content,
702 $title,
703 $revision,
704 $popts
705 );
706 // TODO T371004 move runOutputPipeline out of $parserOutput
707 $text = $po->runOutputPipeline( $popts, [] )->getContentHolderText();
708 }
709
710 if ( $text === null ) {
711 $format = $this->contentFormat ?: $content->getDefaultFormat();
712 $model = $content->getModel();
713
714 if ( !$content->isSupportedFormat( $format ) ) {
715 $name = wfEscapeWikiText( $title->getPrefixedText() );
716 $this->addWarning( [ 'apierror-badformat', $this->contentFormat, $model, $name ] );
717 $vals['badcontentformat'] = true;
718 $text = false;
719 } else {
720 $text = $content->serialize( $format );
721 // always include format and model.
722 // Format is needed to deserialize, model is needed to interpret.
723 $vals['contentformat'] = $format;
724 $vals['contentmodel'] = $model;
725 }
726 }
727
728 if ( $text !== false ) {
729 ApiResult::setContentValue( $vals, 'content', $text );
730 }
731 }
732
733 if ( $content && ( $this->diffto !== null || $this->difftotext !== null ) ) {
734 if ( $this->numUncachedDiffs < $this->getConfig()->get( MainConfigNames::APIMaxUncachedDiffs ) ) {
735 $vals['diff'] = [];
736 $context = new DerivativeContext( $this->getContext() );
737 $context->setTitle( $title );
738 $handler = $content->getContentHandler();
739
740 if ( $this->difftotext !== null ) {
741 $model = $title->getContentModel();
742
743 if ( $this->contentFormat
744 && !$this->contentHandlerFactory->getContentHandler( $model )
745 ->isSupportedFormat( $this->contentFormat )
746 ) {
747 $name = wfEscapeWikiText( $title->getPrefixedText() );
748 $this->addWarning( [ 'apierror-badformat', $this->contentFormat, $model, $name ] );
749 $vals['diff']['badcontentformat'] = true;
750 $engine = null;
751 } else {
752 $difftocontent = $this->contentHandlerFactory->getContentHandler( $model )
753 ->unserializeContent( $this->difftotext, $this->contentFormat );
754
755 if ( $this->difftotextpst ) {
756 $popts = ParserOptions::newFromContext( $this->getContext() );
757 $difftocontent = $this->contentTransformer->preSaveTransform(
758 $difftocontent,
759 $title,
760 $this->getUserForPreview(),
761 $popts
762 );
763 }
764
765 $engine = $handler->createDifferenceEngine( $context );
766 $engine->setContent( $content, $difftocontent );
767 }
768 } else {
769 $engine = $handler->createDifferenceEngine( $context, $revision->getId(), $this->diffto );
770 $vals['diff']['from'] = $engine->getOldid();
771 $vals['diff']['to'] = $engine->getNewid();
772 }
773 if ( $engine ) {
774 $difftext = $engine->getDiffBody();
775 ApiResult::setContentValue( $vals['diff'], 'body', $difftext );
776 if ( !$engine->wasCacheHit() ) {
777 $this->numUncachedDiffs++;
778 }
779 foreach ( $engine->getRevisionLoadErrors() as $msg ) {
780 $this->addWarning( $msg );
781 }
782 }
783 } else {
784 $vals['diff']['notcached'] = true;
785 }
786 }
787
788 return $vals;
789 }
790
791 private function getUserForPreview() {
792 $user = $this->getUser();
793 if ( $this->tempUserCreator->shouldAutoCreate( $user, 'edit' ) ) {
794 return $this->userFactory->newUnsavedTempUser(
795 $this->tempUserCreator->getStashedName( $this->getRequest()->getSession() )
796 );
797 }
798 return $user;
799 }
800
807 public function getCacheMode( $params ) {
808 if ( $this->userCanSeeRevDel() ) {
809 return 'private';
810 }
811
812 return 'public';
813 }
814
819 public function getAllowedParams() {
820 $slotRoles = $this->slotRoleRegistry->getKnownRoles();
821 sort( $slotRoles, SORT_STRING );
822 $smallLimit = $this->getMain()->canApiHighLimits() ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_SML1;
823
824 return [
825 'prop' => [
826 ParamValidator::PARAM_ISMULTI => true,
827 ParamValidator::PARAM_DEFAULT => 'ids|timestamp|flags|comment|user',
828 ParamValidator::PARAM_TYPE => [
829 'ids',
830 'flags',
831 'timestamp',
832 'user',
833 'userid',
834 'size',
835 'slotsize',
836 'sha1',
837 'slotsha1',
838 'contentmodel',
839 'comment',
840 'parsedcomment',
841 'content',
842 'tags',
843 'roles',
844 'parsetree',
845 ],
846 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-prop',
847 ApiBase::PARAM_HELP_MSG_PER_VALUE => [
848 'ids' => 'apihelp-query+revisions+base-paramvalue-prop-ids',
849 'flags' => 'apihelp-query+revisions+base-paramvalue-prop-flags',
850 'timestamp' => 'apihelp-query+revisions+base-paramvalue-prop-timestamp',
851 'user' => 'apihelp-query+revisions+base-paramvalue-prop-user',
852 'userid' => 'apihelp-query+revisions+base-paramvalue-prop-userid',
853 'size' => 'apihelp-query+revisions+base-paramvalue-prop-size',
854 'slotsize' => 'apihelp-query+revisions+base-paramvalue-prop-slotsize',
855 'sha1' => 'apihelp-query+revisions+base-paramvalue-prop-sha1',
856 'slotsha1' => 'apihelp-query+revisions+base-paramvalue-prop-slotsha1',
857 'contentmodel' => 'apihelp-query+revisions+base-paramvalue-prop-contentmodel',
858 'comment' => 'apihelp-query+revisions+base-paramvalue-prop-comment',
859 'parsedcomment' => 'apihelp-query+revisions+base-paramvalue-prop-parsedcomment',
860 'content' => [ 'apihelp-query+revisions+base-paramvalue-prop-content', $smallLimit ],
861 'tags' => 'apihelp-query+revisions+base-paramvalue-prop-tags',
862 'roles' => 'apihelp-query+revisions+base-paramvalue-prop-roles',
863 'parsetree' => [ 'apihelp-query+revisions+base-paramvalue-prop-parsetree',
864 CONTENT_MODEL_WIKITEXT, $smallLimit ],
865 ],
866 EnumDef::PARAM_DEPRECATED_VALUES => [
867 'parsetree' => true,
868 ],
869 ],
870 'slots' => [
871 ParamValidator::PARAM_TYPE => $slotRoles,
872 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-slots',
873 ParamValidator::PARAM_ISMULTI => true,
874 ParamValidator::PARAM_ALL => true,
875 ],
876 'contentformat-{slot}' => [
877 ApiBase::PARAM_TEMPLATE_VARS => [ 'slot' => 'slots' ],
878 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-contentformat-slot',
879 ParamValidator::PARAM_TYPE => $this->contentHandlerFactory->getAllContentFormats(),
880 ],
881 'limit' => [
882 ParamValidator::PARAM_TYPE => 'limit',
883 IntegerDef::PARAM_MIN => 1,
884 IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
885 IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2,
886 ApiBase::PARAM_HELP_MSG => [ 'apihelp-query+revisions+base-param-limit',
887 $smallLimit, self::LIMIT_PARSE ],
888 ],
889 'expandtemplates' => [
890 ParamValidator::PARAM_DEFAULT => false,
891 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-expandtemplates',
892 ParamValidator::PARAM_DEPRECATED => true,
893 ],
894 'generatexml' => [
895 ParamValidator::PARAM_DEFAULT => false,
896 ParamValidator::PARAM_DEPRECATED => true,
897 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-generatexml',
898 ],
899 'parse' => [
900 ParamValidator::PARAM_DEFAULT => false,
901 ApiBase::PARAM_HELP_MSG => [ 'apihelp-query+revisions+base-param-parse', self::LIMIT_PARSE ],
902 ParamValidator::PARAM_DEPRECATED => true,
903 ],
904 'section' => [
905 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-section',
906 ],
907 'diffto' => [
908 ApiBase::PARAM_HELP_MSG => [ 'apihelp-query+revisions+base-param-diffto', $smallLimit ],
909 ParamValidator::PARAM_DEPRECATED => true,
910 ],
911 'difftotext' => [
912 ApiBase::PARAM_HELP_MSG => [ 'apihelp-query+revisions+base-param-difftotext', $smallLimit ],
913 ParamValidator::PARAM_DEPRECATED => true,
914 ],
915 'difftotextpst' => [
916 ParamValidator::PARAM_DEFAULT => false,
917 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-difftotextpst',
918 ParamValidator::PARAM_DEPRECATED => true,
919 ],
920 'contentformat' => [
921 ParamValidator::PARAM_TYPE => $this->contentHandlerFactory->getAllContentFormats(),
922 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-contentformat',
923 ParamValidator::PARAM_DEPRECATED => true,
924 ],
925 ];
926 }
927}
928
930class_alias( ApiQueryRevisionsBase::class, 'ApiQueryRevisionsBase' );
const CONTENT_MODEL_WIKITEXT
Definition Defines.php:222
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
array $params
The job parameters.
run()
Run the job.
const LIMIT_SML1
Slow query, standard limit.
Definition ApiBase.php:253
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1577
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition ApiBase.php:580
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:571
getMain()
Get the main module.
Definition ApiBase.php:589
getResult()
Get the result object.
Definition ApiBase.php:710
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition ApiBase.php:1495
const LIMIT_SML2
Slow query, apihighlimits limit.
Definition ApiBase.php:255
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition ApiBase.php:251
addDeprecation( $msg, $feature, $data=[])
Add a deprecation warning for this module.
Definition ApiBase.php:1509
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:249
This class contains a list of pages that the client has requested.
getParent()
Get the parent of this module.to 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.
bool $fld_parsetree
The number of uncached diffs that had to be generated for this request.
string[] $slotContentFormats
The number of uncached diffs that had to be generated for this request.
bool $fld_slotsize
The number of uncached diffs that had to be generated for this request.
bool $fld_comment
The number of uncached diffs that had to be generated for this request.
bool $fld_tags
The number of uncached diffs that had to be generated for this request.
bool $fld_roles
The number of uncached diffs that had to be generated for this request.
bool $fld_sha1
The number of uncached diffs that had to be generated for this request.
bool $generateXML
The number of uncached diffs that had to be generated for this request.
bool $fld_slotsha1
The number of uncached diffs that had to be generated for this request.
string null $difftotext
The number of uncached diffs that had to be generated for this request.
array $slotRoles
The number of uncached diffs that had to be generated for this request.
string $contentFormat
The number of uncached diffs that had to be generated for this request.
run(?ApiPageSet $resultPageSet=null)
bool $fld_content
The number of uncached diffs that had to be generated for this request.
__construct(ApiQuery $queryModule, string $moduleName, string $paramPrefix='', ?RevisionStore $revisionStore=null, ?IContentHandlerFactory $contentHandlerFactory=null, ?ParserFactory $parserFactory=null, ?SlotRoleRegistry $slotRoleRegistry=null, ?ContentRenderer $contentRenderer=null, ?ContentTransformer $contentTransformer=null, ?CommentFormatter $commentFormatter=null, ?TempUserCreator $tempUserCreator=null, ?UserFactory $userFactory=null, ?UserNameUtils $userNameUtils=null)
execute()
The number of uncached diffs that had to be generated for this request.
bool $needSlots
The number of uncached diffs that had to be generated for this request.
bool $fld_user
The number of uncached diffs that had to be generated for this request.
extractRevisionInfo(RevisionRecord $revision, $row)
Extract information from the RevisionRecord.
parseParameters( $params)
Parse the parameters into the various instance fields.
int string null $diffto
The number of uncached diffs that had to be generated for this request.
bool $fld_contentmodel
The number of uncached diffs that had to be generated for this request.
bool $setParsedLimit
The number of uncached diffs that had to be generated for this request.
bool $fld_size
The number of uncached diffs that had to be generated for this request.
int string null $section
The number of uncached diffs that had to be generated for this request.
bool $fetchContent
The number of uncached diffs that had to be generated for this request.
int string null $expandTemplates
The number of uncached diffs that had to be generated for this request.
bool $fld_timestamp
The number of uncached diffs that had to be generated for this request.
int string null $limit
The number of uncached diffs that had to be generated for this request.
bool $fld_ids
The number of uncached diffs that had to be generated for this request.
bool $fld_parsedcomment
The number of uncached diffs that had to be generated for this request.
bool $parseContent
The number of uncached diffs that had to be generated for this request.
bool $fld_userid
The number of uncached diffs that had to be generated for this request.
bool $fld_flags
The number of uncached diffs that had to be generated for this request.
executeGenerator( $resultPageSet)
The number of uncached diffs that had to be generated for this request.
bool $difftotextpst
The number of uncached diffs that had to be generated for this request.
This is the main query class.
Definition ApiQuery.php:48
const META_KVP_MERGE
Key for the metadata item that indicates that the KVP key should be added into an assoc value,...
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
static setContentValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name and mark as META_CONTENT.
This is the main service interface for converting single-line comments from various DB comment fields...
Content object for wiki text pages.
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 ...
Create PSR-3 logger objects.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Set options of the Parser.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:145
Exception representing a failure to look up a revision.
Page revision base class.
getSlot( $role, $audience=self::FOR_PUBLIC, ?Authority $performer=null)
Returns meta-data for the given slot.
getSize()
Returns the nominal size of this revision, in bogo-bytes.
getUser( $audience=self::FOR_PUBLIC, ?Authority $performer=null)
Fetch revision's author's user identity, if it's available to the specified audience.
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).
getComment( $audience=self::FOR_PUBLIC, ?Authority $performer=null)
Fetch revision comment, if it's available to the specified audience.
getTimestamp()
MCR migration note: this replaced Revision::getTimestamp.
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.
A registry service for SlotRoleHandlers, used to define which slot roles are available on which page.
Represents a title within MediaWiki.
Definition Title.php:78
Service for temporary user creation.
Creates User objects.
UserNameUtils service.
Service for formatting and validating API parameters.
Type definition for enumeration types.
Definition EnumDef.php:32
Type definition for integer types.
Base interface for representing page content.
Definition Content.php:39