Merge "Don't fallback from uk to ru"
[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->dieUsage(
451 'Cannot view all servers info unless $wgShowHostnames is true',
452 'includeAllDenied'
453 );
454 }
455
456 $lags = $lb->getLagTimes();
457 foreach ( $lags as $i => $lag ) {
458 $data[] = [
459 'host' => $lb->getServerName( $i ),
460 'lag' => $lag
461 ];
462 }
463 } else {
464 list( , $lag, $index ) = $lb->getMaxLag();
465 $data[] = [
466 'host' => $showHostnames
467 ? $lb->getServerName( $index )
468 : '',
469 'lag' => intval( $lag )
470 ];
471 }
472
473 ApiResult::setIndexedTagName( $data, 'db' );
474
475 return $this->getResult()->addValue( 'query', $property, $data );
476 }
477
478 protected function appendStatistics( $property ) {
479 $data = [];
480 $data['pages'] = intval( SiteStats::pages() );
481 $data['articles'] = intval( SiteStats::articles() );
482 $data['edits'] = intval( SiteStats::edits() );
483 $data['images'] = intval( SiteStats::images() );
484 $data['users'] = intval( SiteStats::users() );
485 $data['activeusers'] = intval( SiteStats::activeUsers() );
486 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
487 $data['jobs'] = intval( SiteStats::jobs() );
488
489 Hooks::run( 'APIQuerySiteInfoStatisticsInfo', [ &$data ] );
490
491 return $this->getResult()->addValue( 'query', $property, $data );
492 }
493
494 protected function appendUserGroups( $property, $numberInGroup ) {
495 $config = $this->getConfig();
496
497 $data = [];
498 $result = $this->getResult();
499 $allGroups = array_values( User::getAllGroups() );
500 foreach ( $config->get( 'GroupPermissions' ) as $group => $permissions ) {
501 $arr = [
502 'name' => $group,
503 'rights' => array_keys( $permissions, true ),
504 ];
505
506 if ( $numberInGroup ) {
507 $autopromote = $config->get( 'Autopromote' );
508
509 if ( $group == 'user' ) {
510 $arr['number'] = SiteStats::users();
511 // '*' and autopromote groups have no size
512 } elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
513 $arr['number'] = SiteStats::numberingroup( $group );
514 }
515 }
516
517 $groupArr = [
518 'add' => $config->get( 'AddGroups' ),
519 'remove' => $config->get( 'RemoveGroups' ),
520 'add-self' => $config->get( 'GroupsAddToSelf' ),
521 'remove-self' => $config->get( 'GroupsRemoveFromSelf' )
522 ];
523
524 foreach ( $groupArr as $type => $rights ) {
525 if ( isset( $rights[$group] ) ) {
526 if ( $rights[$group] === true ) {
527 $groups = $allGroups;
528 } else {
529 $groups = array_intersect( $rights[$group], $allGroups );
530 }
531 if ( $groups ) {
532 $arr[$type] = $groups;
533 ApiResult::setArrayType( $arr[$type], 'BCarray' );
534 ApiResult::setIndexedTagName( $arr[$type], 'group' );
535 }
536 }
537 }
538
539 ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
540 $data[] = $arr;
541 }
542
543 ApiResult::setIndexedTagName( $data, 'group' );
544
545 return $result->addValue( 'query', $property, $data );
546 }
547
548 protected function appendFileExtensions( $property ) {
549 $data = [];
550 foreach ( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) as $ext ) {
551 $data[] = [ 'ext' => $ext ];
552 }
553 ApiResult::setIndexedTagName( $data, 'fe' );
554
555 return $this->getResult()->addValue( 'query', $property, $data );
556 }
557
558 protected function appendInstalledLibraries( $property ) {
559 global $IP;
560 $path = "$IP/vendor/composer/installed.json";
561 if ( !file_exists( $path ) ) {
562 return true;
563 }
564
565 $data = [];
566 $installed = new ComposerInstalled( $path );
567 foreach ( $installed->getInstalledDependencies() as $name => $info ) {
568 if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
569 // Skip any extensions or skins since they'll be listed
570 // in their proper section
571 continue;
572 }
573 $data[] = [
574 'name' => $name,
575 'version' => $info['version'],
576 ];
577 }
578 ApiResult::setIndexedTagName( $data, 'library' );
579
580 return $this->getResult()->addValue( 'query', $property, $data );
581 }
582
583 protected function appendExtensions( $property ) {
584 $data = [];
585 foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $type => $extensions ) {
586 foreach ( $extensions as $ext ) {
587 $ret = [];
588 $ret['type'] = $type;
589 if ( isset( $ext['name'] ) ) {
590 $ret['name'] = $ext['name'];
591 }
592 if ( isset( $ext['namemsg'] ) ) {
593 $ret['namemsg'] = $ext['namemsg'];
594 }
595 if ( isset( $ext['description'] ) ) {
596 $ret['description'] = $ext['description'];
597 }
598 if ( isset( $ext['descriptionmsg'] ) ) {
599 // Can be a string or [ key, param1, param2, ... ]
600 if ( is_array( $ext['descriptionmsg'] ) ) {
601 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
602 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
603 ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
604 } else {
605 $ret['descriptionmsg'] = $ext['descriptionmsg'];
606 }
607 }
608 if ( isset( $ext['author'] ) ) {
609 $ret['author'] = is_array( $ext['author'] ) ?
610 implode( ', ', $ext['author'] ) : $ext['author'];
611 }
612 if ( isset( $ext['url'] ) ) {
613 $ret['url'] = $ext['url'];
614 }
615 if ( isset( $ext['version'] ) ) {
616 $ret['version'] = $ext['version'];
617 }
618 if ( isset( $ext['path'] ) ) {
619 $extensionPath = dirname( $ext['path'] );
620 $gitInfo = new GitInfo( $extensionPath );
621 $vcsVersion = $gitInfo->getHeadSHA1();
622 if ( $vcsVersion !== false ) {
623 $ret['vcs-system'] = 'git';
624 $ret['vcs-version'] = $vcsVersion;
625 $ret['vcs-url'] = $gitInfo->getHeadViewUrl();
626 $vcsDate = $gitInfo->getHeadCommitDate();
627 if ( $vcsDate !== false ) {
628 $ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
629 }
630 }
631
632 if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
633 $ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
634 $ret['license'] = SpecialPage::getTitleFor(
635 'Version',
636 "License/{$ext['name']}"
637 )->getLinkURL();
638 }
639
640 if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
641 $ret['credits'] = SpecialPage::getTitleFor(
642 'Version',
643 "Credits/{$ext['name']}"
644 )->getLinkURL();
645 }
646 }
647 $data[] = $ret;
648 }
649 }
650
651 ApiResult::setIndexedTagName( $data, 'ext' );
652
653 return $this->getResult()->addValue( 'query', $property, $data );
654 }
655
656 protected function appendRightsInfo( $property ) {
657 $config = $this->getConfig();
658 $rightsPage = $config->get( 'RightsPage' );
659 if ( is_string( $rightsPage ) ) {
660 $title = Title::newFromText( $rightsPage );
661 $url = wfExpandUrl( $title, PROTO_CURRENT );
662 } else {
663 $title = false;
664 $url = $config->get( 'RightsUrl' );
665 }
666 $text = $config->get( 'RightsText' );
667 if ( !$text && $title ) {
668 $text = $title->getPrefixedText();
669 }
670
671 $data = [
672 'url' => $url ?: '',
673 'text' => $text ?: ''
674 ];
675
676 return $this->getResult()->addValue( 'query', $property, $data );
677 }
678
679 protected function appendRestrictions( $property ) {
680 $config = $this->getConfig();
681 $data = [
682 'types' => $config->get( 'RestrictionTypes' ),
683 'levels' => $config->get( 'RestrictionLevels' ),
684 'cascadinglevels' => $config->get( 'CascadingRestrictionLevels' ),
685 'semiprotectedlevels' => $config->get( 'SemiprotectedRestrictionLevels' ),
686 ];
687
688 ApiResult::setArrayType( $data['types'], 'BCarray' );
689 ApiResult::setArrayType( $data['levels'], 'BCarray' );
690 ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
691 ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
692
693 ApiResult::setIndexedTagName( $data['types'], 'type' );
694 ApiResult::setIndexedTagName( $data['levels'], 'level' );
695 ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
696 ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
697
698 return $this->getResult()->addValue( 'query', $property, $data );
699 }
700
701 public function appendLanguages( $property ) {
702 $params = $this->extractRequestParams();
703 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
704 $langNames = Language::fetchLanguageNames( $langCode );
705
706 $data = [];
707
708 foreach ( $langNames as $code => $name ) {
709 $lang = [ 'code' => $code ];
710 ApiResult::setContentValue( $lang, 'name', $name );
711 $data[] = $lang;
712 }
713 ApiResult::setIndexedTagName( $data, 'lang' );
714
715 return $this->getResult()->addValue( 'query', $property, $data );
716 }
717
718 public function appendSkins( $property ) {
719 $data = [];
720 $allowed = Skin::getAllowedSkins();
721 $default = Skin::normalizeKey( 'default' );
722 foreach ( Skin::getSkinNames() as $name => $displayName ) {
723 $msg = $this->msg( "skinname-{$name}" );
724 $code = $this->getParameter( 'inlanguagecode' );
725 if ( $code && Language::isValidCode( $code ) ) {
726 $msg->inLanguage( $code );
727 } else {
728 $msg->inContentLanguage();
729 }
730 if ( $msg->exists() ) {
731 $displayName = $msg->text();
732 }
733 $skin = [ 'code' => $name ];
734 ApiResult::setContentValue( $skin, 'name', $displayName );
735 if ( !isset( $allowed[$name] ) ) {
736 $skin['unusable'] = true;
737 }
738 if ( $name === $default ) {
739 $skin['default'] = true;
740 }
741 $data[] = $skin;
742 }
743 ApiResult::setIndexedTagName( $data, 'skin' );
744
745 return $this->getResult()->addValue( 'query', $property, $data );
746 }
747
748 public function appendExtensionTags( $property ) {
749 global $wgParser;
750 $wgParser->firstCallInit();
751 $tags = array_map( [ $this, 'formatParserTags' ], $wgParser->getTags() );
752 ApiResult::setArrayType( $tags, 'BCarray' );
753 ApiResult::setIndexedTagName( $tags, 't' );
754
755 return $this->getResult()->addValue( 'query', $property, $tags );
756 }
757
758 public function appendFunctionHooks( $property ) {
759 global $wgParser;
760 $wgParser->firstCallInit();
761 $hooks = $wgParser->getFunctionHooks();
762 ApiResult::setArrayType( $hooks, 'BCarray' );
763 ApiResult::setIndexedTagName( $hooks, 'h' );
764
765 return $this->getResult()->addValue( 'query', $property, $hooks );
766 }
767
768 public function appendVariables( $property ) {
769 $variables = MagicWord::getVariableIDs();
770 ApiResult::setArrayType( $variables, 'BCarray' );
771 ApiResult::setIndexedTagName( $variables, 'v' );
772
773 return $this->getResult()->addValue( 'query', $property, $variables );
774 }
775
776 public function appendProtocols( $property ) {
777 // Make a copy of the global so we don't try to set the _element key of it - bug 45130
778 $protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
779 ApiResult::setArrayType( $protocols, 'BCarray' );
780 ApiResult::setIndexedTagName( $protocols, 'p' );
781
782 return $this->getResult()->addValue( 'query', $property, $protocols );
783 }
784
785 public function appendDefaultOptions( $property ) {
786 $options = User::getDefaultOptions();
787 $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
788 return $this->getResult()->addValue( 'query', $property, $options );
789 }
790
791 public function appendUploadDialog( $property ) {
792 $config = $this->getConfig()->get( 'UploadDialog' );
793 return $this->getResult()->addValue( 'query', $property, $config );
794 }
795
796 private function formatParserTags( $item ) {
797 return "<{$item}>";
798 }
799
800 public function appendSubscribedHooks( $property ) {
801 $hooks = $this->getConfig()->get( 'Hooks' );
802 $myWgHooks = $hooks;
803 ksort( $myWgHooks );
804
805 $data = [];
806 foreach ( $myWgHooks as $name => $subscribers ) {
807 $arr = [
808 'name' => $name,
809 'subscribers' => array_map( [ 'SpecialVersion', 'arrayToString' ], $subscribers ),
810 ];
811
812 ApiResult::setArrayType( $arr['subscribers'], 'array' );
813 ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
814 $data[] = $arr;
815 }
816
817 ApiResult::setIndexedTagName( $data, 'hook' );
818
819 return $this->getResult()->addValue( 'query', $property, $data );
820 }
821
822 public function getCacheMode( $params ) {
823 // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
824 if (
825 count( $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' ) ) &&
826 !is_null( $params['prop'] ) &&
827 in_array( 'interwikimap', $params['prop'] )
828 ) {
829 return 'anon-public-user-private';
830 }
831
832 return 'public';
833 }
834
835 public function getAllowedParams() {
836 return [
837 'prop' => [
838 ApiBase::PARAM_DFLT => 'general',
839 ApiBase::PARAM_ISMULTI => true,
840 ApiBase::PARAM_TYPE => [
841 'general',
842 'namespaces',
843 'namespacealiases',
844 'specialpagealiases',
845 'magicwords',
846 'interwikimap',
847 'dbrepllag',
848 'statistics',
849 'usergroups',
850 'libraries',
851 'extensions',
852 'fileextensions',
853 'rightsinfo',
854 'restrictions',
855 'languages',
856 'skins',
857 'extensiontags',
858 'functionhooks',
859 'showhooks',
860 'variables',
861 'protocols',
862 'defaultoptions',
863 'uploaddialog',
864 ],
865 ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
866 ],
867 'filteriw' => [
868 ApiBase::PARAM_TYPE => [
869 'local',
870 '!local',
871 ]
872 ],
873 'showalldb' => false,
874 'numberingroup' => false,
875 'inlanguagecode' => null,
876 ];
877 }
878
879 protected function getExamplesMessages() {
880 return [
881 'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
882 => 'apihelp-query+siteinfo-example-simple',
883 'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
884 => 'apihelp-query+siteinfo-example-interwiki',
885 'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
886 => 'apihelp-query+siteinfo-example-replag',
887 ];
888 }
889
890 public function getHelpUrls() {
891 return 'https://www.mediawiki.org/wiki/API:Siteinfo';
892 }
893 }