MediaWiki
REL1_23
|
00001 <?php 00038 class ApiQuery extends ApiBase { 00039 00044 private static $QueryPropModules = array( 00045 'categories' => 'ApiQueryCategories', 00046 'categoryinfo' => 'ApiQueryCategoryInfo', 00047 'contributors' => 'ApiQueryContributors', 00048 'duplicatefiles' => 'ApiQueryDuplicateFiles', 00049 'extlinks' => 'ApiQueryExternalLinks', 00050 'images' => 'ApiQueryImages', 00051 'imageinfo' => 'ApiQueryImageInfo', 00052 'info' => 'ApiQueryInfo', 00053 'links' => 'ApiQueryLinks', 00054 'iwlinks' => 'ApiQueryIWLinks', 00055 'langlinks' => 'ApiQueryLangLinks', 00056 'pageprops' => 'ApiQueryPageProps', 00057 'redirects' => 'ApiQueryRedirects', 00058 'revisions' => 'ApiQueryRevisions', 00059 'stashimageinfo' => 'ApiQueryStashImageInfo', 00060 'templates' => 'ApiQueryLinks', 00061 ); 00062 00067 private static $QueryListModules = array( 00068 'allcategories' => 'ApiQueryAllCategories', 00069 'allfileusages' => 'ApiQueryAllLinks', 00070 'allimages' => 'ApiQueryAllImages', 00071 'alllinks' => 'ApiQueryAllLinks', 00072 'allpages' => 'ApiQueryAllPages', 00073 'allredirects' => 'ApiQueryAllLinks', 00074 'alltransclusions' => 'ApiQueryAllLinks', 00075 'allusers' => 'ApiQueryAllUsers', 00076 'backlinks' => 'ApiQueryBacklinks', 00077 'blocks' => 'ApiQueryBlocks', 00078 'categorymembers' => 'ApiQueryCategoryMembers', 00079 'deletedrevs' => 'ApiQueryDeletedrevs', 00080 'embeddedin' => 'ApiQueryBacklinks', 00081 'exturlusage' => 'ApiQueryExtLinksUsage', 00082 'filearchive' => 'ApiQueryFilearchive', 00083 'imageusage' => 'ApiQueryBacklinks', 00084 'iwbacklinks' => 'ApiQueryIWBacklinks', 00085 'langbacklinks' => 'ApiQueryLangBacklinks', 00086 'logevents' => 'ApiQueryLogEvents', 00087 'pageswithprop' => 'ApiQueryPagesWithProp', 00088 'pagepropnames' => 'ApiQueryPagePropNames', 00089 'prefixsearch' => 'ApiQueryPrefixSearch', 00090 'protectedtitles' => 'ApiQueryProtectedTitles', 00091 'querypage' => 'ApiQueryQueryPage', 00092 'random' => 'ApiQueryRandom', 00093 'recentchanges' => 'ApiQueryRecentChanges', 00094 'search' => 'ApiQuerySearch', 00095 'tags' => 'ApiQueryTags', 00096 'usercontribs' => 'ApiQueryContributions', 00097 'users' => 'ApiQueryUsers', 00098 'watchlist' => 'ApiQueryWatchlist', 00099 'watchlistraw' => 'ApiQueryWatchlistRaw', 00100 ); 00101 00106 private static $QueryMetaModules = array( 00107 'allmessages' => 'ApiQueryAllMessages', 00108 'siteinfo' => 'ApiQuerySiteinfo', 00109 'userinfo' => 'ApiQueryUserInfo', 00110 'filerepoinfo' => 'ApiQueryFileRepoInfo', 00111 ); 00112 00116 private $mPageSet; 00117 00118 private $mParams; 00119 private $mNamedDB = array(); 00120 private $mModuleMgr; 00121 private $mGeneratorContinue; 00122 private $mUseLegacyContinue; 00123 00128 public function __construct( $main, $action ) { 00129 parent::__construct( $main, $action ); 00130 00131 $this->mModuleMgr = new ApiModuleManager( $this ); 00132 00133 // Allow custom modules to be added in LocalSettings.php 00134 global $wgAPIPropModules, $wgAPIListModules, $wgAPIMetaModules; 00135 $this->mModuleMgr->addModules( self::$QueryPropModules, 'prop' ); 00136 $this->mModuleMgr->addModules( $wgAPIPropModules, 'prop' ); 00137 $this->mModuleMgr->addModules( self::$QueryListModules, 'list' ); 00138 $this->mModuleMgr->addModules( $wgAPIListModules, 'list' ); 00139 $this->mModuleMgr->addModules( self::$QueryMetaModules, 'meta' ); 00140 $this->mModuleMgr->addModules( $wgAPIMetaModules, 'meta' ); 00141 00142 // Create PageSet that will process titles/pageids/revids/generator 00143 $this->mPageSet = new ApiPageSet( $this ); 00144 } 00145 00150 public function getModuleManager() { 00151 return $this->mModuleMgr; 00152 } 00153 00164 public function getNamedDB( $name, $db, $groups ) { 00165 if ( !array_key_exists( $name, $this->mNamedDB ) ) { 00166 $this->profileDBIn(); 00167 $this->mNamedDB[$name] = wfGetDB( $db, $groups ); 00168 $this->profileDBOut(); 00169 } 00170 00171 return $this->mNamedDB[$name]; 00172 } 00173 00178 public function getPageSet() { 00179 return $this->mPageSet; 00180 } 00181 00187 public function getModules() { 00188 wfDeprecated( __METHOD__, '1.21' ); 00189 00190 return $this->getModuleManager()->getNamesWithClasses(); 00191 } 00192 00198 public function getGenerators() { 00199 wfDeprecated( __METHOD__, '1.21' ); 00200 $gens = array(); 00201 foreach ( $this->mModuleMgr->getNamesWithClasses() as $name => $class ) { 00202 if ( is_subclass_of( $class, 'ApiQueryGeneratorBase' ) ) { 00203 $gens[$name] = $class; 00204 } 00205 } 00206 00207 return $gens; 00208 } 00209 00216 function getModuleType( $moduleName ) { 00217 return $this->getModuleManager()->getModuleGroup( $moduleName ); 00218 } 00219 00223 public function getCustomPrinter() { 00224 // If &exportnowrap is set, use the raw formatter 00225 if ( $this->getParameter( 'export' ) && 00226 $this->getParameter( 'exportnowrap' ) 00227 ) { 00228 return new ApiFormatRaw( $this->getMain(), 00229 $this->getMain()->createPrinterByName( 'xml' ) ); 00230 } else { 00231 return null; 00232 } 00233 } 00234 00245 public function execute() { 00246 $this->mParams = $this->extractRequestParams(); 00247 00248 // $pagesetParams is a array of parameter names used by the pageset generator 00249 // or null if pageset has already finished and is no longer needed 00250 // $completeModules is a set of complete modules with the name as key 00251 $this->initContinue( $pagesetParams, $completeModules ); 00252 00253 // Instantiate requested modules 00254 $allModules = array(); 00255 $this->instantiateModules( $allModules, 'prop' ); 00256 $propModules = $allModules; // Keep a copy 00257 $this->instantiateModules( $allModules, 'list' ); 00258 $this->instantiateModules( $allModules, 'meta' ); 00259 00260 // Filter modules based on continue parameter 00261 $modules = $this->initModules( $allModules, $completeModules, $pagesetParams !== null ); 00262 00263 // Execute pageset if in legacy mode or if pageset is not done 00264 if ( $completeModules === null || $pagesetParams !== null ) { 00265 // Populate page/revision information 00266 $this->mPageSet->execute(); 00267 // Record page information (title, namespace, if exists, etc) 00268 $this->outputGeneralPageInfo(); 00269 } else { 00270 $this->mPageSet->executeDryRun(); 00271 } 00272 00273 $cacheMode = $this->mPageSet->getCacheMode(); 00274 00275 // Execute all unfinished modules 00277 foreach ( $modules as $module ) { 00278 $params = $module->extractRequestParams(); 00279 $cacheMode = $this->mergeCacheMode( 00280 $cacheMode, $module->getCacheMode( $params ) ); 00281 $module->profileIn(); 00282 $module->execute(); 00283 wfRunHooks( 'APIQueryAfterExecute', array( &$module ) ); 00284 $module->profileOut(); 00285 } 00286 00287 // Set the cache mode 00288 $this->getMain()->setCacheMode( $cacheMode ); 00289 00290 if ( $completeModules === null ) { 00291 return; // Legacy continue, we are done 00292 } 00293 00294 // Reformat query-continue result section 00295 $result = $this->getResult(); 00296 $qc = $result->getData(); 00297 if ( isset( $qc['query-continue'] ) ) { 00298 $qc = $qc['query-continue']; 00299 $result->unsetValue( null, 'query-continue' ); 00300 } elseif ( $this->mGeneratorContinue !== null ) { 00301 $qc = array(); 00302 } else { 00303 // no more "continue"s, we are done! 00304 return; 00305 } 00306 00307 // we are done with all the modules that do not have result in query-continue 00308 $completeModules = array_merge( $completeModules, array_diff_key( $modules, $qc ) ); 00309 if ( $pagesetParams !== null ) { 00310 // The pageset is still in use, check if all props have finished 00311 $incompleteProps = array_intersect_key( $propModules, $qc ); 00312 if ( count( $incompleteProps ) > 0 ) { 00313 // Properties are not done, continue with the same pageset state - copy current parameters 00314 $main = $this->getMain(); 00315 $contValues = array(); 00316 foreach ( $pagesetParams as $param ) { 00317 // The param name is already prefix-encoded 00318 $contValues[$param] = $main->getVal( $param ); 00319 } 00320 } elseif ( $this->mGeneratorContinue !== null ) { 00321 // Move to the next set of pages produced by pageset, properties need to be restarted 00322 $contValues = $this->mGeneratorContinue; 00323 $pagesetParams = array_keys( $contValues ); 00324 $completeModules = array_diff_key( $completeModules, $propModules ); 00325 } else { 00326 // Done with the pageset, finish up with the the lists and meta modules 00327 $pagesetParams = null; 00328 } 00329 } 00330 00331 $continue = '||' . implode( '|', array_keys( $completeModules ) ); 00332 if ( $pagesetParams !== null ) { 00333 // list of all pageset parameters to use in the next request 00334 $continue = implode( '|', $pagesetParams ) . $continue; 00335 } else { 00336 // we are done with the pageset 00337 $contValues = array(); 00338 $continue = '-' . $continue; 00339 } 00340 $contValues['continue'] = $continue; 00341 foreach ( $qc as $qcModule ) { 00342 foreach ( $qcModule as $qcKey => $qcValue ) { 00343 $contValues[$qcKey] = $qcValue; 00344 } 00345 } 00346 $this->getResult()->addValue( null, 'continue', $contValues ); 00347 } 00348 00354 private function initContinue( &$pagesetParams, &$completeModules ) { 00355 $pagesetParams = array(); 00356 $continue = $this->mParams['continue']; 00357 if ( $continue !== null ) { 00358 $this->mUseLegacyContinue = false; 00359 if ( $continue !== '' ) { 00360 // Format: ' pagesetParam1 | pagesetParam2 || module1 | module2 | module3 | ... 00361 // If pageset is done, use '-' 00362 $continue = explode( '||', $continue ); 00363 $this->dieContinueUsageIf( count( $continue ) !== 2 ); 00364 if ( $continue[0] === '-' ) { 00365 $pagesetParams = null; // No need to execute pageset 00366 } elseif ( $continue[0] !== '' ) { 00367 // list of pageset params that might need to be repeated 00368 $pagesetParams = explode( '|', $continue[0] ); 00369 } 00370 $continue = $continue[1]; 00371 } 00372 if ( $continue !== '' ) { 00373 $completeModules = array_flip( explode( '|', $continue ) ); 00374 } else { 00375 $completeModules = array(); 00376 } 00377 } else { 00378 $this->mUseLegacyContinue = true; 00379 $completeModules = null; 00380 } 00381 } 00382 00390 private function initModules( $allModules, $completeModules, $usePageset ) { 00391 $modules = $allModules; 00392 $tmp = $completeModules; 00393 $wasPosted = $this->getRequest()->wasPosted(); 00394 00396 foreach ( $allModules as $moduleName => $module ) { 00397 if ( !$wasPosted && $module->mustBePosted() ) { 00398 $this->dieUsageMsgOrDebug( array( 'mustbeposted', $moduleName ) ); 00399 } 00400 if ( $completeModules !== null && array_key_exists( $moduleName, $completeModules ) ) { 00401 // If this module is done, mark all its params as used 00402 $module->extractRequestParams(); 00403 // Make sure this module is not used during execution 00404 unset( $modules[$moduleName] ); 00405 unset( $tmp[$moduleName] ); 00406 } elseif ( $completeModules === null || $usePageset ) { 00407 // Query modules may optimize data requests through the $this->getPageSet() 00408 // object by adding extra fields from the page table. 00409 // This function will gather all the extra request fields from the modules. 00410 $module->requestExtraData( $this->mPageSet ); 00411 } else { 00412 // Error - this prop module must have finished before generator is done 00413 $this->dieContinueUsageIf( $this->mModuleMgr->getModuleGroup( $moduleName ) === 'prop' ); 00414 } 00415 } 00416 $this->dieContinueUsageIf( $completeModules !== null && count( $tmp ) !== 0 ); 00417 00418 return $modules; 00419 } 00420 00430 protected function mergeCacheMode( $cacheMode, $modCacheMode ) { 00431 if ( $modCacheMode === 'anon-public-user-private' ) { 00432 if ( $cacheMode !== 'private' ) { 00433 $cacheMode = 'anon-public-user-private'; 00434 } 00435 } elseif ( $modCacheMode === 'public' ) { 00436 // do nothing, if it's public already it will stay public 00437 } else { // private 00438 $cacheMode = 'private'; 00439 } 00440 00441 return $cacheMode; 00442 } 00443 00449 private function instantiateModules( &$modules, $param ) { 00450 if ( isset( $this->mParams[$param] ) ) { 00451 foreach ( $this->mParams[$param] as $moduleName ) { 00452 $instance = $this->mModuleMgr->getModule( $moduleName, $param ); 00453 if ( $instance === null ) { 00454 ApiBase::dieDebug( __METHOD__, 'Error instantiating module' ); 00455 } 00456 // Ignore duplicates. TODO 2.0: die()? 00457 if ( !array_key_exists( $moduleName, $modules ) ) { 00458 $modules[$moduleName] = $instance; 00459 } 00460 } 00461 } 00462 } 00463 00469 private function outputGeneralPageInfo() { 00470 $pageSet = $this->getPageSet(); 00471 $result = $this->getResult(); 00472 00473 // We don't check for a full result set here because we can't be adding 00474 // more than 380K. The maximum revision size is in the megabyte range, 00475 // and the maximum result size must be even higher than that. 00476 00477 $values = $pageSet->getNormalizedTitlesAsResult( $result ); 00478 if ( $values ) { 00479 $result->addValue( 'query', 'normalized', $values ); 00480 } 00481 $values = $pageSet->getConvertedTitlesAsResult( $result ); 00482 if ( $values ) { 00483 $result->addValue( 'query', 'converted', $values ); 00484 } 00485 $values = $pageSet->getInterwikiTitlesAsResult( $result, $this->mParams['iwurl'] ); 00486 if ( $values ) { 00487 $result->addValue( 'query', 'interwiki', $values ); 00488 } 00489 $values = $pageSet->getRedirectTitlesAsResult( $result ); 00490 if ( $values ) { 00491 $result->addValue( 'query', 'redirects', $values ); 00492 } 00493 $values = $pageSet->getMissingRevisionIDsAsResult( $result ); 00494 if ( $values ) { 00495 $result->addValue( 'query', 'badrevids', $values ); 00496 } 00497 00498 // Page elements 00499 $pages = array(); 00500 00501 // Report any missing titles 00502 foreach ( $pageSet->getMissingTitles() as $fakeId => $title ) { 00503 $vals = array(); 00504 ApiQueryBase::addTitleInfo( $vals, $title ); 00505 $vals['missing'] = ''; 00506 $pages[$fakeId] = $vals; 00507 } 00508 // Report any invalid titles 00509 foreach ( $pageSet->getInvalidTitles() as $fakeId => $title ) { 00510 $pages[$fakeId] = array( 'title' => $title, 'invalid' => '' ); 00511 } 00512 // Report any missing page ids 00513 foreach ( $pageSet->getMissingPageIDs() as $pageid ) { 00514 $pages[$pageid] = array( 00515 'pageid' => $pageid, 00516 'missing' => '' 00517 ); 00518 } 00519 // Report special pages 00521 foreach ( $pageSet->getSpecialTitles() as $fakeId => $title ) { 00522 $vals = array(); 00523 ApiQueryBase::addTitleInfo( $vals, $title ); 00524 $vals['special'] = ''; 00525 if ( $title->isSpecialPage() && 00526 !SpecialPageFactory::exists( $title->getDBkey() ) 00527 ) { 00528 $vals['missing'] = ''; 00529 } elseif ( $title->getNamespace() == NS_MEDIA && 00530 !wfFindFile( $title ) 00531 ) { 00532 $vals['missing'] = ''; 00533 } 00534 $pages[$fakeId] = $vals; 00535 } 00536 00537 // Output general page information for found titles 00538 foreach ( $pageSet->getGoodTitles() as $pageid => $title ) { 00539 $vals = array(); 00540 $vals['pageid'] = $pageid; 00541 ApiQueryBase::addTitleInfo( $vals, $title ); 00542 $pages[$pageid] = $vals; 00543 } 00544 00545 if ( count( $pages ) ) { 00546 if ( $this->mParams['indexpageids'] ) { 00547 $pageIDs = array_keys( $pages ); 00548 // json treats all map keys as strings - converting to match 00549 $pageIDs = array_map( 'strval', $pageIDs ); 00550 $result->setIndexedTagName( $pageIDs, 'id' ); 00551 $result->addValue( 'query', 'pageids', $pageIDs ); 00552 } 00553 00554 $result->setIndexedTagName( $pages, 'page' ); 00555 $result->addValue( 'query', 'pages', $pages ); 00556 } 00557 if ( $this->mParams['export'] ) { 00558 $this->doExport( $pageSet, $result ); 00559 } 00560 } 00561 00571 public function setGeneratorContinue( $module, $paramName, $paramValue ) { 00572 if ( $this->mUseLegacyContinue ) { 00573 return false; 00574 } 00575 $paramName = $module->encodeParamName( $paramName ); 00576 if ( $this->mGeneratorContinue === null ) { 00577 $this->mGeneratorContinue = array(); 00578 } 00579 $this->mGeneratorContinue[$paramName] = $paramValue; 00580 00581 return true; 00582 } 00583 00588 private function doExport( $pageSet, $result ) { 00589 $exportTitles = array(); 00590 $titles = $pageSet->getGoodTitles(); 00591 if ( count( $titles ) ) { 00592 $user = $this->getUser(); 00594 foreach ( $titles as $title ) { 00595 if ( $title->userCan( 'read', $user ) ) { 00596 $exportTitles[] = $title; 00597 } 00598 } 00599 } 00600 00601 $exporter = new WikiExporter( $this->getDB() ); 00602 // WikiExporter writes to stdout, so catch its 00603 // output with an ob 00604 ob_start(); 00605 $exporter->openStream(); 00606 foreach ( $exportTitles as $title ) { 00607 $exporter->pageByTitle( $title ); 00608 } 00609 $exporter->closeStream(); 00610 $exportxml = ob_get_contents(); 00611 ob_end_clean(); 00612 00613 // Don't check the size of exported stuff 00614 // It's not continuable, so it would cause more 00615 // problems than it'd solve 00616 $result->disableSizeCheck(); 00617 if ( $this->mParams['exportnowrap'] ) { 00618 $result->reset(); 00619 // Raw formatter will handle this 00620 $result->addValue( null, 'text', $exportxml ); 00621 $result->addValue( null, 'mime', 'text/xml' ); 00622 } else { 00623 $r = array(); 00624 ApiResult::setContent( $r, $exportxml ); 00625 $result->addValue( 'query', 'export', $r ); 00626 } 00627 $result->enableSizeCheck(); 00628 } 00629 00630 public function getAllowedParams( $flags = 0 ) { 00631 $result = array( 00632 'prop' => array( 00633 ApiBase::PARAM_ISMULTI => true, 00634 ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'prop' ) 00635 ), 00636 'list' => array( 00637 ApiBase::PARAM_ISMULTI => true, 00638 ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'list' ) 00639 ), 00640 'meta' => array( 00641 ApiBase::PARAM_ISMULTI => true, 00642 ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'meta' ) 00643 ), 00644 'indexpageids' => false, 00645 'export' => false, 00646 'exportnowrap' => false, 00647 'iwurl' => false, 00648 'continue' => null, 00649 ); 00650 if ( $flags ) { 00651 $result += $this->getPageSet()->getFinalParams( $flags ); 00652 } 00653 00654 return $result; 00655 } 00656 00661 public function makeHelpMsg() { 00662 00663 // Use parent to make default message for the query module 00664 $msg = parent::makeHelpMsg(); 00665 00666 $querySeparator = str_repeat( '--- ', 12 ); 00667 $moduleSeparator = str_repeat( '*** ', 14 ); 00668 $msg .= "\n$querySeparator Query: Prop $querySeparator\n\n"; 00669 $msg .= $this->makeHelpMsgHelper( 'prop' ); 00670 $msg .= "\n$querySeparator Query: List $querySeparator\n\n"; 00671 $msg .= $this->makeHelpMsgHelper( 'list' ); 00672 $msg .= "\n$querySeparator Query: Meta $querySeparator\n\n"; 00673 $msg .= $this->makeHelpMsgHelper( 'meta' ); 00674 $msg .= "\n\n$moduleSeparator Modules: continuation $moduleSeparator\n\n"; 00675 00676 return $msg; 00677 } 00678 00684 private function makeHelpMsgHelper( $group ) { 00685 $moduleDescriptions = array(); 00686 00687 $moduleNames = $this->mModuleMgr->getNames( $group ); 00688 sort( $moduleNames ); 00689 foreach ( $moduleNames as $name ) { 00693 $module = $this->mModuleMgr->getModule( $name ); 00694 00695 $msg = ApiMain::makeHelpMsgHeader( $module, $group ); 00696 $msg2 = $module->makeHelpMsg(); 00697 if ( $msg2 !== false ) { 00698 $msg .= $msg2; 00699 } 00700 if ( $module instanceof ApiQueryGeneratorBase ) { 00701 $msg .= "Generator:\n This module may be used as a generator\n"; 00702 } 00703 $moduleDescriptions[] = $msg; 00704 } 00705 00706 return implode( "\n", $moduleDescriptions ); 00707 } 00708 00709 public function shouldCheckMaxlag() { 00710 return true; 00711 } 00712 00713 public function getParamDescription() { 00714 return $this->getPageSet()->getFinalParamDescription() + array( 00715 'prop' => 'Which properties to get for the titles/revisions/pageids. ' . 00716 'Module help is available below', 00717 'list' => 'Which lists to get. Module help is available below', 00718 'meta' => 'Which metadata to get about the site. Module help is available below', 00719 'indexpageids' => 'Include an additional pageids section listing all returned page IDs', 00720 'export' => 'Export the current revisions of all given or generated pages', 00721 'exportnowrap' => 'Return the export XML without wrapping it in an ' . 00722 'XML result (same format as Special:Export). Can only be used with export', 00723 'iwurl' => 'Whether to get the full URL if the title is an interwiki link', 00724 'continue' => array( 00725 'When present, formats query-continue as key-value pairs that ' . 00726 'should simply be merged into the original request.', 00727 'This parameter must be set to an empty string in the initial query.', 00728 'This parameter is recommended for all new development, and ' . 00729 'will be made default in the next API version.' 00730 ), 00731 ); 00732 } 00733 00734 public function getDescription() { 00735 return array( 00736 'Query API module allows applications to get needed pieces of data ' . 00737 'from the MediaWiki databases,', 00738 'and is loosely based on the old query.php interface.', 00739 'All data modifications will first have to use query to acquire a ' . 00740 'token to prevent abuse from malicious sites.' 00741 ); 00742 } 00743 00744 public function getPossibleErrors() { 00745 return array_merge( 00746 parent::getPossibleErrors(), 00747 $this->getPageSet()->getFinalPossibleErrors() 00748 ); 00749 } 00750 00751 public function getExamples() { 00752 return array( 00753 'api.php?action=query&prop=revisions&meta=siteinfo&' . 00754 'titles=Main%20Page&rvprop=user|comment&continue=', 00755 'api.php?action=query&generator=allpages&gapprefix=API/&prop=revisions&continue=', 00756 ); 00757 } 00758 00759 public function getHelpUrls() { 00760 return array( 00761 'https://www.mediawiki.org/wiki/API:Query', 00762 'https://www.mediawiki.org/wiki/API:Meta', 00763 'https://www.mediawiki.org/wiki/API:Properties', 00764 'https://www.mediawiki.org/wiki/API:Lists', 00765 ); 00766 } 00767 }