00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 if ( !defined( 'MEDIAWIKI' ) ) {
00027
00028 require_once ( 'ApiBase.php' );
00029 }
00030
00042 class ApiQuery extends ApiBase {
00043
00044 private $mPropModuleNames, $mListModuleNames, $mMetaModuleNames;
00045 private $mPageSet;
00046 private $params, $redirect;
00047
00048 private $mQueryPropModules = array (
00049 'info' => 'ApiQueryInfo',
00050 'revisions' => 'ApiQueryRevisions',
00051 'links' => 'ApiQueryLinks',
00052 'langlinks' => 'ApiQueryLangLinks',
00053 'images' => 'ApiQueryImages',
00054 'imageinfo' => 'ApiQueryImageInfo',
00055 'templates' => 'ApiQueryLinks',
00056 'categories' => 'ApiQueryCategories',
00057 'extlinks' => 'ApiQueryExternalLinks',
00058 'categoryinfo' => 'ApiQueryCategoryInfo',
00059 'duplicatefiles' => 'ApiQueryDuplicateFiles',
00060 );
00061
00062 private $mQueryListModules = array (
00063 'allimages' => 'ApiQueryAllimages',
00064 'allpages' => 'ApiQueryAllpages',
00065 'alllinks' => 'ApiQueryAllLinks',
00066 'allcategories' => 'ApiQueryAllCategories',
00067 'allusers' => 'ApiQueryAllUsers',
00068 'backlinks' => 'ApiQueryBacklinks',
00069 'blocks' => 'ApiQueryBlocks',
00070 'categorymembers' => 'ApiQueryCategoryMembers',
00071 'deletedrevs' => 'ApiQueryDeletedrevs',
00072 'embeddedin' => 'ApiQueryBacklinks',
00073 'imageusage' => 'ApiQueryBacklinks',
00074 'logevents' => 'ApiQueryLogEvents',
00075 'recentchanges' => 'ApiQueryRecentChanges',
00076 'search' => 'ApiQuerySearch',
00077 'tags' => 'ApiQueryTags',
00078 'usercontribs' => 'ApiQueryContributions',
00079 'watchlist' => 'ApiQueryWatchlist',
00080 'watchlistraw' => 'ApiQueryWatchlistRaw',
00081 'exturlusage' => 'ApiQueryExtLinksUsage',
00082 'users' => 'ApiQueryUsers',
00083 'random' => 'ApiQueryRandom',
00084 'protectedtitles' => 'ApiQueryProtectedTitles',
00085 );
00086
00087 private $mQueryMetaModules = array (
00088 'siteinfo' => 'ApiQuerySiteinfo',
00089 'userinfo' => 'ApiQueryUserInfo',
00090 'allmessages' => 'ApiQueryAllmessages',
00091 );
00092
00093 private $mSlaveDB = null;
00094 private $mNamedDB = array();
00095
00096 public function __construct( $main, $action ) {
00097 parent :: __construct( $main, $action );
00098
00099
00100 global $wgAPIPropModules, $wgAPIListModules, $wgAPIMetaModules;
00101 self :: appendUserModules( $this->mQueryPropModules, $wgAPIPropModules );
00102 self :: appendUserModules( $this->mQueryListModules, $wgAPIListModules );
00103 self :: appendUserModules( $this->mQueryMetaModules, $wgAPIMetaModules );
00104
00105 $this->mPropModuleNames = array_keys( $this->mQueryPropModules );
00106 $this->mListModuleNames = array_keys( $this->mQueryListModules );
00107 $this->mMetaModuleNames = array_keys( $this->mQueryMetaModules );
00108
00109
00110
00111 $this->mAllowedGenerators = array_merge( $this->mListModuleNames, $this->mPropModuleNames );
00112 }
00113
00119 private static function appendUserModules( &$modules, $newModules ) {
00120 if ( is_array( $newModules ) ) {
00121 foreach ( $newModules as $moduleName => $moduleClass ) {
00122 $modules[$moduleName] = $moduleClass;
00123 }
00124 }
00125 }
00126
00131 public function getDB() {
00132 if ( !isset ( $this->mSlaveDB ) ) {
00133 $this->profileDBIn();
00134 $this->mSlaveDB = wfGetDB( DB_SLAVE, 'api' );
00135 $this->profileDBOut();
00136 }
00137 return $this->mSlaveDB;
00138 }
00139
00150 public function getNamedDB( $name, $db, $groups ) {
00151 if ( !array_key_exists( $name, $this->mNamedDB ) ) {
00152 $this->profileDBIn();
00153 $this->mNamedDB[$name] = wfGetDB( $db, $groups );
00154 $this->profileDBOut();
00155 }
00156 return $this->mNamedDB[$name];
00157 }
00158
00163 public function getPageSet() {
00164 return $this->mPageSet;
00165 }
00166
00171 function getModules() {
00172 return array_merge( $this->mQueryPropModules, $this->mQueryListModules, $this->mQueryMetaModules );
00173 }
00174
00175 public function getCustomPrinter() {
00176
00177 if ( $this->getParameter( 'export' ) &&
00178 $this->getParameter( 'exportnowrap' ) )
00179 return new ApiFormatRaw( $this->getMain(),
00180 $this->getMain()->createPrinterByName( 'xml' ) );
00181 else
00182 return null;
00183 }
00184
00195 public function execute() {
00196
00197 $this->params = $this->extractRequestParams();
00198 $this->redirects = $this->params['redirects'];
00199
00200
00201 $this->mPageSet = new ApiPageSet( $this, $this->redirects );
00202
00203
00204 $modules = array ();
00205 $this->InstantiateModules( $modules, 'prop', $this->mQueryPropModules );
00206 $this->InstantiateModules( $modules, 'list', $this->mQueryListModules );
00207 $this->InstantiateModules( $modules, 'meta', $this->mQueryMetaModules );
00208
00209 $cacheMode = 'public';
00210
00211
00212 if ( isset ( $this->params['generator'] ) ) {
00213 $generator = $this->newGenerator( $this->params['generator'] );
00214 $params = $generator->extractRequestParams();
00215 $cacheMode = $this->mergeCacheMode( $cacheMode,
00216 $generator->getCacheMode( $params ) );
00217 $this->executeGeneratorModule( $generator, $modules );
00218 } else {
00219
00220 $this->addCustomFldsToPageSet( $modules, $this->mPageSet );
00221 $this->mPageSet->execute();
00222 }
00223
00224
00225 $this->outputGeneralPageInfo();
00226
00227
00228 foreach ( $modules as $module ) {
00229 $params = $module->extractRequestParams();
00230 $cacheMode = $this->mergeCacheMode(
00231 $cacheMode, $module->getCacheMode( $params ) );
00232 $module->profileIn();
00233 $module->execute();
00234 wfRunHooks( 'APIQueryAfterExecute', array( &$module ) );
00235 $module->profileOut();
00236 }
00237
00238
00239 $this->getMain()->setCacheMode( $cacheMode );
00240 }
00241
00247 protected function mergeCacheMode( $cacheMode, $modCacheMode ) {
00248 if ( $modCacheMode === 'anon-public-user-private' ) {
00249 if ( $cacheMode !== 'private' ) {
00250 $cacheMode = 'anon-public-user-private';
00251 }
00252 } elseif ( $modCacheMode === 'public' ) {
00253
00254 } else {
00255 $cacheMode = 'private';
00256 }
00257 return $cacheMode;
00258 }
00259
00267 private function addCustomFldsToPageSet( $modules, $pageSet ) {
00268
00269 foreach ( $modules as $module ) {
00270 $module->requestExtraData( $pageSet );
00271 }
00272 }
00273
00280 private function InstantiateModules( &$modules, $param, $moduleList ) {
00281 $list = @$this->params[$param];
00282 if ( !is_null ( $list ) )
00283 foreach ( $list as $moduleName )
00284 $modules[] = new $moduleList[$moduleName] ( $this, $moduleName );
00285 }
00286
00292 private function outputGeneralPageInfo() {
00293
00294 $pageSet = $this->getPageSet();
00295 $result = $this->getResult();
00296
00297
00298
00299
00300
00301
00302 $normValues = array ();
00303 foreach ( $pageSet->getNormalizedTitles() as $rawTitleStr => $titleStr ) {
00304 $normValues[] = array (
00305 'from' => $rawTitleStr,
00306 'to' => $titleStr
00307 );
00308 }
00309
00310 if ( count( $normValues ) ) {
00311 $result->setIndexedTagName( $normValues, 'n' );
00312 $result->addValue( 'query', 'normalized', $normValues );
00313 }
00314
00315
00316 $intrwValues = array ();
00317 foreach ( $pageSet->getInterwikiTitles() as $rawTitleStr => $interwikiStr ) {
00318 $intrwValues[] = array (
00319 'title' => $rawTitleStr,
00320 'iw' => $interwikiStr
00321 );
00322 }
00323
00324 if ( count( $intrwValues ) ) {
00325 $result->setIndexedTagName( $intrwValues, 'i' );
00326 $result->addValue( 'query', 'interwiki', $intrwValues );
00327 }
00328
00329
00330 $redirValues = array ();
00331 foreach ( $pageSet->getRedirectTitles() as $titleStrFrom => $titleStrTo ) {
00332 $redirValues[] = array (
00333 'from' => strval( $titleStrFrom ),
00334 'to' => $titleStrTo
00335 );
00336 }
00337
00338 if ( count( $redirValues ) ) {
00339 $result->setIndexedTagName( $redirValues, 'r' );
00340 $result->addValue( 'query', 'redirects', $redirValues );
00341 }
00342
00343
00344
00345
00346 $missingRevIDs = $pageSet->getMissingRevisionIDs();
00347 if ( count( $missingRevIDs ) ) {
00348 $revids = array ();
00349 foreach ( $missingRevIDs as $revid ) {
00350 $revids[$revid] = array (
00351 'revid' => $revid
00352 );
00353 }
00354 $result->setIndexedTagName( $revids, 'rev' );
00355 $result->addValue( 'query', 'badrevids', $revids );
00356 }
00357
00358
00359
00360
00361 $pages = array ();
00362
00363
00364 foreach ( $pageSet->getMissingTitles() as $fakeId => $title ) {
00365 $vals = array();
00366 ApiQueryBase :: addTitleInfo( $vals, $title );
00367 $vals['missing'] = '';
00368 $pages[$fakeId] = $vals;
00369 }
00370
00371 foreach ( $pageSet->getInvalidTitles() as $fakeId => $title )
00372 $pages[$fakeId] = array( 'title' => $title, 'invalid' => '' );
00373
00374 foreach ( $pageSet->getMissingPageIDs() as $pageid ) {
00375 $pages[$pageid] = array (
00376 'pageid' => $pageid,
00377 'missing' => ''
00378 );
00379 }
00380
00381
00382 foreach ( $pageSet->getGoodTitles() as $pageid => $title ) {
00383 $vals = array();
00384 $vals['pageid'] = $pageid;
00385 ApiQueryBase :: addTitleInfo( $vals, $title );
00386 $pages[$pageid] = $vals;
00387 }
00388
00389 if ( count( $pages ) ) {
00390
00391 if ( $this->params['indexpageids'] ) {
00392 $pageIDs = array_keys( $pages );
00393
00394 $pageIDs = array_map( 'strval', $pageIDs );
00395 $result->setIndexedTagName( $pageIDs, 'id' );
00396 $result->addValue( 'query', 'pageids', $pageIDs );
00397 }
00398
00399 $result->setIndexedTagName( $pages, 'page' );
00400 $result->addValue( 'query', 'pages', $pages );
00401 }
00402 if ( $this->params['export'] ) {
00403 $exporter = new WikiExporter( $this->getDB() );
00404
00405
00406 ob_start();
00407 $exporter->openStream();
00408 foreach ( @$pageSet->getGoodTitles() as $title )
00409 if ( $title->userCanRead() )
00410 $exporter->pageByTitle( $title );
00411 $exporter->closeStream();
00412 $exportxml = ob_get_contents();
00413 ob_end_clean();
00414
00415
00416
00417
00418 $result->disableSizeCheck();
00419 if ( $this->params['exportnowrap'] ) {
00420 $result->reset();
00421
00422 $result->addValue( null, 'text', $exportxml );
00423 $result->addValue( null, 'mime', 'text/xml' );
00424 } else {
00425 $r = array();
00426 ApiResult::setContent( $r, $exportxml );
00427 $result->addValue( 'query', 'export', $r );
00428 }
00429 $result->enableSizeCheck();
00430 }
00431 }
00432
00436 public function newGenerator( $generatorName ) {
00437
00438
00439 if ( isset ( $this->mQueryListModules[$generatorName] ) ) {
00440 $className = $this->mQueryListModules[$generatorName];
00441 } elseif ( isset ( $this->mQueryPropModules[$generatorName] ) ) {
00442 $className = $this->mQueryPropModules[$generatorName];
00443 } else {
00444 ApiBase :: dieDebug( __METHOD__, "Unknown generator=$generatorName" );
00445 }
00446
00447
00448 $resultPageSet = new ApiPageSet( $this, $this->redirects );
00449
00450
00451 $generator = new $className ( $this, $generatorName );
00452 if ( !$generator instanceof ApiQueryGeneratorBase )
00453 $this->dieUsage( "Module $generatorName cannot be used as a generator", "badgenerator" );
00454 $generator->setGeneratorMode();
00455 return $generator;
00456 }
00457
00464 protected function executeGeneratorModule( $generator, $modules ) {
00465
00466 $resultPageSet = new ApiPageSet( $this, $this->redirects, $this->convertTitles );
00467
00468
00469 $generator->requestExtraData( $this->mPageSet );
00470 $this->addCustomFldsToPageSet( $modules, $resultPageSet );
00471
00472
00473 $this->mPageSet->execute();
00474
00475
00476 $generator->profileIn();
00477 $generator->executeGenerator( $resultPageSet );
00478 wfRunHooks( 'APIQueryGeneratorAfterExecute', array( &$generator, &$resultPageSet ) );
00479 $resultPageSet->finishPageSetGeneration();
00480 $generator->profileOut();
00481
00482
00483 $this->mPageSet = $resultPageSet;
00484 }
00485
00486 public function getAllowedParams() {
00487 return array (
00488 'prop' => array (
00489 ApiBase :: PARAM_ISMULTI => true,
00490 ApiBase :: PARAM_TYPE => $this->mPropModuleNames
00491 ),
00492 'list' => array (
00493 ApiBase :: PARAM_ISMULTI => true,
00494 ApiBase :: PARAM_TYPE => $this->mListModuleNames
00495 ),
00496 'meta' => array (
00497 ApiBase :: PARAM_ISMULTI => true,
00498 ApiBase :: PARAM_TYPE => $this->mMetaModuleNames
00499 ),
00500 'generator' => array (
00501 ApiBase :: PARAM_TYPE => $this->mAllowedGenerators
00502 ),
00503 'redirects' => false,
00504 'indexpageids' => false,
00505 'export' => false,
00506 'exportnowrap' => false,
00507 );
00508 }
00509
00514 public function makeHelpMsg() {
00515
00516 $msg = '';
00517
00518
00519
00520 $this->mPageSet = null;
00521 $this->mAllowedGenerators = array();
00522
00523 $astriks = str_repeat( '--- ', 8 );
00524 $astriks2 = str_repeat( '*** ', 10 );
00525 $msg .= "\n$astriks Query: Prop $astriks\n\n";
00526 $msg .= $this->makeHelpMsgHelper( $this->mQueryPropModules, 'prop' );
00527 $msg .= "\n$astriks Query: List $astriks\n\n";
00528 $msg .= $this->makeHelpMsgHelper( $this->mQueryListModules, 'list' );
00529 $msg .= "\n$astriks Query: Meta $astriks\n\n";
00530 $msg .= $this->makeHelpMsgHelper( $this->mQueryMetaModules, 'meta' );
00531 $msg .= "\n\n$astriks2 Modules: continuation $astriks2\n\n";
00532
00533
00534
00535
00536 $msg = parent :: makeHelpMsg() . $msg;
00537
00538 return $msg;
00539 }
00540
00547 private function makeHelpMsgHelper( $moduleList, $paramName ) {
00548
00549 $moduleDescriptions = array ();
00550
00551 foreach ( $moduleList as $moduleName => $moduleClass ) {
00552 $module = new $moduleClass ( $this, $moduleName, null );
00553
00554 $msg = ApiMain::makeHelpMsgHeader( $module, $paramName );
00555 $msg2 = $module->makeHelpMsg();
00556 if ( $msg2 !== false )
00557 $msg .= $msg2;
00558 if ( $module instanceof ApiQueryGeneratorBase ) {
00559 $this->mAllowedGenerators[] = $moduleName;
00560 $msg .= "Generator:\n This module may be used as a generator\n";
00561 }
00562 $moduleDescriptions[] = $msg;
00563 }
00564
00565 return implode( "\n", $moduleDescriptions );
00566 }
00567
00572 public function makeHelpMsgParameters() {
00573 $psModule = new ApiPageSet( $this );
00574 return $psModule->makeHelpMsgParameters() . parent :: makeHelpMsgParameters();
00575 }
00576
00577 public function shouldCheckMaxlag() {
00578 return true;
00579 }
00580
00581 public function getParamDescription() {
00582 return array (
00583 'prop' => 'Which properties to get for the titles/revisions/pageids',
00584 'list' => 'Which lists to get',
00585 'meta' => 'Which meta data to get about the site',
00586 'generator' => array( 'Use the output of a list as the input for other prop/list/meta items',
00587 'NOTE: generator parameter names must be prefixed with a \'g\', see examples.' ),
00588 'redirects' => 'Automatically resolve redirects',
00589 'indexpageids' => 'Include an additional pageids section listing all returned page IDs.',
00590 'export' => 'Export the current revisions of all given or generated pages',
00591 'exportnowrap' => 'Return the export XML without wrapping it in an XML result (same format as Special:Export). Can only be used with export',
00592 );
00593 }
00594
00595 public function getDescription() {
00596 return array (
00597 'Query API module allows applications to get needed pieces of data from the MediaWiki databases,',
00598 'and is loosely based on the old query.php interface.',
00599 'All data modifications will first have to use query to acquire a token to prevent abuse from malicious sites.'
00600 );
00601 }
00602
00603 public function getPossibleErrors() {
00604 return array_merge( parent::getPossibleErrors(), array(
00605 array( 'code' => 'badgenerator', 'info' => 'Module $generatorName cannot be used as a generator' ),
00606 ) );
00607 }
00608
00609 protected function getExamples() {
00610 return array (
00611 'api.php?action=query&prop=revisions&meta=siteinfo&titles=Main%20Page&rvprop=user|comment',
00612 'api.php?action=query&generator=allpages&gapprefix=API/&prop=revisions',
00613 );
00614 }
00615
00616 public function getVersion() {
00617 $psModule = new ApiPageSet( $this );
00618 $vers = array ();
00619 $vers[] = __CLASS__ . ': $Id: ApiQuery.php 69932 2010-07-26 08:03:21Z tstarling $';
00620 $vers[] = $psModule->getVersion();
00621 return $vers;
00622 }
00623 }