mirror of
https://github.com/wismna/ModernKeePass.git
synced 2025-10-03 15:40:18 -04:00
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:
@@ -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" />
|
||||||
|
@@ -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);
|
||||||
|
@@ -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; }
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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))
|
||||||
|
20
ModernKeePass.Application/Entry/Models/FieldVm.cs
Normal file
20
ModernKeePass.Application/Entry/Models/FieldVm.cs
Normal 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>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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" />
|
||||||
|
@@ -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[]>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
9
ModernKeePass.Domain/Entities/FieldEntity.cs
Normal file
9
ModernKeePass.Domain/Entities/FieldEntity.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
@@ -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
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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" />
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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": {
|
||||||
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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))
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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}">
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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">
|
||||||
|
@@ -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
|
@@ -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
|
@@ -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" />
|
||||||
|
@@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
51
WinAppCommon/ViewModels/Items/EntryFieldVm.cs
Normal file
51
WinAppCommon/ViewModels/Items/EntryFieldVm.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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)
|
||||||
{
|
{
|
||||||
|
@@ -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" />
|
||||||
|
Reference in New Issue
Block a user