Merge "Extracted some code from PrefixSearch for re-usability by other search engines."
[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 $data['invalidusernamechars'] = $config->get( 'InvalidUsernameCharacters' );
180
181 global $IP;
182 $git = SpecialVersion::getGitHeadSha1( $IP );
183 if ( $git ) {
184 $data['git-hash'] = $git;
185 $data['git-branch'] =
186 SpecialVersion::getGitCurrentBranch( $GLOBALS['IP'] );
187 } else {
188 $svn = SpecialVersion::getSvnRevision( $IP );
189 if ( $svn ) {
190 $data['rev'] = $svn;
191 }
192 }
193
194 // 'case-insensitive' option is reserved for future
195 $data['case'] = $config->get( 'CapitalLinks' ) ? 'first-letter' : 'case-sensitive';
196 $data['lang'] = $config->get( 'LanguageCode' );
197
198 $fallbacks = array();
199 foreach ( $wgContLang->getFallbackLanguages() as $code ) {
200 $fallbacks[] = array( 'code' => $code );
201 }
202 $data['fallback'] = $fallbacks;
203 ApiResult::setIndexedTagName( $data['fallback'], 'lang' );
204
205 if ( $wgContLang->hasVariants() ) {
206 $variants = array();
207 foreach ( $wgContLang->getVariants() as $code ) {
208 $variants[] = array(
209 'code' => $code,
210 'name' => $wgContLang->getVariantname( $code ),
211 );
212 }
213 $data['variants'] = $variants;
214 ApiResult::setIndexedTagName( $data['variants'], 'lang' );
215 }
216
217 $data['rtl'] = $wgContLang->isRTL();
218 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
219
220 $data['readonly'] = wfReadOnly();
221 if ( $data['readonly'] ) {
222 $data['readonlyreason'] = wfReadOnlyReason();
223 }
224 $data['writeapi'] = (bool)$config->get( 'EnableWriteAPI' );
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['maxuploadsize'] = UploadBase::getMaxUploadSize();
249 $data['minuploadchunksize'] = (int)$this->getConfig()->get( 'MinUploadChunkSize' );
250
251 $data['thumblimits'] = $config->get( 'ThumbLimits' );
252 ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
253 ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
254 $data['imagelimits'] = array();
255 ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
256 ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
257 foreach ( $config->get( 'ImageLimits' ) as $k => $limit ) {
258 $data['imagelimits'][$k] = array( 'width' => $limit[0], 'height' => $limit[1] );
259 }
260
261 $favicon = $config->get( 'Favicon' );
262 if ( !empty( $favicon ) ) {
263 // wgFavicon can either be a relative or an absolute path
264 // make sure we always return an absolute path
265 $data['favicon'] = wfExpandUrl( $favicon, PROTO_RELATIVE );
266 }
267
268 $data['centralidlookupprovider'] = $this->getConfig()->get( 'CentralIdLookupProvider' );
269 $providerIds = array_keys( $this->getConfig()->get( 'CentralIdLookupProviders' ) );
270 $data['allcentralidlookupproviders'] = $providerIds;
271
272 Hooks::run( 'APIQuerySiteInfoGeneralInfo', array( $this, &$data ) );
273
274 return $this->getResult()->addValue( 'query', $property, $data );
275 }
276
277 protected function appendNamespaces( $property ) {
278 global $wgContLang;
279 $data = array(
280 ApiResult::META_TYPE => 'assoc',
281 );
282 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
283 $data[$ns] = array(
284 'id' => intval( $ns ),
285 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
286 );
287 ApiResult::setContentValue( $data[$ns], 'name', $title );
288 $canonical = MWNamespace::getCanonicalName( $ns );
289
290 $data[$ns]['subpages'] = MWNamespace::hasSubpages( $ns );
291
292 if ( $canonical ) {
293 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
294 }
295
296 $data[$ns]['content'] = MWNamespace::isContent( $ns );
297 $data[$ns]['nonincludable'] = MWNamespace::isNonincludable( $ns );
298
299 $contentmodel = MWNamespace::getNamespaceContentModel( $ns );
300 if ( $contentmodel ) {
301 $data[$ns]['defaultcontentmodel'] = $contentmodel;
302 }
303 }
304
305 ApiResult::setArrayType( $data, 'assoc' );
306 ApiResult::setIndexedTagName( $data, 'ns' );
307
308 return $this->getResult()->addValue( 'query', $property, $data );
309 }
310
311 protected function appendNamespaceAliases( $property ) {
312 global $wgContLang;
313 $aliases = array_merge( $this->getConfig()->get( 'NamespaceAliases' ),
314 $wgContLang->getNamespaceAliases() );
315 $namespaces = $wgContLang->getNamespaces();
316 $data = array();
317 foreach ( $aliases as $title => $ns ) {
318 if ( $namespaces[$ns] == $title ) {
319 // Don't list duplicates
320 continue;
321 }
322 $item = array(
323 'id' => intval( $ns )
324 );
325 ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
326 $data[] = $item;
327 }
328
329 sort( $data );
330
331 ApiResult::setIndexedTagName( $data, 'ns' );
332
333 return $this->getResult()->addValue( 'query', $property, $data );
334 }
335
336 protected function appendSpecialPageAliases( $property ) {
337 global $wgContLang;
338 $data = array();
339 $aliases = $wgContLang->getSpecialPageAliases();
340 foreach ( SpecialPageFactory::getNames() as $specialpage ) {
341 if ( isset( $aliases[$specialpage] ) ) {
342 $arr = array( 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] );
343 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
344 $data[] = $arr;
345 }
346 }
347 ApiResult::setIndexedTagName( $data, 'specialpage' );
348
349 return $this->getResult()->addValue( 'query', $property, $data );
350 }
351
352 protected function appendMagicWords( $property ) {
353 global $wgContLang;
354 $data = array();
355 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
356 $caseSensitive = array_shift( $aliases );
357 $arr = array( 'name' => $magicword, 'aliases' => $aliases );
358 $arr['case-sensitive'] = (bool)$caseSensitive;
359 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
360 $data[] = $arr;
361 }
362 ApiResult::setIndexedTagName( $data, 'magicword' );
363
364 return $this->getResult()->addValue( 'query', $property, $data );
365 }
366
367 protected function appendInterwikiMap( $property, $filter ) {
368 $local = null;
369 if ( $filter === 'local' ) {
370 $local = 1;
371 } elseif ( $filter === '!local' ) {
372 $local = 0;
373 } elseif ( $filter ) {
374 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
375 }
376
377 $params = $this->extractRequestParams();
378 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
379 $langNames = Language::fetchLanguageNames( $langCode );
380
381 $getPrefixes = Interwiki::getAllPrefixes( $local );
382 $extraLangPrefixes = $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' );
383 $localInterwikis = $this->getConfig()->get( 'LocalInterwikis' );
384 $data = array();
385
386 foreach ( $getPrefixes as $row ) {
387 $prefix = $row['iw_prefix'];
388 $val = array();
389 $val['prefix'] = $prefix;
390 if ( isset( $row['iw_local'] ) && $row['iw_local'] == '1' ) {
391 $val['local'] = true;
392 }
393 if ( isset( $row['iw_trans'] ) && $row['iw_trans'] == '1' ) {
394 $val['trans'] = true;
395 }
396
397 if ( isset( $langNames[$prefix] ) ) {
398 $val['language'] = $langNames[$prefix];
399 }
400 if ( in_array( $prefix, $localInterwikis ) ) {
401 $val['localinterwiki'] = true;
402 }
403 if ( in_array( $prefix, $extraLangPrefixes ) ) {
404 $val['extralanglink'] = true;
405
406 $linktext = wfMessage( "interlanguage-link-$prefix" );
407 if ( !$linktext->isDisabled() ) {
408 $val['linktext'] = $linktext->text();
409 }
410
411 $sitename = wfMessage( "interlanguage-link-sitename-$prefix" );
412 if ( !$sitename->isDisabled() ) {
413 $val['sitename'] = $sitename->text();
414 }
415 }
416
417 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
418 $val['protorel'] = substr( $row['iw_url'], 0, 2 ) == '//';
419 if ( isset( $row['iw_wikiid'] ) && $row['iw_wikiid'] !== '' ) {
420 $val['wikiid'] = $row['iw_wikiid'];
421 }
422 if ( isset( $row['iw_api'] ) && $row['iw_api'] !== '' ) {
423 $val['api'] = $row['iw_api'];
424 }
425
426 $data[] = $val;
427 }
428
429 ApiResult::setIndexedTagName( $data, 'iw' );
430
431 return $this->getResult()->addValue( 'query', $property, $data );
432 }
433
434 protected function appendDbReplLagInfo( $property, $includeAll ) {
435 $data = array();
436 $lb = wfGetLB();
437 $showHostnames = $this->getConfig()->get( 'ShowHostnames' );
438 if ( $includeAll ) {
439 if ( !$showHostnames ) {
440 $this->dieUsage(
441 'Cannot view all servers info unless $wgShowHostnames is true',
442 'includeAllDenied'
443 );
444 }
445
446 $lags = $lb->getLagTimes();
447 foreach ( $lags as $i => $lag ) {
448 $data[] = array(
449 'host' => $lb->getServerName( $i ),
450 'lag' => $lag
451 );
452 }
453 } else {
454 list( , $lag, $index ) = $lb->getMaxLag();
455 $data[] = array(
456 'host' => $showHostnames
457 ? $lb->getServerName( $index )
458 : '',
459 'lag' => intval( $lag )
460 );
461 }
462
463 ApiResult::setIndexedTagName( $data, 'db' );
464
465 return $this->getResult()->addValue( 'query', $property, $data );
466 }
467
468 protected function appendStatistics( $property ) {
469 $data = array();
470 $data['pages'] = intval( SiteStats::pages() );
471 $data['articles'] = intval( SiteStats::articles() );
472 $data['edits'] = intval( SiteStats::edits() );
473 $data['images'] = intval( SiteStats::images() );
474 $data['users'] = intval( SiteStats::users() );
475 $data['activeusers'] = intval( SiteStats::activeUsers() );
476 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
477 $data['jobs'] = intval( SiteStats::jobs() );
478
479 Hooks::run( 'APIQuerySiteInfoStatisticsInfo', array( &$data ) );
480
481 return $this->getResult()->addValue( 'query', $property, $data );
482 }
483
484 protected function appendUserGroups( $property, $numberInGroup ) {
485 $config = $this->getConfig();
486
487 $data = array();
488 $result = $this->getResult();
489 $allGroups = User::getAllGroups();
490 foreach ( $config->get( 'GroupPermissions' ) as $group => $permissions ) {
491 $arr = array(
492 'name' => $group,
493 'rights' => array_keys( $permissions, true ),
494 );
495
496 if ( $numberInGroup ) {
497 $autopromote = $config->get( 'Autopromote' );
498
499 if ( $group == 'user' ) {
500 $arr['number'] = SiteStats::users();
501 // '*' and autopromote groups have no size
502 } elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
503 $arr['number'] = SiteStats::numberingroup( $group );
504 }
505 }
506
507 $groupArr = array(
508 'add' => $config->get( 'AddGroups' ),
509 'remove' => $config->get( 'RemoveGroups' ),
510 'add-self' => $config->get( 'GroupsAddToSelf' ),
511 'remove-self' => $config->get( 'GroupsRemoveFromSelf' )
512 );
513
514 foreach ( $groupArr as $type => $rights ) {
515 if ( isset( $rights[$group] ) ) {
516 $groups = array_intersect( $rights[$group], $allGroups );
517 if ( $groups ) {
518 $arr[$type] = $groups;
519 ApiResult::setArrayType( $arr[$type], 'BCarray' );
520 ApiResult::setIndexedTagName( $arr[$type], 'group' );
521 }
522 }
523 }
524
525 ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
526 $data[] = $arr;
527 }
528
529 ApiResult::setIndexedTagName( $data, 'group' );
530
531 return $result->addValue( 'query', $property, $data );
532 }
533
534 protected function appendFileExtensions( $property ) {
535 $data = array();
536 foreach ( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) as $ext ) {
537 $data[] = array( 'ext' => $ext );
538 }
539 ApiResult::setIndexedTagName( $data, 'fe' );
540
541 return $this->getResult()->addValue( 'query', $property, $data );
542 }
543
544 protected function appendInstalledLibraries( $property ) {
545 global $IP;
546 $path = "$IP/vendor/composer/installed.json";
547 if ( !file_exists( $path ) ) {
548 return true;
549 }
550
551 $data = array();
552 $installed = new ComposerInstalled( $path );
553 foreach ( $installed->getInstalledDependencies() as $name => $info ) {
554 if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
555 // Skip any extensions or skins since they'll be listed
556 // in their proper section
557 continue;
558 }
559 $data[] = array(
560 'name' => $name,
561 'version' => $info['version'],
562 );
563 }
564 ApiResult::setIndexedTagName( $data, 'library' );
565
566 return $this->getResult()->addValue( 'query', $property, $data );
567
568 }
569
570 protected function appendExtensions( $property ) {
571 $data = array();
572 foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $type => $extensions ) {
573 foreach ( $extensions as $ext ) {
574 $ret = array();
575 $ret['type'] = $type;
576 if ( isset( $ext['name'] ) ) {
577 $ret['name'] = $ext['name'];
578 }
579 if ( isset( $ext['namemsg'] ) ) {
580 $ret['namemsg'] = $ext['namemsg'];
581 }
582 if ( isset( $ext['description'] ) ) {
583 $ret['description'] = $ext['description'];
584 }
585 if ( isset( $ext['descriptionmsg'] ) ) {
586 // Can be a string or array( key, param1, param2, ... )
587 if ( is_array( $ext['descriptionmsg'] ) ) {
588 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
589 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
590 ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
591 } else {
592 $ret['descriptionmsg'] = $ext['descriptionmsg'];
593 }
594 }
595 if ( isset( $ext['author'] ) ) {
596 $ret['author'] = is_array( $ext['author'] ) ?
597 implode( ', ', $ext['author'] ) : $ext['author'];
598 }
599 if ( isset( $ext['url'] ) ) {
600 $ret['url'] = $ext['url'];
601 }
602 if ( isset( $ext['version'] ) ) {
603 $ret['version'] = $ext['version'];
604 } elseif ( isset( $ext['svn-revision'] ) &&
605 preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
606 $ext['svn-revision'], $m )
607 ) {
608 $ret['version'] = 'r' . $m[1];
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 } else {
623 $svnInfo = SpecialVersion::getSvnInfo( $extensionPath );
624 if ( $svnInfo !== false ) {
625 $ret['vcs-system'] = 'svn';
626 $ret['vcs-version'] = $svnInfo['checkout-rev'];
627 $ret['vcs-url'] = isset( $svnInfo['viewvc-url'] ) ? $svnInfo['viewvc-url'] : '';
628 }
629 }
630
631 if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
632 $ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
633 $ret['license'] = SpecialPage::getTitleFor(
634 'Version',
635 "License/{$ext['name']}"
636 )->getLinkURL();
637 }
638
639 if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
640 $ret['credits'] = SpecialPage::getTitleFor(
641 'Version',
642 "Credits/{$ext['name']}"
643 )->getLinkURL();
644 }
645 }
646 $data[] = $ret;
647 }
648 }
649
650 ApiResult::setIndexedTagName( $data, 'ext' );
651
652 return $this->getResult()->addValue( 'query', $property, $data );
653 }
654
655 protected function appendRightsInfo( $property ) {
656 $config = $this->getConfig();
657 $rightsPage = $config->get( 'RightsPage' );
658 if ( is_string( $rightsPage ) ) {
659 $title = Title::newFromText( $rightsPage );
660 $url = wfExpandUrl( $title, PROTO_CURRENT );
661 } else {
662 $title = false;
663 $url = $config->get( 'RightsUrl' );
664 }
665 $text = $config->get( 'RightsText' );
666 if ( !$text && $title ) {
667 $text = $title->getPrefixedText();
668 }
669
670 $data = array(
671 'url' => $url ? $url : '',
672 'text' => $text ? $text : ''
673 );
674
675 return $this->getResult()->addValue( 'query', $property, $data );
676 }
677
678 protected function appendRestrictions( $property ) {
679 $config = $this->getConfig();
680 $data = array(
681 'types' => $config->get( 'RestrictionTypes' ),
682 'levels' => $config->get( 'RestrictionLevels' ),
683 'cascadinglevels' => $config->get( 'CascadingRestrictionLevels' ),
684 'semiprotectedlevels' => $config->get( 'SemiprotectedRestrictionLevels' ),
685 );
686
687 ApiResult::setArrayType( $data['types'], 'BCarray' );
688 ApiResult::setArrayType( $data['levels'], 'BCarray' );
689 ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
690 ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
691
692 ApiResult::setIndexedTagName( $data['types'], 'type' );
693 ApiResult::setIndexedTagName( $data['levels'], 'level' );
694 ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
695 ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
696
697 return $this->getResult()->addValue( 'query', $property, $data );
698 }
699
700 public function appendLanguages( $property ) {
701 $params = $this->extractRequestParams();
702 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
703 $langNames = Language::fetchLanguageNames( $langCode );
704
705 $data = array();
706
707 foreach ( $langNames as $code => $name ) {
708 $lang = array( 'code' => $code );
709 ApiResult::setContentValue( $lang, 'name', $name );
710 $data[] = $lang;
711 }
712 ApiResult::setIndexedTagName( $data, 'lang' );
713
714 return $this->getResult()->addValue( 'query', $property, $data );
715 }
716
717 public function appendSkins( $property ) {
718 $data = array();
719 $allowed = Skin::getAllowedSkins();
720 $default = Skin::normalizeKey( 'default' );
721 foreach ( Skin::getSkinNames() as $name => $displayName ) {
722 $msg = $this->msg( "skinname-{$name}" );
723 $code = $this->getParameter( 'inlanguagecode' );
724 if ( $code && Language::isValidCode( $code ) ) {
725 $msg->inLanguage( $code );
726 } else {
727 $msg->inContentLanguage();
728 }
729 if ( $msg->exists() ) {
730 $displayName = $msg->text();
731 }
732 $skin = array( 'code' => $name );
733 ApiResult::setContentValue( $skin, 'name', $displayName );
734 if ( !isset( $allowed[$name] ) ) {
735 $skin['unusable'] = true;
736 }
737 if ( $name === $default ) {
738 $skin['default'] = true;
739 }
740 $data[] = $skin;
741 }
742 ApiResult::setIndexedTagName( $data, 'skin' );
743
744 return $this->getResult()->addValue( 'query', $property, $data );
745 }
746
747 public function appendExtensionTags( $property ) {
748 global $wgParser;
749 $wgParser->firstCallInit();
750 $tags = array_map( array( $this, 'formatParserTags' ), $wgParser->getTags() );
751 ApiResult::setArrayType( $tags, 'BCarray' );
752 ApiResult::setIndexedTagName( $tags, 't' );
753
754 return $this->getResult()->addValue( 'query', $property, $tags );
755 }
756
757 public function appendFunctionHooks( $property ) {
758 global $wgParser;
759 $wgParser->firstCallInit();
760 $hooks = $wgParser->getFunctionHooks();
761 ApiResult::setArrayType( $hooks, 'BCarray' );
762 ApiResult::setIndexedTagName( $hooks, 'h' );
763
764 return $this->getResult()->addValue( 'query', $property, $hooks );
765 }
766
767 public function appendVariables( $property ) {
768 $variables = MagicWord::getVariableIDs();
769 ApiResult::setArrayType( $variables, 'BCarray' );
770 ApiResult::setIndexedTagName( $variables, 'v' );
771
772 return $this->getResult()->addValue( 'query', $property, $variables );
773 }
774
775 public function appendProtocols( $property ) {
776 // Make a copy of the global so we don't try to set the _element key of it - bug 45130
777 $protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
778 ApiResult::setArrayType( $protocols, 'BCarray' );
779 ApiResult::setIndexedTagName( $protocols, 'p' );
780
781 return $this->getResult()->addValue( 'query', $property, $protocols );
782 }
783
784 public function appendDefaultOptions( $property ) {
785 $options = User::getDefaultOptions();
786 $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
787 return $this->getResult()->addValue( 'query', $property, $options );
788 }
789
790 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 = array();
800 foreach ( $myWgHooks as $name => $subscribers ) {
801 $arr = array(
802 'name' => $name,
803 'subscribers' => array_map( array( '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 array(
831 'prop' => array(
832 ApiBase::PARAM_DFLT => 'general',
833 ApiBase::PARAM_ISMULTI => true,
834 ApiBase::PARAM_TYPE => array(
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 ),
858 ApiBase::PARAM_HELP_MSG_PER_VALUE => array(),
859 ),
860 'filteriw' => array(
861 ApiBase::PARAM_TYPE => array(
862 'local',
863 '!local',
864 )
865 ),
866 'showalldb' => false,
867 'numberingroup' => false,
868 'inlanguagecode' => null,
869 );
870 }
871
872 protected function getExamplesMessages() {
873 return array(
874 'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
875 => 'apihelp-query+siteinfo-example-simple',
876 'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
877 => 'apihelp-query+siteinfo-example-interwiki',
878 'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
879 => 'apihelp-query+siteinfo-example-replag',
880 );
881 }
882
883 public function getHelpUrls() {
884 return 'https://www.mediawiki.org/wiki/API:Siteinfo';
885 }
886 }