26 Commits
V1.10 ... V1.13

Author SHA1 Message Date
BONNEVILLE Geoffroy
4aefbcb8b9 RecycleBin now uses resources
RecycleBin bugs correction
2018-03-12 17:30:03 +01:00
BONNEVILLE Geoffroy
56129253d9 Use ModernKeePass Lib 2.38.2
Changed suspension save errors handling
2018-03-12 12:45:12 +01:00
BONNEVILLE Geoffroy
7613629d87 Deactivation of buggy Splat custom icon implementation 2018-03-12 12:40:07 +01:00
BONNEVILLE Geoffroy
fb0eab00c2 Create group and delete current group hidden in Recycle Bin 2018-03-12 11:27:15 +01:00
BONNEVILLE Geoffroy
5b8c3b9b11 Typo in restore button 2018-03-12 11:18:42 +01:00
BONNEVILLE Geoffroy
700f76679a Corrected RecycleBin group bug
Database Settings page now has radio button for recycle bin
2018-03-12 10:21:36 +01:00
BONNEVILLE Geoffroy
e7d731bb04 KeepassLib version 2.38
Added a new settings page to allow auto-save or not
App now resumes correctly from suspend
Settings service now allows getting default values
Removed french special characters from metadata
Code cleanup
2018-03-09 18:06:06 +01:00
BONNEVILLE Geoffroy
49637fcc3b KeepassLib version update to 2.38 2018-03-09 17:49:47 +01:00
BONNEVILLE Geoffroy
fc25d7ea93 Update some packages 2018-03-07 18:39:56 +01:00
BONNEVILLE Geoffroy
cca6579274 Removed useless code in Donate page 2018-02-23 18:13:44 +01:00
BONNEVILLE Geoffroy
7dbf93fe7b Changed most services to singletons
Refactor the Database Service (no more enum, ...)
Restored the Donate page with Paypal web page
Added (but not working) MS App Center integration
Corrected tests accordingly
WIP AOP to detect database changes
2018-02-23 18:09:21 +01:00
BONNEVILLE Geoffroy
b46ab8db51 Code cleanup
Popup discard action now works
2018-01-09 18:40:11 +01:00
BONNEVILLE Geoffroy
a19519fa73 Removed database status in favor of much cleaner code
Implemented (but deactivated) anti corruption mechanism
WIP detect changes and save them if opening another database
2018-01-08 18:52:03 +01:00
BONNEVILLE Geoffroy
4a3f36d38b Version bump to 1.13
New group button is now at the bottom of the listview
2018-01-08 11:02:53 +01:00
BONNEVILLE Geoffroy
4ae02fc07b Corrected test case to reflect page removal 2018-01-03 11:37:09 +01:00
BONNEVILLE Geoffroy
047fca32bf Hid donations pages (need to implement 3rd party API) 2018-01-03 11:29:51 +01:00
BONNEVILLE Geoffroy
abbff449c0 Removed User Account from composite key (probably never going to work as intended)
Changed copy URL to navigate to URL in entry quick menu
2017-12-26 17:54:13 +01:00
BONNEVILLE Geoffroy
fba668860b WIP user accounts - not working at all 2017-12-21 18:24:01 +01:00
BONNEVILLE Geoffroy
acb196d9c2 WIP Windows User Accounts Composite Key integration 2017-12-20 18:49:11 +01:00
BONNEVILLE Geoffroy
dfa3a21e6b Removed an useless converter
Groups in menu now use instead a Template selector (depending on IIsSelected)
2017-12-19 18:44:35 +01:00
BONNEVILLE Geoffroy
7ff6bccbc4 Added some tests
Removed false first group, replaced it a button in the header
Code refactor
2017-12-18 18:53:42 +01:00
BONNEVILLE Geoffroy
88e5b80778 Version bump to 1.12
Added small menu on entries list to copy login, password and URL
2017-12-18 14:09:04 +01:00
BONNEVILLE Geoffroy
d127431396 Third try on store special character escaping 2017-12-18 11:52:49 +01:00
BONNEVILLE Geoffroy
588703ecd6 Added a small text explaining how to reorder entries
Code cleanup
2017-12-14 17:54:14 +01:00
BONNEVILLE Geoffroy
223c9b641a Updated release notes
HTML encoded french text
2017-12-14 17:28:22 +01:00
BONNEVILLE Geoffroy
7db34d6517 Corrected critical error when opening file from explorer
Reverted filter mechanisme to search box because of numerous regressions (on ordering, refresh etc.)
2017-12-14 17:15:28 +01:00
181 changed files with 1463 additions and 823 deletions

View File

@@ -0,0 +1,33 @@
using System;
using Windows.UI.Xaml;
using Microsoft.Xaml.Interactivity;
using ModernKeePass.Common;
namespace ModernKeePass.Actions
{
public class NavigateToUrlAction : DependencyObject, IAction
{
public string Url
{
get { return (string)GetValue(UrlProperty); }
set { SetValue(UrlProperty, value); }
}
public static readonly DependencyProperty UrlProperty =
DependencyProperty.Register("Url", typeof(string), typeof(NavigateToUrlAction), new PropertyMetadata(string.Empty));
public object Execute(object sender, object parameter)
{
try
{
var uri = new Uri(Url);
return Windows.System.Launcher.LaunchUriAsync(uri).GetAwaiter().GetResult();
}
catch (Exception ex)
{
MessageDialogHelper.ShowErrorDialog(ex);
return false;
}
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
using ModernKeePass.Services;
namespace ModernKeePass.Attributes
{
[AttributeUsage(AttributeTargets.All)]
public class DatabaseChangedAttribute: Attribute
{
public DatabaseChangedAttribute()
{
DatabaseService.Instance.HasChanged = true;
}
}
}

View File

@@ -0,0 +1,26 @@
using System.Reflection;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Aop
{
public class DatabaseChangedProxy<T>: IProxyInvocationHandler
{
private readonly T _decorated;
private readonly IDatabaseService _databaseService;
public DatabaseChangedProxy(T decorated, IDatabaseService databaseService)
{
_decorated = decorated;
_databaseService = databaseService;
}
public object Invoke(object proxy, MethodInfo method, object[] parameters)
{
object retVal = null;
retVal = method.Invoke(proxy, parameters);
_databaseService.HasChanged = true;
return retVal;
}
}
}

View File

@@ -6,6 +6,9 @@ using Windows.Storage;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics;
using Microsoft.AppCenter.Push;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Exceptions; using ModernKeePass.Exceptions;
using ModernKeePass.Services; using ModernKeePass.Services;
@@ -20,19 +23,20 @@ namespace ModernKeePass
/// </summary> /// </summary>
sealed partial class App sealed partial class App
{ {
public DatabaseService Database { get; private set; }
/// <summary> /// <summary>
/// Initializes the singleton application object. This is the first line of authored code /// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain(). /// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary> /// </summary>
public App() public App()
{ {
AppCenter.Start("79d23520-a486-4f63-af81-8d90bf4e1bea", typeof(Analytics), typeof(Push));
InitializeComponent(); InitializeComponent();
Suspending += OnSuspending; Suspending += OnSuspending;
Resuming += OnResuming;
UnhandledException += OnUnhandledException; UnhandledException += OnUnhandledException;
} }
#region Event Handlers #region Event Handlers
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs) private void OnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
@@ -45,9 +49,16 @@ namespace ModernKeePass
? exception.InnerException ? exception.InnerException
: exception; : exception;
if (!(realException is SaveException)) return; if (realException is SaveException)
{
unhandledExceptionEventArgs.Handled = true; unhandledExceptionEventArgs.Handled = true;
MessageDialogHelper.SaveErrorDialog(realException as SaveException, Database); MessageDialogHelper.SaveErrorDialog(realException as SaveException, DatabaseService.Instance);
}
else if (realException is DatabaseOpenedException)
{
unhandledExceptionEventArgs.Handled = true;
MessageDialogHelper.SaveUnchangedDialog(realException as DatabaseOpenedException, DatabaseService.Instance);
}
} }
/// <summary> /// <summary>
@@ -89,7 +100,10 @@ namespace ModernKeePass
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{ {
//TODO: Load state from previously suspended application //TODO: Load state from previously terminated application
#if DEBUG
MessageDialogHelper.ShowNotificationDialog("App terminated", "Windows or an error made the app terminate");
#endif
} }
// Place the frame in the current Window // Place the frame in the current Window
@@ -122,7 +136,31 @@ namespace ModernKeePass
}*/ }*/
// Ensure the current window is active // Ensure the current window is active
Window.Current.Activate(); Window.Current.Activate();
Database = new DatabaseService(); }
private async void OnResuming(object sender, object e)
{
var currentFrame = Window.Current.Content as Frame;
var database = DatabaseService.Instance;
if (database.DatabaseFile == null)
{
#if DEBUG
ToastNotificationHelper.ShowGenericToast("App suspended", "Nothing to do, no previous database opened");
#endif
return;
}
try
{
if (database.CompositeKey != null) await database.ReOpen();
}
catch (Exception ex)
{
currentFrame?.Navigate(typeof(MainPage));
#if DEBUG
MessageDialogHelper.ShowErrorDialog(ex);
#endif
ToastNotificationHelper.ShowGenericToast("App suspended", "Database was closed (changes were saved)");
}
} }
/// <summary> /// <summary>
@@ -142,11 +180,21 @@ namespace ModernKeePass
/// </summary> /// </summary>
/// <param name="sender">The source of the suspend request.</param> /// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param> /// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e) private async void OnSuspending(object sender, SuspendingEventArgs e)
{ {
var deferral = e.SuspendingOperation.GetDeferral(); var deferral = e.SuspendingOperation.GetDeferral();
UnhandledException -= OnUnhandledException; var database = DatabaseService.Instance;
Database.Save(); try
{
if (SettingsService.Instance.GetSetting("SaveSuspend", true)) database.Save();
await database.Close(false);
}
catch (Exception exception)
{
#if DEBUG
ToastNotificationHelper.ShowErrorToast(exception);
#endif
}
deferral.Complete(); deferral.Complete();
} }
@@ -158,7 +206,7 @@ namespace ModernKeePass
{ {
base.OnFileActivated(args); base.OnFileActivated(args);
var rootFrame = new Frame(); var rootFrame = new Frame();
Database.DatabaseFile = args.Files[0] as StorageFile; DatabaseService.Instance.DatabaseFile = args.Files[0] as StorageFile;
rootFrame.Navigate(typeof(MainPage), args); rootFrame.Navigate(typeof(MainPage), args);
Window.Current.Content = rootFrame; Window.Current.Content = rootFrame;
Window.Current.Activate(); Window.Current.Activate();

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Windows.Storage.Pickers; using Windows.Storage.Pickers;
using Windows.UI.Popups; using Windows.UI.Popups;
using Windows.UI.Xaml.Media.Animation;
using ModernKeePass.Exceptions; using ModernKeePass.Exceptions;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
@@ -10,19 +9,20 @@ namespace ModernKeePass.Common
{ {
public static class MessageDialogHelper public static class MessageDialogHelper
{ {
public static async void ShowActionDialog(string title, string contentText, string actionButtonText, string cancelButtonText, UICommandInvokedHandler action) // TODO: include resources
public static async void ShowActionDialog(string title, string contentText, string actionButtonText, string cancelButtonText, UICommandInvokedHandler actionCommand, UICommandInvokedHandler cancelCommand)
{ {
// Create the message dialog and set its content // Create the message dialog and set its content
var messageDialog = CreateBasicDialog(title, contentText, cancelButtonText); var messageDialog = CreateBasicDialog(title, contentText, cancelButtonText, cancelCommand);
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers // Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
messageDialog.Commands.Add(new UICommand(actionButtonText, action)); messageDialog.Commands.Add(new UICommand(actionButtonText, actionCommand));
// Show the message dialog // Show the message dialog
await messageDialog.ShowAsync(); await messageDialog.ShowAsync();
} }
public static void SaveErrorDialog(SaveException exception, IDatabase database) public static void SaveErrorDialog(SaveException exception, IDatabaseService database)
{ {
ShowActionDialog("Save error", exception.InnerException.Message, "Save as", "Discard", async command => ShowActionDialog("Save error", exception.InnerException.Message, "Save as", "Discard", async command =>
{ {
@@ -35,6 +35,19 @@ namespace ModernKeePass.Common
var file = await savePicker.PickSaveFileAsync(); var file = await savePicker.PickSaveFileAsync();
if (file != null) database.Save(file); if (file != null) database.Save(file);
}, null);
}
public static void SaveUnchangedDialog(DatabaseOpenedException exception, IDatabaseService database)
{
ShowActionDialog("Opened database", $"Database {database.Name} is currently opened. What to you wish to do?", "Save changes", "Discard", command =>
{
database.Save();
database.Close();
},
command =>
{
database.Close();
}); });
} }
@@ -42,7 +55,7 @@ namespace ModernKeePass.Common
{ {
if (exception == null) return; if (exception == null) return;
// Create the message dialog and set its content // Create the message dialog and set its content
var messageDialog = CreateBasicDialog("Error occured", exception.Message, "OK"); var messageDialog = CreateBasicDialog(exception.Message, exception.StackTrace, "OK");
// Show the message dialog // Show the message dialog
await messageDialog.ShowAsync(); await messageDialog.ShowAsync();
@@ -56,13 +69,13 @@ namespace ModernKeePass.Common
await dialog.ShowAsync(); await dialog.ShowAsync();
} }
private static MessageDialog CreateBasicDialog(string title, string message, string dismissActionText) private static MessageDialog CreateBasicDialog(string title, string message, string dismissActionText, UICommandInvokedHandler cancelCommand = null)
{ {
// Create the message dialog and set its content // Create the message dialog and set its content
var messageDialog = new MessageDialog(message, title); var messageDialog = new MessageDialog(message, title);
// Add commands and set their callbacks; // Add commands and set their callbacks;
messageDialog.Commands.Add(new UICommand(dismissActionText)); messageDialog.Commands.Add(new UICommand(dismissActionText, cancelCommand));
// Set the command that will be invoked by default // Set the command that will be invoked by default
messageDialog.DefaultCommandIndex = 1; messageDialog.DefaultCommandIndex = 1;

View File

@@ -45,5 +45,10 @@ namespace ModernKeePass.Common
}; };
ToastNotificationManager.CreateToastNotifier().Show(toast); ToastNotificationManager.CreateToastNotifier().Show(toast);
} }
public static void ShowErrorToast(Exception exception)
{
ShowGenericToast(exception.Source, exception.Message);
}
} }
} }

View File

@@ -1,20 +0,0 @@
using System;
using Windows.UI.Text;
using Windows.UI.Xaml.Data;
namespace ModernKeePass.Converters
{
public class BooleanToFontStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var boolean = value is bool ? (bool)value : false;
return boolean ? FontStyle.Italic : FontStyle.Normal;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace ModernKeePass.Exceptions
{
public class DatabaseOpenedException: Exception
{
}
}

View File

@@ -1,4 +1,5 @@
using Windows.Storage; using System.Threading.Tasks;
using Windows.Storage;
using ModernKeePass.ViewModels; using ModernKeePass.ViewModels;
using ModernKeePassLib; using ModernKeePassLib;
using ModernKeePassLib.Cryptography.KeyDerivation; using ModernKeePassLib.Cryptography.KeyDerivation;
@@ -6,24 +7,29 @@ using ModernKeePassLib.Keys;
namespace ModernKeePass.Interfaces namespace ModernKeePass.Interfaces
{ {
public interface IDatabase public interface IDatabaseService
{ {
string Name { get; } string Name { get; }
bool RecycleBinEnabled { get; set; } bool RecycleBinEnabled { get; set; }
int Status { get; set; } //int Status { get; set; }
GroupVm RootGroup { get; set; } GroupVm RootGroup { get; set; }
GroupVm RecycleBin { get; set; } GroupVm RecycleBin { get; set; }
StorageFile DatabaseFile { get; set; } StorageFile DatabaseFile { get; set; }
CompositeKey CompositeKey { get; set; }
PwUuid DataCipher { get; set; } PwUuid DataCipher { get; set; }
PwCompressionAlgorithm CompressionAlgorithm { get; set; } PwCompressionAlgorithm CompressionAlgorithm { get; set; }
KdfParameters KeyDerivation { get; set; } KdfParameters KeyDerivation { get; set; }
bool IsOpen { get; }
bool IsFileOpen { get; }
bool IsClosed { get; }
bool HasChanged { get; set; }
void Open(CompositeKey key, bool createNew); Task Open(CompositeKey key, bool createNew = false);
void UpdateCompositeKey(CompositeKey key); Task ReOpen();
void Save(); void Save();
void Save(StorageFile file); void Save(StorageFile file);
void CreateRecycleBin(); void CreateRecycleBin(string title);
void AddDeletedItem(PwUuid id); void AddDeletedItem(PwUuid id);
void Close(); Task Close(bool releaseFile = true);
} }
} }

View File

@@ -0,0 +1,9 @@
using System.Reflection;
namespace ModernKeePass.Interfaces
{
public interface IProxyInvocationHandler
{
object Invoke(object proxy, MethodInfo method, object[] parameters);
}
}

View File

@@ -34,6 +34,6 @@ namespace ModernKeePass.Interfaces
/// <summary> /// <summary>
/// Delete from ViewModel /// Delete from ViewModel
/// </summary> /// </summary>
void MarkForDelete(); void MarkForDelete(string recycleBinTitle);
} }
} }

View File

@@ -4,7 +4,7 @@ using Windows.Storage;
namespace ModernKeePass.Interfaces namespace ModernKeePass.Interfaces
{ {
public interface IRecent public interface IRecentService
{ {
int EntryCount { get; } int EntryCount { get; }
Task<IStorageItem> GetFileAsync(string token); Task<IStorageItem> GetFileAsync(string token);

View File

@@ -1,6 +1,6 @@
namespace ModernKeePass.Interfaces namespace ModernKeePass.Interfaces
{ {
public interface IResource public interface IResourceService
{ {
string GetResourceValue(string key); string GetResourceValue(string key);
} }

View File

@@ -1,8 +0,0 @@
namespace ModernKeePass.Interfaces
{
public interface ISettings
{
T GetSetting<T>(string property);
void PutSetting<T>(string property, T value);
}
}

View File

@@ -0,0 +1,8 @@
namespace ModernKeePass.Interfaces
{
public interface ISettingsService
{
T GetSetting<T>(string property, T defaultValue = default(T));
void PutSetting<T>(string property, T value);
}
}

View File

@@ -109,20 +109,27 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Actions\ClipboardAction.cs" /> <Compile Include="Actions\ClipboardAction.cs" />
<Compile Include="Actions\NavigateToUrlAction.cs" />
<Compile Include="Actions\SetupFocusAction.cs" /> <Compile Include="Actions\SetupFocusAction.cs" />
<Compile Include="Aop\DatabaseChangedProxy.cs" />
<Compile Include="App.xaml.cs"> <Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon> <DependentUpon>App.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Aop\DatabaseChanged.cs" />
<Compile Include="Exceptions\DatabaseOpenedException.cs" />
<Compile Include="Interfaces\ILicenseService.cs" /> <Compile Include="Interfaces\ILicenseService.cs" />
<Compile Include="Interfaces\IRecent.cs" /> <Compile Include="Interfaces\IProxyInvocationHandler.cs" />
<Compile Include="Interfaces\IRecentService.cs" />
<Compile Include="Interfaces\IRecentItem.cs" /> <Compile Include="Interfaces\IRecentItem.cs" />
<Compile Include="Interfaces\IResource.cs" /> <Compile Include="Interfaces\IResourceService.cs" />
<Compile Include="ViewModels\DonateVm.cs" /> <Compile Include="Services\SingletonServiceBase.cs" />
<Compile Include="TemplateSelectors\SelectableDataTemplateSelector.cs" />
<Compile Include="ViewModels\Items\SettingsSaveVm.cs" />
<Compile Include="Views\MainPageFrames\DonatePage.xaml.cs"> <Compile Include="Views\MainPageFrames\DonatePage.xaml.cs">
<DependentUpon>DonatePage.xaml</DependentUpon> <DependentUpon>DonatePage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Services\DatabaseService.cs" /> <Compile Include="Services\DatabaseService.cs" />
<Compile Include="Interfaces\ISettings.cs" /> <Compile Include="Interfaces\ISettingsService.cs" />
<Compile Include="Common\MessageDialogHelper.cs" /> <Compile Include="Common\MessageDialogHelper.cs" />
<Compile Include="Common\NavigationHelper.cs" /> <Compile Include="Common\NavigationHelper.cs" />
<Compile Include="Common\NotifyPropertyChangedBase.cs" /> <Compile Include="Common\NotifyPropertyChangedBase.cs" />
@@ -139,7 +146,7 @@
<Compile Include="Converters\NullToBooleanConverter.cs" /> <Compile Include="Converters\NullToBooleanConverter.cs" />
<Compile Include="Exceptions\SaveException.cs" /> <Compile Include="Exceptions\SaveException.cs" />
<Compile Include="Extensions\DispatcherTaskExtensions.cs" /> <Compile Include="Extensions\DispatcherTaskExtensions.cs" />
<Compile Include="Interfaces\IDatabase.cs" /> <Compile Include="Interfaces\IDatabaseService.cs" />
<Compile Include="Interfaces\IHasSelectableObject.cs" /> <Compile Include="Interfaces\IHasSelectableObject.cs" />
<Compile Include="Interfaces\ISelectableModel.cs" /> <Compile Include="Interfaces\ISelectableModel.cs" />
<Compile Include="Views\BasePages\LayoutAwarePageBase.cs" /> <Compile Include="Views\BasePages\LayoutAwarePageBase.cs" />
@@ -149,6 +156,9 @@
<Compile Include="Views\SettingsPageFrames\SettingsNewDatabasePage.xaml.cs"> <Compile Include="Views\SettingsPageFrames\SettingsNewDatabasePage.xaml.cs">
<DependentUpon>SettingsNewDatabasePage.xaml</DependentUpon> <DependentUpon>SettingsNewDatabasePage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\SettingsPageFrames\SettingsSavePage.xaml.cs">
<DependentUpon>SettingsSavePage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPageFrames\SettingsSecurityPage.xaml.cs"> <Compile Include="Views\SettingsPageFrames\SettingsSecurityPage.xaml.cs">
<DependentUpon>SettingsSecurityPage.xaml</DependentUpon> <DependentUpon>SettingsSecurityPage.xaml</DependentUpon>
</Compile> </Compile>
@@ -165,7 +175,6 @@
<Compile Include="Converters\ColorToBrushConverter.cs" /> <Compile Include="Converters\ColorToBrushConverter.cs" />
<Compile Include="Converters\DoubleToSolidColorBrushConverter.cs" /> <Compile Include="Converters\DoubleToSolidColorBrushConverter.cs" />
<Compile Include="Converters\InverseBooleanToVisibilityConverter.cs" /> <Compile Include="Converters\InverseBooleanToVisibilityConverter.cs" />
<Compile Include="Converters\BooleanToFontStyleConverter.cs" />
<Compile Include="Converters\PluralizationConverter.cs" /> <Compile Include="Converters\PluralizationConverter.cs" />
<Compile Include="Converters\ProgressBarLegalValuesConverter.cs" /> <Compile Include="Converters\ProgressBarLegalValuesConverter.cs" />
<Compile Include="Converters\TextToWidthConverter.cs" /> <Compile Include="Converters\TextToWidthConverter.cs" />
@@ -240,6 +249,10 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Page Include="Views\SettingsPageFrames\SettingsSavePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\UserControls\CompositeKeyUserControl.xaml"> <Page Include="Views\UserControls\CompositeKeyUserControl.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
@@ -335,12 +348,24 @@
<HintPath>..\packages\Portable.BouncyCastle.1.8.1.3\lib\netstandard1.0\BouncyCastle.Crypto.dll</HintPath> <HintPath>..\packages\Portable.BouncyCastle.1.8.1.3\lib\netstandard1.0\BouncyCastle.Crypto.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Microsoft.AppCenter, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AppCenter.1.5.0\lib\portable-net45+win8+wpa81+wp8\Microsoft.AppCenter.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.AppCenter.Analytics, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AppCenter.Analytics.1.5.0\lib\portable-net45+win8+wpa81+wp8\Microsoft.AppCenter.Analytics.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.AppCenter.Push, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AppCenter.Push.1.5.0\lib\portable-net45+win8+wpa81+wp8\Microsoft.AppCenter.Push.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Toolkit.Uwp.Notifications, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Microsoft.Toolkit.Uwp.Notifications, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Toolkit.Uwp.Notifications.2.0.0\lib\dotnet\Microsoft.Toolkit.Uwp.Notifications.dll</HintPath> <HintPath>..\packages\Microsoft.Toolkit.Uwp.Notifications.2.0.0\lib\dotnet\Microsoft.Toolkit.Uwp.Notifications.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="ModernKeePassLib, Version=2.37.0.2000, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="ModernKeePassLib, Version=2.37.0.2000, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ModernKeePassLib.2.37.8000\lib\netstandard1.2\ModernKeePassLib.dll</HintPath> <HintPath>..\packages\ModernKeePassLib.2.38.2\lib\netstandard1.2\ModernKeePassLib.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
@@ -434,9 +459,6 @@
<Content Include="Assets\Wide310x150Logo.scale-140.png" /> <Content Include="Assets\Wide310x150Logo.scale-140.png" />
<Content Include="Assets\Wide310x150Logo.scale-180.png" /> <Content Include="Assets\Wide310x150Logo.scale-180.png" />
<Content Include="Assets\Wide310x150Logo.scale-80.png" /> <Content Include="Assets\Wide310x150Logo.scale-80.png" />
<Content Include="Data\WindowsStoreProxy.xml">
<SubType>Designer</SubType>
</Content>
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '12.0' "> <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '12.0' ">
<VisualStudioVersion>12.0</VisualStudioVersion> <VisualStudioVersion>12.0</VisualStudioVersion>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest"> <Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest">
<Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.10.0.31" /> <Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.13.0.32" />
<Properties> <Properties>
<DisplayName>ModernKeePass</DisplayName> <DisplayName>ModernKeePass</DisplayName>
<PublisherDisplayName>wismna</PublisherDisplayName> <PublisherDisplayName>wismna</PublisherDisplayName>

View File

@@ -24,6 +24,6 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.10.0.0")] [assembly: AssemblyVersion("1.13.0.0")]
[assembly: AssemblyFileVersion("1.10.0.0")] [assembly: AssemblyFileVersion("1.13.0.0")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Threading.Tasks;
using Windows.Storage; using Windows.Storage;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using ModernKeePass.Exceptions; using ModernKeePass.Exceptions;
@@ -14,22 +15,14 @@ using ModernKeePassLib.Serialization;
namespace ModernKeePass.Services namespace ModernKeePass.Services
{ {
public class DatabaseService: IDatabase public class DatabaseService: SingletonServiceBase<DatabaseService>, IDatabaseService
{ {
public enum DatabaseStatus
{
Error = -3,
NoCompositeKey = -2,
CompositeKeyError = -1,
Closed = 0,
Opening = 1,
Opened = 2
}
private readonly PwDatabase _pwDatabase = new PwDatabase(); private readonly PwDatabase _pwDatabase = new PwDatabase();
private readonly ISettings _settings; private readonly ISettingsService _settings;
private readonly IResource _resource; private StorageFile _realDatabaseFile;
private StorageFile _databaseFile; private StorageFile _databaseFile;
private GroupVm _recycleBin; private GroupVm _recycleBin;
private CompositeKey _compositeKey;
public GroupVm RootGroup { get; set; } public GroupVm RootGroup { get; set; }
@@ -39,11 +32,10 @@ namespace ModernKeePass.Services
set set
{ {
_recycleBin = value; _recycleBin = value;
_pwDatabase.RecycleBinUuid = _recycleBin.IdUuid; _pwDatabase.RecycleBinUuid = _recycleBin?.IdUuid;
} }
} }
public int Status { get; set; } = (int)DatabaseStatus.Closed;
public string Name => DatabaseFile?.Name; public string Name => DatabaseFile?.Name;
public bool RecycleBinEnabled public bool RecycleBinEnabled
@@ -57,9 +49,18 @@ namespace ModernKeePass.Services
get { return _databaseFile; } get { return _databaseFile; }
set set
{ {
_databaseFile = value; if (IsOpen && HasChanged)
Status = (int)DatabaseStatus.Opening; {
throw new DatabaseOpenedException();
} }
_databaseFile = value;
}
}
public CompositeKey CompositeKey
{
get { return _compositeKey; }
set { _compositeKey = value; }
} }
public PwUuid DataCipher public PwUuid DataCipher
@@ -80,29 +81,38 @@ namespace ModernKeePass.Services
set { _pwDatabase.KdfParameters = value; } set { _pwDatabase.KdfParameters = value; }
} }
public DatabaseService() : this(new SettingsService()) public bool IsOpen => _pwDatabase.IsOpen;
{ } public bool IsFileOpen => !_pwDatabase.IsOpen && _databaseFile != null;
public bool IsClosed => _databaseFile == null;
public bool HasChanged { get; set; }
public DatabaseService(ISettings settings) public DatabaseService() : this(SettingsService.Instance)
{
}
public DatabaseService(ISettingsService settings)
{ {
_settings = settings; _settings = settings;
} }
/// <summary> /// <summary>
/// Open a KeePass database /// Open a KeePass database
/// </summary> /// </summary>
/// <param name="key">The database composite key</param> /// <param name="key">The database composite key</param>
/// <param name="createNew">True to create a new database before opening it</param> /// <param name="createNew">True to create a new database before opening it</param>
/// <returns>An error message, if any</returns> /// <returns>An error message, if any</returns>
public void Open(CompositeKey key, bool createNew = false) public async Task Open(CompositeKey key, bool createNew = false)
{ {
// TODO: Check if there is an existing backup file
try try
{ {
if (key == null) if (key == null)
{ {
Status = (int)DatabaseStatus.NoCompositeKey; throw new ArgumentNullException(nameof(key));
return;
} }
_compositeKey = key;
var ioConnection = IOConnectionInfo.FromFile(DatabaseFile); var ioConnection = IOConnectionInfo.FromFile(DatabaseFile);
if (createNew) if (createNew)
{ {
@@ -120,18 +130,50 @@ namespace ModernKeePass.Services
} }
else _pwDatabase.Open(ioConnection, key, new NullStatusLogger()); else _pwDatabase.Open(ioConnection, key, new NullStatusLogger());
if (!_pwDatabase.IsOpen) return; //if (!_pwDatabase.IsOpen) return;
Status = (int)DatabaseStatus.Opened;
// Copy database in temp directory and use this file for operations
if (_settings.GetSetting<bool>("AntiCorruption"))
{
_realDatabaseFile = _databaseFile;
var backupFile =
await ApplicationData.Current.RoamingFolder.CreateFileAsync(Name,
CreationCollisionOption.FailIfExists);
Save(backupFile);
}
RootGroup = new GroupVm(_pwDatabase.RootGroup, null, RecycleBinEnabled ? _pwDatabase.RecycleBinUuid : null); RootGroup = new GroupVm(_pwDatabase.RootGroup, null, RecycleBinEnabled ? _pwDatabase.RecycleBinUuid : null);
} }
catch (InvalidCompositeKeyException) catch (InvalidCompositeKeyException ex)
{ {
Status = (int)DatabaseStatus.CompositeKeyError; throw new ArgumentException(ex.Message, ex);
} }
catch (Exception) }
public async Task ReOpen()
{ {
Status = (int)DatabaseStatus.Error; await Open(_compositeKey);
throw; }
/// <summary>
/// Commit the changes to the currently opened database to file
/// </summary>
public void Save()
{
if (!IsOpen) return;
try
{
_pwDatabase.Save(new NullStatusLogger());
// Test if save worked correctly
if (_settings.GetSetting<bool>("AntiCorruption"))
{
_pwDatabase.Open(_pwDatabase.IOConnectionInfo, _pwDatabase.MasterKey, new NullStatusLogger());
}
}
catch (Exception e)
{
throw new SaveException(e);
} }
} }
@@ -152,35 +194,23 @@ namespace ModernKeePass.Services
DatabaseFile = oldFile; DatabaseFile = oldFile;
throw; throw;
} }
finally
{
Status = (int)DatabaseStatus.Opened;
}
}
/// <summary>
/// Commit the changes to the currently opened database to file
/// </summary>
public void Save()
{
if (_pwDatabase == null || !_pwDatabase.IsOpen) return;
try
{
_pwDatabase.Save(new NullStatusLogger());
}
catch (Exception e)
{
throw new SaveException(e);
}
} }
/// <summary> /// <summary>
/// Close the currently opened database /// Close the currently opened database
/// </summary> /// </summary>
public void Close() public async Task Close(bool releaseFile = true)
{ {
_pwDatabase?.Close(); _pwDatabase?.Close();
Status = (int)DatabaseStatus.Closed;
// Restore the backup DB to the original one
if (_settings.GetSetting<bool>("AntiCorruption"))
{
if (_pwDatabase != null && _pwDatabase.Modified)
Save(_realDatabaseFile);
await DatabaseFile.DeleteAsync();
}
if (releaseFile) DatabaseFile = null;
} }
public void AddDeletedItem(PwUuid id) public void AddDeletedItem(PwUuid id)
@@ -188,18 +218,13 @@ namespace ModernKeePass.Services
_pwDatabase.DeletedObjects.Add(new PwDeletedObject(id, DateTime.UtcNow)); _pwDatabase.DeletedObjects.Add(new PwDeletedObject(id, DateTime.UtcNow));
} }
public void CreateRecycleBin() public void CreateRecycleBin(string title)
{ {
RecycleBin = RootGroup.AddNewGroup("Recycle bin"); RecycleBin = RootGroup.AddNewGroup(title);
RecycleBin.IsSelected = true; RecycleBin.IsSelected = true;
RecycleBin.IconSymbol = Symbol.Delete; RecycleBin.IconSymbol = Symbol.Delete;
} }
public void UpdateCompositeKey(CompositeKey key)
{
_pwDatabase.MasterKey = key;
}
private void CreateSampleData() private void CreateSampleData()
{ {
_pwDatabase.RootGroup.AddGroup(new PwGroup(true, true, "Banking", PwIcon.Count), true); _pwDatabase.RootGroup.AddGroup(new PwGroup(true, true, "Banking", PwIcon.Count), true);

View File

@@ -1,13 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Store; using Windows.ApplicationModel.Store;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
namespace ModernKeePass.Services namespace ModernKeePass.Services
{ {
public class LicenseService : ILicenseService public class LicenseService : SingletonServiceBase<LicenseService>, ILicenseService
{ {
public enum PurchaseResult public enum PurchaseResult
{ {
@@ -27,34 +26,13 @@ namespace ModernKeePass.Services
public LicenseService() public LicenseService()
{ {
// Initialize the license info for use in the app that is uploaded to the Store.
// Uncomment the following line in the release version of your app.
//_licenseInformation = CurrentApp.LicenseInformation;
// Initialize the license info for testing.
// Comment the following line in the release version of your app.
//_licenseInformation = CurrentAppSimulator.LicenseInformation;
#if DEBUG
try
{
var proxyFile = Package.Current.InstalledLocation.GetFileAsync("data\\WindowsStoreProxy.xml").GetAwaiter().GetResult();
CurrentAppSimulator.ReloadSimulatorAsync(proxyFile).GetAwaiter().GetResult();
}
catch { }
var listing = CurrentAppSimulator.LoadListingInformationAsync().GetAwaiter().GetResult();
#else
var listing = CurrentApp.LoadListingInformationAsync().GetAwaiter().GetResult(); var listing = CurrentApp.LoadListingInformationAsync().GetAwaiter().GetResult();
#endif
Products = listing.ProductListings; Products = listing.ProductListings;
} }
public async Task<int> Purchase(string addOn) public async Task<int> Purchase(string addOn)
{ {
#if DEBUG
var purchaseResults = await CurrentAppSimulator.RequestProductPurchaseAsync(addOn);
#else
var purchaseResults = await CurrentApp.RequestProductPurchaseAsync(addOn); var purchaseResults = await CurrentApp.RequestProductPurchaseAsync(addOn);
#endif
switch (purchaseResults.Status) switch (purchaseResults.Status)
{ {
case ProductPurchaseStatus.Succeeded: case ProductPurchaseStatus.Succeeded:
@@ -79,11 +57,7 @@ namespace ModernKeePass.Services
private async Task<PurchaseResult> ReportFulfillmentAsync(Guid transactionId, string productName) private async Task<PurchaseResult> ReportFulfillmentAsync(Guid transactionId, string productName)
{ {
#if DEBUG
var result = await CurrentAppSimulator.ReportConsumableFulfillmentAsync(productName, transactionId);
#else
var result = await CurrentApp.ReportConsumableFulfillmentAsync(productName, transactionId); var result = await CurrentApp.ReportConsumableFulfillmentAsync(productName, transactionId);
#endif
return (PurchaseResult) result; return (PurchaseResult) result;
} }

View File

@@ -8,7 +8,7 @@ using ModernKeePass.ViewModels;
namespace ModernKeePass.Services namespace ModernKeePass.Services
{ {
public class RecentService : IRecent public class RecentService : SingletonServiceBase<RecentService>, IRecentService
{ {
private readonly StorageItemMostRecentlyUsedList _mru = StorageApplicationPermissions.MostRecentlyUsedList; private readonly StorageItemMostRecentlyUsedList _mru = StorageApplicationPermissions.MostRecentlyUsedList;

View File

@@ -3,7 +3,7 @@ using ModernKeePass.Interfaces;
namespace ModernKeePass.Services namespace ModernKeePass.Services
{ {
public class ResourcesService: IResource public class ResourcesService: IResourceService
{ {
private const string ResourceFileName = "CodeBehind"; private const string ResourceFileName = "CodeBehind";
private readonly ResourceLoader _resourceLoader = ResourceLoader.GetForCurrentView(); private readonly ResourceLoader _resourceLoader = ResourceLoader.GetForCurrentView();

View File

@@ -5,11 +5,11 @@ using ModernKeePass.Interfaces;
namespace ModernKeePass.Services namespace ModernKeePass.Services
{ {
public class SettingsService : ISettings public class SettingsService : SingletonServiceBase<SettingsService>, ISettingsService
{ {
private readonly IPropertySet _values = ApplicationData.Current.LocalSettings.Values; private readonly IPropertySet _values = ApplicationData.Current.LocalSettings.Values;
public T GetSetting<T>(string property) public T GetSetting<T>(string property, T defaultValue = default(T))
{ {
try try
{ {
@@ -17,7 +17,7 @@ namespace ModernKeePass.Services
} }
catch (InvalidCastException) catch (InvalidCastException)
{ {
return default(T); return defaultValue;
} }
} }

View File

@@ -0,0 +1,12 @@
using System;
namespace ModernKeePass.Services
{
public abstract class SingletonServiceBase<T> where T : new()
{
private static readonly Lazy<T> LazyInstance =
new Lazy<T>(() => new T());
public static T Instance => LazyInstance.Value;
}
}

View File

@@ -204,9 +204,6 @@
<data name="EntryDeletingConfirmation" xml:space="preserve"> <data name="EntryDeletingConfirmation" xml:space="preserve">
<value>Are you sure you want to delete this entry?</value> <value>Are you sure you want to delete this entry?</value>
</data> </data>
<data name="EntryNew" xml:space="preserve">
<value>&lt; New entry &gt;</value>
</data>
<data name="EntryRecycled" xml:space="preserve"> <data name="EntryRecycled" xml:space="preserve">
<value>Entry moved to the Recycle bin</value> <value>Entry moved to the Recycle bin</value>
</data> </data>
@@ -222,9 +219,6 @@
<data name="GroupDeletingConfirmation" xml:space="preserve"> <data name="GroupDeletingConfirmation" xml:space="preserve">
<value>Are you sure you want to delete the whole group and all its entries?</value> <value>Are you sure you want to delete the whole group and all its entries?</value>
</data> </data>
<data name="GroupNew" xml:space="preserve">
<value>&lt; New group &gt;</value>
</data>
<data name="GroupRecycled" xml:space="preserve"> <data name="GroupRecycled" xml:space="preserve">
<value>Group moved to the Recycle bin</value> <value>Group moved to the Recycle bin</value>
</data> </data>
@@ -270,4 +264,13 @@
<data name="SettingsMenuItemSecurity" xml:space="preserve"> <data name="SettingsMenuItemSecurity" xml:space="preserve">
<value>Security</value> <value>Security</value>
</data> </data>
<data name="CompositeKeyErrorUserAccount" xml:space="preserve">
<value>user account</value>
</data>
<data name="SettingsMenuItemSave" xml:space="preserve">
<value>Saving</value>
</data>
<data name="RecycleBinTitle" xml:space="preserve">
<value>Recycle Bin</value>
</data>
</root> </root>

View File

@@ -171,11 +171,14 @@
<data name="CompositeKeyPassword.PlaceholderText" xml:space="preserve"> <data name="CompositeKeyPassword.PlaceholderText" xml:space="preserve">
<value>Password</value> <value>Password</value>
</data> </data>
<data name="CompositeKeyUserAccount.Text" xml:space="preserve">
<value>Windows User Account</value>
</data>
<data name="DonateButton.Content" xml:space="preserve"> <data name="DonateButton.Content" xml:space="preserve">
<value>Donate</value> <value>Donate</value>
</data> </data>
<data name="DonateDesc.Text" xml:space="preserve"> <data name="DonateDesc.Text" xml:space="preserve">
<value>Like this app? Why not make a small donation to support my work and help this app stay ad-free :) ?</value> <value>Like this app? Why not make a small donation to support my work and help me keep it ad-free :) ?</value>
</data> </data>
<data name="EntryExpirationDate.Content" xml:space="preserve"> <data name="EntryExpirationDate.Content" xml:space="preserve">
<value>Expiration date</value> <value>Expiration date</value>
@@ -183,6 +186,15 @@
<data name="EntryExpirationTooltip.Content" xml:space="preserve"> <data name="EntryExpirationTooltip.Content" xml:space="preserve">
<value>Password has expired</value> <value>Password has expired</value>
</data> </data>
<data name="EntryItemCopyLogin.Text" xml:space="preserve">
<value>Copy login</value>
</data>
<data name="EntryItemCopyPassword.Text" xml:space="preserve">
<value>Copy password</value>
</data>
<data name="EntryItemCopyUrl.Text" xml:space="preserve">
<value>Navigate to URL</value>
</data>
<data name="EntryLogin.Text" xml:space="preserve"> <data name="EntryLogin.Text" xml:space="preserve">
<value>User name or login</value> <value>User name or login</value>
</data> </data>
@@ -202,10 +214,10 @@
<value>Filter...</value> <value>Filter...</value>
</data> </data>
<data name="GroupNewItemTextBox.Text" xml:space="preserve"> <data name="GroupNewItemTextBox.Text" xml:space="preserve">
<value>&lt; New group &gt;</value> <value>New group</value>
</data> </data>
<data name="GroupNewItemTooltip.Content" xml:space="preserve"> <data name="GroupNewItemTooltip.Content" xml:space="preserve">
<value>&lt; New group &gt;</value> <value>New group</value>
</data> </data>
<data name="GroupSearch.PlaceholderText" xml:space="preserve"> <data name="GroupSearch.PlaceholderText" xml:space="preserve">
<value>Search...</value> <value>Search...</value>
@@ -270,6 +282,9 @@
<data name="RecentClear.Text" xml:space="preserve"> <data name="RecentClear.Text" xml:space="preserve">
<value>Clear all</value> <value>Clear all</value>
</data> </data>
<data name="ReorderEntriesLabel.Text" xml:space="preserve">
<value>Drag and drop entries to reorder them</value>
</data>
<data name="SaveAsButton.Content" xml:space="preserve"> <data name="SaveAsButton.Content" xml:space="preserve">
<value>Save as...</value> <value>Save as...</value>
</data> </data>
@@ -300,6 +315,12 @@
<data name="SettingsDatabaseRecycleBin.OnContent" xml:space="preserve"> <data name="SettingsDatabaseRecycleBin.OnContent" xml:space="preserve">
<value>Enabled</value> <value>Enabled</value>
</data> </data>
<data name="SettingsDatabaseRecycleBinCreate.Content" xml:space="preserve">
<value>Create a new group</value>
</data>
<data name="SettingsDatabaseRecycleBinExisting.Content" xml:space="preserve">
<value>Use an existing group</value>
</data>
<data name="SettingsNewDatabaseDesc.Text" xml:space="preserve"> <data name="SettingsNewDatabaseDesc.Text" xml:space="preserve">
<value>Here, you can change some default options when creating a database.</value> <value>Here, you can change some default options when creating a database.</value>
</data> </data>
@@ -318,6 +339,18 @@
<data name="SettingsNewDatabaseSample.OnContent" xml:space="preserve"> <data name="SettingsNewDatabaseSample.OnContent" xml:space="preserve">
<value>Yes</value> <value>Yes</value>
</data> </data>
<data name="SettingsSaveDatabaseSuspend.OffContent" xml:space="preserve">
<value>Don't save</value>
</data>
<data name="SettingsSaveDatabaseSuspend.OnContent" xml:space="preserve">
<value>Save</value>
</data>
<data name="SettingsSaveDatabaseSuspendDesc.Text" xml:space="preserve">
<value>This settings is generally recommended. If you enable it, database will automatically be saved on application suspension and closing. However, if your database is huge, saving may be deemed too long by Windows, which will then forcibly kill the app.</value>
</data>
<data name="SettingsSaveDatabaseSuspendTitle.Text" xml:space="preserve">
<value>Auto-save on suspend?</value>
</data>
<data name="SettingsSecurityDesc1.Text" xml:space="preserve"> <data name="SettingsSecurityDesc1.Text" xml:space="preserve">
<value>Here, you may change your database password, key file, or both. Just click on on</value> <value>Here, you may change your database password, key file, or both. Just click on on</value>
</data> </data>

View File

@@ -204,10 +204,6 @@
<data name="EntryDeletingConfirmation" xml:space="preserve"> <data name="EntryDeletingConfirmation" xml:space="preserve">
<value>Êtes-vous sûr de vouloir supprimer cette entrée ?</value> <value>Êtes-vous sûr de vouloir supprimer cette entrée ?</value>
</data> </data>
<data name="EntryNew" xml:space="preserve">
<value>&lt; Nouvelle entrée &gt;</value>
<comment>Unused</comment>
</data>
<data name="EntryRecycled" xml:space="preserve"> <data name="EntryRecycled" xml:space="preserve">
<value>Entrée placée dans la Corbeille</value> <value>Entrée placée dans la Corbeille</value>
</data> </data>
@@ -223,9 +219,6 @@
<data name="GroupDeletingConfirmation" xml:space="preserve"> <data name="GroupDeletingConfirmation" xml:space="preserve">
<value>Êtes-vous sûr de vouloir supprimer ce group et toutes ses entrées ?</value> <value>Êtes-vous sûr de vouloir supprimer ce group et toutes ses entrées ?</value>
</data> </data>
<data name="GroupNew" xml:space="preserve">
<value>&lt; Nouveau groupe &gt;</value>
</data>
<data name="GroupRecycled" xml:space="preserve"> <data name="GroupRecycled" xml:space="preserve">
<value>Groupe placé dans la Corbeille</value> <value>Groupe placé dans la Corbeille</value>
</data> </data>
@@ -271,4 +264,13 @@
<data name="SettingsMenuItemSecurity" xml:space="preserve"> <data name="SettingsMenuItemSecurity" xml:space="preserve">
<value>Sécurité</value> <value>Sécurité</value>
</data> </data>
<data name="CompositeKeyErrorUserAccount" xml:space="preserve">
<value>compte utilisateur</value>
</data>
<data name="SettingsMenuItemSave" xml:space="preserve">
<value>Sauvegardes</value>
</data>
<data name="RecycleBinTitle" xml:space="preserve">
<value>Corbeille</value>
</data>
</root> </root>

View File

@@ -142,7 +142,7 @@
<value>Accueil</value> <value>Accueil</value>
</data> </data>
<data name="AppBarRestore.Label" xml:space="preserve"> <data name="AppBarRestore.Label" xml:space="preserve">
<value>Restorer</value> <value>Restaurer</value>
</data> </data>
<data name="AppBarSave.Label" xml:space="preserve"> <data name="AppBarSave.Label" xml:space="preserve">
<value>Sauvegarder</value> <value>Sauvegarder</value>
@@ -171,6 +171,9 @@
<data name="CompositeKeyPassword.PlaceholderText" xml:space="preserve"> <data name="CompositeKeyPassword.PlaceholderText" xml:space="preserve">
<value>Mot de passe</value> <value>Mot de passe</value>
</data> </data>
<data name="CompositeKeyUserAccount.Text" xml:space="preserve">
<value>Compte Utilisateur Windows</value>
</data>
<data name="DonateButton.Content" xml:space="preserve"> <data name="DonateButton.Content" xml:space="preserve">
<value>Donner</value> <value>Donner</value>
</data> </data>
@@ -183,6 +186,15 @@
<data name="EntryExpirationTooltip.Content" xml:space="preserve"> <data name="EntryExpirationTooltip.Content" xml:space="preserve">
<value>Le mot de passe a expiré</value> <value>Le mot de passe a expiré</value>
</data> </data>
<data name="EntryItemCopyLogin.Text" xml:space="preserve">
<value>Copier le login</value>
</data>
<data name="EntryItemCopyPassword.Text" xml:space="preserve">
<value>Copier le mot de passe</value>
</data>
<data name="EntryItemCopyUrl.Text" xml:space="preserve">
<value>Naviguer vers l'URL</value>
</data>
<data name="EntryLogin.Text" xml:space="preserve"> <data name="EntryLogin.Text" xml:space="preserve">
<value>Nom d'utilisateur ou login</value> <value>Nom d'utilisateur ou login</value>
</data> </data>
@@ -202,10 +214,10 @@
<value>Filtrer...</value> <value>Filtrer...</value>
</data> </data>
<data name="GroupNewItemTextBox.Text" xml:space="preserve"> <data name="GroupNewItemTextBox.Text" xml:space="preserve">
<value>&lt; Nouveau groupe &gt;</value> <value>Nouveau groupe</value>
</data> </data>
<data name="GroupNewItemTooltip.Content" xml:space="preserve"> <data name="GroupNewItemTooltip.Content" xml:space="preserve">
<value>&lt; Nouveau groupe &gt;</value> <value>Nouveau groupe</value>
</data> </data>
<data name="GroupSearch.PlaceholderText" xml:space="preserve"> <data name="GroupSearch.PlaceholderText" xml:space="preserve">
<value>Rechercher...</value> <value>Rechercher...</value>
@@ -270,6 +282,9 @@
<data name="RecentClear.Text" xml:space="preserve"> <data name="RecentClear.Text" xml:space="preserve">
<value>Supprimer tout</value> <value>Supprimer tout</value>
</data> </data>
<data name="ReorderEntriesLabel.Text" xml:space="preserve">
<value>Glissez-déposez les entrées pour les réorganiser</value>
</data>
<data name="SaveAsButton.Content" xml:space="preserve"> <data name="SaveAsButton.Content" xml:space="preserve">
<value>Sauvegarder sous...</value> <value>Sauvegarder sous...</value>
</data> </data>
@@ -300,6 +315,12 @@
<data name="SettingsDatabaseRecycleBin.OnContent" xml:space="preserve"> <data name="SettingsDatabaseRecycleBin.OnContent" xml:space="preserve">
<value>Activé</value> <value>Activé</value>
</data> </data>
<data name="SettingsDatabaseRecycleBinCreate.Content" xml:space="preserve">
<value>Créer un nouveau groupe</value>
</data>
<data name="SettingsDatabaseRecycleBinExisting.Content" xml:space="preserve">
<value>Utiliser un groupe existant</value>
</data>
<data name="SettingsNewDatabaseDesc.Text" xml:space="preserve"> <data name="SettingsNewDatabaseDesc.Text" xml:space="preserve">
<value>Ici, vous pouvez changer certains options lors de la création d'une nouvelle base de données</value> <value>Ici, vous pouvez changer certains options lors de la création d'une nouvelle base de données</value>
</data> </data>
@@ -318,6 +339,18 @@
<data name="SettingsNewDatabaseSample.OnContent" xml:space="preserve"> <data name="SettingsNewDatabaseSample.OnContent" xml:space="preserve">
<value>Oui</value> <value>Oui</value>
</data> </data>
<data name="SettingsSaveDatabaseSuspend.OffContent" xml:space="preserve">
<value>Ne pas sauvegarder</value>
</data>
<data name="SettingsSaveDatabaseSuspend.OnContent" xml:space="preserve">
<value>Sauvegarder</value>
</data>
<data name="SettingsSaveDatabaseSuspendDesc.Text" xml:space="preserve">
<value>Ce paramètre est généralement recommandé. Si vous l'activez, la base de données sera sauvegardée automatiquement lors de la suspension et de la fermeture. Cependant, si votre base de données est très volumineuse, il se peut que Windows estime que cela prend trop de temps et tue l'application.</value>
</data>
<data name="SettingsSaveDatabaseSuspendTitle.Text" xml:space="preserve">
<value>Sauvegarder automatiquement lors de la suspension ?</value>
</data>
<data name="SettingsSecurityDesc1.Text" xml:space="preserve"> <data name="SettingsSecurityDesc1.Text" xml:space="preserve">
<value>Ici, vous pouvez changer le mot de passe maître, le fichier de clé, ou les deux. Cliquez simplement sur</value> <value>Ici, vous pouvez changer le mot de passe maître, le fichier de clé, ou les deux. Cliquez simplement sur</value>
</data> </data>

View File

@@ -0,0 +1,18 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Interfaces;
namespace ModernKeePass.TemplateSelectors
{
public class SelectableDataTemplateSelector: DataTemplateSelector
{
public DataTemplate TrueItem { get; set; }
public DataTemplate FalseItem { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var isSelectableItem = item as ISelectableModel;
return isSelectableItem != null && isSelectableItem.IsSelected ? TrueItem : FalseItem;
}
}
}

View File

@@ -2,7 +2,6 @@
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.Storage; using Windows.Storage;
using Windows.UI.Xaml;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Services; using ModernKeePass.Services;
@@ -22,7 +21,7 @@ namespace ModernKeePass.ViewModels
Success = 5 Success = 5
} }
public IDatabase Database { get; set; } public IDatabaseService Database { get; set; }
public bool HasPassword public bool HasPassword
{ {
@@ -44,7 +43,17 @@ namespace ModernKeePass.ViewModels
} }
} }
public bool IsValid => !_isOpening && (HasPassword || HasKeyFile && KeyFile != null); public bool HasUserAccount
{
get { return _hasUserAccount; }
set
{
SetProperty(ref _hasUserAccount, value);
OnPropertyChanged("IsValid");
}
}
public bool IsValid => !_isOpening && (HasPassword || HasKeyFile && KeyFile != null || HasUserAccount);
public string Status public string Status
{ {
@@ -91,19 +100,21 @@ namespace ModernKeePass.ViewModels
public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password?.ToCharArray()); public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password?.ToCharArray());
private bool _hasPassword; private bool _hasPassword;
private bool _hasKeyFile; private bool _hasKeyFile;
private bool _hasUserAccount;
private bool _isOpening; private bool _isOpening;
private string _password = string.Empty; private string _password = string.Empty;
private string _status; private string _status;
private StatusTypes _statusType; private StatusTypes _statusType;
private StorageFile _keyFile; private StorageFile _keyFile;
private string _keyFileText; private string _keyFileText;
private readonly IResource _resource; private readonly IResourceService _resource;
public CompositeKeyVm() : this((Application.Current as App)?.Database, new ResourcesService()) { } public CompositeKeyVm() : this(DatabaseService.Instance, new ResourcesService()) { }
public CompositeKeyVm(IDatabase database, IResource resource) public CompositeKeyVm(IDatabaseService database, IResourceService resource)
{ {
_resource = resource; _resource = resource;
_keyFileText = _resource.GetResourceValue("CompositeKeyDefaultKeyFile"); _keyFileText = _resource.GetResourceValue("CompositeKeyDefaultKeyFile");
@@ -112,42 +123,37 @@ namespace ModernKeePass.ViewModels
public async Task<bool> OpenDatabase(bool createNew) public async Task<bool> OpenDatabase(bool createNew)
{ {
var error = string.Empty;
try try
{ {
_isOpening = true; _isOpening = true;
Database.Open(CreateCompositeKey(), createNew); await Database.Open(CreateCompositeKey(), createNew);
}
catch (Exception e)
{
error = $"{_resource.GetResourceValue("CompositeKeyErrorOpen")}: {e.Message}";
}
finally
{
_isOpening = false;
}
switch ((DatabaseService.DatabaseStatus)Database.Status)
{
case DatabaseService.DatabaseStatus.Opened:
await Task.Run(() => RootGroup = Database.RootGroup); await Task.Run(() => RootGroup = Database.RootGroup);
return true; return true;
case DatabaseService.DatabaseStatus.CompositeKeyError: }
catch (ArgumentException)
{
var errorMessage = new StringBuilder(_resource.GetResourceValue("CompositeKeyErrorUserStart")); var errorMessage = new StringBuilder(_resource.GetResourceValue("CompositeKeyErrorUserStart"));
if (HasPassword) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserPassword")); if (HasPassword) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserPassword"));
if (HasPassword && HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserOr")); if (HasPassword && HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserOr"));
if (HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserKeyFile")); if (HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserKeyFile"));
if (HasUserAccount) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserAccount"));
UpdateStatus(errorMessage.ToString(), StatusTypes.Error); UpdateStatus(errorMessage.ToString(), StatusTypes.Error);
break; }
case DatabaseService.DatabaseStatus.Error: catch (Exception e)
{
var error = $"{_resource.GetResourceValue("CompositeKeyErrorOpen")}: {e.Message}";
UpdateStatus(error, StatusTypes.Error); UpdateStatus(error, StatusTypes.Error);
break; }
finally
{
_isOpening = false;
} }
return false; return false;
} }
public void UpdateKey() public void UpdateKey()
{ {
Database.UpdateCompositeKey(CreateCompositeKey()); Database.CompositeKey = CreateCompositeKey();
UpdateStatus(_resource.GetResourceValue("CompositeKeyUpdated"), StatusTypes.Success); UpdateStatus(_resource.GetResourceValue("CompositeKeyUpdated"), StatusTypes.Success);
} }
@@ -169,6 +175,7 @@ namespace ModernKeePass.ViewModels
var compositeKey = new CompositeKey(); var compositeKey = new CompositeKey();
if (HasPassword) compositeKey.AddUserKey(new KcpPassword(Password)); if (HasPassword) compositeKey.AddUserKey(new KcpPassword(Password));
if (HasKeyFile && KeyFile != null) compositeKey.AddUserKey(new KcpKeyFile(IOConnectionInfo.FromFile(KeyFile))); if (HasKeyFile && KeyFile != null) compositeKey.AddUserKey(new KcpKeyFile(IOConnectionInfo.FromFile(KeyFile)));
if (HasUserAccount) compositeKey.AddUserKey(new KcpUserAccount());
return compositeKey; return compositeKey;
} }
} }

View File

@@ -1,44 +0,0 @@
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.Store;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.Services;
namespace ModernKeePass.ViewModels
{
public class DonateVm: NotifyPropertyChangedBase
{
public ObservableCollection<ProductListing> Donations { get; }
public ProductListing SelectedItem
{
get { return _selectedItem; }
set { SetProperty(ref _selectedItem, value); }
}
private readonly ILicenseService _license;
private ProductListing _selectedItem;
public DonateVm() : this (new LicenseService()) { }
public DonateVm(ILicenseService license)
{
// TODO: find a nice way to order products
_license = license;
Donations = new ObservableCollection<ProductListing>(
_license.Products.Values
/*.OrderBy(p => decimal.Parse(p.FormattedPrice.Replace('\u00A0', ' '), NumberStyles.Currency,
CultureInfo.CurrentCulture.NumberFormat))*/
);
}
public async Task<int> Purchase()
{
return await _license.Purchase(SelectedItem.ProductId);
}
}
}

View File

@@ -1,8 +1,8 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Text; using System.Text;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using ModernKeePass.Attributes;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Mappings; using ModernKeePass.Mappings;
using ModernKeePass.Services; using ModernKeePass.Services;
@@ -47,12 +47,7 @@ namespace ModernKeePass.ViewModels
public string Name public string Name
{ {
get get { return GetEntryValue(PwDefs.TitleField); }
{
/*var title = GetEntryValue(PwDefs.TitleField);
return title == null ? _resource.GetResourceValue("EntryNew") : title;*/
return GetEntryValue(PwDefs.TitleField);
}
set { SetEntryValue(PwDefs.TitleField, value); } set { SetEntryValue(PwDefs.TitleField, value); }
} }
@@ -73,6 +68,7 @@ namespace ModernKeePass.ViewModels
NotifyPropertyChanged("PasswordComplexityIndicator"); NotifyPropertyChanged("PasswordComplexityIndicator");
} }
} }
public string Url public string Url
{ {
get { return GetEntryValue(PwDefs.UrlField); } get { return GetEntryValue(PwDefs.UrlField); }
@@ -117,6 +113,16 @@ namespace ModernKeePass.ViewModels
} }
} }
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
NotifyPropertyChanged("IsVisible");
}
}
public bool IsRecycleOnDelete => _database.RecycleBinEnabled && !ParentGroup.IsSelected; public bool IsRecycleOnDelete => _database.RecycleBinEnabled && !ParentGroup.IsSelected;
public bool IsRevealPassword public bool IsRevealPassword
@@ -151,10 +157,11 @@ namespace ModernKeePass.ViewModels
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
private readonly PwEntry _pwEntry; private readonly PwEntry _pwEntry;
private readonly IDatabase _database; private readonly IDatabaseService _database;
private bool _isEditMode; private bool _isEditMode;
private bool _isRevealPassword; private bool _isRevealPassword;
private double _passwordLength = 25; private double _passwordLength = 25;
private bool _isVisible = true;
private void NotifyPropertyChanged(string propertyName) private void NotifyPropertyChanged(string propertyName)
{ {
@@ -163,9 +170,9 @@ namespace ModernKeePass.ViewModels
public EntryVm() { } public EntryVm() { }
internal EntryVm(PwEntry entry, GroupVm parent) : this(entry, parent, (Application.Current as App)?.Database) { } internal EntryVm(PwEntry entry, GroupVm parent) : this(entry, parent, DatabaseService.Instance) { }
public EntryVm(PwEntry entry, GroupVm parent, IDatabase database) public EntryVm(PwEntry entry, GroupVm parent, IDatabaseService database)
{ {
_database = database; _database = database;
_pwEntry = entry; _pwEntry = entry;
@@ -206,15 +213,16 @@ namespace ModernKeePass.ViewModels
return _pwEntry?.Strings.GetSafe(key).ReadString(); return _pwEntry?.Strings.GetSafe(key).ReadString();
} }
[DatabaseChanged]
private void SetEntryValue(string key, string newValue) private void SetEntryValue(string key, string newValue)
{ {
_pwEntry?.Strings.Set(key, new ProtectedString(true, newValue)); _pwEntry?.Strings.Set(key, new ProtectedString(true, newValue));
} }
public void MarkForDelete() public void MarkForDelete(string recycleBinTitle)
{ {
if (_database.RecycleBinEnabled && _database.RecycleBin?.IdUuid == null) if (_database.RecycleBinEnabled && _database.RecycleBin?.IdUuid == null)
_database.CreateRecycleBin(); _database.CreateRecycleBin(recycleBinTitle);
Move(_database.RecycleBinEnabled && !ParentGroup.IsSelected ? _database.RecycleBin : null); Move(_database.RecycleBinEnabled && !ParentGroup.IsSelected ? _database.RecycleBin : null);
} }

View File

@@ -3,11 +3,12 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using ModernKeePass.Attributes;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Mappings; using ModernKeePass.Mappings;
using ModernKeePass.Services;
using ModernKeePassLib; using ModernKeePassLib;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
@@ -54,6 +55,7 @@ namespace ModernKeePass.ViewModels
public string Name public string Name
{ {
get { return _pwGroup == null ? string.Empty : _pwGroup.Name; } get { return _pwGroup == null ? string.Empty : _pwGroup.Name; }
[DatabaseChanged]
set { _pwGroup.Name = value; } set { _pwGroup.Name = value; }
} }
@@ -61,7 +63,6 @@ namespace ModernKeePass.ViewModels
{ {
get get
{ {
if (_pwGroup == null) return Symbol.Add;
var result = PwIconToSegoeMapping.GetSymbolFromIcon(_pwGroup.IconId); var result = PwIconToSegoeMapping.GetSymbolFromIcon(_pwGroup.IconId);
return result == Symbol.More ? Symbol.Folder : result; return result == Symbol.More ? Symbol.Folder : result;
} }
@@ -80,19 +81,6 @@ namespace ModernKeePass.ViewModels
set { SetProperty(ref _isMenuClosed, value); } set { SetProperty(ref _isMenuClosed, value); }
} }
public string Filter
{
get { return _filter; }
set
{
SetProperty(ref _filter, value);
OnPropertyChanged("EntriesFiltered");
}
}
public ObservableCollection<EntryVm> EntriesFiltered =>
new ObservableCollection<EntryVm>(Entries.Where(e => e.Name.IndexOf(Filter, StringComparison.OrdinalIgnoreCase) >= 0));
public string Path public string Path
{ {
get get
@@ -105,20 +93,19 @@ namespace ModernKeePass.ViewModels
} }
private readonly PwGroup _pwGroup; private readonly PwGroup _pwGroup;
private readonly IDatabase _database; private readonly IDatabaseService _database;
private bool _isEditMode; private bool _isEditMode;
private PwEntry _reorderedEntry; private PwEntry _reorderedEntry;
private ObservableCollection<EntryVm> _entries = new ObservableCollection<EntryVm>(); private ObservableCollection<EntryVm> _entries = new ObservableCollection<EntryVm>();
private string _filter = string.Empty;
private bool _isMenuClosed = true; private bool _isMenuClosed = true;
public GroupVm() {} public GroupVm() {}
internal GroupVm(PwGroup pwGroup, GroupVm parent, PwUuid recycleBinId = null) : this(pwGroup, parent, internal GroupVm(PwGroup pwGroup, GroupVm parent, PwUuid recycleBinId = null) : this(pwGroup, parent,
(Application.Current as App)?.Database, recycleBinId) DatabaseService.Instance, recycleBinId)
{ } { }
public GroupVm(PwGroup pwGroup, GroupVm parent, IDatabase database, PwUuid recycleBinId = null) public GroupVm(PwGroup pwGroup, GroupVm parent, IDatabaseService database, PwUuid recycleBinId = null)
{ {
_pwGroup = pwGroup; _pwGroup = pwGroup;
_database = database; _database = database;
@@ -128,9 +115,9 @@ namespace ModernKeePass.ViewModels
Entries = new ObservableCollection<EntryVm>(pwGroup.Entries.Select(e => new EntryVm(e, this))); Entries = new ObservableCollection<EntryVm>(pwGroup.Entries.Select(e => new EntryVm(e, this)));
Entries.CollectionChanged += Entries_CollectionChanged; Entries.CollectionChanged += Entries_CollectionChanged;
Groups = new ObservableCollection<GroupVm>(pwGroup.Groups.Select(g => new GroupVm(g, this, recycleBinId))); Groups = new ObservableCollection<GroupVm>(pwGroup.Groups.Select(g => new GroupVm(g, this, recycleBinId)));
Groups.Insert(0, new GroupVm());
} }
[DatabaseChanged]
private void Entries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) private void Entries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{ {
switch (e.Action) switch (e.Action)
@@ -147,6 +134,7 @@ namespace ModernKeePass.ViewModels
} }
} }
[DatabaseChanged]
public GroupVm AddNewGroup(string name = "") public GroupVm AddNewGroup(string name = "")
{ {
var pwGroup = new PwGroup(true, true, name, PwIcon.Folder); var pwGroup = new PwGroup(true, true, name, PwIcon.Folder);
@@ -165,10 +153,10 @@ namespace ModernKeePass.ViewModels
return newEntry; return newEntry;
} }
public void MarkForDelete() public void MarkForDelete(string recycleBinTitle)
{ {
if (_database.RecycleBinEnabled && _database.RecycleBin?.IdUuid == null) if (_database.RecycleBinEnabled && _database.RecycleBin?.IdUuid == null)
_database.CreateRecycleBin(); _database.CreateRecycleBin(recycleBinTitle);
Move(_database.RecycleBinEnabled && !IsSelected ? _database.RecycleBin : null); Move(_database.RecycleBinEnabled && !IsSelected ? _database.RecycleBin : null);
} }
@@ -177,6 +165,7 @@ namespace ModernKeePass.ViewModels
Move(PreviousGroup); Move(PreviousGroup);
} }
[DatabaseChanged]
public void Move(GroupVm destination) public void Move(GroupVm destination)
{ {
PreviousGroup = ParentGroup; PreviousGroup = ParentGroup;
@@ -204,6 +193,7 @@ namespace ModernKeePass.ViewModels
_database.Save(); _database.Save();
} }
[DatabaseChanged]
public void SortEntries() public void SortEntries()
{ {
var comparer = new PwEntryComparer(PwDefs.TitleField, true, false); var comparer = new PwEntryComparer(PwDefs.TitleField, true, false);
@@ -218,6 +208,7 @@ namespace ModernKeePass.ViewModels
} }
} }
[DatabaseChanged]
public void SortGroups() public void SortGroups()
{ {
try try

View File

@@ -1,6 +1,5 @@
using Windows.Storage; using Windows.Storage;
using ModernKeePass.Common; using ModernKeePass.Common;
using Windows.UI.Xaml;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Services; using ModernKeePass.Services;
@@ -31,20 +30,20 @@ namespace ModernKeePass.ViewModels
public void OpenDatabaseFile() public void OpenDatabaseFile()
{ {
OpenDatabaseFile((Application.Current as App)?.Database); OpenDatabaseFile(DatabaseService.Instance);
} }
public void OpenDatabaseFile(IDatabase database) public void OpenDatabaseFile(IDatabaseService database)
{ {
database.DatabaseFile = DatabaseFile; database.DatabaseFile = DatabaseFile;
} }
public void UpdateAccessTime() public void UpdateAccessTime()
{ {
UpdateAccessTime(new RecentService()); UpdateAccessTime(RecentService.Instance);
} }
public async void UpdateAccessTime(IRecent recent) public async void UpdateAccessTime(IRecentService recent)
{ {
await recent.GetFileAsync(Token); await recent.GetFileAsync(Token);
} }

View File

@@ -2,9 +2,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Windows.UI.Xaml;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Services;
using ModernKeePassLib; using ModernKeePassLib;
using ModernKeePassLib.Cryptography.Cipher; using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Cryptography.KeyDerivation; using ModernKeePassLib.Cryptography.KeyDerivation;
@@ -14,7 +14,7 @@ namespace ModernKeePass.ViewModels
// TODO: implement Kdf settings // TODO: implement Kdf settings
public class SettingsDatabaseVm: NotifyPropertyChangedBase, IHasSelectableObject public class SettingsDatabaseVm: NotifyPropertyChangedBase, IHasSelectableObject
{ {
private readonly IDatabase _database; private readonly IDatabaseService _database;
private GroupVm _selectedItem; private GroupVm _selectedItem;
public bool HasRecycleBin public bool HasRecycleBin
@@ -27,6 +27,15 @@ namespace ModernKeePass.ViewModels
} }
} }
public bool IsNewRecycleBin
{
get { return _database.RecycleBin == null; }
set
{
if (value) _database.RecycleBin = null;
}
}
public ObservableCollection<GroupVm> Groups { get; set; } public ObservableCollection<GroupVm> Groups { get; set; }
public IEnumerable<string> Ciphers public IEnumerable<string> Ciphers
@@ -73,7 +82,7 @@ namespace ModernKeePass.ViewModels
get { return Groups.FirstOrDefault(g => g.IsSelected); } get { return Groups.FirstOrDefault(g => g.IsSelected); }
set set
{ {
if (_selectedItem == value) return; if (_selectedItem == value || IsNewRecycleBin) return;
if (_selectedItem != null) if (_selectedItem != null)
{ {
_selectedItem.IsSelected = false; _selectedItem.IsSelected = false;
@@ -88,9 +97,9 @@ namespace ModernKeePass.ViewModels
} }
} }
public SettingsDatabaseVm() : this((Application.Current as App)?.Database) { } public SettingsDatabaseVm() : this(DatabaseService.Instance) { }
public SettingsDatabaseVm(IDatabase database) public SettingsDatabaseVm(IDatabaseService database)
{ {
_database = database; _database = database;
Groups = _database?.RootGroup.Groups; Groups = _database?.RootGroup.Groups;

View File

@@ -6,12 +6,12 @@ namespace ModernKeePass.ViewModels
{ {
public class SettingsNewVm public class SettingsNewVm
{ {
private ISettings _settings; private readonly ISettingsService _settings;
public SettingsNewVm() : this(new SettingsService()) public SettingsNewVm() : this(SettingsService.Instance)
{ } { }
public SettingsNewVm(ISettings settings) public SettingsNewVm(ISettingsService settings)
{ {
_settings = settings; _settings = settings;
} }

View File

@@ -0,0 +1,24 @@
using ModernKeePass.Interfaces;
using ModernKeePass.Services;
namespace ModernKeePass.ViewModels
{
public class SettingsSaveVm
{
private readonly ISettingsService _settings;
public SettingsSaveVm() : this(SettingsService.Instance)
{ }
public SettingsSaveVm(ISettingsService settings)
{
_settings = settings;
}
public bool IsSaveSuspend
{
get { return _settings.GetSetting("SaveSuspend", true); }
set { _settings.PutSetting("SaveSuspend", value); }
}
}
}

View File

@@ -1,7 +1,6 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Windows.ApplicationModel; using Windows.ApplicationModel;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
@@ -46,12 +45,12 @@ namespace ModernKeePass.ViewModels
public MainVm() {} public MainVm() {}
internal MainVm(Frame referenceFrame, Frame destinationFrame) : this(referenceFrame, destinationFrame, internal MainVm(Frame referenceFrame, Frame destinationFrame) : this(referenceFrame, destinationFrame,
(Application.Current as App)?.Database, new ResourcesService(), new RecentService()) DatabaseService.Instance, new ResourcesService(), RecentService.Instance)
{ } { }
public MainVm(Frame referenceFrame, Frame destinationFrame, IDatabase database, IResource resource, IRecent recent) public MainVm(Frame referenceFrame, Frame destinationFrame, IDatabaseService database, IResourceService resource, IRecentService recent)
{ {
var isDatabaseOpen = database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opened; var isDatabaseOpen = database != null && database.IsOpen;
var mainMenuItems = new ObservableCollection<MainMenuItemVm> var mainMenuItems = new ObservableCollection<MainMenuItemVm>
{ {
@@ -62,7 +61,7 @@ namespace ModernKeePass.ViewModels
Destination = destinationFrame, Destination = destinationFrame,
Parameter = referenceFrame, Parameter = referenceFrame,
SymbolIcon = Symbol.Page2, SymbolIcon = Symbol.Page2,
IsSelected = database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opening IsSelected = database != null && database.IsFileOpen && !database.IsOpen
}, },
new MainMenuItemVm new MainMenuItemVm
{ {
@@ -90,7 +89,7 @@ namespace ModernKeePass.ViewModels
Parameter = referenceFrame, Parameter = referenceFrame,
SymbolIcon = Symbol.Copy, SymbolIcon = Symbol.Copy,
IsSelected = IsSelected =
(database == null || database.Status == (int) DatabaseService.DatabaseStatus.Closed) && (database == null || database.IsClosed) &&
recent.EntryCount > 0, recent.EntryCount > 0,
IsEnabled = recent.EntryCount > 0 IsEnabled = recent.EntryCount > 0
}, },
@@ -120,7 +119,7 @@ namespace ModernKeePass.ViewModels
SelectedItem = mainMenuItems.FirstOrDefault(m => m.IsSelected); SelectedItem = mainMenuItems.FirstOrDefault(m => m.IsSelected);
// Add currently opened database to the menu // Add currently opened database to the menu
if (database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opened) if (database != null && database.IsOpen)
mainMenuItems.Add(new MainMenuItemVm mainMenuItems.Add(new MainMenuItemVm
{ {
Title = database.Name, Title = database.Name,

View File

@@ -1,5 +1,4 @@
using Windows.Storage; using Windows.Storage;
using Windows.UI.Xaml;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Services; using ModernKeePass.Services;
@@ -8,27 +7,27 @@ namespace ModernKeePass.ViewModels
{ {
public class OpenVm: NotifyPropertyChangedBase public class OpenVm: NotifyPropertyChangedBase
{ {
public bool ShowPasswordBox => _database?.Status == (int) DatabaseService.DatabaseStatus.Opening; public bool ShowPasswordBox => _database.IsFileOpen;
public string Name => _database?.Name; public string Name => _database?.Name;
private readonly IDatabase _database; private readonly IDatabaseService _database;
public OpenVm() : this((Application.Current as App)?.Database) { } public OpenVm() : this(DatabaseService.Instance) { }
public OpenVm(IDatabase database) public OpenVm(IDatabaseService database)
{ {
_database = database; _database = database;
if (database == null || database.Status != (int) DatabaseService.DatabaseStatus.Opening) return; if (database == null || !database.IsFileOpen) return;
OpenFile(database.DatabaseFile); OpenFile(database.DatabaseFile);
} }
public void OpenFile(StorageFile file) public void OpenFile(StorageFile file)
{ {
OpenFile(file, new RecentService()); OpenFile(file, RecentService.Instance);
} }
public void OpenFile(StorageFile file, IRecent recent) public void OpenFile(StorageFile file, IRecentService recent)
{ {
_database.DatabaseFile = file; _database.DatabaseFile = file;
OnPropertyChanged("Name"); OnPropertyChanged("Name");
@@ -36,7 +35,7 @@ namespace ModernKeePass.ViewModels
AddToRecentList(file, recent); AddToRecentList(file, recent);
} }
private void AddToRecentList(StorageFile file, IRecent recent) private void AddToRecentList(StorageFile file, IRecentService recent)
{ {
recent.Add(file, file.DisplayName); recent.Add(file, file.DisplayName);
} }

View File

@@ -7,7 +7,7 @@ namespace ModernKeePass.ViewModels
{ {
public class RecentVm : NotifyPropertyChangedBase, IHasSelectableObject public class RecentVm : NotifyPropertyChangedBase, IHasSelectableObject
{ {
private readonly IRecent _recent; private readonly IRecentService _recent;
private ISelectableModel _selectedItem; private ISelectableModel _selectedItem;
private ObservableCollection<IRecentItem> _recentItems = new ObservableCollection<IRecentItem>(); private ObservableCollection<IRecentItem> _recentItems = new ObservableCollection<IRecentItem>();
@@ -35,10 +35,10 @@ namespace ModernKeePass.ViewModels
} }
} }
public RecentVm() : this (new RecentService()) public RecentVm() : this (RecentService.Instance)
{ } { }
public RecentVm(IRecent recent) public RecentVm(IRecentService recent)
{ {
_recent = recent; _recent = recent;
RecentItems = _recent.GetAllFiles(); RecentItems = _recent.GetAllFiles();

View File

@@ -1,23 +1,25 @@
using Windows.Storage; using System.Threading.Tasks;
using Windows.UI.Xaml; using Windows.Storage;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Services;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
{ {
public class SaveVm public class SaveVm
{ {
private readonly IDatabase _database; private readonly IDatabaseService _database;
public SaveVm() : this((Application.Current as App)?.Database) { } public SaveVm() : this(DatabaseService.Instance) { }
public SaveVm(IDatabase database) public SaveVm(IDatabaseService database)
{ {
_database = database; _database = database;
} }
public void Save(bool close = true) public async Task Save(bool close = true)
{ {
_database.Save(); _database.Save();
if (close) _database.Close(); if (close)
await _database.Close();
} }
public void Save(StorageFile file) public void Save(StorageFile file)

View File

@@ -1,6 +1,5 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
@@ -41,9 +40,9 @@ namespace ModernKeePass.ViewModels
} }
} }
public SettingsVm() : this((Application.Current as App)?.Database, new ResourcesService()) { } public SettingsVm() : this(DatabaseService.Instance, new ResourcesService()) { }
public SettingsVm(IDatabase database, IResource resource) public SettingsVm(IDatabaseService database, IResourceService resource)
{ {
var menuItems = new ObservableCollection<ListMenuItemVm> var menuItems = new ObservableCollection<ListMenuItemVm>
{ {
@@ -56,12 +55,19 @@ namespace ModernKeePass.ViewModels
IsSelected = true IsSelected = true
}, },
new ListMenuItemVm new ListMenuItemVm
{
Title = resource.GetResourceValue("SettingsMenuItemSave"),
Group = resource.GetResourceValue("SettingsMenuGroupApplication"),
SymbolIcon = Symbol.Save,
PageType = typeof(SettingsSavePage)
},
new ListMenuItemVm
{ {
Title = resource.GetResourceValue("SettingsMenuItemGeneral"), Title = resource.GetResourceValue("SettingsMenuItemGeneral"),
Group = resource.GetResourceValue("SettingsMenuGroupDatabase"), Group = resource.GetResourceValue("SettingsMenuGroupDatabase"),
SymbolIcon = Symbol.Setting, SymbolIcon = Symbol.Setting,
PageType = typeof(SettingsDatabasePage), PageType = typeof(SettingsDatabasePage),
IsEnabled = database?.Status == 2 IsEnabled = database.IsOpen
}, },
new ListMenuItemVm new ListMenuItemVm
{ {
@@ -69,7 +75,7 @@ namespace ModernKeePass.ViewModels
Group = resource.GetResourceValue("SettingsMenuGroupDatabase"), Group = resource.GetResourceValue("SettingsMenuGroupDatabase"),
SymbolIcon = Symbol.Permissions, SymbolIcon = Symbol.Permissions,
PageType = typeof(SettingsSecurityPage), PageType = typeof(SettingsSecurityPage),
IsEnabled = database?.Status == 2 IsEnabled = database.IsOpen
} }
}; };
SelectedItem = menuItems.FirstOrDefault(m => m.IsSelected); SelectedItem = menuItems.FirstOrDefault(m => m.IsSelected);

View File

@@ -458,7 +458,13 @@
<ProgressBar Value="{Binding PasswordComplexityIndicator, ConverterParameter=0\,128, Converter={StaticResource ProgressBarLegalValuesConverter}}" Maximum="128" Width="350" HorizontalAlignment="Left" Foreground="{Binding PasswordComplexityIndicator, ConverterParameter=128, Converter={StaticResource DoubleToForegroungBrushComplexityConverter}}" /> <ProgressBar Value="{Binding PasswordComplexityIndicator, ConverterParameter=0\,128, Converter={StaticResource ProgressBarLegalValuesConverter}}" Maximum="128" Width="350" HorizontalAlignment="Left" Foreground="{Binding PasswordComplexityIndicator, ConverterParameter=128, Converter={StaticResource DoubleToForegroungBrushComplexityConverter}}" />
<CheckBox x:Uid="EntryShowPassword" HorizontalAlignment="Left" Margin="-3,0,0,0" IsChecked="{Binding IsRevealPassword, Mode=TwoWay}" IsEnabled="{Binding IsRevealPasswordEnabled}" /> <CheckBox x:Uid="EntryShowPassword" HorizontalAlignment="Left" Margin="-3,0,0,0" IsChecked="{Binding IsRevealPassword, Mode=TwoWay}" IsEnabled="{Binding IsRevealPasswordEnabled}" />
<TextBlock TextWrapping="Wrap" Text="URL" FontSize="18"/> <TextBlock TextWrapping="Wrap" Text="URL" FontSize="18"/>
<local:TextBoxWithButton x:Name="UrlTextBox" HorizontalAlignment="Left" Text="{Binding Url, Mode=TwoWay}" Height="32" Width="350" MaxLength="256" Style="{StaticResource TextBoxWithButtonStyle}" ButtonClick="UrlButton_Click" ButtonSymbol="&#xE111;" ButtonTooltip="Navigate to URL" /> <local:TextBoxWithButton x:Name="UrlTextBox" HorizontalAlignment="Left" Text="{Binding Url, Mode=TwoWay}" Height="32" Width="350" MaxLength="256" Style="{StaticResource TextBoxWithButtonStyle}" ButtonSymbol="&#xE111;" ButtonTooltip="Navigate to URL">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:NavigateToUrlAction Url="{Binding Url}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</local:TextBoxWithButton>
<TextBlock x:Uid="EntryNotes" TextWrapping="Wrap" FontSize="18" /> <TextBlock x:Uid="EntryNotes" TextWrapping="Wrap" FontSize="18" />
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Notes, Mode=TwoWay}" Width="350" Height="200" AcceptsReturn="True" IsSpellCheckEnabled="True" /> <TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Notes, Mode=TwoWay}" Width="350" Height="200" AcceptsReturn="True" IsSpellCheckEnabled="True" />
<CheckBox x:Uid="EntryExpirationDate" FontSize="18" IsChecked="{Binding HasExpirationDate, Mode=TwoWay}" /> <CheckBox x:Uid="EntryExpirationDate" FontSize="18" IsChecked="{Binding HasExpirationDate, Mode=TwoWay}" />

View File

@@ -81,9 +81,9 @@ namespace ModernKeePass.Views
resource.GetResourceValue("EntityDeleteCancelButton"), a => resource.GetResourceValue("EntityDeleteCancelButton"), a =>
{ {
ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityDeleting"), text); ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityDeleting"), text);
Model.MarkForDelete(); Model.MarkForDelete(resource.GetResourceValue("RecycleBinTitle"));
if (Frame.CanGoBack) Frame.GoBack(); if (Frame.CanGoBack) Frame.GoBack();
}); }, null);
} }
private void RestoreButton_Click(object sender, RoutedEventArgs e) private void RestoreButton_Click(object sender, RoutedEventArgs e)
@@ -93,19 +93,6 @@ namespace ModernKeePass.Views
if (Frame.CanGoBack) Frame.GoBack(); if (Frame.CanGoBack) Frame.GoBack();
} }
private async void UrlButton_Click(object sender, RoutedEventArgs e)
{
try
{
var uri = new Uri(UrlTextBox.Text);
await Windows.System.Launcher.LaunchUriAsync(uri);
}
catch (Exception ex)
{
MessageDialogHelper.ShowErrorDialog(ex);
}
}
private void EntryDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e) private void EntryDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e)
{ {
VisualStateManager.GoToState(this, e.NewSize.Width < 700 ? "Small" : "Large", true); VisualStateManager.GoToState(this, e.NewSize.Width < 700 ? "Small" : "Large", true);

View File

@@ -8,7 +8,6 @@
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core" xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:ModernKeePass.Actions" xmlns:actions="using:ModernKeePass.Actions"
xmlns:controls="using:ModernKeePass.Controls"
xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors" xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors"
x:Name="PageRoot" x:Name="PageRoot"
x:Class="ModernKeePass.Views.GroupDetailPage" x:Class="ModernKeePass.Views.GroupDetailPage"
@@ -16,7 +15,6 @@
SizeChanged="GroupDetailPage_OnSizeChanged"> SizeChanged="GroupDetailPage_OnSizeChanged">
<Page.Resources> <Page.Resources>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/> <converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<converters:BooleanToFontStyleConverter x:Key="BooleanToFontStyleConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/> <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/> <converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<converters:NullToBooleanConverter x:Key="NullToBooleanConverter"/> <converters:NullToBooleanConverter x:Key="NullToBooleanConverter"/>
@@ -84,7 +82,7 @@
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</AppBarButton> </AppBarButton>
<AppBarButton Icon="Delete" x:Uid="AppBarDelete" IsEnabled="{Binding IsNotRoot}" Click="DeleteButton_Click" /> <AppBarButton Icon="Delete" x:Uid="AppBarDelete" IsEnabled="{Binding IsNotRoot}" Visibility="{Binding IsSelected, Converter={StaticResource InverseBooleanToVisibilityConverter}}" Click="DeleteButton_Click" />
</CommandBar> </CommandBar>
</Page.BottomAppBar> </Page.BottomAppBar>
<Grid> <Grid>
@@ -94,7 +92,7 @@
Source="{Binding Groups}" /> Source="{Binding Groups}" />
<CollectionViewSource <CollectionViewSource
x:Name="EntriesViewSource" x:Name="EntriesViewSource"
Source="{Binding EntriesFiltered}" /> Source="{Binding Entries}" />
<CollectionViewSource <CollectionViewSource
x:Name="EntriesZoomedOutViewSource" x:Name="EntriesZoomedOutViewSource"
Source="{Binding EntriesZoomedOut}" IsSourceGrouped="True"/> Source="{Binding EntriesZoomedOut}" IsSourceGrouped="True"/>
@@ -127,27 +125,30 @@
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"> Foreground="{ThemeResource DefaultTextForegroundThemeBrush}">
<ListView.Resources> <ListView.Resources>
<DataTemplate x:Name="GroupOtherItem"> <DataTemplate x:Name="IsRecycleBin">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0"> <SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0">
<ToolTipService.ToolTip> <ToolTipService.ToolTip>
<ToolTip Content="{Binding Name}" /> <ToolTip Content="{Binding Name}" />
</ToolTipService.ToolTip> </ToolTipService.ToolTip>
</SymbolIcon> </SymbolIcon>
<TextBlock Text="{Binding Name}" x:Name="GroupTextBlock" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" FontStyle="{Binding IsSelected, Converter={StaticResource BooleanToFontStyleConverter}}" /> <TextBlock Text="{Binding Name}" x:Name="GroupTextBlock" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" FontStyle="Italic" />
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
<DataTemplate x:Name="GroupFirstItem"> <DataTemplate x:Name="IsNotRecycleBin">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0"> <SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0">
<ToolTipService.ToolTip> <ToolTipService.ToolTip>
<ToolTip x:Uid="GroupNewItemTooltip" /> <ToolTip Content="{Binding Name}" />
</ToolTipService.ToolTip> </ToolTipService.ToolTip>
</SymbolIcon> </SymbolIcon>
<TextBlock x:Name="GroupTextBlock" x:Uid="GroupNewItemTextBox" FontWeight="SemiBold" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" /> <TextBlock Text="{Binding Name}" x:Name="GroupTextBlock" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" />
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</ListView.Resources> </ListView.Resources>
<ListView.ItemTemplateSelector>
<templateSelectors:SelectableDataTemplateSelector FalseItem="{StaticResource IsNotRecycleBin}" TrueItem="{StaticResource IsRecycleBin}" />
</ListView.ItemTemplateSelector>
<ListView.ItemsSource> <ListView.ItemsSource>
<Binding Source="{StaticResource GroupsViewSource}"/> <Binding Source="{StaticResource GroupsViewSource}"/>
</ListView.ItemsSource> </ListView.ItemsSource>
@@ -173,13 +174,25 @@
</ToggleButton> </ToggleButton>
</DataTemplate> </DataTemplate>
</ListView.HeaderTemplate> </ListView.HeaderTemplate>
<ListView.ItemTemplateSelector> <ListView.FooterTemplate>
<templateSelectors:FirstItemDataTemplateSelector <DataTemplate>
FirstItem="{StaticResource GroupFirstItem}" <StackPanel Orientation="Vertical" Visibility="{Binding IsSelected, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
OtherItem="{StaticResource GroupOtherItem}" /> <Border BorderBrush="White" BorderThickness="0,0,0,1" />
</ListView.ItemTemplateSelector> <Button Padding="0" Height="50" Margin="0" Style="{StaticResource NoBorderButtonStyle}" Background="Transparent" BorderThickness="0" Click="CreateGroup_ButtonClick">
<StackPanel Orientation="Horizontal" Margin="13,0,5,0">
<SymbolIcon Symbol="Add">
<ToolTipService.ToolTip>
<ToolTip x:Uid="GroupNewItemTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock x:Uid="GroupNewItemTextBox" FontWeight="SemiBold" TextWrapping="NoWrap" FontSize="16" VerticalAlignment="Center" Margin="30,0,20,0" />
</StackPanel>
</Button>
</StackPanel>
</DataTemplate>
</ListView.FooterTemplate>
</ListView> </ListView>
<!-- Horizontal scrolling grid --> <TextBlock Grid.Column="1" x:Uid="ReorderEntriesLabel" Margin="20,20,0,0" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource BodyTextBlockStyle}" />
<HyperlinkButton Grid.Column="1" VerticalAlignment="Top" Margin="40,10,0,0" Click="CreateEntry_ButtonClick" Visibility="{Binding IsSelected, Converter={StaticResource InverseBooleanToVisibilityConverter}}" HorizontalAlignment="Right"> <HyperlinkButton Grid.Column="1" VerticalAlignment="Top" Margin="40,10,0,0" Click="CreateEntry_ButtonClick" Visibility="{Binding IsSelected, Converter={StaticResource InverseBooleanToVisibilityConverter}}" HorizontalAlignment="Right">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Add"></SymbolIcon> <SymbolIcon Symbol="Add"></SymbolIcon>
@@ -188,6 +201,7 @@
</HyperlinkButton> </HyperlinkButton>
<SemanticZoom Grid.Column="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" Margin="20,60,0,0"> <SemanticZoom Grid.Column="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" Margin="20,60,0,0">
<SemanticZoom.ZoomedInView> <SemanticZoom.ZoomedInView>
<!-- Horizontal scrolling grid -->
<GridView <GridView
x:Name="GridView" x:Name="GridView"
AutomationProperties.AutomationId="ItemGridView" AutomationProperties.AutomationId="ItemGridView"
@@ -211,6 +225,7 @@
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<SymbolIcon Grid.Column="0" Symbol="{Binding IconSymbol}" Width="100" Height="100" RenderTransformOrigin="0.5,0.5" > <SymbolIcon Grid.Column="0" Symbol="{Binding IconSymbol}" Width="100" Height="100" RenderTransformOrigin="0.5,0.5" >
<SymbolIcon.RenderTransform> <SymbolIcon.RenderTransform>
@@ -223,6 +238,34 @@
<TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" /> <TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
<TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" /> <TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
</StackPanel> </StackPanel>
<Button Grid.Column="2" Style="{StaticResource NoBorderButtonStyle}" VerticalAlignment="Bottom">
<SymbolIcon Symbol="More" />
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="EntryItemCopyLogin">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{Binding UserName}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemCopyPassword">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{Binding Password}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemCopyUrl">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:NavigateToUrlAction Url="{Binding Url}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
</MenuFlyout>
</Button.Flyout>
</Button>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</GridView.ItemTemplate> </GridView.ItemTemplate>
@@ -336,15 +379,22 @@
</TextBox> </TextBox>
<TextBlock FontSize="12" Text="{Binding Path}" /> <TextBlock FontSize="12" Text="{Binding Path}" />
</StackPanel> </StackPanel>
<Button Grid.Column="2" x:Name="FilterButton" Style="{StaticResource NoBorderButtonStyle}" Height="50"> <Button Grid.Column="2" x:Name="SearchButton" Style="{StaticResource NoBorderButtonStyle}" Height="50">
<SymbolIcon Symbol="Filter" /> <SymbolIcon Symbol="Find" />
<Button.Flyout> <Button.Flyout>
<Flyout> <Flyout>
<controls:TextBoxWithButton x:Uid="GroupFilter" ButtonSymbol="&#xE16E;" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18" VerticalContentAlignment="Center" Width="400" Style="{StaticResource TextBoxWithButtonStyle}" IsButtonEnabled="False" /> <Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0" />
</Style>
</Flyout.FlyoutPresenterStyle>
<!--<controls:TextBoxWithButton x:Uid="GroupFilter" ButtonSymbol="&#xE16E;" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18" VerticalContentAlignment="Center" Width="400" Style="{StaticResource TextBoxWithButtonStyle}" IsButtonEnabled="False" />-->
<SearchBox x:Uid="GroupSearch" Width="350" Padding="12" Background="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" BorderThickness="0" FontSize="18" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" />
</Flyout> </Flyout>
</Button.Flyout> </Button.Flyout>
</Button> </Button>
<controls:TextBoxWithButton Grid.Column="2" x:Name="FilterBox" x:Uid="GroupFilter" ButtonSymbol="&#xE16E;" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18" VerticalContentAlignment="Center" Width="400" Style="{StaticResource TextBoxWithButtonStyle}" IsButtonEnabled="False" /> <!--<controls:TextBoxWithButton Grid.Column="2" x:Name="FilterBox" x:Uid="GroupFilter" ButtonSymbol="&#xE16E;" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18" VerticalContentAlignment="Center" Width="400" Style="{StaticResource TextBoxWithButtonStyle}" IsButtonEnabled="False" />-->
<SearchBox Grid.Column="2" x:Uid="GroupSearch" x:Name="SearchBox" Padding="12" Width="350" Background="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" BorderThickness="0" FontSize="18" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" />
</Grid> </Grid>
<VisualStateManager.VisualStateGroups> <VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DragDropGroup"> <VisualStateGroup x:Name="DragDropGroup">
@@ -363,23 +413,23 @@
</Storyboard> </Storyboard>
</VisualState> </VisualState>
</VisualStateGroup> </VisualStateGroup>
<VisualStateGroup x:Name="FilterGroup"> <VisualStateGroup x:Name="SearchGroup">
<VisualState x:Name="Small"> <VisualState x:Name="Small">
<Storyboard> <Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FilterButton" Storyboard.TargetProperty="Visibility"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/> <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FilterBox" Storyboard.TargetProperty="Visibility"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchBox" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/> <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
</Storyboard> </Storyboard>
</VisualState> </VisualState>
<VisualState x:Name="Large"> <VisualState x:Name="Large">
<Storyboard> <Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FilterButton" Storyboard.TargetProperty="Visibility"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/> <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FilterBox" Storyboard.TargetProperty="Visibility"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchBox" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/> <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
</Storyboard> </Storyboard>

View File

@@ -1,5 +1,7 @@
using System; using System;
using System.Linq;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using Windows.Storage.Streams;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
@@ -82,9 +84,6 @@ namespace ModernKeePass.Views
{ {
case -1: case -1:
return; return;
case 0:
group = Model.AddNewGroup();
break;
default: default:
group = LeftListView.SelectedItem as GroupVm; group = LeftListView.SelectedItem as GroupVm;
break; break;
@@ -118,9 +117,9 @@ namespace ModernKeePass.Views
resource.GetResourceValue("EntityDeleteCancelButton"), a => resource.GetResourceValue("EntityDeleteCancelButton"), a =>
{ {
ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityDeleting"), text); ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityDeleting"), text);
Model.MarkForDelete(); Model.MarkForDelete(resource.GetResourceValue("RecycleBinTitle"));
if (Frame.CanGoBack) Frame.GoBack(); if (Frame.CanGoBack) Frame.GoBack();
}); }, null);
} }
private void RestoreButton_Click(object sender, RoutedEventArgs e) private void RestoreButton_Click(object sender, RoutedEventArgs e)
@@ -143,6 +142,10 @@ namespace ModernKeePass.Views
{ {
Frame.Navigate(typeof(EntryDetailPage), Model.AddNewEntry()); Frame.Navigate(typeof(EntryDetailPage), Model.AddNewEntry());
} }
private void CreateGroup_ButtonClick(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(GroupDetailPage), Model.AddNewGroup());
}
private void GridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e) private void GridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{ {
@@ -150,6 +153,22 @@ namespace ModernKeePass.Views
e.Data.RequestedOperation = DataPackageOperation.Move; e.Data.RequestedOperation = DataPackageOperation.Move;
} }
private void SearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
var imageUri = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx://Assets/ModernKeePass-SmallLogo.scale-80.png"));
var results = Model.Entries.Where(e => e.Name.IndexOf(args.QueryText, StringComparison.OrdinalIgnoreCase) >= 0).Take(5);
foreach (var result in results)
{
args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Name, result.ParentGroup.Name, result.Id, imageUri, string.Empty);
}
}
private void SearchBox_OnResultSuggestionChosen(SearchBox sender, SearchBoxResultSuggestionChosenEventArgs args)
{
var entry = Model.Entries.FirstOrDefault(e => e.Id == args.Tag);
Frame.Navigate(typeof(EntryDetailPage), entry);
}
private void GroupDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e) private void GroupDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e)
{ {
VisualStateManager.GoToState(this, e.NewSize.Width < 700 ? "Small" : "Large", true); VisualStateManager.GoToState(this, e.NewSize.Width < 700 ? "Small" : "Large", true);

View File

@@ -4,27 +4,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:converters="using:ModernKeePass.Converters"
mc:Ignorable="d"> mc:Ignorable="d">
<Page.DataContext> <WebView Source="https://PayPal.Me/wismna"></WebView>
<viewModels:DonateVm />
</Page.DataContext>
<Page.Resources>
<converters:NullToBooleanConverter x:Key="NullToBooleanConverter"/>
<CollectionViewSource
x:Name="DonateItemsSource"
Source="{Binding Donations}" />
</Page.Resources>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="10,0,0,0">
<TextBlock x:Uid="DonateDesc" Style="{StaticResource BodyTextBlockStyle}" />
<ItemsControl ItemsSource="{Binding Source={StaticResource DonateItemsSource}}" Margin="0,10,0,10">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton GroupName="DonateOptions" Content="{Binding FormattedPrice}" Checked="ToggleButton_OnChecked" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button x:Uid="DonateButton" Click="ButtonBase_OnClick" IsEnabled="{Binding SelectedItem, Converter={StaticResource NullToBooleanConverter}}" />
</StackPanel>
</Page> </Page>

View File

@@ -1,12 +1,4 @@
using System; // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
using Windows.ApplicationModel.Store;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
using ModernKeePass.Services;
using ModernKeePass.ViewModels;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views namespace ModernKeePass.Views
{ {
@@ -15,57 +7,9 @@ namespace ModernKeePass.Views
/// </summary> /// </summary>
public sealed partial class DonatePage public sealed partial class DonatePage
{ {
public DonateVm Model => DataContext as DonateVm;
public DonatePage() public DonatePage()
{ {
InitializeComponent(); InitializeComponent();
} }
private void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
{
var source = sender as RadioButton;
Model.SelectedItem = source?.DataContext as ProductListing;
}
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var resource = new ResourcesService();
try
{
var result = await Model.Purchase();
switch ((LicenseService.PurchaseResult)result)
{
case LicenseService.PurchaseResult.Succeeded:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonateSucceededTitle"), resource.GetResourceValue("DonateSucceededMessage"));
break;
case LicenseService.PurchaseResult.NothingToFulfill:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonateNothingToFulfillTitle"), resource.GetResourceValue("DonateNothingToFulfillMessage"));
break;
case LicenseService.PurchaseResult.PurchasePending:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonatePurchasePendingTitle"), resource.GetResourceValue("DonatePurchasePendingMessage"));
break;
case LicenseService.PurchaseResult.PurchaseReverted:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonatePurchaseRevertedTitle"), resource.GetResourceValue("DonatePurchaseRevertedMessage"));
break;
case LicenseService.PurchaseResult.ServerError:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonateServerErrorTitle"), resource.GetResourceValue("DonateServerErrorMessage"));
break;
case LicenseService.PurchaseResult.NotPurchased:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonateNotPurchasedTitle"), resource.GetResourceValue("DonateNotPurchasedMessage"));
break;
// Should never happen because these are consumables
case LicenseService.PurchaseResult.AlreadyPurchased:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonateAlreadyPurchasedTitle"), resource.GetResourceValue("DonateAlreadyPurchasedMessage"));
break;
default:
throw new ArgumentOutOfRangeException();
}
}
catch (Exception exception)
{
MessageDialogHelper.ShowErrorDialog(exception);
}
}
} }
} }

View File

@@ -28,10 +28,10 @@ namespace ModernKeePass.Views
_mainFrame = e.Parameter as Frame; _mainFrame = e.Parameter as Frame;
} }
private void SaveButton_OnClick(object sender, RoutedEventArgs e) private async void SaveButton_OnClick(object sender, RoutedEventArgs e)
{ {
Model.Save(); await Model.Save();
_mainFrame.Navigate(typeof(Views.MainPage)); _mainFrame.Navigate(typeof(MainPage));
} }
private async void SaveAsButton_OnClick(object sender, RoutedEventArgs e) private async void SaveAsButton_OnClick(object sender, RoutedEventArgs e)
@@ -47,7 +47,7 @@ namespace ModernKeePass.Views
if (file == null) return; if (file == null) return;
Model.Save(file); Model.Save(file);
_mainFrame.Navigate(typeof(Views.MainPage)); _mainFrame.Navigate(typeof(MainPage));
} }
} }
} }

View File

@@ -5,13 +5,15 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ModernKeePass.ViewModels" xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors" xmlns:converters="using:ModernKeePass.Converters"
mc:Ignorable="d"> mc:Ignorable="d">
<Page.Resources> <Page.Resources>
<CollectionViewSource x:Name="RecycleBinGroups" Source="{Binding Groups}" /> <CollectionViewSource x:Name="RecycleBinGroups" Source="{Binding Groups}" />
<CollectionViewSource x:Name="Ciphers" Source="{Binding Ciphers}" /> <CollectionViewSource x:Name="Ciphers" Source="{Binding Ciphers}" />
<CollectionViewSource x:Name="Compressions" Source="{Binding Compressions}" /> <CollectionViewSource x:Name="Compressions" Source="{Binding Compressions}" />
<CollectionViewSource x:Name="KeyDerivations" Source="{Binding KeyDerivations}" /> <CollectionViewSource x:Name="KeyDerivations" Source="{Binding KeyDerivations}" />
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:NullToBooleanConverter x:Key="NullToBooleanConverter"/>
</Page.Resources> </Page.Resources>
<Page.DataContext> <Page.DataContext>
<viewModels:SettingsDatabaseVm /> <viewModels:SettingsDatabaseVm />
@@ -19,21 +21,11 @@
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleSwitch x:Uid="SettingsDatabaseRecycleBin" IsOn="{Binding HasRecycleBin, Mode=TwoWay}" /> <ToggleSwitch x:Uid="SettingsDatabaseRecycleBin" IsOn="{Binding HasRecycleBin, Mode=TwoWay}" />
<ComboBox ItemsSource="{Binding Source={StaticResource RecycleBinGroups}}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" IsEnabled="{Binding HasRecycleBin}"> <StackPanel Visibility="{Binding HasRecycleBin, Converter={StaticResource BooleanToVisibilityConverter}}">
<ComboBox.Resources> <RadioButton x:Uid="SettingsDatabaseRecycleBinCreate" GroupName="Recycle" IsChecked="{Binding IsNewRecycleBin, Mode=TwoWay}" />
<DataTemplate x:Name="GroupFirstItem"> <RadioButton x:Name="RadioButton" x:Uid="SettingsDatabaseRecycleBinExisting" GroupName="Recycle" IsChecked="{Binding SelectedItem, Converter={StaticResource NullToBooleanConverter}}" />
<TextBlock x:Uid="GroupNewItemTextBox" /> <ComboBox ItemsSource="{Binding Source={StaticResource RecycleBinGroups}}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" IsEnabled="{Binding IsChecked, ElementName=RadioButton}" />
</DataTemplate> </StackPanel>
<DataTemplate x:Name="GroupOtherItem">
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.Resources>
<ComboBox.ItemTemplateSelector>
<templateSelectors:FirstItemDataTemplateSelector
FirstItem="{StaticResource GroupFirstItem}"
OtherItem="{StaticResource GroupOtherItem}" />
</ComboBox.ItemTemplateSelector>
</ComboBox>
<TextBlock x:Uid="SettingsDatabaseEncryption" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" /> <TextBlock x:Uid="SettingsDatabaseEncryption" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />
<ComboBox ItemsSource="{Binding Source={StaticResource Ciphers}}" SelectedIndex="{Binding CipherIndex, Mode=TwoWay}" /> <ComboBox ItemsSource="{Binding Source={StaticResource Ciphers}}" SelectedIndex="{Binding CipherIndex, Mode=TwoWay}" />
<TextBlock x:Uid="SettingsDatabaseCompression" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" /> <TextBlock x:Uid="SettingsDatabaseCompression" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />

View File

@@ -0,0 +1,18 @@
<Page
x:Class="ModernKeePass.Views.SettingsSavePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ModernKeePass.ViewModels"
mc:Ignorable="d">
<Page.DataContext>
<viewModels:SettingsSaveVm />
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Uid="SettingsSaveDatabaseSuspendTitle" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,0,0,10"/>
<TextBlock x:Uid="SettingsSaveDatabaseSuspendDesc" TextWrapping="WrapWholeWords" Margin="5,0,0,10"/>
<ToggleSwitch x:Uid="SettingsSaveDatabaseSuspend" IsOn="{Binding IsSaveSuspend, Mode=TwoWay}" />
</StackPanel>
</Page>

View File

@@ -0,0 +1,15 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class SettingsSavePage
{
public SettingsSavePage()
{
InitializeComponent();
}
}
}

View File

@@ -2,7 +2,6 @@
x:Class="ModernKeePass.Views.SettingsSecurityPage" x:Class="ModernKeePass.Views.SettingsSecurityPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ModernKeePass.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:userControls="using:ModernKeePass.Views.UserControls" xmlns:userControls="using:ModernKeePass.Views.UserControls"

View File

@@ -11,7 +11,6 @@
xmlns:viewModels="using:ModernKeePass.ViewModels" xmlns:viewModels="using:ModernKeePass.ViewModels"
mc:Ignorable="d" > mc:Ignorable="d" >
<UserControl.Resources> <UserControl.Resources>
<SolidColorBrush x:Key="ErrorColorBrush" Color="Red"/>
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter"/> <converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter"/>
<converters:DoubleToSolidColorBrushConverter x:Key="DoubleToForegroungBrushConverter"/> <converters:DoubleToSolidColorBrushConverter x:Key="DoubleToForegroungBrushConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/> <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
@@ -55,7 +54,7 @@
</ToolTipService.ToolTip> </ToolTipService.ToolTip>
</SymbolIcon> </SymbolIcon>
</HyperlinkButton> </HyperlinkButton>
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="3" Height="28" FontSize="14" FontWeight="Light" Text="{Binding Status}" Foreground="{Binding StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" Visibility="{Binding Status, Converter={StaticResource EmptyStringToVisibilityConverter}}" />
<Button Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" Content="{Binding ButtonLabel, ElementName=UserControl}" Click="OpenButton_OnClick" Background="{ThemeResource ListViewItemSelectedPointerOverBorderThemeBrush}" Foreground="{ThemeResource TextBoxBackgroundThemeBrush}" IsEnabled="{Binding IsValid}" /> <Button Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" Content="{Binding ButtonLabel, ElementName=UserControl}" Click="OpenButton_OnClick" Background="{ThemeResource ListViewItemSelectedPointerOverBorderThemeBrush}" Foreground="{ThemeResource TextBoxBackgroundThemeBrush}" IsEnabled="{Binding IsValid}" />
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="3" Height="28" FontSize="14" FontWeight="Light" Text="{Binding Status}" Foreground="{Binding StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" Visibility="{Binding Status, Converter={StaticResource EmptyStringToVisibilityConverter}}" />
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -90,7 +90,12 @@ namespace ModernKeePass.Views.UserControls
private void PasswordBox_KeyDown(object sender, KeyRoutedEventArgs e) private void PasswordBox_KeyDown(object sender, KeyRoutedEventArgs e)
{ {
if (e.Key == VirtualKey.Enter && Model.IsValid) OpenButton_OnClick(null, null); if (e.Key == VirtualKey.Enter && Model.IsValid)
{
OpenButton_OnClick(sender, e);
// Stop the event from triggering twice
e.Handled = true;
}
} }
private async void KeyFileButton_Click(object sender, RoutedEventArgs e) private async void KeyFileButton_Click(object sender, RoutedEventArgs e)

View File

@@ -1,11 +1,4 @@
You can now reorder entries Application now correctly resumes from suspend
Entries and groups are sortable Code enhancements
New Home button that allows returning to the main menu from anywhere Return of the Donate page, with Paypal
Change default database creation settings KeePassLib version bump to 2.38
Settings is accessible from main menu
Clear Recent databases
New French translation
Better layout whith small screen size
Search becomes Filter
Design and usability improvements
Bugs correction

View File

@@ -1,6 +1,6 @@
En avez-vous assez d'essayer de retenir des quantit<EFBFBD>s de mots de passe ? Etes-vous soucieux que le fait d'utiliser le m<EFBFBD>me mot de passe partout vous rend vuln<EFBFBD>rable ? En avez-vous assez d'essayer de retenir des quantites de mots de passe ? Etes-vous soucieux que le fait d'utiliser le meme mot de passe partout vous rend vulnerable ?
ModernKeePass est un gestionnaire de mots de passe gratuit, libre, facile <EFBFBD> utiliser mais n<EFBFBD>anmoins s<EFBFBD>r, bas<EFBFBD> sur la technologie certifi<EFBFBD>e et r<EFBFBD>pandue KeePass 2.x et compatible avec celui-ci. ModernKeePass est un gestionnaire de mots de passe gratuit, libre, facile a utiliser mais neanmoins sur, base sur la technologie certifiee et repandue KeePass 2.x et compatible avec celui-ci.
Vous pouvez stocker ou g<EFBFBD>n<EFBFBD>rer vos mots de passe dans une base de donn<EFBFBD>es chiffr<EFBFBD>e, qui peut <EFBFBD>tre plac<EFBFBD>e n'importe o<EFBFBD> (ordinateur personel/tablette, cloud, cl<EFBFBD> USB...) Vous pouvez stocker ou generer vos mots de passe dans une base de donnees chiffree, qui peut etre placee n'importe ou (ordinateur personnel/tablette, cloud, cle USB...)
Bien que la s<EFBFBD>curit<EFBFBD> informatique soit tr<EFBFBD>s importante, cette application essaie de rester simple <EFBFBD> utiliser et <EFBFBD> comprendre. Vos suggestions sont les bienvenues ! Bien que la securite informatique soit tres importante, cette application essaie de rester simple a utiliser et a comprendre. Vos suggestions sont les bienvenues !
Fonctionne sur Windows 10, 8.1 et RT. Fonctionne sur Windows 10, 8.1 et RT.

View File

@@ -1 +1 @@
Page d'entr<EFBFBD>e avec g<EFBFBD>n<EFBFBD>rateur de mot de passe Page d'entree avec generateur de mot de passe

View File

@@ -1 +1 @@
Filtrez vos entr<EFBFBD>es pour rapidement trouver celle qui vous int<EFBFBD>resse Filtrez vos entrees pour rapidement trouver celle qui vous interesse

View File

@@ -1 +1 @@
Vue d'un groupe, avec ses sous-groupes et ses entr<EFBFBD>es Vue d'un groupe, avec ses sous-groupes et ses entrees

View File

@@ -1 +1 @@
Cr<EFBFBD>ez de nouvelles bases de donn<EFBFBD>es, en d<EFBFBD>finissant un mot de passe et/ou un fichier de cl<EFBFBD> existant ou nouveau Creez de nouvelles bases de donnees, en definissant un mot de passe et/ou un fichier de cle existant ou nouveau

View File

@@ -1 +1 @@
Ouvrez une base de donn<EFBFBD>es avec mot de passe et/ou fichier de cl<EFBFBD> Ouvrez une base de donnees avec mot de passe et/ou fichier de cle

View File

@@ -1 +1 @@
Ouvrez les fichiers r<EFBFBD>cents Ouvrez les fichiers recents

View File

@@ -1 +1 @@
Vue d'ensemble avec le zoom s<EFBFBD>mantique Vue d'ensemble avec le zoom semantique

View File

@@ -1 +1 @@
Param<EFBFBD>tres de l'application Parametres de l'application

View File

@@ -1,11 +1,4 @@
Vous pouvez d<>sormais r<>organiser vos entr<74>es L'application recupere correctement d'une suspension
Les entr<74>es et les groupes peuvent <20>tre tri<72>s Ameliorations de code
Nouveau bouton d'Accueil qui permet de retourner au menu principal Retour de la page de donation, avec Paypal
Changez les options de cr<EFBFBD>ation des nouvelles bases de donn<6E>es Version de la KeePassLib montee a 2.38
Les param<61>tres sont accessibles depuis le menu principal
Effacez les <20>l<EFBFBD>ments r<>cents
Traduction fran<61>aise
Meilleur rendu en petit <20>cran
La recherche devient Filtre
Am<EFBFBD>liorations de design et d'ergonomie
Corrections de bogues

View File

@@ -1,10 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Microsoft.AppCenter" version="1.5.0" targetFramework="win81" />
<package id="Microsoft.AppCenter.Analytics" version="1.5.0" targetFramework="win81" />
<package id="Microsoft.AppCenter.Push" version="1.5.0" targetFramework="win81" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="win81" /> <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="win81" />
<package id="Microsoft.NETCore.Platforms" version="2.0.1" targetFramework="win81" /> <package id="Microsoft.NETCore.Platforms" version="2.0.1" targetFramework="win81" />
<package id="Microsoft.NETCore.Portable.Compatibility" version="1.0.2" targetFramework="win81" /> <package id="Microsoft.NETCore.Portable.Compatibility" version="1.0.2" targetFramework="win81" />
<package id="Microsoft.Toolkit.Uwp.Notifications" version="2.0.0" targetFramework="win81" /> <package id="Microsoft.Toolkit.Uwp.Notifications" version="2.0.0" targetFramework="win81" />
<package id="ModernKeePassLib" version="2.37.8000" targetFramework="win81" /> <package id="ModernKeePassLib" version="2.38.2" targetFramework="win81" />
<package id="NETStandard.Library" version="2.0.1" targetFramework="win81" /> <package id="NETStandard.Library" version="2.0.1" targetFramework="win81" />
<package id="Portable.BouncyCastle" version="1.8.1.3" targetFramework="win81" /> <package id="Portable.BouncyCastle" version="1.8.1.3" targetFramework="win81" />
<package id="Splat" version="2.0.0" targetFramework="win81" /> <package id="Splat" version="2.0.0" targetFramework="win81" />

View File

@@ -16,20 +16,20 @@ namespace ModernKeePassApp.Test
[TestMethod] [TestMethod]
public void TestCreate() public void TestCreate()
{ {
Assert.AreEqual((int) DatabaseService.DatabaseStatus.Closed, _database.Status); Assert.IsTrue(_database.IsClosed);
_database.DatabaseFile = ApplicationData.Current.TemporaryFolder.CreateFileAsync("NewDatabase.kdbx").GetAwaiter().GetResult(); _database.DatabaseFile = ApplicationData.Current.TemporaryFolder.CreateFileAsync("NewDatabase.kdbx").GetAwaiter().GetResult();
Assert.AreEqual((int)DatabaseService.DatabaseStatus.Opening, _database.Status); Assert.IsTrue(_database.IsFileOpen);
OpenOrCreateDatabase(true); OpenOrCreateDatabase(true);
_database.Close(); _database.Close().GetAwaiter().GetResult();
Assert.AreEqual((int)DatabaseService.DatabaseStatus.Closed, _database.Status); Assert.IsTrue(_database.IsClosed);
} }
[TestMethod] [TestMethod]
public void TestOpen() public void TestOpen()
{ {
Assert.AreEqual((int)DatabaseService.DatabaseStatus.Closed, _database.Status); Assert.IsTrue(_database.IsClosed);
_database.DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Databases\TestDatabase.kdbx").GetAwaiter().GetResult(); _database.DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx").GetAwaiter().GetResult();
Assert.AreEqual((int)DatabaseService.DatabaseStatus.Opening, _database.Status); Assert.IsTrue(_database.IsFileOpen);
OpenOrCreateDatabase(false); OpenOrCreateDatabase(false);
} }
@@ -38,23 +38,23 @@ namespace ModernKeePassApp.Test
{ {
TestOpen(); TestOpen();
_database.Save(ApplicationData.Current.TemporaryFolder.CreateFileAsync("SaveDatabase.kdbx").GetAwaiter().GetResult()); _database.Save(ApplicationData.Current.TemporaryFolder.CreateFileAsync("SaveDatabase.kdbx").GetAwaiter().GetResult());
Assert.AreEqual((int)DatabaseService.DatabaseStatus.Opened, _database.Status); Assert.IsTrue(_database.IsOpen);
_database.Close(); _database.Close().GetAwaiter().GetResult();
Assert.AreEqual((int)DatabaseService.DatabaseStatus.Closed, _database.Status); Assert.IsTrue(_database.IsClosed);
TestOpen(); TestOpen();
} }
private void OpenOrCreateDatabase(bool createNew) private void OpenOrCreateDatabase(bool createNew)
{ {
_database.Open(null, createNew); Assert.ThrowsException<ArgumentNullException>(
Assert.AreEqual((int)DatabaseService.DatabaseStatus.NoCompositeKey, _database.Status); () => _database.Open(null, createNew).GetAwaiter().GetResult());
var compositeKey = new CompositeKeyVm(_database, new ResourceServiceMock()) var compositeKey = new CompositeKeyVm(_database, new ResourceServiceMock())
{ {
HasPassword = true, HasPassword = true,
Password = "test" Password = "test"
}; };
compositeKey.OpenDatabase(createNew).GetAwaiter().GetResult(); compositeKey.OpenDatabase(createNew).GetAwaiter().GetResult();
Assert.AreEqual((int)DatabaseService.DatabaseStatus.Opened, _database.Status); Assert.IsTrue(_database.IsOpen);
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Threading.Tasks;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.ViewModels; using ModernKeePass.ViewModels;
using ModernKeePassLib; using ModernKeePassLib;
@@ -8,16 +9,34 @@ using Windows.Storage;
namespace ModernKeePassApp.Test.Mock namespace ModernKeePassApp.Test.Mock
{ {
public class DatabaseServiceMock : IDatabase public class DatabaseServiceMock : IDatabaseService
{ {
private bool _isOpen;
private bool _isClosed;
private CompositeKey _compositeKey;
public PwCompressionAlgorithm CompressionAlgorithm { get; set; } public PwCompressionAlgorithm CompressionAlgorithm { get; set; }
public StorageFile DatabaseFile { get; set; } public StorageFile DatabaseFile { get; set; }
public CompositeKey CompositeKey
{
get { return _compositeKey; }
set { _compositeKey = value; }
}
public PwUuid DataCipher { get; set; } public PwUuid DataCipher { get; set; }
public KdfParameters KeyDerivation { get; set; } public KdfParameters KeyDerivation { get; set; }
public bool IsOpen => _isOpen;
public bool IsFileOpen => DatabaseFile != null;
public bool IsClosed => _isClosed;
public bool HasChanged { get; set; }
public string Name => "MockDatabase"; public string Name => "MockDatabase";
public GroupVm RecycleBin { get; set; } public GroupVm RecycleBin { get; set; }
@@ -26,41 +45,48 @@ namespace ModernKeePassApp.Test.Mock
public GroupVm RootGroup { get; set; } public GroupVm RootGroup { get; set; }
public int Status { get; set; }
public void AddDeletedItem(PwUuid id) public void AddDeletedItem(PwUuid id)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Close() public Task Close(bool releaseFile = true)
{ {
Status = 0; return Task.Run(() =>
{
_isClosed = true;
_isOpen = false;
});
} }
public void CreateRecycleBin() public void CreateRecycleBin(string title)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Open(CompositeKey key, bool createNew) public Task Open(CompositeKey key, bool createNew = false)
{ {
Status = 2; _compositeKey = key;
return Task.Run(() =>
{
_isOpen = true;
_isClosed = false;
});
}
public async Task ReOpen()
{
await Open(_compositeKey);
} }
public void Save() public void Save()
{ {
throw new NotImplementedException(); // Do Nothing
} }
public void Save(StorageFile file) public void Save(StorageFile file)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void UpdateCompositeKey(CompositeKey key)
{
throw new NotImplementedException();
}
} }
} }

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Store;
using ModernKeePass.Interfaces;
namespace ModernKeePassApp.Test.Mock
{
public class LicenseServiceMock: ILicenseService
{
public IReadOnlyDictionary<string, ProductListing> Products { get; }
public LicenseServiceMock()
{
try
{
var proxyFile = Package.Current.InstalledLocation.GetFileAsync("data\\WindowsStoreProxy.xml").GetAwaiter().GetResult();
CurrentAppSimulator.ReloadSimulatorAsync(proxyFile).GetAwaiter().GetResult();
}
catch { }
var listing = CurrentAppSimulator.LoadListingInformationAsync().GetAwaiter().GetResult();
Products = listing.ProductListings;
}
public Task<int> Purchase(string addOn)
{
throw new System.NotImplementedException();
}
}
}

View File

@@ -6,7 +6,7 @@ using Windows.Storage;
namespace ModernKeePassApp.Test.Mock namespace ModernKeePassApp.Test.Mock
{ {
class RecentServiceMock : IRecent class RecentServiceMock : IRecentService
{ {
public int EntryCount => 0; public int EntryCount => 0;

View File

@@ -1,9 +1,8 @@
using System; using ModernKeePass.Interfaces;
using ModernKeePass.Interfaces;
namespace ModernKeePassApp.Test.Mock namespace ModernKeePassApp.Test.Mock
{ {
class ResourceServiceMock : IResource class ResourceServiceMock : IResourceService
{ {
public string GetResourceValue(string key) public string GetResourceValue(string key)
{ {

View File

@@ -3,11 +3,11 @@ using ModernKeePass.Interfaces;
namespace ModernKeePassApp.Test.Mock namespace ModernKeePassApp.Test.Mock
{ {
public class SettingsServiceMock : ISettings public class SettingsServiceMock : ISettingsService
{ {
public T GetSetting<T>(string property) public T GetSetting<T>(string property, T defaultValue = default(T))
{ {
return default(T); return defaultValue;
} }
public void PutSetting<T>(string property, T value) public void PutSetting<T>(string property, T value)

View File

@@ -121,6 +121,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="DatabaseTests.cs" /> <Compile Include="DatabaseTests.cs" />
<Compile Include="Mock\DatabaseServiceMock.cs" /> <Compile Include="Mock\DatabaseServiceMock.cs" />
<Compile Include="Mock\LicenseServiceMock.cs" />
<Compile Include="Mock\RecentServiceMock.cs" /> <Compile Include="Mock\RecentServiceMock.cs" />
<Compile Include="Mock\ResourceServiceMock.cs" /> <Compile Include="Mock\ResourceServiceMock.cs" />
<Compile Include="Mock\SettingsServiceMock.cs" /> <Compile Include="Mock\SettingsServiceMock.cs" />
@@ -131,11 +132,14 @@
<AppxManifest Include="Package.appxmanifest"> <AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</AppxManifest> </AppxManifest>
<Content Include="Databases\TestDatabase.kdbx" /> <Content Include="Data\TestDatabase.kdbx" />
<None Include="ModernKeePassApp.Test_TemporaryKey.pfx" /> <None Include="ModernKeePassApp.Test_TemporaryKey.pfx" />
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Data\WindowsStoreProxy.xml">
<SubType>Designer</SubType>
</Content>
<Content Include="Images\UnitTestLogo.scale-100.png"> <Content Include="Images\UnitTestLogo.scale-100.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>

View File

@@ -6,14 +6,15 @@ using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using ModernKeePass.ViewModels; using ModernKeePass.ViewModels;
using ModernKeePass.Views; using ModernKeePass.Views;
using ModernKeePassApp.Test.Mock; using ModernKeePassApp.Test.Mock;
using ModernKeePassLib;
namespace ModernKeePassApp.Test namespace ModernKeePassApp.Test
{ {
[TestClass] [TestClass]
public class ViewModelsTests public class ViewModelsTests
{ {
private RecentServiceMock _recent = new RecentServiceMock(); private readonly RecentServiceMock _recent = new RecentServiceMock();
private ResourceServiceMock _resource = new ResourceServiceMock(); private readonly ResourceServiceMock _resource = new ResourceServiceMock();
[TestMethod] [TestMethod]
public void TestAboutVm() public void TestAboutVm()
@@ -32,12 +33,13 @@ namespace ModernKeePassApp.Test
var firstGroup = mainVm.MainMenuItems.FirstOrDefault(); var firstGroup = mainVm.MainMenuItems.FirstOrDefault();
Assert.AreEqual(7, firstGroup.Count()); Assert.AreEqual(7, firstGroup.Count());
database.Status = 1; database.DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx")
.GetAwaiter().GetResult();
mainVm = new MainVm(null, null, database, _resource, _recent); mainVm = new MainVm(null, null, database, _resource, _recent);
Assert.IsNotNull(mainVm.SelectedItem); Assert.IsNotNull(mainVm.SelectedItem);
Assert.AreEqual(typeof(OpenDatabasePage), ((MainMenuItemVm) mainVm.SelectedItem).PageType); Assert.AreEqual(typeof(OpenDatabasePage), ((MainMenuItemVm) mainVm.SelectedItem).PageType);
database.Status = 2; database.Open(null, false).GetAwaiter().GetResult();
mainVm = new MainVm(null, null, database, _resource, _recent); mainVm = new MainVm(null, null, database, _resource, _recent);
Assert.IsNotNull(mainVm.SelectedItem); Assert.IsNotNull(mainVm.SelectedItem);
Assert.AreEqual(2, mainVm.MainMenuItems.Count()); Assert.AreEqual(2, mainVm.MainMenuItems.Count());
@@ -61,8 +63,7 @@ namespace ModernKeePassApp.Test
{ {
var database = new DatabaseServiceMock var database = new DatabaseServiceMock
{ {
Status = 1, DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx")
DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Databases\TestDatabase.kdbx")
.GetAwaiter().GetResult() .GetAwaiter().GetResult()
}; };
var openVm = new OpenVm(database); var openVm = new OpenVm(database);
@@ -79,7 +80,7 @@ namespace ModernKeePassApp.Test
public void TestRecentVm() public void TestRecentVm()
{ {
var mru = StorageApplicationPermissions.MostRecentlyUsedList; var mru = StorageApplicationPermissions.MostRecentlyUsedList;
mru.Add(Package.Current.InstalledLocation.GetFileAsync(@"Databases\TestDatabase.kdbx") mru.Add(Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx")
.GetAwaiter().GetResult(), "MockDatabase"); .GetAwaiter().GetResult(), "MockDatabase");
var recentVm = new RecentVm(); var recentVm = new RecentVm();
Assert.IsTrue(recentVm.RecentItems.Count == 1); Assert.IsTrue(recentVm.RecentItems.Count == 1);
@@ -88,10 +89,17 @@ namespace ModernKeePassApp.Test
mru.Clear(); mru.Clear();
} }
/*[TestMethod] [TestMethod]
public void TestSaveVm() public void TestSaveVm()
{ {
}*/ var database = new DatabaseServiceMock();
var saveVm = new SaveVm(database);
database.Open(null, false).GetAwaiter().GetResult();
saveVm.Save(false).GetAwaiter().GetResult();
Assert.IsTrue(database.IsOpen);
saveVm.Save().GetAwaiter().GetResult();
Assert.IsFalse(database.IsOpen);
}
[TestMethod] [TestMethod]
public void TestSettingsVm() public void TestSettingsVm()
@@ -100,10 +108,32 @@ namespace ModernKeePassApp.Test
Assert.AreEqual(1, settingsVm.MenuItems.Count()); Assert.AreEqual(1, settingsVm.MenuItems.Count());
var firstGroup = settingsVm.MenuItems.FirstOrDefault(); var firstGroup = settingsVm.MenuItems.FirstOrDefault();
// All groups have an empty title, so all settings are put inside the empty group // All groups have an empty title, so all settings are put inside the empty group
Assert.AreEqual(3, firstGroup.Count()); Assert.AreEqual(4, firstGroup.Count());
Assert.IsNotNull(settingsVm.SelectedItem); Assert.IsNotNull(settingsVm.SelectedItem);
var selectedItem = (ListMenuItemVm) settingsVm.SelectedItem; var selectedItem = (ListMenuItemVm) settingsVm.SelectedItem;
Assert.AreEqual(typeof(SettingsNewDatabasePage), selectedItem.PageType); Assert.AreEqual(typeof(SettingsNewDatabasePage), selectedItem.PageType);
} }
[TestMethod]
public void TestEntryVm()
{
var database = new DatabaseServiceMock();
var entryVm = new EntryVm(new PwEntry(true, true), new GroupVm(), database)
{
Name = "Test",
UserName = "login",
Password = "password"
};
}
[TestMethod]
public void TestGroupVm()
{
var database = new DatabaseServiceMock();
var entryVm = new GroupVm(new PwGroup(true, true), new GroupVm(), database)
{
Name = "Test"
};
}
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -69,7 +69,7 @@ namespace ModernKeePassLib.Cryptography.Cipher
} }
} }
public sealed class ChaCha20Stream : Stream internal sealed class ChaCha20Stream : Stream
{ {
private Stream m_sBase; private Stream m_sBase;
private readonly bool m_bWriting; private readonly bool m_bWriting;

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -22,9 +22,13 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
#if ModernKeePassLib || KeePassUAP
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Parameters;
#else
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Cryptography; using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Native; using ModernKeePassLib.Native;
@@ -134,7 +138,7 @@ namespace ModernKeePassLib.Cryptography.KeyDerivation
return null; return null;
} }
public static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32, internal static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
ulong uNumRounds) ulong uNumRounds)
{ {
#if ModernKeePassLib || KeePassUAP #if ModernKeePassLib || KeePassUAP

Some files were not shown because too many files have changed in this diff Show More