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