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