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