53 private const QUERY_PROP_MODULES = [
55 'class' => ApiQueryCategories::class,
58 'class' => ApiQueryCategoryInfo::class,
61 'class' => ApiQueryContributors::class,
66 'GroupPermissionsLookup',
70 'deletedrevisions' => [
71 'class' => ApiQueryDeletedRevisions::class,
74 'ContentHandlerFactory',
88 'class' => ApiQueryDuplicateFiles::class,
94 'class' => ApiQueryExternalLinks::class,
100 'class' => ApiQueryBacklinksprop::class,
107 'class' => ApiQueryImages::class,
110 'class' => ApiQueryImageInfo::class,
119 'class' => ApiQueryInfo::class,
127 'LanguageConverterFactory',
132 'IntroMessageBuilder',
133 'PreloadedContentBuilder',
139 'class' => ApiQueryLinks::class,
147 'class' => ApiQueryBacklinksprop::class,
154 'class' => ApiQueryIWLinks::class,
160 'class' => ApiQueryLangLinks::class,
168 'class' => ApiQueryPageProps::class,
174 'class' => ApiQueryBacklinksprop::class,
181 'class' => ApiQueryRevisions::class,
184 'ContentHandlerFactory',
191 'ContentTransformer',
198 'stashimageinfo' => [
199 'class' => ApiQueryStashImageInfo::class,
208 'class' => ApiQueryLinks::class,
216 'class' => ApiQueryBacklinksprop::class,
227 private const QUERY_LIST_MODULES = [
229 'class' => ApiQueryAllCategories::class,
231 'alldeletedrevisions' => [
232 'class' => ApiQueryAllDeletedRevisions::class,
235 'ContentHandlerFactory',
242 'ContentTransformer',
249 'class' => ApiQueryAllLinks::class,
258 'class' => ApiQueryAllImages::class,
261 'GroupPermissionsLookup',
265 'class' => ApiQueryAllLinks::class,
274 'class' => ApiQueryAllPages::class,
282 'class' => ApiQueryAllLinks::class,
291 'class' => ApiQueryAllRevisions::class,
294 'ContentHandlerFactory',
301 'ContentTransformer',
307 'mystashedfiles' => [
308 'class' => ApiQueryMyStashedFiles::class,
310 'alltransclusions' => [
311 'class' => ApiQueryAllLinks::class,
320 'class' => ApiQueryAllUsers::class,
324 'GroupPermissionsLookup',
330 'class' => ApiQueryBacklinks::class,
336 'class' => ApiQueryBlocks::class,
338 'DatabaseBlockStore',
340 'BlockRestrictionStore',
345 'categorymembers' => [
346 'class' => ApiQueryCategoryMembers::class,
352 'class' => ApiQueryDeletedrevs::class,
355 'RowCommentFormatter',
363 'class' => ApiQueryBacklinks::class,
369 'class' => ApiQueryExtLinksUsage::class,
375 'class' => ApiQueryFilearchive::class,
382 'class' => ApiQueryBacklinks::class,
388 'class' => ApiQueryIWBacklinks::class,
391 'class' => ApiQueryLangBacklinks::class,
394 'class' => ApiQueryLogEvents::class,
397 'RowCommentFormatter',
401 'LogFormatterFactory',
405 'class' => ApiQueryPagesWithProp::class,
408 'class' => ApiQueryPagePropNames::class,
411 'class' => ApiQueryPrefixSearch::class,
413 'SearchEngineConfig',
414 'SearchEngineFactory',
417 'protectedtitles' => [
418 'class' => ApiQueryProtectedTitles::class,
421 'RowCommentFormatter'
425 'class' => ApiQueryQueryPage::class,
427 'SpecialPageFactory',
431 'class' => ApiQueryRandom::class,
434 'class' => ApiQueryRecentChanges::class,
437 'RowCommentFormatter',
444 'LogFormatterFactory',
448 'class' => ApiQuerySearch::class,
450 'SearchEngineConfig',
451 'SearchEngineFactory',
456 'class' => ApiQueryTags::class,
462 'class' => ApiQueryUserContribs::class,
465 'UserIdentityLookup',
475 'class' => ApiQueryUsers::class,
485 'class' => ApiQueryWatchlist::class,
488 'WatchedItemQueryService',
494 'LogFormatterFactory',
498 'class' => ApiQueryWatchlistRaw::class,
500 'WatchedItemQueryService',
511 private const QUERY_META_MODULES = [
513 'class' => ApiQueryAllMessages::class,
522 'authmanagerinfo' => [
523 'class' => ApiQueryAuthManagerInfo::class,
529 'class' => ApiQuerySiteinfo::class,
534 'LanguageConverterFactory',
542 'SpecialPageFactory',
551 'class' => ApiQueryUserInfo::class,
553 'TalkPageNotificationManager',
561 'class' => ApiQueryFileRepoInfo::class,
567 'class' => ApiQueryTokens::class,
570 'class' => ApiQueryLanguageinfo::class,
575 'LanguageConverterFactory',
597 ObjectFactory $objectFactory,
602 parent::__construct( $main, $action );
611 $this->mModuleMgr->addModules( self::QUERY_PROP_MODULES,
'prop' );
613 $this->mModuleMgr->addModules( self::QUERY_LIST_MODULES,
'list' );
615 $this->mModuleMgr->addModules( self::QUERY_META_MODULES,
'meta' );
618 $this->
getHookRunner()->onApiQuery__moduleManager( $this->mModuleMgr );
622 $this->wikiExporterFactory = $wikiExporterFactory;
623 $this->titleFormatter = $titleFormatter;
624 $this->titleFactory = $titleFactory;
632 return $this->mModuleMgr;
640 return $this->mPageSet;
652 $this->
getMain()->createPrinterByName(
'xml' ) );
673 $this->instantiateModules( $allModules,
'prop' );
674 $propModules = array_keys( $allModules );
675 $this->instantiateModules( $allModules,
'list' );
676 $this->instantiateModules( $allModules,
'meta' );
682 $modules = $continuationManager->getRunModules();
683 '@phan-var ApiQueryBase[] $modules';
686 $message =
'hookaborted';
687 if ( !$this->
getHookRunner()->onApiQueryCheckCanExecute( $modules, $this->
getUser(), $message ) ) {
693 if ( !$continuationManager->isGeneratorDone() ) {
696 foreach ( $modules as $module ) {
698 $timer = $statsFactory->getTiming(
'api_query_extraDataTiming_seconds' )
699 ->setLabel(
'module', $module->getModuleName() )
700 ->copyToStatsdAt(
'api-query.' . $module->getModuleName() .
'.extraDataTiming' );
702 $module->requestExtraData( $this->mPageSet );
706 $this->mPageSet->execute();
708 $this->outputGeneralPageInfo();
710 $this->mPageSet->executeDryRun();
713 $cacheMode = $this->mPageSet->getCacheMode();
716 foreach ( $modules as $module ) {
718 $timer = $statsFactory->getTiming(
'api_query_executeTiming_seconds' )
719 ->setLabel(
'module', $module->getModuleName() )
720 ->copyToStatsdAt(
'api-query.' . $module->getModuleName() .
'.executeTiming' );
723 $params = $module->extractRequestParams();
725 $cacheMode, $module->getCacheMode(
$params ) );
734 $this->
getMain()->setCacheMode( $cacheMode );
738 if ( $this->mParams[
'rawcontinue'] ) {
739 $data = $continuationManager->getRawNonContinuation();
741 $this->
getResult()->addValue(
null,
'query-noncontinue', $data,
744 $data = $continuationManager->getRawContinuation();
746 $this->
getResult()->addValue(
null,
'query-continue', $data,
750 $continuationManager->setContinuationIntoResult( $this->
getResult() );
764 if ( $modCacheMode ===
'anon-public-user-private' ) {
765 if ( $cacheMode !==
'private' ) {
766 $cacheMode =
'anon-public-user-private';
768 } elseif ( $modCacheMode ===
'public' ) {
771 $cacheMode =
'private';
782 private function instantiateModules( &$modules, $param ) {
783 $wasPosted = $this->
getRequest()->wasPosted();
784 if ( isset( $this->mParams[$param] ) ) {
785 foreach ( $this->mParams[$param] as $moduleName ) {
786 $instance = $this->mModuleMgr->getModule( $moduleName, $param );
787 if ( $instance ===
null ) {
790 if ( !$wasPosted && $instance->mustBePosted() ) {
794 if ( !array_key_exists( $moduleName, $modules ) ) {
795 $modules[$moduleName] = $instance;
806 private function outputGeneralPageInfo() {
814 $values = $pageSet->getNormalizedTitlesAsResult( $result );
817 $fit = $fit && $result->addValue(
'query',
'normalized', $values );
819 $values = $pageSet->getConvertedTitlesAsResult( $result );
821 $fit = $fit && $result->addValue(
'query',
'converted', $values );
823 $values = $pageSet->getInterwikiTitlesAsResult( $result, $this->mParams[
'iwurl'] );
825 $fit = $fit && $result->addValue(
'query',
'interwiki', $values );
827 $values = $pageSet->getRedirectTitlesAsResult( $result );
829 $fit = $fit && $result->addValue(
'query',
'redirects', $values );
831 $values = $pageSet->getMissingRevisionIDsAsResult( $result );
833 $fit = $fit && $result->addValue(
'query',
'badrevids', $values );
841 foreach ( $pageSet->getMissingPages() as $fakeId => $page ) {
843 $vals[
'ns'] = $page->getNamespace();
844 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
845 $vals[
'missing'] =
true;
846 $title = $this->titleFactory->newFromPageIdentity( $page );
847 if ( $title->isKnown() ) {
848 $vals[
'known'] =
true;
850 $pages[$fakeId] = $vals;
853 foreach ( $pageSet->getInvalidTitlesAndReasons() as $fakeId => $data ) {
854 $pages[$fakeId] = $data + [
'invalid' => true ];
857 foreach ( $pageSet->getMissingPageIDs() as $pageid ) {
865 foreach ( $pageSet->getSpecialPages() as $fakeId => $page ) {
867 $vals[
'ns'] = $page->getNamespace();
868 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
869 $vals[
'special'] =
true;
870 $title = $this->titleFactory->newFromPageReference( $page );
871 if ( !$title->isKnown() ) {
872 $vals[
'missing'] =
true;
874 $pages[$fakeId] = $vals;
878 foreach ( $pageSet->getGoodPages() as $pageid => $page ) {
880 $vals[
'pageid'] = $pageid;
881 $vals[
'ns'] = $page->getNamespace();
882 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
883 $pages[$pageid] = $vals;
886 if ( count( $pages ) ) {
887 $pageSet->populateGeneratorData( $pages );
890 if ( $this->mParams[
'indexpageids'] ) {
893 $pageIDs = array_map(
'strval', $pageIDs );
895 $fit = $fit && $result->addValue(
'query',
'pageids', $pageIDs );
899 $fit = $fit && $result->addValue(
'query',
'pages', $pages );
903 $this->
dieWithError(
'apierror-badconfig-resulttoosmall',
'badconfig' );
906 if ( $this->mParams[
'export'] ) {
907 $this->doExport( $pageSet, $result );
915 private function doExport( $pageSet, $result ) {
917 $titles = $pageSet->getGoodPages();
918 if ( count( $titles ) ) {
920 foreach ( $titles as $title ) {
921 if ( $this->
getAuthority()->authorizeRead(
'read', $title ) ) {
922 $exportTitles[] = $title;
927 $exporter = $this->wikiExporterFactory->getWikiExporter( $this->
getDB() );
929 $exporter->setOutputSink( $sink );
930 $exporter->setSchemaVersion( $this->mParams[
'exportschema'] );
931 $exporter->openStream();
932 foreach ( $exportTitles as $title ) {
933 $exporter->pageByTitle( $title );
935 $exporter->closeStream();
940 if ( $this->mParams[
'exportnowrap'] ) {
955 ParamValidator::PARAM_ISMULTI =>
true,
956 ParamValidator::PARAM_TYPE =>
'submodule',
959 ParamValidator::PARAM_ISMULTI =>
true,
960 ParamValidator::PARAM_TYPE =>
'submodule',
963 ParamValidator::PARAM_ISMULTI =>
true,
964 ParamValidator::PARAM_TYPE =>
'submodule',
966 'indexpageids' =>
false,
968 'exportnowrap' =>
false,
977 'rawcontinue' =>
false,
980 $result += $this->
getPageSet()->getFinalParams( $flags );
993 $allowedParams = [
'rawcontinue' => 1,
'indexpageids' => 1 ];
997 $needed = $param ===
'meta';
998 if ( !isset( $allowedParams[$param] ) && $request->getCheck( $param ) !== $needed ) {
1006 $this->instantiateModules( $modules,
'meta' );
1007 foreach ( $modules as $module ) {
1008 if ( $module->isReadMode() ) {
1020 $this->instantiateModules( $modules,
'list' );
1021 $this->instantiateModules( $modules,
'meta' );
1022 $this->instantiateModules( $modules,
'prop' );
1023 foreach ( $modules as $module ) {
1024 if ( $module->isWriteMode() ) {
1033 $title = Title::newMainPage()->getPrefixedText();
1034 $mp = rawurlencode( $title );
1037 'action=query&prop=revisions&meta=siteinfo&' .
1038 "titles={$mp}&rvprop=user|comment&continue="
1039 =>
'apihelp-query-example-revisions',
1040 'action=query&generator=allpages&gapprefix=API/&prop=revisions&continue='
1041 =>
'apihelp-query-example-allpages',
1047 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Query',
1048 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Meta',
1049 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Properties',
1050 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Lists',
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)