Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 265 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
ApiParamInfo | |
0.00% |
0 / 265 |
|
0.00% |
0 / 9 |
5852 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 74 |
|
0.00% |
0 / 1 |
812 | |||
listAllSubmodules | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
30 | |||
formatHelpMessages | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
132 | |||
getModuleInfo | |
0.00% |
0 / 107 |
|
0.00% |
0 / 1 |
756 | |||
isReadMode | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getAllowedParams | |
0.00% |
0 / 30 |
|
0.00% |
0 / 1 |
2 | |||
getExamplesMessages | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
getHelpUrls | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Copyright © 2008 Roan Kattouw <roan.kattouw@gmail.com> |
4 | * |
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. |
9 | * |
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. |
14 | * |
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 |
19 | * |
20 | * @file |
21 | */ |
22 | |
23 | use MediaWiki\Context\RequestContext; |
24 | use MediaWiki\Parser\Parser; |
25 | use MediaWiki\SpecialPage\SpecialPage; |
26 | use MediaWiki\User\UserFactory; |
27 | use MediaWiki\Utils\ExtensionInfo; |
28 | use Wikimedia\ParamValidator\ParamValidator; |
29 | |
30 | /** |
31 | * @ingroup API |
32 | */ |
33 | class ApiParamInfo extends ApiBase { |
34 | |
35 | private $helpFormat; |
36 | |
37 | /** @var RequestContext */ |
38 | private $context; |
39 | |
40 | /** @var UserFactory */ |
41 | private $userFactory; |
42 | |
43 | /** |
44 | * @param ApiMain $main |
45 | * @param string $action |
46 | * @param UserFactory $userFactory |
47 | */ |
48 | public function __construct( |
49 | ApiMain $main, |
50 | $action, |
51 | UserFactory $userFactory |
52 | ) { |
53 | parent::__construct( $main, $action ); |
54 | $this->userFactory = $userFactory; |
55 | } |
56 | |
57 | public function execute() { |
58 | // Get parameters |
59 | $params = $this->extractRequestParams(); |
60 | |
61 | $this->helpFormat = $params['helpformat']; |
62 | $this->context = new RequestContext; |
63 | $this->context->setUser( $this->userFactory->newAnonymous() ); // anon to avoid caching issues |
64 | $this->context->setLanguage( $this->getMain()->getLanguage() ); |
65 | |
66 | if ( is_array( $params['modules'] ) ) { |
67 | $modules = []; |
68 | foreach ( $params['modules'] as $path ) { |
69 | if ( $path === '*' || $path === '**' ) { |
70 | $path = "main+$path"; |
71 | } |
72 | if ( str_ends_with( $path, '+*' ) || str_ends_with( $path, ' *' ) ) { |
73 | $submodules = true; |
74 | $path = substr( $path, 0, -2 ); |
75 | $recursive = false; |
76 | } elseif ( str_ends_with( $path, '+**' ) || str_ends_with( $path, ' **' ) ) { |
77 | $submodules = true; |
78 | $path = substr( $path, 0, -3 ); |
79 | $recursive = true; |
80 | } else { |
81 | $submodules = false; |
82 | } |
83 | |
84 | if ( $submodules ) { |
85 | try { |
86 | $module = $this->getModuleFromPath( $path ); |
87 | } catch ( ApiUsageException $ex ) { |
88 | foreach ( $ex->getStatusValue()->getErrors() as $error ) { |
89 | $this->addWarning( $error ); |
90 | } |
91 | continue; |
92 | } |
93 | // @phan-suppress-next-next-line PhanTypeMismatchArgumentNullable,PhanPossiblyUndeclaredVariable |
94 | // recursive is set when used |
95 | $submodules = $this->listAllSubmodules( $module, $recursive ); |
96 | if ( $submodules ) { |
97 | $modules = array_merge( $modules, $submodules ); |
98 | } else { |
99 | $this->addWarning( [ 'apierror-badmodule-nosubmodules', $path ], 'badmodule' ); |
100 | } |
101 | } else { |
102 | $modules[] = $path; |
103 | } |
104 | } |
105 | } else { |
106 | $modules = []; |
107 | } |
108 | |
109 | if ( is_array( $params['querymodules'] ) ) { |
110 | $queryModules = $params['querymodules']; |
111 | foreach ( $queryModules as $m ) { |
112 | $modules[] = 'query+' . $m; |
113 | } |
114 | } else { |
115 | $queryModules = []; |
116 | } |
117 | |
118 | if ( is_array( $params['formatmodules'] ) ) { |
119 | $formatModules = $params['formatmodules']; |
120 | foreach ( $formatModules as $m ) { |
121 | $modules[] = $m; |
122 | } |
123 | } else { |
124 | $formatModules = []; |
125 | } |
126 | |
127 | $modules = array_unique( $modules ); |
128 | |
129 | $res = []; |
130 | |
131 | foreach ( $modules as $m ) { |
132 | try { |
133 | $module = $this->getModuleFromPath( $m ); |
134 | } catch ( ApiUsageException $ex ) { |
135 | foreach ( $ex->getStatusValue()->getErrors() as $error ) { |
136 | $this->addWarning( $error ); |
137 | } |
138 | continue; |
139 | } |
140 | $key = 'modules'; |
141 | |
142 | // Back compat |
143 | $isBCQuery = false; |
144 | if ( $module->getParent() && $module->getParent()->getModuleName() == 'query' && |
145 | in_array( $module->getModuleName(), $queryModules ) |
146 | ) { |
147 | $isBCQuery = true; |
148 | $key = 'querymodules'; |
149 | } |
150 | if ( in_array( $module->getModuleName(), $formatModules ) ) { |
151 | $key = 'formatmodules'; |
152 | } |
153 | |
154 | $item = $this->getModuleInfo( $module ); |
155 | if ( $isBCQuery ) { |
156 | $item['querytype'] = $item['group']; |
157 | } |
158 | $res[$key][] = $item; |
159 | } |
160 | |
161 | $result = $this->getResult(); |
162 | $result->addValue( [ $this->getModuleName() ], 'helpformat', $this->helpFormat ); |
163 | |
164 | foreach ( $res as $key => $stuff ) { |
165 | ApiResult::setIndexedTagName( $res[$key], 'module' ); |
166 | } |
167 | |
168 | if ( $params['mainmodule'] ) { |
169 | $res['mainmodule'] = $this->getModuleInfo( $this->getMain() ); |
170 | } |
171 | |
172 | if ( $params['pagesetmodule'] ) { |
173 | $pageSet = new ApiPageSet( $this->getMain()->getModuleManager()->getModule( 'query' ) ); |
174 | $res['pagesetmodule'] = $this->getModuleInfo( $pageSet ); |
175 | unset( $res['pagesetmodule']['name'] ); |
176 | unset( $res['pagesetmodule']['path'] ); |
177 | unset( $res['pagesetmodule']['group'] ); |
178 | } |
179 | |
180 | $result->addValue( null, $this->getModuleName(), $res ); |
181 | } |
182 | |
183 | /** |
184 | * List all submodules of a module |
185 | * @param ApiBase $module |
186 | * @param bool $recursive |
187 | * @return string[] |
188 | */ |
189 | private function listAllSubmodules( ApiBase $module, $recursive ) { |
190 | $paths = []; |
191 | $manager = $module->getModuleManager(); |
192 | if ( $manager ) { |
193 | $names = $manager->getNames(); |
194 | sort( $names ); |
195 | foreach ( $names as $name ) { |
196 | $submodule = $manager->getModule( $name ); |
197 | $paths[] = $submodule->getModulePath(); |
198 | if ( $recursive && $submodule->getModuleManager() ) { |
199 | $paths = array_merge( $paths, $this->listAllSubmodules( $submodule, $recursive ) ); |
200 | } |
201 | } |
202 | } |
203 | return $paths; |
204 | } |
205 | |
206 | /** |
207 | * @param array &$res Result array |
208 | * @param string $key Result key |
209 | * @param Message[] $msgs |
210 | * @param bool $joinLists |
211 | */ |
212 | protected function formatHelpMessages( array &$res, $key, array $msgs, $joinLists = false ) { |
213 | switch ( $this->helpFormat ) { |
214 | case 'none': |
215 | break; |
216 | |
217 | case 'wikitext': |
218 | $ret = []; |
219 | foreach ( $msgs as $m ) { |
220 | $ret[] = $m->setContext( $this->context )->text(); |
221 | } |
222 | $res[$key] = implode( "\n\n", $ret ); |
223 | if ( $joinLists ) { |
224 | $res[$key] = preg_replace( '!^(([*#:;])[^\n]*)\n\n(?=\2)!m', "$1\n", $res[$key] ); |
225 | } |
226 | break; |
227 | |
228 | case 'html': |
229 | $ret = []; |
230 | foreach ( $msgs as $m ) { |
231 | $ret[] = $m->setContext( $this->context )->parseAsBlock(); |
232 | } |
233 | $ret = implode( "\n", $ret ); |
234 | if ( $joinLists ) { |
235 | $ret = preg_replace( '!\s*</([oud]l)>\s*<\1>\s*!', "\n", $ret ); |
236 | } |
237 | $res[$key] = Parser::stripOuterParagraph( $ret ); |
238 | break; |
239 | |
240 | case 'raw': |
241 | $res[$key] = []; |
242 | foreach ( $msgs as $m ) { |
243 | $a = [ |
244 | 'key' => $m->getKey(), |
245 | 'params' => $m->getParams(), |
246 | ]; |
247 | ApiResult::setIndexedTagName( $a['params'], 'param' ); |
248 | if ( $m instanceof ApiHelpParamValueMessage ) { |
249 | $a['forvalue'] = $m->getParamValue(); |
250 | } |
251 | $res[$key][] = $a; |
252 | } |
253 | ApiResult::setIndexedTagName( $res[$key], 'msg' ); |
254 | break; |
255 | } |
256 | } |
257 | |
258 | /** |
259 | * @param ApiBase $module |
260 | * @return array |
261 | */ |
262 | private function getModuleInfo( $module ) { |
263 | $ret = []; |
264 | $path = $module->getModulePath(); |
265 | $paramValidator = $module->getMain()->getParamValidator(); |
266 | |
267 | $ret['name'] = $module->getModuleName(); |
268 | $ret['classname'] = get_class( $module ); |
269 | $ret['path'] = $path; |
270 | if ( !$module->isMain() ) { |
271 | $ret['group'] = $module->getParent()->getModuleManager()->getModuleGroup( |
272 | $module->getModuleName() |
273 | ); |
274 | } |
275 | $ret['prefix'] = $module->getModulePrefix(); |
276 | |
277 | $sourceInfo = $module->getModuleSourceInfo(); |
278 | if ( $sourceInfo ) { |
279 | $ret['source'] = $sourceInfo['name']; |
280 | if ( isset( $sourceInfo['namemsg'] ) ) { |
281 | $ret['sourcename'] = $this->context->msg( $sourceInfo['namemsg'] )->text(); |
282 | } else { |
283 | $ret['sourcename'] = $ret['source']; |
284 | } |
285 | |
286 | $link = SpecialPage::getTitleFor( 'Version', 'License/' . $sourceInfo['name'] )->getFullURL(); |
287 | if ( isset( $sourceInfo['license-name'] ) ) { |
288 | $ret['licensetag'] = $sourceInfo['license-name']; |
289 | $ret['licenselink'] = (string)$link; |
290 | } elseif ( ExtensionInfo::getLicenseFileNames( dirname( $sourceInfo['path'] ) ) ) { |
291 | $ret['licenselink'] = (string)$link; |
292 | } |
293 | } |
294 | |
295 | $this->formatHelpMessages( $ret, 'description', $module->getFinalDescription() ); |
296 | |
297 | foreach ( $module->getHelpFlags() as $flag ) { |
298 | $ret[$flag] = true; |
299 | } |
300 | |
301 | $ret['helpurls'] = (array)$module->getHelpUrls(); |
302 | if ( isset( $ret['helpurls'][0] ) && $ret['helpurls'][0] === false ) { |
303 | $ret['helpurls'] = []; |
304 | } |
305 | // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset False positive |
306 | ApiResult::setIndexedTagName( $ret['helpurls'], 'helpurl' ); |
307 | |
308 | if ( $this->helpFormat !== 'none' ) { |
309 | $ret['examples'] = []; |
310 | $examples = $module->getExamplesMessages(); |
311 | foreach ( $examples as $qs => $msg ) { |
312 | $item = [ |
313 | 'query' => $qs |
314 | ]; |
315 | $msg = ApiBase::makeMessage( $msg, $this->context, [ |
316 | $module->getModulePrefix(), |
317 | $module->getModuleName(), |
318 | $module->getModulePath() |
319 | ] ); |
320 | $this->formatHelpMessages( $item, 'description', [ $msg ] ); |
321 | if ( isset( $item['description'] ) ) { |
322 | if ( is_array( $item['description'] ) ) { |
323 | $item['description'] = $item['description'][0]; |
324 | } else { |
325 | ApiResult::setSubelementsList( $item, 'description' ); |
326 | } |
327 | } |
328 | $ret['examples'][] = $item; |
329 | } |
330 | ApiResult::setIndexedTagName( $ret['examples'], 'example' ); |
331 | } |
332 | |
333 | $ret['parameters'] = []; |
334 | $ret['templatedparameters'] = []; |
335 | $params = $module->getFinalParams( ApiBase::GET_VALUES_FOR_HELP ); |
336 | $paramDesc = $module->getFinalParamDescription(); |
337 | $index = 0; |
338 | foreach ( $params as $name => $settings ) { |
339 | $settings = $paramValidator->normalizeSettings( $settings ); |
340 | |
341 | $item = [ |
342 | 'index' => ++$index, |
343 | 'name' => $name, |
344 | ]; |
345 | |
346 | if ( !empty( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ) { |
347 | $item['templatevars'] = $settings[ApiBase::PARAM_TEMPLATE_VARS]; |
348 | ApiResult::setIndexedTagName( $item['templatevars'], 'var' ); |
349 | } |
350 | |
351 | if ( isset( $paramDesc[$name] ) ) { |
352 | $this->formatHelpMessages( $item, 'description', $paramDesc[$name], true ); |
353 | } |
354 | |
355 | foreach ( $paramValidator->getParamInfo( $module, $name, $settings, [] ) as $k => $v ) { |
356 | $item[$k] = $v; |
357 | } |
358 | |
359 | if ( $name === 'token' && $module->needsToken() ) { |
360 | $item['tokentype'] = $module->needsToken(); |
361 | } |
362 | |
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' ); |
369 | } |
370 | |
371 | if ( !empty( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) { |
372 | $item['info'] = []; |
373 | foreach ( $settings[ApiBase::PARAM_HELP_MSG_INFO] as $i ) { |
374 | $tag = array_shift( $i ); |
375 | $info = [ |
376 | 'name' => $tag, |
377 | ]; |
378 | if ( count( $i ) ) { |
379 | $info['values'] = $i; |
380 | ApiResult::setIndexedTagName( $info['values'], 'v' ); |
381 | } |
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() ) |
387 | ] ); |
388 | ApiResult::setSubelementsList( $info, 'text' ); |
389 | $item['info'][] = $info; |
390 | } |
391 | ApiResult::setIndexedTagName( $item['info'], 'i' ); |
392 | } |
393 | |
394 | $key = empty( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ? 'parameters' : 'templatedparameters'; |
395 | $ret[$key][] = $item; |
396 | } |
397 | ApiResult::setIndexedTagName( $ret['parameters'], 'param' ); |
398 | ApiResult::setIndexedTagName( $ret['templatedparameters'], 'param' ); |
399 | |
400 | $dynamicParams = $module->dynamicParameterDocumentation(); |
401 | if ( $dynamicParams !== null ) { |
402 | if ( $this->helpFormat === 'none' ) { |
403 | $ret['dynamicparameters'] = true; |
404 | } else { |
405 | $dynamicParams = ApiBase::makeMessage( $dynamicParams, $this->context, [ |
406 | $module->getModulePrefix(), |
407 | $module->getModuleName(), |
408 | $module->getModulePath() |
409 | ] ); |
410 | $this->formatHelpMessages( $ret, 'dynamicparameters', [ $dynamicParams ] ); |
411 | } |
412 | } |
413 | |
414 | return $ret; |
415 | } |
416 | |
417 | public function isReadMode() { |
418 | return false; |
419 | } |
420 | |
421 | public function getAllowedParams() { |
422 | // back compat |
423 | $querymodules = $this->getMain()->getModuleManager() |
424 | ->getModule( 'query' )->getModuleManager()->getNames(); |
425 | sort( $querymodules ); |
426 | $formatmodules = $this->getMain()->getModuleManager()->getNames( 'format' ); |
427 | sort( $formatmodules ); |
428 | |
429 | return [ |
430 | 'modules' => [ |
431 | ParamValidator::PARAM_ISMULTI => true, |
432 | ], |
433 | 'helpformat' => [ |
434 | ParamValidator::PARAM_DEFAULT => 'none', |
435 | ParamValidator::PARAM_TYPE => [ 'html', 'wikitext', 'raw', 'none' ], |
436 | ], |
437 | |
438 | 'querymodules' => [ |
439 | ParamValidator::PARAM_DEPRECATED => true, |
440 | ParamValidator::PARAM_ISMULTI => true, |
441 | ParamValidator::PARAM_TYPE => $querymodules, |
442 | ], |
443 | 'mainmodule' => [ |
444 | ParamValidator::PARAM_DEPRECATED => true, |
445 | ], |
446 | 'pagesetmodule' => [ |
447 | ParamValidator::PARAM_DEPRECATED => true, |
448 | ], |
449 | 'formatmodules' => [ |
450 | ParamValidator::PARAM_DEPRECATED => true, |
451 | ParamValidator::PARAM_ISMULTI => true, |
452 | ParamValidator::PARAM_TYPE => $formatmodules, |
453 | ] |
454 | ]; |
455 | } |
456 | |
457 | protected function getExamplesMessages() { |
458 | return [ |
459 | 'action=paraminfo&modules=parse|phpfm|query%2Ballpages|query%2Bsiteinfo' |
460 | => 'apihelp-paraminfo-example-1', |
461 | 'action=paraminfo&modules=query%2B*' |
462 | => 'apihelp-paraminfo-example-2', |
463 | ]; |
464 | } |
465 | |
466 | public function getHelpUrls() { |
467 | return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Parameter_information'; |
468 | } |
469 | } |