101 parent::__construct( $queryModule, $moduleName, $paramPrefix );
104 $services = MediaWikiServices::getInstance();
105 $this->revisionStore =
$revisionStore ?? $services->getRevisionStore();
107 $this->parserFactory =
$parserFactory ?? $services->getParserFactory();
117 $this->
run( $resultPageSet );
132 $prop = array_fill_keys( $params[
'prop'],
true );
134 $this->fld_ids = isset( $prop[
'ids'] );
135 $this->fld_flags = isset( $prop[
'flags'] );
136 $this->fld_timestamp = isset( $prop[
'timestamp'] );
137 $this->fld_comment = isset( $prop[
'comment'] );
138 $this->fld_parsedcomment = isset( $prop[
'parsedcomment'] );
139 $this->fld_size = isset( $prop[
'size'] );
140 $this->fld_slotsize = isset( $prop[
'slotsize'] );
141 $this->fld_sha1 = isset( $prop[
'sha1'] );
142 $this->fld_slotsha1 = isset( $prop[
'slotsha1'] );
143 $this->fld_content = isset( $prop[
'content'] );
144 $this->fld_contentmodel = isset( $prop[
'contentmodel'] );
145 $this->fld_userid = isset( $prop[
'userid'] );
146 $this->fld_user = isset( $prop[
'user'] );
147 $this->fld_tags = isset( $prop[
'tags'] );
148 $this->fld_roles = isset( $prop[
'roles'] );
149 $this->fld_parsetree = isset( $prop[
'parsetree'] );
151 $this->slotRoles = $params[
'slots'];
153 if ( $this->slotRoles !==
null ) {
154 if ( $this->fld_parsetree ) {
156 'apierror-invalidparammix-cannotusewith',
159 ],
'invalidparammix' );
162 'expandtemplates',
'generatexml',
'parse',
'diffto',
'difftotext',
'difftotextpst',
165 if ( $params[$p] !==
null && $params[$p] !==
false ) {
167 'apierror-invalidparammix-cannotusewith',
170 ],
'invalidparammix' );
175 if ( !empty( $params[
'contentformat'] ) ) {
176 $this->contentFormat = $params[
'contentformat'];
179 $this->limit = $params[
'limit'];
181 if ( $params[
'difftotext'] !==
null ) {
182 $this->difftotext = $params[
'difftotext'];
183 $this->difftotextpst = $params[
'difftotextpst'];
184 } elseif ( $params[
'diffto'] !==
null ) {
185 if ( $params[
'diffto'] ==
'cur' ) {
186 $params[
'diffto'] = 0;
188 if ( ( !ctype_digit( $params[
'diffto'] ) || $params[
'diffto'] < 0 )
189 && $params[
'diffto'] !=
'prev' && $params[
'diffto'] !=
'next'
192 $this->
dieWithError( [
'apierror-baddiffto', $p ],
'diffto' );
197 if ( $params[
'diffto'] != 0 ) {
198 $difftoRev = $this->revisionStore->getRevisionById( $params[
'diffto'] );
200 $this->
dieWithError( [
'apierror-nosuchrevid', $params[
'diffto'] ] );
202 $revDel = $this->
checkRevDel( $difftoRev, RevisionRecord::DELETED_TEXT );
203 if ( $revDel & self::CANNOT_VIEW ) {
204 $this->
addWarning( [
'apiwarn-difftohidden', $difftoRev->getId() ] );
205 $params[
'diffto'] =
null;
208 $this->diffto = $params[
'diffto'];
211 $this->fetchContent = $this->fld_content || $this->diffto !==
null
215 if ( $this->fetchContent ) {
217 $this->expandTemplates = $params[
'expandtemplates'];
218 $this->generateXML = $params[
'generatexml'];
219 $this->parseContent = $params[
'parse'];
220 if ( $this->parseContent ) {
222 if ( $this->limit ===
null ) {
226 $this->section = $params[
'section'] ??
false;
231 if ( $this->limit ==
'max' ) {
232 $this->limit = $this->
getMain()->canApiHighLimits() ? $botMax : $userMax;
233 if ( $this->setParsedLimit ) {
238 $this->limit = $this->
getMain()->getParamValidator()->validateValue(
239 $this,
'limit', $this->limit ?? 10, [
240 ParamValidator::PARAM_TYPE =>
'limit',
241 IntegerDef::PARAM_MIN => 1,
242 IntegerDef::PARAM_MAX => $userMax,
243 IntegerDef::PARAM_MAX2 => $botMax,
244 IntegerDef::PARAM_IGNORE_RANGE =>
true,
248 $this->needSlots = $this->fetchContent || $this->fld_contentmodel ||
250 if ( $this->needSlots && $this->slotRoles ===
null ) {
254 $parentParam = $parent->encodeParamName( $parent->getModuleManager()->getModuleGroup( $name ) );
256 [
'apiwarn-deprecation-missingparam', $encParam ],
257 "action=query&{$parentParam}={$name}&!{$encParam}"
290 if ( $this->fld_ids ) {
291 $vals[
'revid'] = (int)$revision->
getId();
297 if ( $this->fld_flags ) {
298 $vals[
'minor'] = $revision->
isMinor();
301 if ( $this->fld_user || $this->fld_userid ) {
302 $revDel = $this->
checkRevDel( $revision, RevisionRecord::DELETED_USER );
303 if ( $revDel & self::IS_DELETED ) {
304 $vals[
'userhidden'] =
true;
307 if ( !( $revDel & self::CANNOT_VIEW ) ) {
308 $u = $revision->
getUser( RevisionRecord::RAW );
309 if ( $this->fld_user ) {
310 $vals[
'user'] = $u->getName();
312 $userid = $u->getId();
314 $vals[
'anon'] =
true;
317 if ( $this->fld_userid ) {
318 $vals[
'userid'] = $userid;
323 if ( $this->fld_timestamp ) {
327 if ( $this->fld_size ) {
329 $vals[
'size'] = (int)$revision->
getSize();
337 if ( $this->fld_sha1 ) {
338 $revDel = $this->
checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
339 if ( $revDel & self::IS_DELETED ) {
340 $vals[
'sha1hidden'] =
true;
343 if ( !( $revDel & self::CANNOT_VIEW ) ) {
345 $vals[
'sha1'] = Wikimedia\base_convert( $revision->
getSha1(), 36, 16, 40 );
355 if ( $this->fld_roles ) {
359 if ( $this->needSlots ) {
360 $revDel = $this->
checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
361 if ( ( $this->fld_slotsha1 || $this->fetchContent ) && ( $revDel & self::IS_DELETED ) ) {
369 $vals[
'slotsmissing'] =
true;
371 LoggerFactory::getInstance(
'api-warning' )->error(
372 'Failed to access revision slots',
373 [
'revision' => $revision->
getId(),
'exception' => $ex, ]
377 if ( $this->fld_comment || $this->fld_parsedcomment ) {
378 $revDel = $this->
checkRevDel( $revision, RevisionRecord::DELETED_COMMENT );
379 if ( $revDel & self::IS_DELETED ) {
380 $vals[
'commenthidden'] =
true;
383 if ( !( $revDel & self::CANNOT_VIEW ) ) {
384 $comment = $revision->
getComment( RevisionRecord::RAW );
385 $comment = $comment ? $comment->text :
'';
387 if ( $this->fld_comment ) {
388 $vals[
'comment'] = $comment;
391 if ( $this->fld_parsedcomment ) {
399 if ( $this->fld_tags ) {
400 if ( $row->ts_tags ) {
401 $tags = explode(
',', $row->ts_tags );
402 ApiResult::setIndexedTagName( $tags,
'tag' );
403 $vals[
'tags'] = $tags;
409 if ( $anyHidden && $revision->
isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
410 $vals[
'suppressed'] =
true;
428 if ( $this->slotRoles ===
null ) {
430 $slot = $revision->
getSlot( SlotRecord::MAIN, RevisionRecord::RAW );
434 $vals[
'textmissing'] =
true;
441 if ( !empty( $vals[
'nosuchsection'] ) ) {
444 'apierror-nosuchsection-what',
446 $this->
msg(
'revid', $revision->
getId() )
456 $roles = array_intersect( $this->slotRoles, $revision->
getSlotRoles() );
458 ApiResult::META_KVP_MERGE =>
true,
460 foreach ( $roles as $role ) {
462 $slot = $revision->
getSlot( $role, RevisionRecord::RAW );
466 $vals[
'slots'][$role][
'missing'] =
true;
475 $vals[
'slots'][$role][
'contentmodel'] =
$content->getModel();
476 $vals[
'slots'][$role][
'contentformat'] =
$content->getDefaultFormat();
478 $vals[
'slots'][$role],
501 ApiResult::setArrayType( $vals,
'assoc' );
503 if ( $this->fld_slotsize ) {
504 $vals[
'size'] = (int)$slot->
getSize();
507 if ( $this->fld_slotsha1 ) {
508 if ( $revDel & self::IS_DELETED ) {
509 $vals[
'sha1hidden'] =
true;
511 if ( !( $revDel & self::CANNOT_VIEW ) ) {
512 if ( $slot->
getSha1() !=
'' ) {
513 $vals[
'sha1'] = Wikimedia\base_convert( $slot->
getSha1(), 36, 16, 40 );
520 if ( $this->fld_contentmodel ) {
521 $vals[
'contentmodel'] = $slot->
getModel();
525 if ( $this->fetchContent ) {
526 if ( $revDel & self::IS_DELETED ) {
527 $vals[
'texthidden'] =
true;
529 if ( !( $revDel & self::CANNOT_VIEW ) ) {
534 $vals[
'textmissing'] =
true;
539 if (
$content && $this->section !==
false ) {
542 $vals[
'nosuchsection'] =
true;
561 if ( $this->fld_parsetree || ( $this->fld_content && $this->generateXML ) ) {
564 '@phan-var WikitextContent $content';
565 $t =
$content->getText(); # note: don
't set $text
567 $parser = $this->parserFactory->create();
568 $parser->startExternalParse(
570 ParserOptions::newFromContext( $this->getContext() ),
571 Parser::OT_PREPROCESS
573 $dom = $parser->preprocessToDom( $t );
574 if ( is_callable( [ $dom, 'saveXML
' ] ) ) {
575 // @phan-suppress-next-line PhanUndeclaredMethod
576 $xml = $dom->saveXML();
578 // @phan-suppress-next-line PhanUndeclaredMethod
579 $xml = $dom->__toString();
581 $vals['parsetree
'] = $xml;
583 $vals['badcontentformatforparsetree
'] = true;
586 'apierror-parsetree-notwikitext-title
',
587 wfEscapeWikiText( $title->getPrefixedText() ),
590 'parsetree-notwikitext
'
595 if ( $this->fld_content ) {
598 if ( $this->expandTemplates && !$this->parseContent ) {
599 if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
602 $text = $content->getText();
604 $text = $this->parserFactory->create()->preprocess(
607 ParserOptions::newFromContext( $this->getContext() )
611 'apierror-templateexpansion-notwikitext
',
612 wfEscapeWikiText( $title->getPrefixedText() ),
615 $vals['badcontentformat
'] = true;
619 if ( $this->parseContent ) {
620 $po = $content->getParserOutput(
623 ParserOptions::newFromContext( $this->getContext() )
625 $text = $po->getText();
628 if ( $text === null ) {
629 $format = $this->contentFormat ?: $content->getDefaultFormat();
630 $model = $content->getModel();
632 if ( !$content->isSupportedFormat( $format ) ) {
633 $name = wfEscapeWikiText( $title->getPrefixedText() );
634 $this->addWarning( [ 'apierror-badformat
', $this->contentFormat, $model, $name ] );
635 $vals['badcontentformat
'] = true;
638 $text = $content->serialize( $format );
639 // always include format and model.
640 // Format is needed to deserialize, model is needed to interpret.
641 $vals['contentformat
'] = $format;
642 $vals['contentmodel
'] = $model;
646 if ( $text !== false ) {
647 ApiResult::setContentValue( $vals, 'content
', $text );
651 if ( $content && ( $this->diffto !== null || $this->difftotext !== null ) ) {
652 static $n = 0; // Number of uncached diffs we've had
654 if ( $n < $this->getConfig()->
get(
'APIMaxUncachedDiffs' ) ) {
657 $context->setTitle(
$title );
658 $handler =
$content->getContentHandler();
660 if ( $this->difftotext !==
null ) {
661 $model =
$title->getContentModel();
663 if ( $this->contentFormat
664 && !$this->contentHandlerFactory->getContentHandler( $model )
665 ->isSupportedFormat( $this->contentFormat )
668 $this->addWarning( [
'apierror-badformat', $this->contentFormat, $model, $name ] );
669 $vals[
'diff'][
'badcontentformat'] =
true;
672 $difftocontent = ContentHandler::makeContent(
679 if ( $this->difftotextpst ) {
680 $popts = ParserOptions::newFromContext( $this->
getContext() );
681 $difftocontent = $this->contentTransformer->preSaveTransform(
689 $engine = $handler->createDifferenceEngine( $context );
690 $engine->setContent(
$content, $difftocontent );
693 $engine = $handler->createDifferenceEngine( $context, $revision->
getId(), $this->diffto );
694 $vals[
'diff'][
'from'] = $engine->getOldid();
695 $vals[
'diff'][
'to'] = $engine->getNewid();
698 $difftext = $engine->getDiffBody();
699 ApiResult::setContentValue( $vals[
'diff'],
'body', $difftext );
700 if ( !$engine->wasCacheHit() ) {
705 $vals[
'diff'][
'notcached'] =
true;
719 if ( $this->userCanSeeRevDel() ) {
732 $slotRoles = $this->slotRoleRegistry->getKnownRoles();
733 sort( $slotRoles, SORT_STRING );
759 'ids' =>
'apihelp-query+revisions+base-paramvalue-prop-ids',
760 'flags' =>
'apihelp-query+revisions+base-paramvalue-prop-flags',
761 'timestamp' =>
'apihelp-query+revisions+base-paramvalue-prop-timestamp',
762 'user' =>
'apihelp-query+revisions+base-paramvalue-prop-user',
763 'userid' =>
'apihelp-query+revisions+base-paramvalue-prop-userid',
764 'size' =>
'apihelp-query+revisions+base-paramvalue-prop-size',
765 'slotsize' =>
'apihelp-query+revisions+base-paramvalue-prop-slotsize',
766 'sha1' =>
'apihelp-query+revisions+base-paramvalue-prop-sha1',
767 'slotsha1' =>
'apihelp-query+revisions+base-paramvalue-prop-slotsha1',
768 'contentmodel' =>
'apihelp-query+revisions+base-paramvalue-prop-contentmodel',
769 'comment' =>
'apihelp-query+revisions+base-paramvalue-prop-comment',
770 'parsedcomment' =>
'apihelp-query+revisions+base-paramvalue-prop-parsedcomment',
771 'content' =>
'apihelp-query+revisions+base-paramvalue-prop-content',
772 'tags' =>
'apihelp-query+revisions+base-paramvalue-prop-tags',
773 'roles' =>
'apihelp-query+revisions+base-paramvalue-prop-roles',
774 'parsetree' => [
'apihelp-query+revisions+base-paramvalue-prop-parsetree',
794 'expandtemplates' => [
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,...
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
const PARAM_DEPRECATED_VALUES
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.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.
executeGenerator( $resultPageSet)
extractAllSlotInfo(RevisionRecord $revision, $revDel)
Extracts information about all relevant slots.
__construct(ApiQuery $queryModule, $moduleName, $paramPrefix='', RevisionStore $revisionStore=null, IContentHandlerFactory $contentHandlerFactory=null, ParserFactory $parserFactory=null, SlotRoleRegistry $slotRoleRegistry=null, ContentTransformer $contentTransformer=null)
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
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.
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 transform content.
Content object for wiki text pages.
Base interface for content objects.
getModel()
Returns the ID of the content model used by this Content object.
getDefaultFormat()
Convenience method that returns the default serialization format for the content model that this Cont...