49 parent::__construct( $main, $action );
50 $this->skinFactory = $skinFactory;
57 foreach ( $params[
'modules'] as
$path ) {
63 $context->setSkin( $this->skinFactory->makeSkin(
'apioutput' ) );
67 $out->setRobotPolicy(
'noindex,nofollow' );
68 $out->setCopyrightUrl(
'https://www.mediawiki.org/wiki/Special:MyLanguage/Copyright' );
69 $out->disallowUserJs();
70 $context->setOutput( $out );
77 $html = ob_get_clean();
80 if ( $params[
'wrap'] ) {
82 'mime' =>
'text/html',
83 'filename' =>
'api-help.html',
86 ApiResult::setSubelementsList( $data,
'help' );
91 'Types' => [
'AssocAsObject' =>
true ],
94 $errors = array_filter( [
95 'errors' => $this->
getResult()->getResultData( [
'errors' ], $transform ),
96 'warnings' => $this->
getResult()->getResultData( [
'warnings' ], $transform ),
99 $json = FormatJson::encode( $errors,
true, FormatJson::UTF8_OK );
102 $json = str_replace(
'--',
'-\u002D', $json );
103 $html =
"<!-- API warnings and errors:\n$json\n-->\n$html";
107 $result->addValue(
null,
'text', $html, ApiResult::NO_SIZE_CHECK );
108 $result->addValue(
null,
'mime',
'text/html', ApiResult::NO_SIZE_CHECK );
109 $result->addValue(
null,
'filename',
'api-help.html', ApiResult::NO_SIZE_CHECK );
138 $out->addModuleStyles( [
140 'mediawiki.apipretty',
142 $out->setPageTitle( $context->
msg(
'api-help-title' ) );
144 $services = MediaWikiServices::getInstance();
145 $cache = $services->getMainWANObjectCache();
148 $options[
'recursivesubmodules'] &&
149 $context->
getLanguage()->equals( $services->getContentLanguage() )
151 $cacheHelpTimeout = $context->
getConfig()->get( MainConfigNames::APICacheHelpTimeout );
152 if ( $cacheHelpTimeout > 0 ) {
155 (
int)!empty( $options[
'toc'] ),
157 $cached =
$cache->get( $cacheKey );
159 $out->addHTML( $cached );
164 if ( $out->getHTML() !==
'' ) {
170 $options[
'recursivesubmodules'] = !empty( $options[
'recursivesubmodules'] );
171 $options[
'submodules'] = $options[
'recursivesubmodules'] || !empty( $options[
'submodules'] );
174 if ( empty( $options[
'nolead'] ) ) {
175 $msg = $context->
msg(
'api-help-lead' );
176 if ( !$msg->isDisabled() ) {
177 $out->addHTML( $msg->parseAsBlock() );
182 $html = self::getHelpInternal( $context,
$modules, $options, $haveModules );
183 if ( !empty( $options[
'toc'] ) && $haveModules ) {
187 $out->addHTML( $html );
189 $helptitle = $options[
'helptitle'] ??
null;
192 $out->addHTML( $html );
194 if ( $cacheKey !==
null ) {
196 $cache->set( $cacheKey, $out->getHTML(), $cacheHelpTimeout );
208 public static function fixHelpLinks( $html, $helptitle =
null, $localModules = [] ) {
209 $formatter =
new HtmlFormatter( $html );
210 $doc = $formatter->getDoc();
211 $xpath =
new DOMXPath( $doc );
212 $nodes = $xpath->query(
'//a[@href][not(contains(@class,\'apihelp-linktrail\'))]' );
214 foreach ( $nodes as $node ) {
215 $href = $node->getAttribute(
'href' );
218 $href = rawurldecode( $href );
219 }
while ( $old !== $href );
220 if ( preg_match(
'!Special:ApiHelp/([^&/|#]+)((?:#.*)?)!', $href, $m ) ) {
221 if ( isset( $localModules[$m[1]] ) ) {
222 $href = $m[2] ===
'' ?
'#' . $m[1] : $m[2];
223 } elseif ( $helptitle !==
null ) {
224 $href = Title::newFromText( str_replace(
'$1', $m[1], $helptitle ) . $m[2] )
232 $node->setAttribute(
'href', $href );
233 $node->removeAttribute(
'title' );
237 return $formatter->getText();
248 private static function wrap(
Message $msg, $class, $tag =
'span' ) {
249 return Html::rawElement( $tag, [
'class' => $class ],
264 array $options, &$haveModules
268 $level = empty( $options[
'headerlevel'] ) ? 2 : $options[
'headerlevel'];
269 if ( empty( $options[
'tocnumber'] ) ) {
270 $tocnumber = [ 2 => 0 ];
272 $tocnumber = &$options[
'tocnumber'];
276 $paramValidator = $module->getMain()->getParamValidator();
277 $tocnumber[$level]++;
278 $path = $module->getModulePath();
279 $module->setContext( $context );
290 if ( empty( $options[
'noheader'] ) || !empty( $options[
'toc'] ) ) {
293 while ( isset( $haveModules[$anchor] ) ) {
294 $anchor =
$path .
'|' . ++$i;
297 if ( $module->isMain() ) {
298 $headerContent = $context->
msg(
'api-help-main-header' )->parse();
300 'class' =>
'apihelp-header',
303 $name = $module->getModuleName();
304 $headerContent = htmlspecialchars(
305 $module->getParent()->getModuleManager()->getModuleGroup( $name ) .
"=$name"
307 if ( $module->getModulePrefix() !==
'' ) {
308 $headerContent .=
' ' .
309 $context->
msg(
'parentheses', $module->getModulePrefix() )->parse();
315 'class' => [
'apihelp-header',
'apihelp-module-name' ],
321 $headerAttr[
'id'] = $anchor;
323 $haveModules[$anchor] = [
324 'toclevel' => count( $tocnumber ),
327 'line' => $headerContent,
328 'number' => implode(
'.', $tocnumber ),
331 if ( empty( $options[
'noheader'] ) ) {
333 'h' . min( 6, $level ),
339 $haveModules[
$path] =
true;
344 for ( $m = $module; $m !==
null; $m = $m->getParent() ) {
345 $name = $m->getModuleName();
346 if ( $name ===
'main_int' ) {
351 !( !empty( $options[
'submodules'] ) && $m->getModuleManager() )
353 $link =
Html::element(
'b', [
'dir' =>
'ltr',
'lang' =>
'en' ], $name );
357 [
'href' => $link,
'class' =>
'apihelp-linktrail',
'dir' =>
'ltr',
'lang' =>
'en' ],
362 array_unshift( $links, $link );
365 $help[
'header'] .= self::wrap(
366 $context->
msg(
'parentheses' )
367 ->rawParams( $context->
getLanguage()->pipeList( $links ) ),
368 'apihelp-linktrail',
'div'
372 $flags = $module->getHelpFlags();
374 [
'class' => [
'apihelp-block',
'apihelp-flags' ] ] );
375 $msg = $context->
msg(
'api-help-flags' );
376 if ( !$msg->isDisabled() ) {
377 $help[
'flags'] .= self::wrap(
378 $msg->numParams( count( $flags ) ),
'apihelp-block-head',
'div'
382 foreach ( $flags as $flag ) {
390 self::wrap( $context->
msg(
"api-help-flag-$flag" ),
"apihelp-flag-$flag" )
393 $sourceInfo = $module->getModuleSourceInfo();
395 if ( isset( $sourceInfo[
'namemsg'] ) ) {
396 $extname = $context->
msg( $sourceInfo[
'namemsg'] )->text();
399 $extname =
Html::element(
'span', [
'dir' =>
'ltr',
'lang' =>
'en' ], $sourceInfo[
'name'] );
403 $context->
msg(
'api-help-source', $extname, $sourceInfo[
'name'] ),
409 if ( isset( $sourceInfo[
'license-name'] ) ) {
410 $msg = $context->
msg(
'api-help-license', $link,
411 Html::element(
'span', [
'dir' =>
'ltr',
'lang' =>
'en' ], $sourceInfo[
'license-name'] )
413 } elseif ( ExtensionInfo::getLicenseFileNames( dirname( $sourceInfo[
'path'] ) ) ) {
414 $msg = $context->
msg(
'api-help-license-noname', $link );
416 $msg = $context->
msg(
'api-help-license-unknown' );
419 self::wrap( $msg,
'apihelp-license' )
423 self::wrap( $context->
msg(
'api-help-source-unknown' ),
'apihelp-source' )
426 self::wrap( $context->
msg(
'api-help-license-unknown' ),
'apihelp-license' )
432 foreach ( $module->getFinalDescription() as $msg ) {
433 $msg->setContext( $context );
434 $help[
'description'] .= $msg->parseAsBlock();
437 $urls = $module->getHelpUrls();
439 if ( !is_array( $urls ) ) {
443 [
'class' => [
'apihelp-block',
'apihelp-help-urls' ] ]
445 $msg = $context->
msg(
'api-help-help-urls' );
446 if ( !$msg->isDisabled() ) {
447 $help[
'help-urls'] .= self::wrap(
448 $msg->numParams( count( $urls ) ),
'apihelp-block-head',
'div'
452 foreach ( $urls as $url ) {
454 Html::element(
'a', [
'href' => $url,
'dir' =>
'ltr' ], $url )
461 $params = $module->getFinalParams( ApiBase::GET_VALUES_FOR_HELP );
462 $dynamicParams = $module->dynamicParameterDocumentation();
464 if ( $params || $dynamicParams !==
null ) {
466 [
'class' => [
'apihelp-block',
'apihelp-parameters' ] ]
468 $msg = $context->
msg(
'api-help-parameters' );
469 if ( !$msg->isDisabled() ) {
470 $help[
'parameters'] .= self::wrap(
471 $msg->numParams( count( $params ) ),
'apihelp-block-head',
'div'
473 if ( !$module->isMain() ) {
475 $help[
'parameters'] .= self::wrap(
476 $context->
msg(
'api-help-parameters-note' ),
'apihelp-block-header',
'div'
482 $descriptions = $module->getFinalParamDescription();
484 foreach ( $params as $name => $settings ) {
485 $settings = $paramValidator->normalizeSettings( $settings );
487 if ( $settings[ParamValidator::PARAM_TYPE] ===
'submodule' ) {
492 Html::element(
'span', [
'dir' =>
'ltr',
'lang' =>
'en' ], $module->encodeParamName( $name ) )
497 if ( isset( $descriptions[$name] ) ) {
498 foreach ( $descriptions[$name] as $msg ) {
499 $msg->setContext( $context );
500 $description[] = $msg->parseAsBlock();
503 if ( !array_filter( $description ) ) {
504 $description = [ self::wrap(
505 $context->
msg(
'api-help-param-no-description' ),
511 if ( !empty( $settings[ParamValidator::PARAM_DEPRECATED] ) ) {
513 [
'class' =>
'info' ] );
514 $help[
'parameters'] .= self::wrap(
515 $context->
msg(
'api-help-param-deprecated' ),
516 'apihelp-deprecated',
'strong'
521 if ( $description ) {
522 $description = implode(
'', $description );
523 $description = preg_replace(
'!\s*</([oud]l)>\s*<\1>\s*!',
"\n", $description );
525 [
'class' =>
'description' ], $description );
530 $paramHelp = $paramValidator->getHelpInfo( $module, $name, $settings, [] );
532 unset( $paramHelp[ParamValidator::PARAM_DEPRECATED] );
534 if ( isset( $paramHelp[ParamValidator::PARAM_REQUIRED] ) ) {
535 $paramHelp[ParamValidator::PARAM_REQUIRED]->setContext( $context );
536 $info[] = $paramHelp[ParamValidator::PARAM_REQUIRED];
537 unset( $paramHelp[ParamValidator::PARAM_REQUIRED] );
541 if ( !empty( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
542 foreach ( $settings[ApiBase::PARAM_HELP_MSG_INFO] as $i ) {
543 $tag = array_shift( $i );
544 $info[] = $context->
msg(
"apihelp-{$path}-paraminfo-{$tag}" )
545 ->numParams( count( $i ) )
546 ->params( $context->
getLanguage()->commaList( $i ) )
547 ->params( $module->getModulePrefix() )
553 if ( !empty( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
555 $msg =
'api-help-param-templated-var-first';
556 foreach ( $settings[ApiBase::PARAM_TEMPLATE_VARS] as $k => $v ) {
557 $vars[] = $context->
msg( $msg, $k, $module->encodeParamName( $v ) );
558 $msg =
'api-help-param-templated-var';
560 $info[] = $context->
msg(
'api-help-param-templated' )
561 ->numParams( count( $vars ) )
567 foreach ( $paramHelp as $m ) {
568 $m->setContext( $context );
572 foreach ( $info as $i ) {
577 if ( $dynamicParams !==
null ) {
579 $module->getModulePrefix(),
580 $module->getModuleName(),
581 $module->getModulePath()
585 [
'class' =>
'description' ], $dynamicParams->parse() );
592 $examples = $module->getExamplesMessages();
595 [
'class' => [
'apihelp-block',
'apihelp-examples' ] ] );
596 $msg = $context->
msg(
'api-help-examples' );
597 if ( !$msg->isDisabled() ) {
598 $help[
'examples'] .= self::wrap(
599 $msg->numParams( count( $examples ) ),
'apihelp-block-head',
'div'
604 foreach ( $examples as $qs => $msg ) {
606 $module->getModulePrefix(),
607 $module->getModuleName(),
608 $module->getModulePath()
615 Html::element(
'a', [
619 ],
"api.php?$qs" ) .
' ' .
620 Html::rawElement(
'a', [
'href' => $sandbox ],
621 $context->
msg(
'api-help-open-in-apisandbox' )->parse() )
628 $subtocnumber = $tocnumber;
629 $subtocnumber[$level + 1] = 0;
631 'submodules' => $options[
'recursivesubmodules'],
632 'headerlevel' => $level + 1,
633 'tocnumber' => &$subtocnumber,
638 if ( $options[
'submodules'] && $module->getModuleManager() ) {
639 $manager = $module->getModuleManager();
641 foreach ( $groups as $group ) {
642 $names = $manager->getNames( $group );
644 foreach ( $names as $name ) {
645 $submodules[] = $manager->getModule( $name );
648 $help[
'submodules'] .= self::getHelpInternal(
656 $module->modifyHelp(
$help, $suboptions, $haveModules );
658 $module->getHookRunner()->onAPIHelpModifyOutput( $module,
$help,
659 $suboptions, $haveModules );
661 $out .= implode(
"\n",
$help );
677 if ( $params[
'wrap'] ) {
682 $errorPrinter = $main->createPrinterByName( $main->getParameter(
'format' ) );
689 ParamValidator::PARAM_DEFAULT =>
'main',
690 ParamValidator::PARAM_ISMULTI =>
true,
692 'submodules' =>
false,
693 'recursivesubmodules' =>
false,
702 =>
'apihelp-help-example-main',
703 'action=help&modules=query&submodules=1'
704 =>
'apihelp-help-example-submodules',
705 'action=help&recursivesubmodules=1'
706 =>
'apihelp-help-example-recursive',
707 'action=help&modules=help'
708 =>
'apihelp-help-example-help',
709 'action=help&modules=query+info|query+categorymembers'
710 =>
'apihelp-help-example-query',
716 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Main_page',
717 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:FAQ',
718 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Quick_start_guide',
This is the main API class, used for both external and internal processing.
An IContextSource implementation which will inherit context from another source but allow individual ...
This is one of the Core classes and should be read at least once by any new developers.
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,...
Interface for objects which can provide a MediaWiki context on request.