Merge "Revert "Use display name in category page subheadings if provided""
[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['thumblimits'] = $config->get( 'ThumbLimits' );
257 ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
258 ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
259 $data['imagelimits'] = [];
260 ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
261 ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
262 foreach ( $config->get( 'ImageLimits' ) as $k => $limit ) {
263 $data['imagelimits'][$k] = [ 'width' => $limit[0], 'height' => $limit[1] ];
264 }
265
266 $favicon = $config->get( 'Favicon' );
267 if ( !empty( $favicon ) ) {
268 // wgFavicon can either be a relative or an absolute path
269 // make sure we always return an absolute path
270 $data['favicon'] = wfExpandUrl( $favicon, PROTO_RELATIVE );
271 }
272
273 $data['centralidlookupprovider'] = $config->get( 'CentralIdLookupProvider' );
274 $providerIds = array_keys( $config->get( 'CentralIdLookupProviders' ) );
275 $data['allcentralidlookupproviders'] = $providerIds;
276
277 $data['interwikimagic'] = (bool)$config->get( 'InterwikiMagic' );
278 $data['magiclinks'] = $config->get( 'EnableMagicLinks' );
279
280 Hooks::run( 'APIQuerySiteInfoGeneralInfo', [ $this, &$data ] );
281
282 return $this->getResult()->addValue( 'query', $property, $data );
283 }
284
285 protected function appendNamespaces( $property ) {
286 global $wgContLang;
287 $data = [
288 ApiResult::META_TYPE => 'assoc',
289 ];
290 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
291 $data[$ns] = [
292 'id' => intval( $ns ),
293 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
294 ];
295 ApiResult::setContentValue( $data[$ns], 'name', $title );
296 $canonical = MWNamespace::getCanonicalName( $ns );
297
298 $data[$ns]['subpages'] = MWNamespace::hasSubpages( $ns );
299
300 if ( $canonical ) {
301 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
302 }
303
304 $data[$ns]['content'] = MWNamespace::isContent( $ns );
305 $data[$ns]['nonincludable'] = MWNamespace::isNonincludable( $ns );
306
307 $contentmodel = MWNamespace::getNamespaceContentModel( $ns );
308 if ( $contentmodel ) {
309 $data[$ns]['defaultcontentmodel'] = $contentmodel;
310 }
311 }
312
313 ApiResult::setArrayType( $data, 'assoc' );
314 ApiResult::setIndexedTagName( $data, 'ns' );
315
316 return $this->getResult()->addValue( 'query', $property, $data );
317 }
318
319 protected function appendNamespaceAliases( $property ) {
320 global $wgContLang;
321 $aliases = array_merge( $this->getConfig()->get( 'NamespaceAliases' ),
322 $wgContLang->getNamespaceAliases() );
323 $namespaces = $wgContLang->getNamespaces();
324 $data = [];
325 foreach ( $aliases as $title => $ns ) {
326 if ( $namespaces[$ns] == $title ) {
327 // Don't list duplicates
328 continue;
329 }
330 $item = [
331 'id' => intval( $ns )
332 ];
333 ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
334 $data[] = $item;
335 }
336
337 sort( $data );
338
339 ApiResult::setIndexedTagName( $data, 'ns' );
340
341 return $this->getResult()->addValue( 'query', $property, $data );
342 }
343
344 protected function appendSpecialPageAliases( $property ) {
345 global $wgContLang;
346 $data = [];
347 $aliases = $wgContLang->getSpecialPageAliases();
348 foreach ( SpecialPageFactory::getNames() as $specialpage ) {
349 if ( isset( $aliases[$specialpage] ) ) {
350 $arr = [ 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] ];
351 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
352 $data[] = $arr;
353 }
354 }
355 ApiResult::setIndexedTagName( $data, 'specialpage' );
356
357 return $this->getResult()->addValue( 'query', $property, $data );
358 }
359
360 protected function appendMagicWords( $property ) {
361 global $wgContLang;
362 $data = [];
363 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
364 $caseSensitive = array_shift( $aliases );
365 $arr = [ 'name' => $magicword, 'aliases' => $aliases ];
366 $arr['case-sensitive'] = (bool)$caseSensitive;
367 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
368 $data[] = $arr;
369 }
370 ApiResult::setIndexedTagName( $data, 'magicword' );
371
372 return $this->getResult()->addValue( 'query', $property, $data );
373 }
374
375 protected function appendInterwikiMap( $property, $filter ) {
376 $local = null;
377 if ( $filter === 'local' ) {
378 $local = 1;
379 } elseif ( $filter === '!local' ) {
380 $local = 0;
381 } elseif ( $filter ) {
382 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
383 }
384
385 $params = $this->extractRequestParams();
386 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
387 $langNames = Language::fetchLanguageNames( $langCode );
388
389 $getPrefixes = Interwiki::getAllPrefixes( $local );
390 $extraLangPrefixes = $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' );
391 $localInterwikis = $this->getConfig()->get( 'LocalInterwikis' );
392 $data = [];
393
394 foreach ( $getPrefixes as $row ) {
395 $prefix = $row['iw_prefix'];
396 $val = [];
397 $val['prefix'] = $prefix;
398 if ( isset( $row['iw_local'] ) && $row['iw_local'] == '1' ) {
399 $val['local'] = true;
400 }
401 if ( isset( $row['iw_trans'] ) && $row['iw_trans'] == '1' ) {
402 $val['trans'] = true;
403 }
404
405 if ( isset( $langNames[$prefix] ) ) {
406 $val['language'] = $langNames[$prefix];
407 }
408 if ( in_array( $prefix, $localInterwikis ) ) {
409 $val['localinterwiki'] = true;
410 }
411 if ( in_array( $prefix, $extraLangPrefixes ) ) {
412 $val['extralanglink'] = true;
413
414 $linktext = wfMessage( "interlanguage-link-$prefix" );
415 if ( !$linktext->isDisabled() ) {
416 $val['linktext'] = $linktext->text();
417 }
418
419 $sitename = wfMessage( "interlanguage-link-sitename-$prefix" );
420 if ( !$sitename->isDisabled() ) {
421 $val['sitename'] = $sitename->text();
422 }
423 }
424
425 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
426 $val['protorel'] = substr( $row['iw_url'], 0, 2 ) == '//';
427 if ( isset( $row['iw_wikiid'] ) && $row['iw_wikiid'] !== '' ) {
428 $val['wikiid'] = $row['iw_wikiid'];
429 }
430 if ( isset( $row['iw_api'] ) && $row['iw_api'] !== '' ) {
431 $val['api'] = $row['iw_api'];
432 }
433
434 $data[] = $val;
435 }
436
437 ApiResult::setIndexedTagName( $data, 'iw' );
438
439 return $this->getResult()->addValue( 'query', $property, $data );
440 }
441
442 protected function appendDbReplLagInfo( $property, $includeAll ) {
443 $data = [];
444 $lb = wfGetLB();
445 $showHostnames = $this->getConfig()->get( 'ShowHostnames' );
446 if ( $includeAll ) {
447 if ( !$showHostnames ) {
448 $this->dieUsage(
449 'Cannot view all servers info unless $wgShowHostnames is true',
450 'includeAllDenied'
451 );
452 }
453
454 $lags = $lb->getLagTimes();
455 foreach ( $lags as $i => $lag ) {
456 $data[] = [
457 'host' => $lb->getServerName( $i ),
458 'lag' => $lag
459 ];
460 }
461 } else {
462 list( , $lag, $index ) = $lb->getMaxLag();
463 $data[] = [
464 'host' => $showHostnames
465 ? $lb->getServerName( $index )
466 : '',
467 'lag' => intval( $lag )
468 ];
469 }
470
471 ApiResult::setIndexedTagName( $data, 'db' );
472
473 return $this->getResult()->addValue( 'query', $property, $data );
474 }
475
476 protected function appendStatistics( $property ) {
477 $data = [];
478 $data['pages'] = intval( SiteStats::pages() );
479 $data['articles'] = intval( SiteStats::articles() );
480 $data['edits'] = intval( SiteStats::edits() );
481 $data['images'] = intval( SiteStats::images() );
482 $data['users'] = intval( SiteStats::users() );
483 $data['activeusers'] = intval( SiteStats::activeUsers() );
484 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
485 $data['jobs'] = intval( SiteStats::jobs() );
486
487 Hooks::run( 'APIQuerySiteInfoStatisticsInfo', [ &$data ] );
488
489 return $this->getResult()->addValue( 'query', $property, $data );
490 }
491
492 protected function appendUserGroups( $property, $numberInGroup ) {
493 $config = $this->getConfig();
494
495 $data = [];
496 $result = $this->getResult();
497 $allGroups = array_values( User::getAllGroups() );
498 foreach ( $config->get( 'GroupPermissions' ) as $group => $permissions ) {
499 $arr = [
500 'name' => $group,
501 'rights' => array_keys( $permissions, true ),
502 ];
503
504 if ( $numberInGroup ) {
505 $autopromote = $config->get( 'Autopromote' );
506
507 if ( $group == 'user' ) {
508 $arr['number'] = SiteStats::users();
509 // '*' and autopromote groups have no size
510 } elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
511 $arr['number'] = SiteStats::numberingroup( $group );
512 }
513 }
514
515 $groupArr = [
516 'add' => $config->get( 'AddGroups' ),
517 'remove' => $config->get( 'RemoveGroups' ),
518 'add-self' => $config->get( 'GroupsAddToSelf' ),
519 'remove-self' => $config->get( 'GroupsRemoveFromSelf' )
520 ];
521
522 foreach ( $groupArr as $type => $rights ) {
523 if ( isset( $rights[$group] ) ) {
524 if ( $rights[$group] === true ) {
525 $groups = $allGroups;
526 } else {
527 $groups = array_intersect( $rights[$group], $allGroups );
528 }
529 if ( $groups ) {
530 $arr[$type] = $groups;
531 ApiResult::setArrayType( $arr[$type], 'BCarray' );
532 ApiResult::setIndexedTagName( $arr[$type], 'group' );
533 }
534 }
535 }
536
537 ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
538 $data[] = $arr;
539 }
540
541 ApiResult::setIndexedTagName( $data, 'group' );
542
543 return $result->addValue( 'query', $property, $data );
544 }
545
546 protected function appendFileExtensions( $property ) {
547 $data = [];
548 foreach ( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) as $ext ) {
549 $data[] = [ 'ext' => $ext ];
550 }
551 ApiResult::setIndexedTagName( $data, 'fe' );
552
553 return $this->getResult()->addValue( 'query', $property, $data );
554 }
555
556 protected function appendInstalledLibraries( $property ) {
557 global $IP;
558 $path = "$IP/vendor/composer/installed.json";
559 if ( !file_exists( $path ) ) {
560 return true;
561 }
562
563 $data = [];
564 $installed = new ComposerInstalled( $path );
565 foreach ( $installed->getInstalledDependencies() as $name => $info ) {
566 if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
567 // Skip any extensions or skins since they'll be listed
568 // in their proper section
569 continue;
570 }
571 $data[] = [
572 'name' => $name,
573 'version' => $info['version'],
574 ];
575 }
576 ApiResult::setIndexedTagName( $data, 'library' );
577
578 return $this->getResult()->addValue( 'query', $property, $data );
579 }
580
581 protected function appendExtensions( $property ) {
582 $data = [];
583 foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $type => $extensions ) {
584 foreach ( $extensions as $ext ) {
585 $ret = [];
586 $ret['type'] = $type;
587 if ( isset( $ext['name'] ) ) {
588 $ret['name'] = $ext['name'];
589 }
590 if ( isset( $ext['namemsg'] ) ) {
591 $ret['namemsg'] = $ext['namemsg'];
592 }
593 if ( isset( $ext['description'] ) ) {
594 $ret['description'] = $ext['description'];
595 }
596 if ( isset( $ext['descriptionmsg'] ) ) {
597 // Can be a string or [ key, param1, param2, ... ]
598 if ( is_array( $ext['descriptionmsg'] ) ) {
599 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
600 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
601 ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
602 } else {
603 $ret['descriptionmsg'] = $ext['descriptionmsg'];
604 }
605 }
606 if ( isset( $ext['author'] ) ) {
607 $ret['author'] = is_array( $ext['author'] ) ?
608 implode( ', ', $ext['author'] ) : $ext['author'];
609 }
610 if ( isset( $ext['url'] ) ) {
611 $ret['url'] = $ext['url'];
612 }
613 if ( isset( $ext['version'] ) ) {
614 $ret['version'] = $ext['version'];
615 }
616 if ( isset( $ext['path'] ) ) {
617 $extensionPath = dirname( $ext['path'] );
618 $gitInfo = new GitInfo( $extensionPath );
619 $vcsVersion = $gitInfo->getHeadSHA1();
620 if ( $vcsVersion !== false ) {
621 $ret['vcs-system'] = 'git';
622 $ret['vcs-version'] = $vcsVersion;
623 $ret['vcs-url'] = $gitInfo->getHeadViewUrl();
624 $vcsDate = $gitInfo->getHeadCommitDate();
625 if ( $vcsDate !== false ) {
626 $ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
627 }
628 }
629
630 if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
631 $ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
632 $ret['license'] = SpecialPage::getTitleFor(
633 'Version',
634 "License/{$ext['name']}"
635 )->getLinkURL();
636 }
637
638 if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
639 $ret['credits'] = SpecialPage::getTitleFor(
640 'Version',
641 "Credits/{$ext['name']}"
642 )->getLinkURL();
643 }
644 }
645 $data[] = $ret;
646 }
647 }
648
649 ApiResult::setIndexedTagName( $data, 'ext' );
650
651 return $this->getResult()->addValue( 'query', $property, $data );
652 }
653
654 protected function appendRightsInfo( $property ) {
655 $config = $this->getConfig();
656 $rightsPage = $config->get( 'RightsPage' );
657 if ( is_string( $rightsPage ) ) {
658 $title = Title::newFromText( $rightsPage );
659 $url = wfExpandUrl( $title, PROTO_CURRENT );
660 } else {
661 $title = false;
662 $url = $config->get( 'RightsUrl' );
663 }
664 $text = $config->get( 'RightsText' );
665 if ( !$text && $title ) {
666 $text = $title->getPrefixedText();
667 }
668
669 $data = [
670 'url' => $url ?: '',
671 'text' => $text ?: ''
672 ];
673
674 return $this->getResult()->addValue( 'query', $property, $data );
675 }
676
677 protected function appendRestrictions( $property ) {
678 $config = $this->getConfig();
679 $data = [
680 'types' => $config->get( 'RestrictionTypes' ),
681 'levels' => $config->get( 'RestrictionLevels' ),
682 'cascadinglevels' => $config->get( 'CascadingRestrictionLevels' ),
683 'semiprotectedlevels' => $config->get( 'SemiprotectedRestrictionLevels' ),
684 ];
685
686 ApiResult::setArrayType( $data['types'], 'BCarray' );
687 ApiResult::setArrayType( $data['levels'], 'BCarray' );
688 ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
689 ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
690
691 ApiResult::setIndexedTagName( $data['types'], 'type' );
692 ApiResult::setIndexedTagName( $data['levels'], 'level' );
693 ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
694 ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
695
696 return $this->getResult()->addValue( 'query', $property, $data );
697 }
698
699 public function appendLanguages( $property ) {
700 $params = $this->extractRequestParams();
701 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
702 $langNames = Language::fetchLanguageNames( $langCode );
703
704 $data = [];
705
706 foreach ( $langNames as $code => $name ) {
707 $lang = [ 'code' => $code ];
708 ApiResult::setContentValue( $lang, 'name', $name );
709 $data[] = $lang;
710 }
711 ApiResult::setIndexedTagName( $data, 'lang' );
712
713 return $this->getResult()->addValue( 'query', $property, $data );
714 }
715
716 public function appendSkins( $property ) {
717 $data = [];
718 $allowed = Skin::getAllowedSkins();
719 $default = Skin::normalizeKey( 'default' );
720 foreach ( Skin::getSkinNames() as $name => $displayName ) {
721 $msg = $this->msg( "skinname-{$name}" );
722 $code = $this->getParameter( 'inlanguagecode' );
723 if ( $code && Language::isValidCode( $code ) ) {
724 $msg->inLanguage( $code );
725 } else {
726 $msg->inContentLanguage();
727 }
728 if ( $msg->exists() ) {
729 $displayName = $msg->text();
730 }
731 $skin = [ 'code' => $name ];
732 ApiResult::setContentValue( $skin, 'name', $displayName );
733 if ( !isset( $allowed[$name] ) ) {
734 $skin['unusable'] = true;
735 }
736 if ( $name === $default ) {
737 $skin['default'] = true;
738 }
739 $data[] = $skin;
740 }
741 ApiResult::setIndexedTagName( $data, 'skin' );
742
743 return $this->getResult()->addValue( 'query', $property, $data );
744 }
745
746 public function appendExtensionTags( $property ) {
747 global $wgParser;
748 $wgParser->firstCallInit();
749 $tags = array_map( [ $this, 'formatParserTags' ], $wgParser->getTags() );
750 ApiResult::setArrayType( $tags, 'BCarray' );
751 ApiResult::setIndexedTagName( $tags, 't' );
752
753 return $this->getResult()->addValue( 'query', $property, $tags );
754 }
755
756 public function appendFunctionHooks( $property ) {
757 global $wgParser;
758 $wgParser->firstCallInit();
759 $hooks = $wgParser->getFunctionHooks();
760 ApiResult::setArrayType( $hooks, 'BCarray' );
761 ApiResult::setIndexedTagName( $hooks, 'h' );
762
763 return $this->getResult()->addValue( 'query', $property, $hooks );
764 }
765
766 public function appendVariables( $property ) {
767 $variables = MagicWord::getVariableIDs();
768 ApiResult::setArrayType( $variables, 'BCarray' );
769 ApiResult::setIndexedTagName( $variables, 'v' );
770
771 return $this->getResult()->addValue( 'query', $property, $variables );
772 }
773
774 public function appendProtocols( $property ) {
775 // Make a copy of the global so we don't try to set the _element key of it - bug 45130
776 $protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
777 ApiResult::setArrayType( $protocols, 'BCarray' );
778 ApiResult::setIndexedTagName( $protocols, 'p' );
779
780 return $this->getResult()->addValue( 'query', $property, $protocols );
781 }
782
783 public function appendDefaultOptions( $property ) {
784 $options = User::getDefaultOptions();
785 $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
786 return $this->getResult()->addValue( 'query', $property, $options );
787 }
788
789 public function appendUploadDialog( $property ) {
790 $config = $this->getConfig()->get( 'UploadDialog' );
791 return $this->getResult()->addValue( 'query', $property, $config );
792 }
793
794 private function formatParserTags( $item ) {
795 return "<{$item}>";
796 }
797
798 public function appendSubscribedHooks( $property ) {
799 $hooks = $this->getConfig()->get( 'Hooks' );
800 $myWgHooks = $hooks;
801 ksort( $myWgHooks );
802
803 $data = [];
804 foreach ( $myWgHooks as $name => $subscribers ) {
805 $arr = [
806 'name' => $name,
807 'subscribers' => array_map( [ 'SpecialVersion', 'arrayToString' ], $subscribers ),
808 ];
809
810 ApiResult::setArrayType( $arr['subscribers'], 'array' );
811 ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
812 $data[] = $arr;
813 }
814
815 ApiResult::setIndexedTagName( $data, 'hook' );
816
817 return $this->getResult()->addValue( 'query', $property, $data );
818 }
819
820 public function getCacheMode( $params ) {
821 // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
822 if (
823 count( $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' ) ) &&
824 !is_null( $params['prop'] ) &&
825 in_array( 'interwikimap', $params['prop'] )
826 ) {
827 return 'anon-public-user-private';
828 }
829
830 return 'public';
831 }
832
833 public function getAllowedParams() {
834 return [
835 'prop' => [
836 ApiBase::PARAM_DFLT => 'general',
837 ApiBase::PARAM_ISMULTI => true,
838 ApiBase::PARAM_TYPE => [
839 'general',
840 'namespaces',
841 'namespacealiases',
842 'specialpagealiases',
843 'magicwords',
844 'interwikimap',
845 'dbrepllag',
846 'statistics',
847 'usergroups',
848 'libraries',
849 'extensions',
850 'fileextensions',
851 'rightsinfo',
852 'restrictions',
853 'languages',
854 'skins',
855 'extensiontags',
856 'functionhooks',
857 'showhooks',
858 'variables',
859 'protocols',
860 'defaultoptions',
861 'uploaddialog',
862 ],
863 ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
864 ],
865 'filteriw' => [
866 ApiBase::PARAM_TYPE => [
867 'local',
868 '!local',
869 ]
870 ],
871 'showalldb' => false,
872 'numberingroup' => false,
873 'inlanguagecode' => null,
874 ];
875 }
876
877 protected function getExamplesMessages() {
878 return [
879 'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
880 => 'apihelp-query+siteinfo-example-simple',
881 'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
882 => 'apihelp-query+siteinfo-example-interwiki',
883 'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
884 => 'apihelp-query+siteinfo-example-replag',
885 ];
886 }
887
888 public function getHelpUrls() {
889 return 'https://www.mediawiki.org/wiki/API:Siteinfo';
890 }
891 }