Merge "Normalize header case for FileBackend operations"
[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 /**
28 * A query action to return meta information about the wiki site.
29 *
30 * @ingroup API
31 */
32 class ApiQuerySiteinfo extends ApiQueryBase {
33
34 public function __construct( ApiQuery $query, $moduleName ) {
35 parent::__construct( $query, $moduleName, 'si' );
36 }
37
38 public function execute() {
39 $params = $this->extractRequestParams();
40 $done = array();
41 $fit = false;
42 foreach ( $params['prop'] as $p ) {
43 switch ( $p ) {
44 case 'general':
45 $fit = $this->appendGeneralInfo( $p );
46 break;
47 case 'namespaces':
48 $fit = $this->appendNamespaces( $p );
49 break;
50 case 'namespacealiases':
51 $fit = $this->appendNamespaceAliases( $p );
52 break;
53 case 'specialpagealiases':
54 $fit = $this->appendSpecialPageAliases( $p );
55 break;
56 case 'magicwords':
57 $fit = $this->appendMagicWords( $p );
58 break;
59 case 'interwikimap':
60 $filteriw = isset( $params['filteriw'] ) ? $params['filteriw'] : false;
61 $fit = $this->appendInterwikiMap( $p, $filteriw );
62 break;
63 case 'dbrepllag':
64 $fit = $this->appendDbReplLagInfo( $p, $params['showalldb'] );
65 break;
66 case 'statistics':
67 $fit = $this->appendStatistics( $p );
68 break;
69 case 'usergroups':
70 $fit = $this->appendUserGroups( $p, $params['numberingroup'] );
71 break;
72 case 'libraries':
73 $fit = $this->appendInstalledLibraries( $p );
74 break;
75 case 'extensions':
76 $fit = $this->appendExtensions( $p );
77 break;
78 case 'fileextensions':
79 $fit = $this->appendFileExtensions( $p );
80 break;
81 case 'rightsinfo':
82 $fit = $this->appendRightsInfo( $p );
83 break;
84 case 'restrictions':
85 $fit = $this->appendRestrictions( $p );
86 break;
87 case 'languages':
88 $fit = $this->appendLanguages( $p );
89 break;
90 case 'skins':
91 $fit = $this->appendSkins( $p );
92 break;
93 case 'extensiontags':
94 $fit = $this->appendExtensionTags( $p );
95 break;
96 case 'functionhooks':
97 $fit = $this->appendFunctionHooks( $p );
98 break;
99 case 'showhooks':
100 $fit = $this->appendSubscribedHooks( $p );
101 break;
102 case 'variables':
103 $fit = $this->appendVariables( $p );
104 break;
105 case 'protocols':
106 $fit = $this->appendProtocols( $p );
107 break;
108 case 'defaultoptions':
109 $fit = $this->appendDefaultOptions( $p );
110 break;
111 default:
112 ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
113 }
114 if ( !$fit ) {
115 // Abuse siprop as a query-continue parameter
116 // and set it to all unprocessed props
117 $this->setContinueEnumParameter( 'prop', implode( '|',
118 array_diff( $params['prop'], $done ) ) );
119 break;
120 }
121 $done[] = $p;
122 }
123 }
124
125 protected function appendGeneralInfo( $property ) {
126 global $wgContLang;
127
128 $config = $this->getConfig();
129
130 $data = array();
131 $mainPage = Title::newMainPage();
132 $data['mainpage'] = $mainPage->getPrefixedText();
133 $data['base'] = wfExpandUrl( $mainPage->getFullURL(), PROTO_CURRENT );
134 $data['sitename'] = $config->get( 'Sitename' );
135
136 // wgLogo can either be a relative or an absolute path
137 // make sure we always return an absolute path
138 $data['logo'] = wfExpandUrl( $config->get( 'Logo' ), PROTO_RELATIVE );
139
140 $data['generator'] = "MediaWiki {$config->get( 'Version' )}";
141
142 $data['phpversion'] = PHP_VERSION;
143 $data['phpsapi'] = PHP_SAPI;
144 if ( defined( 'HHVM_VERSION' ) ) {
145 $data['hhvmversion'] = HHVM_VERSION;
146 }
147 $data['dbtype'] = $config->get( 'DBtype' );
148 $data['dbversion'] = $this->getDB()->getServerVersion();
149
150 $allowFrom = array( '' );
151 $allowException = true;
152 if ( !$config->get( 'AllowExternalImages' ) ) {
153 $data['imagewhitelistenabled'] = (bool)$config->get( 'EnableImageWhitelist' );
154 $allowFrom = $config->get( 'AllowExternalImagesFrom' );
155 $allowException = !empty( $allowFrom );
156 }
157 if ( $allowException ) {
158 $data['externalimages'] = (array)$allowFrom;
159 ApiResult::setIndexedTagName( $data['externalimages'], 'prefix' );
160 }
161
162 $data['langconversion'] = !$config->get( 'DisableLangConversion' );
163 $data['titleconversion'] = !$config->get( 'DisableTitleConversion' );
164
165 if ( $wgContLang->linkPrefixExtension() ) {
166 $linkPrefixCharset = $wgContLang->linkPrefixCharset();
167 $data['linkprefixcharset'] = $linkPrefixCharset;
168 // For backwards compatibility
169 $data['linkprefix'] = "/^((?>.*[^$linkPrefixCharset]|))(.+)$/sDu";
170 } else {
171 $data['linkprefixcharset'] = '';
172 $data['linkprefix'] = '';
173 }
174
175 $linktrail = $wgContLang->linkTrail();
176 $data['linktrail'] = $linktrail ?: '';
177
178 $data['legaltitlechars'] = Title::legalChars();
179 $data['invalidusernamechars'] = $config->get( 'InvalidUsernameCharacters' );
180
181 global $IP;
182 $git = SpecialVersion::getGitHeadSha1( $IP );
183 if ( $git ) {
184 $data['git-hash'] = $git;
185 $data['git-branch'] =
186 SpecialVersion::getGitCurrentBranch( $GLOBALS['IP'] );
187 } else {
188 $svn = SpecialVersion::getSvnRevision( $IP );
189 if ( $svn ) {
190 $data['rev'] = $svn;
191 }
192 }
193
194 // 'case-insensitive' option is reserved for future
195 $data['case'] = $config->get( 'CapitalLinks' ) ? 'first-letter' : 'case-sensitive';
196 $data['lang'] = $config->get( 'LanguageCode' );
197
198 $fallbacks = array();
199 foreach ( $wgContLang->getFallbackLanguages() as $code ) {
200 $fallbacks[] = array( 'code' => $code );
201 }
202 $data['fallback'] = $fallbacks;
203 ApiResult::setIndexedTagName( $data['fallback'], 'lang' );
204
205 if ( $wgContLang->hasVariants() ) {
206 $variants = array();
207 foreach ( $wgContLang->getVariants() as $code ) {
208 $variants[] = array(
209 'code' => $code,
210 'name' => $wgContLang->getVariantname( $code ),
211 );
212 }
213 $data['variants'] = $variants;
214 ApiResult::setIndexedTagName( $data['variants'], 'lang' );
215 }
216
217 $data['rtl'] = $wgContLang->isRTL();
218 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
219
220 $data['readonly'] = wfReadOnly();
221 if ( $data['readonly'] ) {
222 $data['readonlyreason'] = wfReadOnlyReason();
223 }
224 $data['writeapi'] = (bool)$config->get( 'EnableWriteAPI' );
225
226 $tz = $config->get( 'Localtimezone' );
227 $offset = $config->get( 'LocalTZoffset' );
228 if ( is_null( $tz ) ) {
229 $tz = 'UTC';
230 $offset = 0;
231 } elseif ( is_null( $offset ) ) {
232 $offset = 0;
233 }
234 $data['timezone'] = $tz;
235 $data['timeoffset'] = intval( $offset );
236 $data['articlepath'] = $config->get( 'ArticlePath' );
237 $data['scriptpath'] = $config->get( 'ScriptPath' );
238 $data['script'] = $config->get( 'Script' );
239 $data['variantarticlepath'] = $config->get( 'VariantArticlePath' );
240 $data[ApiResult::META_BC_BOOLS][] = 'variantarticlepath';
241 $data['server'] = $config->get( 'Server' );
242 $data['servername'] = $config->get( 'ServerName' );
243 $data['wikiid'] = wfWikiID();
244 $data['time'] = wfTimestamp( TS_ISO_8601, time() );
245
246 $data['misermode'] = (bool)$config->get( 'MiserMode' );
247
248 $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
249 $data['minuploadchunksize'] = (int)$this->getConfig()->get( 'MinUploadChunkSize' );
250
251 $data['thumblimits'] = $config->get( 'ThumbLimits' );
252 ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
253 ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
254 $data['imagelimits'] = array();
255 ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
256 ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
257 foreach ( $config->get( 'ImageLimits' ) as $k => $limit ) {
258 $data['imagelimits'][$k] = array( 'width' => $limit[0], 'height' => $limit[1] );
259 }
260
261 $favicon = $config->get( 'Favicon' );
262 if ( !empty( $favicon ) ) {
263 // wgFavicon can either be a relative or an absolute path
264 // make sure we always return an absolute path
265 $data['favicon'] = wfExpandUrl( $favicon, PROTO_RELATIVE );
266 }
267
268 Hooks::run( 'APIQuerySiteInfoGeneralInfo', array( $this, &$data ) );
269
270 return $this->getResult()->addValue( 'query', $property, $data );
271 }
272
273 protected function appendNamespaces( $property ) {
274 global $wgContLang;
275 $data = array(
276 ApiResult::META_TYPE => 'assoc',
277 );
278 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
279 $data[$ns] = array(
280 'id' => intval( $ns ),
281 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
282 );
283 ApiResult::setContentValue( $data[$ns], 'name', $title );
284 $canonical = MWNamespace::getCanonicalName( $ns );
285
286 $data[$ns]['subpages'] = MWNamespace::hasSubpages( $ns );
287
288 if ( $canonical ) {
289 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
290 }
291
292 $data[$ns]['content'] = MWNamespace::isContent( $ns );
293 $data[$ns]['nonincludable'] = MWNamespace::isNonincludable( $ns );
294
295 $contentmodel = MWNamespace::getNamespaceContentModel( $ns );
296 if ( $contentmodel ) {
297 $data[$ns]['defaultcontentmodel'] = $contentmodel;
298 }
299 }
300
301 ApiResult::setArrayType( $data, 'assoc' );
302 ApiResult::setIndexedTagName( $data, 'ns' );
303
304 return $this->getResult()->addValue( 'query', $property, $data );
305 }
306
307 protected function appendNamespaceAliases( $property ) {
308 global $wgContLang;
309 $aliases = array_merge( $this->getConfig()->get( 'NamespaceAliases' ),
310 $wgContLang->getNamespaceAliases() );
311 $namespaces = $wgContLang->getNamespaces();
312 $data = array();
313 foreach ( $aliases as $title => $ns ) {
314 if ( $namespaces[$ns] == $title ) {
315 // Don't list duplicates
316 continue;
317 }
318 $item = array(
319 'id' => intval( $ns )
320 );
321 ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
322 $data[] = $item;
323 }
324
325 sort( $data );
326
327 ApiResult::setIndexedTagName( $data, 'ns' );
328
329 return $this->getResult()->addValue( 'query', $property, $data );
330 }
331
332 protected function appendSpecialPageAliases( $property ) {
333 global $wgContLang;
334 $data = array();
335 $aliases = $wgContLang->getSpecialPageAliases();
336 foreach ( SpecialPageFactory::getNames() as $specialpage ) {
337 if ( isset( $aliases[$specialpage] ) ) {
338 $arr = array( 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] );
339 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
340 $data[] = $arr;
341 }
342 }
343 ApiResult::setIndexedTagName( $data, 'specialpage' );
344
345 return $this->getResult()->addValue( 'query', $property, $data );
346 }
347
348 protected function appendMagicWords( $property ) {
349 global $wgContLang;
350 $data = array();
351 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
352 $caseSensitive = array_shift( $aliases );
353 $arr = array( 'name' => $magicword, 'aliases' => $aliases );
354 $arr['case-sensitive'] = (bool)$caseSensitive;
355 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
356 $data[] = $arr;
357 }
358 ApiResult::setIndexedTagName( $data, 'magicword' );
359
360 return $this->getResult()->addValue( 'query', $property, $data );
361 }
362
363 protected function appendInterwikiMap( $property, $filter ) {
364 $local = null;
365 if ( $filter === 'local' ) {
366 $local = 1;
367 } elseif ( $filter === '!local' ) {
368 $local = 0;
369 } elseif ( $filter ) {
370 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
371 }
372
373 $params = $this->extractRequestParams();
374 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
375 $langNames = Language::fetchLanguageNames( $langCode );
376
377 $getPrefixes = Interwiki::getAllPrefixes( $local );
378 $extraLangPrefixes = $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' );
379 $localInterwikis = $this->getConfig()->get( 'LocalInterwikis' );
380 $data = array();
381
382 foreach ( $getPrefixes as $row ) {
383 $prefix = $row['iw_prefix'];
384 $val = array();
385 $val['prefix'] = $prefix;
386 if ( isset( $row['iw_local'] ) && $row['iw_local'] == '1' ) {
387 $val['local'] = true;
388 }
389 if ( isset( $row['iw_trans'] ) && $row['iw_trans'] == '1' ) {
390 $val['trans'] = true;
391 }
392
393 if ( isset( $langNames[$prefix] ) ) {
394 $val['language'] = $langNames[$prefix];
395 }
396 if ( in_array( $prefix, $localInterwikis ) ) {
397 $val['localinterwiki'] = true;
398 }
399 if ( in_array( $prefix, $extraLangPrefixes ) ) {
400 $val['extralanglink'] = true;
401
402 $linktext = wfMessage( "interlanguage-link-$prefix" );
403 if ( !$linktext->isDisabled() ) {
404 $val['linktext'] = $linktext->text();
405 }
406
407 $sitename = wfMessage( "interlanguage-link-sitename-$prefix" );
408 if ( !$sitename->isDisabled() ) {
409 $val['sitename'] = $sitename->text();
410 }
411 }
412
413 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
414 $val['protorel'] = substr( $row['iw_url'], 0, 2 ) == '//';
415 if ( isset( $row['iw_wikiid'] ) && $row['iw_wikiid'] !== '' ) {
416 $val['wikiid'] = $row['iw_wikiid'];
417 }
418 if ( isset( $row['iw_api'] ) && $row['iw_api'] !== '' ) {
419 $val['api'] = $row['iw_api'];
420 }
421
422 $data[] = $val;
423 }
424
425 ApiResult::setIndexedTagName( $data, 'iw' );
426
427 return $this->getResult()->addValue( 'query', $property, $data );
428 }
429
430 protected function appendDbReplLagInfo( $property, $includeAll ) {
431 $data = array();
432 $lb = wfGetLB();
433 $showHostnames = $this->getConfig()->get( 'ShowHostnames' );
434 if ( $includeAll ) {
435 if ( !$showHostnames ) {
436 $this->dieUsage(
437 'Cannot view all servers info unless $wgShowHostnames is true',
438 'includeAllDenied'
439 );
440 }
441
442 $lags = $lb->getLagTimes();
443 foreach ( $lags as $i => $lag ) {
444 $data[] = array(
445 'host' => $lb->getServerName( $i ),
446 'lag' => $lag
447 );
448 }
449 } else {
450 list( , $lag, $index ) = $lb->getMaxLag();
451 $data[] = array(
452 'host' => $showHostnames
453 ? $lb->getServerName( $index )
454 : '',
455 'lag' => intval( $lag )
456 );
457 }
458
459 $result = $this->getResult();
460 ApiResult::setIndexedTagName( $data, 'db' );
461
462 return $this->getResult()->addValue( 'query', $property, $data );
463 }
464
465 protected function appendStatistics( $property ) {
466 $data = array();
467 $data['pages'] = intval( SiteStats::pages() );
468 $data['articles'] = intval( SiteStats::articles() );
469 $data['edits'] = intval( SiteStats::edits() );
470 $data['images'] = intval( SiteStats::images() );
471 $data['users'] = intval( SiteStats::users() );
472 $data['activeusers'] = intval( SiteStats::activeUsers() );
473 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
474 $data['jobs'] = intval( SiteStats::jobs() );
475
476 Hooks::run( 'APIQuerySiteInfoStatisticsInfo', array( &$data ) );
477
478 return $this->getResult()->addValue( 'query', $property, $data );
479 }
480
481 protected function appendUserGroups( $property, $numberInGroup ) {
482 $config = $this->getConfig();
483
484 $data = array();
485 $result = $this->getResult();
486 $allGroups = User::getAllGroups();
487 foreach ( $config->get( 'GroupPermissions' ) as $group => $permissions ) {
488 $arr = array(
489 'name' => $group,
490 'rights' => array_keys( $permissions, true ),
491 );
492
493 if ( $numberInGroup ) {
494 $autopromote = $config->get( 'Autopromote' );
495
496 if ( $group == 'user' ) {
497 $arr['number'] = SiteStats::users();
498 // '*' and autopromote groups have no size
499 } elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
500 $arr['number'] = SiteStats::numberInGroup( $group );
501 }
502 }
503
504 $groupArr = array(
505 'add' => $config->get( 'AddGroups' ),
506 'remove' => $config->get( 'RemoveGroups' ),
507 'add-self' => $config->get( 'GroupsAddToSelf' ),
508 'remove-self' => $config->get( 'GroupsRemoveFromSelf' )
509 );
510
511 foreach ( $groupArr as $type => $rights ) {
512 if ( isset( $rights[$group] ) ) {
513 $groups = array_intersect( $rights[$group], $allGroups );
514 if ( $groups ) {
515 $arr[$type] = $groups;
516 ApiResult::setArrayType( $arr[$type], 'BCarray' );
517 ApiResult::setIndexedTagName( $arr[$type], 'group' );
518 }
519 }
520 }
521
522 ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
523 $data[] = $arr;
524 }
525
526 ApiResult::setIndexedTagName( $data, 'group' );
527
528 return $result->addValue( 'query', $property, $data );
529 }
530
531 protected function appendFileExtensions( $property ) {
532 $data = array();
533 foreach ( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) as $ext ) {
534 $data[] = array( 'ext' => $ext );
535 }
536 ApiResult::setIndexedTagName( $data, 'fe' );
537
538 return $this->getResult()->addValue( 'query', $property, $data );
539 }
540
541 protected function appendInstalledLibraries( $property ) {
542 global $IP;
543 $path = "$IP/vendor/composer/installed.json";
544 if ( !file_exists( $path ) ) {
545 return true;
546 }
547
548 $data = array();
549 $installed = new ComposerInstalled( $path );
550 foreach ( $installed->getInstalledDependencies() as $name => $info ) {
551 if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
552 // Skip any extensions or skins since they'll be listed
553 // in their proper section
554 continue;
555 }
556 $data[] = array(
557 'name' => $name,
558 'version' => $info['version'],
559 );
560 }
561 ApiResult::setIndexedTagName( $data, 'library' );
562
563 return $this->getResult()->addValue( 'query', $property, $data );
564
565 }
566
567 protected function appendExtensions( $property ) {
568 $data = array();
569 foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $type => $extensions ) {
570 foreach ( $extensions as $ext ) {
571 $ret = array();
572 $ret['type'] = $type;
573 if ( isset( $ext['name'] ) ) {
574 $ret['name'] = $ext['name'];
575 }
576 if ( isset( $ext['namemsg'] ) ) {
577 $ret['namemsg'] = $ext['namemsg'];
578 }
579 if ( isset( $ext['description'] ) ) {
580 $ret['description'] = $ext['description'];
581 }
582 if ( isset( $ext['descriptionmsg'] ) ) {
583 // Can be a string or array( key, param1, param2, ... )
584 if ( is_array( $ext['descriptionmsg'] ) ) {
585 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
586 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
587 ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
588 } else {
589 $ret['descriptionmsg'] = $ext['descriptionmsg'];
590 }
591 }
592 if ( isset( $ext['author'] ) ) {
593 $ret['author'] = is_array( $ext['author'] ) ?
594 implode( ', ', $ext['author'] ) : $ext['author'];
595 }
596 if ( isset( $ext['url'] ) ) {
597 $ret['url'] = $ext['url'];
598 }
599 if ( isset( $ext['version'] ) ) {
600 $ret['version'] = $ext['version'];
601 } elseif ( isset( $ext['svn-revision'] ) &&
602 preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
603 $ext['svn-revision'], $m )
604 ) {
605 $ret['version'] = 'r' . $m[1];
606 }
607 if ( isset( $ext['path'] ) ) {
608 $extensionPath = dirname( $ext['path'] );
609 $gitInfo = new GitInfo( $extensionPath );
610 $vcsVersion = $gitInfo->getHeadSHA1();
611 if ( $vcsVersion !== false ) {
612 $ret['vcs-system'] = 'git';
613 $ret['vcs-version'] = $vcsVersion;
614 $ret['vcs-url'] = $gitInfo->getHeadViewUrl();
615 $vcsDate = $gitInfo->getHeadCommitDate();
616 if ( $vcsDate !== false ) {
617 $ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
618 }
619 } else {
620 $svnInfo = SpecialVersion::getSvnInfo( $extensionPath );
621 if ( $svnInfo !== false ) {
622 $ret['vcs-system'] = 'svn';
623 $ret['vcs-version'] = $svnInfo['checkout-rev'];
624 $ret['vcs-url'] = isset( $svnInfo['viewvc-url'] ) ? $svnInfo['viewvc-url'] : '';
625 }
626 }
627
628 if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
629 $ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
630 $ret['license'] = SpecialPage::getTitleFor(
631 'Version',
632 "License/{$ext['name']}"
633 )->getLinkURL();
634 }
635
636 if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
637 $ret['credits'] = SpecialPage::getTitleFor(
638 'Version',
639 "Credits/{$ext['name']}"
640 )->getLinkURL();
641 }
642 }
643 $data[] = $ret;
644 }
645 }
646
647 ApiResult::setIndexedTagName( $data, 'ext' );
648
649 return $this->getResult()->addValue( 'query', $property, $data );
650 }
651
652 protected function appendRightsInfo( $property ) {
653 $config = $this->getConfig();
654 $rightsPage = $config->get( 'RightsPage' );
655 if ( is_string( $rightsPage ) ) {
656 $title = Title::newFromText( $rightsPage );
657 $url = wfExpandUrl( $title, PROTO_CURRENT );
658 } else {
659 $title = false;
660 $url = $config->get( 'RightsUrl' );
661 }
662 $text = $config->get( 'RightsText' );
663 if ( !$text && $title ) {
664 $text = $title->getPrefixedText();
665 }
666
667 $data = array(
668 'url' => $url ? $url : '',
669 'text' => $text ? $text : ''
670 );
671
672 return $this->getResult()->addValue( 'query', $property, $data );
673 }
674
675 protected function appendRestrictions( $property ) {
676 $config = $this->getConfig();
677 $data = array(
678 'types' => $config->get( 'RestrictionTypes' ),
679 'levels' => $config->get( 'RestrictionLevels' ),
680 'cascadinglevels' => $config->get( 'CascadingRestrictionLevels' ),
681 'semiprotectedlevels' => $config->get( 'SemiprotectedRestrictionLevels' ),
682 );
683
684 ApiResult::setArrayType( $data['types'], 'BCarray' );
685 ApiResult::setArrayType( $data['levels'], 'BCarray' );
686 ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
687 ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
688
689 ApiResult::setIndexedTagName( $data['types'], 'type' );
690 ApiResult::setIndexedTagName( $data['levels'], 'level' );
691 ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
692 ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
693
694 return $this->getResult()->addValue( 'query', $property, $data );
695 }
696
697 public function appendLanguages( $property ) {
698 $params = $this->extractRequestParams();
699 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
700 $langNames = Language::fetchLanguageNames( $langCode );
701
702 $data = array();
703
704 foreach ( $langNames as $code => $name ) {
705 $lang = array( 'code' => $code );
706 ApiResult::setContentValue( $lang, 'name', $name );
707 $data[] = $lang;
708 }
709 ApiResult::setIndexedTagName( $data, 'lang' );
710
711 return $this->getResult()->addValue( 'query', $property, $data );
712 }
713
714 public function appendSkins( $property ) {
715 $data = array();
716 $allowed = Skin::getAllowedSkins();
717 $default = Skin::normalizeKey( 'default' );
718 foreach ( Skin::getSkinNames() as $name => $displayName ) {
719 $msg = $this->msg( "skinname-{$name}" );
720 $code = $this->getParameter( 'inlanguagecode' );
721 if ( $code && Language::isValidCode( $code ) ) {
722 $msg->inLanguage( $code );
723 } else {
724 $msg->inContentLanguage();
725 }
726 if ( $msg->exists() ) {
727 $displayName = $msg->text();
728 }
729 $skin = array( 'code' => $name );
730 ApiResult::setContentValue( $skin, 'name', $displayName );
731 if ( !isset( $allowed[$name] ) ) {
732 $skin['unusable'] = true;
733 }
734 if ( $name === $default ) {
735 $skin['default'] = true;
736 }
737 $data[] = $skin;
738 }
739 ApiResult::setIndexedTagName( $data, 'skin' );
740
741 return $this->getResult()->addValue( 'query', $property, $data );
742 }
743
744 public function appendExtensionTags( $property ) {
745 global $wgParser;
746 $wgParser->firstCallInit();
747 $tags = array_map( array( $this, 'formatParserTags' ), $wgParser->getTags() );
748 ApiResult::setArrayType( $tags, 'BCarray' );
749 ApiResult::setIndexedTagName( $tags, 't' );
750
751 return $this->getResult()->addValue( 'query', $property, $tags );
752 }
753
754 public function appendFunctionHooks( $property ) {
755 global $wgParser;
756 $wgParser->firstCallInit();
757 $hooks = $wgParser->getFunctionHooks();
758 ApiResult::setArrayType( $hooks, 'BCarray' );
759 ApiResult::setIndexedTagName( $hooks, 'h' );
760
761 return $this->getResult()->addValue( 'query', $property, $hooks );
762 }
763
764 public function appendVariables( $property ) {
765 $variables = MagicWord::getVariableIDs();
766 ApiResult::setArrayType( $variables, 'BCarray' );
767 ApiResult::setIndexedTagName( $variables, 'v' );
768
769 return $this->getResult()->addValue( 'query', $property, $variables );
770 }
771
772 public function appendProtocols( $property ) {
773 // Make a copy of the global so we don't try to set the _element key of it - bug 45130
774 $protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
775 ApiResult::setArrayType( $protocols, 'BCarray' );
776 ApiResult::setIndexedTagName( $protocols, 'p' );
777
778 return $this->getResult()->addValue( 'query', $property, $protocols );
779 }
780
781 public function appendDefaultOptions( $property ) {
782 $options = User::getDefaultOptions();
783 $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
784 return $this->getResult()->addValue( 'query', $property, $options );
785 }
786
787 private function formatParserTags( $item ) {
788 return "<{$item}>";
789 }
790
791 public function appendSubscribedHooks( $property ) {
792 $hooks = $this->getConfig()->get( 'Hooks' );
793 $myWgHooks = $hooks;
794 ksort( $myWgHooks );
795
796 $data = array();
797 foreach ( $myWgHooks as $name => $subscribers ) {
798 $arr = array(
799 'name' => $name,
800 'subscribers' => array_map( array( 'SpecialVersion', 'arrayToString' ), $subscribers ),
801 );
802
803 ApiResult::setArrayType( $arr['subscribers'], 'array' );
804 ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
805 $data[] = $arr;
806 }
807
808 ApiResult::setIndexedTagName( $data, 'hook' );
809
810 return $this->getResult()->addValue( 'query', $property, $data );
811 }
812
813 public function getCacheMode( $params ) {
814 // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
815 if (
816 count( $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' ) ) &&
817 !is_null( $params['prop'] ) &&
818 in_array( 'interwikimap', $params['prop'] )
819 ) {
820 return 'anon-public-user-private';
821 }
822
823 return 'public';
824 }
825
826 public function getAllowedParams() {
827 return array(
828 'prop' => array(
829 ApiBase::PARAM_DFLT => 'general',
830 ApiBase::PARAM_ISMULTI => true,
831 ApiBase::PARAM_TYPE => array(
832 'general',
833 'namespaces',
834 'namespacealiases',
835 'specialpagealiases',
836 'magicwords',
837 'interwikimap',
838 'dbrepllag',
839 'statistics',
840 'usergroups',
841 'libraries',
842 'extensions',
843 'fileextensions',
844 'rightsinfo',
845 'restrictions',
846 'languages',
847 'skins',
848 'extensiontags',
849 'functionhooks',
850 'showhooks',
851 'variables',
852 'protocols',
853 'defaultoptions',
854 ),
855 ApiBase::PARAM_HELP_MSG_PER_VALUE => array(),
856 ),
857 'filteriw' => array(
858 ApiBase::PARAM_TYPE => array(
859 'local',
860 '!local',
861 )
862 ),
863 'showalldb' => false,
864 'numberingroup' => false,
865 'inlanguagecode' => null,
866 );
867 }
868
869 protected function getExamplesMessages() {
870 return array(
871 'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
872 => 'apihelp-query+siteinfo-example-simple',
873 'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
874 => 'apihelp-query+siteinfo-example-interwiki',
875 'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
876 => 'apihelp-query+siteinfo-example-replag',
877 );
878 }
879
880 public function getHelpUrls() {
881 return 'https://www.mediawiki.org/wiki/API:Siteinfo';
882 }
883 }