From cfa7ef642ca982f5126b21fde055cb38390eaf33 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 22:38:48 +0000 Subject: [PATCH 1/2] Initial plan From 26587882bdfdb7014767be5e07449fe964e11709 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 22:49:41 +0000 Subject: [PATCH 2/2] Add profile file location management with policy support and rename folder location properties Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> --- .../Resources/Strings.Designer.cs | 22 +++ .../Resources/Strings.resx | 10 + .../NETworkManager.Profiles/ProfileManager.cs | 115 ++++++++++- .../LocalSettingsInfo.cs | 12 +- Source/NETworkManager.Settings/PolicyInfo.cs | 7 +- .../NETworkManager.Settings/PolicyManager.cs | 3 +- .../NETworkManager.Settings/SettingsInfo.cs | 19 ++ .../SettingsManager.cs | 22 +-- .../config.json.example | 3 +- .../ViewModels/SettingsProfilesViewModel.cs | 141 ++++++++++++++ .../ViewModels/SettingsSettingsViewModel.cs | 6 +- .../Views/SettingsProfilesView.xaml | 184 ++++++++++++++++-- .../Views/SettingsProfilesView.xaml.cs | 17 ++ Website/docs/changelog/next-release.md | 5 + Website/docs/settings/profiles.md | 24 +++ Website/docs/settings/settings.md | 4 +- Website/docs/system-wide-policies.md | 2 +- 17 files changed, 544 insertions(+), 52 deletions(-) diff --git a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs index 40ef350d30..b431a282ac 100644 --- a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs +++ b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs @@ -1475,6 +1475,17 @@ public static string ChangeLocationSettingsMessage { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die The location is changed and the application is restarted afterwards. + /// + ///You can copy your profile files from "{0}" to "{1}" to migrate your previous profiles, if necessary. The application must be closed for this to prevent the profiles from being overwritten. ähnelt. + /// + public static string ChangeLocationProfilesMessage { + get { + return ResourceManager.GetString("ChangeLocationProfilesMessage", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Changelog ähnelt. /// @@ -8902,6 +8913,17 @@ public static string RestoreDefaultLocationSettingsMessage { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die The default path is restored and the application is restarted afterwards. + /// + ///You can copy your profile files from "{0}" to "{1}" to migrate your previous profiles, if necessary. The application must be closed for this to prevent the profiles from being overwritten. ähnelt. + /// + public static string RestoreDefaultLocationProfilesMessage { + get { + return ResourceManager.GetString("RestoreDefaultLocationProfilesMessage", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Restore defaults ähnelt. /// diff --git a/Source/NETworkManager.Localization/Resources/Strings.resx b/Source/NETworkManager.Localization/Resources/Strings.resx index ad2eb07c93..550f519f38 100644 --- a/Source/NETworkManager.Localization/Resources/Strings.resx +++ b/Source/NETworkManager.Localization/Resources/Strings.resx @@ -3988,4 +3988,14 @@ You can copy the “settings.json” file from "{0}" to "{1}" to migrate your pr Enter a valid folder path! + + The location is changed and the application is restarted afterwards. + +You can copy your profile files from “{0}” to “{1}” to migrate your previous profiles, if necessary. The application must be closed for this to prevent the profiles from being overwritten. + + + The default path is restored and the application is restarted afterwards. + +You can copy your profile files from “{0}” to “{1}” to migrate your previous profiles, if necessary. The application must be closed for this to prevent the profiles from being overwritten. + \ No newline at end of file diff --git a/Source/NETworkManager.Profiles/ProfileManager.cs b/Source/NETworkManager.Profiles/ProfileManager.cs index bbb62fa382..6978c51d45 100644 --- a/Source/NETworkManager.Profiles/ProfileManager.cs +++ b/Source/NETworkManager.Profiles/ProfileManager.cs @@ -197,14 +197,121 @@ private static void ProfilesUpdated(bool profilesChanged = true) /// /// Method to get the path of the profiles folder. + /// Priority: 1. Policy override, 2. Custom user path (from SettingsInfo), 3. Portable/default. /// /// Path to the profiles folder. public static string GetProfilesFolderLocation() { - return ConfigurationManager.Current.IsPortable - ? Path.Combine(AssemblyManager.Current.Location, ProfilesFolderName) - : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), - AssemblyManager.Current.Name, ProfilesFolderName); + // 1. Policy override takes precedence (for IT administrators) + if (!string.IsNullOrWhiteSpace(PolicyManager.Current?.Profiles_FolderLocation)) + { + var validatedPath = ValidateProfilesFolderPath( + PolicyManager.Current.Profiles_FolderLocation, + "Policy-provided", + "next priority"); + + if (validatedPath != null) + return validatedPath; + } + + // 2. Custom user-configured path (not available in portable mode) + if (!ConfigurationManager.Current.IsPortable && + !string.IsNullOrWhiteSpace(SettingsManager.Current?.Profiles_CustomProfilesFolderLocation)) + { + var validatedPath = ValidateProfilesFolderPath( + SettingsManager.Current.Profiles_CustomProfilesFolderLocation, + "Custom", + "default location"); + + if (validatedPath != null) + return validatedPath; + } + + // 3. Fall back to portable or default location + if (ConfigurationManager.Current.IsPortable) + return GetPortableProfilesFolderLocation(); + else + return GetDefaultProfilesFolderLocation(); + } + + /// + /// Method to get the default profiles folder location in the user's Documents directory. + /// + /// Path to the default profiles folder location. + public static string GetDefaultProfilesFolderLocation() + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), + AssemblyManager.Current.Name, ProfilesFolderName); + } + + /// + /// Method to get the portable profiles folder location (in the same directory as the application). + /// + /// Path to the portable profiles folder location. + public static string GetPortableProfilesFolderLocation() + { + return Path.Combine(AssemblyManager.Current.Location, ProfilesFolderName); + } + + /// + /// Validates a profiles folder path for correctness and accessibility. + /// + /// The path to validate. + /// Description of the path source for logging (e.g., "Policy-provided", "Custom"). + /// Message describing what happens on validation failure (e.g., "next priority", "default location"). + /// The validated full path if valid; otherwise, null. + private static string ValidateProfilesFolderPath(string path, string pathSource, string fallbackMessage) + { + // Expand environment variables first (e.g. %userprofile%\profiles -> C:\Users\...\profiles) + path = Environment.ExpandEnvironmentVariables(path); + + // Validate that the path is rooted (absolute) + if (!Path.IsPathRooted(path)) + { + Log.Error($"{pathSource} Profiles_FolderLocation is not an absolute path: {path}. Falling back to {fallbackMessage}."); + return null; + } + + // Validate that the path doesn't contain invalid characters + try + { + // This will throw ArgumentException, NotSupportedException, SecurityException, PathTooLongException, or IOException if the path is invalid + var fullPath = Path.GetFullPath(path); + + // Check if the path is a directory (not a file) + if (File.Exists(fullPath)) + { + Log.Error($"{pathSource} Profiles_FolderLocation is a file, not a directory: {path}. Falling back to {fallbackMessage}."); + return null; + } + + return Path.TrimEndingDirectorySeparator(fullPath); + } + catch (ArgumentException ex) + { + Log.Error($"{pathSource} Profiles_FolderLocation contains invalid characters: {path}. Falling back to {fallbackMessage}.", ex); + return null; + } + catch (NotSupportedException ex) + { + Log.Error($"{pathSource} Profiles_FolderLocation format is not supported: {path}. Falling back to {fallbackMessage}.", ex); + return null; + } + catch (SecurityException ex) + { + Log.Error($"Insufficient permissions to access {pathSource} Profiles_FolderLocation: {path}. Falling back to {fallbackMessage}.", ex); + return null; + } + catch (PathTooLongException ex) + { + Log.Error($"{pathSource} Profiles_FolderLocation path is too long: {path}. Falling back to {fallbackMessage}.", ex); + return null; + } + catch (IOException ex) + { + Log.Error($"{pathSource} Profiles_FolderLocation caused an I/O error: {path}. Falling back to {fallbackMessage}.", ex); + return null; + } } /// diff --git a/Source/NETworkManager.Settings/LocalSettingsInfo.cs b/Source/NETworkManager.Settings/LocalSettingsInfo.cs index 6c7d1a692f..b3388d396e 100644 --- a/Source/NETworkManager.Settings/LocalSettingsInfo.cs +++ b/Source/NETworkManager.Settings/LocalSettingsInfo.cs @@ -34,23 +34,23 @@ private void OnPropertyChanged([CallerMemberName] string propertyName = null) [JsonIgnore] public bool SettingsChanged { get; set; } /// - /// Private field for the property." + /// Private field for the property." /// - private string _settingsFolderLocation; + private string _settings_FolderLocation; /// /// 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. /// - public string SettingsFolderLocation + public string Settings_FolderLocation { - get => _settingsFolderLocation; + get => _settings_FolderLocation; set { - if (_settingsFolderLocation == value) + if (_settings_FolderLocation == value) return; - _settingsFolderLocation = value; + _settings_FolderLocation = value; OnPropertyChanged(); } } diff --git a/Source/NETworkManager.Settings/PolicyInfo.cs b/Source/NETworkManager.Settings/PolicyInfo.cs index 2f17ee8abf..55d6767084 100644 --- a/Source/NETworkManager.Settings/PolicyInfo.cs +++ b/Source/NETworkManager.Settings/PolicyInfo.cs @@ -11,6 +11,9 @@ public class PolicyInfo [JsonPropertyName("Update_CheckForUpdatesAtStartup")] public bool? Update_CheckForUpdatesAtStartup { get; set; } - [JsonPropertyName("SettingsFolderLocation")] - public string? SettingsFolderLocation { get; set; } + [JsonPropertyName("Settings_FolderLocation")] + public string? Settings_FolderLocation { get; set; } + + [JsonPropertyName("Profiles_FolderLocation")] + public string? Profiles_FolderLocation { get; set; } } diff --git a/Source/NETworkManager.Settings/PolicyManager.cs b/Source/NETworkManager.Settings/PolicyManager.cs index ddbdfd3d38..26063ac16b 100644 --- a/Source/NETworkManager.Settings/PolicyManager.cs +++ b/Source/NETworkManager.Settings/PolicyManager.cs @@ -84,7 +84,8 @@ public static void Load() // Log enabled settings Log.Info($"System-wide policy - Update_CheckForUpdatesAtStartup: {Current.Update_CheckForUpdatesAtStartup?.ToString() ?? "Not set"}"); - Log.Info($"System-wide policy - SettingsFolderLocation: {Current.SettingsFolderLocation ?? "Not set"}"); + Log.Info($"System-wide policy - Settings_FolderLocation: {Current.Settings_FolderLocation ?? "Not set"}"); + Log.Info($"System-wide policy - Profiles_FolderLocation: {Current.Profiles_FolderLocation ?? "Not set"}"); } } catch (Exception ex) diff --git a/Source/NETworkManager.Settings/SettingsInfo.cs b/Source/NETworkManager.Settings/SettingsInfo.cs index 5e810c8ec3..0bbf9d31f1 100644 --- a/Source/NETworkManager.Settings/SettingsInfo.cs +++ b/Source/NETworkManager.Settings/SettingsInfo.cs @@ -622,6 +622,25 @@ public int Profiles_MaximumNumberOfBackups } } + private string _profiles_CustomProfilesFolderLocation; + + /// + /// Custom profiles folder location set by the user. + /// When set, overrides the default profiles folder location. + /// + public string Profiles_CustomProfilesFolderLocation + { + get => _profiles_CustomProfilesFolderLocation; + set + { + if (value == _profiles_CustomProfilesFolderLocation) + return; + + _profiles_CustomProfilesFolderLocation = value; + OnPropertyChanged(); + } + } + // Settings private bool _settings_IsDailyBackupEnabled = GlobalStaticConfiguration.Settings_IsDailyBackupEnabled; diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs index 8a289a632d..6852016be9 100644 --- a/Source/NETworkManager.Settings/SettingsManager.cs +++ b/Source/NETworkManager.Settings/SettingsManager.cs @@ -79,10 +79,10 @@ public static class SettingsManager public static string GetSettingsFolderLocation() { // 1. Policy override takes precedence (for IT administrators) - if (!string.IsNullOrWhiteSpace(PolicyManager.Current?.SettingsFolderLocation)) + if (!string.IsNullOrWhiteSpace(PolicyManager.Current?.Settings_FolderLocation)) { var validatedPath = ValidateSettingsFolderPath( - PolicyManager.Current.SettingsFolderLocation, + PolicyManager.Current.Settings_FolderLocation, "Policy-provided", "next priority"); @@ -92,10 +92,10 @@ public static string GetSettingsFolderLocation() // 2. Custom user-configured path (not available in portable mode) if (!ConfigurationManager.Current.IsPortable && - !string.IsNullOrWhiteSpace(LocalSettingsManager.Current?.SettingsFolderLocation)) + !string.IsNullOrWhiteSpace(LocalSettingsManager.Current?.Settings_FolderLocation)) { var validatedPath = ValidateSettingsFolderPath( - LocalSettingsManager.Current.SettingsFolderLocation, + LocalSettingsManager.Current.Settings_FolderLocation, "Custom", "default location"); @@ -144,7 +144,7 @@ private static string ValidateSettingsFolderPath(string path, string pathSource, // Validate that the path is rooted (absolute) if (!Path.IsPathRooted(path)) { - Log.Error($"{pathSource} SettingsFolderLocation is not an absolute path: {path}. Falling back to {fallbackMessage}."); + Log.Error($"{pathSource} Settings_FolderLocation is not an absolute path: {path}. Falling back to {fallbackMessage}."); return null; } @@ -157,7 +157,7 @@ private static string ValidateSettingsFolderPath(string path, string pathSource, // Check if the path is a directory (not a file) if (File.Exists(fullPath)) { - Log.Error($"{pathSource} SettingsFolderLocation is a file, not a directory: {path}. Falling back to {fallbackMessage}."); + Log.Error($"{pathSource} Settings_FolderLocation is a file, not a directory: {path}. Falling back to {fallbackMessage}."); return null; } @@ -165,27 +165,27 @@ private static string ValidateSettingsFolderPath(string path, string pathSource, } catch (ArgumentException ex) { - Log.Error($"{pathSource} SettingsFolderLocation contains invalid characters: {path}. Falling back to {fallbackMessage}.", ex); + Log.Error($"{pathSource} Settings_FolderLocation contains invalid characters: {path}. Falling back to {fallbackMessage}.", ex); return null; } catch (NotSupportedException ex) { - Log.Error($"{pathSource} SettingsFolderLocation format is not supported: {path}. Falling back to {fallbackMessage}.", ex); + Log.Error($"{pathSource} Settings_FolderLocation format is not supported: {path}. Falling back to {fallbackMessage}.", ex); return null; } catch (SecurityException ex) { - Log.Error($"Insufficient permissions to access {pathSource} SettingsFolderLocation: {path}. Falling back to {fallbackMessage}.", ex); + Log.Error($"Insufficient permissions to access {pathSource} Settings_FolderLocation: {path}. Falling back to {fallbackMessage}.", ex); return null; } catch (PathTooLongException ex) { - Log.Error($"{pathSource} SettingsFolderLocation path is too long: {path}. Falling back to {fallbackMessage}.", ex); + Log.Error($"{pathSource} Settings_FolderLocation path is too long: {path}. Falling back to {fallbackMessage}.", ex); return null; } catch (IOException ex) { - Log.Error($"{pathSource} SettingsFolderLocation caused an I/O error: {path}. Falling back to {fallbackMessage}.", ex); + Log.Error($"{pathSource} Settings_FolderLocation caused an I/O error: {path}. Falling back to {fallbackMessage}.", ex); return null; } } diff --git a/Source/NETworkManager.Settings/config.json.example b/Source/NETworkManager.Settings/config.json.example index 0a47ffa700..3747efaeaa 100644 --- a/Source/NETworkManager.Settings/config.json.example +++ b/Source/NETworkManager.Settings/config.json.example @@ -1,4 +1,5 @@ { "Update_CheckForUpdatesAtStartup": false, - "SettingsFolderLocation": "C:\\CustomPath\\NETworkManager\\Settings" + "Settings_FolderLocation": "C:\\CustomPath\\NETworkManager\\Settings", + "Profiles_FolderLocation": "C:\\CustomPath\\NETworkManager\\Profiles" } \ No newline at end of file diff --git a/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs b/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs index e2c58d980d..c9efb56184 100644 --- a/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs +++ b/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs @@ -7,6 +7,7 @@ using System; using System.ComponentModel; using System.Diagnostics; +using System.IO; using System.Linq; using System.Security.Cryptography; using System.Threading.Tasks; @@ -34,11 +35,55 @@ public string Location if (value == _location) return; + if (!_isLoading) + IsLocationChanged = !string.Equals(value, ProfileManager.GetProfilesFolderLocation(), StringComparison.OrdinalIgnoreCase); + _location = value; OnPropertyChanged(); } } + /// + /// Indicates whether the profiles location is managed by a system-wide policy. + /// + public bool IsLocationManagedByPolicy => !string.IsNullOrWhiteSpace(PolicyManager.Current?.Profiles_FolderLocation); + + private bool _isLocationChanged; + + /// + /// Gets or sets a value indicating whether the location has changed. + /// + public bool IsLocationChanged + { + get => _isLocationChanged; + set + { + if (value == _isLocationChanged) + return; + + _isLocationChanged = value; + OnPropertyChanged(); + } + } + + private bool _isDefaultLocation; + + /// + /// Indicates whether the current location is the default profiles folder location. + /// + public bool IsDefaultLocation + { + get => _isDefaultLocation; + set + { + if (value == _isDefaultLocation) + return; + + _isDefaultLocation = value; + OnPropertyChanged(); + } + } + private readonly ICollectionView _profileFiles; public ICollectionView ProfileFiles @@ -126,6 +171,7 @@ public SettingsProfilesViewModel() private void LoadSettings() { Location = ProfileManager.GetProfilesFolderLocation(); + IsDefaultLocation = string.Equals(Location, ProfileManager.GetDefaultProfilesFolderLocation(), StringComparison.OrdinalIgnoreCase); IsDailyBackupEnabled = SettingsManager.Current.Profiles_IsDailyBackupEnabled; MaximumNumberOfBackups = SettingsManager.Current.Profiles_MaximumNumberOfBackups; } @@ -141,6 +187,101 @@ private static void OpenLocationAction() Process.Start("explorer.exe", ProfileManager.GetProfilesFolderLocation()); } + /// + /// Gets the command that opens the location folder selection dialog. + /// + public ICommand BrowseLocationFolderCommand => new RelayCommand(p => BrowseLocationFolderAction()); + + /// + /// Opens a dialog that allows the user to select a folder location and updates the Location property with the + /// selected path if the user confirms the selection. + /// + private void BrowseLocationFolderAction() + { + using var dialog = new System.Windows.Forms.FolderBrowserDialog(); + + if (Directory.Exists(Location)) + dialog.SelectedPath = Location; + + var dialogResult = dialog.ShowDialog(); + + if (dialogResult == System.Windows.Forms.DialogResult.OK) + Location = dialog.SelectedPath; + } + + /// + /// Sets the location path based on the provided drag-and-drop input. + /// + /// The path to set as the location. + public void SetLocationPathFromDragDrop(string path) + { + Location = path; + } + + /// + /// Gets the command that initiates the action to change the location. + /// + public ICommand ChangeLocationCommand => new RelayCommand(_ => ChangeLocationAction().ConfigureAwait(false)); + + /// + /// Prompts the user to confirm and then changes the location of the profiles folder. + /// + /// A task that represents the asynchronous operation. + private async Task ChangeLocationAction() + { + var result = await DialogHelper.ShowConfirmationMessageAsync(Application.Current.MainWindow, + Strings.ChangeLocationQuestion, + string.Format(Strings.ChangeLocationProfilesMessage, ProfileManager.GetProfilesFolderLocation(), Location), + ChildWindowIcon.Question, + Strings.Change); + + if (!result) + return; + + // Save settings at the current location before changing it to prevent + // unintended saves to the new location (e.g., triggered by background timer or the app close & restart). + SettingsManager.Save(); + + // Set new location in SettingsInfo + SettingsManager.Current.Profiles_CustomProfilesFolderLocation = Location; + SettingsManager.Save(); + + // Restart the application + (Application.Current.MainWindow as MainWindow)?.RestartApplication(); + } + + /// + /// Gets the command that restores the default location. + /// + public ICommand RestoreDefaultLocationCommand => new RelayCommand(_ => RestoreDefaultLocationActionAsync().ConfigureAwait(false)); + + /// + /// Restores the profiles folder location to the default path after obtaining user confirmation. + /// + /// A task that represents the asynchronous operation. + private async Task RestoreDefaultLocationActionAsync() + { + var result = await DialogHelper.ShowConfirmationMessageAsync(Application.Current.MainWindow, + Strings.RestoreDefaultLocationQuestion, + string.Format(Strings.RestoreDefaultLocationProfilesMessage, ProfileManager.GetProfilesFolderLocation(), ProfileManager.GetDefaultProfilesFolderLocation()), + ChildWindowIcon.Question, + Strings.Restore); + + if (!result) + return; + + // Save settings at the current location before changing it to prevent + // unintended saves to the new location (e.g., triggered by background timer or the app close & restart). + SettingsManager.Save(); + + // Clear custom location to revert to default + SettingsManager.Current.Profiles_CustomProfilesFolderLocation = null; + SettingsManager.Save(); + + // Restart the application + (Application.Current.MainWindow as MainWindow)?.RestartApplication(); + } + public ICommand AddProfileFileCommand => new RelayCommand(async _ => await AddProfileFileAction().ConfigureAwait(false)); private async Task AddProfileFileAction() diff --git a/Source/NETworkManager/ViewModels/SettingsSettingsViewModel.cs b/Source/NETworkManager/ViewModels/SettingsSettingsViewModel.cs index e14ee3639c..b726b0cab9 100644 --- a/Source/NETworkManager/ViewModels/SettingsSettingsViewModel.cs +++ b/Source/NETworkManager/ViewModels/SettingsSettingsViewModel.cs @@ -50,7 +50,7 @@ public string Location /// /// Indicates whether the settings location is managed by a system-wide policy. /// - public bool IsLocationManagedByPolicy => !string.IsNullOrWhiteSpace(PolicyManager.Current?.SettingsFolderLocation); + public bool IsLocationManagedByPolicy => !string.IsNullOrWhiteSpace(PolicyManager.Current?.Settings_FolderLocation); /// /// Private field of property. @@ -265,7 +265,7 @@ private async Task ChangeLocationAction() SettingsManager.Save(); // Set new location - LocalSettingsManager.Current.SettingsFolderLocation = Location; + LocalSettingsManager.Current.Settings_FolderLocation = Location; LocalSettingsManager.Save(); // Restart the application @@ -301,7 +301,7 @@ private async Task RestoreDefaultLocationActionAsync() SettingsManager.Save(); // Clear custom location to revert to default - LocalSettingsManager.Current.SettingsFolderLocation = null; + LocalSettingsManager.Current.Settings_FolderLocation = null; LocalSettingsManager.Save(); // Restart the application diff --git a/Source/NETworkManager/Views/SettingsProfilesView.xaml b/Source/NETworkManager/Views/SettingsProfilesView.xaml index 0989c4a24d..3a19703688 100644 --- a/Source/NETworkManager/Views/SettingsProfilesView.xaml +++ b/Source/NETworkManager/Views/SettingsProfilesView.xaml @@ -10,36 +10,178 @@ xmlns:viewModels="clr-namespace:NETworkManager.ViewModels" xmlns:localization="clr-namespace:NETworkManager.Localization.Resources;assembly=NETworkManager.Localization" xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" + xmlns:validators="clr-namespace:NETworkManager.Validators;assembly=NETworkManager.Validators" + xmlns:settings="clr-namespace:NETworkManager.Settings;assembly=NETworkManager.Settings" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:SettingsProfilesViewModel}" Loaded="UserControl_Loaded"> + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Check for updates at startup](../settings/update.md#check-for-updates-at-startup) documentation for more information) - Settings folder location (see [Settings > Location](../settings/settings.md#location) documentation for more information) + - Profiles folder location (see [Profiles > Location](../settings/profiles.md#location) documentation for more information)
@@ -61,6 +62,10 @@ Release date: **xx.xx.2025** - Settings folder location can now be changed by the user in the settings (see [Settings > Location](../settings/settings.md#location) documentation for more information). Additionally, it can be configured via a system-wide policy, allowing administrators to enforce a specific location for all users. [#3324](https://github.com/BornToBeRoot/NETworkManager/pull/3324) +**Profiles** + +- Profiles folder location can now be changed by the user in the settings (see [Profiles > Location](../settings/profiles.md#location) documentation for more information). Additionally, it can be configured via a system-wide policy, allowing administrators to enforce a specific location for all users. + **Update** - The update check at startup can now be configured via a system-wide policy, allowing administrators to control whether the application checks for updates (see [Update > Check for updates at startup](../settings/update.md#check-for-updates-at-startup) documentation for more information). [#3313](https://github.com/BornToBeRoot/NETworkManager/pull/3313) diff --git a/Website/docs/settings/profiles.md b/Website/docs/settings/profiles.md index 186924aa4b..bee88adbda 100644 --- a/Website/docs/settings/profiles.md +++ b/Website/docs/settings/profiles.md @@ -17,6 +17,30 @@ Folder where the application profiles are stored. | Setup / Archiv | `%UserProfile%\Documents\NETworkManager\Profiles` | | Portable | `\Profiles` | +
+System-Wide Policy + +This setting can be controlled by administrators using a system-wide policy. See [System-Wide Policies](../system-wide-policies.md) for more information. + +**Policy Property:** `Profiles_FolderLocation` + +**Values:** + +- Absolute path (e.g., `C:\\Path\\To\\Profiles`) +- Path with environment variables (e.g., `%UserProfile%\\NETworkManager\\Profiles`) +- UNC path (e.g., `\\\\server\\share$\\NETworkManager\\Profiles`) +- Omit the property to allow the default location logic to apply (portable vs. non-portable) + +**Example:** + +```json +{ + "Profiles_FolderLocation": "%UserProfile%\\NETworkManager\\Profiles" +} +``` + +
+ :::note **Recommendation** diff --git a/Website/docs/settings/settings.md b/Website/docs/settings/settings.md index 4164bf37b8..8ed1f795de 100644 --- a/Website/docs/settings/settings.md +++ b/Website/docs/settings/settings.md @@ -22,7 +22,7 @@ Folder where the application settings are stored. This setting can be controlled by administrators using a system-wide policy. See [System-Wide Policies](../system-wide-policies.md) for more information. -**Policy Property:** `SettingsFolderLocation` +**Policy Property:** `Settings_FolderLocation` **Values:** @@ -35,7 +35,7 @@ This setting can be controlled by administrators using a system-wide policy. See ```json { - "SettingsFolderLocation": "%UserProfile%\\NETworkManager\\Settings" + "Settings_FolderLocation": "%UserProfile%\\NETworkManager\\Settings" } ``` diff --git a/Website/docs/system-wide-policies.md b/Website/docs/system-wide-policies.md index 1756d5755a..103b1a8302 100644 --- a/Website/docs/system-wide-policies.md +++ b/Website/docs/system-wide-policies.md @@ -37,7 +37,7 @@ The `config.json` file uses a simple JSON structure to define policy values. An ```json { "Update_CheckForUpdatesAtStartup": false, - "SettingsFolderLocation": "C:\\CustomPath\\NETworkManager\\Settings" + "Settings_FolderLocation": "C:\\CustomPath\\NETworkManager\\Settings" } ```