34 use Wikimedia\Parsoid\Core\TOCData;
35 use Wikimedia\RemexHtml\Serializer\SerializerNode;
56 parent::__construct( $main, $action );
57 $this->skinFactory = $skinFactory;
64 foreach ( $params[
'modules'] as
$path ) {
70 $context->setSkin( $this->skinFactory->makeSkin(
'apioutput' ) );
72 $context->setTitle( SpecialPage::getTitleFor(
'ApiHelp' ) );
74 $out->setRobotPolicy(
'noindex,nofollow' );
75 $out->setCopyrightUrl(
'https://www.mediawiki.org/wiki/Special:MyLanguage/Copyright' );
76 $out->disallowUserJs();
77 $context->setOutput( $out );
84 $html = ob_get_clean();
87 if ( $params[
'wrap'] ) {
89 'mime' =>
'text/html',
90 'filename' =>
'api-help.html',
98 'Types' => [
'AssocAsObject' =>
true ],
101 $errors = array_filter( [
102 'errors' => $this->
getResult()->getResultData( [
'errors' ], $transform ),
103 'warnings' => $this->
getResult()->getResultData( [
'warnings' ], $transform ),
109 $json = str_replace(
'--',
'-\u002D', $json );
110 $html =
"<!-- API warnings and errors:\n$json\n-->\n$html";
145 $out->addModuleStyles( [
147 'mediawiki.apipretty',
149 $out->setPageTitleMsg( $context->
msg(
'api-help-title' ) );
151 $services = MediaWikiServices::getInstance();
152 $cache = $services->getMainWANObjectCache();
155 $options[
'recursivesubmodules'] &&
156 $context->
getLanguage()->equals( $services->getContentLanguage() )
158 $cacheHelpTimeout = $context->
getConfig()->get( MainConfigNames::APICacheHelpTimeout );
159 if ( $cacheHelpTimeout > 0 ) {
162 (
int)!empty( $options[
'toc'] ),
163 str_replace(
' ',
'_', SpecialVersion::getVersion(
'nodb' ) ) );
164 $cached = $cache->get( $cacheKey );
166 $out->addHTML( $cached );
171 if ( $out->getHTML() !==
'' ) {
177 $options[
'recursivesubmodules'] = !empty( $options[
'recursivesubmodules'] );
178 $options[
'submodules'] = $options[
'recursivesubmodules'] || !empty( $options[
'submodules'] );
181 if ( empty( $options[
'nolead'] ) ) {
182 $msg = $context->
msg(
'api-help-lead' );
183 if ( !$msg->isDisabled() ) {
184 $out->addHTML( $msg->parseAsBlock() );
189 $html = self::getHelpInternal( $context,
$modules, $options, $haveModules );
190 if ( !empty( $options[
'toc'] ) && $haveModules ) {
191 $tocData = TOCData::fromLegacy( array_values( $haveModules ) );
192 $out->addHTML( Linker::generateTOC( $tocData, $context->
getLanguage() ) );
194 $out->addHTML( $html );
196 $helptitle = $options[
'helptitle'] ??
null;
199 $out->addHTML( $html );
201 if ( $cacheKey !==
null ) {
203 $cache->set( $cacheKey, $out->getHTML(), $cacheHelpTimeout );
215 public static function fixHelpLinks( $html, $helptitle =
null, $localModules = [] ) {
216 return HtmlHelper::modifyElements(
218 static function ( SerializerNode $node ):
bool {
219 return $node->name ===
'a'
220 && isset( $node->attrs[
'href'] )
221 && !str_contains( $node->attrs[
'class'] ??
'',
'apihelp-linktrail' );
223 static function ( SerializerNode $node ) use ( $helptitle, $localModules ): SerializerNode {
224 $href = $node->attrs[
'href'];
228 $href = rawurldecode( $href );
229 }
while ( $old !== $href );
230 if ( preg_match(
'!Special:ApiHelp/([^&/|#]+)((?:#.*)?)!', $href, $m ) ) {
231 if ( isset( $localModules[$m[1]] ) ) {
232 $href = $m[2] ===
'' ?
'#' . $m[1] : $m[2];
233 } elseif ( $helptitle !==
null ) {
234 $href = Title::newFromText( str_replace(
'$1', $m[1], $helptitle ) . $m[2] )
242 $node->attrs[
'href'] = $href;
243 unset( $node->attrs[
'title'] );
259 private static function wrap(
Message $msg, $class, $tag =
'span' ) {
260 return Html::rawElement( $tag, [
'class' => $class ],
275 array $options, &$haveModules
279 $level = empty( $options[
'headerlevel'] ) ? 2 : $options[
'headerlevel'];
280 if ( empty( $options[
'tocnumber'] ) ) {
281 $tocnumber = [ 2 => 0 ];
283 $tocnumber = &$options[
'tocnumber'];
287 $paramValidator = $module->getMain()->getParamValidator();
288 $tocnumber[$level]++;
289 $path = $module->getModulePath();
290 $module->setContext( $context );
301 if ( empty( $options[
'noheader'] ) || !empty( $options[
'toc'] ) ) {
304 while ( isset( $haveModules[$anchor] ) ) {
305 $anchor =
$path .
'|' . ++$i;
308 if ( $module->isMain() ) {
309 $headerContent = $context->
msg(
'api-help-main-header' )->parse();
311 'class' =>
'apihelp-header',
314 $name = $module->getModuleName();
315 $headerContent = htmlspecialchars(
316 $module->getParent()->getModuleManager()->getModuleGroup( $name ) .
"=$name"
318 if ( $module->getModulePrefix() !==
'' ) {
319 $headerContent .=
' ' .
320 $context->
msg(
'parentheses', $module->getModulePrefix() )->parse();
326 'class' => [
'apihelp-header',
'apihelp-module-name' ],
332 $headerAttr[
'id'] = $anchor;
335 $haveModules[$anchor] = [
336 'toclevel' => count( $tocnumber ),
339 'line' => $headerContent,
340 'number' => implode(
'.', $tocnumber ),
343 if ( empty( $options[
'noheader'] ) ) {
344 $help[
'header'] .= Html::rawElement(
345 'h' . min( 6, $level ),
351 $haveModules[
$path] =
true;
356 for ( $m = $module; $m !==
null; $m = $m->getParent() ) {
357 $name = $m->getModuleName();
358 if ( $name ===
'main_int' ) {
363 !( !empty( $options[
'submodules'] ) && $m->getModuleManager() )
365 $link = Html::element(
'b', [
'dir' =>
'ltr',
'lang' =>
'en' ], $name );
367 $link = SpecialPage::getTitleFor(
'ApiHelp', $m->getModulePath() )->getLocalURL();
368 $link = Html::element(
'a',
369 [
'href' => $link,
'class' =>
'apihelp-linktrail',
'dir' =>
'ltr',
'lang' =>
'en' ],
374 array_unshift( $links, $link );
377 $help[
'header'] .= self::wrap(
378 $context->
msg(
'parentheses' )
379 ->rawParams( $context->
getLanguage()->pipeList( $links ) ),
380 'apihelp-linktrail',
'div'
384 $flags = $module->getHelpFlags();
385 $help[
'flags'] .= Html::openElement(
'div',
386 [
'class' => [
'apihelp-block',
'apihelp-flags' ] ] );
387 $msg = $context->
msg(
'api-help-flags' );
388 if ( !$msg->isDisabled() ) {
389 $help[
'flags'] .= self::wrap(
390 $msg->numParams( count( $flags ) ),
'apihelp-block-head',
'div'
393 $help[
'flags'] .= Html::openElement(
'ul' );
394 foreach ( $flags as $flag ) {
395 $help[
'flags'] .= Html::rawElement(
'li', [],
402 self::wrap( $context->
msg(
"api-help-flag-$flag" ),
"apihelp-flag-$flag" )
405 $sourceInfo = $module->getModuleSourceInfo();
407 if ( isset( $sourceInfo[
'namemsg'] ) ) {
408 $extname = $context->
msg( $sourceInfo[
'namemsg'] )->text();
411 $extname = Html::element(
'span', [
'dir' =>
'ltr',
'lang' =>
'en' ], $sourceInfo[
'name'] );
413 $help[
'flags'] .= Html::rawElement(
'li', [],
415 $context->
msg(
'api-help-source', $extname, $sourceInfo[
'name'] ),
420 $link = SpecialPage::getTitleFor(
'Version',
'License/' . $sourceInfo[
'name'] );
421 if ( isset( $sourceInfo[
'license-name'] ) ) {
422 $msg = $context->
msg(
'api-help-license', $link,
423 Html::element(
'span', [
'dir' =>
'ltr',
'lang' =>
'en' ], $sourceInfo[
'license-name'] )
425 } elseif ( ExtensionInfo::getLicenseFileNames( dirname( $sourceInfo[
'path'] ) ) ) {
426 $msg = $context->
msg(
'api-help-license-noname', $link );
428 $msg = $context->
msg(
'api-help-license-unknown' );
430 $help[
'flags'] .= Html::rawElement(
'li', [],
431 self::wrap( $msg,
'apihelp-license' )
434 $help[
'flags'] .= Html::rawElement(
'li', [],
435 self::wrap( $context->
msg(
'api-help-source-unknown' ),
'apihelp-source' )
437 $help[
'flags'] .= Html::rawElement(
'li', [],
438 self::wrap( $context->
msg(
'api-help-license-unknown' ),
'apihelp-license' )
441 $help[
'flags'] .= Html::closeElement(
'ul' );
442 $help[
'flags'] .= Html::closeElement(
'div' );
444 foreach ( $module->getFinalDescription() as $msg ) {
445 $msg->setContext( $context );
446 $help[
'description'] .= $msg->parseAsBlock();
449 $urls = $module->getHelpUrls();
451 if ( !is_array( $urls ) ) {
454 $help[
'help-urls'] .= Html::openElement(
'div',
455 [
'class' => [
'apihelp-block',
'apihelp-help-urls' ] ]
457 $msg = $context->
msg(
'api-help-help-urls' );
458 if ( !$msg->isDisabled() ) {
459 $help[
'help-urls'] .= self::wrap(
460 $msg->numParams( count( $urls ) ),
'apihelp-block-head',
'div'
463 $help[
'help-urls'] .= Html::openElement(
'ul' );
464 foreach ( $urls as $url ) {
465 $help[
'help-urls'] .= Html::rawElement(
'li', [],
466 Html::element(
'a', [
'href' => $url,
'dir' =>
'ltr' ], $url )
469 $help[
'help-urls'] .= Html::closeElement(
'ul' );
470 $help[
'help-urls'] .= Html::closeElement(
'div' );
474 $dynamicParams = $module->dynamicParameterDocumentation();
476 if ( $params || $dynamicParams !==
null ) {
477 $help[
'parameters'] .= Html::openElement(
'div',
478 [
'class' => [
'apihelp-block',
'apihelp-parameters' ] ]
480 $msg = $context->
msg(
'api-help-parameters' );
481 if ( !$msg->isDisabled() ) {
482 $help[
'parameters'] .= self::wrap(
483 $msg->numParams( count( $params ) ),
'apihelp-block-head',
'div'
485 if ( !$module->isMain() ) {
487 $help[
'parameters'] .= self::wrap(
488 $context->
msg(
'api-help-parameters-note' ),
'apihelp-block-header',
'div'
492 $help[
'parameters'] .= Html::openElement(
'dl' );
494 $descriptions = $module->getFinalParamDescription();
496 foreach ( $params as $name => $settings ) {
497 $settings = $paramValidator->normalizeSettings( $settings );
499 if ( $settings[ParamValidator::PARAM_TYPE] ===
'submodule' ) {
503 $encodedParamName = $module->encodeParamName( $name );
504 $paramNameAttribs = [
'dir' =>
'ltr',
'lang' =>
'en' ];
505 if ( isset( $anchor ) ) {
506 $paramNameAttribs[
'id'] =
"$anchor:$encodedParamName";
508 $help[
'parameters'] .= Html::rawElement(
'dt', [],
509 Html::element(
'span', $paramNameAttribs, $encodedParamName )
514 if ( isset( $descriptions[$name] ) ) {
515 foreach ( $descriptions[$name] as $msg ) {
516 $msg->setContext( $context );
517 $description[] = $msg->parseAsBlock();
520 if ( !array_filter( $description ) ) {
521 $description = [ self::wrap(
522 $context->
msg(
'api-help-param-no-description' ),
528 if ( !empty( $settings[ParamValidator::PARAM_DEPRECATED] ) ) {
529 $help[
'parameters'] .= Html::openElement(
'dd',
530 [
'class' =>
'info' ] );
531 $help[
'parameters'] .= self::wrap(
532 $context->
msg(
'api-help-param-deprecated' ),
533 'apihelp-deprecated',
'strong'
535 $help[
'parameters'] .= Html::closeElement(
'dd' );
538 if ( $description ) {
539 $description = implode(
'', $description );
540 $description = preg_replace(
'!\s*</([oud]l)>\s*<\1>\s*!',
"\n", $description );
541 $help[
'parameters'] .= Html::rawElement(
'dd',
542 [
'class' =>
'description' ], $description );
547 $paramHelp = $paramValidator->getHelpInfo( $module, $name, $settings, [] );
549 unset( $paramHelp[ParamValidator::PARAM_DEPRECATED] );
551 if ( isset( $paramHelp[ParamValidator::PARAM_REQUIRED] ) ) {
552 $paramHelp[ParamValidator::PARAM_REQUIRED]->setContext( $context );
553 $info[] = $paramHelp[ParamValidator::PARAM_REQUIRED];
554 unset( $paramHelp[ParamValidator::PARAM_REQUIRED] );
560 $tag = array_shift( $i );
561 $info[] = $context->
msg(
"apihelp-{$path}-paraminfo-{$tag}" )
562 ->numParams( count( $i ) )
563 ->params( $context->
getLanguage()->commaList( $i ) )
564 ->params( $module->getModulePrefix() )
572 $msg =
'api-help-param-templated-var-first';
574 $vars[] = $context->
msg( $msg, $k, $module->encodeParamName( $v ) );
575 $msg =
'api-help-param-templated-var';
577 $info[] = $context->
msg(
'api-help-param-templated' )
578 ->numParams( count( $vars ) )
584 foreach ( $paramHelp as $m ) {
585 $m->setContext( $context );
586 $info[] = $m->parse();
589 foreach ( $info as $i ) {
590 $help[
'parameters'] .= Html::rawElement(
'dd', [
'class' =>
'info' ], $i );
594 if ( $dynamicParams !==
null ) {
596 $module->getModulePrefix(),
597 $module->getModuleName(),
598 $module->getModulePath()
600 $help[
'parameters'] .= Html::element(
'dt', [],
'*' );
601 $help[
'parameters'] .= Html::rawElement(
'dd',
602 [
'class' =>
'description' ], $dynamicParams->parse() );
605 $help[
'parameters'] .= Html::closeElement(
'dl' );
606 $help[
'parameters'] .= Html::closeElement(
'div' );
609 $examples = $module->getExamplesMessages();
611 $help[
'examples'] .= Html::openElement(
'div',
612 [
'class' => [
'apihelp-block',
'apihelp-examples' ] ] );
613 $msg = $context->
msg(
'api-help-examples' );
614 if ( !$msg->isDisabled() ) {
615 $help[
'examples'] .= self::wrap(
616 $msg->numParams( count( $examples ) ),
'apihelp-block-head',
'div'
620 $help[
'examples'] .= Html::openElement(
'dl' );
621 foreach ( $examples as $qs => $msg ) {
623 $module->getModulePrefix(),
624 $module->getModuleName(),
625 $module->getModulePath()
629 $sandbox = SpecialPage::getTitleFor(
'ApiSandbox' )->getLocalURL() .
'#' . $qs;
630 $help[
'examples'] .= Html::rawElement(
'dt', [], $msg->parse() );
631 $help[
'examples'] .= Html::rawElement(
'dd', [],
632 Html::element(
'a', [
636 ],
"api.php?$qs" ) .
' ' .
637 Html::rawElement(
'a', [
'href' => $sandbox ],
638 $context->
msg(
'api-help-open-in-apisandbox' )->parse() )
641 $help[
'examples'] .= Html::closeElement(
'dl' );
642 $help[
'examples'] .= Html::closeElement(
'div' );
645 $subtocnumber = $tocnumber;
646 $subtocnumber[$level + 1] = 0;
648 'submodules' => $options[
'recursivesubmodules'],
649 'headerlevel' => $level + 1,
650 'tocnumber' => &$subtocnumber,
655 if ( $options[
'submodules'] && $module->getModuleManager() ) {
656 $manager = $module->getModuleManager();
658 foreach ( $groups as $group ) {
659 $names = $manager->getNames( $group );
661 foreach ( $names as $name ) {
662 $submodules[] = $manager->getModule( $name );
665 $help[
'submodules'] .= self::getHelpInternal(
673 $module->modifyHelp( $help, $suboptions, $haveModules );
675 $module->getHookRunner()->onAPIHelpModifyOutput( $module, $help,
676 $suboptions, $haveModules );
678 $out .= implode(
"\n", $help );
694 if ( $params[
'wrap'] ) {
699 $errorPrinter = $main->createPrinterByName( $main->getParameter(
'format' ) );
706 ParamValidator::PARAM_DEFAULT =>
'main',
707 ParamValidator::PARAM_ISMULTI =>
true,
709 'submodules' =>
false,
710 'recursivesubmodules' =>
false,
719 =>
'apihelp-help-example-main',
720 'action=help&modules=query&submodules=1'
721 =>
'apihelp-help-example-submodules',
722 'action=help&recursivesubmodules=1'
723 =>
'apihelp-help-example-recursive',
724 'action=help&modules=help'
725 =>
'apihelp-help-example-help',
726 'action=help&modules=query+info|query+categorymembers'
727 =>
'apihelp-help-example-query',
733 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Main_page',
734 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:FAQ',
735 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Quick_start_guide',
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 URL path to a MediaWiki entry point.
This abstract class implements many basic API functions, and is the base of all API classes.
getModuleFromPath( $path)
Get a module from its module path.
static makeMessage( $msg, IContextSource $context, array $params=null)
Create a Message from a string or array.
getMain()
Get the main module.
const PARAM_HELP_MSG_INFO
(array) Specify additional information tags for the parameter.
const PARAM_TEMPLATE_VARS
(array) Indicate that this is a templated parameter, and specify replacements.
getResult()
Get the result object.
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
getModulePath()
Get the path to this module.
const GET_VALUES_FOR_HELP
getAllowedParams() flag: When this is set, the result could take longer to generate,...
getModuleName()
Get the name of the module being executed by this instance.
Class to output help for an API module.
isReadMode()
Indicates whether this module requires read rights.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
getExamplesMessages()
Returns usage examples for this module.
static fixHelpLinks( $html, $helptitle=null, $localModules=[])
Replace Special:ApiHelp links with links to api.php.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
getHelpUrls()
Return links to more detailed help pages about the module.
static getHelp(IContextSource $context, $modules, array $options)
Generate help for the specified modules.
shouldCheckMaxlag()
Indicates if this module needs maxlag to be checked.
__construct(ApiMain $main, $action, SkinFactory $skinFactory)
getCustomPrinter()
If the module may only be used with a certain format module, it should override this method to return...
This is the main API class, used for both external and internal processing.
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don't use this unless yo...
static setSubelementsList(array &$arr, $names)
Causes the elements with the specified names to be output as subelements rather than attributes.
getContext()
Get the base IContextSource object.
An IContextSource implementation which will inherit context from another source but allow individual ...
A class containing constants representing the names of configuration variables.
This is one of the Core classes and should be read at least once by any new developers.
Parent class for all special pages.
The Message class deals with fetching and processing of interface message into a variety of formats.
static listParam(array $list, $type='text')
parse()
Fully parse the text from wikitext to HTML.
Factory class to create Skin objects.
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.