Call Linker methods statically
[lhc/web/wiklou.git] / includes / api / ApiQuerySiteinfo.php
1 <?php
2 /**
3 *
4 *
5 * Created on Sep 25, 2006
6 *
7 * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * http://www.gnu.org/copyleft/gpl.html
23 *
24 * @file
25 */
26
27 if ( !defined( 'MEDIAWIKI' ) ) {
28 // Eclipse helper - will be ignored in production
29 require_once( 'ApiQueryBase.php' );
30 }
31
32 /**
33 * A query action to return meta information about the wiki site.
34 *
35 * @ingroup API
36 */
37 class ApiQuerySiteinfo extends ApiQueryBase {
38
39 public function __construct( $query, $moduleName ) {
40 parent::__construct( $query, $moduleName, 'si' );
41 }
42
43 public function execute() {
44 $params = $this->extractRequestParams();
45 $done = array();
46 $fit = false;
47 foreach ( $params['prop'] as $p ) {
48 switch ( $p ) {
49 case 'general':
50 $fit = $this->appendGeneralInfo( $p );
51 break;
52 case 'namespaces':
53 $fit = $this->appendNamespaces( $p );
54 break;
55 case 'namespacealiases':
56 $fit = $this->appendNamespaceAliases( $p );
57 break;
58 case 'specialpagealiases':
59 $fit = $this->appendSpecialPageAliases( $p );
60 break;
61 case 'magicwords':
62 $fit = $this->appendMagicWords( $p );
63 break;
64 case 'interwikimap':
65 $filteriw = isset( $params['filteriw'] ) ? $params['filteriw'] : false;
66 $fit = $this->appendInterwikiMap( $p, $filteriw );
67 break;
68 case 'dbrepllag':
69 $fit = $this->appendDbReplLagInfo( $p, $params['showalldb'] );
70 break;
71 case 'statistics':
72 $fit = $this->appendStatistics( $p );
73 break;
74 case 'usergroups':
75 $fit = $this->appendUserGroups( $p, $params['numberingroup'] );
76 break;
77 case 'extensions':
78 $fit = $this->appendExtensions( $p );
79 break;
80 case 'fileextensions':
81 $fit = $this->appendFileExtensions( $p );
82 break;
83 case 'rightsinfo':
84 $fit = $this->appendRightsInfo( $p );
85 break;
86 case 'languages':
87 $fit = $this->appendLanguages( $p );
88 break;
89 case 'skins':
90 $fit = $this->appendSkins( $p );
91 break;
92 case 'extensiontags':
93 $fit = $this->appendExtensionTags( $p );
94 break;
95 case 'functionhooks':
96 $fit = $this->appendFunctionHooks( $p );
97 break;
98 case 'showhooks':
99 $fit = $this->appendSubscribedHooks( $p );
100 break;
101 default:
102 ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
103 }
104 if ( !$fit ) {
105 // Abuse siprop as a query-continue parameter
106 // and set it to all unprocessed props
107 $this->setContinueEnumParameter( 'prop', implode( '|',
108 array_diff( $params['prop'], $done ) ) );
109 break;
110 }
111 $done[] = $p;
112 }
113 }
114
115 protected function appendGeneralInfo( $property ) {
116 global $wgContLang;
117
118 $data = array();
119 $mainPage = Title::newMainPage();
120 $data['mainpage'] = $mainPage->getPrefixedText();
121 $data['base'] = wfExpandUrl( $mainPage->getFullUrl(), PROTO_CURRENT );
122 $data['sitename'] = $GLOBALS['wgSitename'];
123 $data['generator'] = "MediaWiki {$GLOBALS['wgVersion']}";
124 $data['phpversion'] = phpversion();
125 $data['phpsapi'] = php_sapi_name();
126 $data['dbtype'] = $GLOBALS['wgDBtype'];
127 $data['dbversion'] = $this->getDB()->getServerVersion();
128
129 $svn = SpecialVersion::getSvnRevision( $GLOBALS['IP'] );
130 if ( $svn ) {
131 $data['rev'] = $svn;
132 }
133
134 // 'case-insensitive' option is reserved for future
135 $data['case'] = $GLOBALS['wgCapitalLinks'] ? 'first-letter' : 'case-sensitive';
136
137 if ( isset( $GLOBALS['wgRightsCode'] ) ) {
138 $data['rightscode'] = $GLOBALS['wgRightsCode'];
139 }
140 $data['rights'] = $GLOBALS['wgRightsText'];
141 $data['lang'] = $GLOBALS['wgLanguageCode'];
142
143 $fallbacks = array();
144 foreach( $wgContLang->getFallbackLanguages() as $code ) {
145 $fallbacks[] = array( 'code' => $code );
146 }
147 $data['fallback'] = $fallbacks;
148 $this->getResult()->setIndexedTagName( $data['fallback'], 'lang' );
149
150 if ( $wgContLang->isRTL() ) {
151 $data['rtl'] = '';
152 }
153 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
154
155 if ( wfReadOnly() ) {
156 $data['readonly'] = '';
157 $data['readonlyreason'] = wfReadOnlyReason();
158 }
159 if ( $GLOBALS['wgEnableWriteAPI'] ) {
160 $data['writeapi'] = '';
161 }
162
163 $tz = $GLOBALS['wgLocaltimezone'];
164 $offset = $GLOBALS['wgLocalTZoffset'];
165 if ( is_null( $tz ) ) {
166 $tz = 'UTC';
167 $offset = 0;
168 } elseif ( is_null( $offset ) ) {
169 $offset = 0;
170 }
171 $data['timezone'] = $tz;
172 $data['timeoffset'] = intval( $offset );
173 $data['articlepath'] = $GLOBALS['wgArticlePath'];
174 $data['scriptpath'] = $GLOBALS['wgScriptPath'];
175 $data['script'] = $GLOBALS['wgScript'];
176 $data['variantarticlepath'] = $GLOBALS['wgVariantArticlePath'];
177 $data['server'] = $GLOBALS['wgServer'];
178 $data['wikiid'] = wfWikiID();
179 $data['time'] = wfTimestamp( TS_ISO_8601, time() );
180
181 if ( $GLOBALS['wgMiserMode'] ) {
182 $data['misermode'] = '';
183 }
184
185 wfRunHooks( 'APIQuerySiteInfoGeneralInfo', array( $this, &$data ) );
186
187 return $this->getResult()->addValue( 'query', $property, $data );
188 }
189
190 protected function appendNamespaces( $property ) {
191 global $wgContLang;
192 $data = array();
193 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
194 $data[$ns] = array(
195 'id' => intval( $ns ),
196 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
197 );
198 ApiResult::setContent( $data[$ns], $title );
199 $canonical = MWNamespace::getCanonicalName( $ns );
200
201 if ( MWNamespace::hasSubpages( $ns ) ) {
202 $data[$ns]['subpages'] = '';
203 }
204
205 if ( $canonical ) {
206 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
207 }
208
209 if ( MWNamespace::isContent( $ns ) ) {
210 $data[$ns]['content'] = '';
211 }
212 }
213
214 $this->getResult()->setIndexedTagName( $data, 'ns' );
215 return $this->getResult()->addValue( 'query', $property, $data );
216 }
217
218 protected function appendNamespaceAliases( $property ) {
219 global $wgNamespaceAliases, $wgContLang;
220 $aliases = array_merge( $wgNamespaceAliases, $wgContLang->getNamespaceAliases() );
221 $namespaces = $wgContLang->getNamespaces();
222 $data = array();
223 foreach ( $aliases as $title => $ns ) {
224 if ( $namespaces[$ns] == $title ) {
225 // Don't list duplicates
226 continue;
227 }
228 $item = array(
229 'id' => intval( $ns )
230 );
231 ApiResult::setContent( $item, strtr( $title, '_', ' ' ) );
232 $data[] = $item;
233 }
234
235 $this->getResult()->setIndexedTagName( $data, 'ns' );
236 return $this->getResult()->addValue( 'query', $property, $data );
237 }
238
239 protected function appendSpecialPageAliases( $property ) {
240 global $wgContLang;
241 $data = array();
242 foreach ( $wgContLang->getSpecialPageAliases() as $specialpage => $aliases ) {
243 $arr = array( 'realname' => $specialpage, 'aliases' => $aliases );
244 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
245 $data[] = $arr;
246 }
247 $this->getResult()->setIndexedTagName( $data, 'specialpage' );
248 return $this->getResult()->addValue( 'query', $property, $data );
249 }
250
251 protected function appendMagicWords( $property ) {
252 global $wgContLang;
253 $data = array();
254 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
255 $caseSensitive = array_shift( $aliases );
256 $arr = array( 'name' => $magicword, 'aliases' => $aliases );
257 if ( $caseSensitive ) {
258 $arr['case-sensitive'] = '';
259 }
260 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
261 $data[] = $arr;
262 }
263 $this->getResult()->setIndexedTagName( $data, 'magicword' );
264 return $this->getResult()->addValue( 'query', $property, $data );
265 }
266
267 protected function appendInterwikiMap( $property, $filter ) {
268 $local = null;
269 if ( $filter === 'local' ) {
270 $local = 1;
271 } elseif ( $filter === '!local' ) {
272 $local = 0;
273 } elseif ( $filter ) {
274 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
275 }
276
277 $params = $this->extractRequestParams();
278 $langCode = isset( $params['inlanguagecode '] ) ? $params['inlanguagecode '] : '';
279
280 if( $langCode ) {
281 $langNames = Language::getTranslatedLanguageNames( $langCode );
282 } else {
283 $langNames = Language::getLanguageNames();
284 }
285
286 $getPrefixes = Interwiki::getAllPrefixes( $local );
287 $data = array();
288
289 foreach ( $getPrefixes as $row ) {
290 $prefix = $row['iw_prefix'];
291 $val = array();
292 $val['prefix'] = $prefix;
293 if ( $row['iw_local'] == '1' ) {
294 $val['local'] = '';
295 }
296 // $val['trans'] = intval( $row['iw_trans'] ); // should this be exposed?
297 if ( isset( $langNames[$prefix] ) ) {
298 $val['language'] = $langNames[$prefix];
299 }
300 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
301 if( isset( $row['iw_wikiid'] ) ) {
302 $val['wikiid'] = $row['iw_wikiid'];
303 }
304 if( isset( $row['iw_api'] ) ) {
305 $val['api'] = $row['iw_api'];
306 }
307
308 $data[] = $val;
309 }
310
311 $this->getResult()->setIndexedTagName( $data, 'iw' );
312 return $this->getResult()->addValue( 'query', $property, $data );
313 }
314
315 protected function appendDbReplLagInfo( $property, $includeAll ) {
316 global $wgShowHostnames;
317 $data = array();
318 $lb = wfGetLB();
319 if ( $includeAll ) {
320 if ( !$wgShowHostnames ) {
321 $this->dieUsage( 'Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied' );
322 }
323
324 $lags = $lb->getLagTimes();
325 foreach ( $lags as $i => $lag ) {
326 $data[] = array(
327 'host' => $lb->getServerName( $i ),
328 'lag' => $lag
329 );
330 }
331 } else {
332 list( $host, $lag, $index ) = $lb->getMaxLag();
333 $data[] = array(
334 'host' => $wgShowHostnames
335 ? $lb->getServerName( $index )
336 : '',
337 'lag' => intval( $lag )
338 );
339 }
340
341 $result = $this->getResult();
342 $result->setIndexedTagName( $data, 'db' );
343 return $this->getResult()->addValue( 'query', $property, $data );
344 }
345
346 protected function appendStatistics( $property ) {
347 global $wgDisableCounters;
348 $data = array();
349 $data['pages'] = intval( SiteStats::pages() );
350 $data['articles'] = intval( SiteStats::articles() );
351 if ( !$wgDisableCounters ) {
352 $data['views'] = intval( SiteStats::views() );
353 }
354 $data['edits'] = intval( SiteStats::edits() );
355 $data['images'] = intval( SiteStats::images() );
356 $data['users'] = intval( SiteStats::users() );
357 $data['activeusers'] = intval( SiteStats::activeUsers() );
358 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
359 $data['jobs'] = intval( SiteStats::jobs() );
360 return $this->getResult()->addValue( 'query', $property, $data );
361 }
362
363 protected function appendUserGroups( $property, $numberInGroup ) {
364 global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
365
366 $data = array();
367 $result = $this->getResult();
368 foreach ( $wgGroupPermissions as $group => $permissions ) {
369 $arr = array(
370 'name' => $group,
371 'rights' => array_keys( $permissions, true ),
372 );
373
374 if ( $numberInGroup ) {
375 global $wgAutopromote;
376
377 if ( $group == 'user' ) {
378 $arr['number'] = SiteStats::users();
379
380 // '*' and autopromote groups have no size
381 } elseif ( $group !== '*' && !isset( $wgAutopromote[$group] ) ) {
382 $arr['number'] = SiteStats::numberInGroup( $group );
383 }
384 }
385
386 $groupArr = array(
387 'add' => $wgAddGroups,
388 'remove' => $wgRemoveGroups,
389 'add-self' => $wgGroupsAddToSelf,
390 'remove-self' => $wgGroupsRemoveFromSelf
391 );
392
393 foreach ( $groupArr as $type => $rights ) {
394 if ( isset( $rights[$group] ) ) {
395 $arr[$type] = $rights[$group];
396 $result->setIndexedTagName( $arr[$type], 'group' );
397 }
398 }
399
400 $result->setIndexedTagName( $arr['rights'], 'permission' );
401 $data[] = $arr;
402 }
403
404 $result->setIndexedTagName( $data, 'group' );
405 return $result->addValue( 'query', $property, $data );
406 }
407
408 protected function appendFileExtensions( $property ) {
409 global $wgFileExtensions;
410
411 $data = array();
412 foreach ( $wgFileExtensions as $ext ) {
413 $data[] = array( 'ext' => $ext );
414 }
415 $this->getResult()->setIndexedTagName( $data, 'fe' );
416 return $this->getResult()->addValue( 'query', $property, $data );
417 }
418
419 protected function appendExtensions( $property ) {
420 global $wgExtensionCredits;
421 $data = array();
422 foreach ( $wgExtensionCredits as $type => $extensions ) {
423 foreach ( $extensions as $ext ) {
424 $ret = array();
425 $ret['type'] = $type;
426 if ( isset( $ext['name'] ) ) {
427 $ret['name'] = $ext['name'];
428 }
429 if ( isset( $ext['description'] ) ) {
430 $ret['description'] = $ext['description'];
431 }
432 if ( isset( $ext['descriptionmsg'] ) ) {
433 // Can be a string or array( key, param1, param2, ... )
434 if ( is_array( $ext['descriptionmsg'] ) ) {
435 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
436 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
437 $this->getResult()->setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
438 } else {
439 $ret['descriptionmsg'] = $ext['descriptionmsg'];
440 }
441 }
442 if ( isset( $ext['author'] ) ) {
443 $ret['author'] = is_array( $ext['author'] ) ?
444 implode( ', ', $ext['author' ] ) : $ext['author'];
445 }
446 if ( isset( $ext['url'] ) ) {
447 $ret['url'] = $ext['url'];
448 }
449 if ( isset( $ext['version'] ) ) {
450 $ret['version'] = $ext['version'];
451 } elseif ( isset( $ext['svn-revision'] ) &&
452 preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
453 $ext['svn-revision'], $m ) )
454 {
455 $ret['version'] = 'r' . $m[1];
456 }
457 $data[] = $ret;
458 }
459 }
460
461 $this->getResult()->setIndexedTagName( $data, 'ext' );
462 return $this->getResult()->addValue( 'query', $property, $data );
463 }
464
465 protected function appendRightsInfo( $property ) {
466 global $wgRightsPage, $wgRightsUrl, $wgRightsText;
467 $title = Title::newFromText( $wgRightsPage );
468 $url = $title ? wfExpandUrl( $title->getFullURL(), PROTO_CURRENT ) : $wgRightsUrl;
469 $text = $wgRightsText;
470 if ( !$text && $title ) {
471 $text = $title->getPrefixedText();
472 }
473
474 $data = array(
475 'url' => $url ? $url : '',
476 'text' => $text ? $text : ''
477 );
478
479 return $this->getResult()->addValue( 'query', $property, $data );
480 }
481
482 public function appendLanguages( $property ) {
483 $params = $this->extractRequestParams();
484 $langCode = isset( $params['inlanguagecode '] ) ? $params['inlanguagecode '] : '';
485
486 if( $langCode ) {
487 $langNames = Language::getTranslatedLanguageNames( $langCode );
488 } else {
489 $langNames = Language::getLanguageNames();
490 }
491
492 $data = array();
493
494 foreach ( $langNames as $code => $name ) {
495 $lang = array( 'code' => $code );
496 ApiResult::setContent( $lang, $name );
497 $data[] = $lang;
498 }
499 $this->getResult()->setIndexedTagName( $data, 'lang' );
500 return $this->getResult()->addValue( 'query', $property, $data );
501 }
502
503 public function appendSkins( $property ) {
504 $data = array();
505 foreach ( Skin::getSkinNames() as $name => $displayName ) {
506 $skin = array( 'code' => $name );
507 ApiResult::setContent( $skin, $displayName );
508 $data[] = $skin;
509 }
510 $this->getResult()->setIndexedTagName( $data, 'skin' );
511 return $this->getResult()->addValue( 'query', $property, $data );
512 }
513
514 public function appendExtensionTags( $property ) {
515 global $wgParser;
516 $wgParser->firstCallInit();
517 $tags = array_map( array( $this, 'formatParserTags'), $wgParser->getTags() );
518 $this->getResult()->setIndexedTagName( $tags, 't' );
519 return $this->getResult()->addValue( 'query', $property, $tags );
520 }
521
522 public function appendFunctionHooks( $property ) {
523 global $wgParser;
524 $wgParser->firstCallInit();
525 $hooks = $wgParser->getFunctionHooks();
526 $this->getResult()->setIndexedTagName( $hooks, 'h' );
527 return $this->getResult()->addValue( 'query', $property, $hooks );
528 }
529
530 private function formatParserTags( $item ) {
531 return "<{$item}>";
532 }
533
534 public function appendSubscribedHooks( $property ) {
535 global $wgHooks;
536 $myWgHooks = $wgHooks;
537 ksort( $myWgHooks );
538
539 $data = array();
540 foreach ( $myWgHooks as $hook => $hooks ) {
541 $arr = array(
542 'name' => $hook,
543 'subscribers' => array_map( array( 'SpecialVersion', 'arrayToString' ), $hooks ),
544 );
545
546 $this->getResult()->setIndexedTagName( $arr['subscribers'], 's' );
547 $data[] = $arr;
548 }
549
550 $this->getResult()->setIndexedTagName( $data, 'hook' );
551 return $this->getResult()->addValue( 'query', $property, $data );
552 }
553
554 public function getCacheMode( $params ) {
555 return 'public';
556 }
557
558 public function getAllowedParams() {
559 return array(
560 'prop' => array(
561 ApiBase::PARAM_DFLT => 'general',
562 ApiBase::PARAM_ISMULTI => true,
563 ApiBase::PARAM_TYPE => array(
564 'general',
565 'namespaces',
566 'namespacealiases',
567 'specialpagealiases',
568 'magicwords',
569 'interwikimap',
570 'dbrepllag',
571 'statistics',
572 'usergroups',
573 'extensions',
574 'fileextensions',
575 'rightsinfo',
576 'languages',
577 'skins',
578 'extensiontags',
579 'functionhooks',
580 'showhooks',
581 )
582 ),
583 'filteriw' => array(
584 ApiBase::PARAM_TYPE => array(
585 'local',
586 '!local',
587 )
588 ),
589 'showalldb' => false,
590 'numberingroup' => false,
591 'inlanguagecode ' => null,
592 );
593 }
594
595 public function getParamDescription() {
596 $p = $this->getModulePrefix();
597 return array(
598 'prop' => array(
599 'Which sysinfo properties to get:',
600 ' general - Overall system information',
601 ' namespaces - List of registered namespaces and their canonical names',
602 ' namespacealiases - List of registered namespace aliases',
603 ' specialpagealiases - List of special page aliases',
604 ' magicwords - List of magic words and their aliases',
605 ' statistics - Returns site statistics',
606 " interwikimap - Returns interwiki map (optionally filtered, (optionally localised by using {$p}inlanguagecode))",
607 ' dbrepllag - Returns database server with the highest replication lag',
608 ' usergroups - Returns user groups and the associated permissions',
609 ' extensions - Returns extensions installed on the wiki',
610 ' fileextensions - Returns list of file extensions allowed to be uploaded',
611 ' rightsinfo - Returns wiki rights (license) information if available',
612 " languages - Returns a list of languages MediaWiki supports (optionally localised by using {$p}inlanguagecode)",
613 ' skins - Returns a list of all enabled skins',
614 ' extensiontags - Returns a list of parser extension tags',
615 ' functionhooks - Returns a list of parser function hooks',
616 ' showhooks - Returns a list of all subscribed hooks (contents of $wgHooks)'
617 ),
618 'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
619 'showalldb' => 'List all database servers, not just the one lagging the most',
620 'numberingroup' => 'Lists the number of users in user groups',
621 'inlanguagecode ' => 'Language code for localised language names (best effort, use CLDR extension)',
622 );
623 }
624
625 public function getDescription() {
626 return 'Return general information about the site';
627 }
628
629 public function getPossibleErrors() {
630 return array_merge( parent::getPossibleErrors(), array(
631 array( 'code' => 'includeAllDenied', 'info' => 'Cannot view all servers info unless $wgShowHostnames is true' ),
632 ) );
633 }
634
635 public function getExamples() {
636 return array(
637 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics',
638 'api.php?action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local',
639 'api.php?action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb=',
640 );
641 }
642
643 public function getHelpUrls() {
644 return 'http://www.mediawiki.org/wiki/API:Meta#siteinfo_.2F_si';
645 }
646
647 public function getVersion() {
648 return __CLASS__ . ': $Id$';
649 }
650 }