#Requires -Version 5.1
# InstaSoft Office Tool v1.14 — PowerShell Edition
# Copyright (c) InstaSoft Informatikai Zrt. 2026
# Office deployment wizard: install, remove, license management
# --- Request admin elevation ---
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Start-Process powershell.exe -ArgumentList "-ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs
exit
}
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName PresentationCore
Add-Type -AssemblyName WindowsBase
Add-Type -AssemblyName System.Net.Http
# ============================================================
# DATA
# ============================================================
$script:Config = @{
Version = 'Office2024'
Edition = $null
Architecture = '64'
Language = 'hu-hu'
ProductKey = ''
ExcludedApps = @()
}
$script:Editions = @{
Office2024 = @(
@{ DisplayName='Standard'; Description='Alapveto irodai alkalmazasok: Word, Excel, PowerPoint, Outlook, OneNote'; ProductId='Standard2024Volume'; Channel='PerpetualVL2024'; IsVolume=$true },
@{ DisplayName='Professional Plus'; Description='Teljes csomag: Word, Excel, PowerPoint, Outlook, Access, Publisher, OneNote'; ProductId='ProPlus2024Volume'; Channel='PerpetualVL2024'; IsVolume=$true },
@{ DisplayName="Otthoni `u{00e9}s kisv`u{00e1}llalati verzi`u{00f3}"; Description="Word, Excel, PowerPoint, Outlook, OneNote `u{2014} v`u{00e1}llalkoz`u{00e1}sokban is haszn`u{00e1}lhat`u{00f3}"; ProductId='HomeBusiness2024Retail'; Channel='Current'; IsVolume=$false }
)
Office2021 = @(
@{ DisplayName='Standard'; Description='Alapveto irodai alkalmazasok: Word, Excel, PowerPoint, Outlook, OneNote'; ProductId='Standard2021Volume'; Channel='PerpetualVL2021'; IsVolume=$true },
@{ DisplayName='Professional Plus'; Description='Teljes csomag: Word, Excel, PowerPoint, Outlook, Access, Publisher, OneNote'; ProductId='ProPlus2021Volume'; Channel='PerpetualVL2021'; IsVolume=$true },
@{ DisplayName="Otthoni `u{00e9}s kisv`u{00e1}llalati verzi`u{00f3}"; Description="Word, Excel, PowerPoint, Outlook, OneNote `u{2014} v`u{00e1}llalkoz`u{00e1}sokban is haszn`u{00e1}lhat`u{00f3}"; ProductId='HomeBusiness2021Retail'; Channel='Current'; IsVolume=$false }
)
Office2019 = @(
@{ DisplayName='Standard'; Description='Alapveto irodai alkalmazasok: Word, Excel, PowerPoint, Outlook, OneNote'; ProductId='Standard2019Volume'; Channel='PerpetualVL2019'; IsVolume=$true },
@{ DisplayName='Professional Plus'; Description='Teljes csomag: Word, Excel, PowerPoint, Outlook, Access, Publisher, OneNote, Skype for Business'; ProductId='ProPlus2019Volume'; Channel='PerpetualVL2019'; IsVolume=$true },
@{ DisplayName="Otthoni `u{00e9}s kisv`u{00e1}llalati verzi`u{00f3}"; Description="Word, Excel, PowerPoint, Outlook, OneNote `u{2014} v`u{00e1}llalkoz`u{00e1}sokban is haszn`u{00e1}lhat`u{00f3}"; ProductId='HomeBusiness2019Retail'; Channel='Current'; IsVolume=$false }
)
}
$script:Languages = @(
@('hu-hu','Magyar'), @('en-us','English (US)'), @('de-de','Deutsch'),
@('fr-fr','Fran\u00e7ais'), @('it-it','Italiano'), @('es-es','Espa\u00f1ol'),
@('pt-pt','Portugu\u00eas'), @('nl-nl','Nederlands'), @('pl-pl','Polski'),
@('cs-cz','\u010ce\u0161tina'), @('sk-sk','Sloven\u010dina'), @('ro-ro','Rom\u00e2n\u0103'),
@('hr-hr','Hrvatski'), @('sl-si','Sloven\u0161\u010dina'), @('sr-latn-rs','Srpski'),
@('bg-bg','Bulgarian'), @('uk-ua','Ukrainian'), @('ru-ru','Russian'),
@('tr-tr','T\u00fcrk\u00e7e'), @('ja-jp','Japanese'), @('zh-cn','Chinese'), @('ko-kr','Korean')
)
$script:ExcludableApps = @(
@{Id='Word'; Name='Word'; Default=$true; Min='all'},
@{Id='Excel'; Name='Excel'; Default=$true; Min='all'},
@{Id='PowerPoint'; Name='PowerPoint'; Default=$true; Min='all'},
@{Id='Outlook'; Name='Outlook'; Default=$true; Min='all'},
@{Id='OneNote'; Name='OneNote'; Default=$true; Min='all'},
@{Id='Access'; Name='Access'; Default=$true; Min='proplus'},
@{Id='Publisher'; Name='Publisher'; Default=$true; Min='standard+'},
@{Id='Teams'; Name='Teams'; Default=$false; Min='all'},
@{Id='Lync'; Name='Skype for Business'; Default=$false; Min='all'}
)
# ============================================================
# XAML — Full UI in one window with panels
# ============================================================
[xml]$xaml = @'
'@
# ============================================================
# CREATE WINDOW
# ============================================================
$reader = New-Object System.Xml.XmlNodeReader $xaml
$window = [Windows.Markup.XamlReader]::Load($reader)
# Find all named elements
$names = @('PanelWelcome','PanelVersion','PanelEdition','PanelConfig','PanelKey','PanelSummary',
'PanelProgress','PanelRemove','PanelLicense',
'BtnInstall','BtnRemoveWelcome','BtnLicenseWelcome','BtnBack','BtnNext','StepDots',
'Rb2024','Rb2021','Rb2019','Rb64','Rb32','CbLanguage','AppChecks',
'EditionSubtitle','EditionCards',
'K1','K2','K3','K4','K5','CbSkipKey',
'SummaryText','XmlPreview',
'ProgressTitle','ProgressStatus','ProgressBar','ProgressLog','DonePanel','DoneText',
'ActivateCard','BtnActivatePost','LaunchCard','LaunchBtns',
'RemoveList','BtnDoRemove',
'LicenseCards','BtnLicRefresh',
'OverlayConfirm','DlgTitle','DlgMsg','DlgOk','DlgCancel')
$el = @{}
foreach ($n in $names) {
$el[$n] = $window.FindName($n)
}
# ============================================================
# SERVICES (PowerShell functions)
# ============================================================
function Show-Panel($name) {
$panels = @('PanelWelcome','PanelVersion','PanelEdition','PanelConfig','PanelKey','PanelSummary','PanelProgress','PanelRemove','PanelLicense')
foreach ($p in $panels) {
$el[$p].Visibility = if ($p -eq $name) { 'Visible' } else { 'Collapsed' }
}
}
$script:CurrentPanel = 'PanelWelcome'
$script:InstallPanels = @('PanelVersion','PanelEdition','PanelConfig','PanelKey','PanelSummary','PanelProgress')
$script:InstallIndex = 0
function Update-StepDots {
$el['StepDots'].Children.Clear()
$total = $script:InstallPanels.Count - 1 # exclude progress
for ($i = 0; $i -lt $total; $i++) {
$dot = New-Object Windows.Shapes.Ellipse
$dot.Margin = [Windows.Thickness]::new(4,0,4,0)
if ($i -le $script:InstallIndex) {
$dot.Width = 10; $dot.Height = 10
$dot.Fill = [Windows.Media.SolidColorBrush]::new([Windows.Media.Color]::FromRgb(0,120,212))
} else {
$dot.Width = 8; $dot.Height = 8
$dot.Fill = [Windows.Media.SolidColorBrush]::new([Windows.Media.Color]::FromRgb(224,224,224))
}
$el['StepDots'].Children.Add($dot) | Out-Null
}
}
function Navigate-Install($index) {
$script:InstallIndex = $index
$panel = $script:InstallPanels[$index]
Show-Panel $panel
$script:CurrentPanel = $panel
if ($panel -eq 'PanelProgress') {
$el['BtnBack'].Visibility = 'Collapsed'
$el['BtnNext'].Visibility = 'Collapsed'
$el['StepDots'].Children.Clear()
} else {
$el['BtnBack'].Visibility = 'Visible'
$el['BtnNext'].Visibility = 'Visible'
$el['BtnNext'].Content = "Tov`u{00e1}bb `u{2192}"
Update-StepDots
}
}
function Get-SelectedVersion {
if ($el['Rb2024'].IsChecked) { return 'Office2024' }
if ($el['Rb2021'].IsChecked) { return 'Office2021' }
if ($el['Rb2019'].IsChecked) { return 'Office2019' }
return 'Office2024'
}
function Get-VersionDisplayName($v) {
switch ($v) {
'Office2024' { 'Office 2024' }
'Office2021' { 'Office 2021' }
'Office2019' { 'Office 2019' }
}
}
function Build-EditionCards {
$el['EditionCards'].Children.Clear()
$ver = Get-SelectedVersion
$script:Config.Version = $ver
$el['EditionSubtitle'].Text = "$(Get-VersionDisplayName $ver) `u{2014} melyik kiad`u{00e1}st szeretn`u{00e9}?"
$eds = $script:Editions[$ver]
for ($i = 0; $i -lt $eds.Count; $i++) {
$ed = $eds[$i]
$rb = New-Object Windows.Controls.RadioButton
$rb.GroupName = 'Edition'
$rb.Style = $window.FindResource('CardRadio')
$rb.Margin = [Windows.Thickness]::new(0,0,0,10)
$rb.IsChecked = ($i -eq 0)
$rb.Tag = $i
$sp = New-Object Windows.Controls.StackPanel
$sp.Margin = [Windows.Thickness]::new(8,2,8,2)
$t1 = New-Object Windows.Controls.TextBlock
$t1.Text = $ed.DisplayName; $t1.FontSize = 18; $t1.FontWeight = 'SemiBold'
$t2 = New-Object Windows.Controls.TextBlock
$t2.Text = $ed.Description; $t2.FontSize = 12
$t2.Foreground = [Windows.Media.SolidColorBrush]::new([Windows.Media.Color]::FromRgb(102,102,102))
$sp.Children.Add($t1) | Out-Null
$sp.Children.Add($t2) | Out-Null
$rb.Content = $sp
$el['EditionCards'].Children.Add($rb) | Out-Null
}
}
function Get-SelectedEdition {
$ver = $script:Config.Version
$eds = $script:Editions[$ver]
foreach ($child in $el['EditionCards'].Children) {
if ($child -is [Windows.Controls.RadioButton] -and $child.IsChecked) {
return $eds[$child.Tag]
}
}
return $eds[0]
}
function Build-AppChecks {
$el['AppChecks'].Children.Clear()
$ed = $script:Config.Edition
$isProPlus = $ed -and $ed.ProductId -match 'ProPlus'
$isStandard = $ed -and $ed.ProductId -match 'Standard'
foreach ($app in $script:ExcludableApps) {
$unavailable = $false
if ($app.Min -eq 'proplus' -and -not $isProPlus) { $unavailable = $true }
elseif ($app.Min -eq 'standard+' -and -not $isProPlus -and -not $isStandard) { $unavailable = $true }
$cb = New-Object Windows.Controls.CheckBox
$cb.Content = $app.Name
$cb.IsChecked = if ($unavailable) { $false } else { $app.Default }
$cb.IsEnabled = -not $unavailable
$cb.Style = $window.FindResource('FluentCB')
$cb.Margin = [Windows.Thickness]::new(0,4,24,4)
$cb.MinWidth = 160
$cb.Tag = $app.Id
$el['AppChecks'].Children.Add($cb) | Out-Null
}
}
function Build-LanguageCombo {
$el['CbLanguage'].Items.Clear()
foreach ($lang in $script:Languages) {
$item = New-Object Windows.Controls.ComboBoxItem
$item.Content = "$($lang[1]) ($($lang[0]))"
$item.Tag = $lang[0]
$el['CbLanguage'].Items.Add($item) | Out-Null
}
$el['CbLanguage'].SelectedIndex = 0
}
function Get-ProductKey {
$parts = @($el['K1'].Text, $el['K2'].Text, $el['K3'].Text, $el['K4'].Text, $el['K5'].Text)
$all = ($parts | Where-Object { $_.Length -eq 5 })
if ($all.Count -eq 5) { return ($parts -join '-').ToUpper() }
return ''
}
function Generate-OdtXml {
$c = $script:Config
$xml = "`n`n"
$xml += " `n"
$xml += " `n"
$xml += " `n"
foreach ($app in $c.ExcludedApps) { $xml += " `n" }
$xml += " `n `n"
$xml += " `n"
$xml += " `n"
$xml += ""
return $xml
}
function Build-Summary {
$c = $script:Config
$keyText = if ($c.ProductKey) { $c.ProductKey } else { 'Nincs megadva' }
$exText = if ($c.ExcludedApps.Count -gt 0) { $c.ExcludedApps -join ', ' } else { "Nincs (minden alkalmazas telepul)" }
$langName = ($script:Languages | Where-Object { $_[0] -eq $c.Language } | Select-Object -First 1)[1]
$el['SummaryText'].Text = "Verzio: $(Get-VersionDisplayName $c.Version)`nKiadas: $($c.Edition.DisplayName)`nArchitektura: $($c.Architecture)-bit`nNyelv: $langName ($($c.Language))`nTermekkulcs: $keyText`nKizart alkalmazasok: $exText"
$el['XmlPreview'].Text = Generate-OdtXml
}
function Append-Log($text) {
$ts = Get-Date -Format 'HH:mm:ss'
$el['ProgressLog'].Text += "$ts $text`n"
$el['ProgressLog'].ScrollToEnd()
[Windows.Threading.Dispatcher]::CurrentDispatcher.Invoke([Action]{}, 'Background')
}
function Download-ODT {
$odtFolder = Join-Path $env:TEMP 'InstaSoftODT'
$setupPath = Join-Path $odtFolder 'setup.exe'
if (Test-Path $setupPath) {
$size = [math]::Round((Get-Item $setupPath).Length / 1MB, 1)
if ((Get-Item $setupPath).Length -gt 1000000) {
Append-Log "Az ODT setup.exe mar elerheto ($size MB)."
return $setupPath
}
}
New-Item -ItemType Directory -Path $odtFolder -Force | Out-Null
Append-Log "Office Deployment Tool letoltese..."
try {
$url = 'https://officecdn.microsoft.com/pr/wsus/setup.exe'
$client = New-Object System.Net.Http.HttpClient
$client.Timeout = [TimeSpan]::FromMinutes(5)
$response = $client.GetAsync($url).Result
if (-not $response.IsSuccessStatusCode) {
Append-Log "Hiba: HTTP $($response.StatusCode)"
return $null
}
$bytes = $response.Content.ReadAsByteArrayAsync().Result
[System.IO.File]::WriteAllBytes($setupPath, $bytes)
$client.Dispose()
Append-Log "Letoltve: $([math]::Round($bytes.Length / 1024)) KB"
return $setupPath
} catch {
Append-Log "Hiba: $($_.Exception.Message)"
return $null
}
}
function Run-Setup($setupPath, $xmlPath) {
Append-Log "setup.exe /configure `"$xmlPath`""
Append-Log "Ez akár 30-40 percet is igénybe vehet..."
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = $setupPath
$psi.Arguments = "/configure `"$xmlPath`""
$psi.UseShellExecute = $false
$psi.CreateNoWindow = $true
$p = [System.Diagnostics.Process]::Start($psi)
$p.WaitForExit()
return $p.ExitCode
}
function Start-Installation {
Show-Panel 'PanelProgress'
$el['BtnBack'].Visibility = 'Collapsed'
$el['BtnNext'].Visibility = 'Collapsed'
$el['StepDots'].Children.Clear()
$el['ProgressLog'].Text = ''
$el['DonePanel'].Visibility = 'Collapsed'
$el['ProgressTitle'].Text = "Telep`u{00ed}t`u{00e9}s folyamatban..."
Append-Log "ODT letoltes indul..."
$setupPath = Download-ODT
if (-not $setupPath) {
$el['ProgressTitle'].Text = "Telep`u{00ed}t`u{00e9}s sikertelen"
$el['DoneText'].Text = "Az ODT letoltese sikertelen."
$el['DoneText'].Foreground = $window.FindResource('ErrorBrush')
$el['DonePanel'].Visibility = 'Visible'
$el['BtnBack'].Visibility = 'Visible'
return
}
Append-Log "Konfiguracios XML generalasa..."
$xml = Generate-OdtXml
$odtFolder = Join-Path $env:TEMP 'InstaSoftODT'
$xmlPath = Join-Path $odtFolder 'configuration.xml'
[System.IO.File]::WriteAllText($xmlPath, $xml)
Append-Log "XML mentve: $xmlPath"
Append-Log "Office telepites inditasa..."
$exitCode = Run-Setup $setupPath $xmlPath
$el['ProgressBar'].IsIndeterminate = $false
$el['ProgressBar'].Value = 100
if ($exitCode -eq 0) {
$el['ProgressTitle'].Text = "Telep`u{00ed}t`u{00e9}s befejezve"
$el['DoneText'].Text = "Az Office sikeresen telepult!"
$el['DoneText'].Foreground = $window.FindResource('SuccessBrush')
if (-not $script:Config.ProductKey) {
$el['ActivateCard'].Visibility = 'Visible'
}
# Launch buttons
$el['LaunchBtns'].Children.Clear()
$apps = @(@('Word','WINWORD.EXE'),@('Excel','EXCEL.EXE'),@('PowerPoint','POWERPNT.EXE'),@('Outlook','OUTLOOK.EXE'))
foreach ($a in $apps) {
if ($script:Config.ExcludedApps -notcontains $a[0]) {
$btn = New-Object Windows.Controls.Button
$btn.Content = $a[0]
$btn.Style = $window.FindResource('SecBtn')
$btn.Padding = [Windows.Thickness]::new(18,8,18,8)
$btn.FontSize = 13
$btn.Margin = [Windows.Thickness]::new(0,0,8,0)
$btn.Tag = $a[1]
$btn.Add_Click({ param($s,$e)
try { Start-Process $s.Tag } catch { }
})
$el['LaunchBtns'].Children.Add($btn) | Out-Null
}
}
$el['LaunchCard'].Visibility = 'Visible'
} else {
$el['ProgressTitle'].Text = "Telep`u{00ed}t`u{00e9}s sikertelen"
$el['DoneText'].Text = "A telepites hibakoddal fejezoedoett be: $exitCode"
$el['DoneText'].Foreground = $window.FindResource('ErrorBrush')
}
Append-Log "setup.exe exit code: $exitCode"
$el['DonePanel'].Visibility = 'Visible'
$el['BtnNext'].Content = "Bez`u{00e1}r`u{00e1}s"
$el['BtnNext'].Visibility = 'Visible'
$script:CurrentPanel = 'done'
}
function Detect-Office {
$results = @()
# Click-to-Run
try {
$c2rKey = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration' -ErrorAction Stop
$ids = $c2rKey.ProductReleaseIds -split ','
foreach ($id in $ids) {
$id = $id.Trim()
if ($id) {
$results += @{
DisplayName = "Microsoft Office Click-to-Run ($id)"
Version = $c2rKey.VersionToReport
ProductCode = $id
IsC2R = $true
}
}
}
} catch {}
# MSI
foreach ($path in @('HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall','HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall')) {
try {
Get-ChildItem $path -ErrorAction Stop | ForEach-Object {
$props = Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue
if ($props.DisplayName -and $props.Publisher -match 'Microsoft' -and ($props.DisplayName -match 'Microsoft Office|Microsoft 365')) {
$isGuid = $_.PSChildName -match '^\{[0-9A-Fa-f\-]+\}$'
$results += @{
DisplayName = $props.DisplayName
Version = $props.DisplayVersion
ProductCode = if ($isGuid) { $_.PSChildName } else { $null }
IsC2R = $false
}
}
}
} catch {}
}
return $results
}
function Find-Ospp {
$paths = @(
"$env:ProgramFiles\Microsoft Office\root\Office16\OSPP.VBS",
"${env:ProgramFiles(x86)}\Microsoft Office\root\Office16\OSPP.VBS",
"$env:ProgramFiles\Microsoft Office\Office16\ospp.vbs",
"${env:ProgramFiles(x86)}\Microsoft Office\Office16\ospp.vbs"
)
# C2R registry path
try {
$c2r = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration' -ErrorAction Stop).InstallationPath
if ($c2r) {
$paths = @(Join-Path $c2r 'root\Office16\OSPP.VBS') + @(Join-Path $c2r 'Office16\OSPP.VBS') + $paths
}
} catch {}
foreach ($p in $paths) {
if (Test-Path $p) { return $p }
}
return $null
}
function Build-LicenseCards {
$el['LicenseCards'].Children.Clear()
$osppPath = Find-Ospp
if (-not $osppPath) {
$tb = New-Object Windows.Controls.TextBlock
$tb.Text = "Az ospp.vbs nem talalhato. Nincs telepitett Office?"
$tb.FontSize = 14; $tb.Foreground = $window.FindResource('ErrorBrush')
$el['LicenseCards'].Children.Add($tb) | Out-Null
return
}
$output = & cscript //Nologo $osppPath /dstatus 2>&1 | Out-String
# Parse entries
$blocks = $output -split '-{10,}'
foreach ($block in $blocks) {
$block = $block.Trim()
if (-not $block -or $block -match '^---') { continue }
$nameMatch = [regex]::Match($block, 'LICENSE NAME:\s*(.+)')
$statusMatch = [regex]::Match($block, 'LICENSE STATUS:\s*(.+)')
$errorMatch = [regex]::Match($block, 'ERROR DESCRIPTION:\s*(.+)')
$keyMatch = [regex]::Match($block, 'Last 5 characters of installed product key:\s*(\S+)')
if ($keyMatch.Success) {
$last5 = $keyMatch.Groups[1].Value
$name = if ($nameMatch.Success) { $nameMatch.Groups[1].Value.Trim() } else { 'Ismeretlen licenc' }
$status = if ($errorMatch.Success) { $errorMatch.Groups[1].Value.Trim() } elseif ($statusMatch.Success) { $statusMatch.Groups[1].Value.Trim() } else { '' }
# Card
$border = New-Object Windows.Controls.Border
$border.Background = [Windows.Media.Brushes]::White
$border.BorderBrush = [Windows.Media.SolidColorBrush]::new([Windows.Media.Color]::FromRgb(224,224,224))
$border.BorderThickness = [Windows.Thickness]::new(1)
$border.CornerRadius = [Windows.CornerRadius]::new(8)
$border.Padding = [Windows.Thickness]::new(16,10,16,10)
$border.Margin = [Windows.Thickness]::new(0,0,0,6)
$grid = New-Object Windows.Controls.Grid
$grid.ColumnDefinitions.Add((New-Object Windows.Controls.ColumnDefinition -Property @{Width='*'}))
$grid.ColumnDefinitions.Add((New-Object Windows.Controls.ColumnDefinition -Property @{Width='Auto'}))
$info = New-Object Windows.Controls.StackPanel
$t1 = New-Object Windows.Controls.TextBlock; $t1.Text = $name; $t1.FontSize = 13; $t1.FontWeight = 'SemiBold'
$t2 = New-Object Windows.Controls.TextBlock; $t2.Text = $status; $t2.FontSize = 11; $t2.Foreground = [Windows.Media.SolidColorBrush]::new([Windows.Media.Color]::FromRgb(102,102,102))
$t3 = New-Object Windows.Controls.TextBlock; $t3.Text = "Kulcs: *****-$last5"; $t3.FontSize = 11; $t3.FontFamily = 'Consolas'
$t3.Foreground = [Windows.Media.SolidColorBrush]::new([Windows.Media.Color]::FromRgb(102,102,102))
$info.Children.Add($t1) | Out-Null; $info.Children.Add($t2) | Out-Null; $info.Children.Add($t3) | Out-Null
[Windows.Controls.Grid]::SetColumn($info, 0)
$grid.Children.Add($info) | Out-Null
$btnPanel = New-Object Windows.Controls.StackPanel
$btnPanel.Orientation = 'Horizontal'
$btnPanel.VerticalAlignment = 'Center'
$rmBtn = New-Object Windows.Controls.Button
$rmBtn.Content = "Eltavolitas"
$rmBtn.Style = $window.FindResource('SecBtn')
$rmBtn.Padding = [Windows.Thickness]::new(14,6,14,6)
$rmBtn.FontSize = 12
$rmBtn.Tag = @{ Last5 = $last5; OsppPath = $osppPath }
$rmBtn.Add_Click({
param($s,$e)
$info = $s.Tag
& cscript //Nologo $info.OsppPath "/unpkey:$($info.Last5)" 2>&1 | Out-Null
Build-LicenseCards
})
$btnPanel.Children.Add($rmBtn) | Out-Null
[Windows.Controls.Grid]::SetColumn($btnPanel, 1)
$grid.Children.Add($btnPanel) | Out-Null
$border.Child = $grid
$el['LicenseCards'].Children.Add($border) | Out-Null
}
}
if ($el['LicenseCards'].Children.Count -eq 0) {
$tb = New-Object Windows.Controls.TextBlock
$tb.Text = "Nincs telepitett termekkulcs."
$tb.FontSize = 13; $tb.Foreground = [Windows.Media.SolidColorBrush]::new([Windows.Media.Color]::FromRgb(102,102,102))
$el['LicenseCards'].Children.Add($tb) | Out-Null
}
}
# ============================================================
# EVENT HANDLERS
# ============================================================
# Welcome buttons
$el['BtnInstall'].Add_Click({
$script:InstallIndex = 0
Build-LanguageCombo
Navigate-Install 0
})
$el['BtnRemoveWelcome'].Add_Click({
Show-Panel 'PanelRemove'
$el['BtnBack'].Visibility = 'Visible'
$el['BtnNext'].Visibility = 'Collapsed'
$el['StepDots'].Children.Clear()
$script:CurrentPanel = 'PanelRemove'
# Detect office
$el['RemoveList'].Children.Clear()
$detected = Detect-Office
if ($detected.Count -eq 0) {
$tb = New-Object Windows.Controls.TextBlock
$tb.Text = "Nem talalhato telepitett Office."
$tb.FontSize = 14; $tb.Foreground = [Windows.Media.SolidColorBrush]::new([Windows.Media.Color]::FromRgb(102,102,102))
$el['RemoveList'].Children.Add($tb) | Out-Null
$el['BtnDoRemove'].IsEnabled = $false
} else {
foreach ($o in $detected) {
$cb = New-Object Windows.Controls.CheckBox
$cb.Content = "$($o.DisplayName) ($($o.Version))"
$cb.IsChecked = $true
$cb.Style = $window.FindResource('FluentCB')
$cb.Tag = $o
$cb.Margin = [Windows.Thickness]::new(0,4,0,4)
$el['RemoveList'].Children.Add($cb) | Out-Null
}
}
})
$el['BtnLicenseWelcome'].Add_Click({
Show-Panel 'PanelLicense'
$el['BtnBack'].Visibility = 'Visible'
$el['BtnNext'].Visibility = 'Collapsed'
$el['StepDots'].Children.Clear()
$script:CurrentPanel = 'PanelLicense'
Build-LicenseCards
})
# Back button
$el['BtnBack'].Add_Click({
$window.Height = 550
if ($script:CurrentPanel -eq 'done') {
$window.Close()
return
}
if ($script:InstallPanels -contains $script:CurrentPanel -and $script:InstallIndex -gt 0) {
Navigate-Install ($script:InstallIndex - 1)
} else {
Show-Panel 'PanelWelcome'
$script:CurrentPanel = 'PanelWelcome'
$el['BtnBack'].Visibility = 'Collapsed'
$el['BtnNext'].Visibility = 'Collapsed'
$el['StepDots'].Children.Clear()
}
})
# Next button
$el['BtnNext'].Add_Click({
if ($script:CurrentPanel -eq 'done') {
$window.Close()
return
}
# Validate + advance
switch ($script:CurrentPanel) {
'PanelVersion' {
$script:Config.Version = Get-SelectedVersion
Build-EditionCards
Navigate-Install 1
}
'PanelEdition' {
$script:Config.Edition = Get-SelectedEdition
Build-AppChecks
Navigate-Install 2
}
'PanelConfig' {
$script:Config.Architecture = if ($el['Rb64'].IsChecked) { '64' } else { '32' }
$sel = $el['CbLanguage'].SelectedItem
if ($sel) { $script:Config.Language = $sel.Tag }
$script:Config.ExcludedApps = @()
foreach ($child in $el['AppChecks'].Children) {
if ($child -is [Windows.Controls.CheckBox] -and (-not $child.IsChecked -or -not $child.IsEnabled)) {
$script:Config.ExcludedApps += $child.Tag
}
}
Navigate-Install 3
}
'PanelKey' {
if ($el['CbSkipKey'].IsChecked) {
$script:Config.ProductKey = ''
} else {
$script:Config.ProductKey = Get-ProductKey
}
Build-Summary
Navigate-Install 4
}
'PanelSummary' {
Start-Installation
}
}
})
# License refresh
$el['BtnLicRefresh'].Add_Click({ Build-LicenseCards })
# Key auto-tab
$keyBoxes = @($el['K1'],$el['K2'],$el['K3'],$el['K4'],$el['K5'])
for ($i = 0; $i -lt $keyBoxes.Count; $i++) {
$box = $keyBoxes[$i]
$idx = $i
$box.Add_TextChanged({
param($s,$e)
if ($s.Text.Length -eq 5 -and $idx -lt 4) {
$keyBoxes[$idx + 1].Focus()
}
}.GetNewClosure())
# Paste handler
[Windows.DataObject]::AddPastingHandler($box, [Windows.DataObjectPastingEventHandler]{
param($s,$e)
if ($e.DataObject.GetDataPresent([string])) {
$pasted = $e.DataObject.GetData([string])
$clean = $pasted -replace '[^A-Za-z0-9]',''
if ($clean.Length -gt 5) {
$e.CancelCommand()
for ($j = 0; $j -lt 5; $j++) {
$start = $j * 5
if ($start -lt $clean.Length) {
$len = [Math]::Min(5, $clean.Length - $start)
$keyBoxes[$j].Text = $clean.Substring($start, $len).ToUpper()
}
}
$keyBoxes[4].Focus()
}
}
})
}
# Skip key checkbox
$el['CbSkipKey'].Add_Checked({ foreach ($b in $keyBoxes) { $b.IsEnabled = $false; $b.Text = '' } })
$el['CbSkipKey'].Add_Unchecked({ foreach ($b in $keyBoxes) { $b.IsEnabled = $true } })
# ============================================================
# RUN
# ============================================================
$window.ShowDialog() | Out-Null