MediaWiki 1.41.2
ApiQuery.php
Go to the documentation of this file.
1<?php
29use Wikimedia\ObjectFactory\ObjectFactory;
31
43class ApiQuery extends ApiBase {
44
48 private const QUERY_PROP_MODULES = [
49 'categories' => [
50 'class' => ApiQueryCategories::class,
51 ],
52 'categoryinfo' => [
53 'class' => ApiQueryCategoryInfo::class,
54 ],
55 'contributors' => [
56 'class' => ApiQueryContributors::class,
57 'services' => [
58 'RevisionStore',
59 'ActorMigration',
60 'UserGroupManager',
61 'GroupPermissionsLookup',
62 ]
63 ],
64 'deletedrevisions' => [
65 'class' => ApiQueryDeletedRevisions::class,
66 'services' => [
67 'RevisionStore',
68 'ContentHandlerFactory',
69 'ParserFactory',
70 'SlotRoleRegistry',
71 'ChangeTagDefStore',
72 'LinkBatchFactory',
73 'ContentRenderer',
74 'ContentTransformer',
75 'CommentFormatter',
76 'TempUserCreator',
77 'UserFactory',
78 ]
79 ],
80 'duplicatefiles' => [
81 'class' => ApiQueryDuplicateFiles::class,
82 'services' => [
83 'RepoGroup',
84 ]
85 ],
86 'extlinks' => [
87 'class' => ApiQueryExternalLinks::class,
88 'services' => [
89 'UrlUtils',
90 ],
91 ],
92 'fileusage' => [
93 'class' => ApiQueryBacklinksprop::class,
94 'services' => [
95 // Same as for linkshere, redirects, transcludedin
96 'LinksMigration',
97 ]
98 ],
99 'images' => [
100 'class' => ApiQueryImages::class,
101 ],
102 'imageinfo' => [
103 'class' => ApiQueryImageInfo::class,
104 'services' => [
105 // Same as for stashimageinfo
106 'RepoGroup',
107 'ContentLanguage',
108 'BadFileLookup',
109 ]
110 ],
111 'info' => [
112 'class' => ApiQueryInfo::class,
113 'services' => [
114 'ContentLanguage',
115 'LinkBatchFactory',
116 'NamespaceInfo',
117 'TitleFactory',
118 'TitleFormatter',
119 'WatchedItemStore',
120 'LanguageConverterFactory',
121 'RestrictionStore',
122 'LinksMigration',
123 'TempUserCreator',
124 'IntroMessageBuilder',
125 'PreloadedContentBuilder',
126 'RevisionLookup',
127 'UrlUtils',
128 ],
129 ],
130 'links' => [
131 'class' => ApiQueryLinks::class,
132 'services' => [
133 // Same as for templates
134 'LinkBatchFactory',
135 'LinksMigration',
136 ]
137 ],
138 'linkshere' => [
139 'class' => ApiQueryBacklinksprop::class,
140 'services' => [
141 // Same as for fileusage, redirects, transcludedin
142 'LinksMigration',
143 ]
144 ],
145 'iwlinks' => [
146 'class' => ApiQueryIWLinks::class,
147 'services' => [
148 'UrlUtils',
149 ]
150 ],
151 'langlinks' => [
152 'class' => ApiQueryLangLinks::class,
153 'services' => [
154 'LanguageNameUtils',
155 'ContentLanguage',
156 'UrlUtils',
157 ]
158 ],
159 'pageprops' => [
160 'class' => ApiQueryPageProps::class,
161 'services' => [
162 'PageProps',
163 ]
164 ],
165 'redirects' => [
166 'class' => ApiQueryBacklinksprop::class,
167 'services' => [
168 // Same as for fileusage, linkshere, transcludedin
169 'LinksMigration',
170 ]
171 ],
172 'revisions' => [
173 'class' => ApiQueryRevisions::class,
174 'services' => [
175 'RevisionStore',
176 'ContentHandlerFactory',
177 'ParserFactory',
178 'SlotRoleRegistry',
179 'ChangeTagDefStore',
180 'ActorMigration',
181 'ContentRenderer',
182 'ContentTransformer',
183 'CommentFormatter',
184 'TempUserCreator',
185 'UserFactory',
186 ]
187 ],
188 'stashimageinfo' => [
189 'class' => ApiQueryStashImageInfo::class,
190 'services' => [
191 // Same as for imageinfo
192 'RepoGroup',
193 'ContentLanguage',
194 'BadFileLookup',
195 ]
196 ],
197 'templates' => [
198 'class' => ApiQueryLinks::class,
199 'services' => [
200 // Same as for links
201 'LinkBatchFactory',
202 'LinksMigration',
203 ]
204 ],
205 'transcludedin' => [
206 'class' => ApiQueryBacklinksprop::class,
207 'services' => [
208 // Same as for fileusage, linkshere, redirects
209 'LinksMigration',
210 ]
211 ],
212 ];
213
217 private const QUERY_LIST_MODULES = [
218 'allcategories' => [
219 'class' => ApiQueryAllCategories::class,
220 ],
221 'alldeletedrevisions' => [
222 'class' => ApiQueryAllDeletedRevisions::class,
223 'services' => [
224 'RevisionStore',
225 'ContentHandlerFactory',
226 'ParserFactory',
227 'SlotRoleRegistry',
228 'ChangeTagDefStore',
229 'NamespaceInfo',
230 'ContentRenderer',
231 'ContentTransformer',
232 'CommentFormatter',
233 'TempUserCreator',
234 'UserFactory',
235 ]
236 ],
237 'allfileusages' => [
238 'class' => ApiQueryAllLinks::class,
239 'services' => [
240 // Same as for alllinks, allredirects, alltransclusions
241 'NamespaceInfo',
242 'GenderCache',
243 'LinksMigration',
244 ]
245 ],
246 'allimages' => [
247 'class' => ApiQueryAllImages::class,
248 'services' => [
249 'RepoGroup',
250 'GroupPermissionsLookup',
251 ]
252 ],
253 'alllinks' => [
254 'class' => ApiQueryAllLinks::class,
255 'services' => [
256 // Same as for allfileusages, allredirects, alltransclusions
257 'NamespaceInfo',
258 'GenderCache',
259 'LinksMigration',
260 ]
261 ],
262 'allpages' => [
263 'class' => ApiQueryAllPages::class,
264 'services' => [
265 'NamespaceInfo',
266 'GenderCache',
267 'RestrictionStore',
268 ]
269 ],
270 'allredirects' => [
271 'class' => ApiQueryAllLinks::class,
272 'services' => [
273 // Same as for allfileusages, alllinks, alltransclusions
274 'NamespaceInfo',
275 'GenderCache',
276 'LinksMigration',
277 ]
278 ],
279 'allrevisions' => [
280 'class' => ApiQueryAllRevisions::class,
281 'services' => [
282 'RevisionStore',
283 'ContentHandlerFactory',
284 'ParserFactory',
285 'SlotRoleRegistry',
286 'ActorMigration',
287 'NamespaceInfo',
288 'ContentRenderer',
289 'ContentTransformer',
290 'CommentFormatter',
291 'TempUserCreator',
292 'UserFactory',
293 ]
294 ],
295 'mystashedfiles' => [
296 'class' => ApiQueryMyStashedFiles::class,
297 ],
298 'alltransclusions' => [
299 'class' => ApiQueryAllLinks::class,
300 'services' => [
301 // Same as for allfileusages, alllinks, allredirects
302 'NamespaceInfo',
303 'GenderCache',
304 'LinksMigration',
305 ]
306 ],
307 'allusers' => [
308 'class' => ApiQueryAllUsers::class,
309 'services' => [
310 'UserFactory',
311 'UserGroupManager',
312 'GroupPermissionsLookup',
313 'ContentLanguage',
314 ]
315 ],
316 'backlinks' => [
317 'class' => ApiQueryBacklinks::class,
318 'services' => [
319 'LinksMigration',
320 ]
321 ],
322 'blocks' => [
323 'class' => ApiQueryBlocks::class,
324 'services' => [
325 'BlockActionInfo',
326 'BlockRestrictionStore',
327 'CommentStore',
328 ],
329 ],
330 'categorymembers' => [
331 'class' => ApiQueryCategoryMembers::class,
332 'services' => [
333 'CollationFactory',
334 ]
335 ],
336 'deletedrevs' => [
337 'class' => ApiQueryDeletedrevs::class,
338 'services' => [
339 'CommentStore',
340 'RowCommentFormatter',
341 'RevisionStore',
342 'ChangeTagDefStore',
343 'LinkBatchFactory',
344 ],
345 ],
346 'embeddedin' => [
347 'class' => ApiQueryBacklinks::class,
348 'services' => [
349 'LinksMigration',
350 ]
351 ],
352 'exturlusage' => [
353 'class' => ApiQueryExtLinksUsage::class,
354 'services' => [
355 'UrlUtils',
356 ],
357 ],
358 'filearchive' => [
359 'class' => ApiQueryFilearchive::class,
360 'services' => [
361 'CommentStore',
362 'CommentFormatter',
363 ],
364 ],
365 'imageusage' => [
366 'class' => ApiQueryBacklinks::class,
367 'services' => [
368 'LinksMigration',
369 ]
370 ],
371 'iwbacklinks' => [
372 'class' => ApiQueryIWBacklinks::class,
373 ],
374 'langbacklinks' => [
375 'class' => ApiQueryLangBacklinks::class,
376 ],
377 'logevents' => [
378 'class' => ApiQueryLogEvents::class,
379 'services' => [
380 'CommentStore',
381 'RowCommentFormatter',
382 'ChangeTagDefStore',
383 ],
384 ],
385 'pageswithprop' => [
386 'class' => ApiQueryPagesWithProp::class,
387 ],
388 'pagepropnames' => [
389 'class' => ApiQueryPagePropNames::class,
390 ],
391 'prefixsearch' => [
392 'class' => ApiQueryPrefixSearch::class,
393 'services' => [
394 'SearchEngineConfig',
395 'SearchEngineFactory',
396 ],
397 ],
398 'protectedtitles' => [
399 'class' => ApiQueryProtectedTitles::class,
400 'services' => [
401 'CommentStore',
402 'RowCommentFormatter'
403 ],
404 ],
405 'querypage' => [
406 'class' => ApiQueryQueryPage::class,
407 'services' => [
408 'SpecialPageFactory',
409 ]
410 ],
411 'random' => [
412 'class' => ApiQueryRandom::class,
413 ],
414 'recentchanges' => [
415 'class' => ApiQueryRecentChanges::class,
416 'services' => [
417 'CommentStore',
418 'RowCommentFormatter',
419 'ChangeTagDefStore',
420 'SlotRoleStore',
421 'SlotRoleRegistry',
422 ],
423 ],
424 'search' => [
425 'class' => ApiQuerySearch::class,
426 'services' => [
427 'SearchEngineConfig',
428 'SearchEngineFactory',
429 'TitleMatcher',
430 ],
431 ],
432 'tags' => [
433 'class' => ApiQueryTags::class,
434 'services' => [
435 'ChangeTagsStore',
436 ]
437 ],
438 'usercontribs' => [
439 'class' => ApiQueryUserContribs::class,
440 'services' => [
441 'CommentStore',
442 'UserIdentityLookup',
443 'UserNameUtils',
444 'RevisionStore',
445 'ChangeTagDefStore',
446 'ActorMigration',
447 'CommentFormatter',
448 ],
449 ],
450 'users' => [
451 'class' => ApiQueryUsers::class,
452 'services' => [
453 'UserNameUtils',
454 'UserFactory',
455 'UserGroupManager',
456 'GenderCache',
457 'AuthManager',
458 ],
459 ],
460 'watchlist' => [
461 'class' => ApiQueryWatchlist::class,
462 'services' => [
463 'CommentStore',
464 'WatchedItemQueryService',
465 'ContentLanguage',
466 'NamespaceInfo',
467 'GenderCache',
468 'CommentFormatter',
469 ],
470 ],
471 'watchlistraw' => [
472 'class' => ApiQueryWatchlistRaw::class,
473 'services' => [
474 'WatchedItemQueryService',
475 'ContentLanguage',
476 'NamespaceInfo',
477 'GenderCache',
478 ]
479 ],
480 ];
481
485 private const QUERY_META_MODULES = [
486 'allmessages' => [
487 'class' => ApiQueryAllMessages::class,
488 'services' => [
489 'ContentLanguage',
490 'LanguageFactory',
491 'LanguageNameUtils',
492 'LocalisationCache',
493 'MessageCache',
494 ]
495 ],
496 'authmanagerinfo' => [
497 'class' => ApiQueryAuthManagerInfo::class,
498 'services' => [
499 'AuthManager',
500 ]
501 ],
502 'siteinfo' => [
503 'class' => ApiQuerySiteinfo::class,
504 'services' => [
505 'UserOptionsLookup',
506 'UserGroupManager',
507 'LanguageConverterFactory',
508 'LanguageFactory',
509 'LanguageNameUtils',
510 'ContentLanguage',
511 'NamespaceInfo',
512 'InterwikiLookup',
513 'ParserFactory',
514 'MagicWordFactory',
515 'SpecialPageFactory',
516 'SkinFactory',
517 'DBLoadBalancer',
518 'ReadOnlyMode',
519 'UrlUtils',
520 ]
521 ],
522 'userinfo' => [
523 'class' => ApiQueryUserInfo::class,
524 'services' => [
525 'TalkPageNotificationManager',
526 'WatchedItemStore',
527 'UserEditTracker',
528 'UserOptionsLookup',
529 'UserGroupManager',
530 ]
531 ],
532 'filerepoinfo' => [
533 'class' => ApiQueryFileRepoInfo::class,
534 'services' => [
535 'RepoGroup',
536 ]
537 ],
538 'tokens' => [
539 'class' => ApiQueryTokens::class,
540 ],
541 'languageinfo' => [
542 'class' => ApiQueryLanguageinfo::class,
543 'services' => [
544 'LanguageFactory',
545 'LanguageNameUtils',
546 'LanguageFallback',
547 'LanguageConverterFactory',
548 ],
549 ],
550 ];
551
555 private $mPageSet;
556
557 private $mParams;
558 private $mModuleMgr;
559
560 private WikiExporterFactory $wikiExporterFactory;
561 private TitleFormatter $titleFormatter;
562 private TitleFactory $titleFactory;
563
572 public function __construct(
573 ApiMain $main,
574 $action,
575 ObjectFactory $objectFactory,
576 WikiExporterFactory $wikiExporterFactory,
577 TitleFormatter $titleFormatter,
578 TitleFactory $titleFactory
579 ) {
580 parent::__construct( $main, $action );
581
582 $this->mModuleMgr = new ApiModuleManager(
583 $this,
584 $objectFactory
585 );
586
587 // Allow custom modules to be added in LocalSettings.php
588 $config = $this->getConfig();
589 $this->mModuleMgr->addModules( self::QUERY_PROP_MODULES, 'prop' );
590 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIPropModules ), 'prop' );
591 $this->mModuleMgr->addModules( self::QUERY_LIST_MODULES, 'list' );
592 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIListModules ), 'list' );
593 $this->mModuleMgr->addModules( self::QUERY_META_MODULES, 'meta' );
594 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIMetaModules ), 'meta' );
595
596 $this->getHookRunner()->onApiQuery__moduleManager( $this->mModuleMgr );
597
598 // Create PageSet that will process titles/pageids/revids/generator
599 $this->mPageSet = new ApiPageSet( $this );
600 $this->wikiExporterFactory = $wikiExporterFactory;
601 $this->titleFormatter = $titleFormatter;
602 $this->titleFactory = $titleFactory;
603 }
604
609 public function getModuleManager() {
610 return $this->mModuleMgr;
611 }
612
617 public function getPageSet() {
618 return $this->mPageSet;
619 }
620
624 public function getCustomPrinter() {
625 // If &exportnowrap is set, use the raw formatter
626 if ( $this->getParameter( 'export' ) &&
627 $this->getParameter( 'exportnowrap' )
628 ) {
629 return new ApiFormatRaw( $this->getMain(),
630 $this->getMain()->createPrinterByName( 'xml' ) );
631 } else {
632 return null;
633 }
634 }
635
646 public function execute() {
647 $this->mParams = $this->extractRequestParams();
648
649 // Instantiate requested modules
650 $allModules = [];
651 $this->instantiateModules( $allModules, 'prop' );
652 $propModules = array_keys( $allModules );
653 $this->instantiateModules( $allModules, 'list' );
654 $this->instantiateModules( $allModules, 'meta' );
655
656 // Filter modules based on continue parameter
657 $continuationManager = new ApiContinuationManager( $this, $allModules, $propModules );
658 $this->setContinuationManager( $continuationManager );
660 $modules = $continuationManager->getRunModules();
661 '@phan-var ApiQueryBase[] $modules';
662
663 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
664
665 if ( !$continuationManager->isGeneratorDone() ) {
666 // Query modules may optimize data requests through the $this->getPageSet()
667 // object by adding extra fields from the page table.
668 foreach ( $modules as $module ) {
669 $t = microtime( true );
670 $module->requestExtraData( $this->mPageSet );
671 $runTime = microtime( true ) - $t;
672
673 // Augment api-query.$module.executeTiming metric with timings for requestExtraData()
674 $stats->timing(
675 'api-query.' . $module->getModuleName() . '.extraDataTiming', 1000 * $runTime
676 );
677 }
678 // Populate page/revision information
679 $this->mPageSet->execute();
680 // Record page information (title, namespace, if exists, etc)
681 $this->outputGeneralPageInfo();
682 } else {
683 $this->mPageSet->executeDryRun();
684 }
685
686 $cacheMode = $this->mPageSet->getCacheMode();
687
688 // Execute all unfinished modules
689 foreach ( $modules as $module ) {
690 $params = $module->extractRequestParams();
691 $cacheMode = $this->mergeCacheMode(
692 $cacheMode, $module->getCacheMode( $params ) );
693
694 $t = microtime( true );
695 $module->execute();
696 $runTime = microtime( true ) - $t;
697
698 // Break down of the api.query.executeTiming metric by query module.
699 $stats->timing(
700 'api-query.' . $module->getModuleName() . '.executeTiming', 1000 * $runTime
701 );
702
703 $this->getHookRunner()->onAPIQueryAfterExecute( $module );
704 }
705
706 // Set the cache mode
707 $this->getMain()->setCacheMode( $cacheMode );
708
709 // Write the continuation data into the result
710 $this->setContinuationManager( null );
711 if ( $this->mParams['rawcontinue'] ) {
712 $data = $continuationManager->getRawNonContinuation();
713 if ( $data ) {
714 $this->getResult()->addValue( null, 'query-noncontinue', $data,
715 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
716 }
717 $data = $continuationManager->getRawContinuation();
718 if ( $data ) {
719 $this->getResult()->addValue( null, 'query-continue', $data,
720 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
721 }
722 } else {
723 $continuationManager->setContinuationIntoResult( $this->getResult() );
724 }
725 }
726
736 protected function mergeCacheMode( $cacheMode, $modCacheMode ) {
737 if ( $modCacheMode === 'anon-public-user-private' ) {
738 if ( $cacheMode !== 'private' ) {
739 $cacheMode = 'anon-public-user-private';
740 }
741 } elseif ( $modCacheMode === 'public' ) {
742 // do nothing, if it's public already it will stay public
743 } else {
744 $cacheMode = 'private';
745 }
746
747 return $cacheMode;
748 }
749
755 private function instantiateModules( &$modules, $param ) {
756 $wasPosted = $this->getRequest()->wasPosted();
757 if ( isset( $this->mParams[$param] ) ) {
758 foreach ( $this->mParams[$param] as $moduleName ) {
759 $instance = $this->mModuleMgr->getModule( $moduleName, $param );
760 if ( $instance === null ) {
761 ApiBase::dieDebug( __METHOD__, 'Error instantiating module' );
762 }
763 if ( !$wasPosted && $instance->mustBePosted() ) {
764 $this->dieWithErrorOrDebug( [ 'apierror-mustbeposted', $moduleName ] );
765 }
766 // Ignore duplicates. TODO 2.0: die()?
767 if ( !array_key_exists( $moduleName, $modules ) ) {
768 $modules[$moduleName] = $instance;
769 }
770 }
771 }
772 }
773
779 private function outputGeneralPageInfo() {
780 $pageSet = $this->getPageSet();
781 $result = $this->getResult();
782
783 // We can't really handle max-result-size failure here, but we need to
784 // check anyway in case someone set the limit stupidly low.
785 $fit = true;
786
787 $values = $pageSet->getNormalizedTitlesAsResult( $result );
788 if ( $values ) {
789 // @phan-suppress-next-line PhanRedundantCondition
790 $fit = $fit && $result->addValue( 'query', 'normalized', $values );
791 }
792 $values = $pageSet->getConvertedTitlesAsResult( $result );
793 if ( $values ) {
794 $fit = $fit && $result->addValue( 'query', 'converted', $values );
795 }
796 $values = $pageSet->getInterwikiTitlesAsResult( $result, $this->mParams['iwurl'] );
797 if ( $values ) {
798 $fit = $fit && $result->addValue( 'query', 'interwiki', $values );
799 }
800 $values = $pageSet->getRedirectTitlesAsResult( $result );
801 if ( $values ) {
802 $fit = $fit && $result->addValue( 'query', 'redirects', $values );
803 }
804 $values = $pageSet->getMissingRevisionIDsAsResult( $result );
805 if ( $values ) {
806 $fit = $fit && $result->addValue( 'query', 'badrevids', $values );
807 }
808
809 // Page elements
810 // Cannot use ApiPageSet::getInvalidTitlesAndRevisions, it does not set $fakeId
811 $pages = [];
812
813 // Report any missing titles
814 foreach ( $pageSet->getMissingPages() as $fakeId => $page ) {
815 $vals = [];
816 $vals['ns'] = $page->getNamespace();
817 $vals['title'] = $this->titleFormatter->getPrefixedText( $page );
818 $vals['missing'] = true;
819 $title = $this->titleFactory->newFromPageIdentity( $page );
820 if ( $title->isKnown() ) {
821 $vals['known'] = true;
822 }
823 $pages[$fakeId] = $vals;
824 }
825 // Report any invalid titles
826 foreach ( $pageSet->getInvalidTitlesAndReasons() as $fakeId => $data ) {
827 $pages[$fakeId] = $data + [ 'invalid' => true ];
828 }
829 // Report any missing page ids
830 foreach ( $pageSet->getMissingPageIDs() as $pageid ) {
831 $pages[$pageid] = [
832 'pageid' => $pageid,
833 'missing' => true,
834 ];
835 }
836 // Report special pages
838 foreach ( $pageSet->getSpecialPages() as $fakeId => $page ) {
839 $vals = [];
840 $vals['ns'] = $page->getNamespace();
841 $vals['title'] = $this->titleFormatter->getPrefixedText( $page );
842 $vals['special'] = true;
843 $title = $this->titleFactory->newFromPageReference( $page );
844 if ( !$title->isKnown() ) {
845 $vals['missing'] = true;
846 }
847 $pages[$fakeId] = $vals;
848 }
849
850 // Output general page information for found titles
851 foreach ( $pageSet->getGoodPages() as $pageid => $page ) {
852 $vals = [];
853 $vals['pageid'] = $pageid;
854 $vals['ns'] = $page->getNamespace();
855 $vals['title'] = $this->titleFormatter->getPrefixedText( $page );
856 $pages[$pageid] = $vals;
857 }
858
859 if ( count( $pages ) ) {
860 $pageSet->populateGeneratorData( $pages );
861 ApiResult::setArrayType( $pages, 'BCarray' );
862
863 if ( $this->mParams['indexpageids'] ) {
864 $pageIDs = array_keys( ApiResult::stripMetadataNonRecursive( $pages ) );
865 // json treats all map keys as strings - converting to match
866 $pageIDs = array_map( 'strval', $pageIDs );
867 ApiResult::setIndexedTagName( $pageIDs, 'id' );
868 $fit = $fit && $result->addValue( 'query', 'pageids', $pageIDs );
869 }
870
871 ApiResult::setIndexedTagName( $pages, 'page' );
872 $fit = $fit && $result->addValue( 'query', 'pages', $pages );
873 }
874
875 if ( !$fit ) {
876 $this->dieWithError( 'apierror-badconfig-resulttoosmall', 'badconfig' );
877 }
878
879 if ( $this->mParams['export'] ) {
880 $this->doExport( $pageSet, $result );
881 }
882 }
883
888 private function doExport( $pageSet, $result ) {
889 $exportTitles = [];
890 $titles = $pageSet->getGoodPages();
891 if ( count( $titles ) ) {
893 foreach ( $titles as $title ) {
894 if ( $this->getAuthority()->authorizeRead( 'read', $title ) ) {
895 $exportTitles[] = $title;
896 }
897 }
898 }
899
900 $exporter = $this->wikiExporterFactory->getWikiExporter( $this->getDB() );
901 $sink = new DumpStringOutput;
902 $exporter->setOutputSink( $sink );
903 $exporter->setSchemaVersion( $this->mParams['exportschema'] );
904 $exporter->openStream();
905 foreach ( $exportTitles as $title ) {
906 $exporter->pageByTitle( $title );
907 }
908 $exporter->closeStream();
909
910 // Don't check the size of exported stuff
911 // It's not continuable, so it would cause more
912 // problems than it'd solve
913 if ( $this->mParams['exportnowrap'] ) {
914 $result->reset();
915 // Raw formatter will handle this
916 $result->addValue( null, 'text', $sink, ApiResult::NO_SIZE_CHECK );
917 $result->addValue( null, 'mime', 'text/xml', ApiResult::NO_SIZE_CHECK );
918 $result->addValue( null, 'filename', 'export.xml', ApiResult::NO_SIZE_CHECK );
919 } else {
920 $result->addValue( 'query', 'export', $sink, ApiResult::NO_SIZE_CHECK );
921 $result->addValue( 'query', ApiResult::META_BC_SUBELEMENTS, [ 'export' ] );
922 }
923 }
924
925 public function getAllowedParams( $flags = 0 ) {
926 $result = [
927 'prop' => [
928 ParamValidator::PARAM_ISMULTI => true,
929 ParamValidator::PARAM_TYPE => 'submodule',
930 ],
931 'list' => [
932 ParamValidator::PARAM_ISMULTI => true,
933 ParamValidator::PARAM_TYPE => 'submodule',
934 ],
935 'meta' => [
936 ParamValidator::PARAM_ISMULTI => true,
937 ParamValidator::PARAM_TYPE => 'submodule',
938 ],
939 'indexpageids' => false,
940 'export' => false,
941 'exportnowrap' => false,
942 'exportschema' => [
943 ParamValidator::PARAM_DEFAULT => WikiExporter::schemaVersion(),
944 ParamValidator::PARAM_TYPE => XmlDumpWriter::$supportedSchemas,
945 ],
946 'iwurl' => false,
947 'continue' => [
948 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
949 ],
950 'rawcontinue' => false,
951 ];
952 if ( $flags ) {
953 $result += $this->getPageSet()->getFinalParams( $flags );
954 }
955
956 return $result;
957 }
958
959 public function isReadMode() {
960 // We need to make an exception for certain meta modules that should be
961 // accessible even without the 'read' right. Restrict the exception as
962 // much as possible: no other modules allowed, and no pageset
963 // parameters either. We do allow the 'rawcontinue' and 'indexpageids'
964 // parameters since frameworks might add these unconditionally and they
965 // can't expose anything here.
966 $allowedParams = [ 'rawcontinue' => 1, 'indexpageids' => 1 ];
967 $this->mParams = $this->extractRequestParams();
968 $request = $this->getRequest();
969 foreach ( $this->mParams + $this->getPageSet()->extractRequestParams() as $param => $value ) {
970 $needed = $param === 'meta';
971 if ( !isset( $allowedParams[$param] ) && $request->getCheck( $param ) !== $needed ) {
972 return true;
973 }
974 }
975
976 // Ask each module if it requires read mode. Any true => this returns
977 // true.
978 $modules = [];
979 $this->instantiateModules( $modules, 'meta' );
980 foreach ( $modules as $module ) {
981 if ( $module->isReadMode() ) {
982 return true;
983 }
984 }
985
986 return false;
987 }
988
989 protected function getExamplesMessages() {
990 $title = Title::newMainPage()->getPrefixedText();
991 $mp = rawurlencode( $title );
992
993 return [
994 'action=query&prop=revisions&meta=siteinfo&' .
995 "titles={$mp}&rvprop=user|comment&continue="
996 => 'apihelp-query-example-revisions',
997 'action=query&generator=allpages&gapprefix=API/&prop=revisions&continue='
998 => 'apihelp-query-example-allpages',
999 ];
1000 }
1001
1002 public function getHelpUrls() {
1003 return [
1004 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Query',
1005 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Meta',
1006 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Properties',
1007 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Lists',
1008 ];
1009 }
1010}
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:62
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1515
getParameter( $paramName, $parseLimit=true)
Get a value for the given parameter.
Definition ApiBase.php:929
getDB()
Gets a default replica DB connection object.
Definition ApiBase.php:691
dieWithErrorOrDebug( $msg, $code=null, $data=null, $httpCode=null)
Will only set a warning instead of failing if the global $wgDebugAPI is set to true.
Definition ApiBase.php:1688
getMain()
Get the main module.
Definition ApiBase.php:546
setContinuationManager(ApiContinuationManager $manager=null)
Definition ApiBase.php:714
getResult()
Get the result object.
Definition ApiBase.php:667
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:807
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:169
getHookRunner()
Get an ApiHookRunner for running core API hooks.
Definition ApiBase.php:752
This manages continuation state.
Formatter that spits out anything you like with any desired MIME type.
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:64
This class holds a list of modules and handles instantiation.
This class contains a list of pages that the client has requested.
This is the main query class.
Definition ApiQuery.php:43
isReadMode()
Indicates whether this module requires read rights.
Definition ApiQuery.php:959
mergeCacheMode( $cacheMode, $modCacheMode)
Update a cache mode string, applying the cache mode of a new module to it.
Definition ApiQuery.php:736
getAllowedParams( $flags=0)
Definition ApiQuery.php:925
getModuleManager()
Overrides to return this instance's module manager.
Definition ApiQuery.php:609
__construct(ApiMain $main, $action, ObjectFactory $objectFactory, WikiExporterFactory $wikiExporterFactory, TitleFormatter $titleFormatter, TitleFactory $titleFactory)
Definition ApiQuery.php:572
getExamplesMessages()
Returns usage examples for this module.
Definition ApiQuery.php:989
getHelpUrls()
Return links to more detailed help pages about the module.
getPageSet()
Gets the set of pages the user has requested (or generated)
Definition ApiQuery.php:617
execute()
Query execution happens in the following steps: #1 Create a PageSet object with any pages requested b...
Definition ApiQuery.php:646
getCustomPrinter()
Definition ApiQuery.php:624
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.
Factory service for WikiExporter instances.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
Creates Title objects.
Represents a title within MediaWiki.
Definition Title.php:76
Service for formatting and validating API parameters.
A title formatter service for MediaWiki.
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition Page.php:30
A helper class for throttling authentication attempts.
return true
Definition router.php:92