MediaWiki REL1_34
ApiHelp.php
Go to the documentation of this file.
1<?php
23use HtmlFormatter\HtmlFormatter;
25
32class ApiHelp extends ApiBase {
33 public function execute() {
34 $params = $this->extractRequestParams();
35 $modules = [];
36
37 foreach ( $params['modules'] as $path ) {
38 $modules[] = $this->getModuleFromPath( $path );
39 }
40
41 // Get the help
42 $context = new DerivativeContext( $this->getMain()->getContext() );
43 $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
44 $context->setSkin( $skinFactory->makeSkin( 'apioutput' ) );
45 $context->setLanguage( $this->getMain()->getLanguage() );
46 $context->setTitle( SpecialPage::getTitleFor( 'ApiHelp' ) );
47 $out = new OutputPage( $context );
48 $out->setRobotPolicy( 'noindex,nofollow' );
49 $out->setCopyrightUrl( 'https://www.mediawiki.org/wiki/Special:MyLanguage/Copyright' );
50 $context->setOutput( $out );
51
52 self::getHelp( $context, $modules, $params );
53
54 // Grab the output from the skin
55 ob_start();
56 $context->getOutput()->output();
57 $html = ob_get_clean();
58
59 $result = $this->getResult();
60 if ( $params['wrap'] ) {
61 $data = [
62 'mime' => 'text/html',
63 'filename' => 'api-help.html',
64 'help' => $html,
65 ];
66 ApiResult::setSubelementsList( $data, 'help' );
67 $result->addValue( null, $this->getModuleName(), $data );
68 } else {
69 // Show any errors at the top of the HTML
70 $transform = [
71 'Types' => [ 'AssocAsObject' => true ],
72 'Strip' => 'all',
73 ];
74 $errors = array_filter( [
75 'errors' => $this->getResult()->getResultData( [ 'errors' ], $transform ),
76 'warnings' => $this->getResult()->getResultData( [ 'warnings' ], $transform ),
77 ] );
78 if ( $errors ) {
79 $json = FormatJson::encode( $errors, true, FormatJson::UTF8_OK );
80 // Escape any "--", some parsers might interpret that as end-of-comment.
81 // The above already escaped any "<" and ">".
82 $json = str_replace( '--', '-\u002D', $json );
83 $html = "<!-- API warnings and errors:\n$json\n-->\n$html";
84 }
85
86 $result->reset();
87 $result->addValue( null, 'text', $html, ApiResult::NO_SIZE_CHECK );
88 $result->addValue( null, 'mime', 'text/html', ApiResult::NO_SIZE_CHECK );
89 $result->addValue( null, 'filename', 'api-help.html', ApiResult::NO_SIZE_CHECK );
90 }
91 }
92
112 public static function getHelp( IContextSource $context, $modules, array $options ) {
113 if ( !is_array( $modules ) ) {
114 $modules = [ $modules ];
115 }
116
117 $out = $context->getOutput();
118 $out->addModuleStyles( [
119 'mediawiki.hlist',
120 'mediawiki.apipretty',
121 ] );
122 if ( !empty( $options['toc'] ) ) {
123 $out->addModuleStyles( 'mediawiki.toc.styles' );
124 }
125 $out->setPageTitle( $context->msg( 'api-help-title' ) );
126
127 $services = MediaWikiServices::getInstance();
128 $cache = $services->getMainWANObjectCache();
129 $cacheKey = null;
130 if ( count( $modules ) == 1 && $modules[0] instanceof ApiMain &&
131 $options['recursivesubmodules'] &&
132 $context->getLanguage()->equals( $services->getContentLanguage() )
133 ) {
134 $cacheHelpTimeout = $context->getConfig()->get( 'APICacheHelpTimeout' );
135 if ( $cacheHelpTimeout > 0 ) {
136 // Get help text from cache if present
137 $cacheKey = $cache->makeKey( 'apihelp', $modules[0]->getModulePath(),
138 (int)!empty( $options['toc'] ),
139 str_replace( ' ', '_', SpecialVersion::getVersion( 'nodb' ) ) );
140 $cached = $cache->get( $cacheKey );
141 if ( $cached ) {
142 $out->addHTML( $cached );
143 return;
144 }
145 }
146 }
147 if ( $out->getHTML() !== '' ) {
148 // Don't save to cache, there's someone else's content in the page
149 // already
150 $cacheKey = null;
151 }
152
153 $options['recursivesubmodules'] = !empty( $options['recursivesubmodules'] );
154 $options['submodules'] = $options['recursivesubmodules'] || !empty( $options['submodules'] );
155
156 // Prepend lead
157 if ( empty( $options['nolead'] ) ) {
158 $msg = $context->msg( 'api-help-lead' );
159 if ( !$msg->isDisabled() ) {
160 $out->addHTML( $msg->parseAsBlock() );
161 }
162 }
163
164 $haveModules = [];
165 $html = self::getHelpInternal( $context, $modules, $options, $haveModules );
166 if ( !empty( $options['toc'] ) && $haveModules ) {
167 $out->addHTML( Linker::generateTOC( $haveModules, $context->getLanguage() ) );
168 }
169 $out->addHTML( $html );
170
171 $helptitle = $options['helptitle'] ?? null;
172 $html = self::fixHelpLinks( $out->getHTML(), $helptitle, $haveModules );
173 $out->clearHTML();
174 $out->addHTML( $html );
175
176 if ( $cacheKey !== null ) {
177 $cache->set( $cacheKey, $out->getHTML(), $cacheHelpTimeout );
178 }
179 }
180
189 public static function fixHelpLinks( $html, $helptitle = null, $localModules = [] ) {
190 $formatter = new HtmlFormatter( $html );
191 $doc = $formatter->getDoc();
192 $xpath = new DOMXPath( $doc );
193 $nodes = $xpath->query( '//a[@href][not(contains(@class,\'apihelp-linktrail\'))]' );
194 foreach ( $nodes as $node ) {
195 $href = $node->getAttribute( 'href' );
196 do {
197 $old = $href;
198 $href = rawurldecode( $href );
199 } while ( $old !== $href );
200 if ( preg_match( '!Special:ApiHelp/([^&/|#]+)((?:#.*)?)!', $href, $m ) ) {
201 if ( isset( $localModules[$m[1]] ) ) {
202 $href = $m[2] === '' ? '#' . $m[1] : $m[2];
203 } elseif ( $helptitle !== null ) {
204 $href = Title::newFromText( str_replace( '$1', $m[1], $helptitle ) . $m[2] )
205 ->getFullURL();
206 } else {
207 $href = wfAppendQuery( wfScript( 'api' ), [
208 'action' => 'help',
209 'modules' => $m[1],
210 ] ) . $m[2];
211 }
212 $node->setAttribute( 'href', $href );
213 $node->removeAttribute( 'title' );
214 }
215 }
216
217 return $formatter->getText();
218 }
219
228 private static function wrap( Message $msg, $class, $tag = 'span' ) {
229 return Html::rawElement( $tag, [ 'class' => $class ],
230 $msg->parse()
231 );
232 }
233
243 private static function getHelpInternal( IContextSource $context, array $modules,
244 array $options, &$haveModules
245 ) {
246 $out = '';
247
248 $level = empty( $options['headerlevel'] ) ? 2 : $options['headerlevel'];
249 if ( empty( $options['tocnumber'] ) ) {
250 $tocnumber = [ 2 => 0 ];
251 } else {
252 $tocnumber = &$options['tocnumber'];
253 }
254
255 foreach ( $modules as $module ) {
256 $tocnumber[$level]++;
257 $path = $module->getModulePath();
258 $module->setContext( $context );
259 $help = [
260 'header' => '',
261 'flags' => '',
262 'description' => '',
263 'help-urls' => '',
264 'parameters' => '',
265 'examples' => '',
266 'submodules' => '',
267 ];
268
269 if ( empty( $options['noheader'] ) || !empty( $options['toc'] ) ) {
270 $anchor = $path;
271 $i = 1;
272 while ( isset( $haveModules[$anchor] ) ) {
273 $anchor = $path . '|' . ++$i;
274 }
275
276 if ( $module->isMain() ) {
277 $headerContent = $context->msg( 'api-help-main-header' )->parse();
278 $headerAttr = [
279 'class' => 'apihelp-header',
280 ];
281 } else {
282 $name = $module->getModuleName();
283 $headerContent = $module->getParent()->getModuleManager()->getModuleGroup( $name ) .
284 "=$name";
285 if ( $module->getModulePrefix() !== '' ) {
286 $headerContent .= ' ' .
287 $context->msg( 'parentheses', $module->getModulePrefix() )->parse();
288 }
289 // Module names are always in English and not localized,
290 // so English language and direction must be set explicitly,
291 // otherwise parentheses will get broken in RTL wikis
292 $headerAttr = [
293 'class' => 'apihelp-header apihelp-module-name',
294 'dir' => 'ltr',
295 'lang' => 'en',
296 ];
297 }
298
299 $headerAttr['id'] = $anchor;
300
301 $haveModules[$anchor] = [
302 'toclevel' => count( $tocnumber ),
303 'level' => $level,
304 'anchor' => $anchor,
305 'line' => $headerContent,
306 'number' => implode( '.', $tocnumber ),
307 'index' => false,
308 ];
309 if ( empty( $options['noheader'] ) ) {
310 $help['header'] .= Html::element(
311 'h' . min( 6, $level ),
312 $headerAttr,
313 $headerContent
314 );
315 }
316 } else {
317 $haveModules[$path] = true;
318 }
319
320 $links = [];
321 $any = false;
322 for ( $m = $module; $m !== null; $m = $m->getParent() ) {
323 $name = $m->getModuleName();
324 if ( $name === 'main_int' ) {
325 $name = 'main';
326 }
327
328 if ( count( $modules ) === 1 && $m === $modules[0] &&
329 !( !empty( $options['submodules'] ) && $m->getModuleManager() )
330 ) {
331 $link = Html::element( 'b', [ 'dir' => 'ltr', 'lang' => 'en' ], $name );
332 } else {
333 $link = SpecialPage::getTitleFor( 'ApiHelp', $m->getModulePath() )->getLocalURL();
334 $link = Html::element( 'a',
335 [ 'href' => $link, 'class' => 'apihelp-linktrail', 'dir' => 'ltr', 'lang' => 'en' ],
336 $name
337 );
338 $any = true;
339 }
340 array_unshift( $links, $link );
341 }
342 if ( $any ) {
343 $help['header'] .= self::wrap(
344 $context->msg( 'parentheses' )
345 ->rawParams( $context->getLanguage()->pipeList( $links ) ),
346 'apihelp-linktrail', 'div'
347 );
348 }
349
350 $flags = $module->getHelpFlags();
351 $help['flags'] .= Html::openElement( 'div',
352 [ 'class' => 'apihelp-block apihelp-flags' ] );
353 $msg = $context->msg( 'api-help-flags' );
354 if ( !$msg->isDisabled() ) {
355 $help['flags'] .= self::wrap(
356 $msg->numParams( count( $flags ) ), 'apihelp-block-head', 'div'
357 );
358 }
359 $help['flags'] .= Html::openElement( 'ul' );
360 foreach ( $flags as $flag ) {
361 $help['flags'] .= Html::rawElement( 'li', null,
362 self::wrap( $context->msg( "api-help-flag-$flag" ), "apihelp-flag-$flag" )
363 );
364 }
365 $sourceInfo = $module->getModuleSourceInfo();
366 if ( $sourceInfo ) {
367 if ( isset( $sourceInfo['namemsg'] ) ) {
368 $extname = $context->msg( $sourceInfo['namemsg'] )->text();
369 } else {
370 // Probably English, so wrap it.
371 $extname = Html::element( 'span', [ 'dir' => 'ltr', 'lang' => 'en' ], $sourceInfo['name'] );
372 }
373 $help['flags'] .= Html::rawElement( 'li', null,
374 self::wrap(
375 $context->msg( 'api-help-source', $extname, $sourceInfo['name'] ),
376 'apihelp-source'
377 )
378 );
379
380 $link = SpecialPage::getTitleFor( 'Version', 'License/' . $sourceInfo['name'] );
381 if ( isset( $sourceInfo['license-name'] ) ) {
382 $msg = $context->msg( 'api-help-license', $link,
383 Html::element( 'span', [ 'dir' => 'ltr', 'lang' => 'en' ], $sourceInfo['license-name'] )
384 );
385 } elseif ( SpecialVersion::getExtLicenseFileName( dirname( $sourceInfo['path'] ) ) ) {
386 $msg = $context->msg( 'api-help-license-noname', $link );
387 } else {
388 $msg = $context->msg( 'api-help-license-unknown' );
389 }
390 $help['flags'] .= Html::rawElement( 'li', null,
391 self::wrap( $msg, 'apihelp-license' )
392 );
393 } else {
394 $help['flags'] .= Html::rawElement( 'li', null,
395 self::wrap( $context->msg( 'api-help-source-unknown' ), 'apihelp-source' )
396 );
397 $help['flags'] .= Html::rawElement( 'li', null,
398 self::wrap( $context->msg( 'api-help-license-unknown' ), 'apihelp-license' )
399 );
400 }
401 $help['flags'] .= Html::closeElement( 'ul' );
402 $help['flags'] .= Html::closeElement( 'div' );
403
404 foreach ( $module->getFinalDescription() as $msg ) {
405 $msg->setContext( $context );
406 $help['description'] .= $msg->parseAsBlock();
407 }
408
409 $urls = $module->getHelpUrls();
410 if ( $urls ) {
411 $help['help-urls'] .= Html::openElement( 'div',
412 [ 'class' => 'apihelp-block apihelp-help-urls' ]
413 );
414 $msg = $context->msg( 'api-help-help-urls' );
415 if ( !$msg->isDisabled() ) {
416 $help['help-urls'] .= self::wrap(
417 $msg->numParams( count( $urls ) ), 'apihelp-block-head', 'div'
418 );
419 }
420 if ( !is_array( $urls ) ) {
421 $urls = [ $urls ];
422 }
423 $help['help-urls'] .= Html::openElement( 'ul' );
424 foreach ( $urls as $url ) {
425 $help['help-urls'] .= Html::rawElement( 'li', null,
426 Html::element( 'a', [ 'href' => $url, 'dir' => 'ltr' ], $url )
427 );
428 }
429 $help['help-urls'] .= Html::closeElement( 'ul' );
430 $help['help-urls'] .= Html::closeElement( 'div' );
431 }
432
433 $params = $module->getFinalParams( ApiBase::GET_VALUES_FOR_HELP );
434 $dynamicParams = $module->dynamicParameterDocumentation();
435 $groups = [];
436 if ( $params || $dynamicParams !== null ) {
437 $help['parameters'] .= Html::openElement( 'div',
438 [ 'class' => 'apihelp-block apihelp-parameters' ]
439 );
440 $msg = $context->msg( 'api-help-parameters' );
441 if ( !$msg->isDisabled() ) {
442 $help['parameters'] .= self::wrap(
443 $msg->numParams( count( $params ) ), 'apihelp-block-head', 'div'
444 );
445 }
446 $help['parameters'] .= Html::openElement( 'dl' );
447
448 $descriptions = $module->getFinalParamDescription();
449
450 foreach ( $params as $name => $settings ) {
451 if ( !is_array( $settings ) ) {
452 $settings = [ ApiBase::PARAM_DFLT => $settings ];
453 }
454
455 $help['parameters'] .= Html::rawElement( 'dt', null,
456 Html::element( 'span', [ 'dir' => 'ltr', 'lang' => 'en' ], $module->encodeParamName( $name ) )
457 );
458
459 // Add description
460 $description = [];
461 if ( isset( $descriptions[$name] ) ) {
462 foreach ( $descriptions[$name] as $msg ) {
463 $msg->setContext( $context );
464 $description[] = $msg->parseAsBlock();
465 }
466 }
467
468 // Add usage info
469 $info = [];
470
471 // Required?
472 if ( !empty( $settings[ApiBase::PARAM_REQUIRED] ) ) {
473 $info[] = $context->msg( 'api-help-param-required' )->parse();
474 }
475
476 // Custom info?
477 if ( !empty( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
478 foreach ( $settings[ApiBase::PARAM_HELP_MSG_INFO] as $i ) {
479 $tag = array_shift( $i );
480 $info[] = $context->msg( "apihelp-{$path}-paraminfo-{$tag}" )
481 ->numParams( count( $i ) )
482 ->params( $context->getLanguage()->commaList( $i ) )
483 ->params( $module->getModulePrefix() )
484 ->parse();
485 }
486 }
487
488 // Templated?
489 if ( !empty( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
490 $vars = [];
491 $msg = 'api-help-param-templated-var-first';
492 foreach ( $settings[ApiBase::PARAM_TEMPLATE_VARS] as $k => $v ) {
493 $vars[] = $context->msg( $msg, $k, $module->encodeParamName( $v ) );
494 $msg = 'api-help-param-templated-var';
495 }
496 $info[] = $context->msg( 'api-help-param-templated' )
497 ->numParams( count( $vars ) )
498 ->params( Message::listParam( $vars ) )
499 ->parse();
500 }
501
502 // Type documentation
503 if ( !isset( $settings[ApiBase::PARAM_TYPE] ) ) {
504 $dflt = $settings[ApiBase::PARAM_DFLT] ?? null;
505 if ( is_bool( $dflt ) ) {
506 $settings[ApiBase::PARAM_TYPE] = 'boolean';
507 } elseif ( is_string( $dflt ) || is_null( $dflt ) ) {
508 $settings[ApiBase::PARAM_TYPE] = 'string';
509 } elseif ( is_int( $dflt ) ) {
510 $settings[ApiBase::PARAM_TYPE] = 'integer';
511 }
512 }
513 if ( isset( $settings[ApiBase::PARAM_TYPE] ) ) {
514 $type = $settings[ApiBase::PARAM_TYPE];
515 $multi = !empty( $settings[ApiBase::PARAM_ISMULTI] );
516 $hintPipeSeparated = true;
517 $count = !empty( $settings[ApiBase::PARAM_ISMULTI_LIMIT2] )
518 ? $settings[ApiBase::PARAM_ISMULTI_LIMIT2] + 1
520
521 if ( is_array( $type ) ) {
522 $count = count( $type );
523 $deprecatedValues = $settings[ApiBase::PARAM_DEPRECATED_VALUES] ?? [];
524 $links = $settings[ApiBase::PARAM_VALUE_LINKS] ?? [];
525 $values = array_map( function ( $v ) use ( $links, $deprecatedValues ) {
526 $attr = [];
527 if ( $v !== '' ) {
528 // We can't know whether this contains LTR or RTL text.
529 $attr['dir'] = 'auto';
530 }
531 if ( isset( $deprecatedValues[$v] ) ) {
532 $attr['class'] = 'apihelp-deprecated-value';
533 }
534 $ret = $attr ? Html::element( 'span', $attr, $v ) : $v;
535 if ( isset( $links[$v] ) ) {
536 $ret = "[[{$links[$v]}|$ret]]";
537 }
538 return $ret;
539 }, $type );
540 $i = array_search( '', $type, true );
541 if ( $i === false ) {
542 $values = $context->getLanguage()->commaList( $values );
543 } else {
544 unset( $values[$i] );
545 $values = $context->msg( 'api-help-param-list-can-be-empty' )
546 ->numParams( count( $values ) )
547 ->params( $context->getLanguage()->commaList( $values ) )
548 ->parse();
549 }
550 $info[] = $context->msg( 'api-help-param-list' )
551 ->params( $multi ? 2 : 1 )
552 ->params( $values )
553 ->parse();
554 $hintPipeSeparated = false;
555 } else {
556 switch ( $type ) {
557 case 'submodule':
558 $groups[] = $name;
559
560 if ( isset( $settings[ApiBase::PARAM_SUBMODULE_MAP] ) ) {
561 $map = $settings[ApiBase::PARAM_SUBMODULE_MAP];
562 $defaultAttrs = [];
563 } else {
564 $prefix = $module->isMain() ? '' : ( $module->getModulePath() . '+' );
565 $map = [];
566 foreach ( $module->getModuleManager()->getNames( $name ) as $submoduleName ) {
567 $map[$submoduleName] = $prefix . $submoduleName;
568 }
569 $defaultAttrs = [ 'dir' => 'ltr', 'lang' => 'en' ];
570 }
571 ksort( $map );
572
573 $submodules = [];
574 $deprecatedSubmodules = [];
575 foreach ( $map as $v => $m ) {
576 $attrs = $defaultAttrs;
577 $arr = &$submodules;
578 try {
579 $submod = $module->getModuleFromPath( $m );
580 if ( $submod && $submod->isDeprecated() ) {
581 $arr = &$deprecatedSubmodules;
582 $attrs['class'] = 'apihelp-deprecated-value';
583 }
584 } catch ( ApiUsageException $ex ) {
585 // Ignore
586 }
587 if ( $attrs ) {
588 $v = Html::element( 'span', $attrs, $v );
589 }
590 $arr[] = "[[Special:ApiHelp/{$m}|{$v}]]";
591 }
592 $submodules = array_merge( $submodules, $deprecatedSubmodules );
593 $count = count( $submodules );
594 $info[] = $context->msg( 'api-help-param-list' )
595 ->params( $multi ? 2 : 1 )
596 ->params( $context->getLanguage()->commaList( $submodules ) )
597 ->parse();
598 $hintPipeSeparated = false;
599 // No type message necessary, we have a list of values.
600 $type = null;
601 break;
602
603 case 'namespace':
604 $namespaces = MediaWikiServices::getInstance()->
605 getNamespaceInfo()->getValidNamespaces();
606 if ( isset( $settings[ApiBase::PARAM_EXTRA_NAMESPACES] ) &&
607 is_array( $settings[ApiBase::PARAM_EXTRA_NAMESPACES] )
608 ) {
609 $namespaces = array_merge( $namespaces, $settings[ApiBase::PARAM_EXTRA_NAMESPACES] );
610 }
611 sort( $namespaces );
612 $count = count( $namespaces );
613 $info[] = $context->msg( 'api-help-param-list' )
614 ->params( $multi ? 2 : 1 )
615 ->params( $context->getLanguage()->commaList( $namespaces ) )
616 ->parse();
617 $hintPipeSeparated = false;
618 // No type message necessary, we have a list of values.
619 $type = null;
620 break;
621
622 case 'tags':
624 $count = count( $tags );
625 $info[] = $context->msg( 'api-help-param-list' )
626 ->params( $multi ? 2 : 1 )
627 ->params( $context->getLanguage()->commaList( $tags ) )
628 ->parse();
629 $hintPipeSeparated = false;
630 $type = null;
631 break;
632
633 case 'limit':
634 if ( isset( $settings[ApiBase::PARAM_MAX2] ) ) {
635 $info[] = $context->msg( 'api-help-param-limit2' )
636 ->numParams( $settings[ApiBase::PARAM_MAX] )
637 ->numParams( $settings[ApiBase::PARAM_MAX2] )
638 ->parse();
639 } else {
640 $info[] = $context->msg( 'api-help-param-limit' )
641 ->numParams( $settings[ApiBase::PARAM_MAX] )
642 ->parse();
643 }
644 break;
645
646 case 'integer':
647 // Possible messages:
648 // api-help-param-integer-min,
649 // api-help-param-integer-max,
650 // api-help-param-integer-minmax
651 $suffix = '';
652 $min = $max = 0;
653 if ( isset( $settings[ApiBase::PARAM_MIN] ) ) {
654 $suffix .= 'min';
655 $min = $settings[ApiBase::PARAM_MIN];
656 }
657 if ( isset( $settings[ApiBase::PARAM_MAX] ) ) {
658 $suffix .= 'max';
659 $max = $settings[ApiBase::PARAM_MAX];
660 }
661 if ( $suffix !== '' ) {
662 $info[] =
663 $context->msg( "api-help-param-integer-$suffix" )
664 ->params( $multi ? 2 : 1 )
665 ->numParams( $min, $max )
666 ->parse();
667 }
668 break;
669
670 case 'upload':
671 $info[] = $context->msg( 'api-help-param-upload' )
672 ->parse();
673 // No type message necessary, api-help-param-upload should handle it.
674 $type = null;
675 break;
676
677 case 'string':
678 case 'text':
679 // Displaying a type message here would be useless.
680 $type = null;
681 break;
682 }
683 }
684
685 // Add type. Messages for grep: api-help-param-type-limit
686 // api-help-param-type-integer api-help-param-type-boolean
687 // api-help-param-type-timestamp api-help-param-type-user
688 // api-help-param-type-password
689 if ( is_string( $type ) ) {
690 $msg = $context->msg( "api-help-param-type-$type" );
691 if ( !$msg->isDisabled() ) {
692 $info[] = $msg->params( $multi ? 2 : 1 )->parse();
693 }
694 }
695
696 if ( $multi ) {
697 $extra = [];
698 $lowcount = !empty( $settings[ApiBase::PARAM_ISMULTI_LIMIT1] )
701 $highcount = !empty( $settings[ApiBase::PARAM_ISMULTI_LIMIT2] )
704
705 if ( $hintPipeSeparated ) {
706 $extra[] = $context->msg( 'api-help-param-multi-separate' )->parse();
707 }
708 if ( $count > $lowcount ) {
709 if ( $lowcount === $highcount ) {
710 $msg = $context->msg( 'api-help-param-multi-max-simple' )
711 ->numParams( $lowcount );
712 } else {
713 $msg = $context->msg( 'api-help-param-multi-max' )
714 ->numParams( $lowcount, $highcount );
715 }
716 $extra[] = $msg->parse();
717 }
718 if ( $extra ) {
719 $info[] = implode( ' ', $extra );
720 }
721
722 $allowAll = $settings[ApiBase::PARAM_ALL] ?? false;
723 if ( $allowAll || $settings[ApiBase::PARAM_TYPE] === 'namespace' ) {
724 if ( $settings[ApiBase::PARAM_TYPE] === 'namespace' ) {
725 $allSpecifier = ApiBase::ALL_DEFAULT_STRING;
726 } else {
727 $allSpecifier = ( is_string( $allowAll ) ? $allowAll : ApiBase::ALL_DEFAULT_STRING );
728 }
729 $info[] = $context->msg( 'api-help-param-multi-all' )
730 ->params( $allSpecifier )
731 ->parse();
732 }
733 }
734 }
735
736 if ( isset( $settings[self::PARAM_MAX_BYTES] ) ) {
737 $info[] = $context->msg( 'api-help-param-maxbytes' )
738 ->numParams( $settings[self::PARAM_MAX_BYTES] );
739 }
740 if ( isset( $settings[self::PARAM_MAX_CHARS] ) ) {
741 $info[] = $context->msg( 'api-help-param-maxchars' )
742 ->numParams( $settings[self::PARAM_MAX_CHARS] );
743 }
744
745 // Add default
746 $default = $settings[ApiBase::PARAM_DFLT] ?? null;
747 if ( $default === '' ) {
748 $info[] = $context->msg( 'api-help-param-default-empty' )
749 ->parse();
750 } elseif ( $default !== null && $default !== false ) {
751 // We can't know whether this contains LTR or RTL text.
752 $info[] = $context->msg( 'api-help-param-default' )
753 ->params( Html::element( 'span', [ 'dir' => 'auto' ], $default ) )
754 ->parse();
755 }
756
757 if ( !array_filter( $description ) ) {
758 $description = [ self::wrap(
759 $context->msg( 'api-help-param-no-description' ),
760 'apihelp-empty'
761 ) ];
762 }
763
764 // Add "deprecated" flag
765 if ( !empty( $settings[ApiBase::PARAM_DEPRECATED] ) ) {
766 $help['parameters'] .= Html::openElement( 'dd',
767 [ 'class' => 'info' ] );
768 $help['parameters'] .= self::wrap(
769 $context->msg( 'api-help-param-deprecated' ),
770 'apihelp-deprecated', 'strong'
771 );
772 $help['parameters'] .= Html::closeElement( 'dd' );
773 }
774
775 if ( $description ) {
776 $description = implode( '', $description );
777 $description = preg_replace( '!\s*</([oud]l)>\s*<\1>\s*!', "\n", $description );
778 $help['parameters'] .= Html::rawElement( 'dd',
779 [ 'class' => 'description' ], $description );
780 }
781
782 foreach ( $info as $i ) {
783 $help['parameters'] .= Html::rawElement( 'dd', [ 'class' => 'info' ], $i );
784 }
785 }
786
787 if ( $dynamicParams !== null ) {
788 $dynamicParams = ApiBase::makeMessage( $dynamicParams, $context, [
789 $module->getModulePrefix(),
790 $module->getModuleName(),
791 $module->getModulePath()
792 ] );
793 $help['parameters'] .= Html::element( 'dt', null, '*' );
794 $help['parameters'] .= Html::rawElement( 'dd',
795 [ 'class' => 'description' ], $dynamicParams->parse() );
796 }
797
798 $help['parameters'] .= Html::closeElement( 'dl' );
799 $help['parameters'] .= Html::closeElement( 'div' );
800 }
801
802 $examples = $module->getExamplesMessages();
803 if ( $examples ) {
804 $help['examples'] .= Html::openElement( 'div',
805 [ 'class' => 'apihelp-block apihelp-examples' ] );
806 $msg = $context->msg( 'api-help-examples' );
807 if ( !$msg->isDisabled() ) {
808 $help['examples'] .= self::wrap(
809 $msg->numParams( count( $examples ) ), 'apihelp-block-head', 'div'
810 );
811 }
812
813 $help['examples'] .= Html::openElement( 'dl' );
814 foreach ( $examples as $qs => $msg ) {
815 $msg = ApiBase::makeMessage( $msg, $context, [
816 $module->getModulePrefix(),
817 $module->getModuleName(),
818 $module->getModulePath()
819 ] );
820
821 $link = wfAppendQuery( wfScript( 'api' ), $qs );
822 $sandbox = SpecialPage::getTitleFor( 'ApiSandbox' )->getLocalURL() . '#' . $qs;
823 $help['examples'] .= Html::rawElement( 'dt', null, $msg->parse() );
824 $help['examples'] .= Html::rawElement( 'dd', null,
825 Html::element( 'a', [ 'href' => $link, 'dir' => 'ltr' ], "api.php?$qs" ) . ' ' .
826 Html::rawElement( 'a', [ 'href' => $sandbox ],
827 $context->msg( 'api-help-open-in-apisandbox' )->parse() )
828 );
829 }
830 $help['examples'] .= Html::closeElement( 'dl' );
831 $help['examples'] .= Html::closeElement( 'div' );
832 }
833
834 $subtocnumber = $tocnumber;
835 $subtocnumber[$level + 1] = 0;
836 $suboptions = [
837 'submodules' => $options['recursivesubmodules'],
838 'headerlevel' => $level + 1,
839 'tocnumber' => &$subtocnumber,
840 'noheader' => false,
841 ] + $options;
842
843 if ( $options['submodules'] && $module->getModuleManager() ) {
844 $manager = $module->getModuleManager();
845 $submodules = [];
846 foreach ( $groups as $group ) {
847 $names = $manager->getNames( $group );
848 sort( $names );
849 foreach ( $names as $name ) {
850 $submodules[] = $manager->getModule( $name );
851 }
852 }
853 $help['submodules'] .= self::getHelpInternal(
854 $context,
855 $submodules,
856 $suboptions,
857 $haveModules
858 );
859 }
860
861 $module->modifyHelp( $help, $suboptions, $haveModules );
862
863 Hooks::run( 'APIHelpModifyOutput', [ $module, &$help, $suboptions, &$haveModules ] );
864
865 $out .= implode( "\n", $help );
866 }
867
868 return $out;
869 }
870
871 public function shouldCheckMaxlag() {
872 return false;
873 }
874
875 public function isReadMode() {
876 return false;
877 }
878
879 public function getCustomPrinter() {
880 $params = $this->extractRequestParams();
881 if ( $params['wrap'] ) {
882 return null;
883 }
884
885 $main = $this->getMain();
886 $errorPrinter = $main->createPrinterByName( $main->getParameter( 'format' ) );
887 return new ApiFormatRaw( $main, $errorPrinter );
888 }
889
890 public function getAllowedParams() {
891 return [
892 'modules' => [
893 ApiBase::PARAM_DFLT => 'main',
895 ],
896 'submodules' => false,
897 'recursivesubmodules' => false,
898 'wrap' => false,
899 'toc' => false,
900 ];
901 }
902
903 protected function getExamplesMessages() {
904 return [
905 'action=help'
906 => 'apihelp-help-example-main',
907 'action=help&modules=query&submodules=1'
908 => 'apihelp-help-example-submodules',
909 'action=help&recursivesubmodules=1'
910 => 'apihelp-help-example-recursive',
911 'action=help&modules=help'
912 => 'apihelp-help-example-help',
913 'action=help&modules=query+info|query+categorymembers'
914 => 'apihelp-help-example-query',
915 ];
916 }
917
918 public function getHelpUrls() {
919 return [
920 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Main_page',
921 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:FAQ',
922 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Quick_start_guide',
923 ];
924 }
925}
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:42
const PARAM_REQUIRED
(boolean) Is the parameter required?
Definition ApiBase.php:118
const PARAM_MAX2
(integer) Max value allowed for the parameter for users with the apihighlimits right,...
Definition ApiBase.php:103
const PARAM_SUBMODULE_MAP
(string[]) When PARAM_TYPE is 'submodule', map parameter values to submodule paths.
Definition ApiBase.php:172
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
Definition ApiBase.php:112
getModuleFromPath( $path)
Get a module from its module path.
Definition ApiBase.php:602
static makeMessage( $msg, IContextSource $context, array $params=null)
Create a Message from a string or array.
Definition ApiBase.php:1800
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition ApiBase.php:97
const PARAM_DEPRECATED_VALUES
(array) When PARAM_TYPE is an array, this indicates which of the values are deprecated.
Definition ApiBase.php:209
const PARAM_ISMULTI_LIMIT1
(integer) Maximum number of values, for normal users.
Definition ApiBase.php:215
getMain()
Get the main module.
Definition ApiBase.php:536
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition ApiBase.php:94
const PARAM_HELP_MSG_INFO
(array) Specify additional information tags for the parameter.
Definition ApiBase.php:148
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition ApiBase.php:55
const PARAM_VALUE_LINKS
(string[]) When PARAM_TYPE is an array, this may be an array mapping those values to page titles whic...
Definition ApiBase.php:155
const PARAM_ISMULTI_LIMIT2
(integer) Maximum number of values, for users with the apihighimits right.
Definition ApiBase.php:222
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition ApiBase.php:106
const LIMIT_SML2
Slow query, apihighlimits limit.
Definition ApiBase.php:265
const PARAM_TEMPLATE_VARS
(array) Indicate that this is a templated parameter, and specify replacements.
Definition ApiBase.php:252
getResult()
Get the result object.
Definition ApiBase.php:640
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:761
getModulePath()
Get the path to this module.
Definition ApiBase.php:584
const PARAM_EXTRA_NAMESPACES
(int[]) When PARAM_TYPE is 'namespace', include these as additional possible values.
Definition ApiBase.php:193
const LIMIT_SML1
Slow query, standard limit.
Definition ApiBase.php:263
const PARAM_ALL
(boolean|string) When PARAM_TYPE has a defined set of values and PARAM_ISMULTI is true,...
Definition ApiBase.php:187
const GET_VALUES_FOR_HELP
getAllowedParams() flag: When set, the result could take longer to generate, but should be more thoro...
Definition ApiBase.php:272
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:520
const ALL_DEFAULT_STRING
Definition ApiBase.php:256
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition ApiBase.php:58
Formatter that spits out anything you like with any desired MIME type.
Class to output help for an API module.
Definition ApiHelp.php:32
static wrap(Message $msg, $class, $tag='span')
Wrap a message in HTML with a class.
Definition ApiHelp.php:228
isReadMode()
Indicates whether this module requires read rights.
Definition ApiHelp.php:875
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition ApiHelp.php:33
getExamplesMessages()
Returns usage examples for this module.
Definition ApiHelp.php:903
static fixHelpLinks( $html, $helptitle=null, $localModules=[])
Replace Special:ApiHelp links with links to api.php.
Definition ApiHelp.php:189
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition ApiHelp.php:890
getHelpUrls()
Return links to more detailed help pages about the module.
Definition ApiHelp.php:918
static getHelpInternal(IContextSource $context, array $modules, array $options, &$haveModules)
Recursively-called function to actually construct the help.
Definition ApiHelp.php:243
static getHelp(IContextSource $context, $modules, array $options)
Generate help for the specified modules.
Definition ApiHelp.php:112
shouldCheckMaxlag()
Indicates if this module needs maxlag to be checked.
Definition ApiHelp.php:871
getCustomPrinter()
If the module may only be used with a certain format module, it should override this method to return...
Definition ApiHelp.php:879
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:41
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don't use this unless yo...
Definition ApiResult.php:58
static setSubelementsList(array &$arr, $names)
Causes the elements with the specified names to be output as subelements rather than attributes.
Exception used to abort API execution with an error.
static listExplicitlyDefinedTags()
Lists tags explicitly defined in the change_tag_def table of the database.
IContextSource $context
getContext()
Get the base IContextSource object.
An IContextSource implementation which will inherit context from another source but allow individual ...
static generateTOC( $tree, Language $lang=null)
Generate a table of contents from a section tree.
Definition Linker.php:1716
MediaWikiServices is the service locator for the application scope of MediaWiki.
The Message class provides methods which fulfil two basic services:
Definition Message.php:162
static listParam(array $list, $type='text')
Definition Message.php:1115
parse()
Fully parse the text from wikitext to HTML.
Definition Message.php:930
This is one of the Core classes and should be read at least once by any new developers.
static getExtLicenseFileName( $extDir)
Obtains the full path of an extensions copying or license file if one exists.
static getVersion( $flags='', $lang=null)
Return a string of the MediaWiki version with Git revision if available.
Interface for objects which can provide a MediaWiki context on request.
getConfig()
Get the site configuration.
msg( $key,... $params)
This is the method for getting translated interface messages.
$cache
Definition mcc.php:33
$help
Definition mcc.php:32
return true
Definition router.php:94