From 98ac418f62ebafc5c8c1e9516a07b1a8ebdd6d4d Mon Sep 17 00:00:00 2001 From: Geoffroy BONNEVILLE Date: Wed, 15 Apr 2020 19:06:13 +0200 Subject: [PATCH] WIP History Restore from history works --- ModernKeePass.Application/Application.csproj | 1 + .../Common/Interfaces/IDatabaseProxy.cs | 4 +- .../SaveDatabase/SaveDatabaseCommand.cs | 2 +- .../RestoreHistory/RestoreHistoryCommand.cs | 29 ++++++++ .../KeePass/KeePassDatabaseClient.cs | 7 ++ ModernKeePass/ViewModels/EntryDetailVm.cs | 31 +++++++-- ModernKeePass/Views/EntryDetailPage.xaml | 11 ++- ModernKeePass/Views/EntryDetailPage.xaml.cs | 7 +- .../UserControls/TopMenuUserControl.xaml | 20 ++++-- .../UserControls/TopMenuUserControl.xaml.cs | 69 +++++++++++++++---- 10 files changed, 144 insertions(+), 37 deletions(-) create mode 100644 ModernKeePass.Application/Entry/Commands/RestoreHistory/RestoreHistoryCommand.cs diff --git a/ModernKeePass.Application/Application.csproj b/ModernKeePass.Application/Application.csproj index 1659906..ad4f0f3 100644 --- a/ModernKeePass.Application/Application.csproj +++ b/ModernKeePass.Application/Application.csproj @@ -89,6 +89,7 @@ + diff --git a/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs b/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs index 6906743..1fc3e48 100644 --- a/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs +++ b/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs @@ -22,7 +22,6 @@ namespace ModernKeePass.Application.Common.Interfaces Task Open(byte[] file, Credentials credentials); Task ReOpen(byte[] file); - void AddHistory(string entryId); Task Create(Credentials credentials, string name, DatabaseVersion version = DatabaseVersion.V2); Task SaveDatabase(); Task SaveDatabase(byte[] newFileContents); @@ -44,6 +43,9 @@ namespace ModernKeePass.Application.Common.Interfaces void SortEntries(string groupId); void SortSubGroups(string groupId); + void AddHistory(string entryId); + void RestoreFromHistory(string entryId, int historyIndex); + IEnumerable Search(string groupId, string text); } } \ No newline at end of file diff --git a/ModernKeePass.Application/Database/Commands/SaveDatabase/SaveDatabaseCommand.cs b/ModernKeePass.Application/Database/Commands/SaveDatabase/SaveDatabaseCommand.cs index 36eafb3..9453d7c 100644 --- a/ModernKeePass.Application/Database/Commands/SaveDatabase/SaveDatabaseCommand.cs +++ b/ModernKeePass.Application/Database/Commands/SaveDatabase/SaveDatabaseCommand.cs @@ -51,7 +51,7 @@ namespace ModernKeePass.Application.Database.Commands.SaveDatabase _database.IsDirty = false; } - catch (ArgumentException exception) + catch (Exception exception) { throw new SaveException(exception); } diff --git a/ModernKeePass.Application/Entry/Commands/RestoreHistory/RestoreHistoryCommand.cs b/ModernKeePass.Application/Entry/Commands/RestoreHistory/RestoreHistoryCommand.cs new file mode 100644 index 0000000..1fd4373 --- /dev/null +++ b/ModernKeePass.Application/Entry/Commands/RestoreHistory/RestoreHistoryCommand.cs @@ -0,0 +1,29 @@ +using MediatR; +using ModernKeePass.Application.Common.Interfaces; +using ModernKeePass.Domain.Exceptions; + +namespace ModernKeePass.Application.Entry.Commands.RestoreHistory +{ + public class RestoreHistoryCommand : IRequest + { + public string EntryId { get; set; } + public int HistoryIndex { get; set; } + + public class RestoreHistoryCommandHandler : IRequestHandler + { + private readonly IDatabaseProxy _database; + + public RestoreHistoryCommandHandler(IDatabaseProxy database) + { + _database = database; + } + + public void Handle(RestoreHistoryCommand message) + { + if (!_database.IsOpen) throw new DatabaseClosedException(); + + _database.RestoreFromHistory(message.EntryId, message.HistoryIndex); + } + } + } +} \ No newline at end of file diff --git a/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs b/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs index 5b878f7..c28a288 100644 --- a/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs +++ b/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs @@ -259,6 +259,13 @@ namespace ModernKeePass.Infrastructure.KeePass pwEntry.CreateBackup(null); } + public void RestoreFromHistory(string entryId, int historyIndex) + { + var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true); + pwEntry.RestoreFromBackup((uint)historyIndex, _pwDatabase); + pwEntry.Touch(true); + } + public void UpdateGroup(string groupId) { throw new NotImplementedException(); diff --git a/ModernKeePass/ViewModels/EntryDetailVm.cs b/ModernKeePass/ViewModels/EntryDetailVm.cs index 7c43227..c5bef11 100644 --- a/ModernKeePass/ViewModels/EntryDetailVm.cs +++ b/ModernKeePass/ViewModels/EntryDetailVm.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Threading.Tasks; using System.Windows.Input; using Windows.UI.Xaml.Controls; @@ -10,6 +11,7 @@ using ModernKeePass.Application.Database.Commands.SaveDatabase; using ModernKeePass.Application.Database.Models; using ModernKeePass.Application.Database.Queries.GetDatabase; using ModernKeePass.Application.Entry.Commands.AddHistory; +using ModernKeePass.Application.Entry.Commands.RestoreHistory; using ModernKeePass.Application.Entry.Commands.SetFieldValue; using ModernKeePass.Application.Entry.Models; using ModernKeePass.Application.Entry.Queries.GetEntry; @@ -173,7 +175,8 @@ namespace ModernKeePass.ViewModels SetFieldValue(nameof(ForegroundColor), _entry.ForegroundColor).Wait(); } } - public IEnumerable History { get; } + + public ObservableCollection History { get; } public bool IsEditMode { @@ -190,6 +193,7 @@ namespace ModernKeePass.ViewModels public ICommand SaveCommand { get; } public ICommand GeneratePasswordCommand { get; } public ICommand MoveCommand { get; } + public ICommand RestoreCommand { get; } private DatabaseVm Database => _mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult(); @@ -208,16 +212,21 @@ namespace ModernKeePass.ViewModels public EntryDetailVm(string entryId, IMediator mediator) { _mediator = mediator; - _entry = _mediator.Send(new GetEntryQuery {Id = entryId}).GetAwaiter().GetResult(); - _parent = _mediator.Send(new GetGroupQuery {Id = _entry.ParentGroupId}).GetAwaiter().GetResult(); - History = _entry.History; + _entry = _mediator.Send(new GetEntryQuery { Id = entryId }).GetAwaiter().GetResult(); + _parent = _mediator.Send(new GetGroupQuery { Id = _entry.ParentGroupId }).GetAwaiter().GetResult(); + History = new ObservableCollection {_entry}; + foreach (var entry in _entry.History) + { + History.Add(entry); + } IsSelected = true; SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty); GeneratePasswordCommand = new RelayCommand(async () => await GeneratePassword()); MoveCommand = new RelayCommand(async () => await Move(_parent), () => _parent != null); + RestoreCommand = new RelayCommand(async () => await RestoreHistory()); } - + public async Task GeneratePassword() { Password = await _mediator.Send(new GeneratePasswordCommand @@ -256,7 +265,7 @@ namespace ModernKeePass.ViewModels public async Task AddHistory() { - if (_entry.IsDirty) await _mediator.Send(new AddHistoryCommand {EntryId = Id}); + if (_entry.IsDirty) await _mediator.Send(new AddHistoryCommand { EntryId = Id }); } internal void SetEntry(EntryVm entry, int index) @@ -266,6 +275,15 @@ namespace ModernKeePass.ViewModels OnPropertyChanged(); } + private async Task RestoreHistory() + { + var index = History.IndexOf(_entry); + var entryToRestore = History[index]; + SetEntry(entryToRestore, 0); + await _mediator.Send(new RestoreHistoryCommand { EntryId = Id, HistoryIndex = index }); + History.Insert(0, entryToRestore); + } + private async Task SaveChanges() { await AddHistory(); @@ -273,6 +291,5 @@ namespace ModernKeePass.ViewModels ((RelayCommand)SaveCommand).RaiseCanExecuteChanged(); _entry.IsDirty = false; } - } } diff --git a/ModernKeePass/Views/EntryDetailPage.xaml b/ModernKeePass/Views/EntryDetailPage.xaml index edcc334..e87892a 100644 --- a/ModernKeePass/Views/EntryDetailPage.xaml +++ b/ModernKeePass/Views/EntryDetailPage.xaml @@ -19,7 +19,6 @@ - - + @@ -530,12 +529,18 @@ + MoveCommand="{Binding MoveCommand}" + RestoreCommand="{Binding RestoreCommand}"> + + + diff --git a/ModernKeePass/Views/EntryDetailPage.xaml.cs b/ModernKeePass/Views/EntryDetailPage.xaml.cs index 3874888..cab2573 100644 --- a/ModernKeePass/Views/EntryDetailPage.xaml.cs +++ b/ModernKeePass/Views/EntryDetailPage.xaml.cs @@ -47,7 +47,7 @@ namespace ModernKeePass.Views if (args != null) { DataContext = new EntryDetailVm(args.Id) { IsEditMode = args.IsNew }; - await Model.GeneratePassword(); + if (args.IsNew) await Model.GeneratePassword(); } } @@ -80,10 +80,5 @@ namespace ModernKeePass.Views break; } } - - private async void Username_OnLostFocus(object sender, RoutedEventArgs e) - { - await Model.SetFieldValue(nameof(Model.UserName), ((TextBox) sender).Text); - } } } diff --git a/ModernKeePass/Views/UserControls/TopMenuUserControl.xaml b/ModernKeePass/Views/UserControls/TopMenuUserControl.xaml index f6d5131..24b3aa0 100644 --- a/ModernKeePass/Views/UserControls/TopMenuUserControl.xaml +++ b/ModernKeePass/Views/UserControls/TopMenuUserControl.xaml @@ -42,17 +42,24 @@ - + - @@ -88,10 +95,11 @@ - + + - + diff --git a/ModernKeePass/Views/UserControls/TopMenuUserControl.xaml.cs b/ModernKeePass/Views/UserControls/TopMenuUserControl.xaml.cs index 481771d..adfb15a 100644 --- a/ModernKeePass/Views/UserControls/TopMenuUserControl.xaml.cs +++ b/ModernKeePass/Views/UserControls/TopMenuUserControl.xaml.cs @@ -56,6 +56,18 @@ namespace ModernKeePass.Views.UserControls typeof(TopMenuUserControl), new PropertyMetadata(null, (o, args) => { })); + public ICommand RestoreCommand + { + get { return (ICommand)GetValue(RestoreCommandProperty); } + set { SetValue(RestoreCommandProperty, value); } + } + public static readonly DependencyProperty RestoreCommandProperty = + DependencyProperty.Register( + nameof(RestoreCommand), + typeof(ICommand), + typeof(TopMenuUserControl), + new PropertyMetadata(null, (o, args) => { })); + public ICommand SortEntriesCommand { get { return (ICommand)GetValue(SortEntriesCommandProperty); } @@ -79,18 +91,6 @@ namespace ModernKeePass.Views.UserControls typeof(ICommand), typeof(TopMenuUserControl), new PropertyMetadata(null, (o, args) => { })); - - public bool IsMoveButtonEnabled - { - get { return (bool)GetValue(IsMoveButtonEnabledProperty); } - set { SetValue(IsMoveButtonEnabledProperty, value); } - } - public static readonly DependencyProperty IsMoveButtonEnabledProperty = - DependencyProperty.Register( - nameof(IsMoveButtonEnabled), - typeof(bool), - typeof(TopMenuUserControl), - new PropertyMetadata(true, (o, args) => { })); public Visibility SortButtonVisibility { @@ -104,6 +104,30 @@ namespace ModernKeePass.Views.UserControls typeof(TopMenuUserControl), new PropertyMetadata(Visibility.Collapsed, (o, args) => { })); + public Visibility MoveButtonVisibility + { + get { return (Visibility)GetValue(MoveButtonVisibilityProperty); } + set { SetValue(MoveButtonVisibilityProperty, value); } + } + public static readonly DependencyProperty MoveButtonVisibilityProperty = + DependencyProperty.Register( + nameof(MoveButtonVisibility), + typeof(Visibility), + typeof(TopMenuUserControl), + new PropertyMetadata(Visibility.Collapsed, (o, args) => { })); + + public Visibility RestoreButtonVisibility + { + get { return (Visibility)GetValue(RestoreButtonVisibilityProperty); } + set { SetValue(RestoreButtonVisibilityProperty, value); } + } + public static readonly DependencyProperty RestoreButtonVisibilityProperty = + DependencyProperty.Register( + nameof(RestoreButtonVisibility), + typeof(Visibility), + typeof(TopMenuUserControl), + new PropertyMetadata(Visibility.Collapsed, (o, args) => { })); + public bool IsDeleteButtonEnabled { get { return (bool)GetValue(IsDeleteButtonEnabledProperty); } @@ -116,6 +140,18 @@ namespace ModernKeePass.Views.UserControls typeof(TopMenuUserControl), new PropertyMetadata(true, (o, args) => { })); + public bool IsMoveButtonEnabled + { + get { return (bool)GetValue(IsMoveButtonEnabledProperty); } + set { SetValue(IsMoveButtonEnabledProperty, value); } + } + public static readonly DependencyProperty IsMoveButtonEnabledProperty = + DependencyProperty.Register( + nameof(IsMoveButtonEnabled), + typeof(bool), + typeof(TopMenuUserControl), + new PropertyMetadata(true, (o, args) => { })); + public bool IsEditButtonChecked { get { return (bool)GetValue(IsEditButtonCheckedProperty); } @@ -131,6 +167,7 @@ namespace ModernKeePass.Views.UserControls public event EventHandler EditButtonClick; public event EventHandler DeleteButtonClick; public event EventHandler MoveButtonClick; + public event EventHandler RestoreButtonClick; public TopMenuUserControl() { @@ -158,6 +195,10 @@ namespace ModernKeePass.Views.UserControls { MoveButtonClick?.Invoke(sender, e); } + private void RestoreButton_Click(object sender, RoutedEventArgs e) + { + RestoreButtonClick?.Invoke(sender, e); + } private void OverflowFlyout_OnOpening(object sender, object e) { @@ -166,7 +207,8 @@ namespace ModernKeePass.Views.UserControls EditFlyout.IsChecked = IsEditButtonChecked; - MoveFlyout.IsEnabled = IsMoveButtonEnabled; + MoveFlyout.Visibility = MoveButtonVisibility; + RestoreFlyout.Visibility = RestoreButtonVisibility; SortEntriesFlyout.Visibility = SortButtonVisibility; SortGroupsFlyout.Visibility = SortButtonVisibility; @@ -179,5 +221,6 @@ namespace ModernKeePass.Views.UserControls SortEntriesButtonFlyout.Command = SortEntriesCommand; SortGroupsButtonFlyout.Command = SortGroupsCommand; } + } }