MediaWiki
REL1_20
|
00001 <?php 00038 class ApiQuery extends ApiBase { 00039 00040 private $mPropModuleNames, $mListModuleNames, $mMetaModuleNames; 00041 00045 private $mPageSet; 00046 00047 private $params, $redirects, $convertTitles, $iwUrl; 00048 00049 private $mQueryPropModules = array( 00050 'categories' => 'ApiQueryCategories', 00051 'categoryinfo' => 'ApiQueryCategoryInfo', 00052 'duplicatefiles' => 'ApiQueryDuplicateFiles', 00053 'extlinks' => 'ApiQueryExternalLinks', 00054 'images' => 'ApiQueryImages', 00055 'imageinfo' => 'ApiQueryImageInfo', 00056 'info' => 'ApiQueryInfo', 00057 'links' => 'ApiQueryLinks', 00058 'iwlinks' => 'ApiQueryIWLinks', 00059 'langlinks' => 'ApiQueryLangLinks', 00060 'pageprops' => 'ApiQueryPageProps', 00061 'revisions' => 'ApiQueryRevisions', 00062 'stashimageinfo' => 'ApiQueryStashImageInfo', 00063 'templates' => 'ApiQueryLinks', 00064 ); 00065 00066 private $mQueryListModules = array( 00067 'allcategories' => 'ApiQueryAllCategories', 00068 'allimages' => 'ApiQueryAllImages', 00069 'alllinks' => 'ApiQueryAllLinks', 00070 'allpages' => 'ApiQueryAllPages', 00071 'allusers' => 'ApiQueryAllUsers', 00072 'backlinks' => 'ApiQueryBacklinks', 00073 'blocks' => 'ApiQueryBlocks', 00074 'categorymembers' => 'ApiQueryCategoryMembers', 00075 'deletedrevs' => 'ApiQueryDeletedrevs', 00076 'embeddedin' => 'ApiQueryBacklinks', 00077 'exturlusage' => 'ApiQueryExtLinksUsage', 00078 'filearchive' => 'ApiQueryFilearchive', 00079 'imageusage' => 'ApiQueryBacklinks', 00080 'iwbacklinks' => 'ApiQueryIWBacklinks', 00081 'langbacklinks' => 'ApiQueryLangBacklinks', 00082 'logevents' => 'ApiQueryLogEvents', 00083 'protectedtitles' => 'ApiQueryProtectedTitles', 00084 'querypage' => 'ApiQueryQueryPage', 00085 'random' => 'ApiQueryRandom', 00086 'recentchanges' => 'ApiQueryRecentChanges', 00087 'search' => 'ApiQuerySearch', 00088 'tags' => 'ApiQueryTags', 00089 'usercontribs' => 'ApiQueryContributions', 00090 'users' => 'ApiQueryUsers', 00091 'watchlist' => 'ApiQueryWatchlist', 00092 'watchlistraw' => 'ApiQueryWatchlistRaw', 00093 ); 00094 00095 private $mQueryMetaModules = array( 00096 'allmessages' => 'ApiQueryAllMessages', 00097 'siteinfo' => 'ApiQuerySiteinfo', 00098 'userinfo' => 'ApiQueryUserInfo', 00099 ); 00100 00101 private $mSlaveDB = null; 00102 private $mNamedDB = array(); 00103 00104 protected $mAllowedGenerators = array(); 00105 00110 public function __construct( $main, $action ) { 00111 parent::__construct( $main, $action ); 00112 00113 // Allow custom modules to be added in LocalSettings.php 00114 global $wgAPIPropModules, $wgAPIListModules, $wgAPIMetaModules, 00115 $wgMemc, $wgAPICacheHelpTimeout; 00116 self::appendUserModules( $this->mQueryPropModules, $wgAPIPropModules ); 00117 self::appendUserModules( $this->mQueryListModules, $wgAPIListModules ); 00118 self::appendUserModules( $this->mQueryMetaModules, $wgAPIMetaModules ); 00119 00120 $this->mPropModuleNames = array_keys( $this->mQueryPropModules ); 00121 $this->mListModuleNames = array_keys( $this->mQueryListModules ); 00122 $this->mMetaModuleNames = array_keys( $this->mQueryMetaModules ); 00123 00124 // Get array of query generators from cache if present 00125 $key = wfMemcKey( 'apiquerygenerators', SpecialVersion::getVersion( 'nodb' ) ); 00126 00127 if ( $wgAPICacheHelpTimeout > 0 ) { 00128 $cached = $wgMemc->get( $key ); 00129 if ( $cached ) { 00130 $this->mAllowedGenerators = $cached; 00131 return; 00132 } 00133 } 00134 $this->makeGeneratorList( $this->mQueryPropModules ); 00135 $this->makeGeneratorList( $this->mQueryListModules ); 00136 00137 if ( $wgAPICacheHelpTimeout > 0 ) { 00138 $wgMemc->set( $key, $this->mAllowedGenerators, $wgAPICacheHelpTimeout ); 00139 } 00140 } 00141 00147 private static function appendUserModules( &$modules, $newModules ) { 00148 if ( is_array( $newModules ) ) { 00149 foreach ( $newModules as $moduleName => $moduleClass ) { 00150 $modules[$moduleName] = $moduleClass; 00151 } 00152 } 00153 } 00154 00159 public function getDB() { 00160 if ( !isset( $this->mSlaveDB ) ) { 00161 $this->profileDBIn(); 00162 $this->mSlaveDB = wfGetDB( DB_SLAVE, 'api' ); 00163 $this->profileDBOut(); 00164 } 00165 return $this->mSlaveDB; 00166 } 00167 00178 public function getNamedDB( $name, $db, $groups ) { 00179 if ( !array_key_exists( $name, $this->mNamedDB ) ) { 00180 $this->profileDBIn(); 00181 $this->mNamedDB[$name] = wfGetDB( $db, $groups ); 00182 $this->profileDBOut(); 00183 } 00184 return $this->mNamedDB[$name]; 00185 } 00186 00191 public function getPageSet() { 00192 return $this->mPageSet; 00193 } 00194 00199 function getModules() { 00200 return array_merge( $this->mQueryPropModules, $this->mQueryListModules, $this->mQueryMetaModules ); 00201 } 00202 00208 function getModuleType( $moduleName ) { 00209 if ( isset( $this->mQueryPropModules[$moduleName] ) ) { 00210 return 'prop'; 00211 } 00212 00213 if ( isset( $this->mQueryListModules[$moduleName] ) ) { 00214 return 'list'; 00215 } 00216 00217 if ( isset( $this->mQueryMetaModules[$moduleName] ) ) { 00218 return 'meta'; 00219 } 00220 00221 return null; 00222 } 00223 00227 public function getCustomPrinter() { 00228 // If &exportnowrap is set, use the raw formatter 00229 if ( $this->getParameter( 'export' ) && 00230 $this->getParameter( 'exportnowrap' ) ) 00231 { 00232 return new ApiFormatRaw( $this->getMain(), 00233 $this->getMain()->createPrinterByName( 'xml' ) ); 00234 } else { 00235 return null; 00236 } 00237 } 00238 00249 public function execute() { 00250 $this->params = $this->extractRequestParams(); 00251 $this->redirects = $this->params['redirects']; 00252 $this->convertTitles = $this->params['converttitles']; 00253 $this->iwUrl = $this->params['iwurl']; 00254 00255 // Create PageSet 00256 $this->mPageSet = new ApiPageSet( $this, $this->redirects, $this->convertTitles ); 00257 00258 // Instantiate requested modules 00259 $modules = array(); 00260 $this->instantiateModules( $modules, 'prop', $this->mQueryPropModules ); 00261 $this->instantiateModules( $modules, 'list', $this->mQueryListModules ); 00262 $this->instantiateModules( $modules, 'meta', $this->mQueryMetaModules ); 00263 00264 $cacheMode = 'public'; 00265 00266 // If given, execute generator to substitute user supplied data with generated data. 00267 if ( isset( $this->params['generator'] ) ) { 00268 $generator = $this->newGenerator( $this->params['generator'] ); 00269 $params = $generator->extractRequestParams(); 00270 $cacheMode = $this->mergeCacheMode( $cacheMode, 00271 $generator->getCacheMode( $params ) ); 00272 $this->executeGeneratorModule( $generator, $modules ); 00273 } else { 00274 // Append custom fields and populate page/revision information 00275 $this->addCustomFldsToPageSet( $modules, $this->mPageSet ); 00276 $this->mPageSet->execute(); 00277 } 00278 00279 // Record page information (title, namespace, if exists, etc) 00280 $this->outputGeneralPageInfo(); 00281 00282 // Execute all requested modules. 00286 foreach ( $modules as $module ) { 00287 $params = $module->extractRequestParams(); 00288 $cacheMode = $this->mergeCacheMode( 00289 $cacheMode, $module->getCacheMode( $params ) ); 00290 $module->profileIn(); 00291 $module->execute(); 00292 wfRunHooks( 'APIQueryAfterExecute', array( &$module ) ); 00293 $module->profileOut(); 00294 } 00295 00296 // Set the cache mode 00297 $this->getMain()->setCacheMode( $cacheMode ); 00298 } 00299 00309 protected function mergeCacheMode( $cacheMode, $modCacheMode ) { 00310 if ( $modCacheMode === 'anon-public-user-private' ) { 00311 if ( $cacheMode !== 'private' ) { 00312 $cacheMode = 'anon-public-user-private'; 00313 } 00314 } elseif ( $modCacheMode === 'public' ) { 00315 // do nothing, if it's public already it will stay public 00316 } else { // private 00317 $cacheMode = 'private'; 00318 } 00319 return $cacheMode; 00320 } 00321 00329 private function addCustomFldsToPageSet( $modules, $pageSet ) { 00330 // Query all requested modules. 00334 foreach ( $modules as $module ) { 00335 $module->requestExtraData( $pageSet ); 00336 } 00337 } 00338 00345 private function instantiateModules( &$modules, $param, $moduleList ) { 00346 if ( isset( $this->params[$param] ) ) { 00347 foreach ( $this->params[$param] as $moduleName ) { 00348 $modules[] = new $moduleList[$moduleName] ( $this, $moduleName ); 00349 } 00350 } 00351 } 00352 00358 private function outputGeneralPageInfo() { 00359 $pageSet = $this->getPageSet(); 00360 $result = $this->getResult(); 00361 00362 // We don't check for a full result set here because we can't be adding 00363 // more than 380K. The maximum revision size is in the megabyte range, 00364 // and the maximum result size must be even higher than that. 00365 00366 // Title normalizations 00367 $normValues = array(); 00368 foreach ( $pageSet->getNormalizedTitles() as $rawTitleStr => $titleStr ) { 00369 $normValues[] = array( 00370 'from' => $rawTitleStr, 00371 'to' => $titleStr 00372 ); 00373 } 00374 00375 if ( count( $normValues ) ) { 00376 $result->setIndexedTagName( $normValues, 'n' ); 00377 $result->addValue( 'query', 'normalized', $normValues ); 00378 } 00379 00380 // Title conversions 00381 $convValues = array(); 00382 foreach ( $pageSet->getConvertedTitles() as $rawTitleStr => $titleStr ) { 00383 $convValues[] = array( 00384 'from' => $rawTitleStr, 00385 'to' => $titleStr 00386 ); 00387 } 00388 00389 if ( count( $convValues ) ) { 00390 $result->setIndexedTagName( $convValues, 'c' ); 00391 $result->addValue( 'query', 'converted', $convValues ); 00392 } 00393 00394 // Interwiki titles 00395 $intrwValues = array(); 00396 foreach ( $pageSet->getInterwikiTitles() as $rawTitleStr => $interwikiStr ) { 00397 $item = array( 00398 'title' => $rawTitleStr, 00399 'iw' => $interwikiStr, 00400 ); 00401 if ( $this->iwUrl ) { 00402 $title = Title::newFromText( $rawTitleStr ); 00403 $item['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT ); 00404 } 00405 $intrwValues[] = $item; 00406 } 00407 00408 if ( count( $intrwValues ) ) { 00409 $result->setIndexedTagName( $intrwValues, 'i' ); 00410 $result->addValue( 'query', 'interwiki', $intrwValues ); 00411 } 00412 00413 // Show redirect information 00414 $redirValues = array(); 00418 foreach ( $pageSet->getRedirectTitles() as $titleStrFrom => $titleTo ) { 00419 $r = array( 00420 'from' => strval( $titleStrFrom ), 00421 'to' => $titleTo->getPrefixedText(), 00422 ); 00423 if ( $titleTo->getFragment() !== '' ) { 00424 $r['tofragment'] = $titleTo->getFragment(); 00425 } 00426 $redirValues[] = $r; 00427 } 00428 00429 if ( count( $redirValues ) ) { 00430 $result->setIndexedTagName( $redirValues, 'r' ); 00431 $result->addValue( 'query', 'redirects', $redirValues ); 00432 } 00433 00434 // Missing revision elements 00435 $missingRevIDs = $pageSet->getMissingRevisionIDs(); 00436 if ( count( $missingRevIDs ) ) { 00437 $revids = array(); 00438 foreach ( $missingRevIDs as $revid ) { 00439 $revids[$revid] = array( 00440 'revid' => $revid 00441 ); 00442 } 00443 $result->setIndexedTagName( $revids, 'rev' ); 00444 $result->addValue( 'query', 'badrevids', $revids ); 00445 } 00446 00447 // Page elements 00448 $pages = array(); 00449 00450 // Report any missing titles 00451 foreach ( $pageSet->getMissingTitles() as $fakeId => $title ) { 00452 $vals = array(); 00453 ApiQueryBase::addTitleInfo( $vals, $title ); 00454 $vals['missing'] = ''; 00455 $pages[$fakeId] = $vals; 00456 } 00457 // Report any invalid titles 00458 foreach ( $pageSet->getInvalidTitles() as $fakeId => $title ) { 00459 $pages[$fakeId] = array( 'title' => $title, 'invalid' => '' ); 00460 } 00461 // Report any missing page ids 00462 foreach ( $pageSet->getMissingPageIDs() as $pageid ) { 00463 $pages[$pageid] = array( 00464 'pageid' => $pageid, 00465 'missing' => '' 00466 ); 00467 } 00468 // Report special pages 00469 foreach ( $pageSet->getSpecialTitles() as $fakeId => $title ) { 00470 $vals = array(); 00471 ApiQueryBase::addTitleInfo( $vals, $title ); 00472 $vals['special'] = ''; 00473 if ( $title->isSpecialPage() && 00474 !SpecialPageFactory::exists( $title->getDbKey() ) ) { 00475 $vals['missing'] = ''; 00476 } elseif ( $title->getNamespace() == NS_MEDIA && 00477 !wfFindFile( $title ) ) { 00478 $vals['missing'] = ''; 00479 } 00480 $pages[$fakeId] = $vals; 00481 } 00482 00483 // Output general page information for found titles 00484 foreach ( $pageSet->getGoodTitles() as $pageid => $title ) { 00485 $vals = array(); 00486 $vals['pageid'] = $pageid; 00487 ApiQueryBase::addTitleInfo( $vals, $title ); 00488 $pages[$pageid] = $vals; 00489 } 00490 00491 if ( count( $pages ) ) { 00492 if ( $this->params['indexpageids'] ) { 00493 $pageIDs = array_keys( $pages ); 00494 // json treats all map keys as strings - converting to match 00495 $pageIDs = array_map( 'strval', $pageIDs ); 00496 $result->setIndexedTagName( $pageIDs, 'id' ); 00497 $result->addValue( 'query', 'pageids', $pageIDs ); 00498 } 00499 00500 $result->setIndexedTagName( $pages, 'page' ); 00501 $result->addValue( 'query', 'pages', $pages ); 00502 } 00503 if ( $this->params['export'] ) { 00504 $this->doExport( $pageSet, $result ); 00505 } 00506 } 00507 00512 private function doExport( $pageSet, $result ) { 00513 $exportTitles = array(); 00514 $titles = $pageSet->getGoodTitles(); 00515 if ( count( $titles ) ) { 00516 foreach ( $titles as $title ) { 00517 if ( $title->userCan( 'read' ) ) { 00518 $exportTitles[] = $title; 00519 } 00520 } 00521 } 00522 00523 $exporter = new WikiExporter( $this->getDB() ); 00524 // WikiExporter writes to stdout, so catch its 00525 // output with an ob 00526 ob_start(); 00527 $exporter->openStream(); 00528 foreach ( $exportTitles as $title ) { 00529 $exporter->pageByTitle( $title ); 00530 } 00531 $exporter->closeStream(); 00532 $exportxml = ob_get_contents(); 00533 ob_end_clean(); 00534 00535 // Don't check the size of exported stuff 00536 // It's not continuable, so it would cause more 00537 // problems than it'd solve 00538 $result->disableSizeCheck(); 00539 if ( $this->params['exportnowrap'] ) { 00540 $result->reset(); 00541 // Raw formatter will handle this 00542 $result->addValue( null, 'text', $exportxml ); 00543 $result->addValue( null, 'mime', 'text/xml' ); 00544 } else { 00545 $r = array(); 00546 ApiResult::setContent( $r, $exportxml ); 00547 $result->addValue( 'query', 'export', $r ); 00548 } 00549 $result->enableSizeCheck(); 00550 } 00551 00557 public function newGenerator( $generatorName ) { 00558 // Find class that implements requested generator 00559 if ( isset( $this->mQueryListModules[$generatorName] ) ) { 00560 $className = $this->mQueryListModules[$generatorName]; 00561 } elseif ( isset( $this->mQueryPropModules[$generatorName] ) ) { 00562 $className = $this->mQueryPropModules[$generatorName]; 00563 } else { 00564 ApiBase::dieDebug( __METHOD__, "Unknown generator=$generatorName" ); 00565 } 00566 $generator = new $className ( $this, $generatorName ); 00567 if ( !$generator instanceof ApiQueryGeneratorBase ) { 00568 $this->dieUsage( "Module $generatorName cannot be used as a generator", 'badgenerator' ); 00569 } 00570 $generator->setGeneratorMode(); 00571 return $generator; 00572 } 00573 00580 protected function executeGeneratorModule( $generator, $modules ) { 00581 // Generator results 00582 $resultPageSet = new ApiPageSet( $this, $this->redirects, $this->convertTitles ); 00583 00584 // Add any additional fields modules may need 00585 $generator->requestExtraData( $this->mPageSet ); 00586 $this->addCustomFldsToPageSet( $modules, $resultPageSet ); 00587 00588 // Populate page information with the original user input 00589 $this->mPageSet->execute(); 00590 00591 // populate resultPageSet with the generator output 00592 $generator->profileIn(); 00593 $generator->executeGenerator( $resultPageSet ); 00594 wfRunHooks( 'APIQueryGeneratorAfterExecute', array( &$generator, &$resultPageSet ) ); 00595 $resultPageSet->finishPageSetGeneration(); 00596 $generator->profileOut(); 00597 00598 // Swap the resulting pageset back in 00599 $this->mPageSet = $resultPageSet; 00600 } 00601 00602 public function getAllowedParams() { 00603 return array( 00604 'prop' => array( 00605 ApiBase::PARAM_ISMULTI => true, 00606 ApiBase::PARAM_TYPE => $this->mPropModuleNames 00607 ), 00608 'list' => array( 00609 ApiBase::PARAM_ISMULTI => true, 00610 ApiBase::PARAM_TYPE => $this->mListModuleNames 00611 ), 00612 'meta' => array( 00613 ApiBase::PARAM_ISMULTI => true, 00614 ApiBase::PARAM_TYPE => $this->mMetaModuleNames 00615 ), 00616 'generator' => array( 00617 ApiBase::PARAM_TYPE => $this->mAllowedGenerators 00618 ), 00619 'redirects' => false, 00620 'converttitles' => false, 00621 'indexpageids' => false, 00622 'export' => false, 00623 'exportnowrap' => false, 00624 'iwurl' => false, 00625 ); 00626 } 00627 00632 public function makeHelpMsg() { 00633 // Make sure the internal object is empty 00634 // (just in case a sub-module decides to optimize during instantiation) 00635 $this->mPageSet = null; 00636 00637 $querySeparator = str_repeat( '--- ', 12 ); 00638 $moduleSeparator = str_repeat( '*** ', 14 ); 00639 $msg = "\n$querySeparator Query: Prop $querySeparator\n\n"; 00640 $msg .= $this->makeHelpMsgHelper( $this->mQueryPropModules, 'prop' ); 00641 $msg .= "\n$querySeparator Query: List $querySeparator\n\n"; 00642 $msg .= $this->makeHelpMsgHelper( $this->mQueryListModules, 'list' ); 00643 $msg .= "\n$querySeparator Query: Meta $querySeparator\n\n"; 00644 $msg .= $this->makeHelpMsgHelper( $this->mQueryMetaModules, 'meta' ); 00645 $msg .= "\n\n$moduleSeparator Modules: continuation $moduleSeparator\n\n"; 00646 00647 // Use parent to make default message for the query module 00648 $msg = parent::makeHelpMsg() . $msg; 00649 00650 return $msg; 00651 } 00652 00659 private function makeHelpMsgHelper( $moduleList, $paramName ) { 00660 $moduleDescriptions = array(); 00661 00662 foreach ( $moduleList as $moduleName => $moduleClass ) { 00666 $module = new $moduleClass( $this, $moduleName, null ); 00667 00668 $msg = ApiMain::makeHelpMsgHeader( $module, $paramName ); 00669 $msg2 = $module->makeHelpMsg(); 00670 if ( $msg2 !== false ) { 00671 $msg .= $msg2; 00672 } 00673 if ( $module instanceof ApiQueryGeneratorBase ) { 00674 $msg .= "Generator:\n This module may be used as a generator\n"; 00675 } 00676 $moduleDescriptions[] = $msg; 00677 } 00678 00679 return implode( "\n", $moduleDescriptions ); 00680 } 00681 00687 private function makeGeneratorList( $moduleList ) { 00688 foreach( $moduleList as $moduleName => $moduleClass ) { 00689 if ( is_subclass_of( $moduleClass, 'ApiQueryGeneratorBase' ) ) { 00690 $this->mAllowedGenerators[] = $moduleName; 00691 } 00692 } 00693 } 00694 00699 public function makeHelpMsgParameters() { 00700 $psModule = new ApiPageSet( $this ); 00701 return $psModule->makeHelpMsgParameters() . parent::makeHelpMsgParameters(); 00702 } 00703 00704 public function shouldCheckMaxlag() { 00705 return true; 00706 } 00707 00708 public function getParamDescription() { 00709 return array( 00710 'prop' => 'Which properties to get for the titles/revisions/pageids. Module help is available below', 00711 'list' => 'Which lists to get. Module help is available below', 00712 'meta' => 'Which metadata to get about the site. Module help is available below', 00713 'generator' => array( 'Use the output of a list as the input for other prop/list/meta items', 00714 'NOTE: generator parameter names must be prefixed with a \'g\', see examples' ), 00715 'redirects' => 'Automatically resolve redirects', 00716 'converttitles' => array( "Convert titles to other variants if necessary. Only works if the wiki's content language supports variant conversion.", 00717 'Languages that support variant conversion include ' . implode( ', ', LanguageConverter::$languagesWithVariants ) ), 00718 'indexpageids' => 'Include an additional pageids section listing all returned page IDs', 00719 'export' => 'Export the current revisions of all given or generated pages', 00720 'exportnowrap' => 'Return the export XML without wrapping it in an XML result (same format as Special:Export). Can only be used with export', 00721 'iwurl' => 'Whether to get the full URL if the title is an interwiki link', 00722 ); 00723 } 00724 00725 public function getDescription() { 00726 return array( 00727 'Query API module allows applications to get needed pieces of data from the MediaWiki databases,', 00728 'and is loosely based on the old query.php interface.', 00729 'All data modifications will first have to use query to acquire a token to prevent abuse from malicious sites' 00730 ); 00731 } 00732 00733 public function getPossibleErrors() { 00734 return array_merge( parent::getPossibleErrors(), array( 00735 array( 'code' => 'badgenerator', 'info' => 'Module $generatorName cannot be used as a generator' ), 00736 ) ); 00737 } 00738 00739 public function getExamples() { 00740 return array( 00741 'api.php?action=query&prop=revisions&meta=siteinfo&titles=Main%20Page&rvprop=user|comment', 00742 'api.php?action=query&generator=allpages&gapprefix=API/&prop=revisions', 00743 ); 00744 } 00745 00746 public function getHelpUrls() { 00747 return array( 00748 'https://www.mediawiki.org/wiki/API:Meta', 00749 'https://www.mediawiki.org/wiki/API:Properties', 00750 'https://www.mediawiki.org/wiki/API:Lists', 00751 ); 00752 } 00753 00754 public function getVersion() { 00755 $psModule = new ApiPageSet( $this ); 00756 $vers = array(); 00757 $vers[] = __CLASS__ . ': $Id$'; 00758 $vers[] = $psModule->getVersion(); 00759 return $vers; 00760 } 00761 }