MediaWiki
REL1_19
|
00001 <?php 00032 class ApiQuerySiteinfo extends ApiQueryBase { 00033 00034 public function __construct( $query, $moduleName ) { 00035 parent::__construct( $query, $moduleName, 'si' ); 00036 } 00037 00038 public function execute() { 00039 $params = $this->extractRequestParams(); 00040 $done = array(); 00041 $fit = false; 00042 foreach ( $params['prop'] as $p ) { 00043 switch ( $p ) { 00044 case 'general': 00045 $fit = $this->appendGeneralInfo( $p ); 00046 break; 00047 case 'namespaces': 00048 $fit = $this->appendNamespaces( $p ); 00049 break; 00050 case 'namespacealiases': 00051 $fit = $this->appendNamespaceAliases( $p ); 00052 break; 00053 case 'specialpagealiases': 00054 $fit = $this->appendSpecialPageAliases( $p ); 00055 break; 00056 case 'magicwords': 00057 $fit = $this->appendMagicWords( $p ); 00058 break; 00059 case 'interwikimap': 00060 $filteriw = isset( $params['filteriw'] ) ? $params['filteriw'] : false; 00061 $fit = $this->appendInterwikiMap( $p, $filteriw ); 00062 break; 00063 case 'dbrepllag': 00064 $fit = $this->appendDbReplLagInfo( $p, $params['showalldb'] ); 00065 break; 00066 case 'statistics': 00067 $fit = $this->appendStatistics( $p ); 00068 break; 00069 case 'usergroups': 00070 $fit = $this->appendUserGroups( $p, $params['numberingroup'] ); 00071 break; 00072 case 'extensions': 00073 $fit = $this->appendExtensions( $p ); 00074 break; 00075 case 'fileextensions': 00076 $fit = $this->appendFileExtensions( $p ); 00077 break; 00078 case 'rightsinfo': 00079 $fit = $this->appendRightsInfo( $p ); 00080 break; 00081 case 'languages': 00082 $fit = $this->appendLanguages( $p ); 00083 break; 00084 case 'skins': 00085 $fit = $this->appendSkins( $p ); 00086 break; 00087 case 'extensiontags': 00088 $fit = $this->appendExtensionTags( $p ); 00089 break; 00090 case 'functionhooks': 00091 $fit = $this->appendFunctionHooks( $p ); 00092 break; 00093 case 'showhooks': 00094 $fit = $this->appendSubscribedHooks( $p ); 00095 break; 00096 default: 00097 ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" ); 00098 } 00099 if ( !$fit ) { 00100 // Abuse siprop as a query-continue parameter 00101 // and set it to all unprocessed props 00102 $this->setContinueEnumParameter( 'prop', implode( '|', 00103 array_diff( $params['prop'], $done ) ) ); 00104 break; 00105 } 00106 $done[] = $p; 00107 } 00108 } 00109 00110 protected function appendGeneralInfo( $property ) { 00111 global $wgContLang; 00112 00113 $data = array(); 00114 $mainPage = Title::newMainPage(); 00115 $data['mainpage'] = $mainPage->getPrefixedText(); 00116 $data['base'] = wfExpandUrl( $mainPage->getFullUrl(), PROTO_CURRENT ); 00117 $data['sitename'] = $GLOBALS['wgSitename']; 00118 $data['generator'] = "MediaWiki {$GLOBALS['wgVersion']}"; 00119 $data['phpversion'] = phpversion(); 00120 $data['phpsapi'] = php_sapi_name(); 00121 $data['dbtype'] = $GLOBALS['wgDBtype']; 00122 $data['dbversion'] = $this->getDB()->getServerVersion(); 00123 00124 $svn = SpecialVersion::getSvnRevision( $GLOBALS['IP'] ); 00125 if ( $svn ) { 00126 $data['rev'] = $svn; 00127 } 00128 00129 // 'case-insensitive' option is reserved for future 00130 $data['case'] = $GLOBALS['wgCapitalLinks'] ? 'first-letter' : 'case-sensitive'; 00131 00132 if ( isset( $GLOBALS['wgRightsCode'] ) ) { 00133 $data['rightscode'] = $GLOBALS['wgRightsCode']; 00134 } 00135 $data['rights'] = $GLOBALS['wgRightsText']; 00136 $data['lang'] = $GLOBALS['wgLanguageCode']; 00137 00138 $fallbacks = array(); 00139 foreach( $wgContLang->getFallbackLanguages() as $code ) { 00140 $fallbacks[] = array( 'code' => $code ); 00141 } 00142 $data['fallback'] = $fallbacks; 00143 $this->getResult()->setIndexedTagName( $data['fallback'], 'lang' ); 00144 00145 if ( $wgContLang->isRTL() ) { 00146 $data['rtl'] = ''; 00147 } 00148 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding(); 00149 00150 if ( wfReadOnly() ) { 00151 $data['readonly'] = ''; 00152 $data['readonlyreason'] = wfReadOnlyReason(); 00153 } 00154 if ( $GLOBALS['wgEnableWriteAPI'] ) { 00155 $data['writeapi'] = ''; 00156 } 00157 00158 $tz = $GLOBALS['wgLocaltimezone']; 00159 $offset = $GLOBALS['wgLocalTZoffset']; 00160 if ( is_null( $tz ) ) { 00161 $tz = 'UTC'; 00162 $offset = 0; 00163 } elseif ( is_null( $offset ) ) { 00164 $offset = 0; 00165 } 00166 $data['timezone'] = $tz; 00167 $data['timeoffset'] = intval( $offset ); 00168 $data['articlepath'] = $GLOBALS['wgArticlePath']; 00169 $data['scriptpath'] = $GLOBALS['wgScriptPath']; 00170 $data['script'] = $GLOBALS['wgScript']; 00171 $data['variantarticlepath'] = $GLOBALS['wgVariantArticlePath']; 00172 $data['server'] = $GLOBALS['wgServer']; 00173 $data['wikiid'] = wfWikiID(); 00174 $data['time'] = wfTimestamp( TS_ISO_8601, time() ); 00175 00176 if ( $GLOBALS['wgMiserMode'] ) { 00177 $data['misermode'] = ''; 00178 } 00179 00180 wfRunHooks( 'APIQuerySiteInfoGeneralInfo', array( $this, &$data ) ); 00181 00182 return $this->getResult()->addValue( 'query', $property, $data ); 00183 } 00184 00185 protected function appendNamespaces( $property ) { 00186 global $wgContLang; 00187 $data = array(); 00188 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) { 00189 $data[$ns] = array( 00190 'id' => intval( $ns ), 00191 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive', 00192 ); 00193 ApiResult::setContent( $data[$ns], $title ); 00194 $canonical = MWNamespace::getCanonicalName( $ns ); 00195 00196 if ( MWNamespace::hasSubpages( $ns ) ) { 00197 $data[$ns]['subpages'] = ''; 00198 } 00199 00200 if ( $canonical ) { 00201 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' ); 00202 } 00203 00204 if ( MWNamespace::isContent( $ns ) ) { 00205 $data[$ns]['content'] = ''; 00206 } 00207 } 00208 00209 $this->getResult()->setIndexedTagName( $data, 'ns' ); 00210 return $this->getResult()->addValue( 'query', $property, $data ); 00211 } 00212 00213 protected function appendNamespaceAliases( $property ) { 00214 global $wgNamespaceAliases, $wgContLang; 00215 $aliases = array_merge( $wgNamespaceAliases, $wgContLang->getNamespaceAliases() ); 00216 $namespaces = $wgContLang->getNamespaces(); 00217 $data = array(); 00218 foreach ( $aliases as $title => $ns ) { 00219 if ( $namespaces[$ns] == $title ) { 00220 // Don't list duplicates 00221 continue; 00222 } 00223 $item = array( 00224 'id' => intval( $ns ) 00225 ); 00226 ApiResult::setContent( $item, strtr( $title, '_', ' ' ) ); 00227 $data[] = $item; 00228 } 00229 00230 $this->getResult()->setIndexedTagName( $data, 'ns' ); 00231 return $this->getResult()->addValue( 'query', $property, $data ); 00232 } 00233 00234 protected function appendSpecialPageAliases( $property ) { 00235 global $wgContLang; 00236 $data = array(); 00237 foreach ( $wgContLang->getSpecialPageAliases() as $specialpage => $aliases ) { 00238 $arr = array( 'realname' => $specialpage, 'aliases' => $aliases ); 00239 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' ); 00240 $data[] = $arr; 00241 } 00242 $this->getResult()->setIndexedTagName( $data, 'specialpage' ); 00243 return $this->getResult()->addValue( 'query', $property, $data ); 00244 } 00245 00246 protected function appendMagicWords( $property ) { 00247 global $wgContLang; 00248 $data = array(); 00249 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) { 00250 $caseSensitive = array_shift( $aliases ); 00251 $arr = array( 'name' => $magicword, 'aliases' => $aliases ); 00252 if ( $caseSensitive ) { 00253 $arr['case-sensitive'] = ''; 00254 } 00255 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' ); 00256 $data[] = $arr; 00257 } 00258 $this->getResult()->setIndexedTagName( $data, 'magicword' ); 00259 return $this->getResult()->addValue( 'query', $property, $data ); 00260 } 00261 00262 protected function appendInterwikiMap( $property, $filter ) { 00263 $local = null; 00264 if ( $filter === 'local' ) { 00265 $local = 1; 00266 } elseif ( $filter === '!local' ) { 00267 $local = 0; 00268 } elseif ( $filter ) { 00269 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" ); 00270 } 00271 00272 $params = $this->extractRequestParams(); 00273 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : ''; 00274 00275 if( $langCode ) { 00276 $langNames = Language::getTranslatedLanguageNames( $langCode ); 00277 } else { 00278 $langNames = Language::getLanguageNames(); 00279 } 00280 00281 $getPrefixes = Interwiki::getAllPrefixes( $local ); 00282 $data = array(); 00283 00284 foreach ( $getPrefixes as $row ) { 00285 $prefix = $row['iw_prefix']; 00286 $val = array(); 00287 $val['prefix'] = $prefix; 00288 if ( $row['iw_local'] == '1' ) { 00289 $val['local'] = ''; 00290 } 00291 // $val['trans'] = intval( $row['iw_trans'] ); // should this be exposed? 00292 if ( isset( $langNames[$prefix] ) ) { 00293 $val['language'] = $langNames[$prefix]; 00294 } 00295 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT ); 00296 if( isset( $row['iw_wikiid'] ) ) { 00297 $val['wikiid'] = $row['iw_wikiid']; 00298 } 00299 if( isset( $row['iw_api'] ) ) { 00300 $val['api'] = $row['iw_api']; 00301 } 00302 00303 $data[] = $val; 00304 } 00305 00306 $this->getResult()->setIndexedTagName( $data, 'iw' ); 00307 return $this->getResult()->addValue( 'query', $property, $data ); 00308 } 00309 00310 protected function appendDbReplLagInfo( $property, $includeAll ) { 00311 global $wgShowHostnames; 00312 $data = array(); 00313 $lb = wfGetLB(); 00314 if ( $includeAll ) { 00315 if ( !$wgShowHostnames ) { 00316 $this->dieUsage( 'Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied' ); 00317 } 00318 00319 $lags = $lb->getLagTimes(); 00320 foreach ( $lags as $i => $lag ) { 00321 $data[] = array( 00322 'host' => $lb->getServerName( $i ), 00323 'lag' => $lag 00324 ); 00325 } 00326 } else { 00327 list( $host, $lag, $index ) = $lb->getMaxLag(); 00328 $data[] = array( 00329 'host' => $wgShowHostnames 00330 ? $lb->getServerName( $index ) 00331 : '', 00332 'lag' => intval( $lag ) 00333 ); 00334 } 00335 00336 $result = $this->getResult(); 00337 $result->setIndexedTagName( $data, 'db' ); 00338 return $this->getResult()->addValue( 'query', $property, $data ); 00339 } 00340 00341 protected function appendStatistics( $property ) { 00342 global $wgDisableCounters; 00343 $data = array(); 00344 $data['pages'] = intval( SiteStats::pages() ); 00345 $data['articles'] = intval( SiteStats::articles() ); 00346 if ( !$wgDisableCounters ) { 00347 $data['views'] = intval( SiteStats::views() ); 00348 } 00349 $data['edits'] = intval( SiteStats::edits() ); 00350 $data['images'] = intval( SiteStats::images() ); 00351 $data['users'] = intval( SiteStats::users() ); 00352 $data['activeusers'] = intval( SiteStats::activeUsers() ); 00353 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) ); 00354 $data['jobs'] = intval( SiteStats::jobs() ); 00355 return $this->getResult()->addValue( 'query', $property, $data ); 00356 } 00357 00358 protected function appendUserGroups( $property, $numberInGroup ) { 00359 global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf; 00360 00361 $data = array(); 00362 $result = $this->getResult(); 00363 foreach ( $wgGroupPermissions as $group => $permissions ) { 00364 $arr = array( 00365 'name' => $group, 00366 'rights' => array_keys( $permissions, true ), 00367 ); 00368 00369 if ( $numberInGroup ) { 00370 global $wgAutopromote; 00371 00372 if ( $group == 'user' ) { 00373 $arr['number'] = SiteStats::users(); 00374 00375 // '*' and autopromote groups have no size 00376 } elseif ( $group !== '*' && !isset( $wgAutopromote[$group] ) ) { 00377 $arr['number'] = SiteStats::numberInGroup( $group ); 00378 } 00379 } 00380 00381 $groupArr = array( 00382 'add' => $wgAddGroups, 00383 'remove' => $wgRemoveGroups, 00384 'add-self' => $wgGroupsAddToSelf, 00385 'remove-self' => $wgGroupsRemoveFromSelf 00386 ); 00387 00388 foreach ( $groupArr as $type => $rights ) { 00389 if ( isset( $rights[$group] ) ) { 00390 $arr[$type] = $rights[$group]; 00391 $result->setIndexedTagName( $arr[$type], 'group' ); 00392 } 00393 } 00394 00395 $result->setIndexedTagName( $arr['rights'], 'permission' ); 00396 $data[] = $arr; 00397 } 00398 00399 $result->setIndexedTagName( $data, 'group' ); 00400 return $result->addValue( 'query', $property, $data ); 00401 } 00402 00403 protected function appendFileExtensions( $property ) { 00404 global $wgFileExtensions; 00405 00406 $data = array(); 00407 foreach ( $wgFileExtensions as $ext ) { 00408 $data[] = array( 'ext' => $ext ); 00409 } 00410 $this->getResult()->setIndexedTagName( $data, 'fe' ); 00411 return $this->getResult()->addValue( 'query', $property, $data ); 00412 } 00413 00414 protected function appendExtensions( $property ) { 00415 global $wgExtensionCredits; 00416 $data = array(); 00417 foreach ( $wgExtensionCredits as $type => $extensions ) { 00418 foreach ( $extensions as $ext ) { 00419 $ret = array(); 00420 $ret['type'] = $type; 00421 if ( isset( $ext['name'] ) ) { 00422 $ret['name'] = $ext['name']; 00423 } 00424 if ( isset( $ext['description'] ) ) { 00425 $ret['description'] = $ext['description']; 00426 } 00427 if ( isset( $ext['descriptionmsg'] ) ) { 00428 // Can be a string or array( key, param1, param2, ... ) 00429 if ( is_array( $ext['descriptionmsg'] ) ) { 00430 $ret['descriptionmsg'] = $ext['descriptionmsg'][0]; 00431 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 ); 00432 $this->getResult()->setIndexedTagName( $ret['descriptionmsgparams'], 'param' ); 00433 } else { 00434 $ret['descriptionmsg'] = $ext['descriptionmsg']; 00435 } 00436 } 00437 if ( isset( $ext['author'] ) ) { 00438 $ret['author'] = is_array( $ext['author'] ) ? 00439 implode( ', ', $ext['author' ] ) : $ext['author']; 00440 } 00441 if ( isset( $ext['url'] ) ) { 00442 $ret['url'] = $ext['url']; 00443 } 00444 if ( isset( $ext['version'] ) ) { 00445 $ret['version'] = $ext['version']; 00446 } elseif ( isset( $ext['svn-revision'] ) && 00447 preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/', 00448 $ext['svn-revision'], $m ) ) 00449 { 00450 $ret['version'] = 'r' . $m[1]; 00451 } 00452 $data[] = $ret; 00453 } 00454 } 00455 00456 $this->getResult()->setIndexedTagName( $data, 'ext' ); 00457 return $this->getResult()->addValue( 'query', $property, $data ); 00458 } 00459 00460 protected function appendRightsInfo( $property ) { 00461 global $wgRightsPage, $wgRightsUrl, $wgRightsText; 00462 $title = Title::newFromText( $wgRightsPage ); 00463 $url = $title ? wfExpandUrl( $title->getFullURL(), PROTO_CURRENT ) : $wgRightsUrl; 00464 $text = $wgRightsText; 00465 if ( !$text && $title ) { 00466 $text = $title->getPrefixedText(); 00467 } 00468 00469 $data = array( 00470 'url' => $url ? $url : '', 00471 'text' => $text ? $text : '' 00472 ); 00473 00474 return $this->getResult()->addValue( 'query', $property, $data ); 00475 } 00476 00477 public function appendLanguages( $property ) { 00478 $params = $this->extractRequestParams(); 00479 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : ''; 00480 00481 if( $langCode ) { 00482 $langNames = Language::getTranslatedLanguageNames( $langCode ); 00483 } else { 00484 $langNames = Language::getLanguageNames(); 00485 } 00486 00487 $data = array(); 00488 00489 foreach ( $langNames as $code => $name ) { 00490 $lang = array( 'code' => $code ); 00491 ApiResult::setContent( $lang, $name ); 00492 $data[] = $lang; 00493 } 00494 $this->getResult()->setIndexedTagName( $data, 'lang' ); 00495 return $this->getResult()->addValue( 'query', $property, $data ); 00496 } 00497 00498 public function appendSkins( $property ) { 00499 $data = array(); 00500 foreach ( Skin::getSkinNames() as $name => $displayName ) { 00501 $skin = array( 'code' => $name ); 00502 ApiResult::setContent( $skin, $displayName ); 00503 $data[] = $skin; 00504 } 00505 $this->getResult()->setIndexedTagName( $data, 'skin' ); 00506 return $this->getResult()->addValue( 'query', $property, $data ); 00507 } 00508 00509 public function appendExtensionTags( $property ) { 00510 global $wgParser; 00511 $wgParser->firstCallInit(); 00512 $tags = array_map( array( $this, 'formatParserTags'), $wgParser->getTags() ); 00513 $this->getResult()->setIndexedTagName( $tags, 't' ); 00514 return $this->getResult()->addValue( 'query', $property, $tags ); 00515 } 00516 00517 public function appendFunctionHooks( $property ) { 00518 global $wgParser; 00519 $wgParser->firstCallInit(); 00520 $hooks = $wgParser->getFunctionHooks(); 00521 $this->getResult()->setIndexedTagName( $hooks, 'h' ); 00522 return $this->getResult()->addValue( 'query', $property, $hooks ); 00523 } 00524 00525 private function formatParserTags( $item ) { 00526 return "<{$item}>"; 00527 } 00528 00529 public function appendSubscribedHooks( $property ) { 00530 global $wgHooks; 00531 $myWgHooks = $wgHooks; 00532 ksort( $myWgHooks ); 00533 00534 $data = array(); 00535 foreach ( $myWgHooks as $hook => $hooks ) { 00536 $arr = array( 00537 'name' => $hook, 00538 'subscribers' => array_map( array( 'SpecialVersion', 'arrayToString' ), $hooks ), 00539 ); 00540 00541 $this->getResult()->setIndexedTagName( $arr['subscribers'], 's' ); 00542 $data[] = $arr; 00543 } 00544 00545 $this->getResult()->setIndexedTagName( $data, 'hook' ); 00546 return $this->getResult()->addValue( 'query', $property, $data ); 00547 } 00548 00549 public function getCacheMode( $params ) { 00550 return 'public'; 00551 } 00552 00553 public function getAllowedParams() { 00554 return array( 00555 'prop' => array( 00556 ApiBase::PARAM_DFLT => 'general', 00557 ApiBase::PARAM_ISMULTI => true, 00558 ApiBase::PARAM_TYPE => array( 00559 'general', 00560 'namespaces', 00561 'namespacealiases', 00562 'specialpagealiases', 00563 'magicwords', 00564 'interwikimap', 00565 'dbrepllag', 00566 'statistics', 00567 'usergroups', 00568 'extensions', 00569 'fileextensions', 00570 'rightsinfo', 00571 'languages', 00572 'skins', 00573 'extensiontags', 00574 'functionhooks', 00575 'showhooks', 00576 ) 00577 ), 00578 'filteriw' => array( 00579 ApiBase::PARAM_TYPE => array( 00580 'local', 00581 '!local', 00582 ) 00583 ), 00584 'showalldb' => false, 00585 'numberingroup' => false, 00586 'inlanguagecode' => null, 00587 ); 00588 } 00589 00590 public function getParamDescription() { 00591 $p = $this->getModulePrefix(); 00592 return array( 00593 'prop' => array( 00594 'Which sysinfo properties to get:', 00595 ' general - Overall system information', 00596 ' namespaces - List of registered namespaces and their canonical names', 00597 ' namespacealiases - List of registered namespace aliases', 00598 ' specialpagealiases - List of special page aliases', 00599 ' magicwords - List of magic words and their aliases', 00600 ' statistics - Returns site statistics', 00601 " interwikimap - Returns interwiki map (optionally filtered, (optionally localised by using {$p}inlanguagecode))", 00602 ' dbrepllag - Returns database server with the highest replication lag', 00603 ' usergroups - Returns user groups and the associated permissions', 00604 ' extensions - Returns extensions installed on the wiki', 00605 ' fileextensions - Returns list of file extensions allowed to be uploaded', 00606 ' rightsinfo - Returns wiki rights (license) information if available', 00607 " languages - Returns a list of languages MediaWiki supports (optionally localised by using {$p}inlanguagecode)", 00608 ' skins - Returns a list of all enabled skins', 00609 ' extensiontags - Returns a list of parser extension tags', 00610 ' functionhooks - Returns a list of parser function hooks', 00611 ' showhooks - Returns a list of all subscribed hooks (contents of $wgHooks)' 00612 ), 00613 'filteriw' => 'Return only local or only nonlocal entries of the interwiki map', 00614 'showalldb' => 'List all database servers, not just the one lagging the most', 00615 'numberingroup' => 'Lists the number of users in user groups', 00616 'inlanguagecode' => 'Language code for localised language names (best effort, use CLDR extension)', 00617 ); 00618 } 00619 00620 public function getDescription() { 00621 return 'Return general information about the site'; 00622 } 00623 00624 public function getPossibleErrors() { 00625 return array_merge( parent::getPossibleErrors(), array( 00626 array( 'code' => 'includeAllDenied', 'info' => 'Cannot view all servers info unless $wgShowHostnames is true' ), 00627 ) ); 00628 } 00629 00630 public function getExamples() { 00631 return array( 00632 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics', 00633 'api.php?action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local', 00634 'api.php?action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb=', 00635 ); 00636 } 00637 00638 public function getHelpUrls() { 00639 return 'https://www.mediawiki.org/wiki/API:Meta#siteinfo_.2F_si'; 00640 } 00641 00642 public function getVersion() { 00643 return __CLASS__ . ': $Id$'; 00644 } 00645 }