99f722da9bacb76d977424314d55c3ac5199e912
[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
582 protected function appendExtensions( $property ) {
583 $data = [];
584 foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $type => $extensions ) {
585 foreach ( $extensions as $ext ) {
586 $ret = [];
587 $ret['type'] = $type;
588 if ( isset( $ext['name'] ) ) {
589 $ret['name'] = $ext['name'];
590 }
591 if ( isset( $ext['namemsg'] ) ) {
592 $ret['namemsg'] = $ext['namemsg'];
593 }
594 if ( isset( $ext['description'] ) ) {
595 $ret['description'] = $ext['description'];
596 }
597 if ( isset( $ext['descriptionmsg'] ) ) {
598 // Can be a string or [ key, param1, param2, ... ]
599 if ( is_array( $ext['descriptionmsg'] ) ) {
600 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
601 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
602 ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
603 } else {
604 $ret['descriptionmsg'] = $ext['descriptionmsg'];
605 }
606 }
607 if ( isset( $ext['author'] ) ) {
608 $ret['author'] = is_array( $ext['author'] ) ?
609 implode( ', ', $ext['author'] ) : $ext['author'];
610 }
611 if ( isset( $ext['url'] ) ) {
612 $ret['url'] = $ext['url'];
613 }
614 if ( isset( $ext['version'] ) ) {
615 $ret['version'] = $ext['version'];
616 }
617 if ( isset( $ext['path'] ) ) {
618 $extensionPath = dirname( $ext['path'] );
619 $gitInfo = new GitInfo( $extensionPath );
620 $vcsVersion = $gitInfo->getHeadSHA1();
621 if ( $vcsVersion !== false ) {
622 $ret['vcs-system'] = 'git';
623 $ret['vcs-version'] = $vcsVersion;
624 $ret['vcs-url'] = $gitInfo->getHeadViewUrl();
625 $vcsDate = $gitInfo->getHeadCommitDate();
626 if ( $vcsDate !== false ) {
627 $ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
628 }
629 }
630
631 if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
632 $ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
633 $ret['license'] = SpecialPage::getTitleFor(
634 'Version',
635 "License/{$ext['name']}"
636 )->getLinkURL();
637 }
638
639 if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
640 $ret['credits'] = SpecialPage::getTitleFor(
641 'Version',
642 "Credits/{$ext['name']}"
643 )->getLinkURL();
644 }
645 }
646 $data[] = $ret;
647 }
648 }
649
650 ApiResult::setIndexedTagName( $data, 'ext' );
651
652 return $this->getResult()->addValue( 'query', $property, $data );
653 }
654
655 protected function appendRightsInfo( $property ) {
656 $config = $this->getConfig();
657 $rightsPage = $config->get( 'RightsPage' );
658 if ( is_string( $rightsPage ) ) {
659 $title = Title::newFromText( $rightsPage );
660 $url = wfExpandUrl( $title, PROTO_CURRENT );
661 } else {
662 $title = false;
663 $url = $config->get( 'RightsUrl' );
664 }
665 $text = $config->get( 'RightsText' );
666 if ( !$text && $title ) {
667 $text = $title->getPrefixedText();
668 }
669
670 $data = [
671 'url' => $url ?: '',
672 'text' => $text ?: ''
673 ];
674
675 return $this->getResult()->addValue( 'query', $property, $data );
676 }
677
678 protected function appendRestrictions( $property ) {
679 $config = $this->getConfig();
680 $data = [
681 'types' => $config->get( 'RestrictionTypes' ),
682 'levels' => $config->get( 'RestrictionLevels' ),
683 'cascadinglevels' => $config->get( 'CascadingRestrictionLevels' ),
684 'semiprotectedlevels' => $config->get( 'SemiprotectedRestrictionLevels' ),
685 ];
686
687 ApiResult::setArrayType( $data['types'], 'BCarray' );
688 ApiResult::setArrayType( $data['levels'], 'BCarray' );
689 ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
690 ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
691
692 ApiResult::setIndexedTagName( $data['types'], 'type' );
693 ApiResult::setIndexedTagName( $data['levels'], 'level' );
694 ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
695 ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
696
697 return $this->getResult()->addValue( 'query', $property, $data );
698 }
699
700 public function appendLanguages( $property ) {
701 $params = $this->extractRequestParams();
702 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
703 $langNames = Language::fetchLanguageNames( $langCode );
704
705 $data = [];
706
707 foreach ( $langNames as $code => $name ) {
708 $lang = [ 'code' => $code ];
709 ApiResult::setContentValue( $lang, 'name', $name );
710 $data[] = $lang;
711 }
712 ApiResult::setIndexedTagName( $data, 'lang' );
713
714 return $this->getResult()->addValue( 'query', $property, $data );
715 }
716
717 public function appendSkins( $property ) {
718 $data = [];
719 $allowed = Skin::getAllowedSkins();
720 $default = Skin::normalizeKey( 'default' );
721 foreach ( Skin::getSkinNames() as $name => $displayName ) {
722 $msg = $this->msg( "skinname-{$name}" );
723 $code = $this->getParameter( 'inlanguagecode' );
724 if ( $code && Language::isValidCode( $code ) ) {
725 $msg->inLanguage( $code );
726 } else {
727 $msg->inContentLanguage();
728 }
729 if ( $msg->exists() ) {
730 $displayName = $msg->text();
731 }
732 $skin = [ 'code' => $name ];
733 ApiResult::setContentValue( $skin, 'name', $displayName );
734 if ( !isset( $allowed[$name] ) ) {
735 $skin['unusable'] = true;
736 }
737 if ( $name === $default ) {
738 $skin['default'] = true;
739 }
740 $data[] = $skin;
741 }
742 ApiResult::setIndexedTagName( $data, 'skin' );
743
744 return $this->getResult()->addValue( 'query', $property, $data );
745 }
746
747 public function appendExtensionTags( $property ) {
748 global $wgParser;
749 $wgParser->firstCallInit();
750 $tags = array_map( [ $this, 'formatParserTags' ], $wgParser->getTags() );
751 ApiResult::setArrayType( $tags, 'BCarray' );
752 ApiResult::setIndexedTagName( $tags, 't' );
753
754 return $this->getResult()->addValue( 'query', $property, $tags );
755 }
756
757 public function appendFunctionHooks( $property ) {
758 global $wgParser;
759 $wgParser->firstCallInit();
760 $hooks = $wgParser->getFunctionHooks();
761 ApiResult::setArrayType( $hooks, 'BCarray' );
762 ApiResult::setIndexedTagName( $hooks, 'h' );
763
764 return $this->getResult()->addValue( 'query', $property, $hooks );
765 }
766
767 public function appendVariables( $property ) {
768 $variables = MagicWord::getVariableIDs();
769 ApiResult::setArrayType( $variables, 'BCarray' );
770 ApiResult::setIndexedTagName( $variables, 'v' );
771
772 return $this->getResult()->addValue( 'query', $property, $variables );
773 }
774
775 public function appendProtocols( $property ) {
776 // Make a copy of the global so we don't try to set the _element key of it - bug 45130
777 $protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
778 ApiResult::setArrayType( $protocols, 'BCarray' );
779 ApiResult::setIndexedTagName( $protocols, 'p' );
780
781 return $this->getResult()->addValue( 'query', $property, $protocols );
782 }
783
784 public function appendDefaultOptions( $property ) {
785 $options = User::getDefaultOptions();
786 $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
787 return $this->getResult()->addValue( 'query', $property, $options );
788 }
789
790 public function appendUploadDialog( $property ) {
791 $config = $this->getConfig()->get( 'UploadDialog' );
792 return $this->getResult()->addValue( 'query', $property, $config );
793 }
794
795 private function formatParserTags( $item ) {
796 return "<{$item}>";
797 }
798
799 public function appendSubscribedHooks( $property ) {
800 $hooks = $this->getConfig()->get( 'Hooks' );
801 $myWgHooks = $hooks;
802 ksort( $myWgHooks );
803
804 $data = [];
805 foreach ( $myWgHooks as $name => $subscribers ) {
806 $arr = [
807 'name' => $name,
808 'subscribers' => array_map( [ 'SpecialVersion', 'arrayToString' ], $subscribers ),
809 ];
810
811 ApiResult::setArrayType( $arr['subscribers'], 'array' );
812 ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
813 $data[] = $arr;
814 }
815
816 ApiResult::setIndexedTagName( $data, 'hook' );
817
818 return $this->getResult()->addValue( 'query', $property, $data );
819 }
820
821 public function getCacheMode( $params ) {
822 // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
823 if (
824 count( $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' ) ) &&
825 !is_null( $params['prop'] ) &&
826 in_array( 'interwikimap', $params['prop'] )
827 ) {
828 return 'anon-public-user-private';
829 }
830
831 return 'public';
832 }
833
834 public function getAllowedParams() {
835 return [
836 'prop' => [
837 ApiBase::PARAM_DFLT => 'general',
838 ApiBase::PARAM_ISMULTI => true,
839 ApiBase::PARAM_TYPE => [
840 'general',
841 'namespaces',
842 'namespacealiases',
843 'specialpagealiases',
844 'magicwords',
845 'interwikimap',
846 'dbrepllag',
847 'statistics',
848 'usergroups',
849 'libraries',
850 'extensions',
851 'fileextensions',
852 'rightsinfo',
853 'restrictions',
854 'languages',
855 'skins',
856 'extensiontags',
857 'functionhooks',
858 'showhooks',
859 'variables',
860 'protocols',
861 'defaultoptions',
862 'uploaddialog',
863 ],
864 ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
865 ],
866 'filteriw' => [
867 ApiBase::PARAM_TYPE => [
868 'local',
869 '!local',
870 ]
871 ],
872 'showalldb' => false,
873 'numberingroup' => false,
874 'inlanguagecode' => null,
875 ];
876 }
877
878 protected function getExamplesMessages() {
879 return [
880 'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
881 => 'apihelp-query+siteinfo-example-simple',
882 'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
883 => 'apihelp-query+siteinfo-example-interwiki',
884 'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
885 => 'apihelp-query+siteinfo-example-replag',
886 ];
887 }
888
889 public function getHelpUrls() {
890 return 'https://www.mediawiki.org/wiki/API:Siteinfo';
891 }
892 }