MediaWiki  master
ApiQuery.php
Go to the documentation of this file.
1 <?php
29 use Wikimedia\ObjectFactory\ObjectFactory;
31 
43 class 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,
716  }
717  $data = $continuationManager->getRawContinuation();
718  if ( $data ) {
719  $this->getResult()->addValue( null, 'query-continue', $data,
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 }
$modules
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
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:1759
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.
Definition: ApiPageSet.php:55
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.
Definition: ApiQuery.php:1002
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 stripMetadataNonRecursive( $data, &$metadata=null)
Remove metadata keys from a data array or object, non-recursive.
Definition: ApiResult.php:1050
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
Definition: ApiResult.php:716
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don't use this unless yo...
Definition: ApiResult.php:58
const ADD_ON_TOP
For addValue(), setValue() and similar functions, if the value does not exist, add it as the first el...
Definition: ApiResult.php:49
const META_BC_SUBELEMENTS
Key for the 'BC subelements' metadata item.
Definition: ApiResult.php:143
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:604
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
static schemaVersion()
Returns the default export schema version, as defined by the XmlDumpSchemaVersion setting.
Service for formatting and validating API parameters.
static string[] $supportedSchemas
the schema versions supported for output @final
A title formatter service for MediaWiki.
return true
Definition: router.php:90