Private/Render/PpaCoverageMatrix.ps1
|
# PpaCoverageMatrix.ps1 - renders the CoverageModel (Wave 4 spec 5.1/5.3/5.5-5.8). # Draw-only: the model comes fully classified from Get-PpaCoverageModel; this file # adds NO logic beyond presentation. Every tenant-derived string (policy names in # tooltips) passes the shared render boundary (ConvertTo-PpaHtmlText/Attr), so # HTML-encoding is unconditional and -Redact/-RedactNames pseudonymize here too. # None vs Unknown is print-safe by construction: distinct color family PLUS # hatching PLUS glyph PLUS in-cell text (see the covm-* rules in the shared CSS). # ASCII-only source (Windows PowerShell 5.1). Set-StrictMode -Off function Get-PpaCoverageCellStyle { param([string]$State) switch ($State) { 'Covered' { return [pscustomobject]@{ Css = 'covm-covered'; Glyph = '✓' } } 'Partial' { return [pscustomobject]@{ Css = 'covm-partial'; Glyph = '◐' } } 'Test-only' { return [pscustomobject]@{ Css = 'covm-testonly'; Glyph = '○' } } 'None' { return [pscustomobject]@{ Css = 'covm-none'; Glyph = '✗' } } 'Unknown' { return [pscustomobject]@{ Css = 'covm-unknown'; Glyph = '?' } } 'N/A' { return [pscustomobject]@{ Css = 'covm-na'; Glyph = '–' } } # Defensive default for out-of-enum states; nothing produces one since the # Copilot x Retention render-hold was lifted (Wave 5 cleanup Part 1). default { return [pscustomobject]@{ Css = 'covm-held'; Glyph = '—' } } } } function Write-PpaCoverageMatrix { [CmdletBinding()] param( [Parameter(Mandatory = $true)] $Coverage ) if ($null -eq $Coverage) { return '' } $sb = New-Object System.Text.StringBuilder # Collapsible glanceable header (Wave 5 addendum): the card-header toggles the grid via # the shared vanilla collapse handler; collapsed-by-default reclaims vertical space. The # one-line coverage tally (assessed cell states) stays visible - aggregate metrics, so # safe to show even under redaction. N/A cells are excluded from the tally. $stateCounts = [ordered]@{ 'Covered'=0; 'Partial'=0; 'Test-only'=0; 'None'=0; 'Unknown'=0 } foreach ($cell in @($Coverage.cells)) { $cs = [string]$cell.state if ($stateCounts.Contains($cs)) { $stateCounts[$cs] = [int]$stateCounts[$cs] + 1 } } $labels = @{ 'Covered'='covered'; 'Partial'='partial'; 'Test-only'='test-only'; 'None'='none'; 'Unknown'='unknown' } $tallyParts = New-Object System.Collections.Generic.List[string] foreach ($k in $stateCounts.Keys) { if ([int]$stateCounts[$k] -gt 0) { $tallyParts.Add(([string][int]$stateCounts[$k]) + ' ' + $labels[$k]) } } $tally = if ($tallyParts.Count -gt 0) { $tallyParts -join ' · ' } else { 'no assessed cells' } [void]$sb.AppendLine(' <div class="card mt-3 covm" id="Coveragematrix">') [void]$sb.Append(' <div class="card-header sumhead" data-toggle="collapse" data-target="#body-Coveragematrix" role="button" tabindex="0" aria-expanded="false" aria-controls="body-Coveragematrix">') [void]$sb.Append('<i class="fas fa-chevron-right chev"></i><strong>Coverage Matrix</strong>') [void]$sb.Append('<span class="sum-glance">').Append($tally).AppendLine('</span></div>') [void]$sb.AppendLine(' <div class="collapse" id="body-Coveragematrix">') [void]$sb.AppendLine(' <div class="card-body">') # Degraded-collector banner above the matrix (ruled 5.1). if ($Coverage.banner.show) { $parts = @($Coverage.banner.degraded | ForEach-Object { (ConvertTo-PpaHtmlText (([string]$_.collector) -replace '_', ' ')) + ' (' + (ConvertTo-PpaHtmlText ([string]$_.outcome)) + ')' }) [void]$sb.Append(' <div class="covm-banner">Coverage visibility is degraded for: ').Append(($parts -join ', ')) [void]$sb.AppendLine('. Affected cells read Unknown and are excluded from gap counts.</div>') } # ---- the grid ---- [void]$sb.AppendLine(' <table class="covm-grid">') [void]$sb.Append(' <thead><tr><th>Workload</th>') foreach ($col in @($Coverage.columns)) { [void]$sb.Append('<th>').Append((ConvertTo-PpaHtmlText ([string]$col.label))).Append('</th>') } [void]$sb.AppendLine('</tr></thead>') [void]$sb.AppendLine(' <tbody>') foreach ($row in @($Coverage.rows)) { [void]$sb.Append(' <tr><th class="covm-row">').Append((ConvertTo-PpaHtmlText ([string]$row.label))).Append('</th>') foreach ($col in @($Coverage.columns)) { $cell = @($Coverage.cells | Where-Object { $_.row -eq $row.key -and $_.column -eq $col.key })[0] $sty = Get-PpaCoverageCellStyle ([string]$cell.state) [void]$sb.Append('<td class="covm-cell ').Append($sty.Css).Append('">') $title = '' if (@($cell.contributors).Count -gt 0) { $title = 'Contributing policies: ' + ((@($cell.contributors)) -join ', ') } elseif ([string]$cell.state -eq 'N/A') { $title = [string]$cell.naReason } [void]$sb.Append('<a class="covm-link" href="#finding-').Append((ConvertTo-PpaHtmlAttr ([string]$cell.checkId))).Append('"') if (-not [string]::IsNullOrEmpty($title)) { [void]$sb.Append(' title="').Append((ConvertTo-PpaHtmlAttr $title)).Append('"') } [void]$sb.Append('><span class="covm-glyph">').Append($sty.Glyph).Append('</span><span class="covm-text">').Append((ConvertTo-PpaHtmlText ([string]$cell.state))).Append('</span></a>') foreach ($r in @($cell.reasons)) { [void]$sb.Append('<br><span class="covm-reason">').Append((ConvertTo-PpaHtmlText ([string]$r))).Append('</span>') } # Provisional-marker legend invariant (Wave 5 cleanup Part 1): the dagger # and its explanatory tooltip render if and only if the cell is still # documented-only, so no orphaned legend can survive a provenance flip. if ([string]$cell.provenance -eq 'documented-only' -and @('Covered', 'Partial', 'Test-only', 'None') -contains [string]$cell.state) { [void]$sb.Append('<span class="covm-prov" title="property shape documented but not yet verified on a live tenant.">†</span>') } [void]$sb.Append('</td>') } [void]$sb.AppendLine('</tr>') } [void]$sb.AppendLine(' </tbody>') [void]$sb.AppendLine(' </table>') # ---- tenant-level strip: audit, once, never per-row (ruled 5.2 + A addendum) ---- $audText = switch ([string]$Coverage.auditStrip.state) { 'On' { 'On' } 'Off' { 'Off' } default { 'Not observed this run' } } [void]$sb.Append(' <p class="covm-strip"><strong>Tenant-level:</strong> Unified audit: ').Append((ConvertTo-PpaHtmlText $audText)) [void]$sb.Append(' · <a href="#finding-').Append((ConvertTo-PpaHtmlAttr ([string]$Coverage.auditStrip.checkId))).AppendLine('">details</a></p>') # ---- "Other solutions" strip (the model's `principal` field; ruled 5.2): counts + # section links. Heading is "Other solutions" not "Principal-scoped" because the set # includes case/matter-scoped eDiscovery, which is not principal-scoped. ---- $pparts = New-Object System.Collections.Generic.List[string] foreach ($p in @($Coverage.principal)) { $txt = '<a href="#' + (ConvertTo-PpaHtmlAttr ([string]$p.sectionId)) + '">' + (ConvertTo-PpaHtmlText ([string]$p.name)) + '</a>: ' if ($null -ne $p.count) { $txt += [string][int]$p.count + ' ' + $(if ([int]$p.count -eq 1) { 'policy' } else { 'policies' }) } else { $txt += (ConvertTo-PpaHtmlText ([string]$p.note)) } $pparts.Add($txt) } [void]$sb.Append(' <p class="covm-strip"><strong>Other solutions (no workload cells):</strong> ').Append(($pparts -join ' · ')).AppendLine('</p>') # ---- framing notes (ruled 5.1) ---- [void]$sb.AppendLine(' <p class="covm-foot">Assessed via Security & Compliance PowerShell only. Container labeling for SharePoint and Teams is out of scope for this matrix.</p>') [void]$sb.AppendLine(' </div>') [void]$sb.AppendLine(' </div>') [void]$sb.AppendLine(' </div>') return $sb.ToString() } |