diff --git a/Source/GlobalAssemblyInfo.cs b/Source/GlobalAssemblyInfo.cs index 8d752ce11e..2c3b62ecf7 100644 --- a/Source/GlobalAssemblyInfo.cs +++ b/Source/GlobalAssemblyInfo.cs @@ -6,5 +6,5 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2025.10.18.0")] -[assembly: AssemblyFileVersion("2025.10.18.0")] +[assembly: AssemblyVersion("2025.11.1.0")] +[assembly: AssemblyFileVersion("2025.11.1.0")] diff --git a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs index 9cfdb253aa..7d7b7c174b 100644 --- a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs +++ b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs @@ -2756,11 +2756,11 @@ public static string DeleteProfileFile { } /// - /// Looks up a localized string similar to Selected profile file will be deleted permanently.. + /// Looks up a localized string similar to Profile file "{0}" will be deleted permanently.. /// - public static string DeleteProfileFileMessage { + public static string DeleteProfileFileXMessage { get { - return ResourceManager.GetString("DeleteProfileFileMessage", resourceCulture); + return ResourceManager.GetString("DeleteProfileFileXMessage", resourceCulture); } } @@ -3422,6 +3422,27 @@ public static string EnableEncryptionDots { } } + /// + /// Looks up a localized string similar to Do you want to enable profile file encryption to protect sensitive data such as hosts, IP addresses, URLs, and stored credentials? + /// + ///You can enable or disable encryption later at any time by right-clicking the profile file to manage encryption settings. + ///If you click Cancel, the profile file will remain unencrypted.. + /// + public static string EnableEncryptionForProfileFileMessage { + get { + return ResourceManager.GetString("EnableEncryptionForProfileFileMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable encryption?. + /// + public static string EnableEncryptionQuestion { + get { + return ResourceManager.GetString("EnableEncryptionQuestion", resourceCulture); + } + } + /// /// Looks up a localized string similar to Enable entry. /// diff --git a/Source/NETworkManager.Localization/Resources/Strings.resx b/Source/NETworkManager.Localization/Resources/Strings.resx index ec8798c515..df3fb92241 100644 --- a/Source/NETworkManager.Localization/Resources/Strings.resx +++ b/Source/NETworkManager.Localization/Resources/Strings.resx @@ -2367,9 +2367,6 @@ $$hostname$$ --> Hostname Delete profile file - - Selected profile file will be deleted permanently. - All profiles in this profile file will be permanently deleted! @@ -3999,4 +3996,16 @@ Right-click for more options. Admin (console) session + + Profile file "{0}" will be deleted permanently. + + + Enable encryption? + + + Do you want to enable profile file encryption to protect sensitive data such as hosts, IP addresses, URLs, and stored credentials? + +You can enable or disable encryption later at any time by right-clicking the profile file to manage encryption settings. +If you click Cancel, the profile file will remain unencrypted. + \ No newline at end of file diff --git a/Source/NETworkManager.Setup/NETworkManager.Setup.wixproj b/Source/NETworkManager.Setup/NETworkManager.Setup.wixproj index eb0f19acfd..a64ab50855 100644 --- a/Source/NETworkManager.Setup/NETworkManager.Setup.wixproj +++ b/Source/NETworkManager.Setup/NETworkManager.Setup.wixproj @@ -1,4 +1,4 @@ - + @@ -7,7 +7,7 @@ - + \ No newline at end of file diff --git a/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs b/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs index e9fc53d933..1290260821 100644 --- a/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs +++ b/Source/NETworkManager/ViewModels/SettingsProfilesViewModel.cs @@ -82,6 +82,8 @@ public SettingsProfilesViewModel(IDialogCoordinator instance) ProfileFiles.SortDescriptions.Add( new SortDescription(nameof(ProfileFileInfo.Name), ListSortDirection.Ascending)); + SelectedProfileFile = ProfileFiles.Cast().FirstOrDefault(); + LoadSettings(); } @@ -101,52 +103,113 @@ private static void OpenLocationAction() Process.Start("explorer.exe", ProfileManager.GetProfilesFolderLocation()); } - public ICommand AddProfileFileCommand => new RelayCommand(_ => AddProfileFileAction()); + public ICommand AddProfileFileCommand => new RelayCommand(async _ => await AddProfileFileAction().ConfigureAwait(false)); - private async void AddProfileFileAction() + private async Task AddProfileFileAction() { - var customDialog = new CustomDialog - { - Title = Strings.AddProfileFile - }; + var profileName = string.Empty; + + var childWindow = new ProfileFileChildWindow(); - var profileFileViewModel = new ProfileFileViewModel(async instance => + var childWindowViewModel = new ProfileFileViewModel(instance => { - await _dialogCoordinator.HideMetroDialogAsync(this, customDialog); + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; + + profileName = instance.Name; ProfileManager.CreateEmptyProfileFile(instance.Name); - }, async _ => { await _dialogCoordinator.HideMetroDialogAsync(this, customDialog); }); + }, _ => + { + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; + }); + + childWindow.Title = Strings.AddProfileFile; + + childWindow.DataContext = childWindowViewModel; + + ConfigurationManager.Current.IsChildWindowOpen = true; + + await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow); + + if (string.IsNullOrEmpty(profileName)) + return; + + SelectedProfileFile = ProfileFiles.Cast() + .FirstOrDefault(p => p.Name.Equals(profileName, StringComparison.OrdinalIgnoreCase)); - customDialog.Content = new ProfileFileDialog + // Ask to enable encryption for the new profile file + if (await ShowEnableEncryptionMessage()) + EnableEncryptionAction(); + } + + private async Task ShowEnableEncryptionMessage() + { + var result = false; + + var childWindow = new OKCancelInfoMessageChildWindow(); + + var childWindowViewModel = new OKCancelInfoMessageViewModel(_ => { - DataContext = profileFileViewModel - }; + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; - await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog); + result = true; + }, _ => + { + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; + }, + Strings.EnableEncryptionForProfileFileMessage + ); + + childWindow.Title = Strings.EnableEncryptionQuestion; + + childWindow.DataContext = childWindowViewModel; + + ConfigurationManager.Current.IsChildWindowOpen = true; + + await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow); + + return result; } - public ICommand EditProfileFileCommand => new RelayCommand(_ => EditProfileFileAction()); + public ICommand EditProfileFileCommand => new RelayCommand(async _ => await EditProfileFileAction().ConfigureAwait(false)); - private async void EditProfileFileAction() + private async Task EditProfileFileAction() { - var customDialog = new CustomDialog - { - Title = Strings.EditProfileFile - }; + var profileName = string.Empty; + + var childWindow = new ProfileFileChildWindow(); - var profileFileViewModel = new ProfileFileViewModel(async instance => + var childWindowViewModel = new ProfileFileViewModel(instance => { - await _dialogCoordinator.HideMetroDialogAsync(this, customDialog); + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; - ProfileManager.RenameProfileFile(SelectedProfileFile, instance.Name); - }, async _ => { await _dialogCoordinator.HideMetroDialogAsync(this, customDialog); }, SelectedProfileFile); + profileName = instance.Name; - customDialog.Content = new ProfileFileDialog + ProfileManager.RenameProfileFile(SelectedProfileFile, instance.Name); + }, _ => { - DataContext = profileFileViewModel - }; + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; + }, SelectedProfileFile); - await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog); + childWindow.Title = Strings.EditProfileFile; + + childWindow.DataContext = childWindowViewModel; + + ConfigurationManager.Current.IsChildWindowOpen = true; + + await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow); + + if (string.IsNullOrEmpty(profileName)) + return; + + SelectedProfileFile = ProfileFiles.Cast() + .FirstOrDefault(p => p.Name.Equals(profileName, StringComparison.OrdinalIgnoreCase)); } public ICommand DeleteProfileFileCommand => @@ -172,7 +235,7 @@ private Task DeleteProfileFileAction() childWindow.IsOpen = false; ConfigurationManager.Current.IsChildWindowOpen = false; }, - Strings.DeleteProfileFileMessage, Strings.Delete); + string.Format(Strings.DeleteProfileFileXMessage, SelectedProfileFile.Name), Strings.Delete); childWindow.Title = Strings.DeleteProfileFile; @@ -187,13 +250,7 @@ private Task DeleteProfileFileAction() private async void EnableEncryptionAction() { - var settings = AppearanceManager.MetroDialog; - - settings.AffirmativeButtonText = Strings.OK; - settings.NegativeButtonText = Strings.Cancel; - settings.DefaultButtonFocus = MessageDialogResult.Affirmative; - - if (await _dialogCoordinator.ShowMessageAsync(this, Strings.Disclaimer, Strings.ProfileEncryptionDisclaimer, MessageDialogStyle.AffirmativeAndNegative, settings) != MessageDialogResult.Affirmative) + if (!await ShowEncryptionDisclaimerAsync()) return; var customDialog = new CustomDialog @@ -228,6 +285,37 @@ await _dialogCoordinator.ShowMessageAsync(this, Strings.EncryptionError, await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog); } + private async Task ShowEncryptionDisclaimerAsync() + { + var result = false; + + var childWindow = new OKCancelInfoMessageChildWindow(); + + var childWindowViewModel = new OKCancelInfoMessageViewModel(_ => + { + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; + + result = true; + }, _ => + { + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; + }, + Strings.ProfileEncryptionDisclaimer + ); + + childWindow.Title = Strings.Disclaimer; + + childWindow.DataContext = childWindowViewModel; + + ConfigurationManager.Current.IsChildWindowOpen = true; + + await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow); + + return result; + } + public ICommand ChangeMasterPasswordCommand => new RelayCommand(_ => ChangeMasterPasswordAction()); private async void ChangeMasterPasswordAction() diff --git a/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml b/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml index 9a0514cd2b..480e739d72 100644 --- a/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml +++ b/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml @@ -8,7 +8,7 @@ xmlns:simpleChildWindow="clr-namespace:MahApps.Metro.SimpleChildWindow;assembly=MahApps.Metro.SimpleChildWindow" xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks" xmlns:converters="clr-namespace:NETworkManager.Converters;assembly=NETworkManager.Converters" - Loaded="ChildWindow_Loaded" + Loaded="ChildWindow_OnLoaded" Style="{StaticResource DefaultChildWindow}" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:OKCancelInfoMessageViewModel}"> diff --git a/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml.cs b/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml.cs index d0fba4585e..5853058f48 100644 --- a/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml.cs +++ b/Source/NETworkManager/Views/OKCancelInfoMessageChildWindow.xaml.cs @@ -10,7 +10,7 @@ public OKCancelInfoMessageChildWindow() InitializeComponent(); } - private void ChildWindow_Loaded(object sender, System.Windows.RoutedEventArgs e) + private void ChildWindow_OnLoaded(object sender, System.Windows.RoutedEventArgs e) { Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(delegate { diff --git a/Source/NETworkManager/Views/OKMessageChildWindow.xaml b/Source/NETworkManager/Views/OKMessageChildWindow.xaml index 2193eddd6b..d743b51e67 100644 --- a/Source/NETworkManager/Views/OKMessageChildWindow.xaml +++ b/Source/NETworkManager/Views/OKMessageChildWindow.xaml @@ -8,14 +8,13 @@ xmlns:simpleChildWindow="clr-namespace:MahApps.Metro.SimpleChildWindow;assembly=MahApps.Metro.SimpleChildWindow" xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks" xmlns:converters="clr-namespace:NETworkManager.Converters;assembly=NETworkManager.Converters" - Loaded="ChildWindow_Loaded" + Loaded="ChildWindow_OnLoaded" Style="{StaticResource DefaultChildWindow}" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:OKMessageViewModel}"> - - + diff --git a/Source/NETworkManager/Views/OKMessageChildWindow.xaml.cs b/Source/NETworkManager/Views/OKMessageChildWindow.xaml.cs index a6a48d5e49..b4a07654d1 100644 --- a/Source/NETworkManager/Views/OKMessageChildWindow.xaml.cs +++ b/Source/NETworkManager/Views/OKMessageChildWindow.xaml.cs @@ -10,7 +10,7 @@ public OKMessageChildWindow() InitializeComponent(); } - private void ChildWindow_Loaded(object sender, System.Windows.RoutedEventArgs e) + private void ChildWindow_OnLoaded(object sender, System.Windows.RoutedEventArgs e) { Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(delegate { diff --git a/Source/NETworkManager/Views/ProfileFileDialog.xaml b/Source/NETworkManager/Views/ProfileFileChildWindow.xaml similarity index 66% rename from Source/NETworkManager/Views/ProfileFileDialog.xaml rename to Source/NETworkManager/Views/ProfileFileChildWindow.xaml index 3bcb7756b1..d990e6dc9c 100644 --- a/Source/NETworkManager/Views/ProfileFileDialog.xaml +++ b/Source/NETworkManager/Views/ProfileFileChildWindow.xaml @@ -1,15 +1,20 @@ - - + + @@ -30,7 +35,7 @@ + mah:TextBoxHelper.Watermark="{x:Static localization:Strings.ExampleProfileFileName}"> @@ -51,7 +56,7 @@ + Value="False" /> @@ -65,7 +70,7 @@