Public/Settings/Update-RpSettingsJson.ps1

function Update-RpSettingsJson {
    <#
    .SYNOPSIS
    Updates or adds a setting in the RpSettings JSON file.
 
    .DESCRIPTION
    The Update-RpSettingsJson function updates or adds a setting in the RpSettings
    JSON file. It can be used via command line interface or a graphical dialog
    interface.
 
    .COMPONENT
    Settings
 
    .PARAMETER SettingsFilePath
    Specifies the path to the settings JSON file. If not provided, the default
    settings file path is used.
 
    .PARAMETER Name
    Specifies the name of the setting to update or add. This parameter is mandatory
    when using the command line interface.
 
    .PARAMETER Value
    Specifies the value of the setting to update or add. This parameter is mandatory
    when using the command line interface.
 
    .PARAMETER ShowDialog
    If specified, a graphical dialog interface is shown to update or add settings.
 
    .EXAMPLE
    Update-RpSettingsJson -Name "SettingName" -Value "NewValue"
 
    Updates the setting "SettingName" to "NewValue" in the default settings JSON
    file.
 
    .EXAMPLE
    Update-RpSettingsJson -SettingsFilePath "C:\Path\To\Settings.json" -Name "SettingName" -Value "NewValue"
 
    Updates the setting "SettingName" to "NewValue" in the specified settings JSON
    file.
 
    .EXAMPLE
    Update-RpSettingsJson -ShowDialog
    Opens a graphical dialog interface to update or add settings in the default
    settings JSON file.
 
    .LINK
    https://www.remotepro.dev/en-US/Update-RpSettingsJson
    #>

    [CmdletBinding(DefaultParameterSetName = 'CommandLineInterface')]
    param (
        [Parameter(Mandatory=$false, Position=0, ParameterSetName='CommandLineInterface')]
        [Parameter(Mandatory=$false, ParameterSetName='ShowDialog')]
        [string]$SettingsFilePath,

        [Parameter(Mandatory=$true, Position=1, ParameterSetName='CommandLineInterface',
        ValueFromPipelineByPropertyName = $true)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
            $settingsFilePath = $fakeBoundParameter['SettingsFilePath']
            if (-not $settingsFilePath) {
                $settingsFilePath = Get-RpSettingsJsonPath
            }
            if (Test-Path -Path $settingsFilePath) {
                $settings = Get-Content -Raw $settingsFilePath | ConvertFrom-Json
                $settings.PSObject.Properties.Name | Where-Object { $_ -like "$wordToComplete*" }
            }
        })]
        [string]$Name,

        [Parameter(Mandatory=$true, Position=2, ParameterSetName='CommandLineInterface',
        ValueFromPipelineByPropertyName = $true)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
            $settingsFilePath = $fakeBoundParameter['SettingsFilePath']
            if (-not $settingsFilePath) {
                $settingsFilePath = Get-RpSettingsJsonPath
            }
            if (Test-Path -Path $settingsFilePath) {
                $settings = Get-Content -Raw $settingsFilePath | ConvertFrom-Json
                if ($settings.PSObject.Properties[$fakeBoundParameter['Name']]) {
                    $currentValue = $settings.PSObject.Properties[$fakeBoundParameter['Name']].Value
                    if ($currentValue -is [System.Collections.IEnumerable] -and -not ($currentValue -is [string])) {
                        $currentValue | Where-Object { $_ -like "$wordToComplete*" }
                    } else {
                        $currentValue
                    }
                }
            }
        })]
        [string]$Value,

        [Parameter(Mandatory=$false, ParameterSetName='ShowDialog')]
        [switch]$ShowDialog
    )

    begin {
        try {
            if (-not $PSBoundParameters.ContainsKey('SettingsFilePath') -or [string]::IsNullOrWhiteSpace($SettingsFilePath)) {
                $SettingsFilePath = Get-RpSettingsJsonPath
            }

            if (-not (Test-Path -Path $SettingsFilePath)) {
                Write-Output "Settings file not found at path '$SettingsFilePath'."
                return
            }

            if ($ShowDialog) {
                Add-Type -AssemblyName PresentationFramework
            }
        } catch {
            Write-Error "An error occurred during initialization: $($_.Exception.Message)"
            return
        }
    }

    process {
        try {
            $settings = Get-Content -Raw $SettingsFilePath | ConvertFrom-Json

            switch ($PSCmdlet.ParameterSetName) {
                'CommandLineInterface' {
                    if ($settings.PSObject.Properties.GetEnumerator() | Where-Object { $_.Name -eq $Name }) {
                        $settings.$Name = $Value
                        $settings | ConvertTo-Json -Depth 4  | Set-Content $SettingsFilePath
                        Write-Output "Setting '$Name' updated to '$Value'."
                    } else {
                        $settings | Add-Member -MemberType NoteProperty -Name $Name -Value $Value -Force
                        $settings | ConvertTo-Json -Depth 4  | Set-Content $SettingsFilePath
                        Write-Output "Setting '$Name' added with value '$Value'."
                    }
                }

                'ShowDialog' {
                    # Define XAML layout for WPF GUI
                    $xaml = @"
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        xmlns:mdControls="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"
        Title="Update Settings"
        Width="400" Height="300"
        WindowStartupLocation="CenterScreen"
        SizeToContent="WidthAndHeight"
        Background="{DynamicResource MaterialDesignPaper}">
 
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <materialDesign:BundledTheme x:Key="AppTheme" BaseTheme="Light" PrimaryColor="Grey" SecondaryColor="Lime" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign2.Defaults.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
 
    <StackPanel Margin="10">
        <ListBox Name="SettingsListBox" Width="350" Height="150" Margin="0,10,0,5"/>
        <TextBlock Text="Setting Value:" Margin="0,10,0,5"/>
        <TextBox Name="ValueTextBox" Width="350" />
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">
            <Button Name="SubmitButton" Content="Submit" Width="75" Margin="5"/>
            <Button Name="CancelButton" Content="Cancel" Width="75" Margin="5"/>
        </StackPanel>
    </StackPanel>
</Window>
"@


                    # Parse XAML and load WPF elements
                    $window = [Windows.Markup.XamlReader]::Parse($xaml)

                    # Set the window icon
                    if ($null -ne $window) {
                        Set-RpWindowIcon -window $window
                    } else {
                        Write-Warning "WPF window failed to load. Cannot set icon."
                    }

                    # Find WPF Controls
                    $settingsListBox = $window.FindName("SettingsListBox")
                    $valueTextBox = $window.FindName("ValueTextBox")
                    $submitButton = $window.FindName("SubmitButton")
                    $cancelButton = $window.FindName("CancelButton")

                    # Populate ListBox with settings
                    foreach ($property in $settings.PSObject.Properties) {
                        [void]$settingsListBox.Items.Add($property.Name)
                    }

                    # Event Handler for ListBox Selection
                    $settingsListBox.Add_SelectionChanged({
                        $selectedSetting = $settingsListBox.SelectedItem
                        if ($selectedSetting) {
                            $valueTextBox.Text = $settings.$selectedSetting
                        }
                    })

                    # Event Handler for Submit Button
                    $submitButton.Add_Click({
                        $selectedSetting = $settingsListBox.SelectedItem
                        if (-not $selectedSetting -or [string]::IsNullOrWhiteSpace($valueTextBox.Text)) {
                            [System.Windows.MessageBox]::Show("Please select a setting and provide a value.", "Validation Error",
                                [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Error) | Out-Null
                            return
                        }
                        $settings.$selectedSetting = $valueTextBox.Text
                        $settings | ConvertTo-Json -Depth 4 | Set-Content $SettingsFilePath
                        Write-Host "Setting '$selectedSetting' updated to '$($valueTextBox.Text)'."
                        $window.DialogResult = $true
                        if ($null -ne $window) {
                            $window.Close()
                        }
                    })

                    # Event Handler for Cancel Button
                    $cancelButton.Add_Click({
                        $window.DialogResult = $false
                        $window.Close()
                    })

                    # Show the WPF Window
                    $result = $window.ShowDialog()
                    if (-not $result) { Write-Verbose "Operation canceled by user."; return }
                }
            }
        } catch {
            Write-Error "An error occurred during processing: $($_.Exception.Message)"
        }
    }

    end {
        try {
            if ($ShowDialog) {
                $window.Close()
            }
        } catch {
            Write-Error "An error occurred during cleanup: $($_.Exception.Message)"
        }
        finally {
            if ($ShowDialog -and $null -ne $window) {
                $window.Close()
            }

            Set-RpSettingsJson # Refresh the RemoteProController
        }
    }
}