PoshCode Logo PowerShell Code Repository

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

Retrieve Online Cmdlet Help

I’m now using New-WebServiceProxy to take advantage of the well-formed XML provided by the MSDN ContentService

  1. #requires -version 2.0
  2. ## An update using New-WebServiceProxy to the MSDN ContentService instead of HttpRest
  3. ## See: http: //services.msdn.microsoft.com/ContentServices/ContentService.asmx
  4.  
  5. ## YOU MUST SAVE THIS FILE AS Get-OnlineHelp.ps1 in your path, and call it as Get-OnlineHelp
  6. ## __OR__ dot-source it -- DO NOT run it twice!
  7.  
  8. ## This is a VERY EARLY prototype of a function that could retrieve cmdlet help from TechNet ...
  9. ## and hypothetically, other online help sites which used the same format (none)
  10.  
  11. ## TODO:
  12. ## Properly format <pre> in the console
  13. ## Properly format the tables at the end of parameters
  14.  
  15. ## Version History
  16. ## 0.9 - 2010-07-27 - Added the rest of the parameters, fixed output, support -FULL, etc. THIS VERSION
  17. ## 0.3 - 2010-03-25 - Fixed a buggy type check which failed on first run.
  18. ## 0.2 - 2010-03-24 - Switched to using the ContentService Web Service.  PoshCode.org/1723
  19. ## 0.1 - 2010-03-23 - Initial release depended on HttpRest.  PoshCode.org/1719
  20.  
  21. [CmdletBinding(DefaultParameterSetName="Default")]
  22. param(
  23.    [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
  24.    [System.String]
  25.    ${Name},
  26.  
  27.    [System.String]
  28.    ${Path},
  29.  
  30.    [System.String[]]
  31.    ${Category},
  32.  
  33.    [System.String[]]
  34.    ${Component},
  35.  
  36.    [System.String[]]
  37.    ${Functionality},
  38.  
  39.    [System.String[]]
  40.    ${Role},
  41.  
  42.    [Parameter(Mandatory=$true,ParameterSetName='Detailed')]
  43.    [Switch]
  44.    ${Detailed},
  45.  
  46.    [Parameter(Mandatory=$true,ParameterSetName='Full')]
  47.    [Switch]
  48.    ${Full},
  49.  
  50.    [Parameter(Mandatory=$true,ParameterSetName='Examples')]
  51.    [Switch]
  52.    ${Examples},
  53.  
  54.    [Parameter(Mandatory=$true,ParameterSetName='Parameters')]
  55.    [System.String]
  56.    ${Parameter},
  57.  
  58.    [Switch]
  59.    ${Online}
  60. )
  61.  
  62.  
  63. # http://poshcode.org/1718
  64. function Select-Expand {
  65. <#
  66. .Synopsis
  67.    Like Select-Object -Expand, but with recursive iteration of a select chain
  68. .Description
  69.    Takes a dot-separated series of properties to expand, and recursively iterates the output of each property ...
  70. .Parameter Property
  71.    A collection of property names to expand.
  72.    
  73.    Each property can be a dot-separated series of properties, and each one is expanded, iterated, and then evaluated against the next
  74. .Parameter InputObject
  75.    The input to be selected from
  76. .Parameter Unique
  77.    If set, this becomes a pipeline hold-up, and the total output is checked to ensure uniqueness
  78. .Parameter EmptyToo
  79.    If set, Select-Expand will include empty/null values in it's output
  80. .Example
  81.    Get-Help Get-Command | Select-Expand relatedLinks.navigationLink.uri -Unique
  82.  
  83.    This will return the online-help link for Get-Command.  It's the equivalent of running the following command:
  84.  
  85.    C:\PS> Get-Help Get-Command | Select-Object -Expand relatedLinks | Select-Object -Expand navigationLink | Select-Object -Expand uri | Where-Object {$_} | Select-Object -Unique
  86. #>
  87. param(
  88.    [Parameter(ValueFromPipeline=$false,Position=0)]
  89.    [string[]]$Property
  90. ,
  91.    [Parameter(ValueFromPipeline=$true)]
  92.    [Alias("IO")]
  93.    [PSObject[]]$InputObject
  94. ,
  95.    [Switch]$Unique
  96. ,
  97.    [Switch]$EmptyToo
  98. )
  99. begin {
  100.    if($unique) {
  101.       $output = @()
  102.    }
  103. }
  104. process {
  105.    foreach($io in $InputObject) {
  106.       foreach($prop in $Property -split "\.") {
  107.          if($io -ne $null) {
  108.             $io = $io | Select-Object -Expand $prop
  109.             Write-Verbose $($io | out-string)
  110.          }
  111.       }
  112.       if(!$EmptyToo -and ($io -ne $null)) {
  113.          $io = $io | Where-Object {$_}
  114.       }
  115.       if($unique) {
  116.          $output += @($io)
  117.       }
  118.       else {
  119.          Write-Output $io
  120.       }
  121.    }
  122. }
  123. end {
  124.    if($unique) {
  125.       Write-Output $output | Select-Object -Unique
  126.    }
  127. }
  128. }
  129. # New-Alias slep Select-Expand
  130.  
  131. # http://poshcode.org/1722
  132. function Get-HttpResponseUri {
  133. #.Synopsis
  134. #   Fetch the HEAD for a url and return the ResponseUri.
  135. #.Description
  136. #   Does a HEAD request for a URL, and returns the ResponseUri. This is useful for resolving (in a service-independent way) shortened urls.
  137. #.Parameter ShortUrl
  138. #   A (possibly) shortened URL to be resolved to its redirect location.
  139. [CmdletBinding()]
  140. param(
  141.    [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
  142.    [Alias("Uri","Url")]
  143.    [string]$ShortUrl
  144. )
  145. process {
  146.    $req = [System.Net.HttpWebRequest]::Create($ShortUrl)
  147.    $req.Method = "HEAD"
  148.    $response = $req.GetResponse()
  149.    Write-Output $response.ResponseUri
  150.    $response.Close() # clean up like a good boy
  151. }
  152. }
  153. # New-Alias Resolve-ShortUri Select-Expand
  154.  
  155. if( -not("Mtps.ContentService" -as [type] -and $global:MtpsWebServiceProxy -as "Mtps.ContentService")) {
  156. $global:MtpsWebServiceProxy = New-WebServiceProxy "http://services.msdn.microsoft.com/ContentServices/ContentService.asmx?wsdl" -Namespace Mtps
  157. }
  158.  
  159. function global:Get-OnlineHelp {
  160. [CmdletBinding(DefaultParameterSetName="Default")]
  161. param(
  162.    [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
  163.    [System.String]
  164.    ${Name},
  165.  
  166.    [System.String]
  167.    ${Path},
  168.  
  169.    [System.String[]]
  170.    ${Category},
  171.  
  172.    [System.String[]]
  173.    ${Component},
  174.  
  175.    [System.String[]]
  176.    ${Functionality},
  177.  
  178.    [System.String[]]
  179.    ${Role},
  180.  
  181.    [Parameter(Mandatory=$true,ParameterSetName='Detailed')]
  182.    [Switch]
  183.    ${Detailed},
  184.  
  185.    [Parameter(Mandatory=$true,ParameterSetName='Full')]
  186.    [Switch]
  187.    ${Full},
  188.  
  189.    [Parameter(Mandatory=$true,ParameterSetName='Examples')]
  190.    [Switch]
  191.    ${Examples},
  192.  
  193.    [Parameter(Mandatory=$true,ParameterSetName='Parameters')]
  194.    [System.String]
  195.    ${Parameter},
  196.  
  197.    [Switch]
  198.    ${Online}
  199. )
  200. begin {
  201.    $HelpValues = $(
  202.       switch($PsCmdlet.ParameterSetName) {
  203.          "Default"   {"name", "synopsis", "syntax", "description", "related links", "remarks"}
  204.          "Full"      {"name", "synopsis", "syntax", "description", "parameters", "inputs and outputs", "examples", "notes", "see also"}
  205.          "Detailed"  {"name", "synopsis", "syntax", "description", "parameters", "examples", "remarks"}
  206.          "Examples"  {"name", "synopsis", "examples"}
  207.          "Parameter" {"name", "synopsis", "parameters"}
  208.       }
  209.    )
  210. }
  211. process {
  212. ### Get the Help ID
  213.    $uri = Get-Help @PSBoundParameters | Select-Expand relatedLinks.navigationLink.uri -Unique | Get-HttpResponseUri
  214.    if(!$uri) { throw "Couldn't find online help URL for $Name" }  
  215.    $id = [IO.Path]::GetFileNameWithoutExtension( $uri.segments[-1] )
  216.    write-verbose "Content Id: $id"
  217.  
  218. ### Get the Help Content
  219.    $content = $MtpsWebServiceProxy.GetContent( (New-Object 'Mtps.getContentRequest' -Property @{locale = $PSUICulture; contentIdentifier = $id; requestedDocuments = (New-Object Mtps.requestedDocument -Property @{Selector="Mtps.Failsafe"}) }) )
  220.    $global:OnlineHelpContent = $content.primaryDocuments |?{$_.primaryFormat -eq "Mtps.Failsafe"} | Select -Expand Any
  221.  
  222. ### NAME
  223.    $NameNode = $global:OnlineHelpContent.SelectSingleNode("//*[local-name()='div' and @class='topic']/*[local-name()='div' and @class='title']")
  224.    $NameNode.SetAttribute("header","NAME")
  225.  
  226. ### Ditch the navigation and stuff
  227.    $global:OnlineHelpContent = $global:OnlineHelpContent.SelectSingleNode("//*[local-name()='div' and @id='mainBody']")
  228.    
  229. ### SYNOPSIS
  230.    $Synopsis = $global:OnlineHelpContent.SelectSingleNode("*[local-name()='p']")
  231.    $Synopsis.SetAttribute("header","SYNOPSIS")
  232.  
  233.    $global:help = @{Name=$NameNode; Synopsis=$Synopsis}
  234.  
  235. ### EVERYTHING ELSE  
  236.    $headers = $OnlineHelpContent.h2  | ForEach-Object { $_.get_InnerText().Trim() }
  237.    $content = $OnlineHelpContent.div | ForEach-Object { $_ }
  238.  
  239.    if($headers.Count -ne $content.Count) {
  240.       Write-Warning "Unmatched content"
  241.       foreach($header in $headers) {
  242.         $help.$header = $OnlineHelpContent.SelectNodes( "//*[preceding-sibling::*[1][local-name()='h2' and normalize-space()='$header']]" )|
  243.                                     ForEach { $_.SetAttribute("header",$header.ToUpper()); $_ }
  244.       }
  245.    }
  246.    else {
  247.       for($h=0;$h -lt $headers.Count; $h++){
  248.          $help.($headers[$h]) = $content[$h]
  249.          $help.($headers[$h]).SetAttribute("header",$headers[$h].ToUpper())
  250.       }
  251.    }
  252.    
  253. ### PARAMETERS
  254.    #  $Global:Parameters = $global:OnlineHelpContent.RemoveChild( $global:OnlineHelpContent.SelectSingleNode("//*[local-name()='div' and @id='sectionSection2']") )
  255.    #  $Parameters.SetAttribute("header","PARAMETERS")
  256.    #  ## Create the parameters
  257.    #  $help.Parameters = $Parameters
  258.    $parameternames = $help.Parameters.h3  | ForEach-Object { $_.get_InnerText().Trim() }
  259.    foreach($header in $parameternames) {
  260.       foreach($node in $help.Parameters.SelectNodes( "//*[preceding-sibling::*[1][local-name()='h3' and normalize-space()='$header']]" ) ) {
  261.          $node.SetAttribute("header",$header)
  262.       }
  263.    }  
  264.  
  265. ### EXAMPLES
  266.    $help.Examples = $Help.Notes.Clone()
  267.    $help.Examples.SetAttribute("header","EXAMPLES")
  268.    $help.Examples.set_InnerXml('')
  269.    ForEach($key in $help.Keys -match "Example \d+" | sort { [int]($_ -replace "Example ") }) {
  270.       $help[$key].Header = $help[$key].Header -creplace "EXAMPLE","Example"
  271.       $null = $help.Examples.AppendChild( $help[$key] )
  272.       $null = $help.Remove($key)
  273.    }
  274.  
  275.    foreach($section in $help[$HelpValues]) {
  276.       if($section -isnot [PSObject]) { $section = New-Object PSObject $section }
  277.       if($Section.div -and @($Section.div)[0].header) {
  278.          $header = New-Object PSObject -Property @{header=$section.Header}
  279.          $header.PSTypeNames.Insert(0,"PoshCode.HtmlCommandHelpInfo+Section")
  280.          $header
  281.          
  282.          foreach($subsection in $Section.div) {
  283.             $null = $subsection.PSTypeNames.Insert(0,"PoshCode.HtmlCommandHelpInfo+Section")
  284.             $subsection
  285.          }
  286.       } else {
  287.          $null = $section.PSTypeNames.Insert(0,"PoshCode.HtmlCommandHelpInfo+Section")
  288.          $section
  289.       }
  290.    }
  291.    
  292. }
  293. }
  294.  
  295. Get-OnlineHelp @PSBoundParameters
  296.  
  297. # Since you can't update format data without a file that has a ps1xml ending, let's make one up...
  298. $tempFile = "$([IO.Path]::GetTempFileName()).ps1xml"
  299. Set-Content $tempFile @'
  300. <?xml version="1.0" encoding="utf-8" ?>
  301. <Configuration>
  302.    <ViewDefinitions>
  303.        <View>
  304.            <Name>HtmlCommandHelpSection</Name>
  305.            <ViewSelectedBy>
  306.                <TypeName>PoshCode.HtmlCommandHelpInfo+Section</TypeName>
  307.            </ViewSelectedBy>
  308.            <CustomControl>
  309.               <CustomEntries>
  310.                   <CustomEntry>
  311.                       <CustomItem>
  312.                           <Frame>
  313.                               <LeftIndent>4</LeftIndent>
  314.                               <CustomItem>
  315.                                   <ExpressionBinding>
  316.                                       <ScriptBlock>
  317.                                          Write-Host $_.Header.Trim() -Fore Cyan -NoNewLine
  318.                                          return " "
  319.                                       </ScriptBlock>
  320.                                   </ExpressionBinding>
  321.                                   <NewLine/>
  322.                                   <ExpressionBinding>
  323.                                       <ScriptBlock>$_.get_InnerText().trim() -replace '^[\n\s]*\n|\n\s+$'</ScriptBlock>
  324.                                   </ExpressionBinding>
  325.                                   <NewLine/>
  326.                               </CustomItem>
  327.                           </Frame>
  328.                       </CustomItem>
  329.                   </CustomEntry>
  330.               </CustomEntries>
  331.            </CustomControl>
  332.        </View>
  333.        <!--View>
  334.            <Name>HtmlCommandHelpHeader</Name>
  335.            <ViewSelectedBy>
  336.                <TypeName>PoshCode.HtmlCommandHelpInfo+Header</TypeName>
  337.            </ViewSelectedBy>
  338.            <CustomControl>
  339.               <CustomEntries>
  340.                   <CustomEntry>
  341.                       <CustomItem>
  342.                           <ExpressionBinding>
  343.                              <ScriptBlock>Write-Host $_ -Fore Cyan; ""</ScriptBlock>
  344.                           </ExpressionBinding>
  345.                       </CustomItem>
  346.                   </CustomEntry>
  347.               </CustomEntries>
  348.            </CustomControl>
  349.        </View-->
  350.    </ViewDefinitions>
  351. </Configuration>
  352. '@
  353. Update-FormatData -Append $tempFile

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