Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
24ad9e1
Initial plan
Copilot Feb 9, 2026
1225587
Add policy to override SettingsFolder Location
Copilot Feb 9, 2026
bf0c78e
Add validation for policy-provided SettingsFolderLocation
Copilot Feb 9, 2026
65e9732
Improve exception handling and documentation structure
Copilot Feb 9, 2026
114f86c
Add SecurityException handling and fix documentation formatting
Copilot Feb 9, 2026
92530c7
Fix using statement and clarify path format documentation
Copilot Feb 9, 2026
41633bf
Use consistent example path in documentation
Copilot Feb 9, 2026
a58b7e0
Address review feedback: validate directory, update docs, add admin i…
Copilot Feb 9, 2026
3264291
Update system-wide policies documentation
BornToBeRoot Feb 9, 2026
712a05d
Update SettingsSettingsView.xaml
BornToBeRoot Feb 9, 2026
6cdf525
Chore: Handle user settings location
BornToBeRoot Feb 15, 2026
26410ca
Feature: UI change settings
BornToBeRoot Feb 16, 2026
4b3010b
Feature: UI
BornToBeRoot Feb 16, 2026
7ed0538
Feature: UI
BornToBeRoot Feb 16, 2026
360ad30
Feature: Env var for folder
BornToBeRoot Feb 17, 2026
fedc544
Docs: Add some docs
BornToBeRoot Feb 18, 2026
47de748
Merge branch 'main' into copilot/add-policy-to-override-settings-fold…
BornToBeRoot Feb 18, 2026
8d83d18
Chore: Update packages
BornToBeRoot Feb 18, 2026
fc9b9ee
Merge branch 'copilot/add-policy-to-override-settings-folder-location…
BornToBeRoot Feb 18, 2026
257efe9
Update Source/NETworkManager.Settings/SettingsManager.cs
BornToBeRoot Feb 18, 2026
c7c743a
Update Source/NETworkManager/ViewModels/SettingsSettingsViewModel.cs
BornToBeRoot Feb 18, 2026
2bcae6e
Update Source/NETworkManager.Settings/SettingsManager.cs
BornToBeRoot Feb 18, 2026
ae6fe1f
Update Source/NETworkManager.Settings/LocalSettingsManager.cs
BornToBeRoot Feb 18, 2026
3dae961
Fix: Handle LocalSettingsManager.Load() gracefully
BornToBeRoot Feb 18, 2026
85952da
Fix exception handling for LocalSettingsManager and path validation
Copilot Feb 18, 2026
66a6ca7
Fix: Minor improvements
BornToBeRoot Feb 18, 2026
e344ae6
Feature: Improve settings load
BornToBeRoot Feb 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 76 additions & 9 deletions Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions Source/NETworkManager.Localization/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -3963,4 +3963,29 @@ If you click Cancel, the profile file will remain unencrypted.</value>
<data name="SettingManagedByAdministrator" xml:space="preserve">
<value>This setting is managed by your administrator.</value>
</data>
<data name="RestoreDefaultLocation" xml:space="preserve">
<value>Restore default location</value>
</data>
<data name="Restore" xml:space="preserve">
<value>Restore</value>
</data>
<data name="RestoreDefaultLocationQuestion" xml:space="preserve">
<value>Restore default location?</value>
</data>
<data name="RestoreDefaultLocationSettingsMessage" xml:space="preserve">
<value>The default path is restored and the application is restarted afterwards.

You can copy the “settings.json” file from "{0}" to "{1}" to migrate your previous settings, if necessary. The application must be closed for this to prevent the settings from being overwritten.</value>
</data>
<data name="ChangeLocationQuestion" xml:space="preserve">
<value>Change location?</value>
</data>
<data name="ChangeLocationSettingsMessage" xml:space="preserve">
<value>The location is changed and the application is restarted afterwards.

You can copy the “settings.json” file from "{0}" to "{1}" to migrate your previous settings, if necessary. The application must be closed for this to prevent the settings from being overwritten.</value>
</data>
<data name="EnterValidFolderPath" xml:space="preserve">
<value>Enter a valid folder path!</value>
</data>
</root>
4 changes: 2 additions & 2 deletions Source/NETworkManager.Models/NETworkManager.Models.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="DnsClient" Version="1.8.0" />
<PackageReference Include="IPNetwork2" Version="3.4.851" />
<PackageReference Include="IPNetwork2" Version="3.4.853" />
<PackageReference Include="Lextm.SharpSnmpLib" Version="12.5.7" />
<PackageReference Include="log4net" Version="3.2.0" />
<PackageReference Include="MahApps.Metro" Version="2.4.11" />
Expand All @@ -36,7 +36,7 @@
<PackageReference Include="MahApps.Metro.IconPacks.MaterialLight" Version="6.2.1" />
<PackageReference Include="MahApps.Metro.IconPacks.Modern" Version="6.2.1" />
<PackageReference Include="MahApps.Metro.IconPacks.Octicons" Version="6.2.1" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3650.58" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3800.47" />
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.5.4" />
Expand Down
6 changes: 4 additions & 2 deletions Source/NETworkManager.Profiles/ProfileManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -616,10 +616,10 @@ private static void Load(ProfileFileInfo profileFileInfo)
{
var loadedProfileUpdated = false;

Log.Info($"Load profile file: {profileFileInfo.Path}");

if (File.Exists(profileFileInfo.Path))
{
Log.Info($"Loading profile file from: {profileFileInfo.Path}");

// Encrypted profile file
if (profileFileInfo.IsEncrypted)
{
Expand Down Expand Up @@ -734,6 +734,8 @@ private static void Load(ProfileFileInfo profileFileInfo)

// Notify subscribers that profiles have been loaded/updated
ProfilesUpdated(false);

Log.Info("Profile file loaded successfully.");
}

/// <summary>
Expand Down
58 changes: 58 additions & 0 deletions Source/NETworkManager.Settings/LocalSettingsInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;

namespace NETworkManager.Settings;

/// <summary>
/// Class contains local settings that are stored outside the main settings file.
/// These settings control where the main settings file is located.
/// </summary>
public class LocalSettingsInfo
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LocalSettingsInfo class declares a PropertyChanged event and OnPropertyChanged method consistent with INotifyPropertyChanged pattern, but does not explicitly implement the INotifyPropertyChanged interface. This is inconsistent with the similar SettingsInfo class pattern. Consider adding : INotifyPropertyChanged to the class declaration for clarity and consistency.

Suggested change
public class LocalSettingsInfo
public class LocalSettingsInfo : INotifyPropertyChanged

Copilot uses AI. Check for mistakes.
{
/// <summary>
/// Occurs when a property value changes.
/// </summary>
/// <remarks>This event is typically used to notify subscribers that a property value has been updated. It
/// is commonly implemented in classes that support data binding or need to signal changes to property
/// values.</remarks>
public event PropertyChangedEventHandler PropertyChanged;

/// <summary>
/// Helper method to raise the <see cref="PropertyChanged" /> event.
/// </summary>
/// <param name="propertyName">Name of the property that changed.</param>
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
SettingsChanged = true;

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

#region Variables

[JsonIgnore] public bool SettingsChanged { get; set; }

/// <summary>
/// Private field for the <see cref="SettingsFolderLocation" /> property."
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XML documentation comment has an extra quotation mark at the end. The closing tag should be /// </summary> not /// </summary>".

Suggested change
/// Private field for the <see cref="SettingsFolderLocation" /> property."
/// Private field for the <see cref="SettingsFolderLocation" /> property.

Copilot uses AI. Check for mistakes.
/// </summary>
private string _settingsFolderLocation;

/// <summary>
/// Location of the folder where the local settings file is stored.
/// This can be changed by the user to move the settings file to a different location.
/// </summary>
public string SettingsFolderLocation
{
get => _settingsFolderLocation;
set
{
if (_settingsFolderLocation == value)
return;

_settingsFolderLocation = value;
OnPropertyChanged();
}
}
#endregion
}
142 changes: 142 additions & 0 deletions Source/NETworkManager.Settings/LocalSettingsManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using log4net;
using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace NETworkManager.Settings;

/// <summary>
/// Manages local application settings that are stored outside the main settings file.
/// This is used for settings that control where the main settings file is located.
/// </summary>
public static class LocalSettingsManager
{
#region Variables

/// <summary>
/// Logger for logging.
/// </summary>
private static readonly ILog Log = LogManager.GetLogger(typeof(LocalSettingsManager));

/// <summary>
/// Settings file name.
/// </summary>
private static string SettingsFileName => "Settings.json";

/// <summary>
/// Settings that are currently loaded.
/// </summary>
public static LocalSettingsInfo Current { get; private set; }

/// <summary>
/// JSON serializer options for consistent serialization/deserialization.
/// </summary>
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Converters = { new JsonStringEnumConverter() }
};
#endregion

#region Methods

/// <summary>
/// Method to get the path of the settings folder.
/// </summary>
/// <returns>Path to the settings folder.</returns>
private static string GetSettingsFolderLocation()
{
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
AssemblyManager.Current.Name);
}

/// <summary>
/// Method to get the settings file path
/// </summary>
/// <returns>Settings file path.</returns>
private static string GetSettingsFilePath()
{
return Path.Combine(
GetSettingsFolderLocation(),
SettingsFileName);
}

/// <summary>
/// Initialize new settings (<see cref="SettingsInfo" />) and save them (to a file).
/// </summary>
private static void Initialize()
{
Log.Info("Initializing new local settings.");

Current = new LocalSettingsInfo();

Save();
}

/// <summary>
/// Method to load the settings from a file.
/// </summary>
public static void Load()
{
var filePath = GetSettingsFilePath();

if (File.Exists(filePath))
{
try
{
Log.Info($"Loading local settings from: {filePath}");

var jsonString = File.ReadAllText(filePath);

// Treat empty or JSON "null" as "no settings" instead of crashing
if (string.IsNullOrWhiteSpace(jsonString))
{
Log.Info("Local settings file is empty, initializing new local settings.");
}
else
{
Current = JsonSerializer.Deserialize<LocalSettingsInfo>(jsonString, JsonOptions) ?? new LocalSettingsInfo();

Log.Info("Local settings loaded successfully.");

// Reset change tracking
Current.SettingsChanged = false;

return;
}
}
catch (Exception ex)
{
Log.Error($"Failed to load local settings from: {filePath}", ex);
}
}

// Initialize new local settings if file does not exist or loading failed
Initialize();
}

/// <summary>
/// Method to save the current settings to a file.
/// </summary>
public static void Save()
{
// Create the directory if it does not exist
Directory.CreateDirectory(GetSettingsFolderLocation());

// Serialize to file
var filePath = GetSettingsFilePath();

var jsonString = JsonSerializer.Serialize(Current, JsonOptions);
File.WriteAllText(filePath, jsonString);

Log.Info($"Local settings saved to {filePath}");

// Reset change tracking
Current.SettingsChanged = false;
}
#endregion
}
Loading