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