Add test/Samy.UI.ps1

This commit is contained in:
2025-12-09 23:00:51 -05:00
parent 8eca393cbb
commit db8fcb509a

348
test/Samy.UI.ps1 Normal file
View File

@@ -0,0 +1,348 @@
# Samy.UI.ps1
# Task metadata and UI HTML generation
# Global task definitions used by UI and headless offboard
$Global:SamyTasks = @(
# On-Boarding, left column
@{ Id='setSVSPowerplan'; Name='setSVSPowerplan'; Label='Set SVS Powerplan'; HandlerFn='Invoke-setSVSPowerPlan'; Page='onboard'; Column='left' },
@{ Id='installSVSMSPModule'; Name='installSVSMSPModule'; Label='Install SVSMSP Module'; HandlerFn='Invoke-InstallSVSMSP'; Page='onboard'; Column='left' },
@{ Id='installCyberQP'; Name='installCyberQP'; Label='Install CyberQP'; HandlerFn='Invoke-InstallCyberQP'; Page='onboard'; Column='left' },
@{ Id='installHelpDesk'; Name='installHelpDesk'; Label='Install HelpDesk'; HandlerFn='Invoke-InstallHelpDesk'; Page='onboard'; Column='left' },
@{ Id='installThreatLocker'; Name='installThreatLocker'; Label='Install ThreatLocker'; HandlerFn='Invoke-InstallThreatLocker'; Page='onboard'; Column='left' },
@{ Id='installRocketCyber'; Name='installRocketCyber'; Label='Install RocketCyber'; HandlerFn='Invoke-InstallRocketCyber'; Page='onboard'; Column='left' },
@{ Id='installDattoRMM'; Name='installDattoRMM'; Label='Install DattoRMM'; HandlerFn='Invoke-InstallDattoRMM'; Page='onboard'; Column='left';
SubOptions= @(
@{ Value='inputVar'; Label='Copy Site Variables' },
@{ Value='rmm'; Label='Install RMM Agent' },
@{ Value='exe'; Label='Download Executable' }
)
},
# On-Boarding, right column
@{ Id='enableBitLocker'; Name='EnableBitLocker'; Label='Enable BitLocker'; HandlerFn='Set-SVSBitLocker'; Page='onboard'; Column='right' },
@{ Id='setEdgeDefaultSearch'; Name='setedgedefaultsearch'; Label='Set Edge Default Search'; Tooltip='Will configure Edge to use Google as default search provider'; HandlerFn='Invoke-SetEdgeDefaultSearchEngine'; Page='onboard'; Column='right' },
# Off-Boarding
@{ Id='offUninstallCyberQP'; Name='offUninstallCyberQP'; Label='Uninstall CyberQP'; HandlerFn='Invoke-UninstallCyberQP'; Page='offboard' },
@{ Id='offUninstallHelpDesk'; Name='offUninstallHelpDesk'; Label='Uninstall HelpDesk'; HandlerFn='Invoke-UninstallHelpDesk'; Page='offboard' },
@{ Id='offUninstallThreatLocker'; Name='offUninstallThreatLocker'; Label='Uninstall ThreatLocker'; HandlerFn='Invoke-UninstallThreatLocker'; Page='offboard' },
@{ Id='offUninstallRocketCyber'; Name='offUninstallRocketCyber'; Label='Uninstall RocketCyber'; HandlerFn='Invoke-UninstallRocketCyber'; Page='offboard' },
@{ Id='offCleanupSVSMSPModule'; Name='offCleanupSVSMSPModule'; Label='Cleanup SVSMSP Toolkit'; HandlerFn='Invoke-CleanupSVSMSP'; Page='offboard' },
# Tweaks
@{ Id='disableAnimations'; Name='disableAnimations'; Label='Disable Animations'; HandlerFn='Disable-Animations'; Page='tweaks' },
# SVS Apps
@{ Id='wingetLastpass'; Name='wingetLastpass'; Label='LastPass Desktop App'; HandlerFn='Install-WingetLastPass'; Page='SVSApps' },
@{ Id='wingetChrome'; Name='wingetChrome'; Label='Google Chrome'; HandlerFn='Invoke-InstallChrome'; Page='SVSApps' },
@{ Id='wingetAcrobat'; Name='wingetAcrobat'; Label='Adobe Acrobat Reader (64-bit)'; HandlerFn='Invoke-InstallAcrobat'; Page='SVSApps' }
)
Write-LogHybrid "Tasks by page: onboard=$(
($Global:SamyTasks | Where-Object Page -eq 'onboard').Count
) offboard=$(
($Global:SamyTasks | Where-Object Page -eq 'offboard').Count
) tweaks=$(
($Global:SamyTasks | Where-Object Page -eq 'tweaks').Count
) apps=$(
($Global:SamyTasks | Where-Object Page -eq 'SVSApps').Count
)" Info UI -LogToEvent
function Publish-Checkboxes {
param(
[Parameter(Mandatory)][string]$Page,
[string]$Column
)
$tasks = $Global:SamyTasks | Where-Object Page -EQ $Page
if (-not [string]::IsNullOrEmpty($Column)) {
$tasks = $tasks | Where-Object Column -EQ $Column
}
(
$tasks |
ForEach-Object {
$taskId = $_.Id
$tooltip = if ($_.PSObject.Properties.Name -contains 'Tooltip' -and $_.Tooltip) {
" title='$($_.Tooltip)'"
} else { '' }
$html = "<label$tooltip><input type='checkbox' id='$taskId' name='$($_.Name)' data-column='$Column'> $($_.Label)</label>"
if ($_.SubOptions) {
$subHtml = (
$_.SubOptions |
ForEach-Object {
"<label style='margin-left:20px; display:block;'>
<input type='checkbox' class='sub-option-$taskId' name='$($_.Value)' value='$($_.Value)'> $($_.Label)
</label>"
}
) -join "`n"
$html += @"
<div id='${taskId}OptionsContainer' style='display:none; margin-top:4px;'>
$subHtml
</div>
"@
}
$html
}
) -join "`n"
}
function Get-ModuleVersionHtml {
$mod = Get-Module -ListAvailable -Name SVSMSP | Sort-Object Version -Descending | Select-Object -First 1
$branchDisplay = switch ($Script:SamyBranch.ToLower()) {
'main' { 'Main / Stable' }
'beta' { 'Beta' }
default { $Script:SamyBranch }
}
if ($mod) {
return "<div style='color:#bbb; font-size:0.9em; margin-top:1em;'>
Module Version: $($mod.Version)<br>
UI Branch: $branchDisplay
</div>"
}
return "<div style='color:#f66;'>SVSMSP_Module not found</div>"
}
function Get-RemoteText {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][string]$Url
)
try {
$resp = Invoke-WebRequest -Uri $Url -UseBasicParsing -ErrorAction Stop
return $resp.Content
}
catch {
Write-LogHybrid "Get-RemoteText failed for ${Url}: $($_.Exception.Message)" Warning UI -LogToEvent
return ""
}
}
function Get-UIHtml {
param([string]$Page = 'onboard')
if (-not $Page) { $Page = 'onboard' }
$onboardLeft = Publish-Checkboxes -Page 'onboard' -Column 'left'
$onboardRight = Publish-Checkboxes -Page 'onboard' -Column 'right'
$offboard = Publish-Checkboxes -Page 'offboard' -Column ''
$tweaks = Publish-Checkboxes -Page 'tweaks' -Column ''
$apps = Publish-Checkboxes -Page 'SVSApps' -Column ''
$tasksJsAll = (
$Global:SamyTasks | ForEach-Object {
" { id: '$($_.Id)', handler: '/$($_.Name)', label: '$($_.Label)' }"
}
) -join ",`n"
$cssContent = Get-RemoteText -Url $Script:SamyCssUrl
$jsContent = Get-RemoteText -Url $Script:SamyJsUrl
if ($cssContent) {
$pattern = 'background-image:\s*url\("SAMY\.png"\);?'
$replacement = "background-image: url('$Script:SamyBgLogoUrl');"
$cssContent = [regex]::Replace($cssContent, $pattern, $replacement)
}
$htmlTemplate = @"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Script Automation Monkey</title>
<link rel="icon" href="$Script:SamyFaviconUrl">
<style>
$cssContent
</style>
</head>
<body>
<div class="logo-container">
<div class="logo-left">
<img src="$Script:SamyTopLogoUrl" alt="SVS Logo">
{{moduleVersion}}
</div>
<div id="tagline" class="tagline">
Script Automation Monkey (Yeah!)
</div>
<div id="samyHint" class="samy-hint"></div>
</div>
<div class="container">
<div class="sidebar">
<button class="tab-button" data-tab="onboardTab">On-Boarding</button>
<button class="tab-button" data-tab="offboardTab">Off-Boarding</button>
<button class="tab-button" data-tab="tweaksTab">Tweaks</button>
<button class="tab-button" data-tab="SVSAppsTab">SVS APPs</button>
<button class="tab-button" data-tab="devicesTab">Devices</button>
<div id="status-box" style="margin-top: 1em; font-family: monospace;"></div>
</div>
<div class="content">
<div id="onboardTab" class="tab-content">
<h2>On-Boarding</h2>
<h3 class="subtitle">This new deployment method ensures everything is successfully deployed with greater ease!</h3>
<div class="columns-container">
<div class="checkbox-group column">
<h3>SVSMSP Stack</h3>
<label><input type="checkbox" id="selectAllLeftCheckbox" onclick="toggleColumn('left')"> Select All</label>
{{onboardLeftColumn}}
</div>
<div class="checkbox-group column">
<h3>Optional</h3>
<label><input type="checkbox" id="selectAllRightCheckbox" onclick="toggleColumn('right')"> Select All</label>
{{onboardRightColumn}}
<div class="option">
<label>
<input type="checkbox" id="chkRenameComputer" data-column="right">
Rename Computer
</label>
</div>
<div id="renameComputerBlock" style="display:none; margin-left: 24px; margin-top: 6px;">
<label for="txtNewComputerName">New computer name:</label>
<input type="text" id="txtNewComputerName" placeholder="e.g. CORP-LAP-123" />
<small style="display:block; margin-top:4px;">
(Max 15 chars; letters, numbers, and hyphens only.)
</small>
</div>
</div>
</div>
<div id="PasswordContainer" style="display:none; margin-bottom:1em;">
<label for="Password">Enter Password:</label>
<div style="display:flex; gap:5px;">
<input type="password" id="Password" placeholder="Enter Password" style="flex:1;" />
<button onclick="fetchSites()" class="go-button">GO!</button>
</div>
</div>
<div id="dattoRmmContainer" style="display:none; margin-bottom:1em;">
<label for="dattoDropdown">Select a Datto RMM site:</label>
<select id="dattoDropdown" style="width:100%;">
<option disabled selected>Fetching sites...</option>
</select>
</div>
</div>
<div id="offboardTab" class="tab-content">
<h2>Off-Boarding</h2>
<div class="columns-container">
<div class="checkbox-group column">
<h3>Remove Stack</h3>
<label>
<input type="checkbox" id="offboardSelectAll" onclick="toggleOffboardAll()">
Select All
</label>
{{offboardCheckboxes}}
</div>
</div>
</div>
<div id="tweaksTab" class="tab-content">
<h2>Tweaks</h2>
<div class="columns-container">
<div class="checkbox-group column">
<h3>Tweaks</h3>
{{tweaksCheckboxes}}
</div>
</div>
</div>
<div id="SVSAppsTab" class="tab-content">
<h2>SVS APPs</h2>
<div class="columns-container">
<div class="checkbox-group column">
<h3>Applications</h3>
{{appsCheckboxes}}
</div>
</div>
</div>
<div id="devicesTab" class="tab-content">
<h2>Devices</h2>
<h3 class="subtitle">Manage printers and other client devices.</h3>
<div id="printerPasswordContainer" style="margin-bottom:1em;">
<label for="PrinterPassword">Enter Printer Password:</label>
<div style="display:flex; gap:5px;">
<input type="password"
id="PrinterPassword"
placeholder="Enter printer password"
style="flex:1;" />
<button onclick="fetchPrinters()" class="go-button">Get Printers</button>
</div>
</div>
<div id="printerClientContainer" style="display:none; margin-bottom:1em;">
<label for="printerClientDropdown">Select Client:</label>
<select id="printerClientDropdown" style="width:100%;">
<option disabled selected>Fetch printers first...</option>
</select>
</div>
<div id="printerListContainer" style="display:none; margin-bottom:1em;">
<label>Printers for selected client:</label>
<small style="display:block; margin-bottom:4px; opacity:0.8;">
Check the printers to install, and mark one as "Make default" (optional).
</small>
<div id="printerCheckboxContainer"
style="max-height:200px; overflow-y:auto; border:1px solid #444; padding:6px; border-radius:4px;">
</div>
<button id="installPrintersButton"
class="go-button"
style="margin-top:8px;"
onclick="installSelectedPrinters()">
Install Selected Printers
</button>
</div>
</div>
</div>
</div>
<script>
window.SAMY_TASKS = [
{{tasksJsAll}}
];
window.SAMY_DEFAULT_PAGE = "{{defaultPage}}";
</script>
<script>
$jsContent
</script>
<div class="fixed-buttons">
<button class="exit-button" onclick="endSession()">Exit</button>
<button class="run-button" onclick="triggerInstall()">Run Selected</button>
</div>
</body>
</html>
"@
$html = $htmlTemplate
$html = $html.Replace('{{moduleVersion}}', (Get-ModuleVersionHtml))
$html = $html.Replace('{{onboardLeftColumn}}', $onboardLeft)
$html = $html.Replace('{{onboardRightColumn}}', $onboardRight)
$html = $html.Replace('{{offboardCheckboxes}}', $offboard)
$html = $html.Replace('{{tweaksCheckboxes}}', $tweaks)
$html = $html.Replace('{{appsCheckboxes}}', $apps)
$html = $html.Replace('{{tasksJsAll}}', $tasksJsAll)
$html = $html.Replace('{{defaultPage}}', $Page)
return $html
}