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