PSHTMLTools.psm1

Function Set-AlternatingRow {
    <#
    .SYNOPSIS
        Simple function to alternate the row colors in an HTML table

    .DESCRIPTION
        This function accepts pipeline input from ConvertTo-HTML or any
        string with HTML in it. It will then search for <tr> and replace
        it with <tr class=(something)>. With the combination of CSS it
        can set alternating colors on table rows.
        
        CSS requirements:
        .odd { background-color:#ffffff; }
        .even { background-color:#dddddd; }
        
        Classnames can be anything and are configurable when executing the
        function. Colors can, of course, be set to your preference.
        
        This function does not add CSS to your report, so you must provide
        the style sheet, typically part of the ConvertTo-HTML cmdlet using
        the -Head parameter.

    .PARAMETER Line
        String containing the HTML line, typically piped in through the
        pipeline.

    .PARAMETER CSSEvenClass
        Define which CSS class is your "even" row and color.

    .PARAMETER CSSOddClass
        Define which CSS class is your "odd" row and color.
    .EXAMPLE $Report | ConvertTo-HTML -Head $Header | Set-AlternateRow -CSSEvenClass even -CSSOddClass odd | Out-File .\HTMLReport.html

    
        $Header can be defined with a here-string as:
        $Header = @"
        <style>
        TABLE {border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
        TH {border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color: #6495ED;}
        TD {border-width: 1px;padding: 3px;border-style: solid;border-color: black;}
        .odd { background-color:#ffffff; }
        .even { background-color:#dddddd; }
        </style>
        "@
        
        This will produce a table with alternating white and grey rows. Custom CSS
        is defined in the $Header string and included with the table thanks to the -Head
        parameter in ConvertTo-HTML.

    .EXAMPLE
        $RawHTML = $Report | ConvertTo-HTML -Head $Header
        $FinalHTML = Set-AlternateRow -CSSEvenClass even -CSSOddClass odd -InputObject $RawHTML
        $FinalHTML | Out-File .\HTMLReport.html

    .NOTES
        Author: Martin Pugh
        Twitter: @thesurlyadm1n
        Spiceworks: Martin9700
        Blog: www.thesurlyadmin.com
        
        Changelog:
            1.5 Added ability to specify HTML instead of piping it. Updated look of the script and added an additional
                        example to help.
            1.1 Modified replace to include the <td> tag, as it was changing the class
                        for the TH row as well.
            1.0 Initial function release
    .LINK
        http://community.spiceworks.com/scripts/show/1745-set-alternatingrows-function-modify-your-html-table-to-have-alternating-row-colors
    .LINK
        http://thesurlyadmin.com/2013/01/21/how-to-create-html-reports/
    #>

    [CmdletBinding()]
       Param(
           [Parameter(Mandatory,ValueFromPipeline)]
        [string[]]$InputObject,
       
           [Parameter(Mandatory)]
           [string]$CSSEvenClass,
       
        [Parameter(Mandatory)]
           [string]$CSSOddClass
       )
    Begin {
        $ClassName = $CSSEvenClass
    }
    Process {
        ForEach ($Line in $InputObject)
        {
            If ($Line.Contains("<tr><td>"))
            {    
                $Line = $Line.Replace("<tr>","<tr class=""$ClassName"">")
                If ($ClassName -eq $CSSEvenClass)
                {    
                    $ClassName = $CSSOddClass
                }
                Else
                {    
                    $ClassName = $CSSEvenClass
                }
            }
            Write-Output $Line
        }
    }
}

Function Set-CellColor
{   <#
    .SYNOPSIS
        Function that allows you to set individual cell colors in an HTML table
    .DESCRIPTION
        To be used inconjunction with ConvertTo-HTML this simple function allows you
        to set particular colors for cells in an HTML table. You provide the criteria
        the script uses to make the determination if a cell should be a particular
        color (property -gt 5, property -like "*Apple*", etc).
        
        You can add the function to your scripts, dot source it to load into your current
        PowerShell session or add it to your $Profile so it is always available.
        
        To dot source:
            .".\Set-CellColor.ps1"
              
    .PARAMETER Color
        Name or 6-digit hex value of the color you want the cell to be
    .PARAMETER InputObject
        HTML you want the script to process. This can be entered directly into the
        parameter or piped to the function.
    .PARAMETER Filter
        Specifies a query to determine if a cell should have its color changed. $true
        results will make the color change while $false result will return nothing.
        
        Syntax
        <Property Name> <Operator> <Value>
        
        <Property Name>::= the same as $Property. This must match exactly
        <Operator>::= "-eq" | "-le" | "-ge" | "-ne" | "-lt" | "-gt"| "-approx" | "-like" | "-notlike"
            <JoinOperator> ::= "-and" | "-or"
            <NotOperator> ::= "-not"
        
        The script first attempts to convert the cell to a number, and if it fails it will
        cast it as a string. So 40 will be a number and you can use -lt, -gt, etc. But 40%
        would be cast as a string so you could only use -eq, -ne, -like, etc.
    .PARAMETER Row
        Instructs the script to change the entire row to the specified color instead of the individual cell.
    .INPUTS
        HTML with table
    .OUTPUTS
        HTML
    .EXAMPLE
        get-process | convertto-html | set-cellcolor -Propety cpu -Color red -Filter "cpu -gt 1000" | out-file c:\test\get-process.html

        Assuming Set-CellColor has been dot sourced, run Get-Process and convert to HTML.
        Then change the CPU cell to red only if the CPU field is greater than 1000.
        
    .EXAMPLE
        get-process | convertto-html | set-cellcolor -color red -filter "cpu -gt 1000 -and cpu -lt 2000" | out-file c:\test\get-process.html
        
        Same as Example 1, but now we will only turn a cell red if CPU is greater than 1000
        but less than 2000.
        
    .EXAMPLE
        $HTML = $Data | sort server | ConvertTo-html -head $header | Set-CellColor -Color red -Filter "cookedvalue -gt 1"
        PS C:\> $HTML = $HTML | Set-CellColor Server green -Filter "server -eq 'dc2'"
        PS C:\> $HTML | Set-CellColor Path Yellow -Filter "Path -like ""*memory*""" | Out-File c:\Test\colortest.html
        
        Takes a collection of objects in $Data, sorts on the property Server and converts to HTML. From there
        we set the "CookedValue" property to red if it's greater then 1. We then send the HTML through Set-CellColor
        again, this time setting the Server cell to green if it's "dc2". One more time through Set-CellColor
        turns the Path cell to Yellow if it contains the word "memory" in it.
        
    .EXAMPLE
        $HTML = $Data | sort server | ConvertTo-html -head $header | Set-CellColor -Color red -Filter "cookedvalue -gt 1" -Row
        
        Now, if the cookedvalue property is greater than 1 the function will highlight the entire row red.
        
    .NOTES
        Author: Martin Pugh
        Twitter: @thesurlyadm1n
        Spiceworks: Martin9700
        Blog: www.thesurlyadmin.com
          
        Changelog:
            2.0 Major rewrite, now only replaces the column you specify (finally). Property parameter has been eliminated.
            1.5 Added ability to set row color with -Row switch instead of the individual cell
            1.03 Added error message in case the $Property field cannot be found in the table header
            1.02 Added some additional text to help. Added some error trapping around $Filter
                            creation.
            1.01 Added verbose output
            1.0 Initial Release
    .LINK
        http://community.spiceworks.com/scripts/show/2450-change-cell-color-in-html-table-with-powershell-set-cellcolor
    #>


    [CmdletBinding()]
    Param (
        [Parameter(Mandatory,Position=0)]
        [string]$Filter,

        [Parameter(Mandatory,Position=0)]
        [string]$Color,

        [Parameter(Mandatory,ValueFromPipeline)]
        [Object[]]$InputObject,
        
        [switch]$Row
    )
    
    Begin {
        Write-Verbose "$(Get-Date): Function Set-CellColor begins"
    }
    
    Process {
        $InputObject = $InputObject -split "`r`n"
        ForEach ($Line in $InputObject)
        {   
            If ($Line.IndexOf("<tr><th") -ge 0)
            {   
                Write-Verbose "$(Get-Date): Processing headers..."
                $Search = $Line | Select-String -Pattern '<th ?.*?>(.*?)<\/th>' -AllMatches
                
                $Index = 0
                $Property = $null
                ForEach ($Header in $Search.Matches)
                {   
                    If ($Filter -match $Header.Groups[1].Value)
                    {   
                        $Property = $Header.Groups[1].Value
                        Break
                    }
                    $Index ++
                }

                If (-not $Property)
                {
                    Write-Error "$(Get-Date): Unable to locate the column in your filter. Filter: $Filter" -ErrorAction Stop
                }
                Else
                {
                    $Filter = $Filter -replace $Property,"`$Value"
                    Try {
                        $Oper = [scriptblock]::Create($Filter)
                    }
                    Catch {
                        Write-Error "$(Get-Date): ""$Filter"" invalid because ""$_""" -ErrorAction Stop
                    }
                }
                Write-Verbose "$(Get-Date): $Property column found at index: $Index"
            }

            If ($Line -match "<tr ?.*?><td ?.*?>(.*?)<\/td><\/tr>")
            {   
                $Search = $Line | Select-String -Pattern '<td ?.*?>(.*?)<\/td>' -AllMatches
                #Determine if the column value is a number
                Try {
                    [double]$Value = $Search.Matches[$Index].Groups[1].Value
                }
                Catch {
                    #Nope, treat it like a string
                    [string]$Value = $Search.Matches[$Index].Groups[1].Value
                }

                If (Invoke-Command $Oper)
                {   
                    If ($Row)
                    {   
                        Write-Verbose "$(Get-Date): Criteria met! Changing row to $Color..."
                        If ($Line -match "<tr style=""background-color:(.+?)"">")
                        {   
                            $Line = $Line -replace "<tr style=""background-color:$($Matches[1])","<tr style=""background-color:$Color"
                        }
                        Else
                        {   
                            $Line = $Line.Replace("<tr>","<tr style=""background-color:$Color"">")
                        }
                    }
                    Else
                    {   
                        Write-Verbose "$(Get-Date): Criteria met! Changing cell to $Color..."
                        $Count = 0
                        $Pattern = ".*?"
                        ForEach ($Column in $Search.Matches)
                        {
                            $Pattern = "$Pattern$($Column.Groups[1].Value)</td>.*?"
                            If ($Count -eq $Index - 1)
                            {
                                Break
                            }
                            $Count ++
                        }
                        $null = $Line -match $Pattern
                        $StartsWith = $Matches.Values
                        $NewLine = $Line -replace $StartsWith,''
                        $NewLine = $NewLine -replace '^<td ?(style="background-color:.*")?>.*?<\/td>',"<td style=""background-color:$Color"">$Value</td>"
                        $Line = "$StartsWith$($NewLine)"
                    }
                }
            }
            Write-Output $Line
        }
    }
    
    End {
        Write-Verbose "$(Get-Date): Function Set-CellColor completed"
    }
}

Function Set-GroupRowColorsByColumn {
    <#
    .SYNOPSIS
        Set rows to alternating colors based on the value of a cell
    .DESCRIPTION
        Rows will alternate color in a HTML table.
        
        **This is designed for use with ConvertTo-HTML and other HTML tables may not work
        with it.**

        I recommend sorting your data by the column you intend to use as a trigger.

    .PARAMETER InputObject
        Your HTML input, either specified directly or through pipeline.

    .PARAMETER ColumnName
        This is the column that you will trigger off of. Whenever this column changes the
        script will flip to the other color.

    .PARAMETER CSSEvenClass
        The Even color used in the report. This will be the starting color.

    .PARAMETER CSSOddClass
        The alternate color in the report.

    .EXAMPLE
        $Data = @"
        "From","To","LastRepliationAttempt","LastReplicationResult","LastReplicationSuccess"
        "corpdc101","CORPDC102","2/11/2015 12:57:47 PM","0","2/11/2015 12:57:47 PM"
        "corpdc101","CORPDC103","2/11/2015 12:57:45 PM","0","2/11/2015 12:57:45 PM"
        "corpdc101","CORPDC701","2/11/2015 12:50:36 PM","0","2/11/2015 12:50:36 PM"
        "corpdc101","CORPDC001","2/11/2015 12:50:36 PM","0","2/11/2015 12:50:36 PM"
        "corpdc101","CORPDC202","2/11/2015 12:50:35 PM","0","2/11/2015 12:50:35 PM"
        "corpdc102","CORPDC103","2/11/2015 12:57:48 PM","0","2/11/2015 12:57:48 PM"
        "corpdc102","CORPDC101","2/11/2015 12:57:44 PM","0","2/11/2015 12:57:44 PM"
        "corpdc102","CORPDC301","2/11/2015 12:54:49 PM","0","2/11/2015 12:54:49 PM"
        "corpdc102","CORPDC402","2/11/2015 12:54:45 PM","0","2/11/2015 12:54:45 PM"
        "CORPRODC101","CORPDC102","2/11/2015 12:50:41 PM","0","2/11/2015 12:50:41 PM"
        "CORPRODC102","CORPDC102","2/11/2015 12:53:40 PM","0","2/11/2015 12:53:40 PM"
        "corpdc401","CORPDC402","2/11/2015 12:57:50 PM","0","2/11/2015 12:57:50 PM"
        "corpdc402","CORPDC401","2/11/2015 12:57:52 PM","0","2/11/2015 12:57:52 PM"
        "corpdc402","CORPDC102","2/11/2015 12:54:59 PM","0","2/11/2015 12:54:59 PM"
        "corpdc201","CORPDC202","2/11/2015 12:57:51 PM","0","2/11/2015 12:57:51 PM"
        "corpdc103","CORPDC102","2/11/2015 12:57:50 PM","0","2/11/2015 12:57:50 PM"
        "corpdc103","CORPDC101","2/11/2015 12:57:47 PM","0","2/11/2015 12:57:47 PM"
        "CORPDC202","CORPDC201","2/11/2015 12:57:43 PM","0","2/11/2015 12:57:43 PM"
        "CORPDC202","CORPDC101","2/11/2015 12:51:53 PM","0","2/11/2015 12:51:53 PM"
        "corpdc001","CORPDC002","2/11/2015 12:57:43 PM","0","2/11/2015 12:57:43 PM"
        "corpdc001","CORPDC101","2/11/2015 12:55:50 PM","0","2/11/2015 12:55:50 PM"
        "corpdc002","CORPDC001","2/11/2015 12:57:46 PM","0","2/11/2015 12:57:46 PM"
        "corpdc301","CORPDC101","2/11/2015 12:46:28 PM","0","2/11/2015 12:46:28 PM"
        "corpdc701","CORPDC702","2/11/2015 12:57:49 PM","0","2/11/2015 12:57:49 PM"
        "corpdc701","CORPDC102","2/11/2015 12:56:49 PM","0","2/11/2015 12:56:49 PM"
        "corpdc702","CORPDC701","2/11/2015 12:57:52 PM","0","2/11/2015 12:57:52 PM"
        "@ | ConvertFrom-CSV | Sort From

        $Header = @"
        <style>
        TABLE {border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
        TH {border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color: #6495ED;}
        TD {border-width: 1px;padding: 3px;border-style: solid;border-color: black;}
        .odd { background-color:#ffffff; }
        .even { background-color:#dddddd; }
        </style>
        "@

        $RawHTML = $Data | ConvertTo-Html -Head $Header


        $New = Set-GroupRowColorsByColumn -InputObject $RawHTML -ColumnName From -CSSEvenClass even -CSSOddClass odd
        
        #Can also use:
        $New = $RawHTML | Set-GroupRowColorsByColumn -ColumnName From -CSSEvenClass even -CSSOddClass odd

        $New | Out-File .\Report.html

    .NOTES
        Author: Martin Pugh
          
        Changelog:
            1.52 Updated the help, removed an unnecessary variable
            1.51 Removed test code, added comment based help
            1.5 Added ability to set row color with -Row switch instead of the individual cell
            1.03 Added error message in case the $Property field cannot be found in the table header
            1.02 Added some additional text to help. Added some error trapping around $Filter
                            creation.
            1.01 Added verbose output
            1.0 Initial Release
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory,ValueFromPipeline)]
        [string[]]$InputObject,
        [Parameter(Mandatory)]
        [string]$ColumnName,
        [string]$CSSEvenClass = "TREven",
        [string]$CSSOddClass = "TROdd"
    )
    Process {
        ForEach ($Line in $InputObject)
        {
            If ($Line -like "*<th>*")
            {
                If ($Line -notlike "*$ColumnName*")
                {
                    Write-Error "Unable to locate a column named $ColumnName" -ErrorAction Stop
                }
                $Search = $Line | Select-String -Pattern "<th>.*?</th>" -AllMatches
                $Index = 0
                ForEach ($Column in $Search.Matches)
                {
                    If (($Column.Groups.Value -replace "<th>|</th>","") -eq $ColumnName)
                    {
                        Break
                    }
                    $Index ++
                }
            }
            If ($Line -like "*<td>*")
            {
                $Search = $Line | Select-String -Pattern "<td>.*?</td>" -AllMatches
                If ($LastColumn -ne $Search.Matches[$Index].Value)
                {
                    If ($Class -eq $CSSEvenClass)
                    {
                        $Class = $CSSOddClass
                    }
                    Else
                    {
                        $Class = $CSSEvenClass
                    }
                }
                $LastColumn = $Search.Matches[$Index].Value
                $Line = $Line.Replace("<tr>","<tr class=""$Class"">")
            }
            Write-Output $Line
        }
    }
}