diff --git a/ModernKeePass.Application/Application.csproj b/ModernKeePass.Application/Application.csproj index baba6de..9ea0f1f 100644 --- a/ModernKeePass.Application/Application.csproj +++ b/ModernKeePass.Application/Application.csproj @@ -91,6 +91,7 @@ + @@ -121,8 +122,8 @@ - - + + diff --git a/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs b/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs index a91a751..c73ea5e 100644 --- a/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs +++ b/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs @@ -31,19 +31,22 @@ namespace ModernKeePass.Application.Common.Interfaces void CloseDatabase(); EntryEntity GetEntry(string id); - GroupEntity GetGroup(string id); Task AddEntry(string parentGroupId, string entryId); Task MoveEntry(string parentGroupId, string entryId, int index); - Task AddGroup(string parentGroupId, string groupId); void UpdateEntry(string entryId, string fieldName, object fieldValue); - void UpdateGroup(GroupEntity group); + void DeleteField(string entryId, string fieldName); Task RemoveEntry(string parentGroupId, string entryId); + EntryEntity CreateEntry(string parentGroupId); + void SortEntries(string groupId); + + GroupEntity GetGroup(string id); + Task AddGroup(string parentGroupId, string groupId); + void UpdateGroup(GroupEntity group); Task RemoveGroup(string parentGroupId, string groupId); void DeleteEntity(string entityId); - EntryEntity CreateEntry(string parentGroupId); GroupEntity CreateGroup(string parentGroupId, string name, bool isRecycleBin = false); - void SortEntries(string groupId); void SortSubGroups(string groupId); + void AddAttachment(string entryId, string attachmentName, byte[] attachmentContent); void DeleteAttachment(string entryId, string attachmentName); diff --git a/ModernKeePass.Application/Entry/Commands/DeleteField/DeleteFieldCommand.cs b/ModernKeePass.Application/Entry/Commands/DeleteField/DeleteFieldCommand.cs new file mode 100644 index 0000000..fb1300c --- /dev/null +++ b/ModernKeePass.Application/Entry/Commands/DeleteField/DeleteFieldCommand.cs @@ -0,0 +1,29 @@ +using MediatR; +using ModernKeePass.Application.Common.Interfaces; +using ModernKeePass.Domain.Exceptions; + +namespace ModernKeePass.Application.Entry.Commands.DeleteField +{ + public class DeleteFieldCommand: IRequest + { + public string EntryId { get; set; } + public string FieldName { get; set; } + + public class DeleteFieldCommandHandler : IRequestHandler + { + private readonly IDatabaseProxy _database; + + public DeleteFieldCommandHandler(IDatabaseProxy database) + { + _database = database; + } + + public void Handle(DeleteFieldCommand message) + { + if (!_database.IsOpen) throw new DatabaseClosedException(); + + _database.DeleteField(message.EntryId, message.FieldName); + } + } + } +} \ No newline at end of file diff --git a/ModernKeePass.Application/Entry/Commands/SetFieldValue/SetFieldValueCommand.cs b/ModernKeePass.Application/Entry/Commands/UpsertField/UpsertFieldCommand.cs similarity index 64% rename from ModernKeePass.Application/Entry/Commands/SetFieldValue/SetFieldValueCommand.cs rename to ModernKeePass.Application/Entry/Commands/UpsertField/UpsertFieldCommand.cs index 32176ec..50bb55a 100644 --- a/ModernKeePass.Application/Entry/Commands/SetFieldValue/SetFieldValueCommand.cs +++ b/ModernKeePass.Application/Entry/Commands/UpsertField/UpsertFieldCommand.cs @@ -2,24 +2,24 @@ using ModernKeePass.Application.Common.Interfaces; using ModernKeePass.Domain.Exceptions; -namespace ModernKeePass.Application.Entry.Commands.SetFieldValue +namespace ModernKeePass.Application.Entry.Commands.UpsertField { - public class SetFieldValueCommand : IRequest + public class UpsertFieldCommand : IRequest { public string EntryId { get; set; } public string FieldName { get; set; } public object FieldValue { get; set; } - public class SetFieldValueCommandHandler : IRequestHandler + public class UpsertFieldCommandHandler : IRequestHandler { private readonly IDatabaseProxy _database; - public SetFieldValueCommandHandler(IDatabaseProxy database) + public UpsertFieldCommandHandler(IDatabaseProxy database) { _database = database; } - public void Handle(SetFieldValueCommand message) + public void Handle(UpsertFieldCommand message) { if (!_database.IsOpen) throw new DatabaseClosedException(); diff --git a/ModernKeePass.Application/Entry/Commands/SetFieldValue/SetFieldValueCommandValidator.cs b/ModernKeePass.Application/Entry/Commands/UpsertField/UpsertFieldCommandValidator.cs similarity index 55% rename from ModernKeePass.Application/Entry/Commands/SetFieldValue/SetFieldValueCommandValidator.cs rename to ModernKeePass.Application/Entry/Commands/UpsertField/UpsertFieldCommandValidator.cs index c2f20d1..97a4adb 100644 --- a/ModernKeePass.Application/Entry/Commands/SetFieldValue/SetFieldValueCommandValidator.cs +++ b/ModernKeePass.Application/Entry/Commands/UpsertField/UpsertFieldCommandValidator.cs @@ -1,10 +1,10 @@ using FluentValidation; -namespace ModernKeePass.Application.Entry.Commands.SetFieldValue +namespace ModernKeePass.Application.Entry.Commands.UpsertField { - public class SetFieldValueCommandValidator: AbstractValidator + public class UpsertFieldCommandValidator: AbstractValidator { - public SetFieldValueCommandValidator() + public UpsertFieldCommandValidator() { RuleFor(v => v.EntryId) .NotNull() diff --git a/ModernKeePass.Domain/Domain.csproj b/ModernKeePass.Domain/Domain.csproj index 83a3c0c..1530133 100644 --- a/ModernKeePass.Domain/Domain.csproj +++ b/ModernKeePass.Domain/Domain.csproj @@ -78,7 +78,6 @@ - diff --git a/ModernKeePass.Domain/Dtos/Field.cs b/ModernKeePass.Domain/Dtos/Field.cs deleted file mode 100644 index 7dce65a..0000000 --- a/ModernKeePass.Domain/Dtos/Field.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ModernKeePass.Domain.Dtos -{ - public class Field - { - public string Name { get; set; } - public string Value { get; set; } - } -} \ No newline at end of file diff --git a/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs b/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs index 513d2d9..b706b06 100644 --- a/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs +++ b/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs @@ -242,9 +242,18 @@ namespace ModernKeePass.Infrastructure.KeePass case EntryFieldName.ForegroundColor: pwEntry.ForegroundColor = (Color)fieldValue; break; + default: + pwEntry.Strings.Set(fieldName, new ProtectedString(true, fieldValue.ToString())); + break; } } - + + public void DeleteField(string entryId, string fieldName) + { + var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true); + pwEntry.Strings.Remove(fieldName); + } + public EntryEntity AddHistory(string entryId) { var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true); @@ -306,7 +315,7 @@ namespace ModernKeePass.Infrastructure.KeePass var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(groupId), true); pwGroup.SortSubGroups(false); } - + public void AddAttachment(string entryId, string attachmentName, byte[] attachmentContent) { var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true); diff --git a/ModernKeePass/Strings/en-US/Resources.resw b/ModernKeePass/Strings/en-US/Resources.resw index 188f861..52518cd 100644 --- a/ModernKeePass/Strings/en-US/Resources.resw +++ b/ModernKeePass/Strings/en-US/Resources.resw @@ -534,4 +534,10 @@ Add attachment + + Add field + + + Delete + \ No newline at end of file diff --git a/ModernKeePass/Strings/fr-FR/CodeBehind.resw b/ModernKeePass/Strings/fr-FR/CodeBehind.resw index 022f0ba..c8e960a 100644 --- a/ModernKeePass/Strings/fr-FR/CodeBehind.resw +++ b/ModernKeePass/Strings/fr-FR/CodeBehind.resw @@ -276,4 +276,7 @@ Clé + + Ajouter un champ + \ No newline at end of file diff --git a/ModernKeePass/Strings/fr-FR/Resources.resw b/ModernKeePass/Strings/fr-FR/Resources.resw index bba4cf5..9bb6e82 100644 --- a/ModernKeePass/Strings/fr-FR/Resources.resw +++ b/ModernKeePass/Strings/fr-FR/Resources.resw @@ -534,4 +534,7 @@ Ajouter une pièce jointe + + Supprimer + \ No newline at end of file diff --git a/ModernKeePass/ViewModels/EntryDetailVm.cs b/ModernKeePass/ViewModels/EntryDetailVm.cs index 714f03a..71cf585 100644 --- a/ModernKeePass/ViewModels/EntryDetailVm.cs +++ b/ModernKeePass/ViewModels/EntryDetailVm.cs @@ -16,9 +16,10 @@ using ModernKeePass.Application.Database.Queries.GetDatabase; using ModernKeePass.Application.Entry.Commands.AddAttachment; using ModernKeePass.Application.Entry.Commands.AddHistory; using ModernKeePass.Application.Entry.Commands.DeleteAttachment; +using ModernKeePass.Application.Entry.Commands.DeleteField; using ModernKeePass.Application.Entry.Commands.DeleteHistory; using ModernKeePass.Application.Entry.Commands.RestoreHistory; -using ModernKeePass.Application.Entry.Commands.SetFieldValue; +using ModernKeePass.Application.Entry.Commands.UpsertField; using ModernKeePass.Application.Entry.Models; using ModernKeePass.Application.Entry.Queries.GetEntry; using ModernKeePass.Application.Group.Commands.AddEntry; @@ -34,6 +35,7 @@ using ModernKeePass.Domain.Dtos; using ModernKeePass.Domain.Exceptions; using ModernKeePass.Extensions; using ModernKeePass.Models; +using ModernKeePass.ViewModels.ListItems; namespace ModernKeePass.ViewModels { @@ -66,7 +68,7 @@ namespace ModernKeePass.ViewModels } public ObservableCollection History { get; private set; } - public ObservableCollection AdditionalFields { get; private set; } + public ObservableCollection AdditionalFields { get; private set; } public ObservableCollection Attachments { get; private set; } /// @@ -82,11 +84,9 @@ namespace ModernKeePass.ViewModels Set(() => SelectedItem, ref _selectedItem, value, true); if (value != null) { - AdditionalFields = new ObservableCollection(SelectedItem.AdditionalFields.Select(f => new Field - { - Name = f.Key, - Value = f.Value - })); + AdditionalFields = + new ObservableCollection( + SelectedItem.AdditionalFields.Select(f => new FieldVm(f.Key, f.Value))); Attachments = new ObservableCollection(SelectedItem.Attachments.Select(f => new Attachment { Name = f.Key, @@ -113,6 +113,16 @@ namespace ModernKeePass.ViewModels } } + public int AdditionalFieldSelectedIndex + { + get { return _additionalFieldSelectedIndex; } + set + { + Set(() => AdditionalFieldSelectedIndex, ref _additionalFieldSelectedIndex, value); + DeleteAdditionalField.RaiseCanExecuteChanged(); + } + } + public double PasswordLength { get { return _passwordLength; } @@ -258,6 +268,8 @@ namespace ModernKeePass.ViewModels public RelayCommand DeleteCommand { get; } public RelayCommand GoBackCommand { get; } public RelayCommand GoToParentCommand { get; set; } + public RelayCommand AddAdditionalField { get; set; } + public RelayCommand DeleteAdditionalField { get; set; } public RelayCommand OpenAttachmentCommand { get; set; } public RelayCommand AddAttachmentCommand { get; set; } public RelayCommand DeleteAttachmentCommand { get; set; } @@ -273,11 +285,12 @@ namespace ModernKeePass.ViewModels private GroupVm _parent; private EntryVm _selectedItem; private int _selectedIndex; + private int _additionalFieldSelectedIndex; private bool _isEditMode; private bool _isRevealPassword; private double _passwordLength = 25; private bool _isDirty; - + public EntryDetailVm(IMediator mediator, INavigationService navigation, IResourceProxy resource, IDialogService dialog, INotificationService notification, IFileProxy file) { _mediator = mediator; @@ -294,13 +307,17 @@ namespace ModernKeePass.ViewModels DeleteCommand = new RelayCommand(async () => await AskForDelete()); GoBackCommand = new RelayCommand(() => _navigation.GoBack()); GoToParentCommand = new RelayCommand(() => GoToGroup(_parent.Id)); + AddAdditionalField = new RelayCommand(AddField); + DeleteAdditionalField = new RelayCommand(async field => await DeleteField(field), field => field != null); OpenAttachmentCommand = new RelayCommand(async attachment => await OpenAttachment(attachment)); AddAttachmentCommand = new RelayCommand(async () => await AddAttachment(), () => IsCurrentEntry); DeleteAttachmentCommand = new RelayCommand(async attachment => await DeleteAttachment(attachment)); MessengerInstance.Register(this, _ => SaveCommand.RaiseCanExecuteChanged()); + MessengerInstance.Register(this, async message => await SetFieldValue(message.FieldName, message.FieldValue)); + MessengerInstance.Register(this, async message => await UpdateFieldName(message.OldName, message.NewName, message.Value)); } - + public async Task Initialize(string entryId) { SelectedIndex = 0; @@ -314,6 +331,70 @@ namespace ModernKeePass.ViewModels History.CollectionChanged += (sender, args) => SaveCommand.RaiseCanExecuteChanged(); } + public async Task GeneratePassword() + { + Password = await _mediator.Send(new GeneratePasswordCommand + { + BracketsPatternSelected = BracketsPatternSelected, + CustomChars = CustomChars, + DigitsPatternSelected = DigitsPatternSelected, + LowerCasePatternSelected = LowerCasePatternSelected, + MinusPatternSelected = MinusPatternSelected, + PasswordLength = (int)PasswordLength, + SpacePatternSelected = SpacePatternSelected, + SpecialPatternSelected = SpecialPatternSelected, + UnderscorePatternSelected = UnderscorePatternSelected, + UpperCasePatternSelected = UpperCasePatternSelected + }); + RaisePropertyChanged(nameof(IsRevealPasswordEnabled)); + } + public async Task AddHistory() + { + if (_isDirty) await _mediator.Send(new AddHistoryCommand { Entry = History[0] }); + } + + public void GoToGroup(string groupId) + { + _navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem { Id = groupId }); + } + + private async Task Move(string destination) + { + await _mediator.Send(new AddEntryCommand { ParentGroupId = destination, EntryId = Id }); + await _mediator.Send(new RemoveEntryCommand { ParentGroupId = _parent.Id, EntryId = Id }); + GoToGroup(destination); + } + + private async Task SetFieldValue(string fieldName, object value) + { + await _mediator.Send(new UpsertFieldCommand { EntryId = Id, FieldName = fieldName, FieldValue = value }); + SaveCommand.RaiseCanExecuteChanged(); + _isDirty = true; + } + + private async Task UpdateFieldName(string oldName, string newName, string value) + { + if (!string.IsNullOrEmpty(oldName)) await _mediator.Send(new DeleteFieldCommand { EntryId = Id, FieldName = oldName }); + await SetFieldValue(newName, value); + } + + private void AddField() + { + AdditionalFields.Add(new FieldVm(string.Empty, string.Empty)); + AdditionalFieldSelectedIndex = AdditionalFields.Count - 1; + } + + private async Task DeleteField(FieldVm field) + { + AdditionalFields.Remove(field); + if (!string.IsNullOrEmpty(field.Name)) + { + await _mediator.Send(new DeleteFieldCommand {EntryId = Id, FieldName = field.Name}); + SaveCommand.RaiseCanExecuteChanged(); + _isDirty = true; + } + } + private async Task AskForDelete() { if (IsCurrentEntry) @@ -349,48 +430,6 @@ namespace ModernKeePass.ViewModels } } - public async Task GeneratePassword() - { - Password = await _mediator.Send(new GeneratePasswordCommand - { - BracketsPatternSelected = BracketsPatternSelected, - CustomChars = CustomChars, - DigitsPatternSelected = DigitsPatternSelected, - LowerCasePatternSelected = LowerCasePatternSelected, - MinusPatternSelected = MinusPatternSelected, - PasswordLength = (int)PasswordLength, - SpacePatternSelected = SpacePatternSelected, - SpecialPatternSelected = SpecialPatternSelected, - UnderscorePatternSelected = UnderscorePatternSelected, - UpperCasePatternSelected = UpperCasePatternSelected - }); - RaisePropertyChanged(nameof(IsRevealPasswordEnabled)); - } - - public async Task Move(string destination) - { - await _mediator.Send(new AddEntryCommand { ParentGroupId = destination, EntryId = Id }); - await _mediator.Send(new RemoveEntryCommand { ParentGroupId = _parent.Id, EntryId = Id }); - GoToGroup(destination); - } - - public async Task SetFieldValue(string fieldName, object value) - { - await _mediator.Send(new SetFieldValueCommand { EntryId = Id, FieldName = fieldName, FieldValue = value }); - SaveCommand.RaiseCanExecuteChanged(); - _isDirty = true; - } - - public async Task AddHistory() - { - if (_isDirty) await _mediator.Send(new AddHistoryCommand { Entry = History[0] }); - } - - public void GoToGroup(string groupId) - { - _navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem { Id = groupId }); - } - private async Task RestoreHistory() { await _mediator.Send(new RestoreHistoryCommand { Entry = History[0], HistoryIndex = History.Count - SelectedIndex - 1 }); @@ -442,12 +481,16 @@ namespace ModernKeePass.ViewModels var contents = await _file.ReadBinaryFile(fileInfo.Id); await _mediator.Send(new AddAttachmentCommand { Entry = SelectedItem, AttachmentName = fileInfo.Name, AttachmentContent = contents }); Attachments.Add(new Attachment { Name = fileInfo.Name, Content = contents }); + SaveCommand.RaiseCanExecuteChanged(); + _isDirty = true; } private async Task DeleteAttachment(Attachment attachment) { await _mediator.Send(new DeleteAttachmentCommand { Entry = SelectedItem, AttachmentName = attachment.Name }); Attachments.Remove(attachment); + SaveCommand.RaiseCanExecuteChanged(); + _isDirty = true; } } diff --git a/ModernKeePass/Views/EntryDetailPage.xaml b/ModernKeePass/Views/EntryDetailPage.xaml index 880ca28..bf41c8b 100644 --- a/ModernKeePass/Views/EntryDetailPage.xaml +++ b/ModernKeePass/Views/EntryDetailPage.xaml @@ -482,29 +482,40 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + +