PoshCode Logo PowerShell Code Repository

Get-HtmlHelp 3.2 by Joel Bennett 30 months ago (modification of post by Joel Bennett view diff)
diff | embed code: <script type="text/javascript" src="http://PoshCode.org/embed/3197"></script>download | new post

A script to convert MAML help to HTML.

Now with improved rendering and help for Get-HtmlHelp

  1. ## Get-HtmlHelp - by Joel Bennett
  2. ## version 3.2
  3. #####################################################################
  4. ## Cool Example, using ShowUI:
  5. ##    Import-Module HtmlHelp
  6. ##    Import-Module ShowUI -Vers 1.4
  7. ##    function Show-Help { [CmdletBinding()]param([String]$Name)  
  8. ##       Window { WebBrowser -Name wb } -On_Loaded {
  9. ##          $wb.NavigateToString((Get-HtmlHelp $Name))
  10. ##          $this.Title = "Get-Help $Name"
  11. ##       } -Show
  12. ##    }
  13. ##    Show-Help Get-Help
  14. ##
  15. #####################################################################
  16. ## Import System.Web in order to use HtmlEncode functionality
  17. Add-Type -Assembly System.Web
  18.  
  19. function ConvertTo-Dictionary([hashtable]$ht) {
  20.    $kvd = new-object "System.Collections.Generic.Dictionary``2[[System.String],[System.String]]"
  21.    foreach($kv in $ht.GetEnumerator()) { $kvd.Add($kv.Key,$kv.Value) }
  22.    return $kvd
  23. }
  24.  
  25.  
  26. function htmlEncode {
  27.    param([Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)][String]$Text)
  28.    process{ [System.Web.HttpUtility]::HtmlEncode($Text) }
  29. }
  30.  
  31. function trim {
  32.    param([Parameter(ValueFromPipeline=$true,Mandatory=$true)][String]$string)
  33.    process{ $string.Trim() }
  34. }
  35.  
  36. function trimUrl{
  37.    param([Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)][String]$Text)
  38.    process{ [System.Web.HttpUtility]::UrlEncode($Text).Trim() }
  39. }
  40.  
  41. function trimHtml{
  42.    param([Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)][String]$Text)
  43.    process{ [System.Web.HttpUtility]::HtmlEncode($Text).Trim() }
  44. }
  45.  
  46. function HHSplit {
  47.    param(
  48.       $Separator="\s*\r?\n",
  49.       [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
  50.       [String]$inputObject
  51.    )
  52.    process{
  53.       foreach($item in [regex]::Split($inputObject,$Separator)) {
  54.          $item.Trim() | Where {$_.Length}
  55.       }
  56.    }
  57. }
  58.  
  59. function HHjoin {
  60.    param(
  61.       [Parameter(Position=0)]
  62.       $Separator=$ofs,
  63.      
  64.       [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
  65.       [String[]]
  66.       $inputObject
  67.    )
  68.    begin   { [string[]]$array = $inputObject }
  69.    process { $array += $inputObject }
  70.    end     {
  71.       if($array.Length -gt 1) {
  72.          [string]::Join($Separator,$array)
  73.       } else {
  74.          $array
  75.       }
  76.    }
  77. }
  78. function Out-HtmlTag {
  79.    param([Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)][String]$Text, $Tag="p")
  80.    process{
  81.       $html = $Text | out-string -width ([int]::MaxValue) | HHSplit | trimHtml | HHjoin "</$tag>`n<$tag>"
  82.       $html = $html -replace '(\S+)(http://.*?)([\s\p{P}](?:\s|$))','<a href="$2">$1</a>$3'
  83.       "<$tag>$html</$tag>"
  84.    }
  85. }
  86. function Out-HtmlList {
  87.    param([Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)][String]$Text)
  88.    process{
  89.       "<li>$($Text | out-string -width ([int]::MaxValue) | HHSplit | trimHtml | HHjoin "</li>`n<li>")</li>"
  90.    }
  91. }
  92. function Out-HtmlDefList {
  93.    param(
  94.       [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)]$Node,
  95.       [String[]]$Term,
  96.       [String[]]$Definition
  97.    )
  98.    # begin { "<dl>"}
  99.    process{
  100.       $tx = $Node
  101.       foreach($t in $Term) { $tx = $tx.$t; Write-Verbose "${t}: $tx" }
  102.       $dx = $Node
  103.       foreach($d in $Definition) { $dx = $dx.$d; Write-Verbose "${t}: $dx"  }
  104.      
  105.       if($tx) { $tx = $tx | trimHtml } else { return }
  106.       if($dx) { $dx = $dx | trimHtml } else { $dx = "" }
  107.       "<dt>{0}</dt><dd>{1}</dd>" -f $tx, $dx
  108.    }
  109.    # end { "</dl>"}
  110. }
  111. function Out-HtmlBr {
  112.    param([Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)][String]$Text)
  113.    process{
  114.       $Text | out-string -width ([int]::MaxValue) | HHSplit | trimHtml | HHjoin "<br />"
  115.    }
  116. }
  117.  
  118. ## Utility Functions
  119. function Get-UtcTime {
  120.    Param($Format="")
  121.    [DateTime]::Now.ToUniversalTime().ToString($Format)
  122. }
  123.  
  124. function Encode-Twice {
  125.    param([Parameter(ValueFromPipeline=$true,Mandatory=$true)][String]$string)
  126.    process {
  127.       [System.Web.HttpUtility]::UrlEncode( [System.Web.HttpUtility]::UrlEncode( $string ) )
  128.    }
  129. }
  130.  
  131.  
  132. ## Get-HtmlHelp - A Helper function for generating help:
  133. ## Usage:  Get-HtmlHelp Get-*
  134. function Get-HtmlHelp {
  135. #.Synopsis
  136. #  Generates HTML for help
  137. #.Description
  138. #  Takes the output of Get-Help for cmdlets for functions and outputs in HTML form with some basic styling.
  139. #
  140. #.Example
  141. #  C:\PS>Get-HtmlHelp Get-HtmlHelp > Get-HtmlHelp.html
  142. #  Generates this help into the specified html file.
  143. #.Example
  144. #  C:\PS>Get-HtmlHelp *-ScheduledJob -OutputFolder .
  145. #  Generates HTML help for all of the *-ScheduledJob commands and saves them as .html files in the current folder.
  146. #.Example
  147. #  C:\PS>Get-Command -Module HtmlHelp | Get-HtmlHelp | ForEach { Set-Content $_.Name $_ }
  148. #  Generates HTML help into files taking advantage of the "Name" property on the output to determine the file name.
  149. #.Example
  150. #  C:\PS>Import-Module HtmlHelp, ShowUI
  151. #  C:\PS>function Show-Help { [CmdletBinding()]param([String]$Name)  
  152. #  >> Window { WebBrowser -Name wb } -On_Loaded {
  153. #  >>    $wb.NavigateToString((Get-HtmlHelp $Name))
  154. #  >>    $this.Title = "Get-Help $Name"
  155. #  >>   } -Show
  156. #  >> }
  157. #  C:\PS>Show-Help Get-Help
  158. #
  159. #  Creates a function Show-Help to show the HTML rendered help in ShowUI without saving it to file.
  160. [CmdletBinding()]
  161. param(
  162.    # The command(s) you want to generate help for.
  163.    [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)]
  164.    [String[]]$Name,
  165.    # The base Url for links to related commands.
  166.    [string]$BaseUrl = "http://technet.microsoft.com/en-us/library/",
  167.    # If set, the output goes to files in the folder.
  168.    [String]$OutputFolder
  169. )
  170. process {
  171.    foreach($help in $Name | Get-Command -EA "SilentlyContinue" | Get-Help -Full) {
  172.       $output = $help | Convert-HelpToHtml $baseUrl |
  173.                         Add-Member -Name Name -Type NoteProperty -Value $(Split-Path $help.Name -Leaf) -Passthru
  174.       if($OutputFolder) {
  175.          Set-Content $(Join-Path $OutputFolder "$($output.Name).html") $output
  176.       } else {
  177.          $Output
  178.       }
  179.    }
  180. }
  181. }
  182.  
  183. function textile {
  184. param([string]$text)
  185.  
  186. $text -replace '(?<=\r\n\r\n|^)\*\s(.*)(?=\r\n)',"<ul>`r`n<li>`$1</li>"                     <# start of a list      #>`
  187.       -replace '(?<=\r\n)\*\s+((?:.|\r\n(?![\*\r]))+)(?=\r\n\r\n|\r\n\*|$)',"<li>`$1</li>"  <# middle of a list     #>`
  188.       -replace '</li>(?=\r\n\r\n|$)',"</li>`r`n</ul>"                                       <# end of the list      #>`
  189.       -replace '(?<=\r\n\r\n|^)([^\n]*)(?=\r\n\r\n|$)',"<p>`r`n`$1`r`n</p>"                 <# regular paragraphs   #>`
  190.       -replace '(?<=\r\n\r\n)([^\r\n]*\s+[^\r\n]*)\r\n(-+)(?=\r\n\r\n)',"<h3>`$1</h3>"      <# headers              #>`
  191.       -replace '(?<=[^\r\n>])(\r\n)(?=[^\r\n]+)',"<br />`$1"                                <# remaining linebreaks #>`
  192.       -replace "  "," &nbsp;"  # Because the content is originally for get-help, preserve whitespace
  193. }
  194.  
  195. function Convert-ParameterHelp {
  196. param([Parameter(ValueFromPipeline=$true,Mandatory=$true)]$ParameterItem)
  197. process {
  198.    $name = $(
  199.       if($ParameterItem.position -ne "named") {
  200.          "[-<strong>{0}</strong>]" -f $ParameterItem.name
  201.       } else {
  202.          "-<strong>{0}</strong>" -f $ParameterItem.name
  203.       }
  204.    )
  205.  
  206.    if($ParameterItem.required -eq "false") {
  207.       "<nobr>[{0} {1}]</nobr>" -f $name, $ParameterItem.parameterValue
  208.    } else {
  209.       "<nobr>{0} {1}</nobr>" -f $name, $ParameterItem.parameterValue
  210.    }
  211. }
  212. }
  213.  
  214. function Convert-SyntaxItem {
  215. param([Parameter(ValueFromPipeline=$true,Mandatory=$true)]$SyntaxItem)
  216. process {
  217.    "<li>{0} {1}</li>" -f $SyntaxItem.name, $($SyntaxItem.parameter | Convert-ParameterHelp | join " ")
  218. }
  219. }
  220.  
  221. function Convert-HelpToHtml {
  222. param(
  223. [string]$baseUrl,
  224.  
  225. [Parameter(ValueFromPipeline=$true)]
  226. $Help
  227. )
  228. PROCESS {
  229.       #  throw "Can only process -Full view help output"
  230.  
  231.       # Name isn't needed, since this is going as the body, but ...
  232.       # $data = "<html><head><title>$(trimHtml($help.Name))</title></head><body>";
  233.       # $data += "<h1>$(trimHtml($help.Name))</h1>"
  234. $data = @"
  235. <html>
  236. <head>
  237. <title>$(trimHtml($help.Name))</title>
  238. <style type="text/css">
  239.   h1, h2, h3, h4, h5, h6 { color: #369 }
  240.   ul.syntax {
  241.      list-style: none outside;
  242.      font-size: .9em;
  243.      font-family: Consolas, "DejaVu Sans Mono", "Lucida Console", monospace;
  244.   }
  245.   ul.syntax li {
  246.      margin: 3px 0px;
  247.   }
  248.   table.parameters th {
  249.      text-align: left;
  250.   }
  251. </style>
  252. </head>
  253. <body>
  254. <h1 id='$(trimHtml($help.Name))'>$(trimHtml($help.Name))</h1>
  255. "@
  256.       # Synopsis
  257.       $data += "`n<h2>Synopsis</h2>`n$($help.Synopsis | Out-HtmlTag -Tag p)"
  258.      
  259.       # Syntax
  260.       $data += "`n<h2 class='syntax'>Syntax</h2>`n<ul class='syntax'>$($help.Syntax.syntaxItem | Convert-SyntaxItem)</ul>"
  261.    
  262.       # Related Commands
  263.       if($help.relatedLinks.navigationLink) {
  264.          $data += "`n<h2>Related Commands</h2>`n"
  265.          foreach ($relatedLink in $help.relatedLinks.navigationLink) {
  266.             $uri = ""
  267.             if( $relatedLink.uri -ne "" ) {
  268.                $text = $uri = $relatedLink.uri
  269.                if($relatedLink.linkText) { $text = $relatedLink.linkText }
  270.             } elseif($relatedLink.linkText) {
  271.                $text = $relatedLink.linkText
  272.                $cmd = get-command $text -EA "SilentlyContinue"
  273.                if($cmd -and $cmd.PSSnapin) {
  274.                   $uri = "$baseUrl/$(trimUrl $cmd.PSSnapin.Name)/$(trimUrl $text)"
  275.                } elseif($cmd -and $cmd.Module) {
  276.                   $uri = "$baseUrl/$(trimUrl $cmd.Module.Name)/$(trimUrl $text)"
  277.                } else {
  278.                   $uri = "$baseUrl/$(trimUrl $text)"
  279.                }
  280.             }
  281.             $data += "<a href='$uri'>$(trimHtml $text)</a><br>"
  282.          }
  283.       }
  284.    
  285.       # Detailed Description
  286.       if($help.Description) {
  287.          $data += "`n<h2>Detailed Description</h2>`n$($help.Description | Out-HtmlTag -Tag p)"
  288.       }
  289.    
  290.       # Parameters
  291.       $data += "`n<h2>Parameters</h2>"
  292.       foreach($param in $help.parameters.parameter) {
  293.          $data += "`n<h4>-$(trimHtml($param.Name)) [&lt;$(trimHtml($param.type.name))&gt;]</h4>"
  294.          if($Param.Description) {
  295.             $data += $param.Description | Out-HtmlTag -Tag p
  296.          }
  297.          $data += "`n<table class='parameters'>"
  298.          $data += "`n<tr><th>Required? &nbsp;</th><td> $(trimHtml($param.Required))</td></tr>"
  299.          $data += "`n<tr><th>Position? &nbsp;</th><td> $(trimHtml($param.Position))</td></tr>"
  300.          if($param.DefaultValue) {
  301.             $data += "`n<tr><th>Default value? &nbsp;</th><td> $(trimHtml($param.defaultValue))</td></tr>"
  302.          }
  303.          $data += "`n<tr><th>Accept pipeline input? &nbsp;</th><td> $(trimHtml($param.pipelineInput))</td></tr>"
  304.          if($param.globbing) {
  305.             $data += "`n<tr><th>Accept wildcard characters? &nbsp;</th><td> $(trimHtml($param.globbing))</td></tr>"
  306.          }
  307.          $data += "</table>`n`n"
  308.       }
  309.    
  310.       # Input Type
  311.       if($help.inputTypes -and $help.inputTypes.inputType) {
  312.          $data += "`n<h3>Input Type</h3>`n<dl class='input'>$(
  313.            $help.inputTypes.inputType | Out-HtmlDefList -Term type, name -Definition description
  314.         )</dl>"
  315.       }
  316.      
  317.       # Output Type
  318.       if($help.returnValues -and $help.returnValues.returnValue ) {
  319.          $data += "`n<h3>Output Type</h3>`n<dl class='output'>$(
  320.            $help.returnValues.returnValue | Out-HtmlDefList -Term type, name -Definition description
  321.         )</dl>"
  322.       }
  323.      
  324.       # Notes
  325.       if($help.alertSet -and $help.alertSet.alert) {
  326.          $data += "`n<h2>Notes</h2>`n$($help.alertSet.alert | Out-HtmlTag -Tag p)"
  327.       }
  328.    
  329.       # Examples
  330.       if($help.Examples.example -and $help.Examples.example.Length) {
  331.          $data += "<h2>Examples</h2>"
  332.          
  333.          foreach($example in $help.Examples.example){
  334.             if($example.title) {
  335.                $data += "<h4>$(trimHtml($example.title.trim(' -')))</h4>`n"
  336.             }
  337.             $data += "<code><strong>C:\PS&gt;</strong>&nbsp;$(($example.code | Out-HtmlBr) -replace "<br />C:\\PS&gt;","<br />`n<strong>C:\PS&gt;</strong>&nbsp;")</code>"
  338.             $data += "<p>$($example.remarks | out-string -width ([int]::MaxValue) | Out-HtmlTag -Tag p)</p>"
  339.          }
  340.       }
  341.       $data += "`n</body></html>"
  342.  
  343.       write-output $data
  344. }}
  345.  
  346. Export-ModuleMember Get-HtmlHelp

Submit a correction or amendment below (
click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:


Remember me