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