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