diff --git a/ModernKeePass/App.xaml.cs b/ModernKeePass/App.xaml.cs index 06f8fb8..3031ae4 100644 --- a/ModernKeePass/App.xaml.cs +++ b/ModernKeePass/App.xaml.cs @@ -1,16 +1,13 @@ using System; -using System.Collections.Generic; using System.Reflection; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; -using Windows.Data.Json; using Windows.Storage; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; using ModernKeePass.Common; using ModernKeePass.Exceptions; -using ModernKeePass.Interfaces; using ModernKeePass.Services; // The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227 @@ -22,7 +19,7 @@ namespace ModernKeePass /// sealed partial class App { - public DatabaseService Database { get; set; } = new DatabaseService(); + public DatabaseService Database { get; private set; } /// /// Initializes the singleton application object. This is the first line of authored code @@ -49,7 +46,7 @@ namespace ModernKeePass if (!(realException is SaveException)) return; unhandledExceptionEventArgs.Handled = true; - MessageDialogService.SaveErrorDialog(realException as SaveException, Database); + MessageDialogHelper.SaveErrorDialog(realException as SaveException, Database); } /// @@ -124,6 +121,7 @@ namespace ModernKeePass }*/ // Ensure the current window is active Window.Current.Activate(); + Database = new DatabaseService(); } /// diff --git a/ModernKeePass/Services/MessageDialogService.cs b/ModernKeePass/Common/MessageDialogHelper.cs similarity index 96% rename from ModernKeePass/Services/MessageDialogService.cs rename to ModernKeePass/Common/MessageDialogHelper.cs index 5f9619d..657d497 100644 --- a/ModernKeePass/Services/MessageDialogService.cs +++ b/ModernKeePass/Common/MessageDialogHelper.cs @@ -5,9 +5,9 @@ using Windows.UI.Popups; using ModernKeePass.Exceptions; using ModernKeePass.Interfaces; -namespace ModernKeePass.Services +namespace ModernKeePass.Common { - public static class MessageDialogService + public static class MessageDialogHelper { public static async void ShowActionDialog(string title, string contentText, string actionButtonText, string cancelButtonText, UICommandInvokedHandler action) { diff --git a/ModernKeePass/Services/ToastNotificationService.cs b/ModernKeePass/Common/ToastNotificationHelper.cs similarity index 96% rename from ModernKeePass/Services/ToastNotificationService.cs rename to ModernKeePass/Common/ToastNotificationHelper.cs index 92c07c8..8cd25a9 100644 --- a/ModernKeePass/Services/ToastNotificationService.cs +++ b/ModernKeePass/Common/ToastNotificationHelper.cs @@ -5,9 +5,9 @@ using Windows.UI.Notifications; using ModernKeePass.Interfaces; using ModernKeePass.ViewModels; -namespace ModernKeePass.Services +namespace ModernKeePass.Common { - public static class ToastNotificationService + public static class ToastNotificationHelper { public static void ShowMovedToast(IPwEntity entity, string action, string text) { diff --git a/ModernKeePass/Interfaces/IRecent.cs b/ModernKeePass/Interfaces/IRecent.cs new file mode 100644 index 0000000..f24f409 --- /dev/null +++ b/ModernKeePass/Interfaces/IRecent.cs @@ -0,0 +1,14 @@ +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using Windows.Storage; + +namespace ModernKeePass.Interfaces +{ + public interface IRecent + { + int EntryCount { get; } + Task GetFileAsync(string token); + ObservableCollection GetAllFiles(bool removeIfNonExistant = true); + void Add(IStorageItem file, string metadata); + } +} \ No newline at end of file diff --git a/ModernKeePass/Interfaces/IRecentItem.cs b/ModernKeePass/Interfaces/IRecentItem.cs new file mode 100644 index 0000000..e468539 --- /dev/null +++ b/ModernKeePass/Interfaces/IRecentItem.cs @@ -0,0 +1,11 @@ +using Windows.Storage; + +namespace ModernKeePass.Interfaces +{ + public interface IRecentItem + { + StorageFile DatabaseFile { get; } + string Token { get; } + string Name { get; } + } +} \ No newline at end of file diff --git a/ModernKeePass/Interfaces/IResource.cs b/ModernKeePass/Interfaces/IResource.cs new file mode 100644 index 0000000..836b449 --- /dev/null +++ b/ModernKeePass/Interfaces/IResource.cs @@ -0,0 +1,7 @@ +namespace ModernKeePass.Interfaces +{ + public interface IResource + { + string GetResourceValue(string key); + } +} diff --git a/ModernKeePass/Interfaces/ISettings.cs b/ModernKeePass/Interfaces/ISettings.cs new file mode 100644 index 0000000..18d1550 --- /dev/null +++ b/ModernKeePass/Interfaces/ISettings.cs @@ -0,0 +1,8 @@ +namespace ModernKeePass.Interfaces +{ + public interface ISettings + { + T GetSetting(string property); + void PutSetting(string property, T value); + } +} \ No newline at end of file diff --git a/ModernKeePass/ModernKeePassApp.csproj b/ModernKeePass/ModernKeePassApp.csproj index 83823fe..24be51e 100644 --- a/ModernKeePass/ModernKeePassApp.csproj +++ b/ModernKeePass/ModernKeePassApp.csproj @@ -113,18 +113,24 @@ App.xaml + + + DonatePage.xaml - + + + + - + @@ -218,6 +224,7 @@ Designer + diff --git a/ModernKeePass/Pages/EntryDetailPage.xaml b/ModernKeePass/Pages/EntryDetailPage.xaml index 08cd0ac..20d5453 100644 --- a/ModernKeePass/Pages/EntryDetailPage.xaml +++ b/ModernKeePass/Pages/EntryDetailPage.xaml @@ -394,11 +394,11 @@ - + diff --git a/ModernKeePass/Pages/EntryDetailPage.xaml.cs b/ModernKeePass/Pages/EntryDetailPage.xaml.cs index 0456c83..747ef7a 100644 --- a/ModernKeePass/Pages/EntryDetailPage.xaml.cs +++ b/ModernKeePass/Pages/EntryDetailPage.xaml.cs @@ -77,9 +77,9 @@ namespace ModernKeePass.Pages ? "Are you sure you want to send this entry to the recycle bin?" : "Are you sure you want to delete this entry?"; var text = isRecycleBinEnabled ? "Item moved to the Recycle bin" : "Item permanently removed"; - MessageDialogService.ShowActionDialog("Warning", message, "Delete", "Cancel", a => + MessageDialogHelper.ShowActionDialog("Warning", message, "Delete", "Cancel", a => { - ToastNotificationService.ShowMovedToast(Model, "Deleting", text); + ToastNotificationHelper.ShowMovedToast(Model, "Deleting", text); Model.MarkForDelete(); if (Frame.CanGoBack) Frame.GoBack(); }); @@ -87,7 +87,7 @@ namespace ModernKeePass.Pages private void RestoreButton_Click(object sender, RoutedEventArgs e) { - ToastNotificationService.ShowMovedToast(Model, "Restored", "Item returned to its original group"); + ToastNotificationHelper.ShowMovedToast(Model, "Restored", "Item returned to its original group"); if (Frame.CanGoBack) Frame.GoBack(); } @@ -100,7 +100,7 @@ namespace ModernKeePass.Pages } catch (Exception ex) { - MessageDialogService.ShowErrorDialog(ex); + MessageDialogHelper.ShowErrorDialog(ex); } } } diff --git a/ModernKeePass/Pages/GroupDetailPage.xaml b/ModernKeePass/Pages/GroupDetailPage.xaml index c2c53b3..2269c4b 100644 --- a/ModernKeePass/Pages/GroupDetailPage.xaml +++ b/ModernKeePass/Pages/GroupDetailPage.xaml @@ -64,11 +64,11 @@ - + diff --git a/ModernKeePass/Pages/GroupDetailPage.xaml.cs b/ModernKeePass/Pages/GroupDetailPage.xaml.cs index 7d0f21c..1308503 100644 --- a/ModernKeePass/Pages/GroupDetailPage.xaml.cs +++ b/ModernKeePass/Pages/GroupDetailPage.xaml.cs @@ -115,9 +115,9 @@ namespace ModernKeePass.Pages ? "Are you sure you want to send the whole group and all its entries to the recycle bin?" : "Are you sure you want to delete the whole group and all its entries?"; var text = isRecycleBinEnabled ? "Item moved to the Recycle bin" : "Item permanently removed"; - MessageDialogService.ShowActionDialog("Warning", message, "Delete", "Cancel", a => + MessageDialogHelper.ShowActionDialog("Warning", message, "Delete", "Cancel", a => { - ToastNotificationService.ShowMovedToast(Model, "Deleting", text); + ToastNotificationHelper.ShowMovedToast(Model, "Deleting", text); Model.MarkForDelete(); if (Frame.CanGoBack) Frame.GoBack(); }); @@ -125,7 +125,7 @@ namespace ModernKeePass.Pages private void RestoreButton_Click(object sender, RoutedEventArgs e) { - ToastNotificationService.ShowMovedToast(Model, "Restored", "Item returned to its original group"); + ToastNotificationHelper.ShowMovedToast(Model, "Restored", "Item returned to its original group"); if (Frame.CanGoBack) Frame.GoBack(); } diff --git a/ModernKeePass/Pages/SettingsPageFrames/SettingsSecurityPage.xaml.cs b/ModernKeePass/Pages/SettingsPageFrames/SettingsSecurityPage.xaml.cs index 9c3a570..2a9da00 100644 --- a/ModernKeePass/Pages/SettingsPageFrames/SettingsSecurityPage.xaml.cs +++ b/ModernKeePass/Pages/SettingsPageFrames/SettingsSecurityPage.xaml.cs @@ -18,7 +18,7 @@ namespace ModernKeePass.Pages private void CompositeKeyUserControl_OnValidationChecked(object sender, PasswordEventArgs e) { - ToastNotificationService.ShowGenericToast("Composite key", "Database successfully updated."); + ToastNotificationHelper.ShowGenericToast("Composite key", "Database successfully updated."); } } } diff --git a/ModernKeePass/Services/DatabaseService.cs b/ModernKeePass/Services/DatabaseService.cs index b27ff15..68341d7 100644 --- a/ModernKeePass/Services/DatabaseService.cs +++ b/ModernKeePass/Services/DatabaseService.cs @@ -26,6 +26,8 @@ namespace ModernKeePass.Services Opened = 2 } private readonly PwDatabase _pwDatabase = new PwDatabase(); + private readonly ISettings _settings; + private readonly IResource _resource; private StorageFile _databaseFile; private GroupVm _recycleBin; @@ -77,7 +79,15 @@ namespace ModernKeePass.Services get { return _pwDatabase.KdfParameters; } set { _pwDatabase.KdfParameters = value; } } - + + public DatabaseService() : this(new SettingsService()) + { } + + public DatabaseService(ISettings settings) + { + _settings = settings; + } + /// /// Open a KeePass database /// @@ -99,8 +109,8 @@ namespace ModernKeePass.Services _pwDatabase.New(ioConnection, key); //Get settings default values - if (SettingsService.GetSetting("Sample")) CreateSampleData(); - var fileFormat = SettingsService.GetSetting("DefaultFileFormat"); + if (_settings.GetSetting("Sample")) CreateSampleData(); + var fileFormat = _settings.GetSetting("DefaultFileFormat"); switch (fileFormat) { case "4": diff --git a/ModernKeePass/Services/RecentService.cs b/ModernKeePass/Services/RecentService.cs new file mode 100644 index 0000000..6fb7ff4 --- /dev/null +++ b/ModernKeePass/Services/RecentService.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using ModernKeePass.Interfaces; +using Windows.Storage; +using Windows.Storage.AccessCache; +using ModernKeePass.ViewModels; + +namespace ModernKeePass.Services +{ + public class RecentService : IRecent + { + private readonly StorageItemMostRecentlyUsedList _mru = StorageApplicationPermissions.MostRecentlyUsedList; + + public int EntryCount => _mru.Entries.Count; + + public ObservableCollection GetAllFiles(bool removeIfNonExistant = true) + { + var result = new ObservableCollection(); + foreach (var entry in _mru.Entries) + { + try + { + var file = _mru.GetFileAsync(entry.Token, AccessCacheOptions.SuppressAccessTimeUpdate).GetAwaiter().GetResult(); + result.Add(new RecentItemVm(entry.Token, entry.Metadata, file)); + } + catch (Exception) + { + if (removeIfNonExistant) _mru.Remove(entry.Token); + } + } + return result; + } + + public void Add(IStorageItem file, string metadata) + { + _mru.Add(file, metadata); + } + + public async Task GetFileAsync(string token) + { + return await _mru.GetFileAsync(token); + } + } +} diff --git a/ModernKeePass/Services/ResourcesService.cs b/ModernKeePass/Services/ResourcesService.cs new file mode 100644 index 0000000..f5db911 --- /dev/null +++ b/ModernKeePass/Services/ResourcesService.cs @@ -0,0 +1,17 @@ +using Windows.ApplicationModel.Resources; +using ModernKeePass.Interfaces; + +namespace ModernKeePass.Services +{ + public class ResourcesService: IResource + { + private const string ResourceFileName = "CodeBehind"; + private readonly ResourceLoader _resourceLoader = ResourceLoader.GetForCurrentView(); + + public string GetResourceValue(string key) + { + var resource = _resourceLoader.GetString($"/{ResourceFileName}/{key}"); + return resource; + } + } +} diff --git a/ModernKeePass/Services/SettingsService.cs b/ModernKeePass/Services/SettingsService.cs index 3c6d887..59ad226 100644 --- a/ModernKeePass/Services/SettingsService.cs +++ b/ModernKeePass/Services/SettingsService.cs @@ -1,15 +1,19 @@ using System; +using Windows.Foundation.Collections; using Windows.Storage; +using ModernKeePass.Interfaces; namespace ModernKeePass.Services { - public class SettingsService + public class SettingsService : ISettings { - public static T GetSetting(string property) + private readonly IPropertySet _values = ApplicationData.Current.LocalSettings.Values; + + public T GetSetting(string property) { try { - return (T)Convert.ChangeType(ApplicationData.Current.LocalSettings.Values[property], typeof(T)); + return (T)Convert.ChangeType(_values[property], typeof(T)); } catch (InvalidCastException) { @@ -17,12 +21,11 @@ namespace ModernKeePass.Services } } - public static void PutSetting(string property, T value) + public void PutSetting(string property, T value) { - var localSettings = ApplicationData.Current.LocalSettings; - if (localSettings.Values.ContainsKey(property)) - localSettings.Values[property] = value; - else localSettings.Values.Add(property, value); + if (_values.ContainsKey(property)) + _values[property] = value; + else _values.Add(property, value); } } } diff --git a/ModernKeePass/Strings/en-US/CodeBehind.resw b/ModernKeePass/Strings/en-US/CodeBehind.resw new file mode 100644 index 0000000..afe912a --- /dev/null +++ b/ModernKeePass/Strings/en-US/CodeBehind.resw @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Select key file from disk... + + + Error + + + key file + + + or + + + password + + + Error: wrong + + + Database composite key updated. + + + < New entry > + + + < New group > + + + About + + + Donate + + + New + + + Open + + + Recent + + + Save + + + Settings + + + Application + + + Database + + + General + + + New + + + Security + + \ No newline at end of file diff --git a/ModernKeePass/ViewModels/CompositeKeyVm.cs b/ModernKeePass/ViewModels/CompositeKeyVm.cs index 341d941..6cea5aa 100644 --- a/ModernKeePass/ViewModels/CompositeKeyVm.cs +++ b/ModernKeePass/ViewModels/CompositeKeyVm.cs @@ -29,7 +29,8 @@ namespace ModernKeePass.ViewModels private string _status; private StatusTypes _statusType; private StorageFile _keyFile; - private string _keyFileText = "Select key file from disk..."; + private string _keyFileText; + private readonly IResource _resource; public IDatabase Database { get; set; } @@ -100,10 +101,12 @@ namespace ModernKeePass.ViewModels public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password?.ToCharArray()); - public CompositeKeyVm() : this((Application.Current as App)?.Database) { } + public CompositeKeyVm() : this((Application.Current as App)?.Database, new ResourcesService()) { } - public CompositeKeyVm(IDatabase database) + public CompositeKeyVm(IDatabase database, IResource resource) { + _resource = resource; + _keyFileText = _resource.GetResourceValue("CompositeKeyDefaultKeyFile"); Database = database; } @@ -116,18 +119,18 @@ namespace ModernKeePass.ViewModels } catch (Exception e) { - error = $"Error: {e.Message}"; + error = $"{_resource.GetResourceValue("CompositeKeyErrorOpen")}: {e.Message}"; } switch ((DatabaseService.DatabaseStatus)Database.Status) { case DatabaseService.DatabaseStatus.Opened: - await Task.Run( () => RootGroup = Database.RootGroup); + await Task.Run(() => RootGroup = Database.RootGroup); return true; case DatabaseService.DatabaseStatus.CompositeKeyError: - var errorMessage = new StringBuilder("Error: wrong "); - if (HasPassword) errorMessage.Append("password"); - if (HasPassword && HasKeyFile) errorMessage.Append(" or "); - if (HasKeyFile) errorMessage.Append("key file"); + var errorMessage = new StringBuilder(_resource.GetResourceValue("CompositeKeyErrorUserStart")); + if (HasPassword) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserPassword")); + if (HasPassword && HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserOr")); + if (HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserKeyFile")); UpdateStatus(errorMessage.ToString(), StatusTypes.Error); break; case DatabaseService.DatabaseStatus.Error: @@ -140,7 +143,7 @@ namespace ModernKeePass.ViewModels public void UpdateKey() { Database.UpdateCompositeKey(CreateCompositeKey()); - UpdateStatus("Database composite key updated.", StatusTypes.Success); + UpdateStatus(_resource.GetResourceValue("CompositeKeyUpdated"), StatusTypes.Success); } public void CreateKeyFile(StorageFile file) diff --git a/ModernKeePass/ViewModels/EntryVm.cs b/ModernKeePass/ViewModels/EntryVm.cs index 898c4b5..c6a7f7f 100644 --- a/ModernKeePass/ViewModels/EntryVm.cs +++ b/ModernKeePass/ViewModels/EntryVm.cs @@ -5,6 +5,7 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using ModernKeePass.Interfaces; using ModernKeePass.Mappings; +using ModernKeePass.Services; using ModernKeePassLib; using ModernKeePassLib.Cryptography.PasswordGenerator; using ModernKeePassLib.Security; @@ -48,8 +49,9 @@ namespace ModernKeePass.ViewModels { get { - var title = GetEntryValue(PwDefs.TitleField); - return title == null ? "< New entry >" : title; + /*var title = GetEntryValue(PwDefs.TitleField); + return title == null ? _resource.GetResourceValue("EntryNew") : title;*/ + return GetEntryValue(PwDefs.TitleField); } set { SetEntryValue(PwDefs.TitleField, value); } } diff --git a/ModernKeePass/ViewModels/GroupVm.cs b/ModernKeePass/ViewModels/GroupVm.cs index 96b0f8c..04ad7cd 100644 --- a/ModernKeePass/ViewModels/GroupVm.cs +++ b/ModernKeePass/ViewModels/GroupVm.cs @@ -19,10 +19,10 @@ namespace ModernKeePass.ViewModels public GroupVm ParentGroup { get; private set; } public GroupVm PreviousGroup { get; private set; } public ObservableCollection Entries { get; set; } = new ObservableCollection(); - public ObservableCollection Groups { get; set; } = new ObservableCollection(); - public int EntryCount => Entries.Count() - 1; + public int EntryCount => Entries.Count; + // TODO: put in a converter public FontWeight FontWeight => _pwGroup == null ? FontWeights.Bold : FontWeights.Normal; public int GroupCount => Groups.Count - 1; public PwUuid IdUuid => _pwGroup?.Uuid; @@ -50,7 +50,7 @@ namespace ModernKeePass.ViewModels public string Name { - get { return _pwGroup == null ? "< New group >" : _pwGroup.Name; } + get { return _pwGroup == null ? string.Empty : _pwGroup.Name; } set { _pwGroup.Name = value; } } @@ -86,10 +86,9 @@ namespace ModernKeePass.ViewModels private readonly IDatabase _database; private bool _isEditMode; private PwEntry _reorderedEntry; - //private int _reorderedEntryIndex; public GroupVm() {} - + internal GroupVm(PwGroup pwGroup, GroupVm parent, PwUuid recycleBinId = null) : this(pwGroup, parent, (Application.Current as App)?.Database, recycleBinId) { } @@ -104,7 +103,7 @@ namespace ModernKeePass.ViewModels Entries = new ObservableCollection(pwGroup.Entries.Select(e => new EntryVm(e, this))); Entries.CollectionChanged += Entries_CollectionChanged; Groups = new ObservableCollection(pwGroup.Groups.Select(g => new GroupVm(g, this, recycleBinId))); - Groups.Insert(0, new GroupVm ()); + Groups.Insert(0, new GroupVm()); } private void Entries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -148,7 +147,6 @@ namespace ModernKeePass.ViewModels Move(_database.RecycleBinEnabled && !IsSelected ? _database.RecycleBin : null); } - public void UndoDelete() { Move(PreviousGroup); @@ -192,7 +190,7 @@ namespace ModernKeePass.ViewModels } catch (Exception e) { - MessageDialogService.ShowErrorDialog(e); + MessageDialogHelper.ShowErrorDialog(e); } } @@ -206,7 +204,7 @@ namespace ModernKeePass.ViewModels } catch (Exception e) { - MessageDialogService.ShowErrorDialog(e); + MessageDialogHelper.ShowErrorDialog(e); } } diff --git a/ModernKeePass/ViewModels/Items/RecentItemVm.cs b/ModernKeePass/ViewModels/Items/RecentItemVm.cs index 6315ffb..202f653 100644 --- a/ModernKeePass/ViewModels/Items/RecentItemVm.cs +++ b/ModernKeePass/ViewModels/Items/RecentItemVm.cs @@ -1,24 +1,15 @@ -using System; -using Windows.Storage; +using Windows.Storage; using ModernKeePass.Common; -using Windows.Storage.AccessCache; using Windows.UI.Xaml; using ModernKeePass.Interfaces; +using ModernKeePass.Services; namespace ModernKeePass.ViewModels { - public class RecentItemVm: NotifyPropertyChangedBase, ISelectableModel + public class RecentItemVm: NotifyPropertyChangedBase, ISelectableModel, IRecentItem { private bool _isSelected; - - public RecentItemVm() {} - public RecentItemVm(AccessListEntry entry, StorageFile file) - { - Token = entry.Token; - Name = entry.Metadata; - DatabaseFile = file; - } - + public StorageFile DatabaseFile { get; } public string Token { get; } public string Name { get; } @@ -30,6 +21,14 @@ namespace ModernKeePass.ViewModels set { SetProperty(ref _isSelected, value); } } + public RecentItemVm() {} + public RecentItemVm(string token, string metadata, IStorageItem file) + { + Token = token; + Name = metadata; + DatabaseFile = file as StorageFile; + } + public void OpenDatabaseFile() { OpenDatabaseFile((Application.Current as App)?.Database); @@ -40,10 +39,14 @@ namespace ModernKeePass.ViewModels database.DatabaseFile = DatabaseFile; } - public async void UpdateAccessTime() + public void UpdateAccessTime() { - var mru = StorageApplicationPermissions.MostRecentlyUsedList; - await mru.GetFileAsync(Token); + UpdateAccessTime(new RecentService()); + } + + public async void UpdateAccessTime(IRecent recent) + { + await recent.GetFileAsync(Token); } } } diff --git a/ModernKeePass/ViewModels/Items/SettingsDatabaseVm.cs b/ModernKeePass/ViewModels/Items/SettingsDatabaseVm.cs index dc57265..1d306a5 100644 --- a/ModernKeePass/ViewModels/Items/SettingsDatabaseVm.cs +++ b/ModernKeePass/ViewModels/Items/SettingsDatabaseVm.cs @@ -11,6 +11,7 @@ using ModernKeePassLib.Cryptography.KeyDerivation; namespace ModernKeePass.ViewModels { + // TODO: implement Kdf settings public class SettingsDatabaseVm: NotifyPropertyChangedBase, IHasSelectableObject { private readonly IDatabase _database; diff --git a/ModernKeePass/ViewModels/Items/SettingsNewVm.cs b/ModernKeePass/ViewModels/Items/SettingsNewVm.cs index 9ae322d..033ea22 100644 --- a/ModernKeePass/ViewModels/Items/SettingsNewVm.cs +++ b/ModernKeePass/ViewModels/Items/SettingsNewVm.cs @@ -1,23 +1,33 @@ using System.Collections.Generic; +using ModernKeePass.Interfaces; using ModernKeePass.Services; namespace ModernKeePass.ViewModels { public class SettingsNewVm { + private ISettings _settings; + + public SettingsNewVm() : this(new SettingsService()) + { } + + public SettingsNewVm(ISettings settings) + { + _settings = settings; + } public bool IsCreateSample { - get { return SettingsService.GetSetting("Sample"); } - set { SettingsService.PutSetting("Sample", value); } + get { return _settings.GetSetting("Sample"); } + set { _settings.PutSetting("Sample", value); } } public IEnumerable FileFormats => new []{"2", "4"}; public string FileFormatVersion { - get { return SettingsService.GetSetting("DefaultFileFormat"); } - set { SettingsService.PutSetting("DefaultFileFormat", value); } + get { return _settings.GetSetting("DefaultFileFormat"); } + set { _settings.PutSetting("DefaultFileFormat", value); } } } } diff --git a/ModernKeePass/ViewModels/MainVm.cs b/ModernKeePass/ViewModels/MainVm.cs index 4bcbff0..6c1ee99 100644 --- a/ModernKeePass/ViewModels/MainVm.cs +++ b/ModernKeePass/ViewModels/MainVm.cs @@ -1,7 +1,6 @@ using System.Collections.ObjectModel; using System.Linq; using Windows.ApplicationModel; -using Windows.Storage.AccessCache; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using ModernKeePass.Common; @@ -46,40 +45,68 @@ namespace ModernKeePass.ViewModels public MainVm() {} - internal MainVm(Frame referenceFrame, Frame destinationFrame) : this(referenceFrame, destinationFrame, (Application.Current as App)?.Database) { } + internal MainVm(Frame referenceFrame, Frame destinationFrame) : this(referenceFrame, destinationFrame, + (Application.Current as App)?.Database, new ResourcesService(), new RecentService()) + { } - public MainVm(Frame referenceFrame, Frame destinationFrame, IDatabase database) + public MainVm(Frame referenceFrame, Frame destinationFrame, IDatabase database, IResource resource, IRecent recent) { - var mru = StorageApplicationPermissions.MostRecentlyUsedList; var isDatabaseOpen = database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opened; var mainMenuItems = new ObservableCollection { new MainMenuItemVm { - Title = "Open", PageType = typeof(OpenDatabasePage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Page2, + Title = resource.GetResourceValue("MainMenuItemOpen"), + PageType = typeof(OpenDatabasePage), + Destination = destinationFrame, + Parameter = referenceFrame, + SymbolIcon = Symbol.Page2, IsSelected = database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opening }, new MainMenuItemVm { - Title = "New" , PageType = typeof(NewDatabasePage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Add + Title = resource.GetResourceValue("MainMenuItemNew"), + PageType = typeof(NewDatabasePage), + Destination = destinationFrame, + Parameter = referenceFrame, + SymbolIcon = Symbol.Add }, new MainMenuItemVm { - Title = "Save" , PageType = typeof(SaveDatabasePage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Save, - IsSelected = isDatabaseOpen, IsEnabled = isDatabaseOpen - }, - new MainMenuItemVm { - Title = "Recent" , PageType = typeof(RecentDatabasesPage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Copy, - IsSelected = (database == null || database.Status == (int) DatabaseService.DatabaseStatus.Closed) && mru.Entries.Count > 0, IsEnabled = mru.Entries.Count > 0 + Title = resource.GetResourceValue("MainMenuItemSave"), + PageType = typeof(SaveDatabasePage), + Destination = destinationFrame, + Parameter = referenceFrame, + SymbolIcon = Symbol.Save, + IsSelected = isDatabaseOpen, + IsEnabled = isDatabaseOpen }, new MainMenuItemVm { - Title = "Settings" , PageType = typeof(SettingsPage), Destination = referenceFrame, SymbolIcon = Symbol.Setting + Title = resource.GetResourceValue("MainMenuItemRecent"), + PageType = typeof(RecentDatabasesPage), + Destination = destinationFrame, + Parameter = referenceFrame, + SymbolIcon = Symbol.Copy, + IsSelected = + (database == null || database.Status == (int) DatabaseService.DatabaseStatus.Closed) && + recent.EntryCount > 0, + IsEnabled = recent.EntryCount > 0 }, new MainMenuItemVm { - Title = "About" , PageType = typeof(AboutPage), Destination = destinationFrame, SymbolIcon = Symbol.Help + Title = resource.GetResourceValue("MainMenuItemSettings"), + PageType = typeof(SettingsPage), + Destination = referenceFrame, + SymbolIcon = Symbol.Setting + }, + new MainMenuItemVm + { + Title = resource.GetResourceValue("MainMenuItemAbout"), + PageType = typeof(AboutPage), + Destination = destinationFrame, + SymbolIcon = Symbol.Help } }; // Auto-select the Recent Items menu item if the conditions are met diff --git a/ModernKeePass/ViewModels/OpenVm.cs b/ModernKeePass/ViewModels/OpenVm.cs index 1b6a99c..9f9834f 100644 --- a/ModernKeePass/ViewModels/OpenVm.cs +++ b/ModernKeePass/ViewModels/OpenVm.cs @@ -1,5 +1,4 @@ using Windows.Storage; -using Windows.Storage.AccessCache; using Windows.UI.Xaml; using ModernKeePass.Common; using ModernKeePass.Interfaces; @@ -23,19 +22,23 @@ namespace ModernKeePass.ViewModels if (database == null || database.Status != (int) DatabaseService.DatabaseStatus.Opening) return; OpenFile(database.DatabaseFile); } - + public void OpenFile(StorageFile file) + { + OpenFile(file, new RecentService()); + } + + public void OpenFile(StorageFile file, IRecent recent) { _database.DatabaseFile = file; OnPropertyChanged("Name"); OnPropertyChanged("ShowPasswordBox"); - AddToRecentList(file); + AddToRecentList(file, recent); } - private void AddToRecentList(StorageFile file) + private void AddToRecentList(StorageFile file, IRecent recent) { - var mru = StorageApplicationPermissions.MostRecentlyUsedList; - mru.Add(file, file.DisplayName); + recent.Add(file, file.DisplayName); } } } diff --git a/ModernKeePass/ViewModels/RecentVm.cs b/ModernKeePass/ViewModels/RecentVm.cs index 49e9270..aba549f 100644 --- a/ModernKeePass/ViewModels/RecentVm.cs +++ b/ModernKeePass/ViewModels/RecentVm.cs @@ -1,17 +1,16 @@ -using System; -using System.Collections.ObjectModel; -using Windows.Storage.AccessCache; +using System.Collections.ObjectModel; using ModernKeePass.Common; using ModernKeePass.Interfaces; +using ModernKeePass.Services; namespace ModernKeePass.ViewModels { public class RecentVm : NotifyPropertyChangedBase, IHasSelectableObject { private ISelectableModel _selectedItem; - private ObservableCollection _recentItems = new ObservableCollection(); + private ObservableCollection _recentItems = new ObservableCollection(); - public ObservableCollection RecentItems + public ObservableCollection RecentItems { get { return _recentItems; } set { SetProperty(ref _recentItems, value); } @@ -35,23 +34,14 @@ namespace ModernKeePass.ViewModels } } - public RecentVm() + public RecentVm() : this (new RecentService()) + { } + + public RecentVm(IRecent recent) { - var mru = StorageApplicationPermissions.MostRecentlyUsedList; - foreach (var entry in mru.Entries) - { - try - { - var file = mru.GetFileAsync(entry.Token, AccessCacheOptions.SuppressAccessTimeUpdate).GetAwaiter().GetResult(); - RecentItems.Add(new RecentItemVm(entry, file)); - } - catch (Exception) - { - mru.Remove(entry.Token); - } - } + RecentItems = recent.GetAllFiles(); if (RecentItems.Count > 0) - SelectedItem = RecentItems[0]; + SelectedItem = RecentItems[0] as RecentItemVm; } } diff --git a/ModernKeePass/ViewModels/SettingsVm.cs b/ModernKeePass/ViewModels/SettingsVm.cs index 3aae16f..222f11a 100644 --- a/ModernKeePass/ViewModels/SettingsVm.cs +++ b/ModernKeePass/ViewModels/SettingsVm.cs @@ -6,6 +6,7 @@ using ModernKeePass.Common; using ModernKeePass.Interfaces; using ModernKeePass.Pages; using ModernKeePass.Pages.SettingsPageFrames; +using ModernKeePass.Services; namespace ModernKeePass.ViewModels { @@ -41,32 +42,32 @@ namespace ModernKeePass.ViewModels } } - public SettingsVm() : this((Application.Current as App)?.Database) { } + public SettingsVm() : this((Application.Current as App)?.Database, new ResourcesService()) { } - public SettingsVm(IDatabase database) + public SettingsVm(IDatabase database, IResource resource) { var menuItems = new ObservableCollection { new ListMenuItemVm { - Title = "New", - Group = "Application", + Title = resource.GetResourceValue("SettingsMenuItemNew"), + Group = resource.GetResourceValue("SettingsMenuGroupApplication"), SymbolIcon = Symbol.Add, PageType = typeof(SettingsNewDatabasePage), IsSelected = true }, new ListMenuItemVm { - Title = "General", - Group = "Database", + Title = resource.GetResourceValue("SettingsMenuItemGeneral"), + Group = resource.GetResourceValue("SettingsMenuGroupDatabase"), SymbolIcon = Symbol.Setting, PageType = typeof(SettingsDatabasePage), IsEnabled = database?.Status == 2 }, new ListMenuItemVm { - Title = "Security", - Group = "Database", + Title = resource.GetResourceValue("SettingsMenuItemSecurity"), + Group = resource.GetResourceValue("SettingsMenuGroupDatabase"), SymbolIcon = Symbol.Permissions, PageType = typeof(SettingsSecurityPage), IsEnabled = database?.Status == 2 diff --git a/ModernKeePassApp.Test/DatabaseTests.cs b/ModernKeePassApp.Test/DatabaseTests.cs index 3dce7ae..c192971 100644 --- a/ModernKeePassApp.Test/DatabaseTests.cs +++ b/ModernKeePassApp.Test/DatabaseTests.cs @@ -4,13 +4,14 @@ using Windows.Storage; using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; using ModernKeePass.Services; using ModernKeePass.ViewModels; +using ModernKeePassApp.Test.Mock; namespace ModernKeePassApp.Test { [TestClass] public class DatabaseTests { - private readonly DatabaseService _database = new DatabaseService(); + private readonly DatabaseService _database = new DatabaseService(new SettingsServiceMock()); [TestMethod] public void TestCreate() @@ -47,12 +48,12 @@ namespace ModernKeePassApp.Test { _database.Open(null, createNew); Assert.AreEqual((int)DatabaseService.DatabaseStatus.NoCompositeKey, _database.Status); - var compositeKey = new CompositeKeyVm(_database) + var compositeKey = new CompositeKeyVm(_database, new ResourceServiceMock()) { HasPassword = true, Password = "test" }; - compositeKey.OpenDatabase(createNew); + compositeKey.OpenDatabase(createNew).GetAwaiter().GetResult(); Assert.AreEqual((int)DatabaseService.DatabaseStatus.Opened, _database.Status); } } diff --git a/ModernKeePassApp.Test/Mock/DatabaseHelperMock.cs b/ModernKeePassApp.Test/Mock/DatabaseServiceMock.cs similarity index 96% rename from ModernKeePassApp.Test/Mock/DatabaseHelperMock.cs rename to ModernKeePassApp.Test/Mock/DatabaseServiceMock.cs index 9a81391..97cb630 100644 --- a/ModernKeePassApp.Test/Mock/DatabaseHelperMock.cs +++ b/ModernKeePassApp.Test/Mock/DatabaseServiceMock.cs @@ -8,7 +8,7 @@ using Windows.Storage; namespace ModernKeePassApp.Test.Mock { - public class DatabaseHelperMock : IDatabase + public class DatabaseServiceMock : IDatabase { public PwCompressionAlgorithm CompressionAlgorithm { get; set; } diff --git a/ModernKeePassApp.Test/Mock/RecentServiceMock.cs b/ModernKeePassApp.Test/Mock/RecentServiceMock.cs new file mode 100644 index 0000000..ace365a --- /dev/null +++ b/ModernKeePassApp.Test/Mock/RecentServiceMock.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using ModernKeePass.Interfaces; +using Windows.Storage; + +namespace ModernKeePassApp.Test.Mock +{ + class RecentServiceMock : IRecent + { + public int EntryCount => 0; + + public void Add(IStorageItem file, string metadata) + { + throw new NotImplementedException(); + } + + public ObservableCollection GetAllFiles(bool removeIfNonExistant = true) + { + throw new NotImplementedException(); + } + + public Task GetFileAsync(string token) + { + throw new NotImplementedException(); + } + } +} diff --git a/ModernKeePassApp.Test/Mock/ResourceServiceMock.cs b/ModernKeePassApp.Test/Mock/ResourceServiceMock.cs new file mode 100644 index 0000000..f1a79f3 --- /dev/null +++ b/ModernKeePassApp.Test/Mock/ResourceServiceMock.cs @@ -0,0 +1,13 @@ +using System; +using ModernKeePass.Interfaces; + +namespace ModernKeePassApp.Test.Mock +{ + class ResourceServiceMock : IResource + { + public string GetResourceValue(string key) + { + return string.Empty; + } + } +} diff --git a/ModernKeePassApp.Test/Mock/SettingsServiceMock.cs b/ModernKeePassApp.Test/Mock/SettingsServiceMock.cs new file mode 100644 index 0000000..49139fb --- /dev/null +++ b/ModernKeePassApp.Test/Mock/SettingsServiceMock.cs @@ -0,0 +1,18 @@ +using System; +using ModernKeePass.Interfaces; + +namespace ModernKeePassApp.Test.Mock +{ + public class SettingsServiceMock : ISettings + { + public T GetSetting(string property) + { + return default(T); + } + + public void PutSetting(string property, T value) + { + throw new NotImplementedException(); + } + } +} diff --git a/ModernKeePassApp.Test/ModernKeePassApp.Test.csproj b/ModernKeePassApp.Test/ModernKeePassApp.Test.csproj index 5288c22..6cd7422 100644 --- a/ModernKeePassApp.Test/ModernKeePassApp.Test.csproj +++ b/ModernKeePassApp.Test/ModernKeePassApp.Test.csproj @@ -120,7 +120,10 @@ - + + + + diff --git a/ModernKeePassApp.Test/ViewModelsTests.cs b/ModernKeePassApp.Test/ViewModelsTests.cs index 0f796c8..ee1ebdc 100644 --- a/ModernKeePassApp.Test/ViewModelsTests.cs +++ b/ModernKeePassApp.Test/ViewModelsTests.cs @@ -3,6 +3,8 @@ using System.Linq; using Windows.ApplicationModel; using Windows.Storage.AccessCache; using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; +using ModernKeePass.Pages; +using ModernKeePass.Pages.SettingsPageFrames; using ModernKeePass.ViewModels; using ModernKeePassApp.Test.Mock; @@ -11,6 +13,9 @@ namespace ModernKeePassApp.Test [TestClass] public class ViewModelsTests { + private RecentServiceMock _recent = new RecentServiceMock(); + private ResourceServiceMock _resource = new ResourceServiceMock(); + [TestMethod] public void TestAboutVm() { @@ -21,29 +26,30 @@ namespace ModernKeePassApp.Test [TestMethod] public void TestMainVm() { - var database = new DatabaseHelperMock(); - var mainVm = new MainVm(null, null, database); + var database = new DatabaseServiceMock(); + + var mainVm = new MainVm(null, null, database, _resource, _recent); Assert.AreEqual(1, mainVm.MainMenuItems.Count()); var firstGroup = mainVm.MainMenuItems.FirstOrDefault(); Assert.AreEqual(6, firstGroup.Count()); database.Status = 1; - mainVm = new MainVm(null, null, database); + mainVm = new MainVm(null, null, database, _resource, _recent); Assert.IsNotNull(mainVm.SelectedItem); - Assert.AreEqual("Open", ((MainMenuItemVm) mainVm.SelectedItem).Title); + Assert.AreEqual(typeof(OpenDatabasePage), ((MainMenuItemVm) mainVm.SelectedItem).PageType); database.Status = 2; - mainVm = new MainVm(null, null, database); + mainVm = new MainVm(null, null, database, _resource, _recent); Assert.IsNotNull(mainVm.SelectedItem); Assert.AreEqual(2, mainVm.MainMenuItems.Count()); - Assert.AreEqual("Save", ((MainMenuItemVm) mainVm.SelectedItem).Title); + Assert.AreEqual(typeof(SaveDatabasePage), ((MainMenuItemVm) mainVm.SelectedItem).PageType); } [TestMethod] public void TestCompositeKeyVm() { - var database = new DatabaseHelperMock(); - var compositeKeyVm = new CompositeKeyVm(database); + var database = new DatabaseServiceMock(); + var compositeKeyVm = new CompositeKeyVm(database, _resource); Assert.IsTrue(compositeKeyVm.OpenDatabase(false).GetAwaiter().GetResult()); compositeKeyVm.StatusType = 1; compositeKeyVm.Password = "test"; @@ -54,7 +60,7 @@ namespace ModernKeePassApp.Test [TestMethod] public void TestOpenVm() { - var database = new DatabaseHelperMock + var database = new DatabaseServiceMock { Status = 1, DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Databases\TestDatabase.kdbx") @@ -78,7 +84,7 @@ namespace ModernKeePassApp.Test .GetAwaiter().GetResult(), "MockDatabase"); var recentVm = new RecentVm(); Assert.IsTrue(recentVm.RecentItems.Count == 1); - recentVm.SelectedItem = recentVm.RecentItems.FirstOrDefault(); + recentVm.SelectedItem = recentVm.RecentItems.FirstOrDefault() as RecentItemVm; Assert.IsTrue(recentVm.SelectedItem.IsSelected); mru.Clear(); } @@ -91,13 +97,14 @@ namespace ModernKeePassApp.Test [TestMethod] public void TestSettingsVm() { - var settingsVm = new SettingsVm(); + var settingsVm = new SettingsVm(new DatabaseServiceMock(), _resource); Assert.AreEqual(1, settingsVm.MenuItems.Count()); var firstGroup = settingsVm.MenuItems.FirstOrDefault(); - Assert.AreEqual(1, firstGroup.Count()); + // All groups have an empty title, so all settings are put inside the empty group + Assert.AreEqual(3, firstGroup.Count()); Assert.IsNotNull(settingsVm.SelectedItem); var selectedItem = (ListMenuItemVm) settingsVm.SelectedItem; - Assert.AreEqual("New", selectedItem.Title); + Assert.AreEqual(typeof(SettingsNewDatabasePage), selectedItem.PageType); } } }