31 use Wikimedia\Parsoid\Core\TOCData;
32 use Wikimedia\RemexHtml\Serializer\SerializerNode;
54 parent::__construct( $main, $action );
55 $this->skinFactory = $skinFactory;
62 foreach ( $params[
'modules'] as
$path ) {
68 $context->setSkin( $this->skinFactory->makeSkin(
'apioutput' ) );
72 $out->setRobotPolicy(
'noindex,nofollow' );
73 $out->setCopyrightUrl(
'https://www.mediawiki.org/wiki/Special:MyLanguage/Copyright' );
74 $out->disallowUserJs();
75 $context->setOutput( $out );
82 $html = ob_get_clean();
85 if ( $params[
'wrap'] ) {
87 'mime' =>
'text/html',
88 'filename' =>
'api-help.html',
96 'Types' => [
'AssocAsObject' =>
true ],
99 $errors = array_filter( [
100 'errors' => $this->
getResult()->getResultData( [
'errors' ], $transform ),
101 'warnings' => $this->
getResult()->getResultData( [
'warnings' ], $transform ),
107 $json = str_replace(
'--',
'-\u002D', $json );
108 $html =
"<!-- API warnings and errors:\n$json\n-->\n$html";
143 $out->addModuleStyles( [
145 'mediawiki.apipretty',
147 $out->setPageTitle( $context->
msg(
'api-help-title' ) );
149 $services = MediaWikiServices::getInstance();
150 $cache = $services->getMainWANObjectCache();
153 $options[
'recursivesubmodules'] &&
154 $context->
getLanguage()->equals( $services->getContentLanguage() )
156 $cacheHelpTimeout = $context->
getConfig()->get( MainConfigNames::APICacheHelpTimeout );
157 if ( $cacheHelpTimeout > 0 ) {
160 (
int)!empty( $options[
'toc'] ),
162 $cached = $cache->get( $cacheKey );
164 $out->addHTML( $cached );
169 if ( $out->getHTML() !==
'' ) {
175 $options[
'recursivesubmodules'] = !empty( $options[
'recursivesubmodules'] );
176 $options[
'submodules'] = $options[
'recursivesubmodules'] || !empty( $options[
'submodules'] );
179 if ( empty( $options[
'nolead'] ) ) {
180 $msg = $context->
msg(
'api-help-lead' );
181 if ( !$msg->isDisabled() ) {
182 $out->addHTML( $msg->parseAsBlock() );
187 $html = self::getHelpInternal( $context,
$modules, $options, $haveModules );
188 if ( !empty( $options[
'toc'] ) && $haveModules ) {
189 $out->addHTML( Linker::generateTOC( TOCData::fromLegacy( $haveModules ), $context->
getLanguage() ) );
191 $out->addHTML( $html );
193 $helptitle = $options[
'helptitle'] ??
null;
196 $out->addHTML( $html );
198 if ( $cacheKey !==
null ) {
200 $cache->set( $cacheKey, $out->getHTML(), $cacheHelpTimeout );
212 public static function fixHelpLinks( $html, $helptitle =
null, $localModules = [] ) {
213 return HtmlHelper::modifyElements(
215 static function ( SerializerNode $node ):
bool {
216 return $node->name ===
'a'
217 && isset( $node->attrs[
'href'] )
218 && !str_contains( $node->attrs[
'class'] ??
'',
'apihelp-linktrail' );
220 static function ( SerializerNode $node ) use ( $helptitle, $localModules ): SerializerNode {
221 $href = $node->attrs[
'href'];
225 $href = rawurldecode( $href );
226 }
while ( $old !== $href );
227 if ( preg_match(
'!Special:ApiHelp/([^&/|#]+)((?:#.*)?)!', $href, $m ) ) {
228 if ( isset( $localModules[$m[1]] ) ) {
229 $href = $m[2] ===
'' ?
'#' . $m[1] : $m[2];
230 } elseif ( $helptitle !==
null ) {
231 $href = Title::newFromText( str_replace(
'$1', $m[1], $helptitle ) . $m[2] )
239 $node->attrs[
'href'] = $href;
240 unset( $node->attrs[
'title'] );
256 private static function wrap(
Message $msg, $class, $tag =
'span' ) {
257 return Html::rawElement( $tag, [
'class' => $class ],
272 array $options, &$haveModules
276 $level = empty( $options[
'headerlevel'] ) ? 2 : $options[
'headerlevel'];
277 if ( empty( $options[
'tocnumber'] ) ) {
278 $tocnumber = [ 2 => 0 ];
280 $tocnumber = &$options[
'tocnumber'];
284 $paramValidator = $module->getMain()->getParamValidator();
285 $tocnumber[$level]++;
286 $path = $module->getModulePath();
287 $module->setContext( $context );
298 if ( empty( $options[
'noheader'] ) || !empty( $options[
'toc'] ) ) {
301 while ( isset( $haveModules[$anchor] ) ) {
302 $anchor =
$path .
'|' . ++$i;
305 if ( $module->isMain() ) {
306 $headerContent = $context->
msg(
'api-help-main-header' )->parse();
308 'class' =>
'apihelp-header',
311 $name = $module->getModuleName();
312 $headerContent = htmlspecialchars(
313 $module->getParent()->getModuleManager()->getModuleGroup( $name ) .
"=$name"
315 if ( $module->getModulePrefix() !==
'' ) {
316 $headerContent .=
' ' .
317 $context->
msg(
'parentheses', $module->getModulePrefix() )->parse();
323 'class' => [
'apihelp-header',
'apihelp-module-name' ],
329 $headerAttr[
'id'] = $anchor;
332 $haveModules[$anchor] = [
333 'toclevel' => count( $tocnumber ),
336 'line' => $headerContent,
337 'number' => implode(
'.', $tocnumber ),
340 if ( empty( $options[
'noheader'] ) ) {
341 $help[
'header'] .= Html::rawElement(
342 'h' . min( 6, $level ),
348 $haveModules[
$path] =
true;
353 for ( $m = $module; $m !==
null; $m = $m->getParent() ) {
354 $name = $m->getModuleName();
355 if ( $name ===
'main_int' ) {
360 !( !empty( $options[
'submodules'] ) && $m->getModuleManager() )
362 $link = Html::element(
'b', [
'dir' =>
'ltr',
'lang' =>
'en' ], $name );
365 $link = Html::element(
'a',
366 [
'href' => $link,
'class' =>
'apihelp-linktrail',
'dir' =>
'ltr',
'lang' =>
'en' ],
371 array_unshift( $links, $link );
374 $help[
'header'] .= self::wrap(
375 $context->
msg(
'parentheses' )
376 ->rawParams( $context->
getLanguage()->pipeList( $links ) ),
377 'apihelp-linktrail',
'div'
381 $flags = $module->getHelpFlags();
382 $help[
'flags'] .= Html::openElement(
'div',
383 [
'class' => [
'apihelp-block',
'apihelp-flags' ] ] );
384 $msg = $context->
msg(
'api-help-flags' );
385 if ( !$msg->isDisabled() ) {
386 $help[
'flags'] .= self::wrap(
387 $msg->numParams( count( $flags ) ),
'apihelp-block-head',
'div'
390 $help[
'flags'] .= Html::openElement(
'ul' );
391 foreach ( $flags as $flag ) {
392 $help[
'flags'] .= Html::rawElement(
'li', [],
399 self::wrap( $context->
msg(
"api-help-flag-$flag" ),
"apihelp-flag-$flag" )
402 $sourceInfo = $module->getModuleSourceInfo();
404 if ( isset( $sourceInfo[
'namemsg'] ) ) {
405 $extname = $context->
msg( $sourceInfo[
'namemsg'] )->text();
408 $extname = Html::element(
'span', [
'dir' =>
'ltr',
'lang' =>
'en' ], $sourceInfo[
'name'] );
410 $help[
'flags'] .= Html::rawElement(
'li', [],
412 $context->
msg(
'api-help-source', $extname, $sourceInfo[
'name'] ),
418 if ( isset( $sourceInfo[
'license-name'] ) ) {
419 $msg = $context->
msg(
'api-help-license', $link,
420 Html::element(
'span', [
'dir' =>
'ltr',
'lang' =>
'en' ], $sourceInfo[
'license-name'] )
422 } elseif ( ExtensionInfo::getLicenseFileNames( dirname( $sourceInfo[
'path'] ) ) ) {
423 $msg = $context->
msg(
'api-help-license-noname', $link );
425 $msg = $context->
msg(
'api-help-license-unknown' );
427 $help[
'flags'] .= Html::rawElement(
'li', [],
428 self::wrap( $msg,
'apihelp-license' )
431 $help[
'flags'] .= Html::rawElement(
'li', [],
432 self::wrap( $context->
msg(
'api-help-source-unknown' ),
'apihelp-source' )
434 $help[
'flags'] .= Html::rawElement(
'li', [],
435 self::wrap( $context->
msg(
'api-help-license-unknown' ),
'apihelp-license' )
438 $help[
'flags'] .= Html::closeElement(
'ul' );
439 $help[
'flags'] .= Html::closeElement(
'div' );
441 foreach ( $module->getFinalDescription() as $msg ) {
442 $msg->setContext( $context );
443 $help[
'description'] .= $msg->parseAsBlock();
446 $urls = $module->getHelpUrls();
448 if ( !is_array( $urls ) ) {
451 $help[
'help-urls'] .= Html::openElement(
'div',
452 [
'class' => [
'apihelp-block',
'apihelp-help-urls' ] ]
454 $msg = $context->
msg(
'api-help-help-urls' );
455 if ( !$msg->isDisabled() ) {
456 $help[
'help-urls'] .= self::wrap(
457 $msg->numParams( count( $urls ) ),
'apihelp-block-head',
'div'
460 $help[
'help-urls'] .= Html::openElement(
'ul' );
461 foreach ( $urls as $url ) {
462 $help[
'help-urls'] .= Html::rawElement(
'li', [],
463 Html::element(
'a', [
'href' => $url,
'dir' =>
'ltr' ], $url )
466 $help[
'help-urls'] .= Html::closeElement(
'ul' );
467 $help[
'help-urls'] .= Html::closeElement(
'div' );
471 $dynamicParams = $module->dynamicParameterDocumentation();
473 if ( $params || $dynamicParams !==
null ) {
474 $help[
'parameters'] .= Html::openElement(
'div',
475 [
'class' => [
'apihelp-block',
'apihelp-parameters' ] ]
477 $msg = $context->
msg(
'api-help-parameters' );
478 if ( !$msg->isDisabled() ) {
479 $help[
'parameters'] .= self::wrap(
480 $msg->numParams( count( $params ) ),
'apihelp-block-head',
'div'
483 $help[
'parameters'] .= Html::openElement(
'dl' );
485 $descriptions = $module->getFinalParamDescription();
487 foreach ( $params as $name => $settings ) {
488 $settings = $paramValidator->normalizeSettings( $settings );
490 if ( $settings[ParamValidator::PARAM_TYPE] ===
'submodule' ) {
494 $help[
'parameters'] .= Html::rawElement(
'dt', [],
495 Html::element(
'span', [
'dir' =>
'ltr',
'lang' =>
'en' ], $module->encodeParamName( $name ) )
500 if ( isset( $descriptions[$name] ) ) {
501 foreach ( $descriptions[$name] as $msg ) {
502 $msg->setContext( $context );
503 $description[] = $msg->parseAsBlock();
506 if ( !array_filter( $description ) ) {
507 $description = [ self::wrap(
508 $context->
msg(
'api-help-param-no-description' ),
514 if ( !empty( $settings[ParamValidator::PARAM_DEPRECATED] ) ) {
515 $help[
'parameters'] .= Html::openElement(
'dd',
516 [
'class' =>
'info' ] );
517 $help[
'parameters'] .= self::wrap(
518 $context->
msg(
'api-help-param-deprecated' ),
519 'apihelp-deprecated',
'strong'
521 $help[
'parameters'] .= Html::closeElement(
'dd' );
524 if ( $description ) {
525 $description = implode(
'', $description );
526 $description = preg_replace(
'!\s*</([oud]l)>\s*<\1>\s*!',
"\n", $description );
527 $help[
'parameters'] .= Html::rawElement(
'dd',
528 [
'class' =>
'description' ], $description );
533 $paramHelp = $paramValidator->getHelpInfo( $module, $name, $settings, [] );
535 unset( $paramHelp[ParamValidator::PARAM_DEPRECATED] );
537 if ( isset( $paramHelp[ParamValidator::PARAM_REQUIRED] ) ) {
538 $paramHelp[ParamValidator::PARAM_REQUIRED]->setContext( $context );
539 $info[] = $paramHelp[ParamValidator::PARAM_REQUIRED];
540 unset( $paramHelp[ParamValidator::PARAM_REQUIRED] );
546 $tag = array_shift( $i );
547 $info[] = $context->
msg(
"apihelp-{$path}-paraminfo-{$tag}" )
548 ->numParams( count( $i ) )
549 ->params( $context->
getLanguage()->commaList( $i ) )
550 ->params( $module->getModulePrefix() )
558 $msg =
'api-help-param-templated-var-first';
560 $vars[] = $context->
msg( $msg, $k, $module->encodeParamName( $v ) );
561 $msg =
'api-help-param-templated-var';
563 $info[] = $context->
msg(
'api-help-param-templated' )
564 ->numParams( count( $vars ) )
570 foreach ( $paramHelp as $m ) {
571 $m->setContext( $context );
575 foreach ( $info as $i ) {
576 $help[
'parameters'] .= Html::rawElement(
'dd', [
'class' =>
'info' ], $i );
580 if ( $dynamicParams !==
null ) {
582 $module->getModulePrefix(),
583 $module->getModuleName(),
584 $module->getModulePath()
586 $help[
'parameters'] .= Html::element(
'dt', [],
'*' );
587 $help[
'parameters'] .= Html::rawElement(
'dd',
588 [
'class' =>
'description' ], $dynamicParams->parse() );
591 $help[
'parameters'] .= Html::closeElement(
'dl' );
592 $help[
'parameters'] .= Html::closeElement(
'div' );
595 $examples = $module->getExamplesMessages();
597 $help[
'examples'] .= Html::openElement(
'div',
598 [
'class' => [
'apihelp-block',
'apihelp-examples' ] ] );
599 $msg = $context->
msg(
'api-help-examples' );
600 if ( !$msg->isDisabled() ) {
601 $help[
'examples'] .= self::wrap(
602 $msg->numParams( count( $examples ) ),
'apihelp-block-head',
'div'
606 $help[
'examples'] .= Html::openElement(
'dl' );
607 foreach ( $examples as $qs => $msg ) {
609 $module->getModulePrefix(),
610 $module->getModuleName(),
611 $module->getModulePath()
616 $help[
'examples'] .= Html::rawElement(
'dt', [], $msg->parse() );
617 $help[
'examples'] .= Html::rawElement(
'dd', [],
618 Html::element(
'a', [
622 ],
"api.php?$qs" ) .
' ' .
623 Html::rawElement(
'a', [
'href' => $sandbox ],
624 $context->
msg(
'api-help-open-in-apisandbox' )->parse() )
627 $help[
'examples'] .= Html::closeElement(
'dl' );
628 $help[
'examples'] .= Html::closeElement(
'div' );
631 $subtocnumber = $tocnumber;
632 $subtocnumber[$level + 1] = 0;
634 'submodules' => $options[
'recursivesubmodules'],
635 'headerlevel' => $level + 1,
636 'tocnumber' => &$subtocnumber,
641 if ( $options[
'submodules'] && $module->getModuleManager() ) {
642 $manager = $module->getModuleManager();
644 foreach ( $groups as $group ) {
645 $names = $manager->getNames( $group );
647 foreach ( $names as $name ) {
648 $submodules[] = $manager->getModule( $name );
651 $help[
'submodules'] .= self::getHelpInternal(
659 $module->modifyHelp( $help, $suboptions, $haveModules );
661 $module->getHookRunner()->onAPIHelpModifyOutput( $module, $help,
662 $suboptions, $haveModules );
664 $out .= implode(
"\n", $help );
680 if ( $params[
'wrap'] ) {
685 $errorPrinter = $main->createPrinterByName( $main->getParameter(
'format' ) );
692 ParamValidator::PARAM_DEFAULT =>
'main',
693 ParamValidator::PARAM_ISMULTI =>
true,
695 'submodules' =>
false,
696 'recursivesubmodules' =>
false,
705 =>
'apihelp-help-example-main',
706 'action=help&modules=query&submodules=1'
707 =>
'apihelp-help-example-submodules',
708 'action=help&recursivesubmodules=1'
709 =>
'apihelp-help-example-recursive',
710 'action=help&modules=help'
711 =>
'apihelp-help-example-help',
712 'action=help&modules=query+info|query+categorymembers'
713 =>
'apihelp-help-example-query',
719 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Main_page',
720 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:FAQ',
721 '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 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.
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 set, the result could take longer to generate, but should be more thoro...
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.
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.
This is one of the Core classes and should be read at least once by any new developers.
Factory class to create Skin objects.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
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.