Public/New-ConfluenceContentTable.ps1
|
function New-ConfluenceContentTable { <# .SYNOPSIS Creates an HTML table for a Confluence page from a collection of objects. .DESCRIPTION New-ConfluenceContentTable converts objects to a table: the property names of the first object become the header row and every object becomes a row. All objects must be of the same type and have the same property names. Cell values that are collections or objects with several properties are rendered as nested tables. Text that contains a web address is converted to links. Values are inserted as given, so they may contain markup. An empty collection returns an empty string. .PARAMETER TableData The objects to show in the table. .PARAMETER TableType The CSS class(es) of the table, for example Wrapped or "Relative Table". .PARAMETER TableTypeStyle A single inline style, for example "Width: 100%" (default), "Width: 600px" or "Border: 1px solid black". .PARAMETER NoHeader Do not add the header row. .PARAMETER HeaderStringFormatting Formatting for the header cells: Bold, Italic, Underline and/or Strikethrough. .PARAMETER HeaderAlignmentFormatting Text alignment of the header cells: Left (default), Center or Right. .PARAMETER VerticalHeader Render the first cell of every row as a row header (<th scope='row'>). .PARAMETER CellStringFormatting Formatting for the data cells (all but the first column): Bold, Italic, Underline and/or Strikethrough. .PARAMETER CellAlignmentFormatting Text alignment of the data cells: Left (default), Center or Right. .PARAMETER FirstCellHeaderFormat Wrap the first cell of each row in a heading: 0 (no heading, default) or 1 to 6. .PARAMETER FirstCellStringFormatting Formatting for the first cell of each row: Bold, Italic, Underline and/or Strikethrough. .PARAMETER FirstCellAlignmentFormatting Text alignment of the first cell of each row. Defaults to -CellAlignmentFormatting. .EXAMPLE $rows = Get-Process | Select-Object -First 5 Name, Id New-ConfluenceContentTable -TableData $rows -HeaderStringFormatting Bold -VerticalHeader Returns a table with a bold header row and the process names as row headers. .OUTPUTS System.String #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Only builds a storage-format string in memory; nothing outside the session is changed.')] [CmdletBinding()] [OutputType([string])] param ( [Parameter(Mandatory, HelpMessage = 'The table data to be converted.')] [AllowEmptyCollection()] [AllowNull()] [array]$TableData, [Parameter(HelpMessage = 'The type of table to be created.')] [ValidateSet('Wrapped', 'Wrapped Relative Table', 'Relative Table', 'Relative Table with Header', 'Wrapped Relative Table with Header', 'Wrapped Relative Table with Header and Vertical Header')] [string]$TableType = 'Wrapped', [Parameter(HelpMessage = 'The style to be applied to the table.')] [ValidatePattern('^(Width: \d+%|Height: \d+%|Width: \d+px|Height: \d+px|Margin: \d+px|Padding: \d+px|Border: \d+px solid [a-zA-Z]+)$')] [string]$TableTypeStyle = 'Width: 100%', [Parameter(HelpMessage = 'Do not add a header row.')] [switch]$NoHeader, [Parameter(HelpMessage = 'The text formatting to be applied to the table header.')] [ValidateSet('Bold', 'Italic', 'Underline', 'Strikethrough')] [string[]]$HeaderStringFormatting, [Parameter(HelpMessage = 'The text alignment to be applied to the table header.')] [ValidateSet('Left', 'Center', 'Right')] [string]$HeaderAlignmentFormatting = 'Left', [Parameter(HelpMessage = 'Render the first cell of each row as a row header.')] [switch]$VerticalHeader, [Parameter(HelpMessage = 'The text formatting to be applied to the table cells.')] [ValidateSet('Bold', 'Italic', 'Underline', 'Strikethrough')] [string[]]$CellStringFormatting, [Parameter(HelpMessage = 'The text alignment to be applied to the table cells.')] [ValidateSet('Left', 'Center', 'Right')] [string]$CellAlignmentFormatting = 'Left', [Parameter(HelpMessage = "The header format to be applied to the table first cell.`n0 Normal Paragraph`n1 Heading 1 (Largest)`n2 Heading 2`n3 Heading 3`n4 Heading 4`n5 Heading 5 (Smallest)`n6 Heading 6 (Quote)")] [ValidateSet('0', '1', '2', '3', '4', '5', '6')] [string]$FirstCellHeaderFormat = '0', [Parameter(HelpMessage = 'The text formatting to be applied to the first cell.')] [ValidateSet('Bold', 'Italic', 'Underline', 'Strikethrough')] [string[]]$FirstCellStringFormatting, [Parameter(HelpMessage = 'The text alignment to be applied to the first cell.')] [ValidateSet('Left', 'Center', 'Right')] [string]$FirstCellAlignmentFormatting ) $TelemetryArgs = @{ ModuleName = $MyInvocation.MyCommand.Module.Name ModuleVersion = [string]$MyInvocation.MyCommand.Module.Version CommandName = $MyInvocation.MyCommand.Name ExecutionID = [guid]::NewGuid().ToString() } Invoke-TelemetryCollection @TelemetryArgs -Stage Start -ClearTimer $telemetryFailed = $false try { if ($null -eq $TableData -or $TableData.Count -eq 0) { return '' } $firstRow = $TableData[0] $firstRowType = $firstRow.GetType() $firstRowProperties = @($firstRow.PSObject.Properties.Name) foreach ($row in $TableData) { if ($null -eq $row -or $row.GetType() -ne $firstRowType) { Write-Error 'All table rows must be of the same type.' return } if (Compare-Object -ReferenceObject $firstRowProperties -DifferenceObject @($row.PSObject.Properties.Name) -SyncWindow 0) { Write-Error 'All table rows must have the same column names.' return } } if ([string]::IsNullOrEmpty($FirstCellAlignmentFormatting)) { $FirstCellAlignmentFormatting = $CellAlignmentFormatting } $URLFormatting = '\b((http|https):\/\/)?((www\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})(\/[a-zA-Z0-9-._~:\/?#[\]@!$&''()*+,;=]*)?\b' # Renders one cell value: nested table for collections/complex objects, links for URLs $renderValue = { param($Value, [bool]$DetectLinks) if ($null -eq $Value) { return '' } $isScalar = ($Value -is [string]) -or ($Value -is [ValueType]) if (-not $isScalar) { if ($Value -is [System.Collections.IEnumerable] -and @($Value).Count -ge 1) { return (New-ConfluenceContentTable -TableData @($Value)) } if (@($Value.PSObject.Properties).Count -gt 1) { return (New-ConfluenceContentTable -TableData @($Value)) } } $text = $Value.ToString() if ($DetectLinks -and [regex]::IsMatch($text, $URLFormatting)) { return (New-ConfluenceContentLink -TextBlock $text) } return $text } $TableHtml = "<table class='$TableType' style='$TableTypeStyle'>" if (-not $NoHeader) { $headerOpen = Get-HtmlFormatTag -Format $HeaderStringFormatting $headerClose = Get-HtmlFormatTag -Format $HeaderStringFormatting -Close $TableHtml += '<thead><tr>' foreach ($header in $firstRowProperties) { $TableHtml += "<th style='text-align: $HeaderAlignmentFormatting;' scope='col'>$headerOpen$header$headerClose</th>" } $TableHtml += '</tr></thead>' } $firstOpen = Get-HtmlFormatTag -Format $FirstCellStringFormatting $firstClose = Get-HtmlFormatTag -Format $FirstCellStringFormatting -Close $cellOpen = Get-HtmlFormatTag -Format $CellStringFormatting $cellClose = Get-HtmlFormatTag -Format $CellStringFormatting -Close $useHeading = $FirstCellHeaderFormat -match '^[1-6]$' $TableHtml += '<tbody>' foreach ($row in $TableData) { $TableHtml += '<tr>' $isFirstCell = $true foreach ($cell in $row.PSObject.Properties) { if ($isFirstCell) { $cellTag = if ($VerticalHeader) { 'th' } else { 'td' } $scope = if ($VerticalHeader) { " scope='row'" } else { '' } $wrapOpen = if ($useHeading) { "<h$FirstCellHeaderFormat>" } else { '<span>' } $wrapClose = if ($useHeading) { "</h$FirstCellHeaderFormat>" } else { '</span>' } $value = & $renderValue $cell.Value $false $TableHtml += "<$cellTag$scope style='text-align: $FirstCellAlignmentFormatting;'>$wrapOpen$firstOpen$value$firstClose$wrapClose</$cellTag>" $isFirstCell = $false } else { $value = & $renderValue $cell.Value $true $TableHtml += "<td style='text-align: $CellAlignmentFormatting;'>$cellOpen$value$cellClose</td>" } } $TableHtml += '</tr>' } $TableHtml += '</tbody></table>' return $TableHtml } catch { $telemetryFailed = $true Invoke-TelemetryCollection @TelemetryArgs -Stage End -Failed $true -Exception $_ throw } finally { if (-not $telemetryFailed) { Invoke-TelemetryCollection @TelemetryArgs -Stage End } } } |