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