Refactor BYO app copy and add overwrite confirmation

Improves the "Copy BYO Apps" functionality by centralizing the `UserAppList.json` update process. The application list is now saved once from the UI before starting the copy, rather than individually within each background task.

Adds a confirmation prompt that warns the user if any application folders already exist in the destination, allowing them to choose whether to overwrite them. The copy logic is updated to remove existing folders before copying to ensure a clean state.
This commit is contained in:
rbalsleyMSFT
2025-07-17 17:17:00 -07:00
parent 62b4816498
commit b04a8460b0
@@ -214,9 +214,59 @@ function Invoke-CopyBYOApps {
[System.Windows.Controls.Button]$Button [System.Windows.Controls.Button]$Button
) )
$appsToCopy = $State.Controls.lstApplications.Items | Where-Object { -not [string]::IsNullOrWhiteSpace($_.Source) } $localAppsPath = $State.Controls.txtApplicationPath.Text
if (-not $appsToCopy) { $userAppListPath = Join-Path -Path $localAppsPath -ChildPath 'UserAppList.json'
[System.Windows.MessageBox]::Show("No applications with a source path specified.", "Copy BYO Apps", "OK", "Information") $listView = $State.Controls.lstApplications
try {
# Ensure items are sorted by current priority before saving
# Exclude CopyStatus when saving and ensure Priority is an integer
$applications = $listView.Items | Sort-Object Priority | Select-Object @{N = 'Priority'; E = { [int]$_.Priority } }, Name, CommandLine, Arguments, Source
$applications | ConvertTo-Json -Depth 5 | Set-Content -Path $userAppListPath -Force -Encoding UTF8
WriteLog "Successfully updated UserAppList.json with all applications from the UI."
}
catch {
$errorMessage = "Failed to update UserAppList.json: $_"
WriteLog $errorMessage
[System.Windows.MessageBox]::Show($errorMessage, "Error", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Error)
return
}
$allAppsWithSource = $State.Controls.lstApplications.Items | Where-Object { -not [string]::IsNullOrWhiteSpace($_.Source) }
if (-not $allAppsWithSource) {
[System.Windows.MessageBox]::Show("UserAppList.json has been updated. No applications with a source path were found to copy.", "Copy BYO Apps", "OK", "Information")
return
}
$win32BasePath = Join-Path -Path $localAppsPath -ChildPath "Win32"
$appsToProcess = [System.Collections.Generic.List[object]]::new()
$appsThatExist = [System.Collections.Generic.List[string]]::new()
$appsToConfirm = [System.Collections.Generic.List[object]]::new()
foreach ($app in $allAppsWithSource) {
$destinationPath = Join-Path -Path $win32BasePath -ChildPath $app.Name
if (Test-Path -Path $destinationPath -PathType Container) {
$appsThatExist.Add($app.Name)
$appsToConfirm.Add($app)
}
else {
$appsToProcess.Add($app)
}
}
if ($appsThatExist.Count -gt 0) {
$message = "The following application folders already exist in the destination and will be overwritten:`n`n$($appsThatExist -join "`n")`n`nDo you want to proceed with copying and overwriting them?"
$result = [System.Windows.MessageBox]::Show($message, "Confirm Overwrite", [System.Windows.MessageBoxButton]::YesNo, [System.Windows.MessageBoxImage]::Warning)
if ($result -eq 'Yes') {
$appsToProcess.AddRange($appsToConfirm)
}
}
if ($appsToProcess.Count -eq 0) {
# This message can be suppressed if you prefer no notification when the user clicks "No"
# [System.Windows.MessageBox]::Show("No applications selected for copying.", "Copy BYO Apps", "OK", "Information")
return return
} }
@@ -225,16 +275,13 @@ function Invoke-CopyBYOApps {
$State.Controls.pbOverallProgress.Value = 0 $State.Controls.pbOverallProgress.Value = 0
$State.Controls.txtStatus.Text = "Starting BYO app copy..." $State.Controls.txtStatus.Text = "Starting BYO app copy..."
# Define necessary task-specific variables locally
$localAppsPath = $State.Controls.txtApplicationPath.Text
# Create hashtable for task-specific arguments # Create hashtable for task-specific arguments
$taskArguments = @{ $taskArguments = @{
AppsPath = $localAppsPath AppsPath = $localAppsPath
} }
# Select only necessary properties before passing # Select only necessary properties before passing
$itemsToProcess = $appsToCopy | Select-Object Priority, Name, CommandLine, Arguments, Source $itemsToProcess = $appsToProcess | Select-Object Priority, Name, CommandLine, Arguments, Source
# Invoke the centralized parallel processing function # Invoke the centralized parallel processing function
# Pass task type and task-specific arguments # Pass task type and task-specific arguments
@@ -304,27 +351,18 @@ function Start-CopyBYOApplicationTask {
$destinationPath = Join-Path -Path $win32BasePath -ChildPath $appName $destinationPath = Join-Path -Path $win32BasePath -ChildPath $appName
try { try {
# Check destination
if (Test-Path -Path $destinationPath -PathType Container) {
$folderSize = (Get-ChildItem -Path $destinationPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
if ($folderSize -gt 1MB) {
$status = "Already copied"
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appName -Status $status
WriteLog "Skipping copy for $($appName): Destination '$destinationPath' exists and has content."
$success = $true
return [PSCustomObject]@{ Name = $appName; Status = $status; Success = $success }
}
else {
WriteLog "Destination '$destinationPath' exists but is empty/small. Proceeding with copy."
}
}
# Ensure base directory exists # Ensure base directory exists
if (-not (Test-Path -Path $win32BasePath -PathType Container)) { if (-not (Test-Path -Path $win32BasePath -PathType Container)) {
New-Item -Path $win32BasePath -ItemType Directory -Force | Out-Null New-Item -Path $win32BasePath -ItemType Directory -Force | Out-Null
WriteLog "Created directory: $win32BasePath" WriteLog "Created directory: $win32BasePath"
} }
# If destination exists, remove it to ensure a clean copy and prevent nesting.
if (Test-Path -Path $destinationPath -PathType Container) {
WriteLog "Removing existing destination folder: $destinationPath"
Remove-Item -Path $destinationPath -Recurse -Force -ErrorAction Stop
}
# Perform the copy # Perform the copy
$status = "Copying..." $status = "Copying..."
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appName -Status $status Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appName -Status $status
@@ -334,60 +372,6 @@ function Start-CopyBYOApplicationTask {
$success = $true $success = $true
WriteLog "Successfully copied '$appName' to '$destinationPath'." WriteLog "Successfully copied '$appName' to '$destinationPath'."
# ------------------------------------------------------------------
# Update (or create) UserAppList.json with the copied application
# ------------------------------------------------------------------
try {
WriteLog "Updating UserAppList.json for '$appName'..."
$userAppListPath = Join-Path -Path $AppsPath -ChildPath 'UserAppList.json'
# Build the new entry
$newEntry = [pscustomobject]@{
Priority = [int]$priority
Name = $appName
CommandLine = $commandLine
Arguments = $arguments
Source = $sourcePath
}
# Load existing list if present, ensuring it's always an array
if (Test-Path -Path $userAppListPath) {
try {
# Attempt to load and ensure it's an array
$appList = @(Get-Content -Path $userAppListPath -Raw | ConvertFrom-Json -ErrorAction Stop)
}
catch {
WriteLog "Warning: Could not parse '$userAppListPath' or it's not a valid JSON array. Initializing as empty array. Error: $($_.Exception.Message)"
$appList = @() # Initialize as empty array on error
}
}
else {
$appList = @() # Initialize as empty array if file doesn't exist
}
# Ensure $appList is an array even if ConvertFrom-Json returned $null or a single object somehow
if ($null -eq $appList -or $appList -isnot [array]) {
# If it was a single object, wrap it in an array. Otherwise, start fresh.
$appList = if ($null -ne $appList) { @($appList) } else { @() }
}
# Skip adding if an entry with the same Name already exists
if (-not ($appList | Where-Object { $_.Name -eq $newEntry.Name })) {
# Now $appList is guaranteed to be an array, so += is safe
$appList += $newEntry
# Sort by Priority before saving
$sortedAppList = $appList | Sort-Object Priority
$sortedAppList | ConvertTo-Json -Depth 10 | Set-Content -Path $userAppListPath -Encoding UTF8
WriteLog "Added '$($newEntry.Name)' to '$userAppListPath'."
}
else {
WriteLog "'$appName' already exists in '$userAppListPath'."
}
}
catch {
WriteLog "Failed to update UserAppList.json for '$appName': $($_.Exception.Message)"
}
} }
catch { catch {
$errorMessage = $_.Exception.Message $errorMessage = $_.Exception.Message