mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Adds UI/CLI to copy additional FFUs to USB build
- Enables selecting multiple existing FFU images to include on the deployment USB for easier distribution and testing. - Adds a UI option with selectable, sortable list from the capture folder, refresh support, and persisted selections. - Validates that selections exist when the option is enabled to prevent empty runs. - Supports unattended/CLI flows by prompting early or accepting a preselected list for USB creation; deduplicates and logs chosen files. - Always includes the just-built (or latest available) FFU as a base. - Improves no-FFU handling and streamlines multi-FFU selection workflow.
This commit is contained in:
@@ -383,6 +383,8 @@ param(
|
|||||||
[bool]$CopyPEDrivers,
|
[bool]$CopyPEDrivers,
|
||||||
[bool]$UseDriversAsPEDrivers,
|
[bool]$UseDriversAsPEDrivers,
|
||||||
[bool]$RemoveFFU,
|
[bool]$RemoveFFU,
|
||||||
|
[bool]$CopyAdditionalFFUFiles,
|
||||||
|
[string[]]$AdditionalFFUFiles,
|
||||||
[bool]$UpdateLatestCU,
|
[bool]$UpdateLatestCU,
|
||||||
[bool]$UpdatePreviewCU,
|
[bool]$UpdatePreviewCU,
|
||||||
[bool]$UpdateLatestMicrocode,
|
[bool]$UpdateLatestMicrocode,
|
||||||
@@ -3462,7 +3464,8 @@ Function Get-USBDrive {
|
|||||||
}
|
}
|
||||||
Function New-DeploymentUSB {
|
Function New-DeploymentUSB {
|
||||||
param(
|
param(
|
||||||
[switch]$CopyFFU
|
[switch]$CopyFFU,
|
||||||
|
[string[]]$FFUFilesToCopy
|
||||||
)
|
)
|
||||||
WriteLog "CopyFFU is set to $CopyFFU"
|
WriteLog "CopyFFU is set to $CopyFFU"
|
||||||
$BuildUSBPath = $PSScriptRoot
|
$BuildUSBPath = $PSScriptRoot
|
||||||
@@ -3472,14 +3475,28 @@ Function New-DeploymentUSB {
|
|||||||
|
|
||||||
# 1. Get FFU File(s) - This happens once before parallel processing
|
# 1. Get FFU File(s) - This happens once before parallel processing
|
||||||
if ($CopyFFU.IsPresent) {
|
if ($CopyFFU.IsPresent) {
|
||||||
|
if ($null -ne $FFUFilesToCopy -and $FFUFilesToCopy.Count -gt 0) {
|
||||||
|
$SelectedFFUFile = $FFUFilesToCopy
|
||||||
|
WriteLog "Using preselected FFU file list. Count: $($FFUFilesToCopy.Count)"
|
||||||
|
WriteLog "FFU files to copy:"
|
||||||
|
foreach ($f in $FFUFilesToCopy) {
|
||||||
|
WriteLog ("- {0}" -f (Split-Path $f -Leaf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
$FFUFiles = Get-ChildItem -Path "$BuildUSBPath\FFU" -Filter "*.ffu"
|
$FFUFiles = Get-ChildItem -Path "$BuildUSBPath\FFU" -Filter "*.ffu"
|
||||||
$FFUCount = $FFUFiles.count
|
$FFUCount = $FFUFiles.count
|
||||||
|
|
||||||
if ($FFUCount -eq 1) {
|
switch ($FFUCount) {
|
||||||
|
0 {
|
||||||
|
Write-Error "No FFU files found in $BuildUSBPath\FFU. Cannot copy FFU to USB drive."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
1 {
|
||||||
$SelectedFFUFile = $FFUFiles.FullName
|
$SelectedFFUFile = $FFUFiles.FullName
|
||||||
WriteLog "One FFU file found, will use: $SelectedFFUFile"
|
WriteLog "One FFU file found, will use: $SelectedFFUFile"
|
||||||
}
|
}
|
||||||
elseif ($FFUCount -gt 1) {
|
default {
|
||||||
WriteLog "Found $FFUCount FFU files"
|
WriteLog "Found $FFUCount FFU files"
|
||||||
if ($VerbosePreference -ne 'Continue') {
|
if ($VerbosePreference -ne 'Continue') {
|
||||||
Write-Host "Found $FFUCount FFU files"
|
Write-Host "Found $FFUCount FFU files"
|
||||||
@@ -3509,9 +3526,7 @@ Function New-DeploymentUSB {
|
|||||||
}
|
}
|
||||||
} while ($null -eq $SelectedFFUFile)
|
} while ($null -eq $SelectedFFUFile)
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
Write-Error "No FFU files found in $BuildUSBPath\FFU. Cannot copy FFU to USB drive."
|
|
||||||
Return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4817,6 +4832,49 @@ If (Test-Path -Path "$FFUDevelopmentPath\dirty.txt") {
|
|||||||
WriteLog 'Creating dirty.txt file'
|
WriteLog 'Creating dirty.txt file'
|
||||||
New-Item -Path .\ -Name "dirty.txt" -ItemType "file" | Out-Null
|
New-Item -Path .\ -Name "dirty.txt" -ItemType "file" | Out-Null
|
||||||
|
|
||||||
|
# Early CLI prompt for additional FFUs (only if enabled and not provided)
|
||||||
|
if ($BuildUSBDrive -and $CopyAdditionalFFUFiles -and ((-not $AdditionalFFUFiles) -or ($AdditionalFFUFiles.Count -eq 0))) {
|
||||||
|
try {
|
||||||
|
$ffuFolder = Join-Path $FFUDevelopmentPath 'FFU'
|
||||||
|
if (Test-Path -Path $ffuFolder) {
|
||||||
|
$cand = Get-ChildItem -Path $ffuFolder -Filter '*.ffu' -File | Sort-Object LastWriteTime -Descending
|
||||||
|
if ($cand.Count -gt 0) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Additional FFU files available in $($ffuFolder):"
|
||||||
|
$i = 1
|
||||||
|
foreach ($c in $cand) {
|
||||||
|
Write-Host ("{0,3}. {1} [{2}]" -f $i, $c.Name, $c.LastWriteTime)
|
||||||
|
$i++
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
$resp = Read-Host "Select additional FFUs to copy (e.g. 1,3,5) or 'A' for all, or press Enter to skip"
|
||||||
|
if ($resp -match '^[Aa]$') {
|
||||||
|
$AdditionalFFUFiles = @($cand.FullName)
|
||||||
|
}
|
||||||
|
elseif ($resp -match '^\s*\d+(\s*,\s*\d+)*\s*$') {
|
||||||
|
$indices = $resp.Split(',') | ForEach-Object { [int]($_.Trim()) }
|
||||||
|
$sel = @()
|
||||||
|
foreach ($idx in $indices) {
|
||||||
|
if ($idx -ge 1 -and $idx -le $cand.Count) {
|
||||||
|
$sel += $cand[$idx - 1].FullName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$AdditionalFFUFiles = @($sel | Select-Object -Unique)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# Skip if blank or invalid
|
||||||
|
if (-not [string]::IsNullOrWhiteSpace($resp)) {
|
||||||
|
WriteLog "Invalid additional FFU selection input. Skipping."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
WriteLog "Early additional FFU selection prompt failed: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#Get drivers first since user could be prompted for additional info
|
#Get drivers first since user could be prompted for additional info
|
||||||
Set-Progress -Percentage 3 -Message "Processing drivers..."
|
Set-Progress -Percentage 3 -Message "Processing drivers..."
|
||||||
if ($driversJsonPath -and (Test-Path $driversJsonPath) -and ($InstallDrivers -or $CopyDrivers)) {
|
if ($driversJsonPath -and (Test-Path $driversJsonPath) -and ($InstallDrivers -or $CopyDrivers)) {
|
||||||
@@ -6084,7 +6142,35 @@ If ($BuildUSBDrive) {
|
|||||||
Set-Progress -Percentage 95 -Message "Building USB drive..."
|
Set-Progress -Percentage 95 -Message "Building USB drive..."
|
||||||
try {
|
try {
|
||||||
If (Test-Path -Path $DeployISO) {
|
If (Test-Path -Path $DeployISO) {
|
||||||
New-DeploymentUSB -CopyFFU
|
$ffuFilesToCopy = @()
|
||||||
|
|
||||||
|
# Always include the FFU that was just built (fallback to most recent .ffu in capture folder)
|
||||||
|
$currentFFU = $null
|
||||||
|
if ($null -ne $FFUFile -and -not [string]::IsNullOrWhiteSpace($FFUFile) -and (Test-Path -LiteralPath $FFUFile)) {
|
||||||
|
$currentFFU = $FFUFile
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
$ffuDir = if (-not [string]::IsNullOrWhiteSpace($FFUCaptureLocation)) { $FFUCaptureLocation } else { Join-Path $FFUDevelopmentPath 'FFU' }
|
||||||
|
if (Test-Path -LiteralPath $ffuDir) {
|
||||||
|
$latest = Get-ChildItem -Path $ffuDir -Filter '*.ffu' -File | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||||
|
if ($null -ne $latest) { $currentFFU = $latest.FullName }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
WriteLog "Failed to resolve latest FFU file to copy: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($null -ne $currentFFU) {
|
||||||
|
$ffuFilesToCopy += $currentFFU
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($CopyAdditionalFFUFiles -and ($null -ne $AdditionalFFUFiles) -and ($AdditionalFFUFiles.Count -gt 0)) {
|
||||||
|
$ffuFilesToCopy += $AdditionalFFUFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
$ffuFilesToCopy = $ffuFilesToCopy | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique
|
||||||
|
New-DeploymentUSB -CopyFFU -FFUFilesToCopy $ffuFilesToCopy
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
WriteLog "$BuildUSBDrive set to true, however unable to find $DeployISO. USB drive not built."
|
WriteLog "$BuildUSBDrive set to true, however unable to find $DeployISO. USB drive not built."
|
||||||
|
|||||||
@@ -400,6 +400,15 @@ $script:uiState.Controls.btnRun.Add_Click({
|
|||||||
|
|
||||||
# Gather config on the UI thread before starting the job
|
# Gather config on the UI thread before starting the job
|
||||||
$config = Get-UIConfig -State $script:uiState
|
$config = Get-UIConfig -State $script:uiState
|
||||||
|
|
||||||
|
# Validate Additional FFU selection if enabled
|
||||||
|
if ($config.BuildUSBDrive -and $config.CopyAdditionalFFUFiles -and (($null -eq $config.AdditionalFFUFiles) -or ($config.AdditionalFFUFiles.Count -eq 0))) {
|
||||||
|
[System.Windows.MessageBox]::Show("Please select at least one additional FFU file to copy, or uncheck 'Copy Additional FFU Files'.", "Selection Required", "OK", "Warning") | Out-Null
|
||||||
|
$btnRun.IsEnabled = $true
|
||||||
|
$script:uiState.Controls.txtStatus.Text = "Build canceled: Additional FFU selection required."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
$configFilePath = Join-Path $config.FFUDevelopmentPath "\config\FFUConfig.json"
|
$configFilePath = Join-Path $config.FFUDevelopmentPath "\config\FFUConfig.json"
|
||||||
$config | ConvertTo-Json -Depth 10 | Set-Content -Path $configFilePath -Encoding UTF8
|
$config | ConvertTo-Json -Depth 10 | Set-Content -Path $configFilePath -Encoding UTF8
|
||||||
$script:uiState.Data.lastConfigFilePath = $configFilePath
|
$script:uiState.Data.lastConfigFilePath = $configFilePath
|
||||||
|
|||||||
@@ -756,6 +756,29 @@
|
|||||||
<CheckBox x:Name="chkCopyAutopilot" Content="Copy Autopilot Profile" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will copy the Autopilot profile to the USB drive."/>
|
<CheckBox x:Name="chkCopyAutopilot" Content="Copy Autopilot Profile" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will copy the Autopilot profile to the USB drive."/>
|
||||||
<CheckBox x:Name="chkCopyUnattend" Content="Copy Unattend.xml" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will copy the Unattend.xml file to the USB drive."/>
|
<CheckBox x:Name="chkCopyUnattend" Content="Copy Unattend.xml" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will copy the Unattend.xml file to the USB drive."/>
|
||||||
<CheckBox x:Name="chkCopyPPKG" Content="Copy Provisioning Package" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will copy the provisioning package to the USB drive."/>
|
<CheckBox x:Name="chkCopyPPKG" Content="Copy Provisioning Package" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will copy the provisioning package to the USB drive."/>
|
||||||
|
<CheckBox x:Name="chkCopyAdditionalFFUFiles" Content="Copy Additional FFU Files" Margin="5" VerticalAlignment="Center" Tag="When set to $true, allows selecting existing FFU files in the capture folder to also copy to the USB drive."/>
|
||||||
|
|
||||||
|
<!-- Additional FFU Selection Section -->
|
||||||
|
<Grid x:Name="additionalFFUPanel" Margin="5,0,0,10" Visibility="Collapsed">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<!-- Header row -->
|
||||||
|
<DockPanel Grid.Row="0" Margin="0,5" LastChildFill="False">
|
||||||
|
<TextBlock Text="Additional FFU Files" DockPanel.Dock="Left" FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||||
|
<Button x:Name="btnRefreshAdditionalFFUs" Content="Refresh" DockPanel.Dock="Left" Padding="10,5" ToolTip="Refresh the list of FFU files from the capture folder"/>
|
||||||
|
</DockPanel>
|
||||||
|
<!-- ListView row -->
|
||||||
|
<ListView x:Name="lstAdditionalFFUs" Grid.Row="1" Margin="0,5" Height="150">
|
||||||
|
<ListView.View>
|
||||||
|
<GridView>
|
||||||
|
<GridViewColumn Header="FFU Name" DisplayMemberBinding="{Binding Name}" Width="300"/>
|
||||||
|
<GridViewColumn Header="Last Modified" DisplayMemberBinding="{Binding LastModified}" Width="200"/>
|
||||||
|
</GridView>
|
||||||
|
</ListView.View>
|
||||||
|
</ListView>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<!-- Max USB Drives -->
|
<!-- Max USB Drives -->
|
||||||
<StackPanel Orientation="Horizontal" Margin="5">
|
<StackPanel Orientation="Horizontal" Margin="5">
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ function Get-UIConfig {
|
|||||||
UseDriversAsPEDrivers = $State.Controls.chkUseDriversAsPEDrivers.IsChecked
|
UseDriversAsPEDrivers = $State.Controls.chkUseDriversAsPEDrivers.IsChecked
|
||||||
CopyPPKG = $State.Controls.chkCopyPPKG.IsChecked
|
CopyPPKG = $State.Controls.chkCopyPPKG.IsChecked
|
||||||
CopyUnattend = $State.Controls.chkCopyUnattend.IsChecked
|
CopyUnattend = $State.Controls.chkCopyUnattend.IsChecked
|
||||||
|
CopyAdditionalFFUFiles = $State.Controls.chkCopyAdditionalFFUFiles.IsChecked
|
||||||
CreateCaptureMedia = $State.Controls.chkCreateCaptureMedia.IsChecked
|
CreateCaptureMedia = $State.Controls.chkCreateCaptureMedia.IsChecked
|
||||||
CreateDeploymentMedia = $State.Controls.chkCreateDeploymentMedia.IsChecked
|
CreateDeploymentMedia = $State.Controls.chkCreateDeploymentMedia.IsChecked
|
||||||
InjectUnattend = $State.Controls.chkInjectUnattend.IsChecked
|
InjectUnattend = $State.Controls.chkInjectUnattend.IsChecked
|
||||||
@@ -116,6 +117,16 @@ function Get-UIConfig {
|
|||||||
$config.USBDriveList[$_.Model] = $_.SerialNumber
|
$config.USBDriveList[$_.Model] = $_.SerialNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Additional FFU file selections
|
||||||
|
$config.AdditionalFFUFiles = @()
|
||||||
|
if ($State.Controls.chkCopyAdditionalFFUFiles.IsChecked) {
|
||||||
|
$config.AdditionalFFUFiles = @(
|
||||||
|
$State.Controls.lstAdditionalFFUs.Items |
|
||||||
|
Where-Object { $_.IsSelected } |
|
||||||
|
ForEach-Object { $_.FullName }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return $config
|
return $config
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +371,7 @@ function Update-UIFromConfig {
|
|||||||
Set-UIValue -ControlName 'chkAllowVHDXCaching' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'AllowVHDXCaching' -State $State
|
Set-UIValue -ControlName 'chkAllowVHDXCaching' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'AllowVHDXCaching' -State $State
|
||||||
Set-UIValue -ControlName 'chkAllowExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'AllowExternalHardDiskMedia' -State $State
|
Set-UIValue -ControlName 'chkAllowExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'AllowExternalHardDiskMedia' -State $State
|
||||||
Set-UIValue -ControlName 'chkPromptExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'PromptExternalHardDiskMedia' -State $State
|
Set-UIValue -ControlName 'chkPromptExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'PromptExternalHardDiskMedia' -State $State
|
||||||
|
Set-UIValue -ControlName 'chkCopyAdditionalFFUFiles' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyAdditionalFFUFiles' -State $State
|
||||||
Set-UIValue -ControlName 'chkCreateCaptureMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateCaptureMedia' -State $State
|
Set-UIValue -ControlName 'chkCreateCaptureMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateCaptureMedia' -State $State
|
||||||
Set-UIValue -ControlName 'chkCreateDeploymentMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateDeploymentMedia' -State $State
|
Set-UIValue -ControlName 'chkCreateDeploymentMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateDeploymentMedia' -State $State
|
||||||
Set-UIValue -ControlName 'chkInjectUnattend' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'InjectUnattend' -State $State
|
Set-UIValue -ControlName 'chkInjectUnattend' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'InjectUnattend' -State $State
|
||||||
@@ -654,8 +666,46 @@ function Update-UIFromConfig {
|
|||||||
else {
|
else {
|
||||||
WriteLog "LoadConfig: Condition to auto-check 'Select Specific USB Drives' was NOT met."
|
WriteLog "LoadConfig: Condition to auto-check 'Select Specific USB Drives' was NOT met."
|
||||||
}
|
}
|
||||||
|
# Populate additional FFU list and apply selections
|
||||||
|
try {
|
||||||
|
if ($State.Controls.chkCopyAdditionalFFUFiles.IsChecked) {
|
||||||
|
$State.Controls.additionalFFUPanel.Visibility = 'Visible'
|
||||||
|
if ($State.Controls.btnRefreshAdditionalFFUs) {
|
||||||
|
$State.Controls.btnRefreshAdditionalFFUs.RaiseEvent([System.Windows.RoutedEventArgs]::new([System.Windows.Controls.Button]::ClickEvent))
|
||||||
|
}
|
||||||
|
$selectedFiles = @()
|
||||||
|
$addFFUKeyExists = $false
|
||||||
|
if ($ConfigContent -is [System.Management.Automation.PSCustomObject] -and $null -ne $ConfigContent.PSObject.Properties) {
|
||||||
|
if (($ConfigContent.PSObject.Properties.Match('AdditionalFFUFiles')).Count -gt 0) {
|
||||||
|
$addFFUKeyExists = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($addFFUKeyExists -and $null -ne $ConfigContent.AdditionalFFUFiles) {
|
||||||
|
$selectedFiles = @($ConfigContent.AdditionalFFUFiles)
|
||||||
|
}
|
||||||
|
if ($selectedFiles.Count -gt 0) {
|
||||||
|
foreach ($item in $State.Controls.lstAdditionalFFUs.Items) {
|
||||||
|
if ($selectedFiles -contains $item.FullName) {
|
||||||
|
$item.IsSelected = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$State.Controls.lstAdditionalFFUs.Items.Refresh()
|
||||||
|
$headerChk = $State.Controls.chkSelectAllAdditionalFFUs
|
||||||
|
if ($null -ne $headerChk) {
|
||||||
|
Update-SelectAllHeaderCheckBoxState -ListView $State.Controls.lstAdditionalFFUs -HeaderCheckBox $headerChk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$State.Controls.additionalFFUPanel.Visibility = 'Collapsed'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
WriteLog "LoadConfig: Error applying Additional FFU selections: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
|
||||||
WriteLog "LoadConfig: Configuration loading process finished."
|
WriteLog "LoadConfig: Configuration loading process finished."
|
||||||
}
|
}
|
||||||
|
|
||||||
function Invoke-SaveConfiguration {
|
function Invoke-SaveConfiguration {
|
||||||
param(
|
param(
|
||||||
|
|||||||
@@ -152,6 +152,50 @@ function Register-EventHandlers {
|
|||||||
$localState.Controls.chkPromptExternalHardDiskMedia.IsChecked = $false
|
$localState.Controls.chkPromptExternalHardDiskMedia.IsChecked = $false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Additional FFU Files events
|
||||||
|
$State.Controls.chkCopyAdditionalFFUFiles.Add_Checked({
|
||||||
|
param($eventSource, $routedEventArgs)
|
||||||
|
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||||
|
$localState = $window.Tag
|
||||||
|
$localState.Controls.additionalFFUPanel.Visibility = 'Visible'
|
||||||
|
Update-AdditionalFFUList -State $localState
|
||||||
|
})
|
||||||
|
$State.Controls.chkCopyAdditionalFFUFiles.Add_Unchecked({
|
||||||
|
param($eventSource, $routedEventArgs)
|
||||||
|
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||||
|
$localState = $window.Tag
|
||||||
|
$localState.Controls.additionalFFUPanel.Visibility = 'Collapsed'
|
||||||
|
$localState.Controls.lstAdditionalFFUs.Items.Clear()
|
||||||
|
$headerChk = $localState.Controls.chkSelectAllAdditionalFFUs
|
||||||
|
if ($null -ne $headerChk) {
|
||||||
|
Update-SelectAllHeaderCheckBoxState -ListView $localState.Controls.lstAdditionalFFUs -HeaderCheckBox $headerChk
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$State.Controls.btnRefreshAdditionalFFUs.Add_Click({
|
||||||
|
param($eventSource, $routedEventArgs)
|
||||||
|
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||||
|
$localState = $window.Tag
|
||||||
|
Update-AdditionalFFUList -State $localState
|
||||||
|
})
|
||||||
|
$State.Controls.lstAdditionalFFUs.Add_PreviewKeyDown({
|
||||||
|
param($eventSource, $keyEvent)
|
||||||
|
if ($keyEvent.Key -eq 'Space') {
|
||||||
|
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||||
|
$localState = $window.Tag
|
||||||
|
Invoke-ListViewItemToggle -ListView $eventSource -State $localState -HeaderCheckBoxKeyName 'chkSelectAllAdditionalFFUs'
|
||||||
|
$keyEvent.Handled = $true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$State.Controls.lstAdditionalFFUs.Add_SelectionChanged({
|
||||||
|
param($eventSource, $selChangeEvent)
|
||||||
|
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||||
|
$localState = $window.Tag
|
||||||
|
$headerChk = $localState.Controls.chkSelectAllAdditionalFFUs
|
||||||
|
if ($null -ne $headerChk) {
|
||||||
|
Update-SelectAllHeaderCheckBoxState -ListView $localState.Controls.lstAdditionalFFUs -HeaderCheckBox $headerChk
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
$State.Controls.btnCheckUSBDrives.Add_Click({
|
$State.Controls.btnCheckUSBDrives.Add_Click({
|
||||||
param($eventSource, $routedEventArgs)
|
param($eventSource, $routedEventArgs)
|
||||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ function Initialize-UIControls {
|
|||||||
$State.Controls.usbSelectionPanel = $window.FindName('usbDriveSelectionPanel')
|
$State.Controls.usbSelectionPanel = $window.FindName('usbDriveSelectionPanel')
|
||||||
$State.Controls.chkAllowExternalHardDiskMedia = $window.FindName('chkAllowExternalHardDiskMedia')
|
$State.Controls.chkAllowExternalHardDiskMedia = $window.FindName('chkAllowExternalHardDiskMedia')
|
||||||
$State.Controls.chkPromptExternalHardDiskMedia = $window.FindName('chkPromptExternalHardDiskMedia')
|
$State.Controls.chkPromptExternalHardDiskMedia = $window.FindName('chkPromptExternalHardDiskMedia')
|
||||||
|
$State.Controls.chkCopyAdditionalFFUFiles = $window.FindName('chkCopyAdditionalFFUFiles')
|
||||||
|
$State.Controls.additionalFFUPanel = $window.FindName('additionalFFUPanel')
|
||||||
|
$State.Controls.lstAdditionalFFUs = $window.FindName('lstAdditionalFFUs')
|
||||||
|
$State.Controls.btnRefreshAdditionalFFUs = $window.FindName('btnRefreshAdditionalFFUs')
|
||||||
$State.Controls.chkInstallWingetApps = $window.FindName('chkInstallWingetApps')
|
$State.Controls.chkInstallWingetApps = $window.FindName('chkInstallWingetApps')
|
||||||
$State.Controls.wingetPanel = $window.FindName('wingetPanel')
|
$State.Controls.wingetPanel = $window.FindName('wingetPanel')
|
||||||
$State.Controls.btnCheckWingetModule = $window.FindName('btnCheckWingetModule')
|
$State.Controls.btnCheckWingetModule = $window.FindName('btnCheckWingetModule')
|
||||||
@@ -257,6 +261,8 @@ function Initialize-UIDefaults {
|
|||||||
$State.Controls.usbSelectionPanel.Visibility = if ($State.Controls.chkSelectSpecificUSBDrives.IsChecked) { 'Visible' } else { 'Collapsed' }
|
$State.Controls.usbSelectionPanel.Visibility = if ($State.Controls.chkSelectSpecificUSBDrives.IsChecked) { 'Visible' } else { 'Collapsed' }
|
||||||
$State.Controls.chkSelectSpecificUSBDrives.IsEnabled = $State.Controls.chkBuildUSBDriveEnable.IsChecked
|
$State.Controls.chkSelectSpecificUSBDrives.IsEnabled = $State.Controls.chkBuildUSBDriveEnable.IsChecked
|
||||||
$State.Controls.chkPromptExternalHardDiskMedia.IsEnabled = $State.Controls.chkAllowExternalHardDiskMedia.IsChecked
|
$State.Controls.chkPromptExternalHardDiskMedia.IsEnabled = $State.Controls.chkAllowExternalHardDiskMedia.IsChecked
|
||||||
|
$State.Controls.chkCopyAdditionalFFUFiles.IsChecked = $State.Defaults.generalDefaults.CopyAdditionalFFUFiles
|
||||||
|
$State.Controls.additionalFFUPanel.Visibility = if ($State.Controls.chkCopyAdditionalFFUFiles.IsChecked) { 'Visible' } else { 'Collapsed' }
|
||||||
|
|
||||||
# Hyper-V Settings defaults from General Defaults
|
# Hyper-V Settings defaults from General Defaults
|
||||||
Initialize-VMSwitchData -State $State
|
Initialize-VMSwitchData -State $State
|
||||||
@@ -454,7 +460,7 @@ function Initialize-DynamicUIElements {
|
|||||||
|
|
||||||
$exitHeaderTextFactory = New-Object System.Windows.FrameworkElementFactory([System.Windows.Controls.TextBlock])
|
$exitHeaderTextFactory = New-Object System.Windows.FrameworkElementFactory([System.Windows.Controls.TextBlock])
|
||||||
$exitHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::TextProperty, "Additional Exit Codes")
|
$exitHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::TextProperty, "Additional Exit Codes")
|
||||||
$exitHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::PaddingProperty, (New-Object System.Windows.Thickness(5,2,5,2)))
|
$exitHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::PaddingProperty, (New-Object System.Windows.Thickness(5, 2, 5, 2)))
|
||||||
$exitHeaderTextFactory.SetValue([System.Windows.FrameworkElement]::VerticalAlignmentProperty, [System.Windows.VerticalAlignment]::Center)
|
$exitHeaderTextFactory.SetValue([System.Windows.FrameworkElement]::VerticalAlignmentProperty, [System.Windows.VerticalAlignment]::Center)
|
||||||
|
|
||||||
$exitHeaderTemplate = New-Object System.Windows.DataTemplate
|
$exitHeaderTemplate = New-Object System.Windows.DataTemplate
|
||||||
@@ -482,7 +488,7 @@ function Initialize-DynamicUIElements {
|
|||||||
|
|
||||||
$ignoreHeaderTextFactory = New-Object System.Windows.FrameworkElementFactory([System.Windows.Controls.TextBlock])
|
$ignoreHeaderTextFactory = New-Object System.Windows.FrameworkElementFactory([System.Windows.Controls.TextBlock])
|
||||||
$ignoreHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::TextProperty, "Ignore Exit Codes")
|
$ignoreHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::TextProperty, "Ignore Exit Codes")
|
||||||
$ignoreHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::PaddingProperty, (New-Object System.Windows.Thickness(5,2,5,2)))
|
$ignoreHeaderTextFactory.SetValue([System.Windows.Controls.TextBlock]::PaddingProperty, (New-Object System.Windows.Thickness(5, 2, 5, 2)))
|
||||||
$ignoreHeaderTextFactory.SetValue([System.Windows.FrameworkElement]::VerticalAlignmentProperty, [System.Windows.VerticalAlignment]::Center)
|
$ignoreHeaderTextFactory.SetValue([System.Windows.FrameworkElement]::VerticalAlignmentProperty, [System.Windows.VerticalAlignment]::Center)
|
||||||
|
|
||||||
$ignoreHeaderTemplate = New-Object System.Windows.DataTemplate
|
$ignoreHeaderTemplate = New-Object System.Windows.DataTemplate
|
||||||
@@ -682,6 +688,51 @@ function Initialize-DynamicUIElements {
|
|||||||
else {
|
else {
|
||||||
WriteLog "Warning: lstUSBDrives.View is not a GridView. Selectable column not added, and sorting cannot be enabled."
|
WriteLog "Warning: lstUSBDrives.View is not a GridView. Selectable column not added, and sorting cannot be enabled."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Additional FFUs ListView setup
|
||||||
|
$itemStyleAdditionalFFUs = New-Object System.Windows.Style([System.Windows.Controls.ListViewItem])
|
||||||
|
$itemStyleAdditionalFFUs.Setters.Add((New-Object System.Windows.Setter([System.Windows.Controls.ListViewItem]::HorizontalContentAlignmentProperty, [System.Windows.HorizontalAlignment]::Stretch)))
|
||||||
|
$State.Controls.lstAdditionalFFUs.ItemContainerStyle = $itemStyleAdditionalFFUs
|
||||||
|
|
||||||
|
if ($State.Controls.lstAdditionalFFUs.View -is [System.Windows.Controls.GridView]) {
|
||||||
|
Add-SelectableGridViewColumn -ListView $State.Controls.lstAdditionalFFUs -State $State -HeaderCheckBoxKeyName "chkSelectAllAdditionalFFUs" -ColumnWidth 70
|
||||||
|
|
||||||
|
$additionalFFUsGridView = $State.Controls.lstAdditionalFFUs.View
|
||||||
|
|
||||||
|
if ($additionalFFUsGridView.Columns.Count -gt 1) {
|
||||||
|
$nameColumn = $additionalFFUsGridView.Columns[1]
|
||||||
|
$nameHeader = New-Object System.Windows.Controls.GridViewColumnHeader
|
||||||
|
$nameHeader.Content = "FFU Name"
|
||||||
|
$nameHeader.Tag = "Name"
|
||||||
|
$nameHeader.HorizontalContentAlignment = [System.Windows.HorizontalAlignment]::Left
|
||||||
|
$nameColumn.Header = $nameHeader
|
||||||
|
}
|
||||||
|
if ($additionalFFUsGridView.Columns.Count -gt 2) {
|
||||||
|
$lastModColumn = $additionalFFUsGridView.Columns[2]
|
||||||
|
$lastModHeader = New-Object System.Windows.Controls.GridViewColumnHeader
|
||||||
|
$lastModHeader.Content = "Last Modified"
|
||||||
|
$lastModHeader.Tag = "LastModified"
|
||||||
|
$lastModHeader.HorizontalContentAlignment = [System.Windows.HorizontalAlignment]::Left
|
||||||
|
$lastModColumn.Header = $lastModHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
$State.Controls.lstAdditionalFFUs.AddHandler(
|
||||||
|
[System.Windows.Controls.GridViewColumnHeader]::ClickEvent,
|
||||||
|
[System.Windows.RoutedEventHandler] {
|
||||||
|
param($eventSource, $e)
|
||||||
|
$header = $e.OriginalSource
|
||||||
|
if ($header -is [System.Windows.Controls.GridViewColumnHeader] -and $header.Tag) {
|
||||||
|
$listViewControl = $eventSource
|
||||||
|
$window = [System.Windows.Window]::GetWindow($listViewControl)
|
||||||
|
$uiStateFromWindowTag = $window.Tag
|
||||||
|
Invoke-ListViewSort -listView $eventSource -property $header.Tag -State $uiStateFromWindowTag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
WriteLog "Warning: lstAdditionalFFUs.View is not a GridView. Selectable column not added, and sorting cannot be enabled."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ function Get-GeneralDefaults {
|
|||||||
AllowExternalHardDiskMedia = $false
|
AllowExternalHardDiskMedia = $false
|
||||||
PromptExternalHardDiskMedia = $true
|
PromptExternalHardDiskMedia = $true
|
||||||
SelectSpecificUSBDrives = $false
|
SelectSpecificUSBDrives = $false
|
||||||
|
CopyAdditionalFFUFiles = $false
|
||||||
CopyAutopilot = $false
|
CopyAutopilot = $false
|
||||||
CopyUnattend = $false
|
CopyUnattend = $false
|
||||||
CopyPPKG = $false
|
CopyPPKG = $false
|
||||||
@@ -198,6 +199,65 @@ function Get-USBDrives {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Returns a list of FFU files from the provided folder with selection metadata
|
||||||
|
function Get-FFUFiles {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Path
|
||||||
|
)
|
||||||
|
if (-not (Test-Path -Path $Path)) {
|
||||||
|
return @()
|
||||||
|
}
|
||||||
|
Get-ChildItem -Path $Path -Filter '*.ffu' -File -ErrorAction SilentlyContinue | ForEach-Object {
|
||||||
|
[PSCustomObject]@{
|
||||||
|
IsSelected = $false
|
||||||
|
Name = $_.Name
|
||||||
|
LastModified = $_.LastWriteTime
|
||||||
|
FullName = $_.FullName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper: Populate Additional FFU List from the capture folder
|
||||||
|
function Update-AdditionalFFUList {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[PSCustomObject]$State
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
$ffuFolder = $State.Controls.txtFFUCaptureLocation.Text
|
||||||
|
$listView = $State.Controls.lstAdditionalFFUs
|
||||||
|
if ($null -eq $listView) { return }
|
||||||
|
$listView.Items.Clear()
|
||||||
|
if ([string]::IsNullOrWhiteSpace($ffuFolder) -or -not (Test-Path -Path $ffuFolder)) {
|
||||||
|
WriteLog "Additional FFUs: Capture folder not set or not found: $ffuFolder"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$items = Get-ChildItem -Path $ffuFolder -Filter '*.ffu' -File -ErrorAction SilentlyContinue |
|
||||||
|
Sort-Object LastWriteTime -Descending |
|
||||||
|
ForEach-Object {
|
||||||
|
[PSCustomObject]@{
|
||||||
|
IsSelected = $false
|
||||||
|
Name = $_.Name
|
||||||
|
LastModified = $_.LastWriteTime
|
||||||
|
FullName = $_.FullName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($it in $items) { $listView.Items.Add($it) | Out-Null }
|
||||||
|
WriteLog "Additional FFUs: Found $($listView.Items.Count) FFU files in $ffuFolder."
|
||||||
|
}
|
||||||
|
$headerChk = $State.Controls.chkSelectAllAdditionalFFUs
|
||||||
|
if ($null -ne $headerChk) {
|
||||||
|
Update-SelectAllHeaderCheckBoxState -ListView $listView -HeaderCheckBox $headerChk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
WriteLog "Update-AdditionalFFUList error: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Function to manage the visibility of the application UI panels
|
# Function to manage the visibility of the application UI panels
|
||||||
function Update-ApplicationPanelVisibility {
|
function Update-ApplicationPanelVisibility {
|
||||||
param(
|
param(
|
||||||
|
|||||||
Reference in New Issue
Block a user