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