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