Files
Win11Debloat/Scripts/GUI/Show-RestoreBackupDialog.ps1

404 lines
14 KiB
PowerShell
Raw Normal View History

function Show-RestoreBackupDialog {
param(
[Parameter(Mandatory = $false)]
[System.Windows.Window]$Owner = $null
)
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
$usesDarkMode = GetSystemUsesDarkMode
$ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow }
$overlay = $null
$overlayWasAlreadyVisible = $false
if ($ownerWindow) {
try {
$overlay = $ownerWindow.FindName('ModalOverlay')
if ($overlay) {
$overlayWasAlreadyVisible = ($overlay.Visibility -eq 'Visible')
if (-not $overlayWasAlreadyVisible) {
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Visible' })
}
}
}
catch { }
}
$schemaPath = $script:RestoreBackupWindowSchema
if (-not $schemaPath -or -not (Test-Path $schemaPath)) {
throw 'Restore backup window schema file could not be found.'
}
$xaml = Get-Content -Path $schemaPath -Raw
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
try {
$window = [System.Windows.Markup.XamlReader]::Load($reader)
}
finally {
$reader.Close()
}
if ($ownerWindow) {
try {
$window.Owner = $ownerWindow
}
catch { }
}
try {
SetWindowThemeResources -window $window -usesDarkMode $usesDarkMode
}
catch { }
$titleBar = $window.FindName('TitleBar')
$titleText = $window.FindName('TitleText')
$closeBtn = $window.FindName('CloseBtn')
$backBtn = $window.FindName('BackBtn')
$primaryActionBtn = $window.FindName('PrimaryActionBtn')
$chooseRegistryBtn = $window.FindName('ChooseRegistryBtn')
$chooseStartMenuBtn = $window.FindName('ChooseStartMenuBtn')
$restoreModeTabs = $window.FindName('RestoreModeTabs')
$startMenuIntroPanel = $window.FindName('StartMenuIntroPanel')
$startMenuScopeCombo = $window.FindName('StartMenuScopeCombo')
$startMenuAutoBackupCheck = $window.FindName('StartMenuAutoBackupCheck')
$introInfoPanel = $window.FindName('IntroInfoPanel')
$overviewPanel = $window.FindName('OverviewPanel')
$overviewFeaturesSection = $window.FindName('OverviewFeaturesSection')
$overviewSummaryText = $window.FindName('OverviewSummaryText')
$backupFileText = $window.FindName('BackupFileText')
$backupCreatedText = $window.FindName('BackupCreatedText')
$backupTargetText = $window.FindName('BackupTargetText')
$featuresItemsControl = $window.FindName('FeaturesItemsControl')
$nonRevertibleSeparator = $window.FindName('NonRevertibleSeparator')
$nonRevertiblePanel = $window.FindName('NonRevertiblePanel')
$nonRevertibleFeaturesItemsControl = $window.FindName('NonRevertibleFeaturesItemsControl')
$nonRevertibleWikiLink = $window.FindName('NonRevertibleWikiLink')
$titleBar.Add_MouseLeftButtonDown({ $window.DragMove() })
$window.Tag = New-RestoreDialogState
$chooseRegistryBtn.IsDefault = $true
$state = @{ WizardStep = 'SelectType'; SelectedRegistryBackup = $null; SelectedStartMenuBackupFilePath = $null }
$getStartMenuScopeInfo = {
$isAllUsersScope = ($startMenuScopeCombo.SelectedItem.Tag -eq 'AllUsers')
$scopeValue = if ($isAllUsersScope) { 'AllUsers' } else { 'CurrentUser' }
$summaryScopeText = if ($isAllUsersScope) { 'all users' } else { 'the current user' }
return [PSCustomObject]@{
Scope = $scopeValue
Target = $scopeValue
SummaryText = $summaryScopeText
}
}
$showStartMenuIntroState = {
$backupFileText.Text = 'Not selected'
$backupCreatedText.Text = 'N/A'
$overviewSummaryText.Visibility = 'Collapsed'
$overviewPanel.Visibility = 'Collapsed'
$startMenuIntroPanel.Visibility = 'Visible'
$restoreModeTabs.SelectedIndex = 2
}
$showStartMenuOverviewState = {
param([string]$BackupFilePath)
$scopeInfo = & $getStartMenuScopeInfo
$backupTargetText.Text = GetFriendlyRegistryBackupTarget -Target $scopeInfo.Target
$overviewSummaryText.Text = "This will replace the current Start Menu pinned apps layout for $($scopeInfo.SummaryText) with the selected backup."
$backupFileText.Text = Split-Path -Path $BackupFilePath -Leaf
$createdText = 'Unknown'
try {
$createdText = (Get-Item -LiteralPath $BackupFilePath -ErrorAction Stop).LastWriteTime.ToString('yyyy-MM-dd HH:mm')
}
catch { }
$backupCreatedText.Text = $createdText
$overviewFeaturesSection.Visibility = 'Collapsed'
$overviewSummaryText.Visibility = 'Visible'
$nonRevertibleSeparator.Visibility = 'Collapsed'
$nonRevertiblePanel.Visibility = 'Collapsed'
$introInfoPanel.Visibility = 'Collapsed'
$overviewPanel.Visibility = 'Visible'
$restoreModeTabs.SelectedIndex = 1
}
$updateStartMenuOverviewPanel = {
if ($state.WizardStep -ne 'StartMenu') {
return
}
if ([string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
& $showStartMenuIntroState
return
}
& $showStartMenuOverviewState $state.SelectedStartMenuBackupFilePath
}
$updateStartMenuPrimaryActionText = {
if ($state.WizardStep -ne 'StartMenu') {
return
}
$isAutoBackupEnabled = ($startMenuAutoBackupCheck.IsChecked -eq $true)
$hasSelectedManualFile = -not [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)
if ($isAutoBackupEnabled -or $hasSelectedManualFile) {
$primaryActionBtn.Content = 'Restore backup'
}
else {
$primaryActionBtn.Content = 'Select backup file'
}
}
$refreshStartMenuUi = {
& $updateStartMenuOverviewPanel
& $updateStartMenuPrimaryActionText
}
$enterSelectTypeStep = {
$titleText.Text = 'Restore Backup'
$restoreModeTabs.SelectedIndex = 0
$backBtn.Visibility = 'Visible'
$backBtn.Content = 'Cancel'
$primaryActionBtn.Visibility = 'Collapsed'
$chooseRegistryBtn.IsDefault = $true
$primaryActionBtn.IsDefault = $false
}
$enterRegistryStep = {
$titleText.Text = 'Restore Registry Backup'
$restoreModeTabs.SelectedIndex = 1
$introInfoPanel.Visibility = 'Visible'
$overviewPanel.Visibility = 'Collapsed'
$overviewFeaturesSection.Visibility = 'Visible'
$overviewSummaryText.Visibility = 'Collapsed'
$backBtn.Visibility = 'Visible'
$backBtn.Content = 'Back'
$primaryActionBtn.Visibility = 'Visible'
$primaryActionBtn.Content = 'Select backup file'
$primaryActionBtn.IsDefault = $true
$chooseRegistryBtn.IsDefault = $false
}
$enterStartMenuStep = {
$titleText.Text = 'Restore Start Menu Backup'
$restoreModeTabs.SelectedIndex = 2
$backBtn.Visibility = 'Visible'
$backBtn.Content = 'Back'
$primaryActionBtn.Visibility = 'Visible'
$primaryActionBtn.IsDefault = $true
$chooseRegistryBtn.IsDefault = $false
& $refreshStartMenuUi
}
$showRegistryOverview = {
param(
[Parameter(Mandatory = $true)]
$SelectedBackup,
[Parameter(Mandatory = $true)]
[string]$SelectedBackupFilePath
)
$createdText = if ([string]::IsNullOrWhiteSpace($SelectedBackup.CreatedAt)) {
'Unknown'
}
else {
try {
[DateTime]::Parse($SelectedBackup.CreatedAt).ToString('yyyy-MM-dd HH:mm')
}
catch {
$SelectedBackup.CreatedAt
}
}
$selectedFeatureIds = Get-SelectedFeatureIdsFromBackup -SelectedBackup $SelectedBackup
$featureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds $selectedFeatureIds -Features $script:Features
$revertibleFeaturesList = @($featureLists.Revertible)
$nonRevertibleFeaturesList = @($featureLists.NonRevertible)
Write-Host "Backup overview prepared. Revertible=$($revertibleFeaturesList.Count), NonRevertible=$($nonRevertibleFeaturesList.Count)"
if ($revertibleFeaturesList.Count -eq 0) {
throw 'The selected backup does not contain any changes that can be restored.'
}
$backupFileText.Text = Split-Path $SelectedBackupFilePath -Leaf
$backupCreatedText.Text = $createdText
$backupTargetText.Text = GetFriendlyRegistryBackupTarget -Target ([string]$SelectedBackup.Target)
$featuresItemsControl.ItemsSource = $revertibleFeaturesList
$overviewFeaturesSection.Visibility = 'Visible'
$overviewSummaryText.Visibility = 'Collapsed'
$nonRevertibleFeaturesItemsControl.ItemsSource = $nonRevertibleFeaturesList
$hasNonRevertibleItems = ($nonRevertibleFeaturesList.Count -gt 0)
if ($hasNonRevertibleItems) { $nonRevertiblePanel.Visibility = 'Visible' } else { $nonRevertiblePanel.Visibility = 'Collapsed' }
if ($hasNonRevertibleItems) { $nonRevertibleSeparator.Visibility = 'Visible' } else { $nonRevertibleSeparator.Visibility = 'Collapsed' }
$introInfoPanel.Visibility = 'Collapsed'
$overviewPanel.Visibility = 'Visible'
return $true
}
$handleRegistryPrimaryAction = {
if ($state.SelectedRegistryBackup) {
$window.Tag = @{
Result = 'RestoreRegistry'
Backup = $state.SelectedRegistryBackup
}
$window.DialogResult = $true
$window.Close()
return
}
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
$openDialog.Title = 'Select Registry Backup File'
$openDialog.Filter = 'Registry backup (*.json)|*.json'
$openDialog.DefaultExt = '.json'
$openDialog.InitialDirectory = $script:RegistryBackupsPath
if ($openDialog.ShowDialog($window) -ne $true) {
return
}
Write-Host "Backup file selected: $($openDialog.FileName)"
$selectedBackup = Load-RegistryBackupFromFile -FilePath $openDialog.FileName
if (-not (& $showRegistryOverview -SelectedBackup $selectedBackup -SelectedBackupFilePath $openDialog.FileName)) {
return
}
$state.SelectedRegistryBackup = $selectedBackup
$primaryActionBtn.Content = 'Restore from backup'
}
$handleStartMenuPrimaryAction = {
$scope = (& $getStartMenuScopeInfo).Scope
$useManualBackupFile = -not ($startMenuAutoBackupCheck.IsChecked -eq $true)
if ($useManualBackupFile -and [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
$openDialog.Title = 'Select Start Menu Backup File'
$openDialog.Filter = 'Start Menu backup (*.bak)|*.bak'
$openDialog.InitialDirectory = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState"
$openDialog.DefaultExt = '.bak'
if ($openDialog.ShowDialog($window) -ne $true) {
return
}
$state.SelectedStartMenuBackupFilePath = $openDialog.FileName
Write-Host "Selected Start Menu backup file: $($state.SelectedStartMenuBackupFilePath)"
& $refreshStartMenuUi
return
}
$window.Tag = @{
Result = 'RestoreStartMenu'
StartMenuScope = $scope
UseManualBackupFile = $useManualBackupFile
BackupFilePath = $state.SelectedStartMenuBackupFilePath
}
$window.DialogResult = $true
$window.Close()
}
$setWizardStep = {
param([string]$step)
$state.WizardStep = $step
switch ($step) {
'SelectType' { & $enterSelectTypeStep }
'Registry' { & $enterRegistryStep }
'StartMenu' { & $enterStartMenuStep }
}
}
$startMenuAutoBackupCheck.Add_Checked({
$state.SelectedStartMenuBackupFilePath = $null
& $refreshStartMenuUi
})
$startMenuAutoBackupCheck.Add_Unchecked({
& $refreshStartMenuUi
})
$startMenuScopeCombo.Add_SelectionChanged({
& $refreshStartMenuUi
})
$nonRevertibleWikiLink.Add_MouseLeftButtonUp({
try {
Start-Process 'https://github.com/Raphire/Win11Debloat/wiki/Reverting-Changes' | Out-Null
}
catch { }
})
$closeBtn.Add_Click({
$window.Tag = New-RestoreDialogState
$window.DialogResult = $false
$window.Close()
})
$chooseRegistryBtn.Add_Click({ & $setWizardStep 'Registry' })
$chooseStartMenuBtn.Add_Click({ & $setWizardStep 'StartMenu' })
$backBtn.Add_Click({
if ($state.WizardStep -eq 'SelectType') {
$window.Tag = New-RestoreDialogState
$window.DialogResult = $false
$window.Close()
return
}
if ($state.WizardStep -eq 'Registry') {
$state.SelectedRegistryBackup = $null
}
if ($state.WizardStep -eq 'StartMenu') {
$state.SelectedStartMenuBackupFilePath = $null
$startMenuAutoBackupCheck.IsChecked = $true
}
& $setWizardStep 'SelectType'
})
$primaryActionBtn.Add_Click({
switch ($state.WizardStep) {
'Registry' { & $handleRegistryPrimaryAction }
'StartMenu' { & $handleStartMenuPrimaryAction }
}
})
$window.Add_KeyDown({
param($source, $e)
if ($e.Key -eq 'Escape') {
$window.Tag = New-RestoreDialogState
$window.DialogResult = $false
$window.Close()
}
})
& $setWizardStep 'SelectType'
try {
$null = $window.ShowDialog()
}
catch {
$innerMessage = if ($_.Exception.InnerException) { $_.Exception.InnerException.Message } else { 'None' }
throw "Failed to show restore backup dialog. Error: $($_.Exception.Message) Inner: $innerMessage"
}
finally {
if ($overlay -and -not $overlayWasAlreadyVisible) {
try {
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' })
}
catch { }
}
}
return $window.Tag
}