From dc15ea106a6e1106d8ba27ef442033bf3604e248 Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Wed, 6 May 2015 13:37:41 -0400 Subject: [PATCH] ApiHelp: Make 'toc' parameter work Bug: T98378 Change-Id: I0219689f621e325fc22f0eab6e4c000e1b2fa06f --- docs/hooks.txt | 2 ++ includes/Linker.php | 5 +-- includes/api/ApiBase.php | 4 ++- includes/api/ApiHelp.php | 71 ++++++++++++++++++++++++++++++---------- includes/api/ApiMain.php | 37 ++++++++++++++++++--- 5 files changed, 94 insertions(+), 25 deletions(-) diff --git a/docs/hooks.txt b/docs/hooks.txt index 6e003632fd..99a9d33a32 100644 --- a/docs/hooks.txt +++ b/docs/hooks.txt @@ -420,6 +420,8 @@ $module: ApiBase Module object $module: ApiBase Module object &$help: Array of HTML strings to be joined for the output. $options: Array Options passed to ApiHelp::getHelp +&$tocData: Array If a TOC is being generated, this array has keys as anchors in +the page and values as for Linker::generateTOC(). 'ApiMain::moduleManager': Called when ApiMain has finished initializing its module manager. Can be used to conditionally register API modules. diff --git a/includes/Linker.php b/includes/Linker.php index b58dabab9d..4a1aa872d8 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -1709,9 +1709,10 @@ class Linker { * Currently unused. * * @param array $tree Return value of ParserOutput::getSections() + * @param string|Language|bool $lang Language for the toc title, defaults to user language * @return string HTML fragment */ - public static function generateTOC( $tree ) { + public static function generateTOC( $tree, $lang = false ) { $toc = ''; $lastLevel = 0; foreach ( $tree as $section ) { @@ -1730,7 +1731,7 @@ class Linker { $lastLevel = $section['toclevel']; } $toc .= self::tocLineEnd(); - return self::tocList( $toc ); + return self::tocList( $toc, $lang ); } /** diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index 2a449dfa32..4870167fcd 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -2302,8 +2302,10 @@ abstract class ApiBase extends ContextSource { * * @param string[] &$help Array of help data * @param array $options Options passed to ApiHelp::getHelp + * @param array &$tocData If a TOC is being generated, this array has keys + * as anchors in the page and values as for Linker::generateTOC(). */ - public function modifyHelp( array &$help, array $options ) { + public function modifyHelp( array &$help, array $options, array &$tocData ) { } /**@}*/ diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php index 1e306163fb..c302ad1d04 100644 --- a/includes/api/ApiHelp.php +++ b/includes/api/ApiHelp.php @@ -82,6 +82,7 @@ class ApiHelp extends ApiBase { * - submodules: (bool) Include help for submodules of the current module * - recursivesubmodules: (bool) Include help for submodules recursively * - helptitle: (string) Title to link for additional modules' help. Should contain $1. + * - toc: (bool) Include a table of contents * * @param IContextSource $context * @param ApiBase[]|ApiBase $modules @@ -97,6 +98,9 @@ class ApiHelp extends ApiBase { $out = $context->getOutput(); $out->addModules( 'mediawiki.apihelp' ); + if ( !empty( $options['toc'] ) ) { + $out->addModules( 'mediawiki.toc' ); + } $out->setPageTitle( $context->msg( 'api-help-title' ) ); $cacheKey = null; @@ -107,6 +111,7 @@ class ApiHelp extends ApiBase { if ( $cacheHelpTimeout > 0 ) { // Get help text from cache if present $cacheKey = wfMemcKey( 'apihelp', $modules[0]->getModulePath(), + $options['toc'] ? 1 : 0, str_replace( ' ', '_', SpecialVersion::getVersion( 'nodb' ) ) ); $cached = $wgMemc->get( $cacheKey ); if ( $cached ) { @@ -133,7 +138,11 @@ class ApiHelp extends ApiBase { } $haveModules = array(); - $out->addHTML( self::getHelpInternal( $context, $modules, $options, $haveModules ) ); + $html = self::getHelpInternal( $context, $modules, $options, $haveModules ); + if ( !empty( $options['toc'] ) && $haveModules ) { + $out->addHTML( Linker::generateTOC( $haveModules, $context->getLanguage() ) ); + } + $out->addHTML( $html ); $helptitle = isset( $options['helptitle'] ) ? $options['helptitle'] : null; $html = self::fixHelpLinks( $out->getHTML(), $helptitle, $haveModules ); @@ -150,7 +159,7 @@ class ApiHelp extends ApiBase { * * @param string $html * @param string|null $helptitle Title to link to rather than api.php, must contain '$1' - * @param array $localModules Modules to link within the current page + * @param array $localModules Keys are modules to link within the current page, values are ignored * @return string */ public static function fixHelpLinks( $html, $helptitle = null, $localModules = array() ) { @@ -212,11 +221,16 @@ class ApiHelp extends ApiBase { ) { $out = ''; - $level = min( 6, empty( $options['headerlevel'] ) ? 2 : $options['headerlevel'] ); - $options['headerlevel'] = $level; + $level = empty( $options['headerlevel'] ) ? 2 : $options['headerlevel']; + if ( empty( $options['tocnumber'] ) ) { + $tocnumber = array( 2 => 0 ); + } else { + $tocnumber = &$options['tocnumber']; + } foreach ( $modules as $module ) { - $haveModules[$module->getModulePath()] = true; + $tocnumber[$level]++; + $path = $module->getModulePath(); $module->setContext( $context ); $help = array( 'header' => '', @@ -228,8 +242,13 @@ class ApiHelp extends ApiBase { 'submodules' => '', ); - if ( empty( $options['noheader'] ) ) { - $path = $module->getModulePath(); + if ( empty( $options['noheader'] ) || !empty( $options['toc'] ) ) { + $anchor = $path; + $i = 1; + while ( isset( $haveModules[$anchor] ) ) { + $anchor = $path . '|' . ++$i; + } + if ( $module->isMain() ) { $header = $context->msg( 'api-help-main-header' )->parse(); } else { @@ -241,10 +260,22 @@ class ApiHelp extends ApiBase { $context->msg( 'parentheses', $module->getModulePrefix() )->parse(); } } - $help['header'] .= Html::element( "h$level", - array( 'id' => $path, 'class' => 'apihelp-header' ), - $header + $haveModules[$anchor] = array( + 'toclevel' => count( $tocnumber ), + 'level' => $level, + 'anchor' => $anchor, + 'line' => $header, + 'number' => join( '.', $tocnumber ), + 'index' => false, ); + if ( empty( $options['noheader'] ) ) { + $help['header'] .= Html::element( 'h' . min( 6, $level ), + array( 'id' => $anchor, 'class' => 'apihelp-header' ), + $header + ); + } + } else { + $haveModules[$path] = true; } $links = array(); @@ -641,6 +672,15 @@ class ApiHelp extends ApiBase { $help['examples'] .= Html::closeElement( 'div' ); } + $subtocnumber = $tocnumber; + $subtocnumber[$level + 1] = 0; + $suboptions = array( + 'submodules' => $options['recursivesubmodules'], + 'headerlevel' => $level + 1, + 'tocnumber' => &$subtocnumber, + 'noheader' => false, + ) + $options; + if ( $options['submodules'] && $module->getModuleManager() ) { $manager = $module->getModuleManager(); $submodules = array(); @@ -651,16 +691,13 @@ class ApiHelp extends ApiBase { $submodules[] = $manager->getModule( $name ); } } - $help['submodules'] .= self::getHelpInternal( $context, $submodules, array( - 'submodules' => $options['recursivesubmodules'], - 'headerlevel' => $level + 1, - 'noheader' => false, - ) + $options, $haveModules ); + $help['submodules'] .= self::getHelpInternal( $context, $submodules, $suboptions, $haveModules ); + $numSubmodules = count( $submodules ); } - $module->modifyHelp( $help, $options ); + $module->modifyHelp( $help, $suboptions, $haveModules ); - Hooks::run( 'APIHelpModifyOutput', array( $module, &$help, $options ) ); + Hooks::run( 'APIHelpModifyOutput', array( $module, &$help, $suboptions, &$haveModules ) ); $out .= join( "\n", $help ); } diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index 2ec3aa8675..2b7937ee77 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -1307,7 +1307,7 @@ class ApiMain extends ApiBase { ); } - public function modifyHelp( array &$help, array $options ) { + public function modifyHelp( array &$help, array $options, array &$tocData ) { // Wish PHP had an "array_insert_before". Instead, we have to manually // reindex the array to get 'permissions' in the right place. $oldHelp = $help; @@ -1353,19 +1353,46 @@ class ApiMain extends ApiBase { // Fill 'datatypes' and 'credits', if applicable if ( empty( $options['nolead'] ) ) { - $help['datatypes'] .= Html::rawelement( 'h' . min( 6, $options['headerlevel'] + 1 ), + $level = $options['headerlevel']; + $tocnumber = &$options['tocnumber']; + + $header = $this->msg( 'api-help-datatypes-header' )->parse(); + $help['datatypes'] .= Html::rawelement( 'h' . min( 6, $level ), array( 'id' => 'main/datatypes', 'class' => 'apihelp-header' ), Html::element( 'span', array( 'id' => Sanitizer::escapeId( 'main/datatypes' ) ) ) . - $this->msg( 'api-help-datatypes-header' )->parse() + $header ); $help['datatypes'] .= $this->msg( 'api-help-datatypes' )->parseAsBlock(); + if ( !isset( $tocData['main/datatypes'] ) ) { + $tocnumber[$level]++; + $tocData['main/datatypes'] = array( + 'toclevel' => count( $tocnumber ), + 'level' => $level, + 'anchor' => 'main/datatypes', + 'line' => $header, + 'number' => join( '.', $tocnumber ), + 'index' => false, + ); + } - $help['credits'] .= Html::rawelement( 'h' . min( 6, $options['headerlevel'] + 1 ), + $header = $this->msg( 'api-credits-header' )->parse(); + $help['credits'] .= Html::rawelement( 'h' . min( 6, $level ), array( 'id' => 'main/credits', 'class' => 'apihelp-header' ), Html::element( 'span', array( 'id' => Sanitizer::escapeId( 'main/credits' ) ) ) . - $this->msg( 'api-credits-header' )->parse() + $header ); $help['credits'] .= $this->msg( 'api-credits' )->useDatabase( false )->parseAsBlock(); + if ( !isset( $tocData['main/credits'] ) ) { + $tocnumber[$level]++; + $tocData['main/credits'] = array( + 'toclevel' => count( $tocnumber ), + 'level' => $level, + 'anchor' => 'main/credits', + 'line' => $header, + 'number' => join( '.', $tocnumber ), + 'index' => false, + ); + } } } -- 2.20.1