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