Public/Convert-MAMLToHTML.ps1
|
Function Convert-MAMLToHTML { <# .DESCRIPTION Will convert MAML text or file to HTML. .EXAMPLE # Convert knowledge article and write to .html file #Example PS C:\> $monitor = Get-SCOMMonitor -DisplayName 'M365 Teams - Chat Synthetic Test Performance Monitor' PS C:\> $Article = $monitor.GetKnowledgeArticle(([System.Globalization.CultureInfo]'en-US')) PS C:\> Convert-MAMLToHTML -XML $Article.MamlContent | Set-Content C:\temp\KnowledgeArticle.html .NOTES Author: Tyson Paul (https://monitoringguys.com/) References: https://devio.wordpress.com/2009/09/15/command-line-xslt-processor-with-powershell/ https://systemcenter.wiki/ Version History: 2022.06.20 - v1 #> [CmdletBinding(DefaultParameterSetName='Parameter Set 1', PositionalBinding=$false, HelpUri = 'https://monitoringguys.com/')] Param ( # Path to XMLFile [Parameter(Mandatory=$true, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=0, ParameterSetName='XMLFileExists')] [ValidateNotNullOrEmpty()] [string]$XMLFile, # XML data (raw) [Parameter(Mandatory=$true, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=0, ParameterSetName='XMLData')] [ValidateNotNullOrEmpty()] [string]$XML, # Path to XSL transform file. [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=1)] [string]$XSLFile ) ################# FUNCTIONS ################# Function Test-XMLFile { <# .SYNOPSIS Test the validity of an XML file .NOTES https://stackoverflow.com/questions/14423861/how-to-validate-xml-for-correct-syntax-format #> [CmdletBinding()] param ( [parameter(mandatory=$true)][ValidateNotNullorEmpty()][string]$xmlFilePath ) # Check the file exists if (!(Test-Path -Path $xmlFilePath)){ throw "$xmlFilePath is not valid. Please provide a valid path to the .xml fileh" } # Check for Load or Parse errors when loading the XML file $xml = New-Object System.Xml.XmlDocument try { $xml.Load((Get-ChildItem -Path $xmlFilePath).FullName) return $true } catch [System.Xml.XmlException] { Write-Verbose "$xmlFilePath : $($_.toString())" return $false } } ############################################## Function Write-DefaultTransformFile { # https://systemcenter.wiki/ $XSLData = @" <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:maml="http://schemas.microsoft.com/maml/2004/10" exclude-result-prefixes="msxsl maml"> <xsl:output method="html" indent="no" encoding="utf-8" /> <xsl:template match="/"> <xsl:apply-templates /> </xsl:template> <xsl:template match="maml:section"> <xsl:apply-templates /> </xsl:template> <xsl:template match="maml:lineBreak"> <br /> </xsl:template> <xsl:template match="maml:navigationLink"> <a> <xsl:attribute name="href"> <xsl:value-of select="maml:uri/@href"/> </xsl:attribute> <xsl:choose> <xsl:when test="maml:uri/@condition!=''"> <xsl:attribute name="condition"> <xsl:value-of select="maml:uri/@condition" /> </xsl:attribute> </xsl:when> </xsl:choose> <xsl:choose> <xsl:when test="maml:uri/@uri!=''"> <xsl:attribute name="uri"> <xsl:value-of select="maml:uri/@uri" /> </xsl:attribute> </xsl:when> </xsl:choose> <xsl:value-of select="maml:linkText"/> </a> </xsl:template> <xsl:template match="maml:list"> <ul> <xsl:apply-templates /> </ul> </xsl:template> <xsl:template match="maml:listItem"> <li> <xsl:apply-templates /> </li> </xsl:template> <xsl:template match="maml:title"> <h1> <xsl:value-of select="."/> </h1> </xsl:template> <xsl:template match="maml:subTitle"> <h2> <xsl:value-of select="." /> </h2> </xsl:template> <xsl:template match="text()"> <xsl:value-of select="."/> </xsl:template> <xsl:template match="maml:example"> <pre> <xsl:apply-templates /> </pre> </xsl:template> <xsl:template match="maml:codeInline"> <code> <xsl:apply-templates /> </code> </xsl:template> <xsl:template match="maml:computerOutputInline"> <pre> <xsl:apply-templates /> </pre> </xsl:template> <xsl:template match="maml:procedure"> <ol> <xsl:apply-templates /> </ol> </xsl:template> <xsl:template match="maml:step"> <li> <xsl:apply-templates /> </li> </xsl:template> <xsl:template match="maml:para"> <p> <xsl:apply-templates /> </p> </xsl:template> <xsl:template match="maml:ui"> <b> <xsl:apply-templates /> </b> </xsl:template> <xsl:template match="maml:entry"> <td> <xsl:apply-templates /> </td> </xsl:template> <xsl:template match="maml:headerEntry"> <th> <xsl:apply-templates /> </th> </xsl:template> <xsl:template match="maml:tableHeader"> <thead> <xsl:apply-templates /> </thead> </xsl:template> <xsl:template match="maml:row"> <tr> <xsl:apply-templates /> </tr> </xsl:template> <xsl:template match="maml:table"> <table> <xsl:apply-templates /> </table> </xsl:template> </xsl:stylesheet> "@ $XSLFile = Join-Path $TempDir 'maml2html.xsl' $XSLData | Set-Content -Path $XSLFile -Force -Encoding UTF8 } ############## END FUNCTIONS ################# ############################################## if ((-NOT $XMLFile) -AND (-NOT $XML)) { Get-Help Convert-MAMLToHtml -Examples Return } # Setup temp dir for output file. Using a redirector to an output file is the only way I could find to obtain the property bag data. $TempDir = (Join-Path (Join-Path $env:Windir "Temp") (Join-Path 'SCOMHelper' 'Export-SCOMKnowledge') ) New-Item -Type Directory -Path $TempDir -ErrorAction SilentlyContinue | Out-Null If (-NOT(Test-Path -Path $TempDir -PathType Container)) { Write-Error "Unable to create/access directory: [$($TempDir)]. " Return $false } If ($XSLFile.Length) { If( -NOT (Test-Path $XSLFile)) { Throw "XSL input file not found: $($_)" Exit } } Else { #dot-sourced . Write-DefaultTransformFile } $tmpTransformFile = New-TemporaryFile If ($XML) { $XMLFile = (New-TemporaryFile).FullName $XML | Set-Content -Path $XMLFile -Force -Encoding UTF8 } If (-NOT (Test-XMLFile -xmlFilePath $XMLFile) ){ $tmpXMLFile = (New-TemporaryFile).FullName # Make sure XML has a root "<Root>$(Get-Content -Path $XMLFile)</Root>" | Set-Content -Path $tmpXMLFile -Force -Encoding UTF8 If (-NOT (Test-XMLFile -xmlFilePath $tmpXMLFile) ){ # Make sure XML has a root Throw "Error loading XML data from path: $XMLFile" Return } # Set path to new tmp file which contains root element. $XMLFile = $tmpXMLFile } $xslt = New-Object System.Xml.Xsl.XslCompiledTransform; Try { $xslt.Load($XSLFile); } Catch { Write-Warning "Failed to load transform file [$($XSLFile)]. Will use default 'maml2html' transform instead." . Write-DefaultTransformFile Try { $xslt.Load($XSLFile); } Catch { Write-Error "Fatal Error: Failed to load default transform file [$($XSLFile)]. " Return } } $xslt.Transform($XMLFile, $tmpTransformFile.FullName); Return (Get-Content -Path $tmpTransformFile.FullName); } |