109 parent::__construct( $queryModule, $moduleName, $paramPrefix );
112 $services = MediaWikiServices::getInstance();
113 $this->revisionStore =
$revisionStore ?? $services->getRevisionStore();
115 $this->parserFactory =
$parserFactory ?? $services->getParserFactory();
117 $this->contentRenderer =
$contentRenderer ?? $services->getContentRenderer();
126 $this->
run( $resultPageSet );
141 $prop = array_fill_keys( $params[
'prop'],
true );
143 $this->fld_ids = isset( $prop[
'ids'] );
144 $this->fld_flags = isset( $prop[
'flags'] );
145 $this->fld_timestamp = isset( $prop[
'timestamp'] );
146 $this->fld_comment = isset( $prop[
'comment'] );
147 $this->fld_parsedcomment = isset( $prop[
'parsedcomment'] );
148 $this->fld_size = isset( $prop[
'size'] );
149 $this->fld_slotsize = isset( $prop[
'slotsize'] );
150 $this->fld_sha1 = isset( $prop[
'sha1'] );
151 $this->fld_slotsha1 = isset( $prop[
'slotsha1'] );
152 $this->fld_content = isset( $prop[
'content'] );
153 $this->fld_contentmodel = isset( $prop[
'contentmodel'] );
154 $this->fld_userid = isset( $prop[
'userid'] );
155 $this->fld_user = isset( $prop[
'user'] );
156 $this->fld_tags = isset( $prop[
'tags'] );
157 $this->fld_roles = isset( $prop[
'roles'] );
158 $this->fld_parsetree = isset( $prop[
'parsetree'] );
160 $this->slotRoles = $params[
'slots'];
162 if ( $this->slotRoles !==
null ) {
163 if ( $this->fld_parsetree ) {
165 'apierror-invalidparammix-cannotusewith',
168 ],
'invalidparammix' );
171 'expandtemplates',
'generatexml',
'parse',
'diffto',
'difftotext',
'difftotextpst',
174 if ( $params[$p] !==
null && $params[$p] !==
false ) {
176 'apierror-invalidparammix-cannotusewith',
179 ],
'invalidparammix' );
184 if ( !empty( $params[
'contentformat'] ) ) {
185 $this->contentFormat = $params[
'contentformat'];
188 $this->limit = $params[
'limit'];
190 if ( $params[
'difftotext'] !==
null ) {
191 $this->difftotext = $params[
'difftotext'];
192 $this->difftotextpst = $params[
'difftotextpst'];
193 } elseif ( $params[
'diffto'] !==
null ) {
194 if ( $params[
'diffto'] ==
'cur' ) {
195 $params[
'diffto'] = 0;
197 if ( ( !ctype_digit( $params[
'diffto'] ) || $params[
'diffto'] < 0 )
198 && $params[
'diffto'] !=
'prev' && $params[
'diffto'] !=
'next'
201 $this->
dieWithError( [
'apierror-baddiffto', $p ],
'diffto' );
206 if ( $params[
'diffto'] != 0 ) {
207 $difftoRev = $this->revisionStore->getRevisionById( $params[
'diffto'] );
209 $this->
dieWithError( [
'apierror-nosuchrevid', $params[
'diffto'] ] );
212 $revDel = $this->
checkRevDel( $difftoRev, RevisionRecord::DELETED_TEXT );
213 if ( $revDel & self::CANNOT_VIEW ) {
214 $this->
addWarning( [
'apiwarn-difftohidden', $difftoRev->getId() ] );
215 $params[
'diffto'] =
null;
218 $this->diffto = $params[
'diffto'];
221 $this->fetchContent = $this->fld_content || $this->diffto !==
null
225 if ( $this->fetchContent ) {
227 $this->expandTemplates = $params[
'expandtemplates'];
228 $this->generateXML = $params[
'generatexml'];
229 $this->parseContent = $params[
'parse'];
230 if ( $this->parseContent ) {
232 if ( $this->limit ===
null ) {
236 $this->section = $params[
'section'] ??
false;
241 if ( $this->limit ==
'max' ) {
242 $this->limit = $this->
getMain()->canApiHighLimits() ? $botMax : $userMax;
243 if ( $this->setParsedLimit ) {
248 $this->limit = $this->
getMain()->getParamValidator()->validateValue(
249 $this,
'limit', $this->limit ?? 10, [
250 ParamValidator::PARAM_TYPE =>
'limit',
251 IntegerDef::PARAM_MIN => 1,
252 IntegerDef::PARAM_MAX => $userMax,
253 IntegerDef::PARAM_MAX2 => $botMax,
254 IntegerDef::PARAM_IGNORE_RANGE =>
true,
258 $this->needSlots = $this->fetchContent || $this->fld_contentmodel ||
260 if ( $this->needSlots && $this->slotRoles ===
null ) {
264 $parentParam = $parent->encodeParamName( $parent->getModuleManager()->getModuleGroup( $name ) );
266 [
'apiwarn-deprecation-missingparam', $encParam ],
267 "action=query&{$parentParam}={$name}&!{$encParam}"
300 if ( $this->fld_ids ) {
301 $vals[
'revid'] = (int)$revision->
getId();
307 if ( $this->fld_flags ) {
308 $vals[
'minor'] = $revision->
isMinor();
311 if ( $this->fld_user || $this->fld_userid ) {
312 $revDel = $this->
checkRevDel( $revision, RevisionRecord::DELETED_USER );
313 if ( $revDel & self::IS_DELETED ) {
314 $vals[
'userhidden'] =
true;
317 if ( !( $revDel & self::CANNOT_VIEW ) ) {
318 $u = $revision->
getUser( RevisionRecord::RAW );
319 if ( $this->fld_user ) {
320 $vals[
'user'] = $u->getName();
322 if ( !$u->isRegistered() ) {
323 $vals[
'anon'] =
true;
326 if ( $this->fld_userid ) {
327 $vals[
'userid'] = $u->getId();
332 if ( $this->fld_timestamp ) {
336 if ( $this->fld_size ) {
338 $vals[
'size'] = (int)$revision->
getSize();
346 if ( $this->fld_sha1 ) {
347 $revDel = $this->
checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
348 if ( $revDel & self::IS_DELETED ) {
349 $vals[
'sha1hidden'] =
true;
352 if ( !( $revDel & self::CANNOT_VIEW ) ) {
354 $vals[
'sha1'] = Wikimedia\base_convert( $revision->
getSha1(), 36, 16, 40 );
364 if ( $this->fld_roles ) {
368 if ( $this->needSlots ) {
369 $revDel = $this->
checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
370 if ( ( $this->fld_slotsha1 || $this->fetchContent ) && ( $revDel & self::IS_DELETED ) ) {
378 $vals[
'slotsmissing'] =
true;
380 LoggerFactory::getInstance(
'api-warning' )->error(
381 'Failed to access revision slots',
382 [
'revision' => $revision->
getId(),
'exception' => $ex, ]
386 if ( $this->fld_comment || $this->fld_parsedcomment ) {
387 $revDel = $this->
checkRevDel( $revision, RevisionRecord::DELETED_COMMENT );
388 if ( $revDel & self::IS_DELETED ) {
389 $vals[
'commenthidden'] =
true;
392 if ( !( $revDel & self::CANNOT_VIEW ) ) {
393 $comment = $revision->
getComment( RevisionRecord::RAW );
394 $comment = $comment ? $comment->text :
'';
396 if ( $this->fld_comment ) {
397 $vals[
'comment'] = $comment;
400 if ( $this->fld_parsedcomment ) {
408 if ( $this->fld_tags ) {
409 if ( $row->ts_tags ) {
410 $tags = explode(
',', $row->ts_tags );
412 $vals[
'tags'] = $tags;
418 if ( $anyHidden && $revision->
isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
419 $vals[
'suppressed'] =
true;
437 if ( $this->slotRoles ===
null ) {
439 $slot = $revision->
getSlot( SlotRecord::MAIN, RevisionRecord::RAW );
443 $vals[
'textmissing'] =
true;
450 if ( !empty( $vals[
'nosuchsection'] ) ) {
453 'apierror-nosuchsection-what',
455 $this->
msg(
'revid', $revision->
getId() )
465 $roles = array_intersect( $this->slotRoles, $revision->
getSlotRoles() );
469 foreach ( $roles as $role ) {
471 $slot = $revision->
getSlot( $role, RevisionRecord::RAW );
475 $vals[
'slots'][$role][
'missing'] =
true;
484 $vals[
'slots'][$role][
'contentmodel'] =
$content->getModel();
485 $vals[
'slots'][$role][
'contentformat'] =
$content->getDefaultFormat();
487 $vals[
'slots'][$role],
512 if ( $this->fld_slotsize ) {
513 $vals[
'size'] = (int)$slot->
getSize();
516 if ( $this->fld_slotsha1 ) {
517 if ( $revDel & self::IS_DELETED ) {
518 $vals[
'sha1hidden'] =
true;
520 if ( !( $revDel & self::CANNOT_VIEW ) ) {
521 if ( $slot->
getSha1() !=
'' ) {
522 $vals[
'sha1'] = Wikimedia\base_convert( $slot->
getSha1(), 36, 16, 40 );
529 if ( $this->fld_contentmodel ) {
530 $vals[
'contentmodel'] = $slot->
getModel();
534 if ( $this->fetchContent ) {
535 if ( $revDel & self::IS_DELETED ) {
536 $vals[
'texthidden'] =
true;
538 if ( !( $revDel & self::CANNOT_VIEW ) ) {
543 $vals[
'textmissing'] =
true;
548 if (
$content && $this->section !==
false ) {
551 $vals[
'nosuchsection'] =
true;
570 if ( $this->fld_parsetree || ( $this->fld_content && $this->generateXML ) ) {
573 '@phan-var WikitextContent $content';
574 $t =
$content->getText(); # note: don
't set $text
576 $parser = $this->parserFactory->create();
577 $parser->startExternalParse(
579 ParserOptions::newFromContext( $this->getContext() ),
580 Parser::OT_PREPROCESS
582 $dom = $parser->preprocessToDom( $t );
583 if ( is_callable( [ $dom, 'saveXML
' ] ) ) {
584 // @phan-suppress-next-line PhanUndeclaredMethod
585 $xml = $dom->saveXML();
587 // @phan-suppress-next-line PhanUndeclaredMethod
588 $xml = $dom->__toString();
590 $vals['parsetree
'] = $xml;
592 $vals['badcontentformatforparsetree
'] = true;
595 'apierror-parsetree-notwikitext-title
',
596 wfEscapeWikiText( $title->getPrefixedText() ),
599 'parsetree-notwikitext
'
604 if ( $this->fld_content ) {
607 if ( $this->expandTemplates && !$this->parseContent ) {
608 if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
611 $text = $content->getText();
613 $text = $this->parserFactory->create()->preprocess(
616 ParserOptions::newFromContext( $this->getContext() )
620 'apierror-templateexpansion-notwikitext
',
621 wfEscapeWikiText( $title->getPrefixedText() ),
624 $vals['badcontentformat
'] = true;
628 if ( $this->parseContent ) {
629 $po = $this->contentRenderer->getParserOutput(
633 ParserOptions::newFromContext( $this->getContext() )
635 $text = $po->getText();
638 if ( $text === null ) {
639 $format = $this->contentFormat ?: $content->getDefaultFormat();
640 $model = $content->getModel();
642 if ( !$content->isSupportedFormat( $format ) ) {
643 $name = wfEscapeWikiText( $title->getPrefixedText() );
644 $this->addWarning( [ 'apierror-badformat
', $this->contentFormat, $model, $name ] );
645 $vals['badcontentformat
'] = true;
648 $text = $content->serialize( $format );
649 // always include format and model.
650 // Format is needed to deserialize, model is needed to interpret.
651 $vals['contentformat
'] = $format;
652 $vals['contentmodel
'] = $model;
656 if ( $text !== false ) {
657 ApiResult::setContentValue( $vals, 'content
', $text );
661 if ( $content && ( $this->diffto !== null || $this->difftotext !== null ) ) {
662 static $n = 0; // Number of uncached diffs we've had
664 if ( $n < $this->getConfig()->
get( MainConfigNames::APIMaxUncachedDiffs ) ) {
667 $context->setTitle(
$title );
668 $handler =
$content->getContentHandler();
670 if ( $this->difftotext !==
null ) {
671 $model =
$title->getContentModel();
673 if ( $this->contentFormat
674 && !$this->contentHandlerFactory->getContentHandler( $model )
675 ->isSupportedFormat( $this->contentFormat )
678 $this->addWarning( [
'apierror-badformat', $this->contentFormat, $model, $name ] );
679 $vals[
'diff'][
'badcontentformat'] =
true;
682 $difftocontent = $this->contentHandlerFactory->getContentHandler( $model )
683 ->unserializeContent( $this->difftotext, $this->contentFormat );
685 if ( $this->difftotextpst ) {
687 $difftocontent = $this->contentTransformer->preSaveTransform(
695 $engine = $handler->createDifferenceEngine( $context );
696 $engine->setContent(
$content, $difftocontent );
699 $engine = $handler->createDifferenceEngine( $context, $revision->
getId(), $this->diffto );
700 $vals[
'diff'][
'from'] = $engine->getOldid();
701 $vals[
'diff'][
'to'] = $engine->getNewid();
704 $difftext = $engine->getDiffBody();
706 if ( !$engine->wasCacheHit() ) {
711 $vals[
'diff'][
'notcached'] =
true;
725 if ( $this->userCanSeeRevDel() ) {
738 $slotRoles = $this->slotRoleRegistry->getKnownRoles();
739 sort( $slotRoles, SORT_STRING );
743 ParamValidator::PARAM_ISMULTI =>
true,
744 ParamValidator::PARAM_DEFAULT =>
'ids|timestamp|flags|comment|user',
745 ParamValidator::PARAM_TYPE => [
765 'ids' =>
'apihelp-query+revisions+base-paramvalue-prop-ids',
766 'flags' =>
'apihelp-query+revisions+base-paramvalue-prop-flags',
767 'timestamp' =>
'apihelp-query+revisions+base-paramvalue-prop-timestamp',
768 'user' =>
'apihelp-query+revisions+base-paramvalue-prop-user',
769 'userid' =>
'apihelp-query+revisions+base-paramvalue-prop-userid',
770 'size' =>
'apihelp-query+revisions+base-paramvalue-prop-size',
771 'slotsize' =>
'apihelp-query+revisions+base-paramvalue-prop-slotsize',
772 'sha1' =>
'apihelp-query+revisions+base-paramvalue-prop-sha1',
773 'slotsha1' =>
'apihelp-query+revisions+base-paramvalue-prop-slotsha1',
774 'contentmodel' =>
'apihelp-query+revisions+base-paramvalue-prop-contentmodel',
775 'comment' =>
'apihelp-query+revisions+base-paramvalue-prop-comment',
776 'parsedcomment' =>
'apihelp-query+revisions+base-paramvalue-prop-parsedcomment',
777 'content' =>
'apihelp-query+revisions+base-paramvalue-prop-content',
778 'tags' =>
'apihelp-query+revisions+base-paramvalue-prop-tags',
779 'roles' =>
'apihelp-query+revisions+base-paramvalue-prop-roles',
780 'parsetree' => [
'apihelp-query+revisions+base-paramvalue-prop-parsetree',
783 EnumDef::PARAM_DEPRECATED_VALUES => [
788 ParamValidator::PARAM_TYPE => $slotRoles,
790 ParamValidator::PARAM_ISMULTI =>
true,
791 ParamValidator::PARAM_ALL =>
true,
794 ParamValidator::PARAM_TYPE =>
'limit',
795 IntegerDef::PARAM_MIN => 1,
800 'expandtemplates' => [
801 ParamValidator::PARAM_DEFAULT =>
false,
803 ParamValidator::PARAM_DEPRECATED =>
true,
806 ParamValidator::PARAM_DEFAULT =>
false,
807 ParamValidator::PARAM_DEPRECATED =>
true,
811 ParamValidator::PARAM_DEFAULT =>
false,
813 ParamValidator::PARAM_DEPRECATED =>
true,
820 ParamValidator::PARAM_DEPRECATED =>
true,
824 ParamValidator::PARAM_DEPRECATED =>
true,
827 ParamValidator::PARAM_DEFAULT =>
false,
829 ParamValidator::PARAM_DEPRECATED =>
true,
832 ParamValidator::PARAM_TYPE => $this->contentHandlerFactory->getAllContentFormats(),
834 ParamValidator::PARAM_DEPRECATED =>
true,
const CONTENT_MODEL_WIKITEXT
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
getMain()
Get the main module.
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
addDeprecation( $msg, $feature, $data=[])
Add a deprecation warning for this module.
const LIMIT_BIG1
Fast query, standard limit.
const LIMIT_SML2
Slow query, apihighlimits limit.
getResult()
Get the result object.
const LIMIT_SML1
Slow query, standard limit.
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
const LIMIT_BIG2
Fast query, apihighlimits limit.
getModuleName()
Get the name of the module being executed by this instance.
This class contains a list of pages that the client has requested.
getParent()
Get the parent of this module.Stability: stableto override 1.25 ApiBase|null
encodeParamName( $paramName)
Overrides ApiBase to prepend 'g' to every generator parameter.
A base class for functions common to producing a list of revisions.
executeGenerator( $resultPageSet)
extractAllSlotInfo(RevisionRecord $revision, $revDel)
Extracts information about all relevant slots.
ContentRenderer $contentRenderer
parseParameters( $params)
Parse the parameters into the various instance fields.
IContentHandlerFactory $contentHandlerFactory
extractDeprecatedContent(Content $content, RevisionRecord $revision)
Format a Content using deprecated options.
ContentTransformer $contentTransformer
SlotRoleRegistry $slotRoleRegistry
RevisionStore $revisionStore
extractSlotInfo(SlotRecord $slot, $revDel, &$content=null)
Extract information from the SlotRecord.
ParserFactory $parserFactory
__construct(ApiQuery $queryModule, $moduleName, $paramPrefix='', RevisionStore $revisionStore=null, IContentHandlerFactory $contentHandlerFactory=null, ParserFactory $parserFactory=null, SlotRoleRegistry $slotRoleRegistry=null, ContentRenderer $contentRenderer=null, ContentTransformer $contentTransformer=null)
run(ApiPageSet $resultPageSet=null)
checkRevDel(RevisionRecord $revision, $field)
Test revision deletion status.
extractRevisionInfo(RevisionRecord $revision, $row)
Extract information from the RevisionRecord.
This is the main query class.
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.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
An IContextSource implementation which will inherit context from another source but allow individual ...
static formatComment( $comment, $title=null, $local=false, $wikiId=null)
This function is called by all recent changes variants, by the page history, and by the user contribu...
A service to render content.
A service to transform content.
A class containing constants representing the names of configuration variables.
static newFromContext(IContextSource $context)
Get a ParserOptions object from a IContextSource object.
static newFromLinkTarget(LinkTarget $linkTarget, $forceClone='')
Returns a Title given a LinkTarget.
Content object for wiki text pages.
Base interface for content objects.