3 * Copyright © 2008 Roan Kattouw <roan.kattouw@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
23 namespace MediaWiki\Api
;
25 use MediaWiki\Context\RequestContext
;
26 use MediaWiki\Message\Message
;
27 use MediaWiki\Parser\Parser
;
28 use MediaWiki\SpecialPage\SpecialPage
;
29 use MediaWiki\User\UserFactory
;
30 use MediaWiki\Utils\ExtensionInfo
;
31 use Wikimedia\ParamValidator\ParamValidator
;
36 class ApiParamInfo
extends ApiBase
{
41 /** @var RequestContext */
44 /** @var UserFactory */
47 public function __construct(
50 UserFactory
$userFactory
52 parent
::__construct( $main, $action );
53 $this->userFactory
= $userFactory;
56 public function execute() {
58 $params = $this->extractRequestParams();
60 $this->helpFormat
= $params['helpformat'];
61 $this->context
= new RequestContext
;
62 $this->context
->setUser( $this->userFactory
->newAnonymous() ); // anon to avoid caching issues
63 $this->context
->setLanguage( $this->getMain()->getLanguage() );
65 if ( is_array( $params['modules'] ) ) {
67 foreach ( $params['modules'] as $path ) {
68 if ( $path === '*' ||
$path === '**' ) {
71 if ( str_ends_with( $path, '+*' ) ||
str_ends_with( $path, ' *' ) ) {
73 $path = substr( $path, 0, -2 );
75 } elseif ( str_ends_with( $path, '+**' ) ||
str_ends_with( $path, ' **' ) ) {
77 $path = substr( $path, 0, -3 );
85 $module = $this->getModuleFromPath( $path );
86 } catch ( ApiUsageException
$ex ) {
87 foreach ( $ex->getStatusValue()->getMessages() as $error ) {
88 $this->addWarning( $error );
92 // @phan-suppress-next-next-line PhanTypeMismatchArgumentNullable,PhanPossiblyUndeclaredVariable
93 // recursive is set when used
94 $submodules = $this->listAllSubmodules( $module, $recursive );
96 $modules = array_merge( $modules, $submodules );
98 $this->addWarning( [ 'apierror-badmodule-nosubmodules', $path ], 'badmodule' );
108 if ( is_array( $params['querymodules'] ) ) {
109 $queryModules = $params['querymodules'];
110 foreach ( $queryModules as $m ) {
111 $modules[] = 'query+' . $m;
117 if ( is_array( $params['formatmodules'] ) ) {
118 $formatModules = $params['formatmodules'];
119 foreach ( $formatModules as $m ) {
126 $modules = array_unique( $modules );
130 foreach ( $modules as $m ) {
132 $module = $this->getModuleFromPath( $m );
133 } catch ( ApiUsageException
$ex ) {
134 foreach ( $ex->getStatusValue()->getMessages() as $error ) {
135 $this->addWarning( $error );
143 if ( $module->getParent() && $module->getParent()->getModuleName() == 'query' &&
144 in_array( $module->getModuleName(), $queryModules )
147 $key = 'querymodules';
149 if ( in_array( $module->getModuleName(), $formatModules ) ) {
150 $key = 'formatmodules';
153 $item = $this->getModuleInfo( $module );
155 $item['querytype'] = $item['group'];
157 $res[$key][] = $item;
160 $result = $this->getResult();
161 $result->addValue( [ $this->getModuleName() ], 'helpformat', $this->helpFormat
);
163 foreach ( $res as $key => $stuff ) {
164 ApiResult
::setIndexedTagName( $res[$key], 'module' );
167 if ( $params['mainmodule'] ) {
168 $res['mainmodule'] = $this->getModuleInfo( $this->getMain() );
171 if ( $params['pagesetmodule'] ) {
172 $pageSet = new ApiPageSet( $this->getMain()->getModuleManager()->getModule( 'query' ) );
173 $res['pagesetmodule'] = $this->getModuleInfo( $pageSet );
174 unset( $res['pagesetmodule']['name'] );
175 unset( $res['pagesetmodule']['path'] );
176 unset( $res['pagesetmodule']['group'] );
179 $result->addValue( null, $this->getModuleName(), $res );
183 * List all submodules of a module
184 * @param ApiBase $module
185 * @param bool $recursive
188 private function listAllSubmodules( ApiBase
$module, $recursive ) {
190 $manager = $module->getModuleManager();
192 $names = $manager->getNames();
194 foreach ( $names as $name ) {
195 $submodule = $manager->getModule( $name );
196 $paths[] = $submodule->getModulePath();
197 if ( $recursive && $submodule->getModuleManager() ) {
198 $paths = array_merge( $paths, $this->listAllSubmodules( $submodule, $recursive ) );
206 * @param array &$res Result array
207 * @param string $key Result key
208 * @param Message[] $msgs
209 * @param bool $joinLists
211 protected function formatHelpMessages( array &$res, $key, array $msgs, $joinLists = false ) {
212 switch ( $this->helpFormat
) {
218 foreach ( $msgs as $m ) {
219 $ret[] = $m->setContext( $this->context
)->text();
221 $res[$key] = implode( "\n\n", $ret );
223 $res[$key] = preg_replace( '!^(([*#:;])[^\n]*)\n\n(?=\2)!m', "$1\n", $res[$key] );
229 foreach ( $msgs as $m ) {
230 $ret[] = $m->setContext( $this->context
)->parseAsBlock();
232 $ret = implode( "\n", $ret );
234 $ret = preg_replace( '!\s*</([oud]l)>\s*<\1>\s*!', "\n", $ret );
236 $res[$key] = Parser
::stripOuterParagraph( $ret );
241 foreach ( $msgs as $m ) {
243 'key' => $m->getKey(),
244 'params' => $m->getParams(),
246 ApiResult
::setIndexedTagName( $a['params'], 'param' );
247 if ( $m instanceof ApiHelpParamValueMessage
) {
248 $a['forvalue'] = $m->getParamValue();
252 ApiResult
::setIndexedTagName( $res[$key], 'msg' );
258 * @param ApiBase $module
261 private function getModuleInfo( $module ) {
263 $path = $module->getModulePath();
264 $paramValidator = $module->getMain()->getParamValidator();
266 $ret['name'] = $module->getModuleName();
267 $ret['classname'] = get_class( $module );
268 $ret['path'] = $path;
269 if ( !$module->isMain() ) {
270 $ret['group'] = $module->getParent()->getModuleManager()->getModuleGroup(
271 $module->getModuleName()
274 $ret['prefix'] = $module->getModulePrefix();
276 $sourceInfo = $module->getModuleSourceInfo();
278 $ret['source'] = $sourceInfo['name'];
279 if ( isset( $sourceInfo['namemsg'] ) ) {
280 $ret['sourcename'] = $this->context
->msg( $sourceInfo['namemsg'] )->text();
282 $ret['sourcename'] = $ret['source'];
285 $link = SpecialPage
::getTitleFor( 'Version', 'License/' . $sourceInfo['name'] )->getFullURL();
286 if ( isset( $sourceInfo['license-name'] ) ) {
287 $ret['licensetag'] = $sourceInfo['license-name'];
288 $ret['licenselink'] = (string)$link;
289 } elseif ( ExtensionInfo
::getLicenseFileNames( dirname( $sourceInfo['path'] ) ) ) {
290 $ret['licenselink'] = (string)$link;
294 $this->formatHelpMessages( $ret, 'description', $module->getFinalDescription() );
296 foreach ( $module->getHelpFlags() as $flag ) {
300 $ret['helpurls'] = (array)$module->getHelpUrls();
301 if ( isset( $ret['helpurls'][0] ) && $ret['helpurls'][0] === false ) {
302 $ret['helpurls'] = [];
304 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset False positive
305 ApiResult
::setIndexedTagName( $ret['helpurls'], 'helpurl' );
307 if ( $this->helpFormat
!== 'none' ) {
308 $ret['examples'] = [];
309 $examples = $module->getExamplesMessages();
310 foreach ( $examples as $qs => $msg ) {
315 Message
::newFromSpecifier( $msg ),
316 $module->getModulePrefix(),
317 $module->getModuleName(),
318 $module->getModulePath()
320 $this->formatHelpMessages( $item, 'description', [ $msg ] );
321 if ( isset( $item['description'] ) ) {
322 if ( is_array( $item['description'] ) ) {
323 $item['description'] = $item['description'][0];
325 ApiResult
::setSubelementsList( $item, 'description' );
328 $ret['examples'][] = $item;
330 ApiResult
::setIndexedTagName( $ret['examples'], 'example' );
333 $ret['parameters'] = [];
334 $ret['templatedparameters'] = [];
335 $params = $module->getFinalParams( ApiBase
::GET_VALUES_FOR_HELP
);
336 $paramDesc = $module->getFinalParamDescription();
338 foreach ( $params as $name => $settings ) {
339 $settings = $paramValidator->normalizeSettings( $settings );
346 if ( !empty( $settings[ApiBase
::PARAM_TEMPLATE_VARS
] ) ) {
347 $item['templatevars'] = $settings[ApiBase
::PARAM_TEMPLATE_VARS
];
348 ApiResult
::setIndexedTagName( $item['templatevars'], 'var' );
351 if ( isset( $paramDesc[$name] ) ) {
352 $this->formatHelpMessages( $item, 'description', $paramDesc[$name], true );
355 foreach ( $paramValidator->getParamInfo( $module, $name, $settings, [] ) as $k => $v ) {
359 if ( $name === 'token' && $module->needsToken() ) {
360 $item['tokentype'] = $module->needsToken();
363 if ( $item['type'] === 'NULL' ) {
364 // Munge "NULL" to "string" for historical reasons
365 $item['type'] = 'string';
366 } elseif ( is_array( $item['type'] ) ) {
367 // Set indexed tag name, for historical reasons
368 ApiResult
::setIndexedTagName( $item['type'], 't' );
371 if ( !empty( $settings[ApiBase
::PARAM_HELP_MSG_INFO
] ) ) {
373 foreach ( $settings[ApiBase
::PARAM_HELP_MSG_INFO
] as $i ) {
374 $tag = array_shift( $i );
379 $info['values'] = $i;
380 ApiResult
::setIndexedTagName( $info['values'], 'v' );
382 $this->formatHelpMessages( $info, 'text', [
383 $this->context
->msg( "apihelp-{$path}-paraminfo-{$tag}" )
384 ->numParams( count( $i ) )
385 ->params( $this->context
->getLanguage()->commaList( $i ) )
386 ->params( $module->getModulePrefix() )
388 ApiResult
::setSubelementsList( $info, 'text' );
389 $item['info'][] = $info;
391 ApiResult
::setIndexedTagName( $item['info'], 'i' );
394 $key = empty( $settings[ApiBase
::PARAM_TEMPLATE_VARS
] ) ?
'parameters' : 'templatedparameters';
395 $ret[$key][] = $item;
397 ApiResult
::setIndexedTagName( $ret['parameters'], 'param' );
398 ApiResult
::setIndexedTagName( $ret['templatedparameters'], 'param' );
400 $dynamicParams = $module->dynamicParameterDocumentation();
401 if ( $dynamicParams !== null ) {
402 if ( $this->helpFormat
=== 'none' ) {
403 $ret['dynamicparameters'] = true;
405 $dynamicParams = $this->msg(
406 Message
::newFromSpecifier( $dynamicParams ),
407 $module->getModulePrefix(),
408 $module->getModuleName(),
409 $module->getModulePath()
411 $this->formatHelpMessages( $ret, 'dynamicparameters', [ $dynamicParams ] );
418 public function isReadMode() {
422 public function getAllowedParams() {
424 $querymodules = $this->getMain()->getModuleManager()
425 ->getModule( 'query' )->getModuleManager()->getNames();
426 sort( $querymodules );
427 $formatmodules = $this->getMain()->getModuleManager()->getNames( 'format' );
428 sort( $formatmodules );
432 ParamValidator
::PARAM_ISMULTI
=> true,
435 ParamValidator
::PARAM_DEFAULT
=> 'none',
436 ParamValidator
::PARAM_TYPE
=> [ 'html', 'wikitext', 'raw', 'none' ],
440 ParamValidator
::PARAM_DEPRECATED
=> true,
441 ParamValidator
::PARAM_ISMULTI
=> true,
442 ParamValidator
::PARAM_TYPE
=> $querymodules,
445 ParamValidator
::PARAM_DEPRECATED
=> true,
448 ParamValidator
::PARAM_DEPRECATED
=> true,
451 ParamValidator
::PARAM_DEPRECATED
=> true,
452 ParamValidator
::PARAM_ISMULTI
=> true,
453 ParamValidator
::PARAM_TYPE
=> $formatmodules,
458 protected function getExamplesMessages() {
460 'action=paraminfo&modules=parse|phpfm|query%2Ballpages|query%2Bsiteinfo'
461 => 'apihelp-paraminfo-example-1',
462 'action=paraminfo&modules=query%2B*'
463 => 'apihelp-paraminfo-example-2',
467 public function getHelpUrls() {
468 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Parameter_information';
472 /** @deprecated class alias since 1.43 */
473 class_alias( ApiParamInfo
::class, 'ApiParamInfo' );