Files
OfficeTool/powershell/InstaSoftOfficeTool.ps1
hariel1985 ae4d7f82bc v1.15 — PowerShell edition + repo restructure
- New powershell/InstaSoftOfficeTool.ps1: single-file WPF GUI version
  - Same Fluent Design UI, no compilation needed
  - Runs on any Windows 7+ with PowerShell 5.1 (built-in)
  - Chrome won't flag .ps1 files as "rarely downloaded"
  - Auto-elevates to admin
- Moved C# source to src/ subfolder
- Updated .gitignore for nested bin/obj folders

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 17:58:15 +02:00

1156 sor
60 KiB
PowerShell

#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 = @'
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="InstaSoft Office Tool" Width="800" Height="550"
ResizeMode="NoResize" WindowStartupLocation="CenterScreen"
Background="#F3F3F3" TextElement.Foreground="#1A1A1A">
<Window.Resources>
<SolidColorBrush x:Key="AccentBrush" Color="#0078D4"/>
<SolidColorBrush x:Key="AccentHoverBrush" Color="#106EBE"/>
<SolidColorBrush x:Key="CardBrush" Color="#FFFFFF"/>
<SolidColorBrush x:Key="BorderBrush" Color="#E0E0E0"/>
<SolidColorBrush x:Key="TextSecondary" Color="#666666"/>
<SolidColorBrush x:Key="SuccessBrush" Color="#107C10"/>
<SolidColorBrush x:Key="ErrorBrush" Color="#D13438"/>
<SolidColorBrush x:Key="WarningBrush" Color="#CA5010"/>
<Style x:Key="Title" TargetType="TextBlock">
<Setter Property="FontSize" Value="24"/>
<Setter Property="FontWeight" Value="Light"/>
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI"/>
</Style>
<Style x:Key="Subtitle" TargetType="TextBlock">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Foreground" Value="{StaticResource TextSecondary}"/>
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI"/>
</Style>
<Style x:Key="PrimaryBtn" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" Background="#0078D4" CornerRadius="4"
Padding="24,10" SnapsToDevicePixels="True"
TextElement.Foreground="White">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="#106EBE"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="bd" Property="Background" Value="#005A9E"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="bd" Property="Opacity" Value="0.4"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SecBtn" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" Background="Transparent" BorderBrush="#E0E0E0"
BorderThickness="1" CornerRadius="4" Padding="24,10"
SnapsToDevicePixels="True">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="#F8F8F8"/>
<Setter TargetName="bd" Property="BorderBrush" Value="#0078D4"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="bd" Property="Opacity" Value="0.4"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CardBtn" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" Background="White" BorderBrush="#E0E0E0"
BorderThickness="1" CornerRadius="8" Padding="20,24"
SnapsToDevicePixels="True">
<Border.Effect>
<DropShadowEffect ShadowDepth="1" BlurRadius="8" Opacity="0.08"/>
</Border.Effect>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="BorderBrush" Value="#0078D4"/>
<Setter TargetName="bd" Property="Background" Value="#FAFCFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CardRadio" TargetType="RadioButton">
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border x:Name="bd" Background="White" BorderBrush="#E0E0E0"
BorderThickness="1" CornerRadius="8" Padding="16,14"
SnapsToDevicePixels="True">
<Border.Effect>
<DropShadowEffect ShadowDepth="1" BlurRadius="6" Opacity="0.06"/>
</Border.Effect>
<ContentPresenter VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="BorderBrush" Value="#0078D4"/>
<Setter TargetName="bd" Property="Background" Value="#FAFCFF"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="bd" Property="BorderBrush" Value="#0078D4"/>
<Setter TargetName="bd" Property="BorderThickness" Value="2"/>
<Setter TargetName="bd" Property="Background" Value="#F0F6FF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="FluentCB" TargetType="CheckBox">
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Border x:Name="rb" Background="Transparent" Padding="6,8" CornerRadius="6">
<StackPanel Orientation="Horizontal">
<Border x:Name="cb" Width="20" Height="20" CornerRadius="4"
BorderThickness="2" BorderBrush="#E0E0E0" Background="White"
VerticalAlignment="Center" Margin="0,0,10,0">
<TextBlock x:Name="cm" Text="&#xE73E;" FontFamily="Segoe MDL2 Assets"
FontSize="12" HorizontalAlignment="Center"
VerticalAlignment="Center" Foreground="White" Visibility="Collapsed"/>
</Border>
<ContentPresenter VerticalAlignment="Center"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="rb" Property="Background" Value="#F5F5F5"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="cb" Property="Background" Value="#0078D4"/>
<Setter TargetName="cb" Property="BorderBrush" Value="#0078D4"/>
<Setter TargetName="cm" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="rb" Property="Opacity" Value="0.5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid x:Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="64"/>
<RowDefinition Height="*"/>
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<!-- Header -->
<Border Grid.Row="0" Background="#FAFAFA" BorderBrush="#E0E0E0" BorderThickness="0,0,0,1">
<Grid Margin="24,0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Grid Width="24" Height="24" Margin="0,0,12,0">
<Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions>
<Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions>
<Rectangle Grid.Row="0" Grid.Column="0" Fill="#F35325" Margin="0,0,1,1"/>
<Rectangle Grid.Row="0" Grid.Column="1" Fill="#81BC06" Margin="1,0,0,1"/>
<Rectangle Grid.Row="1" Grid.Column="0" Fill="#05A6F0" Margin="0,1,1,0"/>
<Rectangle Grid.Row="1" Grid.Column="1" Fill="#FFBA08" Margin="1,1,0,0"/>
</Grid>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="InstaSoft Office Tool" FontSize="16" FontWeight="SemiBold"
FontFamily="Segoe UI Variable Display, Segoe UI"/>
<TextBlock Text="Microsoft Partner" FontSize="11"
Foreground="{StaticResource TextSecondary}" Margin="0,-2,0,0"/>
</StackPanel>
</StackPanel>
<TextBlock Text="v1.14" HorizontalAlignment="Right" VerticalAlignment="Center"
FontSize="12" Foreground="{StaticResource TextSecondary}"/>
</Grid>
</Border>
<!-- Content panels -->
<Grid Grid.Row="1" x:Name="ContentArea">
<!-- Welcome -->
<Grid x:Name="PanelWelcome" Margin="40,30">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Margin="0,0,0,30">
<TextBlock Text="&#220;dv&#246;z&#246;lj&#252;k!" Style="{StaticResource Title}"/>
<TextBlock Text="V&#225;lassza ki a k&#237;v&#225;nt m&#369;veletet:" Style="{StaticResource Subtitle}" Margin="0,4,0,0"/>
</StackPanel>
<UniformGrid Grid.Row="1" Columns="3">
<Button x:Name="BtnInstall" Style="{StaticResource CardBtn}" Margin="0,0,10,0">
<StackPanel>
<TextBlock FontSize="28" Text="&#xE710;" FontFamily="Segoe MDL2 Assets"
Foreground="{StaticResource AccentBrush}" Margin="0,0,0,12"/>
<TextBlock Text="Office telep&#237;t&#233;s" FontSize="16" FontWeight="SemiBold"/>
<TextBlock Text="&#218;j Microsoft Office telep&#237;t&#233;se" FontSize="12"
Foreground="{StaticResource TextSecondary}" TextWrapping="Wrap" Margin="0,6,0,0"/>
</StackPanel>
</Button>
<Button x:Name="BtnRemoveWelcome" Style="{StaticResource CardBtn}" Margin="5,0,5,0">
<StackPanel>
<TextBlock FontSize="28" Text="&#xE74D;" FontFamily="Segoe MDL2 Assets"
Foreground="{StaticResource WarningBrush}" Margin="0,0,0,12"/>
<TextBlock Text="Office elt&#225;vol&#237;t&#225;s" FontSize="16" FontWeight="SemiBold"/>
<TextBlock Text="Megl&#233;v&#337; Office elt&#225;vol&#237;t&#225;sa" FontSize="12"
Foreground="{StaticResource TextSecondary}" TextWrapping="Wrap" Margin="0,6,0,0"/>
</StackPanel>
</Button>
<Button x:Name="BtnLicenseWelcome" Style="{StaticResource CardBtn}" Margin="10,0,0,0">
<StackPanel>
<TextBlock FontSize="28" Text="&#xE8D7;" FontFamily="Segoe MDL2 Assets"
Foreground="{StaticResource SuccessBrush}" Margin="0,0,0,12"/>
<TextBlock Text="Licenc-kezel&#233;s" FontSize="16" FontWeight="SemiBold"/>
<TextBlock Text="Licenc &#225;llapot, aktiv&#225;l&#225;s, kulcs t&#246;rl&#233;s" FontSize="12"
Foreground="{StaticResource TextSecondary}" TextWrapping="Wrap" Margin="0,6,0,0"/>
</StackPanel>
</Button>
</UniformGrid>
</Grid>
<!-- Version selection -->
<Grid x:Name="PanelVersion" Margin="40,30" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Margin="0,0,0,24">
<TextBlock Text="V&#225;lasszon Office verzi&#243;t" Style="{StaticResource Title}"/>
<TextBlock Text="Melyik &#233;vj&#225;ratot szeretn&#233; telep&#237;teni?" Style="{StaticResource Subtitle}" Margin="0,4,0,0"/>
</StackPanel>
<StackPanel Grid.Row="1">
<RadioButton x:Name="Rb2024" GroupName="Ver" IsChecked="True" Style="{StaticResource CardRadio}" Margin="0,0,0,10">
<StackPanel Margin="8,2"><TextBlock Text="Office 2024" FontSize="18" FontWeight="SemiBold"/>
<TextBlock Text="2024. okt&#243;beri kiad&#225;s &#183; Windows 10 / 11" FontSize="12" Foreground="{StaticResource TextSecondary}"/></StackPanel>
</RadioButton>
<RadioButton x:Name="Rb2021" GroupName="Ver" Style="{StaticResource CardRadio}" Margin="0,0,0,10">
<StackPanel Margin="8,2"><TextBlock Text="Office 2021" FontSize="18" FontWeight="SemiBold"/>
<TextBlock Text="2021. okt&#243;beri kiad&#225;s &#183; Windows 10 / 11" FontSize="12" Foreground="{StaticResource TextSecondary}"/></StackPanel>
</RadioButton>
<RadioButton x:Name="Rb2019" GroupName="Ver" Style="{StaticResource CardRadio}" Margin="0,0,0,10">
<StackPanel Margin="8,2"><TextBlock Text="Office 2019" FontSize="18" FontWeight="SemiBold"/>
<TextBlock Text="2019. szeptemberi kiad&#225;s &#183; Windows 7 / 8.1 / 10 / 11 &#183; T&#225;mogat&#225;s lej&#225;rt: 2025.10." FontSize="12" Foreground="{StaticResource TextSecondary}"/></StackPanel>
</RadioButton>
</StackPanel>
</Grid>
<!-- Edition selection -->
<Grid x:Name="PanelEdition" Margin="40,30" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Margin="0,0,0,24">
<TextBlock Text="V&#225;lasszon kiad&#225;st" Style="{StaticResource Title}"/>
<TextBlock x:Name="EditionSubtitle" Style="{StaticResource Subtitle}" Margin="0,4,0,0"/>
</StackPanel>
<StackPanel x:Name="EditionCards" Grid.Row="1"/>
</Grid>
<!-- Config -->
<ScrollViewer x:Name="PanelConfig" Margin="40,30" Visibility="Collapsed" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Text="Be&#225;ll&#237;t&#225;sok" Style="{StaticResource Title}" Margin="0,0,0,20"/>
<Grid Margin="0,0,0,20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="0,0,16,0">
<TextBlock Text="Architekt&#250;ra" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,8"/>
<StackPanel Orientation="Horizontal">
<RadioButton x:Name="Rb64" GroupName="Arch" IsChecked="True" Style="{StaticResource CardRadio}" Margin="0,0,10,0" MinWidth="140">
<TextBlock Text="64-bit (aj&#225;nlott)" FontSize="14" Margin="6,0"/>
</RadioButton>
<RadioButton x:Name="Rb32" GroupName="Arch" Style="{StaticResource CardRadio}" MinWidth="100">
<TextBlock Text="32-bit" FontSize="14" Margin="6,0"/>
</RadioButton>
</StackPanel>
</StackPanel>
<StackPanel Grid.Column="1">
<TextBlock Text="Telep&#237;t&#233;si nyelv" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,8"/>
<ComboBox x:Name="CbLanguage" FontSize="14" Padding="10,8"/>
</StackPanel>
</Grid>
<TextBlock Text="Telep&#237;tend&#337; alkalmaz&#225;sok" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,10"/>
<WrapPanel x:Name="AppChecks" Orientation="Horizontal"/>
</StackPanel>
</ScrollViewer>
<!-- Product Key -->
<Grid x:Name="PanelKey" Margin="40,30" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Margin="0,0,0,24">
<TextBlock Text="Term&#233;kkulcs" Style="{StaticResource Title}"/>
<TextBlock Text="Adja meg a 25 karakteres term&#233;kkulcsot, vagy hagyja &#252;resen." Style="{StaticResource Subtitle}" Margin="0,4,0,0"/>
</StackPanel>
<StackPanel Grid.Row="1">
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<TextBox x:Name="K1" Width="110" MaxLength="5" CharacterCasing="Upper" FontFamily="Consolas" FontSize="16" TextAlignment="Center" Padding="8,8"/>
<TextBlock Text="-" FontSize="20" VerticalAlignment="Center" Margin="6,0" Foreground="{StaticResource TextSecondary}"/>
<TextBox x:Name="K2" Width="110" MaxLength="5" CharacterCasing="Upper" FontFamily="Consolas" FontSize="16" TextAlignment="Center" Padding="8,8"/>
<TextBlock Text="-" FontSize="20" VerticalAlignment="Center" Margin="6,0" Foreground="{StaticResource TextSecondary}"/>
<TextBox x:Name="K3" Width="110" MaxLength="5" CharacterCasing="Upper" FontFamily="Consolas" FontSize="16" TextAlignment="Center" Padding="8,8"/>
<TextBlock Text="-" FontSize="20" VerticalAlignment="Center" Margin="6,0" Foreground="{StaticResource TextSecondary}"/>
<TextBox x:Name="K4" Width="110" MaxLength="5" CharacterCasing="Upper" FontFamily="Consolas" FontSize="16" TextAlignment="Center" Padding="8,8"/>
<TextBlock Text="-" FontSize="20" VerticalAlignment="Center" Margin="6,0" Foreground="{StaticResource TextSecondary}"/>
<TextBox x:Name="K5" Width="110" MaxLength="5" CharacterCasing="Upper" FontFamily="Consolas" FontSize="16" TextAlignment="Center" Padding="8,8"/>
</StackPanel>
<CheckBox x:Name="CbSkipKey" Style="{StaticResource FluentCB}" Content="K&#233;s&#337;bb szeretn&#233;m megadni" Margin="0,20,0,0"/>
<Border Background="#FFF8E1" CornerRadius="6" Padding="16,12" Margin="0,24,0,0" BorderBrush="#FFE082" BorderThickness="1" MaxWidth="600" HorizontalAlignment="Left">
<StackPanel>
<TextBlock Text="Tudnival&#243;" FontWeight="SemiBold" FontSize="13" Margin="0,0,0,4"/>
<TextBlock TextWrapping="Wrap" FontSize="12" Foreground="{StaticResource TextSecondary}"
Text="Ha megadja a term&#233;kkulcsot, az Office automatikusan aktiv&#225;l&#243;dik a telep&#237;t&#233;s ut&#225;n."/>
</StackPanel>
</Border>
</StackPanel>
</Grid>
<!-- Summary -->
<ScrollViewer x:Name="PanelSummary" Margin="40,30" Visibility="Collapsed" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Text="&#214;sszegz&#233;s" Style="{StaticResource Title}" Margin="0,0,0,20"/>
<TextBlock Text="Ellen&#337;rizze a be&#225;ll&#237;t&#225;sokat a telep&#237;t&#233;s ind&#237;t&#225;sa el&#337;tt." Style="{StaticResource Subtitle}" Margin="0,0,0,16"/>
<Border Background="White" CornerRadius="8" BorderBrush="#E0E0E0" BorderThickness="1" Padding="20,16" Margin="0,0,0,16">
<TextBlock x:Name="SummaryText" FontSize="14" TextWrapping="Wrap"/>
</Border>
<Expander Header="Konfigur&#225;ci&#243;s XML" FontSize="13" Margin="0,0,0,10">
<Border Background="#F8F8F8" CornerRadius="4" Padding="12" Margin="0,8,0,0" BorderBrush="#E0E0E0" BorderThickness="1">
<TextBox x:Name="XmlPreview" IsReadOnly="True" TextWrapping="Wrap" FontFamily="Consolas" FontSize="12"
BorderThickness="0" Background="Transparent" MaxHeight="200" VerticalScrollBarVisibility="Auto" AcceptsReturn="True"/>
</Border>
</Expander>
</StackPanel>
</ScrollViewer>
<!-- Progress -->
<ScrollViewer x:Name="PanelProgress" Margin="40,30" Visibility="Collapsed" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock x:Name="ProgressTitle" Text="Telep&#237;t&#233;s folyamatban..." Style="{StaticResource Title}" Margin="0,0,0,20"/>
<TextBlock x:Name="ProgressStatus" Text="" FontSize="14" Foreground="{StaticResource TextSecondary}" Margin="0,0,0,8"/>
<ProgressBar x:Name="ProgressBar" Height="4" IsIndeterminate="True" Foreground="#0078D4" Background="#E0E0E0" BorderThickness="0" Margin="0,0,0,12"/>
<Border Background="#F8F8F8" CornerRadius="6" Padding="12" BorderBrush="#E0E0E0" BorderThickness="1">
<TextBox x:Name="ProgressLog" IsReadOnly="True" TextWrapping="Wrap" FontFamily="Consolas" FontSize="11"
Foreground="{StaticResource TextSecondary}" BorderThickness="0" Background="Transparent"
VerticalScrollBarVisibility="Auto" AcceptsReturn="True" MinHeight="120"/>
</Border>
<StackPanel x:Name="DonePanel" Margin="0,12,0,0" Visibility="Collapsed">
<TextBlock x:Name="DoneText" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,12"/>
<Border x:Name="ActivateCard" Visibility="Collapsed" Background="White" CornerRadius="8"
BorderBrush="#E0E0E0" BorderThickness="1" Padding="16,12" Margin="0,0,0,12">
<Grid>
<Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="Term&#233;kkulcs megad&#225;sa" FontSize="14" FontWeight="SemiBold"/>
<TextBlock Text="Az Office aktiv&#225;l&#225;s&#225;hoz adja meg a term&#233;kkulcsot." FontSize="12" Foreground="{StaticResource TextSecondary}"/>
</StackPanel>
<Button x:Name="BtnActivatePost" Grid.Column="1" Content="Aktiv&#225;l&#225;s" Style="{StaticResource PrimaryBtn}" Padding="18,8"/>
</Grid>
</Border>
<Border x:Name="LaunchCard" Visibility="Collapsed" Background="White" CornerRadius="8"
BorderBrush="#E0E0E0" BorderThickness="1" Padding="16,12" Margin="0,0,0,8">
<StackPanel>
<TextBlock Text="Alkalmaz&#225;s ind&#237;t&#225;sa" FontSize="14" FontWeight="SemiBold" Margin="0,0,0,10"/>
<WrapPanel x:Name="LaunchBtns" Orientation="Horizontal"/>
</StackPanel>
</Border>
</StackPanel>
</StackPanel>
</ScrollViewer>
<!-- Remove -->
<Grid x:Name="PanelRemove" Margin="40,30" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Margin="0,0,0,20">
<TextBlock Text="Office elt&#225;vol&#237;t&#225;s" Style="{StaticResource Title}"/>
<TextBlock Text="A sz&#225;m&#237;t&#243;g&#233;pen tal&#225;lt Office telep&#237;t&#233;sek:" Style="{StaticResource Subtitle}" Margin="0,4,0,0"/>
</StackPanel>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="RemoveList"/>
</ScrollViewer>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,12,0,0">
<Button x:Name="BtnDoRemove" Content="Elt&#225;vol&#237;t&#225;s" Style="{StaticResource PrimaryBtn}"/>
</StackPanel>
</Grid>
<!-- License/Troubleshoot -->
<Grid x:Name="PanelLicense" Margin="40,30" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Margin="0,0,0,16">
<TextBlock Text="Licenc-kezel&#233;s" Style="{StaticResource Title}"/>
<TextBlock Text="Office licenc &#225;llapot, aktiv&#225;l&#225;s &#233;s term&#233;kkulcsok kezel&#233;se" Style="{StaticResource Subtitle}" Margin="0,4,0,0"/>
</StackPanel>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="LicenseCards"/>
</ScrollViewer>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0">
<Button x:Name="BtnLicRefresh" Content="&#193;llapot friss&#237;t&#233;se" Style="{StaticResource SecBtn}" Margin="0,0,8,0"/>
</StackPanel>
</Grid>
</Grid>
<!-- Bottom bar -->
<Border Grid.Row="2" Background="#FAFAFA" BorderBrush="#E0E0E0" BorderThickness="0,1,0,0">
<Grid Margin="24,0">
<StackPanel x:Name="StepDots" Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
<Button x:Name="BtnBack" Content="&#x2190; Vissza" Style="{StaticResource SecBtn}" Margin="0,0,8,0" Visibility="Collapsed"/>
<Button x:Name="BtnNext" Content="Tov&#225;bb &#x2192;" Style="{StaticResource PrimaryBtn}" Visibility="Collapsed"/>
</StackPanel>
</Grid>
</Border>
</Grid>
<!-- Overlay for confirm dialog -->
<Grid x:Name="OverlayConfirm" Visibility="Collapsed">
<Border Background="#66000000"/>
<Border Background="White" CornerRadius="12" HorizontalAlignment="Center" VerticalAlignment="Center"
MinWidth="400" MaxWidth="500" Padding="28,24">
<Border.Effect><DropShadowEffect ShadowDepth="8" BlurRadius="32" Opacity="0.2"/></Border.Effect>
<StackPanel>
<TextBlock x:Name="DlgTitle" FontSize="18" FontWeight="SemiBold" Margin="0,0,0,12"/>
<TextBlock x:Name="DlgMsg" FontSize="14" TextWrapping="Wrap" Foreground="{StaticResource TextSecondary}" Margin="0,0,0,24"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="DlgCancel" Style="{StaticResource SecBtn}" Margin="0,0,8,0"/>
<Button x:Name="DlgOk" Style="{StaticResource PrimaryBtn}"/>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</Grid>
</Window>
'@
# ============================================================
# 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 = "<?xml version=`"1.0`" encoding=`"utf-8`"?>`n<Configuration>`n"
$xml += " <Add OfficeClientEdition=`"$($c.Architecture)`" Channel=`"$($c.Edition.Channel)`">`n"
$xml += " <Product ID=`"$($c.Edition.ProductId)`""
if ($c.ProductKey) { $xml += " PIDKEY=`"$($c.ProductKey.Replace('-',''))`"" }
$xml += ">`n"
$xml += " <Language ID=`"$($c.Language)`" />`n"
foreach ($app in $c.ExcludedApps) { $xml += " <ExcludeApp ID=`"$app`" />`n" }
$xml += " </Product>`n </Add>`n"
$xml += " <Display Level=`"Full`" AcceptEULA=`"TRUE`" />`n"
$xml += " <Property Name=`"FORCEAPPSHUTDOWN`" Value=`"TRUE`" />`n"
$xml += "</Configuration>"
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