Public/Export-LGReport.ps1
|
function Export-LGCsvReport { [CmdletBinding()] param( [Parameter(Mandatory)][PSCustomObject[]]$AllResults, [Parameter(Mandatory)][PSCustomObject[]]$PolicyFindings, [Parameter(Mandatory)][string]$CsvPath ) try { $rows = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($r in $AllResults) { $rows.Add([PSCustomObject]@{ Type='LicenseStatus'; Module=$r.Module; Name=$r.Name Detail=if($r.Detail){$r.Detail}else{''} Status=$r.Status; PolicyStatus=''; Category=''; Severity='' }) } foreach ($f in $PolicyFindings) { $rows.Add([PSCustomObject]@{ Type='PolicyCheck'; Module='PolicyCheck'; Name=$f.Name Detail=if($f.Detail){$f.Detail}else{''} Status=$f.Status; PolicyStatus=$f.PolicyStatus Category=if($f.Category){$f.Category}else{''} Severity=if($f.Severity){$f.Severity}else{''} }) } $rows | Export-Csv -Path $CsvPath -NoTypeInformation -Encoding UTF8 Write-Host " CSV report saved: $CsvPath" -ForegroundColor Cyan } catch { Write-Warning "CSV error: $($_.Exception.Message)" } } function Export-LGJsonReport { [CmdletBinding()] param( [Parameter(Mandatory)][PSCustomObject[]]$AllResults, [Parameter(Mandatory)][PSCustomObject[]]$PolicyFindings, [Parameter(Mandatory)][string]$JsonPath ) try { [PSCustomObject]@{ Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' Hostname = $env:COMPUTERNAME Version = $script:LGVersion LicenseStatus = $AllResults PolicyFindings = $PolicyFindings } | ConvertTo-Json -Depth 5 | Out-File $JsonPath -Encoding UTF8 Write-Host " JSON report saved: $JsonPath" -ForegroundColor Cyan } catch { Write-Warning "JSON error: $($_.Exception.Message)" } } function Export-LGSarifReport { [CmdletBinding()] param( [Parameter(Mandatory)][PSCustomObject[]]$PolicyFindings, [Parameter(Mandatory)][string]$SarifPath ) try { $rules = @(); $results = @(); $seen = @{} foreach ($f in ($PolicyFindings | Where-Object { $_.PolicyStatus -ne 'ALLOWED' })) { $rid = if ($f.RuleId -and $f.RuleId -ne 'N/A') { $f.RuleId } else { 'LG-POLICY' } if (-not $seen.ContainsKey($rid)) { $seen[$rid] = $true $rules += [PSCustomObject]@{ id = $rid name = "LicensePolicy_$rid" shortDescription = [PSCustomObject]@{ text = $f.Detail } properties = [PSCustomObject]@{ category = $f.Category; severity = $f.Severity } } } $level = switch ($f.PolicyStatus) { 'PROHIBITED' { 'error' } 'REQUIRES_LICENSE' { 'warning' } default { 'note' } } $results += [PSCustomObject]@{ ruleId = $rid; level = $level message = [PSCustomObject]@{ text = "$($f.Name): $($f.Detail)" } locations = @([PSCustomObject]@{ logicalLocations = @([PSCustomObject]@{ name=$env:COMPUTERNAME; kind='machine' }) }) } } [PSCustomObject]@{ '`$schema' = 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json' version = '2.1.0' runs = @([PSCustomObject]@{ tool = [PSCustomObject]@{ driver = [PSCustomObject]@{ name='LicenseGuard'; version=$script:LGVersion; rules=$rules } } results = $results }) } | ConvertTo-Json -Depth 10 | Out-File $SarifPath -Encoding UTF8 Write-Host " SARIF report saved: $SarifPath" -ForegroundColor Cyan } catch { Write-Warning "SARIF error: $($_.Exception.Message)" } } function Export-LGHtmlReport { <# .SYNOPSIS Generates a full interactive HTML compliance report. .EXAMPLE Export-LGHtmlReport -AllResults $results -PolicyFindings $policy -OutputPath .\report.html #> [CmdletBinding()] param( [Parameter(Mandatory)][PSCustomObject[]]$AllResults, [Parameter(Mandatory)][PSCustomObject[]]$PolicyFindings, [string]$OutputPath = '.\license-report.html', [object]$Delta = $null, [string]$BrandColor = '#3b82f6', [string]$BrandCompany = '', [ValidateSet('tr','en')] [string]$Language = 'tr' ) $L = Get-LGDefaultStrings -Language $Language $cfg = Get-LGEffectiveConfig $warnDays = $cfg.WarnDaysBeforeExpiry $ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $hostname = $env:COMPUTERNAME $ver = $script:LGVersion $total = if ($AllResults.Count -gt 0) { $AllResults.Count } else { 1 } $okCount = @($AllResults | Where-Object { $_.Status -eq 'OK' }).Count $warnCount = @($AllResults | Where-Object { $_.Status -eq 'WARN' }).Count $expCount = @($AllResults | Where-Object { $_.Status -in @('EXPIRED','ERROR') }).Count $okPct = [math]::Round($okCount / $total * 100) $warnPct = [math]::Round($warnCount / $total * 100) $expPct = [math]::Round($expCount / $total * 100) $tableRows = ($AllResults | ForEach-Object { $bc = switch ($_.Status) { 'OK' { 'ok' } 'WARN' { 'warn' } default { 'expired' } } $trTx = switch ($_.Status) { 'OK' { 'UYUMLU' } 'WARN' { 'UYARI' } 'EXPIRED' { 'SURESI DOLDU' } default { 'HATA' } } $enTx = switch ($_.Status) { 'OK' { 'OK' } 'WARN' { 'WARNING' } 'EXPIRED' { 'EXPIRED' } default { 'ERROR' } } $det = if ($_.Detail) { Protect-HtmlString $_.Detail } else { '—' } "<tr data-status='$($_.Status)'><td><span class='mod-pill' data-mod='$($_.Module)'>$($_.Module)</span></td><td>$(Protect-HtmlString $_.Name)</td><td>$det</td><td><span class='badge $bc' data-val-tr='$trTx' data-val-en='$enTx'>$trTx</span></td></tr>" }) -join "`n" $prohibCount=0; $licCount=0; $allowedCount=0; $prohibPct=0; $licPct=0; $allowedPct=0; $policyRows='' if ($PolicyFindings -and $PolicyFindings.Count -gt 0) { $pTotal = $PolicyFindings.Count $prohibCount = @($PolicyFindings | Where-Object { $_.PolicyStatus -eq 'PROHIBITED' }).Count $licCount = @($PolicyFindings | Where-Object { $_.PolicyStatus -eq 'REQUIRES_LICENSE' }).Count $allowedCount = @($PolicyFindings | Where-Object { $_.PolicyStatus -eq 'ALLOWED' }).Count $prohibPct = [math]::Round($prohibCount / $pTotal * 100) $licPct = [math]::Round($licCount / $pTotal * 100) $allowedPct = [math]::Round($allowedCount / $pTotal * 100) $policyRows = ($PolicyFindings | ForEach-Object { $bc = switch ($_.PolicyStatus) { 'PROHIBITED' { 'expired' } 'REQUIRES_LICENSE' { 'warn' } default { 'ok' } } $lblTr = switch ($_.PolicyStatus) { 'PROHIBITED' { 'YASAK' } 'REQUIRES_LICENSE' { 'LISANS GEREKLI' } default { 'UYUMLU' } } $lblEn = switch ($_.PolicyStatus) { 'PROHIBITED' { 'PROHIBITED' } 'REQUIRES_LICENSE' { 'REQUIRES LICENSE' } default { 'COMPLIANT' } } $sevCls= switch ($_.Severity) { 'CRITICAL' { 'critical' } 'HIGH' { 'high' } 'MEDIUM' { 'medium' } default { 'low' } } $sevPill = if ($_.PolicyStatus -ne 'ALLOWED' -and $_.Severity) { "<span class='sev-pill $sevCls'>$($_.Severity)</span>" } else { '' } $alt = if ($_.Alternative) { "<br><small class='suggestion'>💡 $(Protect-HtmlString $_.Alternative)</small>" } else { '' } $ref = if ($_.Reference) { " <a href='$(Protect-HtmlString $_.Reference)' target='_blank' class='reflink'>↗</a>" } else { '' } "<tr data-policy='$($_.PolicyStatus)'><td>$(Protect-HtmlString $_.Category)$sevPill</td><td><span class='sw-name'>$(Protect-HtmlString $_.Name)</span><br><small class='sw-meta'>$(Protect-HtmlString $_.Version) · $(Protect-HtmlString $_.Publisher)</small></td><td>$(Protect-HtmlString $_.Detail)$alt$ref</td><td><span class='badge $bc' data-val-tr='$lblTr' data-val-en='$lblEn'>$lblTr</span></td></tr>" }) -join "`n" } $calToday = Get-Date; $calParts = @() foreach ($r in $AllResults) { if ($r.Detail -and $r.Detail -match '(\d{4}-\d{2}-\d{2})') { $dateStr = $Matches[1] try { $expDate = [datetime]$dateStr $days = [int]($expDate - $calToday).TotalDays $nm = ($r.Name -replace '\\','\\' -replace '"','\"') $md = ($r.Module -replace '"','\"') $st = ($r.Status -replace '"','\"') $calParts += "{`"name`":`"$nm`",`"module`":`"$md`",`"date`":`"$dateStr`",`"days`":$days,`"status`":`"$st`"}" } catch {} } } $calJson = '[' + ($calParts -join ',') + ']' $deltaBoxHtml = '' if ($Delta) { $ni = if ($Delta.NewIssues) { @($Delta.NewIssues).Count } else { 0 } $ri = if ($Delta.ResolvedIssues) { @($Delta.ResolvedIssues).Count } else { 0 } $nv = if ($Delta.NewViolations) { @($Delta.NewViolations).Count } else { 0 } $rv = if ($Delta.ResolvedViolations) { @($Delta.ResolvedViolations).Count } else { 0 } if (($ni + $ri + $nv + $rv) -gt 0) { $dParts = @() if ($ni -gt 0) { $dParts += "<span class='delta-new'>▲ $ni $($L['deltaNew'])</span>" } if ($ri -gt 0) { $dParts += "<span class='delta-ok'>▼ $ri $($L['deltaResolved'])</span>" } if ($nv -gt 0) { $dParts += "<span class='delta-new'>▲ $nv policy $($L['deltaNew'])</span>" } if ($rv -gt 0) { $dParts += "<span class='delta-ok'>▼ $rv policy $($L['deltaResolved'])</span>" } $deltaBoxHtml = "<div class='delta-box'><span class='delta-lbl'>$($L['deltaTitle'])</span> — $($L['deltaSince']) <b>$($Delta.PreviousTimestamp)</b> $($dParts -join ' · ')</div>" } } $initLang = $Language $brandSpan = if ($BrandCompany) { "<span style='font-size:.72rem;color:var(--tx3);margin-left:.4rem'>— $(Protect-HtmlString $BrandCompany)</span>" } else { '' } $html = @" <!DOCTYPE html> <html lang="$initLang" id="htmlRoot"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title data-i18n="pageTitle">LicenseGuard Raporu</title> <style> *,*::before,*::after{box-sizing:border-box;margin:0;padding:0} :root{--bg:#080d18;--sf:#0f1623;--sf2:#161e2e;--sf3:#1c2740;--bd:#1e293b;--bd2:#263347;--tx:#e2e8f0;--tx2:#94a3b8;--tx3:#475569;--blue:$BrandColor;--green:#22c55e;--yellow:#f59e0b;--red:#ef4444;--r:10px;} html{scroll-behavior:smooth} body{font-family:'Segoe UI',system-ui,sans-serif;background:var(--bg);color:var(--tx);height:100vh;overflow:hidden;font-size:14px;line-height:1.5} .hdr{position:sticky;top:0;z-index:200;background:rgba(8,13,24,.9);backdrop-filter:blur(12px);border-bottom:1px solid var(--bd);padding:0 1.5rem;display:flex;align-items:center;height:54px;gap:1rem} .hdr-brand{display:flex;align-items:center;gap:.55rem;flex:1} .hdr-brand h1{font-size:.95rem;font-weight:700;color:var(--tx);letter-spacing:-.01em} .hdr-brand h1 span{color:var(--blue)} .hdr-meta{font-size:.72rem;color:var(--tx3);white-space:nowrap} .hdr-meta b{color:var(--tx2)} .btn-lang{background:var(--sf2);border:1px solid var(--bd2);color:var(--tx2);padding:.28rem .7rem;border-radius:6px;cursor:pointer;font-size:.7rem;font-weight:700;letter-spacing:.06em;transition:all .15s} .btn-lang:hover{background:var(--sf3);border-color:var(--blue);color:var(--blue)} .layout{display:flex;height:calc(100vh - 54px);overflow:hidden} .sidebar{width:210px;flex-shrink:0;background:var(--sf);border-right:1px solid var(--bd);padding:1.25rem 0;position:sticky;top:54px;height:calc(100vh - 54px);overflow-y:auto} .main{flex:1;min-width:0;padding:1.75rem;height:calc(100vh - 54px);overflow:hidden;display:flex;flex-direction:column} .nav-sec{padding:0 1rem .4rem;font-size:.62rem;font-weight:700;color:var(--tx3);text-transform:uppercase;letter-spacing:.1em;margin-top:.75rem} .nav-item{display:flex;align-items:center;gap:.6rem;padding:.5rem .9rem;margin:.08rem .6rem;border-radius:8px;cursor:pointer;font-size:.8rem;color:var(--tx2);transition:all .15s;user-select:none;border:1px solid transparent} .nav-item:hover{background:var(--sf2);color:var(--tx)} .nav-item.active{background:rgba(59,130,246,.12);color:var(--blue);border-color:rgba(59,130,246,.2);font-weight:600} .nav-icon{font-size:.95rem;width:18px;text-align:center;flex-shrink:0} .nav-badge{margin-left:auto;font-size:.62rem;font-weight:700;padding:.08rem .42rem;border-radius:20px;background:var(--sf3);color:var(--tx3)} .nav-badge.red{background:rgba(239,68,68,.15);color:var(--red)} .nav-badge.yellow{background:rgba(245,158,11,.15);color:var(--yellow)} .nav-divider{height:1px;background:var(--bd);margin:.9rem .6rem} .health-box{margin:.25rem .6rem 1rem;background:var(--sf2);border:1px solid var(--bd2);border-radius:var(--r);padding:.9rem;text-align:center} .health-lbl{font-size:.62rem;color:var(--tx3);text-transform:uppercase;letter-spacing:.07em;margin-bottom:.6rem} .donut-wrap{position:relative;width:76px;height:76px;margin:0 auto .6rem} .donut-wrap svg{width:76px;height:76px} .donut-inner{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center} .donut-pct{font-size:1.05rem;font-weight:700} .donut-sub{font-size:.58rem;color:var(--tx3);text-transform:uppercase;letter-spacing:.05em} .tab-pane{display:none}.tab-pane.active{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden} .sec-head{display:flex;align-items:center;gap:.65rem;margin-bottom:1.1rem;flex-shrink:0} .sec-head h2{font-size:.95rem;font-weight:600;color:var(--tx)} .stat-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:.65rem;margin-bottom:1.35rem;flex-shrink:0} .sc{background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);padding:.9rem 1rem;transition:transform .15s,box-shadow .15s;position:relative;overflow:hidden} .sc::before{content:'';position:absolute;top:0;left:0;right:0;height:2px} .sc.ok::before{background:var(--green)}.sc.warn::before{background:var(--yellow)}.sc.exp::before{background:var(--red)} .sc:hover{transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,0,0,.4)} .sc-ico{font-size:1.1rem;margin-bottom:.4rem} .sc-num{font-size:1.65rem;font-weight:700;line-height:1;margin-bottom:.15rem} .sc.ok .sc-num{color:var(--green)}.sc.warn .sc-num{color:var(--yellow)}.sc.exp .sc-num{color:var(--red)} .sc-lbl{font-size:.65rem;color:var(--tx3);text-transform:uppercase;letter-spacing:.07em} .sc-bar{height:3px;background:var(--bd2);border-radius:2px;margin-top:.6rem;overflow:hidden} .sc-bar-fill{height:100%;border-radius:2px;width:0;transition:width .7s ease} .sc.ok .sc-bar-fill{background:var(--green)}.sc.warn .sc-bar-fill{background:var(--yellow)}.sc.exp .sc-bar-fill{background:var(--red)} .sc-pct{font-size:.62rem;color:var(--tx3);margin-top:.15rem} .toolbar{display:flex;gap:.6rem;align-items:center;margin-bottom:.7rem;flex-wrap:wrap;flex-shrink:0} .search-wrap{position:relative;flex:1;min-width:180px;max-width:380px} .search-ico{position:absolute;left:.6rem;top:50%;transform:translateY(-50%);width:13px;height:13px;fill:var(--tx3);pointer-events:none} .search-input{width:100%;background:var(--sf);border:1px solid var(--bd2);color:var(--tx);padding:.42rem .7rem .42rem 1.9rem;border-radius:8px;font-size:.82rem;outline:none;transition:border-color .15s} .search-input:focus{border-color:var(--blue);box-shadow:0 0 0 3px rgba(59,130,246,.12)} .search-input::placeholder{color:var(--tx3)} .filter-chip{display:flex;align-items:center;gap:.38rem;padding:.4rem .7rem;background:var(--sf);border:1px solid var(--bd2);border-radius:8px;font-size:.76rem;color:var(--tx2);cursor:pointer;user-select:none;transition:all .15s;white-space:nowrap} .filter-chip:has(input:checked){background:rgba(59,130,246,.1);border-color:rgba(59,130,246,.3);color:var(--blue)} .filter-chip input{accent-color:var(--blue);cursor:pointer} .rc{font-size:.72rem;color:var(--tx3);margin-left:auto;white-space:nowrap} .btn-exp{background:var(--sf);border:1px solid var(--bd2);color:var(--tx2);padding:.38rem .7rem;border-radius:8px;cursor:pointer;font-size:.73rem;transition:all .15s;white-space:nowrap} .btn-exp:hover{background:var(--sf3);border-color:var(--blue);color:var(--blue)} .delta-box{background:rgba(59,130,246,.07);border:1px solid rgba(59,130,246,.2);border-radius:8px;padding:.55rem 1rem;margin-bottom:.8rem;font-size:.78rem;color:var(--tx2);flex-shrink:0} .delta-lbl{font-weight:700;color:var(--blue)} .delta-new{color:var(--red);font-weight:600} .delta-ok{color:var(--green);font-weight:600} .table-wrap{border-radius:var(--r);border:1px solid var(--bd);overflow-y:auto;flex:1;min-height:0} table{width:100%;border-collapse:collapse;background:var(--sf);font-size:.82rem} thead tr{background:var(--sf2)}thead th{position:sticky;top:0;z-index:1} th{padding:.6rem 1rem;text-align:left;font-size:.65rem;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--tx3);white-space:nowrap;border-bottom:1px solid var(--bd)} th.sortable{cursor:pointer;user-select:none} th.sortable:hover{color:var(--tx2)} th.sort-asc::after{content:' \2191';color:var(--blue)} th.sort-desc::after{content:' \2193';color:var(--blue)} td{padding:.65rem 1rem;border-top:1px solid var(--bd);vertical-align:middle} tr:hover td{background:var(--sf2)} tr.row-hidden{display:none} .no-results-row td{padding:2.5rem 1rem;text-align:center;color:var(--tx3);font-size:.83rem} tr[data-status="WARN"] td:first-child{border-left:3px solid var(--yellow);padding-left:calc(1rem - 3px)} tr[data-status="EXPIRED"] td:first-child,tr[data-status="ERROR"] td:first-child{border-left:3px solid var(--red);padding-left:calc(1rem - 3px)} tr[data-policy="PROHIBITED"] td:first-child{border-left:3px solid var(--red);padding-left:calc(1rem - 3px)} tr[data-policy="REQUIRES_LICENSE"] td:first-child{border-left:3px solid var(--yellow);padding-left:calc(1rem - 3px)} .badge{display:inline-flex;align-items:center;gap:.22rem;padding:.18rem .52rem;border-radius:20px;font-size:.65rem;font-weight:700;letter-spacing:.04em;white-space:nowrap} .badge::before{content:'';width:5px;height:5px;border-radius:50%;flex-shrink:0} .badge.ok{background:rgba(34,197,94,.12);color:var(--green);border:1px solid rgba(34,197,94,.25)}.badge.ok::before{background:var(--green)} .badge.warn{background:rgba(245,158,11,.12);color:var(--yellow);border:1px solid rgba(245,158,11,.25)}.badge.warn::before{background:var(--yellow)} .badge.expired{background:rgba(239,68,68,.12);color:var(--red);border:1px solid rgba(239,68,68,.25)}.badge.expired::before{background:var(--red)} .mod-pill{display:inline-block;padding:.1rem .45rem;border-radius:4px;font-size:.63rem;font-weight:600;letter-spacing:.04em;background:var(--sf3);color:var(--tx3)} .sw-name{font-weight:500;color:var(--tx)} .sw-meta{font-size:.73rem;color:var(--tx3);margin-top:.08rem} .suggestion{color:var(--blue);font-size:.73rem;display:flex;align-items:flex-start;gap:.3rem;margin-top:.35rem} .reflink{color:var(--blue);text-decoration:none;font-size:.73rem} .reflink:hover{text-decoration:underline} .sev-pill{display:inline-block;padding:.05rem .35rem;border-radius:3px;font-size:.58rem;font-weight:700;letter-spacing:.04em;margin-left:.4rem;vertical-align:middle} .sev-pill.critical{background:rgba(239,68,68,.25);color:#fca5a5} .sev-pill.high{background:rgba(239,68,68,.15);color:var(--red)} .sev-pill.medium{background:rgba(245,158,11,.15);color:var(--yellow)} .sev-pill.low{background:rgba(34,197,94,.08);color:#4ade80} .cal-days{display:inline-block;font-size:.78rem;font-weight:600} .cal-days.urgent{color:var(--red)}.cal-days.warn{color:var(--yellow)}.cal-days.ok{color:var(--green)} footer{padding:.6rem 0;text-align:center;color:var(--tx3);font-size:.7rem;border-top:1px solid var(--bd);flex-shrink:0} @media(max-width:768px){.sidebar{display:none}.main{padding:1rem}.hdr{padding:0 1rem}.hdr-meta{display:none}.stat-grid{grid-template-columns:repeat(3,1fr)}} @media print{body,layout{height:auto;overflow:visible}.tab-pane{display:block!important;height:auto!important;overflow:visible!important;margin-bottom:2rem}.tab-pane:not(.active){display:none!important}.table-wrap{overflow:visible;height:auto;border:none}.sidebar,.toolbar,.hdr .btn-lang{display:none!important}.hdr{position:static}} </style> </head> <body> <header class="hdr"> <div class="hdr-brand"> <span style="font-size:1.15rem">🔐</span> <h1>License<span>Guard</span></h1>$brandSpan </div> <span class="hdr-meta"><b>$hostname</b> · $ts · <span data-i18n="warnThreshold">Uyari esigi</span>: <b>${warnDays} <span data-i18n="days">gun</span></b></span> <button class="btn-lang" id="langBtn" onclick="toggleLang()">EN</button> </header> <div class="layout"> <nav class="sidebar"> <div class="health-box"> <div class="health-lbl" data-i18n="overallHealth">Genel Saglik</div> <div class="donut-wrap"> <svg viewBox="0 0 76 76"> <circle cx="38" cy="38" r="28" fill="none" stroke="#1e293b" stroke-width="7"/> <circle id="donut-ring" cx="38" cy="38" r="28" fill="none" stroke="#3b82f6" stroke-width="7" stroke-linecap="round" stroke-dasharray="0 175.9" transform="rotate(-90 38 38)" style="transition:stroke-dasharray .9s ease,stroke .4s ease"/> </svg> <div class="donut-inner"><span class="donut-pct" id="health-pct" style="color:#3b82f6">-</span><span class="donut-sub">skor</span></div> </div> </div> <div class="nav-sec" data-i18n="sections">Bolumler</div> <div class="nav-item active" onclick="switchTab('license',this)" id="nav-license"><span class="nav-icon">📊</span><span data-i18n="navLicense">Lisans Durumu</span><span class="nav-badge" id="nb1"></span></div> <div class="nav-item" onclick="switchTab('policy',this)" id="nav-policy"><span class="nav-icon">🛡️</span><span data-i18n="navPolicy">Uyumluluk</span><span class="nav-badge" id="nb2"></span></div> <div class="nav-item" onclick="switchTab('calendar',this)" id="nav-calendar"><span class="nav-icon">📅</span><span data-i18n="navCalendar">Takvim</span><span class="nav-badge" id="nb-cal"></span></div> <div class="nav-divider"></div> <div class="nav-sec" data-i18n="modules">Moduller</div> <div class="nav-item" onclick="filterMod(null,this)"><span class="nav-icon">▦</span><span data-i18n="allModules">Tumu</span></div> <div class="nav-item" onclick="filterMod('WindowsActivation',this)"><span class="nav-icon">🖥️</span><span>Windows</span></div> <div class="nav-item" onclick="filterMod('OfficeLicense',this)"><span class="nav-icon">📄</span><span data-i18n="navOffice">Office</span></div> <div class="nav-item" onclick="filterMod('Software',this)"><span class="nav-icon">📦</span><span data-i18n="navSoftware">Yazilimlar</span></div> <div class="nav-item" onclick="filterMod('EOL',this)"><span class="nav-icon">⏱</span><span data-i18n="eolSection">EOL</span></div> <div class="nav-item" onclick="filterMod('FlexLM',this)"><span class="nav-icon">🔑</span><span>FlexLM</span></div> <div class="nav-item" onclick="filterMod('SaaS',this)"><span class="nav-icon">☁️</span><span>SaaS / API</span></div> <div class="nav-item" onclick="filterMod('BrowserExt',this)"><span class="nav-icon">🧩</span><span data-i18n="navBrowserExt">Tarayici</span></div> <div class="nav-item" onclick="filterMod('VSCodeExt',this)"><span class="nav-icon">💻</span><span data-i18n="navVsCode">VS Code</span></div> <div class="nav-item" onclick="filterMod('Startup',this)"><span class="nav-icon">🚀</span><span data-i18n="navStartup">Baslangic</span></div> <div class="nav-item" onclick="filterMod('Process',this)"><span class="nav-icon">⚡</span><span data-i18n="navProcess">Processler</span></div> <div class="nav-item" onclick="filterMod('Signature',this)"><span class="nav-icon">📜</span><span data-i18n="navSignature">Imza</span></div> </nav> <main class="main"> <div class="tab-pane active" id="tab-license"> <div class="sec-head"><span class="sec-icon">📊</span><h2 data-i18n="licenseSection">Lisans Durumu</h2></div> <div class="stat-grid"> <div class="sc ok"><div class="sc-ico">✅</div><div class="sc-num" id="c1ok-n">-</div><div class="sc-lbl" data-i18n="valid">Gecerli</div><div class="sc-bar"><div class="sc-bar-fill" id="c1ok-b"></div></div><div class="sc-pct" id="c1ok-p"></div></div> <div class="sc warn"><div class="sc-ico">⚠️</div><div class="sc-num" id="c1wn-n">-</div><div class="sc-lbl" data-i18n="warning">Uyari</div><div class="sc-bar"><div class="sc-bar-fill" id="c1wn-b"></div></div><div class="sc-pct" id="c1wn-p"></div></div> <div class="sc exp"><div class="sc-ico">🚨</div><div class="sc-num" id="c1er-n">-</div><div class="sc-lbl" data-i18n="errorExp">Hata/Dolmus</div><div class="sc-bar"><div class="sc-bar-fill" id="c1er-b"></div></div><div class="sc-pct" id="c1er-p"></div></div> </div> $deltaBoxHtml <div class="toolbar"> <div class="search-wrap"><svg class="search-ico" viewBox="0 0 16 16"><path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.099zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/></svg><input class="search-input" id="s1" data-table="t1" oninput="doFilter(this)" placeholder="Yazilim ara..."></div> <label class="filter-chip"><input type="checkbox" id="f1" data-table="t1" onchange="doFilterCb(this)"> <span data-i18n="issuesOnly">Sadece sorunlar</span></label> <button class="btn-exp" onclick="exportTableCsv('t1','lg-license.csv')" data-i18n="exportCsv">CSV Indir</button> <button class="btn-exp" onclick="exportTableJson('t1','lg-license.json')" data-i18n="exportJson">JSON Indir</button> <button class="btn-exp" onclick="window.print()" data-i18n="printPdf">PDF Yazdir</button> <span class="rc" id="rc1"></span> </div> <div class="table-wrap"><table id="t1"><thead><tr><th class="sortable" onclick="sortTable('t1',0)">Modul</th><th class="sortable" onclick="sortTable('t1',1)">Ad</th><th>Detay</th><th class="sortable" onclick="sortTable('t1',3)">Durum</th></tr></thead><tbody> $tableRows </tbody></table></div> </div> <div class="tab-pane" id="tab-policy"> <div class="sec-head"><span class="sec-icon">🛡️</span><h2 data-i18n="policySection">Kurumsal Lisans Uyumluluk</h2></div> <div class="stat-grid"> <div class="sc exp"><div class="sc-ico">🚫</div><div class="sc-num" id="c2bn-n">-</div><div class="sc-lbl" data-i18n="banned">Yasak</div><div class="sc-bar"><div class="sc-bar-fill" id="c2bn-b"></div></div><div class="sc-pct" id="c2bn-p"></div></div> <div class="sc warn"><div class="sc-ico">📋</div><div class="sc-num" id="c2lc-n">-</div><div class="sc-lbl" data-i18n="needsLic">Lisans Gerekli</div><div class="sc-bar"><div class="sc-bar-fill" id="c2lc-b"></div></div><div class="sc-pct" id="c2lc-p"></div></div> <div class="sc ok"><div class="sc-ico">✅</div><div class="sc-num" id="c2ok-n">-</div><div class="sc-lbl" data-i18n="compliant">Uyumlu</div><div class="sc-bar"><div class="sc-bar-fill" id="c2ok-b"></div></div><div class="sc-pct" id="c2ok-p"></div></div> </div> <div class="toolbar"> <div class="search-wrap"><svg class="search-ico" viewBox="0 0 16 16"><path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.099zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/></svg><input class="search-input" id="s2" data-table="t2" oninput="doFilter(this)" placeholder="Yazilim ara..."></div> <label class="filter-chip"><input type="checkbox" id="f2" data-table="t2" onchange="doFilterCb(this)"> <span data-i18n="issuesOnly">Sadece sorunlar</span></label> <button class="btn-exp" onclick="exportTableCsv('t2','lg-policy.csv')" data-i18n="exportCsv">CSV Indir</button> <button class="btn-exp" onclick="exportTableJson('t2','lg-policy.json')" data-i18n="exportJson">JSON Indir</button> <span class="rc" id="rc2"></span> </div> <div class="table-wrap"><table id="t2"><thead><tr><th class="sortable" onclick="sortTable('t2',0)">Kategori</th><th class="sortable" onclick="sortTable('t2',1)">Yazilim</th><th>Aciklama / Oneri</th><th class="sortable" onclick="sortTable('t2',3)">Durum</th></tr></thead><tbody> $policyRows </tbody></table></div> </div> <div class="tab-pane" id="tab-calendar"> <div class="sec-head"><span class="sec-icon">📅</span><h2 data-i18n="calSection">Lisans Takvimi</h2></div> <div class="table-wrap"><table id="t-cal"><thead><tr><th>Ad</th><th>Modul</th><th>Tarih</th><th data-i18n="remaining">Kalan</th><th>Durum</th></tr></thead><tbody id="cal-tbody"><tr class="no-results-row"><td colspan="5">—</td></tr></tbody></table></div> </div> <footer>LicenseGuard v$ver · <a href="https://github.com/mustafasercansak/LicenseGuard" target="_blank" style="color:inherit;text-decoration:none">Mustafa Sercan Sak</a></footer> </main> </div> <script> var i18n={ tr:{pageTitle:'LicenseGuard Raporu',licenseSection:'Lisans Durumu',policySection:'Kurumsal Lisans Uyumluluk',searchPh:'Yazilim ara...',issuesOnly:'Sadece sorunlar',valid:'Gecerli',warning:'Uyari',errorExp:'Hata / Dolmus',banned:'Yasak',needsLic:'Lisans Gerekli',compliant:'Uyumlu',warnThreshold:'Uyari esigi',days:'gun',navLicense:'Lisans Durumu',navPolicy:'Uyumluluk',navSoftware:'Yazilimlar',navCalendar:'Takvim',navBrowserExt:'Tarayici Eklentileri',navVsCode:'VS Code',navStartup:'Baslangic',navProcess:'Aktif Processler',navSignature:'Imza',navOffice:'Office',overallHealth:'Genel Saglik',sections:'Bolumler',modules:'Moduller',allModules:'Tumu',rcOf:'{v} / {t} kayit',calSection:'Lisans Takvimi',eolSection:'EOL / Destek Sonu',noExpiry:'Yaklasan bitis tarihi bulunamadi',exportCsv:'CSV Indir',exportJson:'JSON Indir',printPdf:'PDF Yazdir',remaining:'Kalan'}, en:{pageTitle:'LicenseGuard Report',licenseSection:'License Status',policySection:'Corporate License Compliance',searchPh:'Search software...',issuesOnly:'Issues only',valid:'Valid',warning:'Warning',errorExp:'Error / Expired',banned:'Banned',needsLic:'Needs License',compliant:'Compliant',warnThreshold:'Warning threshold',days:'days',navLicense:'License Status',navPolicy:'Compliance',navSoftware:'Software',navCalendar:'Calendar',navBrowserExt:'Browser Extensions',navVsCode:'VS Code',navStartup:'Startup',navProcess:'Running Processes',navSignature:'Signature',navOffice:'Office',overallHealth:'Overall Health',sections:'Sections',modules:'Modules',allModules:'All',rcOf:'{v} / {t} records',calSection:'License Calendar',eolSection:'EOL / End of Support',noExpiry:'No upcoming expiry dates found',exportCsv:'Export CSV',exportJson:'Export JSON',printPdf:'Print PDF',remaining:'Remaining'} }; var currentLang='$initLang',activeMod=null,calendarData=$calJson; function applyLang(lang){currentLang=lang;document.getElementById('htmlRoot').lang=lang;document.getElementById('langBtn').textContent=lang==='tr'?'EN':'TR';document.querySelectorAll('[data-i18n]').forEach(function(el){var k=el.getAttribute('data-i18n');if(i18n[lang][k]!==undefined)el.innerHTML=i18n[lang][k];});document.querySelectorAll('[data-val-tr]').forEach(function(el){el.textContent=lang==='tr'?el.dataset.valTr:el.dataset.valEn;});document.title=i18n[lang].pageTitle;refreshRC();if(document.getElementById('tab-calendar').classList.contains('active')){buildCalendar();}try{localStorage.setItem('lg_lang',lang);}catch(e){}} function toggleLang(){applyLang(currentLang==='tr'?'en':'tr');} function switchTab(id,el){document.querySelectorAll('.tab-pane').forEach(function(p){p.classList.remove('active');});document.querySelectorAll('.nav-item').forEach(function(n){n.classList.remove('active');});document.getElementById('tab-'+id).classList.add('active');el.classList.add('active');if(id==='calendar'){buildCalendar();}} function filterMod(mod,el){activeMod=mod;document.querySelectorAll('.nav-item').forEach(function(n){n.classList.remove('active');});el.classList.add('active');if(mod){document.querySelectorAll('.tab-pane').forEach(function(p){p.classList.remove('active');});document.getElementById('tab-license').classList.add('active');document.getElementById('nav-license').classList.add('active');}applyFilter('t1',document.getElementById('s1').value.toLowerCase(),document.getElementById('f1').checked);} function doFilter(inp){var n=inp.dataset.table.replace('t','');applyFilter(inp.dataset.table,inp.value.toLowerCase(),document.getElementById('f'+n).checked);} function doFilterCb(cb){var n=cb.dataset.table.replace('t','');applyFilter(cb.dataset.table,document.getElementById('s'+n).value.toLowerCase(),cb.checked);} function applyFilter(tid,q,issOnly){var rows=document.querySelectorAll('#'+tid+' tbody tr:not(.no-results-row)');var vis=0;rows.forEach(function(r){var badge=r.querySelector('.badge');var isIssue=badge&&(badge.classList.contains('expired')||badge.classList.contains('warn'));var mp=r.querySelector('.mod-pill');var modOk=!activeMod||!mp||(mp.dataset.mod===activeMod);var show=(!q||r.textContent.toLowerCase().indexOf(q)!==-1)&&(!issOnly||isIssue)&&modOk;r.classList.toggle('row-hidden',!show);if(show)vis++;});var nr=document.querySelector('#'+tid+' .no-results-row');if(!nr){nr=document.createElement('tr');nr.className='no-results-row';nr.innerHTML='<td colspan="99">∅ No results</td>';document.querySelector('#'+tid+' tbody').appendChild(nr);}nr.classList.toggle('row-hidden',vis>0);setRC(tid,vis,rows.length);} function setRC(tid,vis,total){var n=tid.replace('t','');var el=document.getElementById('rc'+n);if(!el)return;var tmpl=(i18n[currentLang]||{}).rcOf||'{v} / {t}';el.textContent=tmpl.replace('{v}',vis).replace('{t}',total);} function refreshRC(){['t1','t2'].forEach(function(tid){var r=document.querySelectorAll('#'+tid+' tbody tr:not(.no-results-row):not(.row-hidden)').length;var t=document.querySelectorAll('#'+tid+' tbody tr:not(.no-results-row)').length;setRC(tid,r,t);});} var sortState={}; function sortTable(tid,ci){var key=tid+':'+ci;var asc=!sortState[key];sortState[key]=asc;var t=document.getElementById(tid);t.querySelectorAll('th.sortable').forEach(function(th){th.classList.remove('sort-asc','sort-desc');});t.querySelectorAll('th')[ci].classList.add(asc?'sort-asc':'sort-desc');var tb=t.querySelector('tbody');var rows=Array.prototype.slice.call(tb.querySelectorAll('tr:not(.no-results-row)'));rows.sort(function(a,b){var av=(a.cells[ci]?a.cells[ci].textContent:'').trim();var bv=(b.cells[ci]?b.cells[ci].textContent:'').trim();return asc?av.localeCompare(bv,undefined,{numeric:true,sensitivity:'base'}):bv.localeCompare(av,undefined,{numeric:true,sensitivity:'base'});});var nr=tb.querySelector('.no-results-row');rows.forEach(function(r){tb.insertBefore(r,nr||null);});} function setCard(pfx,val,total){var pct=Math.round(val/total*100);var n=document.getElementById(pfx+'-n');var p=document.getElementById(pfx+'-p');var b=document.getElementById(pfx+'-b');if(n)n.textContent=val;if(p)p.textContent=pct+'%';if(b)setTimeout(function(){b.style.width=pct+'%';},120);} function updateDonut(pct){var circ=2*Math.PI*28;var dash=circ*pct/100;var el=document.getElementById('donut-ring');var txt=document.getElementById('health-pct');if(!el||!txt)return;var color=pct>=90?'#22c55e':pct>=70?'#f59e0b':'#ef4444';el.style.stroke=color;el.setAttribute('stroke-dasharray',dash+' '+circ);txt.style.color=color;txt.textContent=pct+'%';} function updateAll(){var t1=document.querySelectorAll('#t1 tbody tr:not(.no-results-row)');var ok1=0,wn1=0,er1=0;t1.forEach(function(r){var s=r.getAttribute('data-status');if(s==='OK')ok1++;else if(s==='WARN')wn1++;else er1++;});var lt=t1.length||1;setCard('c1ok',ok1,lt);setCard('c1wn',wn1,lt);setCard('c1er',er1,lt);var t2=document.querySelectorAll('#t2 tbody tr:not(.no-results-row)');var bn=0,lc=0,ok2=0;t2.forEach(function(r){var s=r.getAttribute('data-policy');if(s==='PROHIBITED')bn++;else if(s==='REQUIRES_LICENSE')lc++;else ok2++;});var pt=t2.length||1;setCard('c2bn',bn,pt);setCard('c2lc',lc,pt);setCard('c2ok',ok2,pt);updateDonut(Math.round(ok1/lt*100));var i1=wn1+er1,i2=bn+lc;var nb1=document.getElementById('nb1'),nb2=document.getElementById('nb2');if(nb1){nb1.textContent=i1||'';nb1.className='nav-badge'+(er1>0?' red':wn1>0?' yellow':'');}if(nb2){nb2.textContent=i2||'';nb2.className='nav-badge'+(bn>0?' red':lc>0?' yellow':'');}if(calendarData&&calendarData.length){var urgent=calendarData.filter(function(d){return d.days<=30;}).length;var nbCal=document.getElementById('nb-cal');if(nbCal){nbCal.textContent=urgent||'';nbCal.className='nav-badge'+(urgent>0?' yellow':'');}}refreshRC();} function buildCalendar(){var tbody=document.getElementById('cal-tbody');if(!calendarData||calendarData.length===0){tbody.innerHTML='<tr class="no-results-row"><td colspan="5">'+((i18n[currentLang]||{}).noExpiry||'No data')+'</td></tr>';return;}var sorted=calendarData.slice().sort(function(a,b){return a.date.localeCompare(b.date);});var html='';sorted.forEach(function(item){var d=item.days;var cls=d<0?'expired':d<=30?'warn':'ok';var dclsCal=d<0?'urgent':d<=30?'warn':'ok';var daysTr=d<0?(Math.abs(d)+' gun once'):(d+' gun kaldi');var daysEn=d<0?(Math.abs(d)+' days ago'):(d+' days left');var badgeTr=cls==='ok'?'UYUMLU':cls==='warn'?'UYARI':'SURESI DOLDU';var badgeEn=cls==='ok'?'OK':cls==='warn'?'WARNING':'EXPIRED';html+='<tr data-status="'+(d<0?'EXPIRED':d<=30?'WARN':'OK')+'"><td>'+item.name+'</td><td><span class="mod-pill">'+item.module+'</span></td><td>'+item.date+'</td><td><span class="cal-days '+dclsCal+'" data-val-tr="'+daysTr+'" data-val-en="'+daysEn+'">'+(currentLang==='tr'?daysTr:daysEn)+'</span></td><td><span class="badge '+cls+'" data-val-tr="'+badgeTr+'" data-val-en="'+badgeEn+'">'+(currentLang==='tr'?badgeTr:badgeEn)+'</span></td></tr>';});tbody.innerHTML=html||'<tr class="no-results-row"><td colspan="5">—</td></tr>';} function exportTableCsv(tid,filename){var t=document.getElementById(tid);var hdr=[];t.querySelectorAll('thead th').forEach(function(th){hdr.push('"'+th.textContent.trim().replace(/"/g,'""')+'"');});var rows=[hdr.join(',')];t.querySelectorAll('tbody tr:not(.no-results-row):not(.row-hidden)').forEach(function(tr){var cells=[];tr.querySelectorAll('td').forEach(function(td){cells.push('"'+td.textContent.trim().replace(/"/g,'""')+'"');});rows.push(cells.join(','));});var blob=new Blob(['\uFEFF'+rows.join('\n')],{type:'text/csv;charset=utf-8;'});var a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=filename;a.click();} function exportTableJson(tid,filename){var t=document.getElementById(tid);var hdr=[];t.querySelectorAll('thead th').forEach(function(th){hdr.push(th.textContent.trim());});var rows=[];t.querySelectorAll('tbody tr:not(.no-results-row):not(.row-hidden)').forEach(function(tr){var obj={};tr.querySelectorAll('td').forEach(function(td,i){obj[hdr[i]||('col'+i)]=td.textContent.trim();});rows.push(obj);});var blob=new Blob([JSON.stringify(rows,null,2)],{type:'application/json'});var a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=filename;a.click();} window.addEventListener('load',function(){updateAll();try{var saved=localStorage.getItem('lg_lang');if(saved&&saved!==currentLang)applyLang(saved);}catch(e){}}); </script> </body> </html> "@ $html | Out-File -FilePath $OutputPath -Encoding UTF8 Write-Host "`n $($L['reportSaved']) $OutputPath" -ForegroundColor Cyan } |