Display a big database size warning

Auto rename additional field when it matches standard
Treated all fields as new Field class
Added the Is Protected property
This commit is contained in:
Geoffroy BONNEVILLE
2020-05-12 17:14:30 +02:00
parent d6dc6a74a3
commit f8f7c19f65
34 changed files with 300 additions and 159 deletions

View File

@@ -94,6 +94,7 @@
<Compile Include="Entry\Commands\DeleteField\DeleteFieldCommand.cs" /> <Compile Include="Entry\Commands\DeleteField\DeleteFieldCommand.cs" />
<Compile Include="Entry\Commands\DeleteHistory\DeleteHistoryCommand.cs" /> <Compile Include="Entry\Commands\DeleteHistory\DeleteHistoryCommand.cs" />
<Compile Include="Entry\Commands\RestoreHistory\RestoreHistoryCommand.cs" /> <Compile Include="Entry\Commands\RestoreHistory\RestoreHistoryCommand.cs" />
<Compile Include="Entry\Models\FieldVm.cs" />
<Compile Include="Entry\Queries\GetEntry\GetEntryQuery.cs" /> <Compile Include="Entry\Queries\GetEntry\GetEntryQuery.cs" />
<Compile Include="Group\Commands\DeleteEntry\DeleteEntryCommand.cs" /> <Compile Include="Group\Commands\DeleteEntry\DeleteEntryCommand.cs" />
<Compile Include="Group\Commands\DeleteGroup\DeleteGroupCommand.cs" /> <Compile Include="Group\Commands\DeleteGroup\DeleteGroupCommand.cs" />

View File

@@ -33,7 +33,7 @@ namespace ModernKeePass.Application.Common.Interfaces
EntryEntity GetEntry(string id); EntryEntity GetEntry(string id);
Task AddEntry(string parentGroupId, string entryId); Task AddEntry(string parentGroupId, string entryId);
Task MoveEntry(string parentGroupId, string entryId, int index); Task MoveEntry(string parentGroupId, string entryId, int index);
void UpdateEntry(string entryId, string fieldName, object fieldValue); void UpdateEntry(string entryId, string fieldName, object fieldValue, bool isProtected);
void DeleteField(string entryId, string fieldName); void DeleteField(string entryId, string fieldName);
Task RemoveEntry(string parentGroupId, string entryId); Task RemoveEntry(string parentGroupId, string entryId);
EntryEntity CreateEntry(string parentGroupId); EntryEntity CreateEntry(string parentGroupId);

View File

@@ -5,7 +5,6 @@ namespace ModernKeePass.Application.Common.Interfaces
public interface IEntityVm public interface IEntityVm
{ {
string Id { get; set; } string Id { get; set; }
string Title { get; set; }
Icon Icon { get; set; } Icon Icon { get; set; }
string ParentGroupId { get; set; } string ParentGroupId { get; set; }
string ParentGroupName { get; set; } string ParentGroupName { get; set; }

View File

@@ -64,17 +64,17 @@ namespace ModernKeePass.Application.Database.Commands.CreateDatabase
_database.UpdateGroup(internetGroup); _database.UpdateGroup(internetGroup);
var sample1 = _database.CreateEntry(_database.RootGroupId); var sample1 = _database.CreateEntry(_database.RootGroupId);
_database.UpdateEntry(sample1.Id, EntryFieldName.Title, "Sample Entry" ); _database.UpdateEntry(sample1.Id, EntryFieldName.Title, "Sample Entry", true);
_database.UpdateEntry(sample1.Id, EntryFieldName.UserName, "Username" ); _database.UpdateEntry(sample1.Id, EntryFieldName.UserName, "Username", true);
_database.UpdateEntry(sample1.Id, EntryFieldName.Password, "Password" ); _database.UpdateEntry(sample1.Id, EntryFieldName.Password, "Password", true);
_database.UpdateEntry(sample1.Id, EntryFieldName.Url, "https://keepass.info/" ); _database.UpdateEntry(sample1.Id, EntryFieldName.Url, "https://keepass.info/", true);
_database.UpdateEntry(sample1.Id, EntryFieldName.Notes, "You may safely delete this sample" ); _database.UpdateEntry(sample1.Id, EntryFieldName.Notes, "You may safely delete this sample", true);
var sample2 = _database.CreateEntry(_database.RootGroupId); var sample2 = _database.CreateEntry(_database.RootGroupId);
_database.UpdateEntry(sample2.Id, EntryFieldName.Title, "Sample Entry #2" ); _database.UpdateEntry(sample2.Id, EntryFieldName.Title, "Sample Entry #2", true);
_database.UpdateEntry(sample2.Id, EntryFieldName.UserName, "Michael321" ); _database.UpdateEntry(sample2.Id, EntryFieldName.UserName, "Michael321", true);
_database.UpdateEntry(sample2.Id, EntryFieldName.Password, "12345" ); _database.UpdateEntry(sample2.Id, EntryFieldName.Password, "12345", true);
_database.UpdateEntry(sample2.Id, EntryFieldName.Url, "https://keepass.info/help/kb/testform.html" ); _database.UpdateEntry(sample2.Id, EntryFieldName.Url, "https://keepass.info/help/kb/testform.html", true);
} }
} }
} }

View File

@@ -9,6 +9,7 @@ namespace ModernKeePass.Application.Entry.Commands.UpsertField
public string EntryId { get; set; } public string EntryId { get; set; }
public string FieldName { get; set; } public string FieldName { get; set; }
public object FieldValue { get; set; } public object FieldValue { get; set; }
public bool IsProtected { get; set; } = true;
public class UpsertFieldCommandHandler : IRequestHandler<UpsertFieldCommand> public class UpsertFieldCommandHandler : IRequestHandler<UpsertFieldCommand>
{ {
@@ -23,7 +24,7 @@ namespace ModernKeePass.Application.Entry.Commands.UpsertField
{ {
if (!_database.IsOpen) throw new DatabaseClosedException(); if (!_database.IsOpen) throw new DatabaseClosedException();
_database.UpdateEntry(message.EntryId, message.FieldName, message.FieldValue); _database.UpdateEntry(message.EntryId, message.FieldName, message.FieldValue, message.IsProtected);
} }
} }
} }

View File

@@ -15,13 +15,13 @@ namespace ModernKeePass.Application.Entry.Models
public string ParentGroupId { get; set; } public string ParentGroupId { get; set; }
public string ParentGroupName { get; set; } public string ParentGroupName { get; set; }
public string Id { get; set; } public string Id { get; set; }
public string Title { get; set; } public FieldVm Title { get; set; }
public string Username { get; set; } public FieldVm Username { get; set; }
public string Password { get; set; } public FieldVm Password { get; set; }
public string Notes { get; set; } public FieldVm Notes { get; set; }
public string Url { get; set; } public FieldVm Url { get; set; }
public bool HasUrl => !string.IsNullOrEmpty(Url); public bool HasUrl => !string.IsNullOrEmpty(Url.Value);
public Dictionary<string, string> AdditionalFields { get; set; } public List<FieldVm> AdditionalFields { get; set; }
public List<EntryVm> History { get; set; } public List<EntryVm> History { get; set; }
public Icon Icon { get; set; } public Icon Icon { get; set; }
public Color ForegroundColor { get; set; } public Color ForegroundColor { get; set; }
@@ -42,12 +42,18 @@ namespace ModernKeePass.Application.Entry.Models
.ForMember(d => d.ParentGroupId, opts => opts.MapFrom(s => s.ParentId)) .ForMember(d => d.ParentGroupId, opts => opts.MapFrom(s => s.ParentId))
.ForMember(d => d.ParentGroupName, opts => opts.MapFrom(s => s.ParentName)) .ForMember(d => d.ParentGroupName, opts => opts.MapFrom(s => s.ParentName))
.ForMember(d => d.Id, opts => opts.MapFrom(s => s.Id)) .ForMember(d => d.Id, opts => opts.MapFrom(s => s.Id))
.ForMember(d => d.Title, opts => opts.MapFrom(s => s.Name)) .ForMember(d => d.Title, opts => opts.MapFrom(s => s.Fields.FirstOrDefault(f =>
.ForMember(d => d.Username, opts => opts.MapFrom(s => s.UserName)) f.Name.Equals(EntryFieldName.Title, StringComparison.OrdinalIgnoreCase)) ?? new FieldEntity { Name = EntryFieldName.Title, IsProtected = true } ))
.ForMember(d => d.Password, opts => opts.MapFrom(s => s.Password)) .ForMember(d => d.Username, opts => opts.MapFrom(s => s.Fields.FirstOrDefault(f =>
.ForMember(d => d.Url, opts => opts.MapFrom(s => s.Url)) f.Name.Equals(EntryFieldName.UserName, StringComparison.OrdinalIgnoreCase)) ?? new FieldEntity { Name = EntryFieldName.UserName, IsProtected = true } ))
.ForMember(d => d.Notes, opts => opts.MapFrom(s => s.Notes)) .ForMember(d => d.Password, opts => opts.MapFrom(s => s.Fields.FirstOrDefault(f =>
.ForMember(d => d.AdditionalFields, opts => opts.MapFrom(s => s.AdditionalFields)) f.Name.Equals(EntryFieldName.Password, StringComparison.OrdinalIgnoreCase)) ?? new FieldEntity { Name = EntryFieldName.Password, IsProtected = true } ))
.ForMember(d => d.Url, opts => opts.MapFrom(s => s.Fields.FirstOrDefault(f =>
f.Name.Equals(EntryFieldName.Url, StringComparison.OrdinalIgnoreCase)) ?? new FieldEntity { Name = EntryFieldName.Url, IsProtected = true } ))
.ForMember(d => d.Notes, opts => opts.MapFrom(s => s.Fields.FirstOrDefault(f =>
f.Name.Equals(EntryFieldName.Notes, StringComparison.OrdinalIgnoreCase)) ?? new FieldEntity { Name = EntryFieldName.Notes, IsProtected = true } ))
.ForMember(d => d.AdditionalFields, opts => opts.MapFrom(s =>
s.Fields.Where(f => !EntryFieldName.StandardFieldNames.Contains(f.Name, StringComparer.OrdinalIgnoreCase))))
.ForMember(d => d.History, opts => opts.MapFrom(s => s.History.Reverse())) .ForMember(d => d.History, opts => opts.MapFrom(s => s.History.Reverse()))
.ForMember(d => d.HasExpirationDate, opts => opts.MapFrom(s => s.HasExpirationDate)) .ForMember(d => d.HasExpirationDate, opts => opts.MapFrom(s => s.HasExpirationDate))
.ForMember(d => d.ExpirationDate, opts => opts.MapFrom(s => s.ExpirationDate)) .ForMember(d => d.ExpirationDate, opts => opts.MapFrom(s => s.ExpirationDate))

View File

@@ -0,0 +1,20 @@
using AutoMapper;
using ModernKeePass.Application.Common.Mappings;
using ModernKeePass.Domain.Entities;
namespace ModernKeePass.Application.Entry.Models
{
public class FieldVm: IMapFrom<FieldEntity>
{
public string Name { get; set; }
public string Value { get; set; }
public bool IsProtected { get; set; }
public override string ToString() => Value;
public void Mapping(Profile profile)
{
profile.CreateMap<FieldEntity, FieldVm>();
}
}
}

View File

@@ -24,7 +24,7 @@ namespace ModernKeePass.Application.Group.Commands.SortEntries
if (!_database.IsOpen) throw new DatabaseClosedException(); if (!_database.IsOpen) throw new DatabaseClosedException();
_database.SortEntries(message.Group.Id); _database.SortEntries(message.Group.Id);
message.Group.Entries = message.Group.Entries.OrderBy(e => e.Title).ToList(); message.Group.Entries = message.Group.Entries.OrderBy(e => e.Title.Value).ToList();
} }
} }
} }

View File

@@ -82,6 +82,7 @@
<Compile Include="Dtos\PasswordGenerationOptions.cs" /> <Compile Include="Dtos\PasswordGenerationOptions.cs" />
<Compile Include="Entities\BaseEntity.cs" /> <Compile Include="Entities\BaseEntity.cs" />
<Compile Include="Entities\EntryEntity.cs" /> <Compile Include="Entities\EntryEntity.cs" />
<Compile Include="Entities\FieldEntity.cs" />
<Compile Include="Entities\GroupEntity.cs" /> <Compile Include="Entities\GroupEntity.cs" />
<Compile Include="Enums\CredentialStatusTypes.cs" /> <Compile Include="Enums\CredentialStatusTypes.cs" />
<Compile Include="Enums\DatabaseVersion.cs" /> <Compile Include="Enums\DatabaseVersion.cs" />

View File

@@ -7,17 +7,13 @@ namespace ModernKeePass.Domain.Entities
{ {
public class EntryEntity: BaseEntity public class EntryEntity: BaseEntity
{ {
public string UserName { get; set; } public IEnumerable<FieldEntity> Fields { get; set; } = new List<FieldEntity>();
public string Password { get; set; }
public string Url { get; set; }
public string Notes { get; set; }
public DateTimeOffset ExpirationDate { get; set; }
public Dictionary<string, string> AdditionalFields { get; set; } = new Dictionary<string, string>();
public IEnumerable<EntryEntity> History { get; set; } = new List<EntryEntity>(); public IEnumerable<EntryEntity> History { get; set; } = new List<EntryEntity>();
public Dictionary<string, byte[]> Attachments { get; set; } = new Dictionary<string, byte[]>();
public DateTimeOffset ExpirationDate { get; set; }
public Icon Icon { get; set; } public Icon Icon { get; set; }
public Color ForegroundColor { get; set; } public Color ForegroundColor { get; set; }
public Color BackgroundColor { get; set; } public Color BackgroundColor { get; set; }
public bool HasExpirationDate { get; set; } public bool HasExpirationDate { get; set; }
public Dictionary<string, byte[]> Attachments { get; set; } = new Dictionary<string, byte[]>();
} }
} }

View File

@@ -0,0 +1,9 @@
namespace ModernKeePass.Domain.Entities
{
public class FieldEntity
{
public string Name { get; set; }
public string Value { get; set; }
public bool IsProtected { get; set; }
}
}

View File

@@ -1,4 +1,6 @@
namespace ModernKeePass.Domain.Enums using System.Collections.Generic;
namespace ModernKeePass.Domain.Enums
{ {
public static class EntryFieldName public static class EntryFieldName
{ {
@@ -12,5 +14,19 @@
public const string HasExpirationDate = nameof(HasExpirationDate); public const string HasExpirationDate = nameof(HasExpirationDate);
public const string BackgroundColor = nameof(BackgroundColor); public const string BackgroundColor = nameof(BackgroundColor);
public const string ForegroundColor = nameof(ForegroundColor); public const string ForegroundColor = nameof(ForegroundColor);
public static IEnumerable<string> StandardFieldNames = new[]
{
Title,
UserName,
Password,
Url,
Notes,
Icon,
ExpirationDate,
HasExpirationDate,
BackgroundColor,
ForegroundColor
};
} }
} }

View File

@@ -80,8 +80,7 @@
<Compile Include="DependencyInjection.cs" /> <Compile Include="DependencyInjection.cs" />
<Compile Include="File\CsvImportFormat.cs" /> <Compile Include="File\CsvImportFormat.cs" />
<Compile Include="KeePass\EntryFieldMapper.cs" /> <Compile Include="KeePass\EntryFieldMapper.cs" />
<Compile Include="KeePass\EntryMappingProfile.cs" /> <Compile Include="KeePass\MappingProfiles.cs" />
<Compile Include="KeePass\GroupMappingProfile.cs" />
<Compile Include="KeePass\IconMapper.cs" /> <Compile Include="KeePass\IconMapper.cs" />
<Compile Include="KeePass\KeePassCryptographyClient.cs" /> <Compile Include="KeePass\KeePassCryptographyClient.cs" />
<Compile Include="KeePass\KeePassDatabaseClient.cs" /> <Compile Include="KeePass\KeePassDatabaseClient.cs" />

View File

@@ -1,23 +0,0 @@
using AutoMapper;
using ModernKeePass.Domain.Entities;
using ModernKeePassLib;
namespace ModernKeePass.Infrastructure.KeePass
{
public class GroupMappingProfile : Profile
{
public GroupMappingProfile()
{
CreateMap<PwGroup, GroupEntity>()
.ForMember(d => d.ParentId, opts => opts.MapFrom(s => s.ParentGroup.Uuid.ToHexString()))
.ForMember(d => d.ParentName, opts => opts.MapFrom(s => s.ParentGroup.Name))
.ForMember(d => d.Id, opts => opts.MapFrom(s => s.Uuid.ToHexString()))
.ForMember(d => d.Name, opts => opts.MapFrom(s => s.Name))
.ForMember(d => d.Icon, opts => opts.MapFrom(s => IconMapper.MapPwIconToIcon(s.IconId)))
.ForMember(d => d.LastModificationDate, opts => opts.MapFrom(s => s.LastModificationTime))
.ForMember(d => d.Entries, opts => opts.MapFrom(s => s.Entries))
.ForMember(d => d.SubGroups, opts => opts.MapFrom(s => s.Groups))
.MaxDepth(2);
}
}
}

View File

@@ -228,7 +228,7 @@ namespace ModernKeePass.Infrastructure.KeePass
_pwDatabase.DeletedObjects.Add(new PwDeletedObject(BuildIdFromString(entityId), _dateTime.Now)); _pwDatabase.DeletedObjects.Add(new PwDeletedObject(BuildIdFromString(entityId), _dateTime.Now));
} }
public void UpdateEntry(string entryId, string fieldName, object fieldValue) public void UpdateEntry(string entryId, string fieldName, object fieldValue, bool isProtected)
{ {
var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true); var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true);
@@ -239,7 +239,7 @@ namespace ModernKeePass.Infrastructure.KeePass
case EntryFieldName.Password: case EntryFieldName.Password:
case EntryFieldName.Notes: case EntryFieldName.Notes:
case EntryFieldName.Url: case EntryFieldName.Url:
pwEntry.Strings.Set(EntryFieldMapper.MapFieldToPwDef(fieldName), new ProtectedString(true, fieldValue.ToString())); pwEntry.Strings.Set(EntryFieldMapper.MapFieldToPwDef(fieldName), new ProtectedString(isProtected, fieldValue.ToString()));
break; break;
case EntryFieldName.HasExpirationDate: case EntryFieldName.HasExpirationDate:
pwEntry.Expires = (bool)fieldValue; pwEntry.Expires = (bool)fieldValue;
@@ -257,7 +257,7 @@ namespace ModernKeePass.Infrastructure.KeePass
pwEntry.ForegroundColor = (Color)fieldValue; pwEntry.ForegroundColor = (Color)fieldValue;
break; break;
default: default:
pwEntry.Strings.Set(fieldName, new ProtectedString(true, fieldValue.ToString())); pwEntry.Strings.Set(fieldName, new ProtectedString(isProtected, fieldValue.ToString()));
break; break;
} }
} }

View File

@@ -4,34 +4,42 @@ using System.Linq;
using AutoMapper; using AutoMapper;
using ModernKeePass.Domain.Entities; using ModernKeePass.Domain.Entities;
using ModernKeePassLib; using ModernKeePassLib;
using ModernKeePassLib.Security;
namespace ModernKeePass.Infrastructure.KeePass namespace ModernKeePass.Infrastructure.KeePass
{ {
public class EntryMappingProfile: Profile public class MappingProfiles: Profile
{ {
public EntryMappingProfile() public MappingProfiles()
{ {
CreateMap<KeyValuePair<string, ProtectedString>, FieldEntity>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Key))
.ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.Value.ReadString()))
.ForMember(dest => dest.IsProtected, opt => opt.MapFrom(src => src.Value.IsProtected));
CreateMap<PwEntry, EntryEntity>() CreateMap<PwEntry, EntryEntity>()
.ForMember(dest => dest.ParentId, opt => opt.MapFrom(src => src.ParentGroup.Uuid.ToHexString())) .ForMember(dest => dest.ParentId, opt => opt.MapFrom(src => src.ParentGroup.Uuid.ToHexString()))
.ForMember(dest => dest.ParentName, opt => opt.MapFrom(src => src.ParentGroup.Name)) .ForMember(dest => dest.ParentName, opt => opt.MapFrom(src => src.ParentGroup.Name))
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Uuid.ToHexString())) .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Uuid.ToHexString()))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => GetEntryValue(src, PwDefs.TitleField))) .ForMember(dest => dest.Fields, opt => opt.MapFrom(src => src.Strings))
.ForMember(dest => dest.UserName, opt => opt.MapFrom(src => GetEntryValue(src, PwDefs.UserNameField)))
.ForMember(dest => dest.Password, opt => opt.MapFrom(src => GetEntryValue(src, PwDefs.PasswordField)))
.ForMember(dest => dest.Url, opt => opt.MapFrom(src => GetEntryValue(src, PwDefs.UrlField)))
.ForMember(dest => dest.Notes, opt => opt.MapFrom(src => GetEntryValue(src, PwDefs.NotesField)))
.ForMember(dest => dest.ForegroundColor, opt => opt.MapFrom(src => src.ForegroundColor)) .ForMember(dest => dest.ForegroundColor, opt => opt.MapFrom(src => src.ForegroundColor))
.ForMember(dest => dest.BackgroundColor, opt => opt.MapFrom(src => src.BackgroundColor)) .ForMember(dest => dest.BackgroundColor, opt => opt.MapFrom(src => src.BackgroundColor))
.ForMember(dest => dest.ExpirationDate, opt => opt.MapFrom(src => new DateTimeOffset(src.ExpiryTime))) .ForMember(dest => dest.ExpirationDate, opt => opt.MapFrom(src => new DateTimeOffset(src.ExpiryTime)))
.ForMember(dest => dest.HasExpirationDate, opt => opt.MapFrom(src => src.Expires)) .ForMember(dest => dest.HasExpirationDate, opt => opt.MapFrom(src => src.Expires))
.ForMember(dest => dest.Icon, opt => opt.MapFrom(src => IconMapper.MapPwIconToIcon(src.IconId))) .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => IconMapper.MapPwIconToIcon(src.IconId)))
.ForMember(dest => dest.AdditionalFields, opt => opt.MapFrom(src =>
src.Strings.Where(s => !PwDefs.GetStandardFields().Contains(s.Key))
.ToDictionary(s => s.Key, s => GetEntryValue(src, s.Key))))
.ForMember(dest => dest.LastModificationDate, opt => opt.MapFrom(src => new DateTimeOffset(src.LastModificationTime))) .ForMember(dest => dest.LastModificationDate, opt => opt.MapFrom(src => new DateTimeOffset(src.LastModificationTime)))
.ForMember(dest => dest.Attachments, opt => opt.MapFrom(src => src.Binaries.Select(b => new KeyValuePair<string, byte[]> (b.Key, b.Value.ReadData()) ))); .ForMember(dest => dest.Attachments, opt => opt.MapFrom(src => src.Binaries.Select(b => new KeyValuePair<string, byte[]> (b.Key, b.Value.ReadData()) )));
CreateMap<PwGroup, GroupEntity>()
.ForMember(d => d.ParentId, opts => opts.MapFrom(s => s.ParentGroup.Uuid.ToHexString()))
.ForMember(d => d.ParentName, opts => opts.MapFrom(s => s.ParentGroup.Name))
.ForMember(d => d.Id, opts => opts.MapFrom(s => s.Uuid.ToHexString()))
.ForMember(d => d.Name, opts => opts.MapFrom(s => s.Name))
.ForMember(d => d.Icon, opts => opts.MapFrom(s => IconMapper.MapPwIconToIcon(s.IconId)))
.ForMember(d => d.LastModificationDate, opts => opts.MapFrom(s => s.LastModificationTime))
.ForMember(d => d.Entries, opts => opts.MapFrom(s => s.Entries))
.ForMember(d => d.SubGroups, opts => opts.MapFrom(s => s.Groups))
.MaxDepth(2);
} }
private string GetEntryValue(PwEntry entry, string key) => entry.Strings.GetSafe(key).ReadString();
} }
} }

View File

@@ -3,7 +3,7 @@
"dependencies": { "dependencies": {
"AutoMapper": "5.2.0", "AutoMapper": "5.2.0",
"Microsoft.NETCore.Portable.Compatibility": "1.0.1", "Microsoft.NETCore.Portable.Compatibility": "1.0.1",
"ModernKeePassLib": "2.44.3", "ModernKeePassLib": "2.45.1",
"NETStandard.Library": "2.0.3" "NETStandard.Library": "2.0.3"
}, },
"frameworks": { "frameworks": {

View File

@@ -276,4 +276,10 @@
<data name="CompositeKeyFileNameSuggestion" xml:space="preserve"> <data name="CompositeKeyFileNameSuggestion" xml:space="preserve">
<value>Key</value> <value>Key</value>
</data> </data>
<data name="DatabaseTooBigDescription" xml:space="preserve">
<value>Database size is too big for auto-save on suspend. Please save your changes before closing the app !</value>
</data>
<data name="DatabaseTooBigTitle" xml:space="preserve">
<value>Attention</value>
</data>
</root> </root>

View File

@@ -376,7 +376,7 @@
<value>New group</value> <value>New group</value>
</data> </data>
<data name="GroupsLeftListView.HeaderLabel" xml:space="preserve"> <data name="GroupsLeftListView.HeaderLabel" xml:space="preserve">
<value>Navigation</value> <value>Groups</value>
</data> </data>
<data name="HistoryLeftListView.HeaderLabel" xml:space="preserve"> <data name="HistoryLeftListView.HeaderLabel" xml:space="preserve">
<value>History</value> <value>History</value>
@@ -546,4 +546,16 @@
<data name="EntryAdditionalFieldNameReserved.Text" xml:space="preserve"> <data name="EntryAdditionalFieldNameReserved.Text" xml:space="preserve">
<value>Invalid field name</value> <value>Invalid field name</value>
</data> </data>
<data name="EntryEnableFieldProtection.Header" xml:space="preserve">
<value>Enable protection ?</value>
</data>
<data name="EntryEnableFieldProtection.OffContent" xml:space="preserve">
<value>No</value>
</data>
<data name="EntryEnableFieldProtection.OnContent" xml:space="preserve">
<value>Yes</value>
</data>
<data name="TopMenuRestoreButton.Content" xml:space="preserve">
<value>Restore</value>
</data>
</root> </root>

View File

@@ -279,4 +279,10 @@
<data name="EntryAddAdditionalField.Text" xml:space="preserve"> <data name="EntryAddAdditionalField.Text" xml:space="preserve">
<value>Ajouter un champ</value> <value>Ajouter un champ</value>
</data> </data>
<data name="DatabaseTooBigDescription" xml:space="preserve">
<value>La base de données est trop grosse pour sauvegarder automatiquement lors de la suspension. Pensez à bien sauvegarder vos changements avant de fermer l'app !</value>
</data>
<data name="DatabaseTooBigTitle" xml:space="preserve">
<value>Attention</value>
</data>
</root> </root>

View File

@@ -379,7 +379,7 @@
<value>Nouveau groupe</value> <value>Nouveau groupe</value>
</data> </data>
<data name="GroupsLeftListView.HeaderLabel" xml:space="preserve"> <data name="GroupsLeftListView.HeaderLabel" xml:space="preserve">
<value>Navigation</value> <value>Groupes</value>
</data> </data>
<data name="HistoryLeftListView.HeaderLabel" xml:space="preserve"> <data name="HistoryLeftListView.HeaderLabel" xml:space="preserve">
<value>Historique</value> <value>Historique</value>
@@ -543,4 +543,16 @@
<data name="EntryAdditionalFieldNameReserved.Text" xml:space="preserve"> <data name="EntryAdditionalFieldNameReserved.Text" xml:space="preserve">
<value>Nom de champ invalide</value> <value>Nom de champ invalide</value>
</data> </data>
<data name="EntryEnableFieldProtection.Header" xml:space="preserve">
<value>Activer la protection ?</value>
</data>
<data name="EntryEnableFieldProtection.OffContent" xml:space="preserve">
<value>Non</value>
</data>
<data name="EntryEnableFieldProtection.OnContent" xml:space="preserve">
<value>Oui</value>
</data>
<data name="TopMenuRestoreButton.Content" xml:space="preserve">
<value>Restaurer</value>
</data>
</root> </root>

View File

@@ -68,7 +68,7 @@ namespace ModernKeePass.ViewModels
} }
public ObservableCollection<EntryVm> History { get; private set; } public ObservableCollection<EntryVm> History { get; private set; }
public ObservableCollection<FieldVm> AdditionalFields { get; private set; } public ObservableCollection<EntryFieldVm> AdditionalFields { get; private set; }
public ObservableCollection<Attachment> Attachments { get; private set; } public ObservableCollection<Attachment> Attachments { get; private set; }
/// <summary> /// <summary>
@@ -85,8 +85,8 @@ namespace ModernKeePass.ViewModels
if (value != null) if (value != null)
{ {
AdditionalFields = AdditionalFields =
new ObservableCollection<FieldVm>( new ObservableCollection<EntryFieldVm>(
SelectedItem.AdditionalFields.Select(f => new FieldVm(f.Key, f.Value))); SelectedItem.AdditionalFields.Select(f => new EntryFieldVm(f.Name, f.Value, f.IsProtected)));
Attachments = new ObservableCollection<Attachment>(SelectedItem.Attachments.Select(f => new Attachment Attachments = new ObservableCollection<Attachment>(SelectedItem.Attachments.Select(f => new Attachment
{ {
Name = f.Key, Name = f.Key,
@@ -131,32 +131,32 @@ namespace ModernKeePass.ViewModels
public string Title public string Title
{ {
get { return SelectedItem.Title; } get { return SelectedItem.Title.Value; }
set set
{ {
SelectedItem.Title = value; SelectedItem.Title.Value = value;
SetFieldValue(nameof(Title), value).Wait(); SetFieldValue(nameof(Title), value, true).Wait();
} }
} }
public string UserName public string UserName
{ {
get { return SelectedItem.Username; } get { return SelectedItem.Username.Value; }
set set
{ {
SelectedItem.Username = value; SelectedItem.Username.Value = value;
SetFieldValue(nameof(UserName), value).Wait(); SetFieldValue(nameof(UserName), value, true).Wait();
RaisePropertyChanged(nameof(UserName)); RaisePropertyChanged(nameof(UserName));
} }
} }
public string Password public string Password
{ {
get { return SelectedItem.Password; } get { return SelectedItem.Password.Value; }
set set
{ {
SelectedItem.Password = value; SelectedItem.Password.Value = value;
SetFieldValue(nameof(Password), value).Wait(); SetFieldValue(nameof(Password), value, true).Wait();
RaisePropertyChanged(nameof(Password)); RaisePropertyChanged(nameof(Password));
RaisePropertyChanged(nameof(PasswordComplexityIndicator)); RaisePropertyChanged(nameof(PasswordComplexityIndicator));
} }
@@ -164,22 +164,22 @@ namespace ModernKeePass.ViewModels
public string Url public string Url
{ {
get { return SelectedItem.Url; } get { return SelectedItem.Url.Value; }
set set
{ {
SelectedItem.Url = value; SelectedItem.Url.Value = value;
SetFieldValue(nameof(Url), value).Wait(); SetFieldValue(nameof(Url), value, true).Wait();
RaisePropertyChanged(nameof(Url)); RaisePropertyChanged(nameof(Url));
} }
} }
public string Notes public string Notes
{ {
get { return SelectedItem.Notes; } get { return SelectedItem.Notes.Value; }
set set
{ {
SelectedItem.Notes = value; SelectedItem.Notes.Value = value;
SetFieldValue(nameof(Notes), value).Wait(); SetFieldValue(nameof(Notes), value, true).Wait();
} }
} }
@@ -189,7 +189,7 @@ namespace ModernKeePass.ViewModels
set set
{ {
SelectedItem.Icon = (Icon)Enum.Parse(typeof(Icon), value.ToString()); SelectedItem.Icon = (Icon)Enum.Parse(typeof(Icon), value.ToString());
SetFieldValue(nameof(Icon), SelectedItem.Icon).Wait(); SetFieldValue(nameof(Icon), SelectedItem.Icon, true).Wait();
} }
} }
@@ -201,7 +201,7 @@ namespace ModernKeePass.ViewModels
if (!HasExpirationDate) return; if (!HasExpirationDate) return;
SelectedItem.ExpirationDate = value.Date; SelectedItem.ExpirationDate = value.Date;
SetFieldValue("ExpirationDate", SelectedItem.ExpirationDate).Wait(); SetFieldValue("ExpirationDate", SelectedItem.ExpirationDate, true).Wait();
} }
} }
@@ -213,7 +213,7 @@ namespace ModernKeePass.ViewModels
if (!HasExpirationDate) return; if (!HasExpirationDate) return;
SelectedItem.ExpirationDate = SelectedItem.ExpirationDate.Date.Add(value); SelectedItem.ExpirationDate = SelectedItem.ExpirationDate.Date.Add(value);
SetFieldValue("ExpirationDate", SelectedItem.ExpirationDate).Wait(); SetFieldValue("ExpirationDate", SelectedItem.ExpirationDate, true).Wait();
} }
} }
@@ -223,7 +223,7 @@ namespace ModernKeePass.ViewModels
set set
{ {
SelectedItem.HasExpirationDate = value; SelectedItem.HasExpirationDate = value;
SetFieldValue(nameof(HasExpirationDate), value).Wait(); SetFieldValue(nameof(HasExpirationDate), value, true).Wait();
RaisePropertyChanged(nameof(HasExpirationDate)); RaisePropertyChanged(nameof(HasExpirationDate));
} }
} }
@@ -234,7 +234,7 @@ namespace ModernKeePass.ViewModels
set set
{ {
SelectedItem.BackgroundColor = value.ToColor(); SelectedItem.BackgroundColor = value.ToColor();
SetFieldValue(nameof(BackgroundColor), SelectedItem.BackgroundColor).Wait(); SetFieldValue(nameof(BackgroundColor), SelectedItem.BackgroundColor, true).Wait();
} }
} }
@@ -244,7 +244,7 @@ namespace ModernKeePass.ViewModels
set set
{ {
SelectedItem.ForegroundColor = value.ToColor(); SelectedItem.ForegroundColor = value.ToColor();
SetFieldValue(nameof(ForegroundColor), SelectedItem.ForegroundColor).Wait(); SetFieldValue(nameof(ForegroundColor), SelectedItem.ForegroundColor, true).Wait();
} }
} }
@@ -269,7 +269,7 @@ namespace ModernKeePass.ViewModels
public RelayCommand GoBackCommand { get; } public RelayCommand GoBackCommand { get; }
public RelayCommand GoToParentCommand { get; set; } public RelayCommand GoToParentCommand { get; set; }
public RelayCommand AddAdditionalField { get; set; } public RelayCommand AddAdditionalField { get; set; }
public RelayCommand<FieldVm> DeleteAdditionalField { get; set; } public RelayCommand<EntryFieldVm> DeleteAdditionalField { get; set; }
public RelayCommand<Attachment> OpenAttachmentCommand { get; set; } public RelayCommand<Attachment> OpenAttachmentCommand { get; set; }
public RelayCommand AddAttachmentCommand { get; set; } public RelayCommand AddAttachmentCommand { get; set; }
public RelayCommand<Attachment> DeleteAttachmentCommand { get; set; } public RelayCommand<Attachment> DeleteAttachmentCommand { get; set; }
@@ -308,14 +308,14 @@ namespace ModernKeePass.ViewModels
GoBackCommand = new RelayCommand(() => _navigation.GoBack()); GoBackCommand = new RelayCommand(() => _navigation.GoBack());
GoToParentCommand = new RelayCommand(() => GoToGroup(_parent.Id)); GoToParentCommand = new RelayCommand(() => GoToGroup(_parent.Id));
AddAdditionalField = new RelayCommand(AddField, () => IsCurrentEntry); AddAdditionalField = new RelayCommand(AddField, () => IsCurrentEntry);
DeleteAdditionalField = new RelayCommand<FieldVm>(async field => await DeleteField(field), field => field != null && IsCurrentEntry); DeleteAdditionalField = new RelayCommand<EntryFieldVm>(async field => await DeleteField(field), field => field != null && IsCurrentEntry);
OpenAttachmentCommand = new RelayCommand<Attachment>(async attachment => await OpenAttachment(attachment)); OpenAttachmentCommand = new RelayCommand<Attachment>(async attachment => await OpenAttachment(attachment));
AddAttachmentCommand = new RelayCommand(async () => await AddAttachment(), () => IsCurrentEntry); AddAttachmentCommand = new RelayCommand(async () => await AddAttachment(), () => IsCurrentEntry);
DeleteAttachmentCommand = new RelayCommand<Attachment>(async attachment => await DeleteAttachment(attachment), _ => IsCurrentEntry); DeleteAttachmentCommand = new RelayCommand<Attachment>(async attachment => await DeleteAttachment(attachment), _ => IsCurrentEntry);
MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged()); MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged());
MessengerInstance.Register<EntryFieldValueChangedMessage>(this, async message => await SetFieldValue(message.FieldName, message.FieldValue)); MessengerInstance.Register<EntryFieldValueChangedMessage>(this, async message => await SetFieldValue(message.FieldName, message.FieldValue, message.IsProtected));
MessengerInstance.Register<EntryFieldNameChangedMessage>(this, async message => await UpdateFieldName(message.OldName, message.NewName, message.Value)); MessengerInstance.Register<EntryFieldNameChangedMessage>(this, async message => await UpdateFieldName(message.OldName, message.NewName, message.Value, message.IsProtected));
} }
public async Task Initialize(string entryId) public async Task Initialize(string entryId)
@@ -348,6 +348,7 @@ namespace ModernKeePass.ViewModels
}); });
RaisePropertyChanged(nameof(IsRevealPasswordEnabled)); RaisePropertyChanged(nameof(IsRevealPasswordEnabled));
} }
public async Task AddHistory() public async Task AddHistory()
{ {
if (_isDirty) await _mediator.Send(new AddHistoryCommand { Entry = History[0] }); if (_isDirty) await _mediator.Send(new AddHistoryCommand { Entry = History[0] });
@@ -365,26 +366,26 @@ namespace ModernKeePass.ViewModels
GoToGroup(destination); GoToGroup(destination);
} }
private async Task SetFieldValue(string fieldName, object value) private async Task SetFieldValue(string fieldName, object value, bool isProtected)
{ {
await _mediator.Send(new UpsertFieldCommand { EntryId = Id, FieldName = fieldName, FieldValue = value }); await _mediator.Send(new UpsertFieldCommand { EntryId = Id, FieldName = fieldName, FieldValue = value, IsProtected = isProtected});
SaveCommand.RaiseCanExecuteChanged(); SaveCommand.RaiseCanExecuteChanged();
_isDirty = true; _isDirty = true;
} }
private async Task UpdateFieldName(string oldName, string newName, string value) private async Task UpdateFieldName(string oldName, string newName, string value, bool isProtected)
{ {
if (!string.IsNullOrEmpty(oldName)) await _mediator.Send(new DeleteFieldCommand { EntryId = Id, FieldName = oldName }); if (!string.IsNullOrEmpty(oldName)) await _mediator.Send(new DeleteFieldCommand { EntryId = Id, FieldName = oldName });
await SetFieldValue(newName, value); await SetFieldValue(newName, value, isProtected);
} }
private void AddField() private void AddField()
{ {
AdditionalFields.Add(new FieldVm(string.Empty, string.Empty)); AdditionalFields.Add(new EntryFieldVm(string.Empty, string.Empty, false));
AdditionalFieldSelectedIndex = AdditionalFields.Count - 1; AdditionalFieldSelectedIndex = AdditionalFields.Count - 1;
} }
private async Task DeleteField(FieldVm field) private async Task DeleteField(EntryFieldVm field)
{ {
AdditionalFields.Remove(field); AdditionalFields.Remove(field);
if (!string.IsNullOrEmpty(field.Name)) if (!string.IsNullOrEmpty(field.Name))

View File

@@ -43,7 +43,7 @@ namespace ModernKeePass.ViewModels
public bool IsNotRoot => Database.RootGroupId != _group.Id; public bool IsNotRoot => Database.RootGroupId != _group.Id;
public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries
group e by e.Title.ToUpper().FirstOrDefault() into grp group e by e.Title.Name.ToUpper().FirstOrDefault() into grp
orderby grp.Key orderby grp.Key
select grp; select grp;

View File

@@ -441,8 +441,8 @@
</ToolTipService.ToolTip> </ToolTipService.ToolTip>
</SymbolIcon> </SymbolIcon>
<StackPanel Grid.Column="1" x:Name="ExpirationDatePanel" Orientation="Vertical" Visibility="{Binding HasExpirationDate, Converter={StaticResource BooleanToVisibilityConverter}}"> <StackPanel Grid.Column="1" x:Name="ExpirationDatePanel" Orientation="Vertical" Visibility="{Binding HasExpirationDate, Converter={StaticResource BooleanToVisibilityConverter}}">
<DatePicker Margin="0,0,20,0" Date="{Binding ExpiryDate, Mode=TwoWay}" /> <DatePicker Margin="10,5,20,0" Date="{Binding ExpiryDate, Mode=TwoWay}" />
<TimePicker Margin="0,10,0,0" Time="{Binding ExpiryTime, Mode=TwoWay}" /> <TimePicker Margin="10,10,0,0" Time="{Binding ExpiryTime, Mode=TwoWay}" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</StackPanel> </StackPanel>
@@ -481,40 +481,51 @@
</HubSection> </HubSection>
<HubSection x:Uid="EntryHubAdditional"> <HubSection x:Uid="EntryHubAdditional">
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Vertical"> <local:SelectableTemplateListView
<HyperlinkButton Command="{Binding Path=DataContext.AddAdditionalField, ElementName=Page}"> ItemsSource="{Binding AdditionalFields}"
<StackPanel Orientation="Horizontal"> SelectedIndex="{Binding AdditionalFieldSelectedIndex, Mode=TwoWay}"
<SymbolIcon Symbol="Add" /> ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<TextBlock x:Uid="EntryAddAdditionalField" VerticalAlignment="Center" Margin="10,0,0,0" /> <local:SelectableTemplateListView.HeaderTemplate>
</StackPanel> <DataTemplate>
</HyperlinkButton> <StackPanel Orientation="Vertical">
<Border BorderBrush="DarkGray" BorderThickness="0,0,0,1" /> <HyperlinkButton Command="{Binding Path=DataContext.AddAdditionalField, ElementName=Page}">
<local:SelectableTemplateListView <StackPanel Orientation="Horizontal">
ItemsSource="{Binding AdditionalFields}" <SymbolIcon Symbol="Add" />
SelectedIndex="{Binding AdditionalFieldSelectedIndex, Mode=TwoWay}" <TextBlock x:Uid="EntryAddAdditionalField" VerticalAlignment="Center" Margin="10,0,0,0" />
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}"> </StackPanel>
<local:SelectableTemplateListView.SelectedItemTemplate> </HyperlinkButton>
<DataTemplate> <Border BorderBrush="DarkGray" BorderThickness="0,0,0,1" />
<StackPanel Orientation="Vertical" Margin="5"> </StackPanel>
<TextBox Text="{Binding Name, Mode=TwoWay}" Width="350" </DataTemplate>
IsEnabled="{Binding Path=DataContext.IsCurrentEntry, ElementName=Page}" /> </local:SelectableTemplateListView.HeaderTemplate>
<TextBox HorizontalAlignment="Left" AcceptsReturn="True" Height="100" TextWrapping="Wrap" Width="350" Margin="0,5,0,0" <local:SelectableTemplateListView.SelectedItemTemplate>
Text="{Binding Value, Mode=TwoWay}" <DataTemplate>
IsEnabled="{Binding Path=DataContext.IsCurrentEntry, ElementName=Page}" /> <Grid Margin="5">
<Button x:Uid="EntryDeleteAdditionalField" HorizontalAlignment="Right" Command="{Binding Path=DataContext.DeleteAdditionalField, ElementName=Page}" CommandParameter="{Binding}" /> <Grid.RowDefinitions>
</StackPanel> <RowDefinition />
</DataTemplate> <RowDefinition />
</local:SelectableTemplateListView.SelectedItemTemplate> <RowDefinition />
<local:SelectableTemplateListView.ItemTemplate> </Grid.RowDefinitions>
<DataTemplate> <TextBox Grid.Row="0" Text="{Binding Name, Mode=TwoWay}" Width="350"
<StackPanel Orientation="Vertical" Margin="5"> IsEnabled="{Binding Path=DataContext.IsCurrentEntry, ElementName=Page}" />
<TextBlock Text="{Binding Name}" Style="{StaticResource EntryTextBlockStyle}" FontWeight="SemiBold" /> <TextBox Grid.Row="1" AcceptsReturn="True" Height="100" TextWrapping="Wrap" Width="350" Margin="0,5,0,0"
<TextBlock HorizontalAlignment="Left" MaxLines="3" FontSize="12" Margin="2,0,2,5" Text="{Binding Value}" Style="{StaticResource EntryTextBlockStyle}"/> Text="{Binding Value, Mode=TwoWay}"
</StackPanel> IsEnabled="{Binding Path=DataContext.IsCurrentEntry, ElementName=Page}" />
</DataTemplate> <ToggleSwitch Grid.Row="2" x:Uid="EntryEnableFieldProtection" HorizontalAlignment="Left" IsOn="{Binding IsProtected, Mode=TwoWay}" />
</local:SelectableTemplateListView.ItemTemplate> <Button Grid.Row="2" x:Uid="EntryDeleteAdditionalField" HorizontalAlignment="Right" Margin="0,15,0,0" Command="{Binding Path=DataContext.DeleteAdditionalField, ElementName=Page}" CommandParameter="{Binding}" />
</local:SelectableTemplateListView> </Grid>
</StackPanel> </DataTemplate>
</local:SelectableTemplateListView.SelectedItemTemplate>
<local:SelectableTemplateListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="5">
<TextBlock Text="{Binding Name}" Style="{StaticResource EntryTextBlockStyle}" FontWeight="SemiBold" />
<TextBlock HorizontalAlignment="Left" MaxLines="3" FontSize="12" Margin="2,0,2,5" Text="*****" Visibility="{Binding IsProtected, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource EntryTextBlockStyle}"/>
<TextBlock HorizontalAlignment="Left" MaxLines="3" FontSize="12" Margin="2,0,2,5" Text="{Binding Value}" Visibility="{Binding IsProtected, Converter={StaticResource InverseBooleanToVisibilityConverter}}" Style="{StaticResource EntryTextBlockStyle}"/>
</StackPanel>
</DataTemplate>
</local:SelectableTemplateListView.ItemTemplate>
</local:SelectableTemplateListView>
</DataTemplate> </DataTemplate>
</HubSection> </HubSection>
<HubSection x:Uid="EntryHubAttachments" Foreground="{StaticResource HubSectionBrush}"> <HubSection x:Uid="EntryHubAttachments" Foreground="{StaticResource HubSectionBrush}">

View File

@@ -221,7 +221,7 @@ namespace ModernKeePass.Views.UserControls
var results = (await Model.Search(args.QueryText)).Take(5); var results = (await Model.Search(args.QueryText)).Take(5);
foreach (var result in results) foreach (var result in results)
{ {
args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Title, result.ParentGroupName, result.Id, imageUri, string.Empty); args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Title.Value, result.ParentGroupName, result.Id, imageUri, string.Empty);
} }
} }

View File

@@ -372,8 +372,8 @@
<HintPath>..\packages\HockeySDK.WINRT.4.1.6\lib\portable-win81\Microsoft.HockeyApp.Kit.dll</HintPath> <HintPath>..\packages\HockeySDK.WINRT.4.1.6\lib\portable-win81\Microsoft.HockeyApp.Kit.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="ModernKeePassLib, Version=2.44.3.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="ModernKeePassLib, Version=2.45.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ModernKeePassLib.2.44.3\lib\netstandard1.2\ModernKeePassLib.dll</HintPath> <HintPath>..\packages\ModernKeePassLib.2.45.1\lib\netstandard1.2\ModernKeePassLib.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="SixLabors.Core, Version=0.1.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="SixLabors.Core, Version=0.1.0.0, Culture=neutral, processorArchitecture=MSIL">

View File

@@ -1,4 +1,5 @@
Support for additional fields Support for additional fields
Support for attachments Support for attachments
Ability to manually reorder groups Ability to manually reorder groups
Design changes Design changes
Update to KeePassLib version 2.45

View File

@@ -1,4 +1,5 @@
Ajout des champs additionnels Ajout des champs additionnels
Ajout des pi<70>ces-jointes Ajout des pi<70>ces-jointes
Possibilite de reorganiser les groupes manuellement Possibilite de reorganiser les groupes manuellement
Quelques changements de design Quelques changements de design
Mise a jour de la KeePassLib version 2.45

View File

@@ -14,7 +14,7 @@
<package id="Microsoft.NETCore.Platforms" version="2.1.1" targetFramework="win81" /> <package id="Microsoft.NETCore.Platforms" version="2.1.1" targetFramework="win81" />
<package id="Microsoft.NETCore.Portable.Compatibility" version="1.0.1" targetFramework="win81" /> <package id="Microsoft.NETCore.Portable.Compatibility" version="1.0.1" targetFramework="win81" />
<package id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.7" targetFramework="win81" /> <package id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.7" targetFramework="win81" />
<package id="ModernKeePassLib" version="2.44.3" targetFramework="win81" /> <package id="ModernKeePassLib" version="2.45.1" targetFramework="win81" />
<package id="MvvmLight" version="5.4.1.1" targetFramework="win81" /> <package id="MvvmLight" version="5.4.1.1" targetFramework="win81" />
<package id="MvvmLightLibs" version="5.4.1.1" targetFramework="win81" /> <package id="MvvmLightLibs" version="5.4.1.1" targetFramework="win81" />
<package id="NETStandard.Library" version="2.0.3" targetFramework="win81" /> <package id="NETStandard.Library" version="2.0.3" targetFramework="win81" />

View File

@@ -5,5 +5,6 @@
public string OldName { get; set; } public string OldName { get; set; }
public string NewName { get; set; } public string NewName { get; set; }
public string Value { get; set; } public string Value { get; set; }
public bool IsProtected { get; set; }
} }
} }

View File

@@ -4,5 +4,6 @@
{ {
public string FieldName { get; set; } public string FieldName { get; set; }
public string FieldValue { get; set; } public string FieldValue { get; set; }
public bool IsProtected { get; set; }
} }
} }

View File

@@ -0,0 +1,51 @@
using System.Linq;
using GalaSoft.MvvmLight;
using Messages;
using ModernKeePass.Domain.Enums;
namespace ModernKeePass.ViewModels.ListItems
{
public class EntryFieldVm: ViewModelBase
{
private string _name;
private string _value;
private bool _isProtected;
public string Name
{
get { return _name; }
set
{
var newName = EntryFieldName.StandardFieldNames.Contains(value) ? $"{value}_1" : value;
MessengerInstance.Send(new EntryFieldNameChangedMessage { OldName = Name, NewName = newName, Value = Value, IsProtected = IsProtected});
Set(nameof(Name), ref _name, newName);
}
}
public string Value
{
get { return _value; }
set
{
MessengerInstance.Send(new EntryFieldValueChangedMessage { FieldName = Name, FieldValue = value, IsProtected = IsProtected });
Set(nameof(Value), ref _value, value);
}
}
public bool IsProtected
{
get { return _isProtected; }
set
{
MessengerInstance.Send(new EntryFieldValueChangedMessage { FieldName = Name, FieldValue = Value, IsProtected = value });
Set(nameof(IsProtected), ref _isProtected, value);
}
}
public EntryFieldVm(string fieldName, string fieldValue, bool isProtected)
{
_name = fieldName;
_value = fieldValue;
_isProtected = isProtected;
}
}
}

View File

@@ -4,6 +4,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using GalaSoft.MvvmLight; using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command; using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Views;
using MediatR; using MediatR;
using Messages; using Messages;
using ModernKeePass.Application.Common.Interfaces; using ModernKeePass.Application.Common.Interfaces;
@@ -93,6 +94,7 @@ namespace ModernKeePass.ViewModels
private readonly IResourceProxy _resource; private readonly IResourceProxy _resource;
private readonly INotificationService _notification; private readonly INotificationService _notification;
private readonly IFileProxy _file; private readonly IFileProxy _file;
private readonly IDialogService _dialog;
private bool _hasPassword; private bool _hasPassword;
private bool _hasKeyFile; private bool _hasKeyFile;
private bool _isOpening; private bool _isOpening;
@@ -102,12 +104,13 @@ namespace ModernKeePass.ViewModels
private string _keyFileText; private string _keyFileText;
private bool _isError; private bool _isError;
public OpenDatabaseControlVm(IMediator mediator, IResourceProxy resource, INotificationService notification, IFileProxy file) public OpenDatabaseControlVm(IMediator mediator, IResourceProxy resource, INotificationService notification, IFileProxy file, IDialogService dialog)
{ {
_mediator = mediator; _mediator = mediator;
_resource = resource; _resource = resource;
_notification = notification; _notification = notification;
_file = file; _file = file;
_dialog = dialog;
OpenKeyFileCommand = new RelayCommand(async () => await OpenKeyFile()); OpenKeyFileCommand = new RelayCommand(async () => await OpenKeyFile());
OpenDatabaseCommand = new RelayCommand<string>(async databaseFilePath => await TryOpenDatabase(databaseFilePath), _ => IsValid); OpenDatabaseCommand = new RelayCommand<string>(async databaseFilePath => await TryOpenDatabase(databaseFilePath), _ => IsValid);
_keyFileText = _resource.GetResourceValue("CompositeKeyDefaultKeyFile"); _keyFileText = _resource.GetResourceValue("CompositeKeyDefaultKeyFile");
@@ -130,7 +133,7 @@ namespace ModernKeePass.ViewModels
if (database.IsDirty) if (database.IsDirty)
{ {
MessengerInstance.Register<DatabaseClosedMessage>(this, async message => await OpenDatabase(message.Parameter as string)); MessengerInstance.Register<DatabaseClosedMessage>(this, async message => await OpenDatabase(message.Parameter as string));
MessengerInstance.Send(new DatabaseAlreadyOpenedMessage {Parameter = databaseFilePath}); MessengerInstance.Send(new DatabaseAlreadyOpenedMessage { Parameter = databaseFilePath });
} }
else await OpenDatabase(databaseFilePath); else await OpenDatabase(databaseFilePath);
} }
@@ -146,9 +149,11 @@ namespace ModernKeePass.ViewModels
KeyFilePath = HasKeyFile ? KeyFilePath : null, KeyFilePath = HasKeyFile ? KeyFilePath : null,
Password = HasPassword ? Password : null, Password = HasPassword ? Password : null,
}); });
var rootGroupId = (await _mediator.Send(new GetDatabaseQuery())).RootGroupId; var database = await _mediator.Send(new GetDatabaseQuery());
MessengerInstance.Send(new DatabaseOpenedMessage { RootGroupId = rootGroupId }); if (database.Size > Common.Constants.File.OneMegaByte)
await _dialog.ShowMessage(_resource.GetResourceValue("DatabaseTooBigDescription"), _resource.GetResourceValue("DatabaseTooBigTitle"));
MessengerInstance.Send(new DatabaseOpenedMessage { RootGroupId = database.RootGroupId });
} }
catch (ArgumentException) catch (ArgumentException)
{ {

View File

@@ -42,7 +42,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Messages\NavigateToPageMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\NavigateToPageMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\SaveErrorMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\SaveErrorMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\AboutVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\AboutVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\FieldVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\EntryFieldVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\ViewModelLocatorCommon.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\ViewModelLocatorCommon.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\ListMenuItemVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\ListMenuItemVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\MainMenuItemVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\MainMenuItemVm.cs" />