36 Commits
V1.9 ... V1.11

Author SHA1 Message Date
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
BONNEVILLE Geoffroy
13901d17ab Added FR store metadata 2017-12-12 14:59:56 +01:00
BONNEVILLE Geoffroy
19bc9005e9 Removed add-ons ordering to avoid Culture bugs - for now 2017-12-12 13:25:42 +01:00
BONNEVILLE Geoffroy
7c86a325d9 Solution metadata files correction 2017-12-12 11:56:25 +01:00
BONNEVILLE Geoffroy
d203b521c1 Small design changes
Create new database button now properly translated
Store metadata and screenshots updated
2017-12-12 11:54:15 +01:00
BONNEVILLE Geoffroy
0afa59fb66 Minor XAML changes 2017-12-11 19:13:28 +01:00
BONNEVILLE Geoffroy
470a08f4bc Entry page now wraps expiration date controls depending on page size
Group page now toggle filter box visibility depending on size, with a button and flyout on small size
Search text renamed to filter
2017-12-11 19:00:14 +01:00
BONNEVILLE Geoffroy
50c5940a0a Donations are sorted ascending 2017-12-11 11:08:33 +01:00
BONNEVILLE Geoffroy
35f64eec1b Implemented Donate page with wirking (?) in app purchases
Moved Pages to Views
2017-12-08 19:38:33 +01:00
BONNEVILLE Geoffroy
e25f9f4aae Create a LicenseService to handle in-app purchases 2017-12-07 18:49:03 +01:00
BONNEVILLE Geoffroy
a86dbf9dac New database settings version flyout now wraps text instead of showing scrollbar 2017-12-06 18:42:02 +01:00
BONNEVILLE Geoffroy
026bfcba78 New group text is now visible again in Group and Settings pages 2017-12-06 18:29:19 +01:00
BONNEVILLE Geoffroy
3b66824c58 Code cleanup 2017-12-04 18:08:42 +01:00
BONNEVILLE Geoffroy
898a9a0935 Search box now directly filters entries, no need for another drop down
TextBoxWithButton now scales button with text size and text can now be properly aligned
Corrected some French translations
2017-12-04 18:07:03 +01:00
BONNEVILLE Geoffroy
7aa342cf9c French translation added 2017-12-04 12:20:05 +01:00
BONNEVILLE Geoffroy
f173283a66 New clear all button in recent pages
First-time open of app correctly shows Welcome page
New Home button in group and entry pages
2017-12-04 10:46:01 +01:00
bg45
4f69b5cdcc Minor code refactor 2017-12-02 08:42:10 -05:00
BONNEVILLE Geoffroy
744858df81 Created a Settings Service
Created a Recent Service
Created a Resources Service
Code refactor
Updated tests
2017-12-01 17:59:38 +01:00
BONNEVILLE Geoffroy
f172e31250 Settings are now disabled instead of not present when database is closed 2017-12-01 09:31:59 +01:00
BONNEVILLE Geoffroy
7530cf8006 Groups sorting now works with new Lib
New database settings improved
2017-11-30 18:56:56 +01:00
BONNEVILLE Geoffroy
33223934e3 Sorting now works for entries (not yet for groups) 2017-11-30 11:05:47 +01:00
BONNEVILLE Geoffroy
f2731c49dd Drag drop finally works
WIP item background
New Donate page stub
Renamed some classes as services
2017-11-29 19:13:38 +01:00
bg45
227bc30dde New database setting page
(Database) Settings available from Main menu
2017-11-28 16:57:16 -05:00
BONNEVILLE Geoffroy
7b39fe79c8 WIP strings in ressource file (XAML is done, code-behind is not)
Invalid URI now shows a message
Create entry is now a link above entries
Entries gridview allows reordering (WIP enable it conditionnally)
Code cleanup in adding/removing PwEntries from VM
2017-11-28 18:53:10 +01:00
BONNEVILLE Geoffroy
fcbda1e33d Adds some VM tests
New tooltip in Textbox with button control
New welcome page in Settings (shown when noting is selected)
Settings are now grouped
2017-11-27 15:26:36 +01:00
BONNEVILLE Geoffroy
42ac04b02c Layout change in CompositeKey user control
Button text is now settable
Opening database is placed in async task dispatcher to return control to the UI
2017-11-24 18:21:06 +01:00
BONNEVILLE Geoffroy
7cd05cb1d8 Better global exception handling
Save error now shows a Save as button
Recent list now not changed at every access (only on actual file open)
Some code refactoring
2017-11-24 12:17:41 +01:00
BONNEVILLE Geoffroy
1b2d25e171 Error messages are now caught at the app level (see if it's a good solution in the long term)
Redesign of the create key file button
2017-11-23 19:02:49 +01:00
BONNEVILLE Geoffroy
675a718107 Update test project certificate 2017-11-23 15:32:44 +01:00
BONNEVILLE Geoffroy
a8f5897364 Changed VMs references to database singleton
Added some unit tests (WIP)
2017-11-23 15:26:57 +01:00
BONNEVILLE Geoffroy
5120c8177b Adding tests for App
WIP make VMs app agnostic
2017-11-22 18:54:03 +01:00
BONNEVILLE Geoffroy
f2d97b4e7e Bump to 1.10
Bug when pressing enter with invalid composite key
2017-11-21 18:45:42 +01:00
BONNEVILLE Geoffroy
197e061bc5 Update release notes 2017-11-20 18:09:20 +01:00
BONNEVILLE Geoffroy
d1a6b418d5 Added store metadata information 2017-11-20 18:08:19 +01:00
144 changed files with 4279 additions and 1044 deletions

View File

@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePassLib.Test", "Mo
EndProject
Project("{F5034706-568F-408A-B7B3-4D38C6DB8A32}") = "Scripts", "Scripts\Scripts.pssproj", "{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePassApp.Test", "ModernKeePassApp.Test\ModernKeePassApp.Test.csproj", "{7E80F5E7-724A-4668-9333-B10F5D75C6D0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -103,6 +105,30 @@ Global
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|x64.Build.0 = Release|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|x86.ActiveCfg = Release|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|x86.Build.0 = Release|Any CPU
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|ARM.ActiveCfg = Debug|ARM
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|ARM.Build.0 = Debug|ARM
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|ARM.Deploy.0 = Debug|ARM
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|x64.ActiveCfg = Debug|x64
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|x64.Build.0 = Debug|x64
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|x64.Deploy.0 = Debug|x64
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|x86.ActiveCfg = Debug|x86
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|x86.Build.0 = Debug|x86
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|x86.Deploy.0 = Debug|x86
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|Any CPU.Build.0 = Release|Any CPU
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|Any CPU.Deploy.0 = Release|Any CPU
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|ARM.ActiveCfg = Release|ARM
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|ARM.Build.0 = Release|ARM
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|ARM.Deploy.0 = Release|ARM
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|x64.ActiveCfg = Release|x64
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|x64.Build.0 = Release|x64
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|x64.Deploy.0 = Release|x64
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|x86.ActiveCfg = Release|x86
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|x86.Build.0 = Release|x86
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -10,6 +10,7 @@
<ResourceDictionary Source="Styles/HamburgerButtonStyle.xaml" />
<ResourceDictionary Source="Styles/ListViewLeftIndicatorStyle.xaml" />
<ResourceDictionary Source="Styles/NoBorderButtonStyle.xaml" />
<ResourceDictionary Source="Styles/TextBlockStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

View File

@@ -1,14 +1,15 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Data.Json;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.Exceptions;
using ModernKeePass.Services;
using ModernKeePass.Views;
// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227
@@ -19,7 +20,7 @@ namespace ModernKeePass
/// </summary>
sealed partial class App
{
public DatabaseHelper Database { get; set; } = new DatabaseHelper();
public DatabaseService Database { get; private set; }
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
@@ -29,9 +30,27 @@ namespace ModernKeePass
{
InitializeComponent();
Suspending += OnSuspending;
UnhandledException += OnUnhandledException;
Database = new DatabaseService();
}
#region Event Handlers
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
// Save the argument exception because it's cleared on first access
var exception = unhandledExceptionEventArgs.Exception;
var realException =
exception is TargetInvocationException &&
exception.InnerException != null
? exception.InnerException
: exception;
if (!(realException is SaveException)) return;
unhandledExceptionEventArgs.Handled = true;
MessageDialogHelper.SaveErrorDialog(realException as SaveException, Database);
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
@@ -86,7 +105,7 @@ namespace ModernKeePass
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(Pages.MainPage), lauchActivatedEventArgs.Arguments);
rootFrame.Navigate(typeof(MainPage), lauchActivatedEventArgs.Arguments);
}
/*else
{
@@ -126,6 +145,7 @@ namespace ModernKeePass
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
UnhandledException -= OnUnhandledException;
Database.Save();
deferral.Complete();
}
@@ -139,10 +159,11 @@ namespace ModernKeePass
base.OnFileActivated(args);
var rootFrame = new Frame();
Database.DatabaseFile = args.Files[0] as StorageFile;
rootFrame.Navigate(typeof(Pages.MainPage), args);
rootFrame.Navigate(typeof(MainPage), args);
Window.Current.Content = rootFrame;
Window.Current.Activate();
}
#endregion
}
}

View File

@@ -1,163 +0,0 @@
using System;
using Windows.Storage;
using Windows.UI.Xaml.Controls;
using ModernKeePass.ViewModels;
using ModernKeePassLib;
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Keys;
using ModernKeePassLib.Serialization;
namespace ModernKeePass.Common
{
public class DatabaseHelper
{
public enum DatabaseStatus
{
Error = -3,
NoCompositeKey = -2,
CompositeKeyError = -1,
Closed = 0,
Opening = 1,
Opened = 2
}
private readonly PwDatabase _pwDatabase = new PwDatabase();
private StorageFile _databaseFile;
private GroupVm _recycleBin;
public GroupVm RootGroup { get; set; }
public GroupVm RecycleBin
{
get { return _recycleBin; }
set
{
_recycleBin = value;
_pwDatabase.RecycleBinUuid = _recycleBin.IdUuid;
}
}
public DatabaseStatus Status { get; private set; } = DatabaseStatus.Closed;
public string Name => DatabaseFile?.Name;
public bool RecycleBinEnabled
{
get { return _pwDatabase.RecycleBinEnabled; }
set { _pwDatabase.RecycleBinEnabled = value; }
}
public StorageFile DatabaseFile
{
get { return _databaseFile; }
set
{
_databaseFile = value;
Status = DatabaseStatus.Opening;
}
}
public PwUuid DataCipher
{
get { return _pwDatabase.DataCipherUuid; }
set { _pwDatabase.DataCipherUuid = value; }
}
public PwCompressionAlgorithm CompressionAlgorithm
{
get { return _pwDatabase.Compression; }
set { _pwDatabase.Compression = value; }
}
public KdfParameters KeyDerivation
{
get { return _pwDatabase.KdfParameters; }
set { _pwDatabase.KdfParameters = value; }
}
/// <summary>
/// Open a KeePass database
/// </summary>
/// <param name="key">The database composite key</param>
/// <param name="createNew">True to create a new database before opening it</param>
/// <returns>An error message, if any</returns>
public void Open(CompositeKey key, bool createNew = false)
{
try
{
if (key == null) Status = DatabaseStatus.NoCompositeKey;
var ioConnection = IOConnectionInfo.FromFile(DatabaseFile);
if (createNew) _pwDatabase.New(ioConnection, key);
else _pwDatabase.Open(ioConnection, key, new NullStatusLogger());
if (_pwDatabase.IsOpen)
{
Status = DatabaseStatus.Opened;
RootGroup = new GroupVm(_pwDatabase.RootGroup, null, RecycleBinEnabled ? _pwDatabase.RecycleBinUuid : null);
}
}
catch (InvalidCompositeKeyException)
{
Status = DatabaseStatus.CompositeKeyError;
}
catch (Exception ex)
{
Status = DatabaseStatus.Error;
}
}
/// <summary>
/// Save the current database to another file and open it
/// </summary>
/// <param name="file">The new database file</param>
internal void Save(StorageFile file)
{
DatabaseFile = file;
_pwDatabase.SaveAs(IOConnectionInfo.FromFile(DatabaseFile), true, new NullStatusLogger());
Status = DatabaseStatus.Opened;
}
/// <summary>
/// Commit the changes to the currently opened database to file
/// </summary>
public bool Save()
{
if (_pwDatabase == null || !_pwDatabase.IsOpen) return false;
try
{
_pwDatabase.Save(new NullStatusLogger());
return true;
}
catch (Exception ex)
{
MessageDialogHelper.ShowErrorDialog(ex);
}
return false;
}
/// <summary>
/// Close the currently opened database
/// </summary>
public void Close()
{
_pwDatabase?.Close();
Status = DatabaseStatus.Closed;
}
public void AddDeletedItem(PwUuid id)
{
_pwDatabase.DeletedObjects.Add(new PwDeletedObject(id, DateTime.UtcNow));
}
public void CreateRecycleBin()
{
RecycleBin = RootGroup.AddNewGroup("Recycle bin");
RecycleBin.IsSelected = true;
RecycleBin.IconSymbol = Symbol.Delete;
}
public void UpdateCompositeKey(CompositeKey key)
{
_pwDatabase.MasterKey = key;
}
}
}

View File

@@ -1,18 +1,68 @@
using System;
using System.Collections.Generic;
using Windows.Storage.Pickers;
using Windows.UI.Popups;
using Windows.UI.Xaml.Media.Animation;
using ModernKeePass.Exceptions;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Common
{
public static class MessageDialogHelper
{
public static async void ShowDeleteConfirmationDialog(string actionText, string contentText, UICommandInvokedHandler action)
public static async void ShowActionDialog(string title, string contentText, string actionButtonText, string cancelButtonText, UICommandInvokedHandler action)
{
// Create the message dialog and set its content
var messageDialog = new MessageDialog(contentText);
var messageDialog = CreateBasicDialog(title, contentText, cancelButtonText);
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
messageDialog.Commands.Add(new UICommand(actionText, action));
messageDialog.Commands.Add(new UICommand("Cancel"));
messageDialog.Commands.Add(new UICommand(actionButtonText, action));
// Show the message dialog
await messageDialog.ShowAsync();
}
public static void SaveErrorDialog(SaveException exception, IDatabase database)
{
ShowActionDialog("Save error", exception.InnerException.Message, "Save as", "Discard", async command =>
{
var savePicker = new FileSavePicker
{
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
SuggestedFileName = $"{database.DatabaseFile.DisplayName} - copy"
};
savePicker.FileTypeChoices.Add("KeePass 2.x database", new List<string> { ".kdbx" });
var file = await savePicker.PickSaveFileAsync();
if (file != null) database.Save(file);
});
}
public static async void ShowErrorDialog(Exception exception)
{
if (exception == null) return;
// Create the message dialog and set its content
var messageDialog = CreateBasicDialog("Error occured", exception.Message, "OK");
// Show the message dialog
await messageDialog.ShowAsync();
}
public static async void ShowNotificationDialog(string title, string message)
{
var dialog = CreateBasicDialog(title, message, "OK");
// Show the message dialog
await dialog.ShowAsync();
}
private static MessageDialog CreateBasicDialog(string title, string message, string dismissActionText)
{
// Create the message dialog and set its content
var messageDialog = new MessageDialog(message, title);
// Add commands and set their callbacks;
messageDialog.Commands.Add(new UICommand(dismissActionText));
// Set the command that will be invoked by default
messageDialog.DefaultCommandIndex = 1;
@@ -20,20 +70,7 @@ namespace ModernKeePass.Common
// Set the command to be invoked when escape is pressed
messageDialog.CancelCommandIndex = 1;
// Show the message dialog
await messageDialog.ShowAsync();
}
public static async void ShowErrorDialog(Exception exception)
{
// Create the message dialog and set its content
var messageDialog = new MessageDialog(exception.Message, "Error occured");
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
messageDialog.Commands.Add(new UICommand("OK"));
// Show the message dialog
await messageDialog.ShowAsync();
return messageDialog;
}
}
}

View File

@@ -32,5 +32,18 @@ namespace ModernKeePass.Common
ToastNotificationManager.CreateToastNotifier().Show(toast);
}
public static void ShowGenericToast(string title, string text)
{
var notificationXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var toastElements = notificationXml.GetElementsByTagName("text");
toastElements[0].AppendChild(notificationXml.CreateTextNode(title));
toastElements[1].AppendChild(notificationXml.CreateTextNode(text));
var toast = new ToastNotification(notificationXml)
{
ExpirationTime = DateTime.Now.AddSeconds(5)
};
ToastNotificationManager.CreateToastNotifier().Show(toast);
}
}
}

View File

@@ -6,18 +6,6 @@ namespace ModernKeePass.Controls
{
public class TextBoxWithButton : TextBox
{
/*public Symbol ButtonSymbol
{
get { return (Symbol)GetValue(ButtonSymbolProperty); }
set { SetValue(ButtonSymbolProperty, value); }
}
public static readonly DependencyProperty ButtonSymbolProperty =
DependencyProperty.Register(
"ButtonSymbol",
typeof(Symbol),
typeof(TextBoxWithButton),
new PropertyMetadata(Symbol.Delete, (o, args) => { }));*/
public string ButtonSymbol
{
get { return (string)GetValue(ButtonSymbolProperty); }
@@ -31,6 +19,31 @@ namespace ModernKeePass.Controls
new PropertyMetadata("&#xE107;", (o, args) => { }));
public event EventHandler<RoutedEventArgs> ButtonClick;
public string ButtonTooltip
{
get { return (string)GetValue(ButtonTooltipProperty); }
set { SetValue(ButtonTooltipProperty, value); }
}
public static readonly DependencyProperty ButtonTooltipProperty =
DependencyProperty.Register(
"ButtonTooltip",
typeof(string),
typeof(TextBoxWithButton),
new PropertyMetadata(string.Empty, (o, args) => { }));
public bool IsButtonEnabled
{
get { return (bool)GetValue(IsButtonEnabledProperty); }
set { SetValue(IsButtonEnabledProperty, value); }
}
public static readonly DependencyProperty IsButtonEnabledProperty =
DependencyProperty.Register(
"IsButtonEnabled",
typeof(bool),
typeof(TextBoxWithButton),
new PropertyMetadata(true, (o, args) => { }));
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();

View File

@@ -0,0 +1,20 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
namespace ModernKeePass.Converters
{
class EmptyStringToVisibilityConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var text = value is string ? value.ToString() : string.Empty;
return string.IsNullOrEmpty(text) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8" ?>
<CurrentApp>
<ListingInformation>
<App>
<AppId>0719A91A-C322-4EE0-A257-E60733EECF06</AppId>
<LinkUri>https://www.microsoft.com/store/apps/9mwq48zk8nhv</LinkUri>
<CurrentMarket>en-us</CurrentMarket>
<AgeRating>3</AgeRating>
<MarketData xml:lang="en-us">
<Name>App with several in-app products</Name>
<Description>Sample app for demonstrating an expiring in-app product and a consumable in-app product</Description>
<Price>5.99</Price>
<CurrencySymbol>$</CurrencySymbol>
</MarketData>
</App>
<Product ProductId="donate1" LicenseDuration="0" ProductType="Consumable">
<MarketData xml:lang="en-us">
<Name>Small Donation</Name>
<Price>0.99</Price>
<CurrencySymbol>$</CurrencySymbol>
</MarketData>
</Product>
<Product ProductId="donate5" LicenseDuration="0" ProductType="Consumable">
<MarketData xml:lang="en-us">
<Name>Medium Donation</Name>
<Price>4.99</Price>
<CurrencySymbol>$</CurrencySymbol>
</MarketData>
</Product>
<Product ProductId="donate10" LicenseDuration="0" ProductType="Consumable">
<MarketData xml:lang="en-us">
<Name>Large Donation</Name>
<Price>9.99</Price>
<CurrencySymbol>$</CurrencySymbol>
</MarketData>
</Product>
<Product ProductId="donate20" LicenseDuration="0" ProductType="Consumable">
<MarketData xml:lang="en-us">
<Name>Generous Donation</Name>
<Price>19.99</Price>
<CurrencySymbol>$</CurrencySymbol>
</MarketData>
</Product>
</ListingInformation>
<LicenseInformation>
<App>
<IsActive>true</IsActive>
<IsTrial>false</IsTrial>
</App>
</LicenseInformation>
<ConsumableInformation>
<Product ProductId="donate1" TransactionId="00000001-0000-0000-0000-000000000000" Status="Active"/>
<Product ProductId="donate5" TransactionId="00000001-0000-0000-0000-000000000000" Status="Active"/>
<Product ProductId="donate10" TransactionId="00000001-0000-0000-0000-000000000000" Status="Active"/>
<Product ProductId="donate20" TransactionId="00000001-0000-0000-0000-000000000000" Status="Active"/>
</ConsumableInformation>
</CurrentApp>

View File

@@ -0,0 +1,14 @@
using System;
namespace ModernKeePass.Exceptions
{
public class SaveException : Exception
{
public new Exception InnerException { get; }
public SaveException(Exception exception)
{
InnerException = exception;
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Threading.Tasks;
using Windows.UI.Core;
namespace ModernKeePass.Extensions
{
public static class DispatcherTaskExtensions
{
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher dispatcher,
Func<Task<T>> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal)
{
var taskCompletionSource = new TaskCompletionSource<T>();
await dispatcher.RunAsync(priority, async () =>
{
try
{
taskCompletionSource.SetResult(await func());
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
return await taskCompletionSource.Task;
}
// There is no TaskCompletionSource<void> so we use a bool that we throw away.
public static async Task RunTaskAsync(this CoreDispatcher dispatcher,
Func<Task> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal) =>
await RunTaskAsync(dispatcher, async () => { await func(); return false; }, priority);
}
}

View File

@@ -0,0 +1,29 @@
using Windows.Storage;
using ModernKeePass.ViewModels;
using ModernKeePassLib;
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Keys;
namespace ModernKeePass.Interfaces
{
public interface IDatabase
{
string Name { get; }
bool RecycleBinEnabled { get; set; }
int Status { get; set; }
GroupVm RootGroup { get; set; }
GroupVm RecycleBin { get; set; }
StorageFile DatabaseFile { get; set; }
PwUuid DataCipher { get; set; }
PwCompressionAlgorithm CompressionAlgorithm { get; set; }
KdfParameters KeyDerivation { get; set; }
void Open(CompositeKey key, bool createNew);
void UpdateCompositeKey(CompositeKey key);
void Save();
void Save(StorageFile file);
void CreateRecycleBin();
void AddDeletedItem(PwUuid id);
void Close();
}
}

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel.Store;
namespace ModernKeePass.Interfaces
{
public interface ILicenseService
{
IReadOnlyDictionary<string, ProductListing> Products { get; }
Task<int> Purchase(string addOn);
}
}

View File

@@ -12,6 +12,7 @@ namespace ModernKeePass.Interfaces
string Name { get; set; }
string Path { get; }
bool IsEditMode { get; }
bool IsRecycleOnDelete { get; }
/// <summary>
/// Move a entity to the destination group

View File

@@ -0,0 +1,15 @@
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Windows.Storage;
namespace ModernKeePass.Interfaces
{
public interface IRecent
{
int EntryCount { get; }
Task<IStorageItem> GetFileAsync(string token);
ObservableCollection<IRecentItem> GetAllFiles(bool removeIfNonExistant = true);
void Add(IStorageItem file, string metadata);
void ClearAll();
}
}

View File

@@ -0,0 +1,11 @@
using Windows.Storage;
namespace ModernKeePass.Interfaces
{
public interface IRecentItem
{
StorageFile DatabaseFile { get; }
string Token { get; }
string Name { get; }
}
}

View File

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

View File

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

View File

@@ -113,28 +113,51 @@
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Common\DatabaseHelper.cs" />
<Compile Include="Interfaces\ILicenseService.cs" />
<Compile Include="Interfaces\IRecent.cs" />
<Compile Include="Interfaces\IRecentItem.cs" />
<Compile Include="Interfaces\IResource.cs" />
<Compile Include="ViewModels\DonateVm.cs" />
<Compile Include="Views\MainPageFrames\DonatePage.xaml.cs">
<DependentUpon>DonatePage.xaml</DependentUpon>
</Compile>
<Compile Include="Services\DatabaseService.cs" />
<Compile Include="Interfaces\ISettings.cs" />
<Compile Include="Common\MessageDialogHelper.cs" />
<Compile Include="Common\NavigationHelper.cs" />
<Compile Include="Common\NotifyPropertyChangedBase.cs" />
<Compile Include="Common\ObservableDictionary.cs" />
<Compile Include="Common\RelayCommand.cs" />
<Compile Include="Common\SuspensionManager.cs" />
<Compile Include="Services\LicenseService.cs" />
<Compile Include="Services\RecentService.cs" />
<Compile Include="Services\ResourcesService.cs" />
<Compile Include="Services\SettingsService.cs" />
<Compile Include="Common\ToastNotificationHelper.cs" />
<Compile Include="Converters\DiscreteIntToSolidColorBrushConverter.cs" />
<Compile Include="Converters\EmptyStringToVisibilityConverter.cs" />
<Compile Include="Converters\NullToBooleanConverter.cs" />
<Compile Include="Exceptions\SaveException.cs" />
<Compile Include="Extensions\DispatcherTaskExtensions.cs" />
<Compile Include="Interfaces\IDatabase.cs" />
<Compile Include="Interfaces\IHasSelectableObject.cs" />
<Compile Include="Interfaces\ISelectableModel.cs" />
<Compile Include="Pages\BasePages\LayoutAwarePageBase.cs" />
<Compile Include="Pages\SettingsPageFrames\SettingsDatabasePage.xaml.cs">
<Compile Include="Views\BasePages\LayoutAwarePageBase.cs" />
<Compile Include="Views\SettingsPageFrames\SettingsDatabasePage.xaml.cs">
<DependentUpon>SettingsDatabasePage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\SettingsPageFrames\SettingsSecurityPage.xaml.cs">
<Compile Include="Views\SettingsPageFrames\SettingsNewDatabasePage.xaml.cs">
<DependentUpon>SettingsNewDatabasePage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPageFrames\SettingsSecurityPage.xaml.cs">
<DependentUpon>SettingsSecurityPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPageFrames\SettingsWelcomePage.xaml.cs">
<DependentUpon>SettingsWelcomePage.xaml</DependentUpon>
</Compile>
<Compile Include="TemplateSelectors\FirstItemDataTemplateSelector.cs" />
<Compile Include="Controls\ListViewWithDisable.cs" />
<Compile Include="Controls\CompositeKeyUserControl.xaml.cs">
<Compile Include="Views\UserControls\CompositeKeyUserControl.xaml.cs">
<DependentUpon>CompositeKeyUserControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TextBoxWithButton.cs" />
@@ -149,20 +172,20 @@
<Compile Include="Events\PasswordEventArgs.cs" />
<Compile Include="Interfaces\IIsEnabled.cs" />
<Compile Include="Interfaces\IPwEntity.cs" />
<Compile Include="Pages\MainPage.xaml.cs">
<Compile Include="Views\MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
<Compile Include="Mappings\PwIconToSegoeMapping.cs" />
<Compile Include="Pages\MainPageFrames\AboutPage.xaml.cs">
<Compile Include="Views\MainPageFrames\AboutPage.xaml.cs">
<DependentUpon>AboutPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\MainPageFrames\NewDatabasePage.xaml.cs">
<Compile Include="Views\MainPageFrames\NewDatabasePage.xaml.cs">
<DependentUpon>NewDatabasePage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\SettingsPage.xaml.cs">
<Compile Include="Views\SettingsPage.xaml.cs">
<DependentUpon>SettingsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\MainPageFrames\WelcomePage.xaml.cs">
<Compile Include="Views\MainPageFrames\WelcomePage.xaml.cs">
<DependentUpon>WelcomePage.xaml</DependentUpon>
</Compile>
<Compile Include="ViewModels\AboutVm.cs" />
@@ -170,24 +193,25 @@
<Compile Include="ViewModels\Items\ListMenuItemVm.cs" />
<Compile Include="ViewModels\Items\MainMenuItemVm.cs" />
<Compile Include="ViewModels\Items\RecentItemVm.cs" />
<Compile Include="Pages\EntryDetailPage.xaml.cs">
<Compile Include="Views\EntryDetailPage.xaml.cs">
<DependentUpon>EntryDetailPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\GroupDetailPage.xaml.cs">
<Compile Include="Views\GroupDetailPage.xaml.cs">
<DependentUpon>GroupDetailPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\MainPageFrames\OpenDatabasePage.xaml.cs">
<Compile Include="Views\MainPageFrames\OpenDatabasePage.xaml.cs">
<DependentUpon>OpenDatabasePage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\MainPageFrames\RecentDatabasesPage.xaml.cs">
<Compile Include="Views\MainPageFrames\RecentDatabasesPage.xaml.cs">
<DependentUpon>RecentDatabasesPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\MainPageFrames\SaveDatabasePage.xaml.cs">
<Compile Include="Views\MainPageFrames\SaveDatabasePage.xaml.cs">
<DependentUpon>SaveDatabasePage.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ViewModels\EntryVm.cs" />
<Compile Include="ViewModels\GroupVm.cs" />
<Compile Include="ViewModels\Items\SettingsNewVm.cs" />
<Compile Include="ViewModels\SettingsVm.cs" />
<Compile Include="ViewModels\MainVm.cs" />
<Compile Include="ViewModels\NewVm.cs" />
@@ -203,6 +227,10 @@
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<PRIResource Include="Strings\fr-FR\Resources.resw" />
<PRIResource Include="Strings\fr-FR\CodeBehind.resw" />
<PRIResource Include="Strings\en-US\CodeBehind.resw" />
<PRIResource Include="Strings\en-US\Resources.resw" />
</ItemGroup>
<ItemGroup>
<None Include="Package.StoreAssociation.xml" />
@@ -212,55 +240,67 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="Controls\CompositeKeyUserControl.xaml">
<Page Include="Views\UserControls\CompositeKeyUserControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\MainPage.xaml">
<Page Include="Views\MainPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Pages\MainPageFrames\AboutPage.xaml">
<Page Include="Views\MainPageFrames\AboutPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\EntryDetailPage.xaml">
<Page Include="Views\EntryDetailPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\GroupDetailPage.xaml">
<Page Include="Views\GroupDetailPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\MainPageFrames\NewDatabasePage.xaml">
<Page Include="Views\MainPageFrames\DonatePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\MainPageFrames\OpenDatabasePage.xaml">
<Page Include="Views\MainPageFrames\NewDatabasePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\MainPageFrames\RecentDatabasesPage.xaml">
<Page Include="Views\MainPageFrames\OpenDatabasePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\MainPageFrames\SaveDatabasePage.xaml">
<Page Include="Views\MainPageFrames\RecentDatabasesPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\SettingsPage.xaml">
<Page Include="Views\MainPageFrames\SaveDatabasePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\MainPageFrames\WelcomePage.xaml">
<Page Include="Views\SettingsPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\SettingsPageFrames\SettingsDatabasePage.xaml">
<Page Include="Views\MainPageFrames\WelcomePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\SettingsPageFrames\SettingsSecurityPage.xaml">
<Page Include="Views\SettingsPageFrames\SettingsDatabasePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPageFrames\SettingsNewDatabasePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPageFrames\SettingsSecurityPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPageFrames\SettingsWelcomePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
@@ -279,6 +319,11 @@
<Generator>MSBuild:Compile</Generator>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Page>
<Page Include="Styles\TextBlockStyles.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Page>
<Page Include="Styles\TextBoxWithButtonStyle.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -295,7 +340,7 @@
<Private>True</Private>
</Reference>
<Reference Include="ModernKeePassLib, Version=2.37.0.2000, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ModernKeePassLib.2.37.7000\lib\netstandard1.2\ModernKeePassLib.dll</HintPath>
<HintPath>..\packages\ModernKeePassLib.2.37.8000\lib\netstandard1.2\ModernKeePassLib.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
@@ -316,8 +361,49 @@
<Name>BehaviorsXamlSDKManaged</Name>
</SDKReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Content Include="appMetadata\en-us\baselisting\description.txt" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\description.Entry.txt" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\description.Filter.txt" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\description.Group.txt" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\description.New.txt" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\description.Open.txt" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\description.Recent.txt" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\description.Semantic.txt" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\description.Settings.txt" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\Entry.png" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\Filter.png" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\Group.png" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\New.png" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\Open.png" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\Recent.png" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\Semantic.png" />
<Content Include="appMetadata\en-us\baselisting\images\Screenshot\Settings.png" />
<Content Include="appMetadata\en-us\baselisting\keywords.txt" />
<Content Include="appMetadata\en-us\baselisting\privacyPolicy.txt" />
<Content Include="appMetadata\en-us\baselisting\releaseNotes.txt" />
<Content Include="appMetadata\en-us\baselisting\websiteUrl.txt" />
<Content Include="appMetadata\fr-fr\baselisting\description.txt" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\description.Entry.txt" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\description.Filter.txt" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\description.Group.txt" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\description.New.txt" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\description.Open.txt" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\description.Recent.txt" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\description.Semantic.txt" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\description.Settings.txt" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\Entry.png" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\Filter.png" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\Group.png" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\New.png" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\Open.png" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\Recent.png" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\Semantic.png" />
<Content Include="appMetadata\fr-fr\baselisting\images\Screenshot\Settings.png" />
<Content Include="appMetadata\fr-fr\baselisting\keywords.txt" />
<Content Include="appMetadata\fr-fr\baselisting\privacyPolicy.txt" />
<Content Include="appMetadata\fr-fr\baselisting\releaseNotes.txt" />
<Content Include="appMetadata\fr-fr\baselisting\websiteUrl.txt" />
<Content Include="Assets\Logo.scale-100.png" />
<Content Include="Assets\Logo.scale-140.png" />
<Content Include="Assets\Logo.scale-180.png" />
@@ -348,6 +434,9 @@
<Content Include="Assets\Wide310x150Logo.scale-140.png" />
<Content Include="Assets\Wide310x150Logo.scale-180.png" />
<Content Include="Assets\Wide310x150Logo.scale-80.png" />
<Content Include="Data\WindowsStoreProxy.xml">
<SubType>Designer</SubType>
</Content>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '12.0' ">
<VisualStudioVersion>12.0</VisualStudioVersion>

View File

@@ -1,6 +1,6 @@
<?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">
<Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.9.0.30" />
<Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.11.0.31" />
<Properties>
<DisplayName>ModernKeePass</DisplayName>
<PublisherDisplayName>wismna</PublisherDisplayName>

View File

@@ -1,304 +0,0 @@
<Page
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"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:ModernKeePass.Actions"
xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors"
x:Name="PageRoot"
x:Class="ModernKeePass.Pages.GroupDetailPage"
mc:Ignorable="d" >
<Page.Resources>
<SolidColorBrush x:Key="Transparent" Color="Transparent"/>
<SolidColorBrush x:Key="SystemColor" Color="{StaticResource SystemColorButtonFaceColor}" />
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<converters:BooleanToFontStyleConverter x:Key="BooleanToFontStyleConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:NullToBooleanConverter x:Key="NullToBooleanConverter"/>
</Page.Resources>
<Page.DataContext>
<viewModels:GroupVm />
</Page.DataContext>
<Page.BottomAppBar>
<CommandBar x:Name="CommandBar" VerticalAlignment="Center">
<CommandBar.SecondaryCommands>
<AppBarButton Icon="Save" Label="Save">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction TargetObject="{Binding}" MethodName="Save"/>
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Setting" Label="Settings">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Pages.SettingsPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
</CommandBar.SecondaryCommands>
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarToggleButton>
<AppBarButton Icon="Undo" Label="Restore" Visibility="{Binding ShowRestore, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding PreviousGroup, Converter={StaticResource NullToBooleanConverter}}" Click="RestoreButton_Click">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction MethodName="UndoDelete" TargetObject="{Binding}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Delete" Label="Delete" IsEnabled="{Binding IsNotRoot}" Click="DeleteButton_Click" />
</CommandBar>
</Page.BottomAppBar>
<Grid>
<Grid.Resources>
<CollectionViewSource
x:Name="GroupsViewSource"
Source="{Binding Groups}" />
<CollectionViewSource
x:Name="EntriesViewSource"
Source="{Binding Entries}" />
<CollectionViewSource
x:Name="EntriesZoomedOutViewSource"
Source="{Binding EntriesZoomedOut}" IsSourceGrouped="True"/>
</Grid.Resources>
<Grid.Background>
<StaticResource ResourceKey="ApplicationPageBackgroundThemeBrush"/>
</Grid.Background>
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListView
Grid.Column="0"
x:Name="LeftListView"
Margin="0,0,0,-10"
SelectionChanged="groups_SelectionChanged"
IsSwipeEnabled="false"
IsSynchronizedWithCurrentItem="False"
DataContext="{Binding DataContext, ElementName=PageRoot}"
RequestedTheme="Dark"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}">
<ListView.Resources>
<DataTemplate x:Name="Collapsed">
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding Name}" />
</ToolTipService.ToolTip>
</SymbolIcon>
</DataTemplate>
<DataTemplate x:Name="Expanded">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0" />
<TextBlock Text="{Binding Name}" FontWeight="{Binding FontWeight}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" FontStyle="{Binding IsSelected, Converter={StaticResource BooleanToFontStyleConverter}}" />
</StackPanel>
</DataTemplate>
</ListView.Resources>
<ListView.ItemsSource>
<Binding Source="{StaticResource GroupsViewSource}"/>
</ListView.ItemsSource>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<!--<Setter Property="HorizontalContentAlignment" Value="Center" />-->
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.HeaderTemplate>
<DataTemplate>
<ToggleButton Style="{StaticResource HamburgerToggleButton}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Loaded">
<core:ChangePropertyAction PropertyName="ItemTemplate" Value="{StaticResource Collapsed}" TargetObject="{Binding ElementName=LeftListView}"/>
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Checked">
<core:ChangePropertyAction PropertyName="ItemTemplate" Value="{StaticResource Expanded}" TargetObject="{Binding ElementName=LeftListView}"/>
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<core:ChangePropertyAction PropertyName="ItemTemplate" Value="{StaticResource Collapsed}" TargetObject="{Binding ElementName=LeftListView}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ToggleButton>
</DataTemplate>
</ListView.HeaderTemplate>
</ListView>
<!-- Horizontal scrolling grid -->
<SemanticZoom Grid.Column="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" Margin="0,30,0,0">
<SemanticZoom.ZoomedInView>
<GridView
x:Name="GridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Entries"
TabIndex="1"
SelectionChanged="entries_SelectionChanged"
IsSynchronizedWithCurrentItem="False">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="False">
<actions:SetupFocusAction TargetObject="{Binding ElementName=GridView}" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
<GridView.Resources>
<DataTemplate x:Name="GroupFirstItem">
<Border
BorderThickness="2"
BorderBrush="{StaticResource SystemColor}"
Background="{StaticResource HubSectionHeaderPressedForegroundThemeBrush}">
<Grid Height="110" Width="480">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<SymbolIcon Grid.Column="0" Symbol="{Binding IconSymbol}" Width="100" Height="100" RenderTransformOrigin="0.5,0.5" Foreground="{ThemeResource TextBoxBackgroundThemeBrush}" >
<SymbolIcon.RenderTransform>
<CompositeTransform ScaleX="2" TranslateX="0" TranslateY="0" ScaleY="2"/>
</SymbolIcon.RenderTransform>
</SymbolIcon>
<TextBlock Grid.Column="1" Text="{Binding Name}" FontWeight="Bold" Style="{ThemeResource TitleTextBlockStyle}" Foreground="{ThemeResource TextBoxBackgroundThemeBrush}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="13,0,0,5"/>
</Grid>
</Border>
</DataTemplate>
<DataTemplate x:Name="GroupOtherItem">
<Grid Height="110" Width="480" x:Name="EntryGrid" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border
Grid.Column="0"
BorderThickness="2"
BorderBrush="{StaticResource SystemColor}"
Background="{Binding BackgroundColor, ConverterParameter={StaticResource Transparent}, Converter={StaticResource ColorToBrushConverter}}">
<SymbolIcon Symbol="{Binding IconSymbol}" Width="100" Height="100" RenderTransformOrigin="0.5,0.5" >
<SymbolIcon.RenderTransform>
<CompositeTransform ScaleX="2" TranslateX="0" TranslateY="0" ScaleY="2"/>
</SymbolIcon.RenderTransform>
</SymbolIcon>
</Border>
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,10,0,0" >
<TextBlock x:Name="NameTextBlock" Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
<TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
<TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
</StackPanel>
</Grid>
</DataTemplate>
</GridView.Resources>
<GridView.ItemsSource>
<Binding Source="{StaticResource EntriesViewSource}"/>
</GridView.ItemsSource>
<GridView.DataContext>
<viewModels:EntryVm/>
</GridView.DataContext>
<GridView.ItemTemplateSelector>
<templateSelectors:FirstItemDataTemplateSelector
FirstItem="{StaticResource GroupFirstItem}"
OtherItem="{StaticResource GroupOtherItem}" />
</GridView.ItemTemplateSelector>
<GridView.ItemContainerStyle>
<Style TargetType="FrameworkElement">
<Setter Property="Margin" Value="52,0,0,2"/>
</Style>
</GridView.ItemContainerStyle>
</GridView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<ListView
x:Name="SemanticListView"
ItemsSource="{Binding Source={StaticResource EntriesZoomedOutViewSource}}"
IsSwipeEnabled="false"
SelectionChanged="groups_SelectionChanged"
IsSynchronizedWithCurrentItem="False">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="LightGray" Margin="0,0,10,0" HorizontalAlignment="Left">
<TextBlock Text="{Binding Key}" Width="40" Foreground="Black" Margin="30" Style="{StaticResource HeaderTextBlockStyle}"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</Grid>
<!-- Back button and page title -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="400"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
x:Name="BackButton"
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Height="50"
Width="50"
VerticalAlignment="Center"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<StackPanel Grid.Column="1" >
<TextBox
x:Name="TitleTextBox"
Text="{Binding Name, Mode=TwoWay}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
Background="Transparent"
IsHitTestVisible="{Binding IsEditMode}"
BorderThickness="0"
FontSize="20"
FontWeight="SemiBold"
TextWrapping="NoWrap"
VerticalAlignment="Center"
PlaceholderText="New group name...">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="True">
<actions:SetupFocusAction TargetObject="{Binding ElementName=TitleTextBox}" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<TextBlock FontSize="12" Text="{Binding Path}" />
</StackPanel>
<SearchBox Grid.Column="2" PlaceholderText="Search..." Width="350" Background="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" BorderThickness="0" FontSize="18" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" />
</Grid>
</Grid>
</Page>

View File

@@ -1,48 +0,0 @@
<Page
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"
xmlns:local="using:ModernKeePass.Controls"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
x:Class="ModernKeePass.Pages.RecentDatabasesPage"
mc:Ignorable="d">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<CollectionViewSource x:Name="RecentItemsSource" Source="{Binding RecentItems}" />
</Page.Resources>
<Page.DataContext>
<viewModels:RecentVm/>
</Page.DataContext>
<ListView Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
ItemsSource="{Binding Source={StaticResource RecentItemsSource}}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="10,0,10,0">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Name}" Padding="5,0,0,0" />
<TextBlock Grid.Row="1" Text="{Binding Path}" Padding="5,0,0,0" FontSize="10" />
<local:CompositeKeyUserControl Grid.Row="2" x:Name="DatabaseUserControl" HorizontalAlignment="Stretch" MinWidth="400" Margin="0,10,0,0" Visibility="{Binding IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}" ValidationChecking="OpenDatabaseUserControl_OnValidationChecking">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ValidationChecked">
<core:NavigateToPageAction TargetPage="ModernKeePass.Pages.GroupDetailPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</local:CompositeKeyUserControl>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Page>

View File

@@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Pages
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class WelcomePage : Page
{
public WelcomePage()
{
this.InitializeComponent();
}
}
}

View File

@@ -1,14 +0,0 @@
<Page
x:Class="ModernKeePass.Pages.SettingsSecurityPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ModernKeePass.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="Change database security options" />
<local:CompositeKeyUserControl UpdateKey="True" />
</StackPanel>
</Page>

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
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.9.0.0")]
[assembly: AssemblyFileVersion("1.9.0.0")]
[assembly: AssemblyVersion("1.11.0.0")]
[assembly: AssemblyFileVersion("1.11.0.0")]
[assembly: ComVisible(false)]

View File

@@ -0,0 +1,235 @@
using System;
using Windows.Storage;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Exceptions;
using ModernKeePass.Interfaces;
using ModernKeePass.ViewModels;
using ModernKeePassLib;
using ModernKeePassLib.Collections;
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Keys;
using ModernKeePassLib.Security;
using ModernKeePassLib.Serialization;
namespace ModernKeePass.Services
{
public class DatabaseService: IDatabase
{
public enum DatabaseStatus
{
Error = -3,
NoCompositeKey = -2,
CompositeKeyError = -1,
Closed = 0,
Opening = 1,
Opened = 2
}
private readonly PwDatabase _pwDatabase = new PwDatabase();
private readonly ISettings _settings;
private readonly IResource _resource;
private StorageFile _databaseFile;
private GroupVm _recycleBin;
public GroupVm RootGroup { get; set; }
public GroupVm RecycleBin
{
get { return _recycleBin; }
set
{
_recycleBin = value;
_pwDatabase.RecycleBinUuid = _recycleBin.IdUuid;
}
}
public int Status { get; set; } = (int)DatabaseStatus.Closed;
public string Name => DatabaseFile?.Name;
public bool RecycleBinEnabled
{
get { return _pwDatabase.RecycleBinEnabled; }
set { _pwDatabase.RecycleBinEnabled = value; }
}
public StorageFile DatabaseFile
{
get { return _databaseFile; }
set
{
_databaseFile = value;
Status = (int)DatabaseStatus.Opening;
}
}
public PwUuid DataCipher
{
get { return _pwDatabase.DataCipherUuid; }
set { _pwDatabase.DataCipherUuid = value; }
}
public PwCompressionAlgorithm CompressionAlgorithm
{
get { return _pwDatabase.Compression; }
set { _pwDatabase.Compression = value; }
}
public KdfParameters KeyDerivation
{
get { return _pwDatabase.KdfParameters; }
set { _pwDatabase.KdfParameters = value; }
}
public DatabaseService() : this(new SettingsService())
{ }
public DatabaseService(ISettings settings)
{
_settings = settings;
}
/// <summary>
/// Open a KeePass database
/// </summary>
/// <param name="key">The database composite key</param>
/// <param name="createNew">True to create a new database before opening it</param>
/// <returns>An error message, if any</returns>
public void Open(CompositeKey key, bool createNew = false)
{
try
{
if (key == null)
{
Status = (int)DatabaseStatus.NoCompositeKey;
return;
}
var ioConnection = IOConnectionInfo.FromFile(DatabaseFile);
if (createNew)
{
_pwDatabase.New(ioConnection, key);
//Get settings default values
if (_settings.GetSetting<bool>("Sample")) CreateSampleData();
var fileFormat = _settings.GetSetting<string>("DefaultFileFormat");
switch (fileFormat)
{
case "4":
KeyDerivation = KdfPool.Get("Argon2").GetDefaultParameters();
break;
}
}
else _pwDatabase.Open(ioConnection, key, new NullStatusLogger());
if (!_pwDatabase.IsOpen) return;
Status = (int)DatabaseStatus.Opened;
RootGroup = new GroupVm(_pwDatabase.RootGroup, null, RecycleBinEnabled ? _pwDatabase.RecycleBinUuid : null);
}
catch (InvalidCompositeKeyException)
{
Status = (int)DatabaseStatus.CompositeKeyError;
}
catch (Exception)
{
Status = (int)DatabaseStatus.Error;
throw;
}
}
/// <summary>
/// Save the current database to another file and open it
/// </summary>
/// <param name="file">The new database file</param>
public void Save(StorageFile file)
{
var oldFile = DatabaseFile;
DatabaseFile = file;
try
{
_pwDatabase.SaveAs(IOConnectionInfo.FromFile(DatabaseFile), true, new NullStatusLogger());
}
catch
{
DatabaseFile = oldFile;
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>
/// Close the currently opened database
/// </summary>
public void Close()
{
_pwDatabase?.Close();
Status = (int)DatabaseStatus.Closed;
}
public void AddDeletedItem(PwUuid id)
{
_pwDatabase.DeletedObjects.Add(new PwDeletedObject(id, DateTime.UtcNow));
}
public void CreateRecycleBin()
{
RecycleBin = RootGroup.AddNewGroup("Recycle bin");
RecycleBin.IsSelected = true;
RecycleBin.IconSymbol = Symbol.Delete;
}
public void UpdateCompositeKey(CompositeKey key)
{
_pwDatabase.MasterKey = key;
}
private void CreateSampleData()
{
_pwDatabase.RootGroup.AddGroup(new PwGroup(true, true, "Banking", PwIcon.Count), true);
_pwDatabase.RootGroup.AddGroup(new PwGroup(true, true, "Email", PwIcon.EMail), true);
_pwDatabase.RootGroup.AddGroup(new PwGroup(true, true, "Internet", PwIcon.World), true);
var pe = new PwEntry(true, true);
pe.Strings.Set(PwDefs.TitleField, new ProtectedString(_pwDatabase.MemoryProtection.ProtectTitle,
"Sample Entry"));
pe.Strings.Set(PwDefs.UserNameField, new ProtectedString(_pwDatabase.MemoryProtection.ProtectUserName,
"Username"));
pe.Strings.Set(PwDefs.UrlField, new ProtectedString(_pwDatabase.MemoryProtection.ProtectUrl,
PwDefs.HomepageUrl));
pe.Strings.Set(PwDefs.PasswordField, new ProtectedString(_pwDatabase.MemoryProtection.ProtectPassword,
"Password"));
pe.Strings.Set(PwDefs.NotesField, new ProtectedString(_pwDatabase.MemoryProtection.ProtectNotes,
"You may safely delete this sample"));
_pwDatabase.RootGroup.AddEntry(pe, true);
pe = new PwEntry(true, true);
pe.Strings.Set(PwDefs.TitleField, new ProtectedString(_pwDatabase.MemoryProtection.ProtectTitle,
"Sample Entry #2"));
pe.Strings.Set(PwDefs.UserNameField, new ProtectedString(_pwDatabase.MemoryProtection.ProtectUserName,
"Michael321"));
pe.Strings.Set(PwDefs.UrlField, new ProtectedString(_pwDatabase.MemoryProtection.ProtectUrl,
PwDefs.HelpUrl + "kb/testform.html"));
pe.Strings.Set(PwDefs.PasswordField, new ProtectedString(_pwDatabase.MemoryProtection.ProtectPassword,
"12345"));
pe.AutoType.Add(new AutoTypeAssociation("*Test Form - KeePass*", string.Empty));
_pwDatabase.RootGroup.AddEntry(pe, true);
}
}
}

View File

@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Store;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Services
{
public class LicenseService : ILicenseService
{
public enum PurchaseResult
{
Succeeded,
NothingToFulfill,
PurchasePending,
PurchaseReverted,
ServerError,
NotPurchased,
AlreadyPurchased
}
public IReadOnlyDictionary<string, ProductListing> Products { get; }
//private LicenseInformation _licenseInformation;
private readonly HashSet<Guid> _consumedTransactionIds = new HashSet<Guid>();
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();
#endif
Products = listing.ProductListings;
}
public async Task<int> Purchase(string addOn)
{
#if DEBUG
var purchaseResults = await CurrentAppSimulator.RequestProductPurchaseAsync(addOn);
#else
var purchaseResults = await CurrentApp.RequestProductPurchaseAsync(addOn);
#endif
switch (purchaseResults.Status)
{
case ProductPurchaseStatus.Succeeded:
GrantFeatureLocally(purchaseResults.TransactionId);
return (int) await ReportFulfillmentAsync(purchaseResults.TransactionId, addOn);
case ProductPurchaseStatus.NotFulfilled:
// The purchase failed because we haven't confirmed fulfillment of a previous purchase.
// Fulfill it now.
if (!IsLocallyFulfilled(purchaseResults.TransactionId))
{
GrantFeatureLocally(purchaseResults.TransactionId);
}
return (int) await ReportFulfillmentAsync(purchaseResults.TransactionId, addOn);
case ProductPurchaseStatus.NotPurchased:
return (int) PurchaseResult.NotPurchased;
case ProductPurchaseStatus.AlreadyPurchased:
return (int) PurchaseResult.AlreadyPurchased;
default:
throw new ArgumentOutOfRangeException();
}
}
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);
#endif
return (PurchaseResult) result;
}
private void GrantFeatureLocally(Guid transactionId)
{
_consumedTransactionIds.Add(transactionId);
}
private bool IsLocallyFulfilled(Guid transactionId)
{
return _consumedTransactionIds.Contains(transactionId);
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using ModernKeePass.Interfaces;
using Windows.Storage;
using Windows.Storage.AccessCache;
using ModernKeePass.ViewModels;
namespace ModernKeePass.Services
{
public class RecentService : IRecent
{
private readonly StorageItemMostRecentlyUsedList _mru = StorageApplicationPermissions.MostRecentlyUsedList;
public int EntryCount => _mru.Entries.Count;
public ObservableCollection<IRecentItem> GetAllFiles(bool removeIfNonExistant = true)
{
var result = new ObservableCollection<IRecentItem>();
foreach (var entry in _mru.Entries)
{
try
{
var file = _mru.GetFileAsync(entry.Token, AccessCacheOptions.SuppressAccessTimeUpdate).GetAwaiter().GetResult();
result.Add(new RecentItemVm(entry.Token, entry.Metadata, file));
}
catch (Exception)
{
if (removeIfNonExistant) _mru.Remove(entry.Token);
}
}
return result;
}
public void Add(IStorageItem file, string metadata)
{
_mru.Add(file, metadata);
}
public void ClearAll()
{
_mru.Clear();
}
public async Task<IStorageItem> GetFileAsync(string token)
{
return await _mru.GetFileAsync(token);
}
}
}

View File

@@ -0,0 +1,17 @@
using Windows.ApplicationModel.Resources;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Services
{
public class ResourcesService: IResource
{
private const string ResourceFileName = "CodeBehind";
private readonly ResourceLoader _resourceLoader = ResourceLoader.GetForCurrentView();
public string GetResourceValue(string key)
{
var resource = _resourceLoader.GetString($"/{ResourceFileName}/{key}");
return resource;
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using Windows.Foundation.Collections;
using Windows.Storage;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Services
{
public class SettingsService : ISettings
{
private readonly IPropertySet _values = ApplicationData.Current.LocalSettings.Values;
public T GetSetting<T>(string property)
{
try
{
return (T)Convert.ChangeType(_values[property], typeof(T));
}
catch (InvalidCastException)
{
return default(T);
}
}
public void PutSetting<T>(string property, T value)
{
if (_values.ContainsKey(property))
_values[property] = value;
else _values.Add(property, value);
}
}
}

View File

@@ -0,0 +1,273 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CompositeKeyDefaultKeyFile" xml:space="preserve">
<value>Select key file from disk...</value>
</data>
<data name="CompositeKeyErrorOpen" xml:space="preserve">
<value>Error</value>
</data>
<data name="CompositeKeyErrorUserKeyFile" xml:space="preserve">
<value>key file</value>
</data>
<data name="CompositeKeyErrorUserOr" xml:space="preserve">
<value> or </value>
</data>
<data name="CompositeKeyErrorUserPassword" xml:space="preserve">
<value>password</value>
</data>
<data name="CompositeKeyErrorUserStart" xml:space="preserve">
<value>Error: wrong </value>
</data>
<data name="CompositeKeyOpening" xml:space="preserve">
<value>Opening...</value>
</data>
<data name="CompositeKeyUpdated" xml:space="preserve">
<value>Database composite key updated.</value>
</data>
<data name="DonateAlreadyPurchasedMessage" xml:space="preserve">
<value>Your product has already been purchased.</value>
</data>
<data name="DonateAlreadyPurchasedTitle" xml:space="preserve">
<value>Purchase</value>
</data>
<data name="DonateNothingToFulfillMessage" xml:space="preserve">
<value>No purchased product to fulfill</value>
</data>
<data name="DonateNothingToFulfillTitle" xml:space="preserve">
<value>Fulfillment</value>
</data>
<data name="DonateNotPurchasedMessage" xml:space="preserve">
<value>The purchase failed because we haven't confirmed fulfillment of a previous purchase.</value>
</data>
<data name="DonateNotPurchasedTitle" xml:space="preserve">
<value>Purchase</value>
</data>
<data name="DonatePurchasePendingMessage" xml:space="preserve">
<value>The purchase is pending so we cannot fulfill the product.</value>
</data>
<data name="DonatePurchasePendingTitle" xml:space="preserve">
<value>Purchase</value>
</data>
<data name="DonatePurchaseRevertedMessage" xml:space="preserve">
<value>Your purchase has been reverted.</value>
</data>
<data name="DonatePurchaseRevertedTitle" xml:space="preserve">
<value>Purchase</value>
</data>
<data name="DonateServerErrorMessage" xml:space="preserve">
<value>Impossible to contact the Windows Store, there was an error when fulfilling.</value>
</data>
<data name="DonateServerErrorTitle" xml:space="preserve">
<value>Error</value>
</data>
<data name="DonateSucceededMessage" xml:space="preserve">
<value>Your donation was successful.</value>
</data>
<data name="DonateSucceededTitle" xml:space="preserve">
<value>Thank you!</value>
</data>
<data name="EntityDeleteActionButton" xml:space="preserve">
<value>Delete</value>
</data>
<data name="EntityDeleteCancelButton" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="EntityDeleteTitle" xml:space="preserve">
<value>Warning</value>
</data>
<data name="EntityDeleting" xml:space="preserve">
<value>Deleting</value>
</data>
<data name="EntityRestoredTitle" xml:space="preserve">
<value>Restored</value>
</data>
<data name="EntryDeleted" xml:space="preserve">
<value>Entry permanently removed</value>
</data>
<data name="EntryDeletingConfirmation" xml:space="preserve">
<value>Are you sure you want to delete this entry?</value>
</data>
<data name="EntryNew" xml:space="preserve">
<value>&lt; New entry &gt;</value>
</data>
<data name="EntryRecycled" xml:space="preserve">
<value>Entry moved to the Recycle bin</value>
</data>
<data name="EntryRecyclingConfirmation" xml:space="preserve">
<value>Are you sure you want to send this entry to the recycle bin?</value>
</data>
<data name="EntryRestored" xml:space="preserve">
<value>Entry returned to its original group</value>
</data>
<data name="GroupDeleted" xml:space="preserve">
<value>Group permanently removed</value>
</data>
<data name="GroupDeletingConfirmation" xml:space="preserve">
<value>Are you sure you want to delete the whole group and all its entries?</value>
</data>
<data name="GroupNew" xml:space="preserve">
<value>&lt; New group &gt;</value>
</data>
<data name="GroupRecycled" xml:space="preserve">
<value>Group moved to the Recycle bin</value>
</data>
<data name="GroupRecyclingConfirmation" xml:space="preserve">
<value>Are you sure you want to send the whole group and all its entries to the recycle bin?</value>
</data>
<data name="GroupRestored" xml:space="preserve">
<value>Group returned to its original group</value>
</data>
<data name="MainMenuItemAbout" xml:space="preserve">
<value>About</value>
</data>
<data name="MainMenuItemDonate" xml:space="preserve">
<value>Donate</value>
</data>
<data name="MainMenuItemNew" xml:space="preserve">
<value>New</value>
</data>
<data name="MainMenuItemOpen" xml:space="preserve">
<value>Open</value>
</data>
<data name="MainMenuItemRecent" xml:space="preserve">
<value>Recent</value>
</data>
<data name="MainMenuItemSave" xml:space="preserve">
<value>Save</value>
</data>
<data name="MainMenuItemSettings" xml:space="preserve">
<value>Settings</value>
</data>
<data name="SettingsMenuGroupApplication" xml:space="preserve">
<value>Application</value>
</data>
<data name="SettingsMenuGroupDatabase" xml:space="preserve">
<value>Database</value>
</data>
<data name="SettingsMenuItemGeneral" xml:space="preserve">
<value>General</value>
</data>
<data name="SettingsMenuItemNew" xml:space="preserve">
<value>New</value>
</data>
<data name="SettingsMenuItemSecurity" xml:space="preserve">
<value>Security</value>
</data>
</root>

View File

@@ -0,0 +1,357 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AboutCredits1.Text" xml:space="preserve">
<value>Dominik Reichl for the KeePass application and file format</value>
</data>
<data name="AboutCredits2.Text" xml:space="preserve">
<value>David Lechner for his PCL adapatation of the KeePass Library and his correlated tests</value>
</data>
<data name="AboutCreditsLabel.Text" xml:space="preserve">
<value>Credits</value>
</data>
<data name="AboutDesc.Text" xml:space="preserve">
<value>A modern password manager for the Windows Store</value>
</data>
<data name="AboutHomepage.Text" xml:space="preserve">
<value>Homepage:</value>
</data>
<data name="AppBarDelete.Label" xml:space="preserve">
<value>Delete</value>
</data>
<data name="AppBarEdit.Label" xml:space="preserve">
<value>Edit</value>
</data>
<data name="AppBarHome.Label" xml:space="preserve">
<value>Home</value>
</data>
<data name="AppBarRestore.Label" xml:space="preserve">
<value>Restore</value>
</data>
<data name="AppBarSave.Label" xml:space="preserve">
<value>Save</value>
</data>
<data name="AppBarSettings.Label" xml:space="preserve">
<value>Settings</value>
</data>
<data name="AppBarSort.Label" xml:space="preserve">
<value>Sort</value>
</data>
<data name="AppBarSortEntries.Text" xml:space="preserve">
<value>Entries</value>
</data>
<data name="AppBarSortGroups.Text" xml:space="preserve">
<value>Groups</value>
</data>
<data name="CompositeKeyNewButton.ButtonLabel" xml:space="preserve">
<value>Create</value>
</data>
<data name="CompositeKeyNewKeyFileTooltip.Content" xml:space="preserve">
<value>Create new key file</value>
</data>
<data name="CompositeKeyOpenButton.ButtonLabel" xml:space="preserve">
<value>Open</value>
</data>
<data name="CompositeKeyPassword.PlaceholderText" xml:space="preserve">
<value>Password</value>
</data>
<data name="DonateButton.Content" xml:space="preserve">
<value>Donate</value>
</data>
<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>
</data>
<data name="EntryExpirationDate.Content" xml:space="preserve">
<value>Expiration date</value>
</data>
<data name="EntryExpirationTooltip.Content" xml:space="preserve">
<value>Password has expired</value>
</data>
<data name="EntryLogin.Text" xml:space="preserve">
<value>User name or login</value>
</data>
<data name="EntryNotes.Text" xml:space="preserve">
<value>Notes</value>
</data>
<data name="EntryPassword.Text" xml:space="preserve">
<value>Password</value>
</data>
<data name="EntryShowPassword.Content" xml:space="preserve">
<value>Show password</value>
</data>
<data name="GroupCreateEntry.Text" xml:space="preserve">
<value>Create new entry</value>
</data>
<data name="GroupFilter.PlaceholderText" xml:space="preserve">
<value>Filter...</value>
</data>
<data name="GroupNewItemTextBox.Text" xml:space="preserve">
<value>&lt; New group &gt;</value>
</data>
<data name="GroupNewItemTooltip.Content" xml:space="preserve">
<value>&lt; New group &gt;</value>
</data>
<data name="GroupSearch.PlaceholderText" xml:space="preserve">
<value>Search...</value>
</data>
<data name="GroupTitle.PlaceholderText" xml:space="preserve">
<value>New group name...</value>
</data>
<data name="NewCreateButton.Content" xml:space="preserve">
<value>Create new...</value>
</data>
<data name="NewCreateDesc.Text" xml:space="preserve">
<value>Create a new password database to the location of your chosing.</value>
</data>
<data name="OpenBrowseButton.Content" xml:space="preserve">
<value>Browse files...</value>
</data>
<data name="OpenBrowseDesc.Text" xml:space="preserve">
<value>Open an existing password database from your PC.</value>
</data>
<data name="OpenUrlButton.Content" xml:space="preserve">
<value>From Url...</value>
</data>
<data name="OpenUrlDesc.Text" xml:space="preserve">
<value>Open an existing password database from an Internet location (not yet implemented).</value>
</data>
<data name="PasswordGeneratorAlso.Text" xml:space="preserve">
<value>Also add these characters:</value>
</data>
<data name="PasswordGeneratorBrackets.Content" xml:space="preserve">
<value>Brackets ([], {}, (), ...)</value>
</data>
<data name="PasswordGeneratorButton.Content" xml:space="preserve">
<value>Generate</value>
</data>
<data name="PasswordGeneratorDigits.Content" xml:space="preserve">
<value>Digits (0, 1, 2, ...)</value>
</data>
<data name="PasswordGeneratorLength.Text" xml:space="preserve">
<value>Password Length:</value>
</data>
<data name="PasswordGeneratorLower.Content" xml:space="preserve">
<value>Lower case (a, b, c, ...)</value>
</data>
<data name="PasswordGeneratorMinus.Content" xml:space="preserve">
<value>Minus (-)</value>
</data>
<data name="PasswordGeneratorSpace.Content" xml:space="preserve">
<value>Space ( )</value>
</data>
<data name="PasswordGeneratorSpecial.Content" xml:space="preserve">
<value>Special (!, $, %, ...)</value>
</data>
<data name="PasswordGeneratorTooltip.Content" xml:space="preserve">
<value>Generate password</value>
</data>
<data name="PasswordGeneratorUnderscore.Content" xml:space="preserve">
<value>Underscore (_)</value>
</data>
<data name="PasswordGeneratorUpper.Content" xml:space="preserve">
<value>Upper case (A, B, C, ...)</value>
</data>
<data name="RecentClear.Text" xml:space="preserve">
<value>Clear all</value>
</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">
<value>Save as...</value>
</data>
<data name="SaveAsDesc.Text" xml:space="preserve">
<value>This will save the currently opened database as a new file and leave it open.</value>
</data>
<data name="SaveButton.Content" xml:space="preserve">
<value>Save and close</value>
</data>
<data name="SaveDesc.Text" xml:space="preserve">
<value>This will save and close the currently opened database.</value>
</data>
<data name="SettingsDatabaseCompression.Text" xml:space="preserve">
<value>Compression Algorithm</value>
</data>
<data name="SettingsDatabaseEncryption.Text" xml:space="preserve">
<value>Encryption Algorithm</value>
</data>
<data name="SettingsDatabaseKdf.Text" xml:space="preserve">
<value>Key Derivation Algorithm</value>
</data>
<data name="SettingsDatabaseRecycleBin.Header" xml:space="preserve">
<value>Recycle bin</value>
</data>
<data name="SettingsDatabaseRecycleBin.OffContent" xml:space="preserve">
<value>Disabled</value>
</data>
<data name="SettingsDatabaseRecycleBin.OnContent" xml:space="preserve">
<value>Enabled</value>
</data>
<data name="SettingsNewDatabaseDesc.Text" xml:space="preserve">
<value>Here, you can change some default options when creating a database.</value>
</data>
<data name="SettingsNewDatabaseKdf.Text" xml:space="preserve">
<value>KDBX database file version</value>
</data>
<data name="SettingsNewDatabaseKdfMoreInfo.Text" xml:space="preserve">
<value>Higher is better, but there may be compatibility issues with older applications</value>
</data>
<data name="SettingsNewDatabaseSample.Header" xml:space="preserve">
<value>Create sample data</value>
</data>
<data name="SettingsNewDatabaseSample.OffContent" xml:space="preserve">
<value>No</value>
</data>
<data name="SettingsNewDatabaseSample.OnContent" xml:space="preserve">
<value>Yes</value>
</data>
<data name="SettingsSecurityDesc1.Text" xml:space="preserve">
<value>Here, you may change your database password, key file, or both. Just click on on</value>
</data>
<data name="SettingsSecurityDesc2.Text" xml:space="preserve">
<value>Update master key</value>
</data>
<data name="SettingsSecurityDesc3.Text" xml:space="preserve">
<value>when you're done. Please make sure to remember the password you choose here!</value>
</data>
<data name="SettingsSecurityTitle.Text" xml:space="preserve">
<value>Change database security options</value>
</data>
<data name="SettingsSecurityUpdateButton.ButtonLabel" xml:space="preserve">
<value>Update master key</value>
</data>
<data name="SettingsTitle.Text" xml:space="preserve">
<value>Settings</value>
</data>
<data name="SettingsWelcomeDesc.Text" xml:space="preserve">
<value>Here, you may change the application or the database settings.</value>
</data>
<data name="SettingsWelcomeHowto.Text" xml:space="preserve">
<value>Select a setting pane on the left to access the options.</value>
</data>
<data name="SettingsWelcomeTitle.Text" xml:space="preserve">
<value>Settings</value>
</data>
<data name="WelcomeNew.Text" xml:space="preserve">
<value>Want to create a new password database? Do it here.</value>
</data>
<data name="WelcomeOpen.Text" xml:space="preserve">
<value>Have an existing password database? Open it here.</value>
</data>
</root>

View File

@@ -0,0 +1,274 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CompositeKeyDefaultKeyFile" xml:space="preserve">
<value>Choisissez un fichier...</value>
</data>
<data name="CompositeKeyErrorOpen" xml:space="preserve">
<value>Erreur</value>
</data>
<data name="CompositeKeyErrorUserKeyFile" xml:space="preserve">
<value>fichier de clé</value>
</data>
<data name="CompositeKeyErrorUserOr" xml:space="preserve">
<value> ou </value>
</data>
<data name="CompositeKeyErrorUserPassword" xml:space="preserve">
<value>mot de passe</value>
</data>
<data name="CompositeKeyErrorUserStart" xml:space="preserve">
<value>Erreur: mauvais </value>
</data>
<data name="CompositeKeyOpening" xml:space="preserve">
<value>Ouverture...</value>
</data>
<data name="CompositeKeyUpdated" xml:space="preserve">
<value>Clé composite de la base de données mise à jour.</value>
</data>
<data name="DonateAlreadyPurchasedMessage" xml:space="preserve">
<value>Vous avez déjà acheté ce produit.</value>
</data>
<data name="DonateAlreadyPurchasedTitle" xml:space="preserve">
<value>Achat</value>
</data>
<data name="DonateNothingToFulfillMessage" xml:space="preserve">
<value>Il n'y a aucun produit acheté à valider.</value>
</data>
<data name="DonateNothingToFulfillTitle" xml:space="preserve">
<value>Validation</value>
</data>
<data name="DonateNotPurchasedMessage" xml:space="preserve">
<value>L'achat a échoué parce que nous n'avons pas pu valider un achat précédent.</value>
</data>
<data name="DonateNotPurchasedTitle" xml:space="preserve">
<value>Achat</value>
</data>
<data name="DonatePurchasePendingMessage" xml:space="preserve">
<value>Un achat est en cours donc nous ne pouvons le valider.</value>
</data>
<data name="DonatePurchasePendingTitle" xml:space="preserve">
<value>Achat</value>
</data>
<data name="DonatePurchaseRevertedMessage" xml:space="preserve">
<value>Votre achat a été annulé.</value>
</data>
<data name="DonatePurchaseRevertedTitle" xml:space="preserve">
<value>Achat</value>
</data>
<data name="DonateServerErrorMessage" xml:space="preserve">
<value>Impossible de contacter le Windows Store, l'achat n'a pas été réalisé.</value>
</data>
<data name="DonateServerErrorTitle" xml:space="preserve">
<value>Erreur</value>
</data>
<data name="DonateSucceededMessage" xml:space="preserve">
<value>Votre don a été enregistré.</value>
</data>
<data name="DonateSucceededTitle" xml:space="preserve">
<value>Merci!</value>
</data>
<data name="EntityDeleteActionButton" xml:space="preserve">
<value>Supprimer</value>
</data>
<data name="EntityDeleteCancelButton" xml:space="preserve">
<value>Annuler</value>
</data>
<data name="EntityDeleteTitle" xml:space="preserve">
<value>Attention</value>
</data>
<data name="EntityDeleting" xml:space="preserve">
<value>Suprression</value>
</data>
<data name="EntityRestoredTitle" xml:space="preserve">
<value>Restauré</value>
</data>
<data name="EntryDeleted" xml:space="preserve">
<value>Entrée supprimée définitivement</value>
</data>
<data name="EntryDeletingConfirmation" xml:space="preserve">
<value>Êtes-vous sûr de vouloir supprimer cette entrée ?</value>
</data>
<data name="EntryNew" xml:space="preserve">
<value>&lt; Nouvelle entrée &gt;</value>
<comment>Unused</comment>
</data>
<data name="EntryRecycled" xml:space="preserve">
<value>Entrée placée dans la Corbeille</value>
</data>
<data name="EntryRecyclingConfirmation" xml:space="preserve">
<value>Êtes-vous sûr de vouloir placer cette entrée dans la Corbeille ?</value>
</data>
<data name="EntryRestored" xml:space="preserve">
<value>Entrée replacée dans son groupe d'origine</value>
</data>
<data name="GroupDeleted" xml:space="preserve">
<value>Groupe supprimé défnitivement</value>
</data>
<data name="GroupDeletingConfirmation" xml:space="preserve">
<value>Êtes-vous sûr de vouloir supprimer ce group et toutes ses entrées ?</value>
</data>
<data name="GroupNew" xml:space="preserve">
<value>&lt; Nouveau groupe &gt;</value>
</data>
<data name="GroupRecycled" xml:space="preserve">
<value>Groupe placé dans la Corbeille</value>
</data>
<data name="GroupRecyclingConfirmation" xml:space="preserve">
<value>Êtes-vous sûr de vouloir envoyer ce groupe et toutes ses entrées vers la Corbeille ?</value>
</data>
<data name="GroupRestored" xml:space="preserve">
<value>Groupe replacé à sa place originelle</value>
</data>
<data name="MainMenuItemAbout" xml:space="preserve">
<value>A propos</value>
</data>
<data name="MainMenuItemDonate" xml:space="preserve">
<value>Donation</value>
</data>
<data name="MainMenuItemNew" xml:space="preserve">
<value>Nouveau</value>
</data>
<data name="MainMenuItemOpen" xml:space="preserve">
<value>Ouvrir</value>
</data>
<data name="MainMenuItemRecent" xml:space="preserve">
<value>Récents</value>
</data>
<data name="MainMenuItemSave" xml:space="preserve">
<value>Sauvegarder</value>
</data>
<data name="MainMenuItemSettings" xml:space="preserve">
<value>Paramètres</value>
</data>
<data name="SettingsMenuGroupApplication" xml:space="preserve">
<value>Application</value>
</data>
<data name="SettingsMenuGroupDatabase" xml:space="preserve">
<value>Base de données</value>
</data>
<data name="SettingsMenuItemGeneral" xml:space="preserve">
<value>Général</value>
</data>
<data name="SettingsMenuItemNew" xml:space="preserve">
<value>Nouveau</value>
</data>
<data name="SettingsMenuItemSecurity" xml:space="preserve">
<value>Sécurité</value>
</data>
</root>

View File

@@ -0,0 +1,357 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AboutCredits1.Text" xml:space="preserve">
<value>Dominik Reichl pour l'application KeePass et le format de fichier</value>
</data>
<data name="AboutCredits2.Text" xml:space="preserve">
<value>David Lechner pour son adaptation PCL de la blibliothèque KeePass et ses tests associés</value>
</data>
<data name="AboutCreditsLabel.Text" xml:space="preserve">
<value>Crédits</value>
</data>
<data name="AboutDesc.Text" xml:space="preserve">
<value>Un gestionnaire de mot de passes moderne pour le Windows Store</value>
</data>
<data name="AboutHomepage.Text" xml:space="preserve">
<value>Page d'accueil :</value>
</data>
<data name="AppBarDelete.Label" xml:space="preserve">
<value>Supprimer</value>
</data>
<data name="AppBarEdit.Label" xml:space="preserve">
<value>Editer</value>
</data>
<data name="AppBarHome.Label" xml:space="preserve">
<value>Accueil</value>
</data>
<data name="AppBarRestore.Label" xml:space="preserve">
<value>Restorer</value>
</data>
<data name="AppBarSave.Label" xml:space="preserve">
<value>Sauvegarder</value>
</data>
<data name="AppBarSettings.Label" xml:space="preserve">
<value>Paramètres</value>
</data>
<data name="AppBarSort.Label" xml:space="preserve">
<value>Trier</value>
</data>
<data name="AppBarSortEntries.Text" xml:space="preserve">
<value>Entrées</value>
</data>
<data name="AppBarSortGroups.Text" xml:space="preserve">
<value>Groupes</value>
</data>
<data name="CompositeKeyNewButton.ButtonLabel" xml:space="preserve">
<value>Créer</value>
</data>
<data name="CompositeKeyNewKeyFileTooltip.Content" xml:space="preserve">
<value>Créer un nouveau fichier de clé</value>
</data>
<data name="CompositeKeyOpenButton.ButtonLabel" xml:space="preserve">
<value>Ouvrir</value>
</data>
<data name="CompositeKeyPassword.PlaceholderText" xml:space="preserve">
<value>Mot de passe</value>
</data>
<data name="DonateButton.Content" xml:space="preserve">
<value>Donner</value>
</data>
<data name="DonateDesc.Text" xml:space="preserve">
<value>Vous aimez cette app? Pourquoi ne pas faire un petit don afin de m'encourager et d'éviter le recours aux publicités :) ?</value>
</data>
<data name="EntryExpirationDate.Content" xml:space="preserve">
<value>Date d'expiration</value>
</data>
<data name="EntryExpirationTooltip.Content" xml:space="preserve">
<value>Le mot de passe a expiré</value>
</data>
<data name="EntryLogin.Text" xml:space="preserve">
<value>Nom d'utilisateur ou login</value>
</data>
<data name="EntryNotes.Text" xml:space="preserve">
<value>Notes</value>
</data>
<data name="EntryPassword.Text" xml:space="preserve">
<value>Mot de passe</value>
</data>
<data name="EntryShowPassword.Content" xml:space="preserve">
<value>Afficher le mot de passe</value>
</data>
<data name="GroupCreateEntry.Text" xml:space="preserve">
<value>Créer une nouvelle entrée</value>
</data>
<data name="GroupFilter.PlaceholderText" xml:space="preserve">
<value>Filtrer...</value>
</data>
<data name="GroupNewItemTextBox.Text" xml:space="preserve">
<value>&lt; Nouveau groupe &gt;</value>
</data>
<data name="GroupNewItemTooltip.Content" xml:space="preserve">
<value>&lt; Nouveau groupe &gt;</value>
</data>
<data name="GroupSearch.PlaceholderText" xml:space="preserve">
<value>Rechercher...</value>
</data>
<data name="GroupTitle.PlaceholderText" xml:space="preserve">
<value>Nom du nouveau groupe...</value>
</data>
<data name="NewCreateButton.Content" xml:space="preserve">
<value>Créer une nouvelle...</value>
</data>
<data name="NewCreateDesc.Text" xml:space="preserve">
<value>Créer une nouvelle base de données de mots de passe à l'endroit de votre choix.</value>
</data>
<data name="OpenBrowseButton.Content" xml:space="preserve">
<value>Parcourir les fichiers...</value>
</data>
<data name="OpenBrowseDesc.Text" xml:space="preserve">
<value>Ouvrir une base de données de mots de passe existante sur votre PC.</value>
</data>
<data name="OpenUrlButton.Content" xml:space="preserve">
<value>Depuis une URL...</value>
</data>
<data name="OpenUrlDesc.Text" xml:space="preserve">
<value>Ouvrir une base de données de mots de passe depuis Internet (pas encore implementé).</value>
</data>
<data name="PasswordGeneratorAlso.Text" xml:space="preserve">
<value>Ajouter aussi ces caractères :</value>
</data>
<data name="PasswordGeneratorBrackets.Content" xml:space="preserve">
<value>Parenthèses ([], {}, (), ...)</value>
</data>
<data name="PasswordGeneratorButton.Content" xml:space="preserve">
<value>Générer</value>
</data>
<data name="PasswordGeneratorDigits.Content" xml:space="preserve">
<value>Chiffres (0, 1, 2, ...)</value>
</data>
<data name="PasswordGeneratorLength.Text" xml:space="preserve">
<value>Longueur de mot de passe :</value>
</data>
<data name="PasswordGeneratorLower.Content" xml:space="preserve">
<value>Minuscules (a, b, c, ...)</value>
</data>
<data name="PasswordGeneratorMinus.Content" xml:space="preserve">
<value>Moins (-)</value>
</data>
<data name="PasswordGeneratorSpace.Content" xml:space="preserve">
<value>Espace ( )</value>
</data>
<data name="PasswordGeneratorSpecial.Content" xml:space="preserve">
<value>Spéciaux (!, $, %, ...)</value>
</data>
<data name="PasswordGeneratorTooltip.Content" xml:space="preserve">
<value>Générer le mot de passe</value>
</data>
<data name="PasswordGeneratorUnderscore.Content" xml:space="preserve">
<value>Underscore (_)</value>
</data>
<data name="PasswordGeneratorUpper.Content" xml:space="preserve">
<value>Majuscules (A, B, C, ...)</value>
</data>
<data name="RecentClear.Text" xml:space="preserve">
<value>Supprimer tout</value>
</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">
<value>Sauvegarder sous...</value>
</data>
<data name="SaveAsDesc.Text" xml:space="preserve">
<value>Cela va sauvegarder la base de données dans un nouveau fichier et l'ouvrir.</value>
</data>
<data name="SaveButton.Content" xml:space="preserve">
<value>Sauvegarder et fermer</value>
</data>
<data name="SaveDesc.Text" xml:space="preserve">
<value>Cela va sauvegarder et fermer la base de données.</value>
</data>
<data name="SettingsDatabaseCompression.Text" xml:space="preserve">
<value>Algorithme de compression</value>
</data>
<data name="SettingsDatabaseEncryption.Text" xml:space="preserve">
<value>Algorithme de chiffrement</value>
</data>
<data name="SettingsDatabaseKdf.Text" xml:space="preserve">
<value>Algorithme de dérivation de clé</value>
</data>
<data name="SettingsDatabaseRecycleBin.Header" xml:space="preserve">
<value>Corbeille</value>
</data>
<data name="SettingsDatabaseRecycleBin.OffContent" xml:space="preserve">
<value>Désactivé</value>
</data>
<data name="SettingsDatabaseRecycleBin.OnContent" xml:space="preserve">
<value>Activé</value>
</data>
<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>
</data>
<data name="SettingsNewDatabaseKdf.Text" xml:space="preserve">
<value>Version de fichier KDBX</value>
</data>
<data name="SettingsNewDatabaseKdfMoreInfo.Text" xml:space="preserve">
<value>Le plus élévé est le mieux, mais vous pourriez rencontrer des problèmes de compatibilité avec des application plus anciennes</value>
</data>
<data name="SettingsNewDatabaseSample.Header" xml:space="preserve">
<value>Créer des données d'exemple</value>
</data>
<data name="SettingsNewDatabaseSample.OffContent" xml:space="preserve">
<value>Non</value>
</data>
<data name="SettingsNewDatabaseSample.OnContent" xml:space="preserve">
<value>Oui</value>
</data>
<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>
</data>
<data name="SettingsSecurityDesc2.Text" xml:space="preserve">
<value>Mettre à jour la clé maître</value>
</data>
<data name="SettingsSecurityDesc3.Text" xml:space="preserve">
<value>quand vous avez fini. Retenez bien le mot de passe que vous utilisez !</value>
</data>
<data name="SettingsSecurityTitle.Text" xml:space="preserve">
<value>Changer les options de sécurité de la base de données</value>
</data>
<data name="SettingsSecurityUpdateButton.ButtonLabel" xml:space="preserve">
<value>Mettre à jour la clé maître</value>
</data>
<data name="SettingsTitle.Text" xml:space="preserve">
<value>Paramètres</value>
</data>
<data name="SettingsWelcomeDesc.Text" xml:space="preserve">
<value>Ici, vous pouvez changer les options de l'application ou de la base de données.</value>
</data>
<data name="SettingsWelcomeHowto.Text" xml:space="preserve">
<value>Choisissez un élément dans le menu de gauche.</value>
</data>
<data name="SettingsWelcomeTitle.Text" xml:space="preserve">
<value>Paramètres</value>
</data>
<data name="WelcomeNew.Text" xml:space="preserve">
<value>Pour créer une nouvelle base de données de mots de passe, c'est ici.</value>
</data>
<data name="WelcomeOpen.Text" xml:space="preserve">
<value>Pour ouvrir une base de données existante, c'est ici.</value>
</data>
</root>

View File

@@ -0,0 +1,9 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBlock" x:Name="TextBlockSettingsHeaderStyle" >
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="14.667" />
<Setter Property="FontWeight" Value="SemiLight" />
</Style>
</ResourceDictionary>

View File

@@ -62,7 +62,7 @@
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<!--<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
@@ -73,7 +73,7 @@
To="0"
Duration="0" />
</Storyboard>
</VisualState>
</VisualState>-->
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="BorderElement"
@@ -89,6 +89,7 @@
HorizontalAlignment="Center"
FontStyle="Normal"
Text="{TemplateBinding Content}"
FontSize="{TemplateBinding FontSize}"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
AutomationProperties.AccessibilityView="Raw"/>
</Border>
@@ -142,9 +143,28 @@
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlPointerOverBorderThemeOpacity}" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ActionButton"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ActionButton"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Focused" />
</VisualStateGroup>
<VisualStateGroup x:Name="ButtonStates">
<VisualState x:Name="ButtonVisible">
@@ -199,6 +219,7 @@
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsTabStop="False"
@@ -209,6 +230,7 @@
Foreground="{ThemeResource TextBoxPlaceholderTextThemeBrush}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
IsTabStop="False"
Grid.ColumnSpan="2"
Content="{TemplateBinding PlaceholderText}"
@@ -222,7 +244,12 @@
Visibility="Collapsed"
FontSize="{TemplateBinding FontSize}"
Content="{TemplateBinding ButtonSymbol}"
VerticalAlignment="Stretch"/>
IsEnabled="{TemplateBinding IsButtonEnabled}"
VerticalAlignment="Stretch">
<ToolTipService.ToolTip>
<ToolTip Content="{TemplateBinding ButtonTooltip}" />
</ToolTipService.ToolTip>
</Button>
</Grid>
</ControlTemplate>
</Setter.Value>

View File

@@ -4,17 +4,24 @@ namespace ModernKeePass.ViewModels
{
public class AboutVm
{
public string Name { get; } = Package.Current.DisplayName;
private readonly Package _package;
public string Name => _package.DisplayName;
public string Version
{
get
{
var package = Package.Current;
var version = package.Id.Version;
var version = _package.Id.Version;
return $"{version.Major}.{version.Minor}";
}
}
public AboutVm() : this(Package.Current) { }
public AboutVm(Package package)
{
_package = package;
}
}
}

View File

@@ -1,7 +1,11 @@
using System.Text;
using System;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.UI.Xaml;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.Services;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Keys;
using ModernKeePassLib.Serialization;
@@ -18,14 +22,7 @@ namespace ModernKeePass.ViewModels
Success = 5
}
private readonly App _app = Application.Current as App;
private bool _hasPassword;
private bool _hasKeyFile;
private string _password = string.Empty;
private string _status;
private StatusTypes _statusType;
private StorageFile _keyFile;
private string _keyFileText = "Select key file from disk...";
public IDatabase Database { get; set; }
public bool HasPassword
{
@@ -47,7 +44,7 @@ namespace ModernKeePass.ViewModels
}
}
public bool IsValid => HasPassword || HasKeyFile && KeyFile != null;
public bool IsValid => !_isOpening && (HasPassword || HasKeyFile && KeyFile != null);
public string Status
{
@@ -69,6 +66,7 @@ namespace ModernKeePass.ViewModels
_password = value;
OnPropertyChanged("PasswordComplexityIndicator");
StatusType = (int)StatusTypes.Normal;
Status = string.Empty;
}
}
@@ -90,31 +88,67 @@ namespace ModernKeePass.ViewModels
}
public GroupVm RootGroup { get; set; }
public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password?.ToCharArray());
public bool OpenDatabase(bool createNew)
private bool _hasPassword;
private bool _hasKeyFile;
private bool _isOpening;
private string _password = string.Empty;
private string _status;
private StatusTypes _statusType;
private StorageFile _keyFile;
private string _keyFileText;
private readonly IResource _resource;
public CompositeKeyVm() : this((Application.Current as App)?.Database, new ResourcesService()) { }
public CompositeKeyVm(IDatabase database, IResource resource)
{
_app.Database.Open(CreateCompositeKey(), createNew);
switch (_app.Database.Status)
_resource = resource;
_keyFileText = _resource.GetResourceValue("CompositeKeyDefaultKeyFile");
Database = database;
}
public async Task<bool> OpenDatabase(bool createNew)
{
case DatabaseHelper.DatabaseStatus.Opened:
RootGroup = _app.Database.RootGroup;
var error = string.Empty;
try
{
_isOpening = true;
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);
return true;
case DatabaseHelper.DatabaseStatus.CompositeKeyError:
var errorMessage = new StringBuilder("Error: wrong ");
if (HasPassword) errorMessage.Append("password");
if (HasPassword && HasKeyFile) errorMessage.Append(" or ");
if (HasKeyFile) errorMessage.Append("key file");
case DatabaseService.DatabaseStatus.CompositeKeyError:
var errorMessage = new StringBuilder(_resource.GetResourceValue("CompositeKeyErrorUserStart"));
if (HasPassword) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserPassword"));
if (HasPassword && HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserOr"));
if (HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserKeyFile"));
UpdateStatus(errorMessage.ToString(), StatusTypes.Error);
break;
case DatabaseService.DatabaseStatus.Error:
UpdateStatus(error, StatusTypes.Error);
break;
}
return false;
}
public void UpdateKey()
{
_app.Database.UpdateCompositeKey(CreateCompositeKey());
UpdateStatus("Database composite key updated.", StatusTypes.Success);
Database.UpdateCompositeKey(CreateCompositeKey());
UpdateStatus(_resource.GetResourceValue("CompositeKeyUpdated"), StatusTypes.Success);
}
public void CreateKeyFile(StorageFile file)

View File

@@ -0,0 +1,44 @@
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

@@ -15,17 +15,13 @@ namespace ModernKeePass.ViewModels
public class EntryVm : INotifyPropertyChanged, IPwEntity
{
public GroupVm ParentGroup { get; private set; }
public GroupVm PreviousGroup { get; private set; }
public System.Drawing.Color? BackgroundColor => _pwEntry?.BackgroundColor;
public System.Drawing.Color? ForegroundColor => _pwEntry?.ForegroundColor;
public bool IsRevealPasswordEnabled => !string.IsNullOrEmpty(Password);
public bool HasExpired => HasExpirationDate && _pwEntry.ExpiryTime < DateTime.Now;
public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password.ToCharArray());
public bool IsFirstItem => _pwEntry == null;
public bool UpperCasePatternSelected { get; set; } = true;
public bool LowerCasePatternSelected { get; set; } = true;
public bool DigitsPatternSelected { get; set; } = true;
@@ -36,6 +32,7 @@ namespace ModernKeePass.ViewModels
public bool BracketsPatternSelected { get; set; }
public string CustomChars { get; set; } = string.Empty;
public PwUuid IdUuid => _pwEntry?.Uuid;
public string Id => _pwEntry?.Uuid.ToHexString();
public double PasswordLength
{
@@ -46,17 +43,18 @@ namespace ModernKeePass.ViewModels
NotifyPropertyChanged("PasswordLength");
}
}
public string Name
{
get
{
var title = GetEntryValue(PwDefs.TitleField);
return title == null ? "< New entry >" : title;
/*var title = GetEntryValue(PwDefs.TitleField);
return title == null ? _resource.GetResourceValue("EntryNew") : title;*/
return GetEntryValue(PwDefs.TitleField);
}
set { SetEntryValue(PwDefs.TitleField, value); }
}
public string Id => _pwEntry?.Uuid.ToHexString();
public string UserName
{
@@ -118,6 +116,18 @@ namespace ModernKeePass.ViewModels
}
}
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
NotifyPropertyChanged("IsVisible");
}
}
public bool IsRecycleOnDelete => _database.RecycleBinEnabled && !ParentGroup.IsSelected;
public bool IsRevealPassword
{
get { return _isRevealPassword; }
@@ -150,10 +160,11 @@ namespace ModernKeePass.ViewModels
public event PropertyChangedEventHandler PropertyChanged;
private readonly PwEntry _pwEntry;
private readonly App _app = Application.Current as App;
private readonly IDatabase _database;
private bool _isEditMode;
private bool _isRevealPassword;
private double _passwordLength = 25;
private bool _isVisible = true;
private void NotifyPropertyChanged(string propertyName)
{
@@ -161,15 +172,19 @@ namespace ModernKeePass.ViewModels
}
public EntryVm() { }
public EntryVm(PwEntry entry, GroupVm parent)
internal EntryVm(PwEntry entry, GroupVm parent) : this(entry, parent, (Application.Current as App)?.Database) { }
public EntryVm(PwEntry entry, GroupVm parent, IDatabase database)
{
_database = database;
_pwEntry = entry;
ParentGroup = parent;
}
public void GeneratePassword()
{
var pwProfile = new PwProfile()
var pwProfile = new PwProfile
{
GeneratorType = PasswordGeneratorType.CharSet,
Length = (uint)PasswordLength,
@@ -208,9 +223,9 @@ namespace ModernKeePass.ViewModels
public void MarkForDelete()
{
if (_app.Database.RecycleBinEnabled && _app.Database.RecycleBin?.IdUuid == null)
_app.Database.CreateRecycleBin();
Move(_app.Database.RecycleBinEnabled && !ParentGroup.IsSelected ? _app.Database.RecycleBin : null);
if (_database.RecycleBinEnabled && _database.RecycleBin?.IdUuid == null)
_database.CreateRecycleBin();
Move(_database.RecycleBinEnabled && !ParentGroup.IsSelected ? _database.RecycleBin : null);
}
public void UndoDelete()
@@ -222,27 +237,29 @@ namespace ModernKeePass.ViewModels
{
PreviousGroup = ParentGroup;
PreviousGroup.Entries.Remove(this);
PreviousGroup.RemovePwEntry(_pwEntry);
if (destination == null)
{
_app.Database.AddDeletedItem(IdUuid);
_database.AddDeletedItem(IdUuid);
return;
}
ParentGroup = destination;
ParentGroup.Entries.Add(this);
ParentGroup.AddPwEntry(_pwEntry);
}
public void CommitDelete()
{
_pwEntry.ParentGroup.Entries.Remove(_pwEntry);
if (_app.Database.RecycleBinEnabled && !PreviousGroup.IsSelected) _app.Database.RecycleBin.AddPwEntry(_pwEntry);
else _app.Database.AddDeletedItem(IdUuid);
if (!_database.RecycleBinEnabled || PreviousGroup.IsSelected) _database.AddDeletedItem(IdUuid);
}
public void Save()
{
_app.Database.Save();
_database.Save();
}
public PwEntry GetPwEntry()
{
return _pwEntry;
}
}
}

View File

@@ -1,7 +1,8 @@
using System.Collections.ObjectModel;
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using Windows.UI.Text;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
@@ -15,33 +16,32 @@ namespace ModernKeePass.ViewModels
{
public GroupVm ParentGroup { get; private set; }
public GroupVm PreviousGroup { get; private set; }
public ObservableCollection<EntryVm> Entries { get; set; } = new ObservableCollection<EntryVm>();
public ObservableCollection<EntryVm> Entries
{
get { return _entries; }
set { SetProperty(ref _entries, value); }
}
public ObservableCollection<GroupVm> Groups { get; set; } = new ObservableCollection<GroupVm>();
public int EntryCount => Entries.Count() - 1;
public FontWeight FontWeight => _pwGroup == null ? FontWeights.Bold : FontWeights.Normal;
public int GroupCount => Groups.Count - 1;
public PwUuid IdUuid => _pwGroup?.Uuid;
public string Id => IdUuid?.ToHexString();
public bool IsNotRoot => ParentGroup != null;
public bool ShowRestore => IsNotRoot && ParentGroup.IsSelected;
public bool IsRecycleOnDelete => _database.RecycleBinEnabled && !IsSelected && !ParentGroup.IsSelected;
/// <summary>
/// Is the Group the database Recycle Bin?
/// </summary>
public bool IsSelected
{
get { return _app.Database.RecycleBinEnabled && _app.Database.RecycleBin?.Id == Id; }
get { return _database != null && _database.RecycleBinEnabled && _database.RecycleBin?.Id == Id; }
set
{
if (value && _pwGroup != null) _app.Database.RecycleBin = this;
/*else if (value && _pwGroup == null)
{
var recycleBin = _app.Database.RootGroup.AddNewGroup("Recycle bin");
recycleBin.IsSelected = true;
recycleBin.IconSymbol = Symbol.Delete;
}*/
if (value && _pwGroup != null) _database.RecycleBin = this;
}
}
@@ -53,7 +53,7 @@ namespace ModernKeePass.ViewModels
public string Name
{
get { return _pwGroup == null ? "< New group >" : _pwGroup.Name; }
get { return _pwGroup == null ? string.Empty : _pwGroup.Name; }
set { _pwGroup.Name = value; }
}
@@ -74,6 +74,12 @@ namespace ModernKeePass.ViewModels
set { SetProperty(ref _isEditMode, value); }
}
public bool IsMenuClosed
{
get { return _isMenuClosed; }
set { SetProperty(ref _isMenuClosed, value); }
}
public string Path
{
get
@@ -86,23 +92,48 @@ namespace ModernKeePass.ViewModels
}
private readonly PwGroup _pwGroup;
private readonly App _app = Application.Current as App;
private readonly IDatabase _database;
private bool _isEditMode;
private PwEntry _reorderedEntry;
private ObservableCollection<EntryVm> _entries = new ObservableCollection<EntryVm>();
private string _filter = string.Empty;
private bool _isMenuClosed = true;
public GroupVm() {}
public GroupVm(PwGroup pwGroup, GroupVm parent, PwUuid recycleBinId = null)
internal GroupVm(PwGroup pwGroup, GroupVm parent, PwUuid recycleBinId = null) : this(pwGroup, parent,
(Application.Current as App)?.Database, recycleBinId)
{ }
public GroupVm(PwGroup pwGroup, GroupVm parent, IDatabase database, PwUuid recycleBinId = null)
{
_pwGroup = pwGroup;
_database = database;
ParentGroup = parent;
if (recycleBinId != null && _pwGroup.Uuid.Equals(recycleBinId)) _app.Database.RecycleBin = this;
Entries = new ObservableCollection<EntryVm>(pwGroup.Entries.Select(e => new EntryVm(e, this)).OrderBy(e => e.Name));
Entries.Insert(0, new EntryVm ());
Groups = new ObservableCollection<GroupVm>(pwGroup.Groups.Select(g => new GroupVm(g, this, recycleBinId)).OrderBy(g => g.Name));
if (recycleBinId != null && _pwGroup.Uuid.Equals(recycleBinId)) _database.RecycleBin = this;
Entries = new ObservableCollection<EntryVm>(pwGroup.Entries.Select(e => new EntryVm(e, this)));
Entries.CollectionChanged += Entries_CollectionChanged;
Groups = new ObservableCollection<GroupVm>(pwGroup.Groups.Select(g => new GroupVm(g, this, recycleBinId)));
Groups.Insert(0, new GroupVm());
}
private void Entries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Remove:
var oldIndex = (uint) e.OldStartingIndex;
_reorderedEntry = _pwGroup.Entries.GetAt(oldIndex);
_pwGroup.Entries.RemoveAt(oldIndex);
break;
case NotifyCollectionChangedAction.Add:
if (_reorderedEntry == null) _pwGroup.AddEntry(((EntryVm) e.NewItems[0]).GetPwEntry(), true);
else _pwGroup.Entries.Insert((uint)e.NewStartingIndex, _reorderedEntry);
break;
}
}
public GroupVm AddNewGroup(string name = "")
{
var pwGroup = new PwGroup(true, true, name, PwIcon.Folder);
@@ -115,30 +146,19 @@ namespace ModernKeePass.ViewModels
public EntryVm AddNewEntry()
{
var pwEntry = new PwEntry(true, true);
_pwGroup.AddEntry(pwEntry, true);
var newEntry = new EntryVm(pwEntry, this) {IsEditMode = true};
newEntry.GeneratePassword();
Entries.Add(newEntry);
return newEntry;
}
public void AddPwEntry(PwEntry entry)
{
_pwGroup.AddEntry(entry, true);
}
public void RemovePwEntry(PwEntry entry)
{
_pwGroup.Entries.Remove(entry);
}
public void MarkForDelete()
{
if (_app.Database.RecycleBinEnabled && _app.Database.RecycleBin?.IdUuid == null)
_app.Database.CreateRecycleBin();
Move(_app.Database.RecycleBinEnabled && !IsSelected ? _app.Database.RecycleBin : null);
if (_database.RecycleBinEnabled && _database.RecycleBin?.IdUuid == null)
_database.CreateRecycleBin();
Move(_database.RecycleBinEnabled && !IsSelected ? _database.RecycleBin : null);
}
public void UndoDelete()
{
Move(PreviousGroup);
@@ -151,7 +171,7 @@ namespace ModernKeePass.ViewModels
PreviousGroup._pwGroup.Groups.Remove(_pwGroup);
if (destination == null)
{
_app.Database.AddDeletedItem(IdUuid);
_database.AddDeletedItem(IdUuid);
return;
}
ParentGroup = destination;
@@ -162,13 +182,41 @@ namespace ModernKeePass.ViewModels
public void CommitDelete()
{
_pwGroup.ParentGroup.Groups.Remove(_pwGroup);
if (_app.Database.RecycleBinEnabled && !PreviousGroup.IsSelected) _app.Database.RecycleBin._pwGroup.AddGroup(_pwGroup, true);
else _app.Database.AddDeletedItem(IdUuid);
if (_database.RecycleBinEnabled && !PreviousGroup.IsSelected) _database.RecycleBin._pwGroup.AddGroup(_pwGroup, true);
else _database.AddDeletedItem(IdUuid);
}
public void Save()
{
_app.Database.Save();
_database.Save();
}
public void SortEntries()
{
var comparer = new PwEntryComparer(PwDefs.TitleField, true, false);
try
{
_pwGroup.Entries.Sort(comparer);
Entries = new ObservableCollection<EntryVm>(Entries.OrderBy(e => e.Name));
}
catch (Exception e)
{
MessageDialogHelper.ShowErrorDialog(e);
}
}
public void SortGroups()
{
try
{
_pwGroup.SortSubGroups(false);
Groups = new ObservableCollection<GroupVm>(Groups.OrderBy(g => g.Name).ThenBy(g => g._pwGroup == null));
OnPropertyChanged("Groups");
}
catch (Exception e)
{
MessageDialogHelper.ShowErrorDialog(e);
}
}
public override string ToString()

View File

@@ -11,7 +11,7 @@ namespace ModernKeePass.ViewModels
public string Title { get; set; }
public int Group { get; set; } = 0;
public string Group { get; set; } = "_";
public Type PageType { get; set; }
public Symbol SymbolIcon { get; set; }
public bool IsEnabled { get; set; } = true;

View File

@@ -1,22 +1,15 @@
using Windows.Storage;
using ModernKeePass.Common;
using Windows.Storage.AccessCache;
using Windows.UI.Xaml;
using ModernKeePass.Interfaces;
using ModernKeePass.Services;
namespace ModernKeePass.ViewModels
{
public class RecentItemVm: NotifyPropertyChangedBase, ISelectableModel
public class RecentItemVm: NotifyPropertyChangedBase, ISelectableModel, IRecentItem
{
private bool _isSelected;
public RecentItemVm() {}
public RecentItemVm(AccessListEntry entry, StorageFile file)
{
Token = entry.Token;
Name = entry.Metadata;
DatabaseFile = file;
}
public StorageFile DatabaseFile { get; }
public string Token { get; }
public string Name { get; }
@@ -27,5 +20,33 @@ namespace ModernKeePass.ViewModels
get { return _isSelected; }
set { SetProperty(ref _isSelected, value); }
}
public RecentItemVm() {}
public RecentItemVm(string token, string metadata, IStorageItem file)
{
Token = token;
Name = metadata;
DatabaseFile = file as StorageFile;
}
public void OpenDatabaseFile()
{
OpenDatabaseFile((Application.Current as App)?.Database);
}
public void OpenDatabaseFile(IDatabase database)
{
database.DatabaseFile = DatabaseFile;
}
public void UpdateAccessTime()
{
UpdateAccessTime(new RecentService());
}
public async void UpdateAccessTime(IRecent recent)
{
await recent.GetFileAsync(Token);
}
}
}

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Windows.Storage;
using Windows.UI.Xaml;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
@@ -12,19 +11,19 @@ using ModernKeePassLib.Cryptography.KeyDerivation;
namespace ModernKeePass.ViewModels
{
// TODO: implement Kdf settings
public class SettingsDatabaseVm: NotifyPropertyChangedBase, IHasSelectableObject
{
private readonly App _app = Application.Current as App;
private readonly ApplicationDataContainer _localSettings = ApplicationData.Current.LocalSettings;
private readonly IDatabase _database;
private GroupVm _selectedItem;
public bool HasRecycleBin
{
get { return _app.Database.RecycleBinEnabled; }
get { return _database.RecycleBinEnabled; }
set
{
_app.Database.RecycleBinEnabled = value;
OnPropertyChanged();
_database.RecycleBinEnabled = value;
OnPropertyChanged("HasRecycleBin");
}
}
@@ -47,26 +46,26 @@ namespace ModernKeePass.ViewModels
{
for (var inx = 0; inx < CipherPool.GlobalPool.EngineCount; ++inx)
{
if (CipherPool.GlobalPool[inx].CipherUuid.Equals(_app.Database.DataCipher)) return inx;
if (CipherPool.GlobalPool[inx].CipherUuid.Equals(_database.DataCipher)) return inx;
}
return -1;
}
set { _app.Database.DataCipher = CipherPool.GlobalPool[value].CipherUuid; }
set { _database.DataCipher = CipherPool.GlobalPool[value].CipherUuid; }
}
public IEnumerable<string> Compressions => Enum.GetNames(typeof(PwCompressionAlgorithm)).Take((int)PwCompressionAlgorithm.Count);
public string CompressionName
{
get { return Enum.GetName(typeof(PwCompressionAlgorithm), _app.Database.CompressionAlgorithm); }
set { _app.Database.CompressionAlgorithm = (PwCompressionAlgorithm)Enum.Parse(typeof(PwCompressionAlgorithm), value); }
get { return Enum.GetName(typeof(PwCompressionAlgorithm), _database.CompressionAlgorithm); }
set { _database.CompressionAlgorithm = (PwCompressionAlgorithm)Enum.Parse(typeof(PwCompressionAlgorithm), value); }
}
public IEnumerable<string> KeyDerivations => KdfPool.Engines.Select(e => e.Name);
public string KeyDerivationName
{
get { return KdfPool.Get(_app.Database.KeyDerivation.KdfUuid).Name; }
set { _app.Database.KeyDerivation = KdfPool.Engines.FirstOrDefault(e => e.Name == value)?.GetDefaultParameters(); }
get { return KdfPool.Get(_database.KeyDerivation.KdfUuid).Name; }
set { _database.KeyDerivation = KdfPool.Engines.FirstOrDefault(e => e.Name == value)?.GetDefaultParameters(); }
}
public ISelectableModel SelectedItem
@@ -89,22 +88,12 @@ namespace ModernKeePass.ViewModels
}
}
public SettingsDatabaseVm()
{
Groups = _app?.Database.RootGroup.Groups;
}
public SettingsDatabaseVm() : this((Application.Current as App)?.Database) { }
// TODO: Move to another setting class (or a static class)
private T GetSetting<T>(string property)
public SettingsDatabaseVm(IDatabase database)
{
try
{
return (T) Convert.ChangeType(_localSettings.Values[property], typeof(T));
}
catch (InvalidCastException)
{
return default(T);
}
_database = database;
Groups = _database?.RootGroup.Groups;
}
}
}

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using ModernKeePass.Interfaces;
using ModernKeePass.Services;
namespace ModernKeePass.ViewModels
{
public class SettingsNewVm
{
private ISettings _settings;
public SettingsNewVm() : this(new SettingsService())
{ }
public SettingsNewVm(ISettings settings)
{
_settings = settings;
}
public bool IsCreateSample
{
get { return _settings.GetSetting<bool>("Sample"); }
set { _settings.PutSetting("Sample", value); }
}
public IEnumerable<string> FileFormats => new []{"2", "4"};
public string FileFormatVersion
{
get { return _settings.GetSetting<string>("DefaultFileFormat"); }
set { _settings.PutSetting("DefaultFileFormat", value); }
}
}
}

View File

@@ -1,23 +1,23 @@
using System.Collections.ObjectModel;
using System.Linq;
using Windows.ApplicationModel;
using Windows.Storage.AccessCache;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.Pages;
using ModernKeePass.Services;
using ModernKeePass.Views;
namespace ModernKeePass.ViewModels
{
public class MainVm : NotifyPropertyChangedBase, IHasSelectableObject
{
private IOrderedEnumerable<IGrouping<int, MainMenuItemVm>> _mainMenuItems;
private IOrderedEnumerable<IGrouping<string, MainMenuItemVm>> _mainMenuItems;
private MainMenuItemVm _selectedItem;
public string Name { get; } = Package.Current.DisplayName;
public IOrderedEnumerable<IGrouping<int, MainMenuItemVm>> MainMenuItems
public IOrderedEnumerable<IGrouping<string, MainMenuItemVm>> MainMenuItems
{
get { return _mainMenuItems; }
set { SetProperty(ref _mainMenuItems, value); }
@@ -45,47 +45,89 @@ namespace ModernKeePass.ViewModels
public MainVm() {}
public MainVm(Frame referenceFrame, Frame destinationFrame)
internal MainVm(Frame referenceFrame, Frame destinationFrame) : this(referenceFrame, destinationFrame,
(Application.Current as App)?.Database, new ResourcesService(), new RecentService())
{ }
public MainVm(Frame referenceFrame, Frame destinationFrame, IDatabase database, IResource resource, IRecent recent)
{
var app = (App)Application.Current;
var mru = StorageApplicationPermissions.MostRecentlyUsedList;
var isDatabaseOpen = app.Database != null && app.Database.Status == DatabaseHelper.DatabaseStatus.Opened;
var isDatabaseOpen = database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opened;
var mainMenuItems = new ObservableCollection<MainMenuItemVm>
{
new MainMenuItemVm
{
Title = "Open", PageType = typeof(OpenDatabasePage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Page2,
IsSelected = app.Database.Status == DatabaseHelper.DatabaseStatus.Opening
Title = resource.GetResourceValue("MainMenuItemOpen"),
PageType = typeof(OpenDatabasePage),
Destination = destinationFrame,
Parameter = referenceFrame,
SymbolIcon = Symbol.Page2,
IsSelected = database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opening
},
new MainMenuItemVm
{
Title = "New" , PageType = typeof(NewDatabasePage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Add
Title = resource.GetResourceValue("MainMenuItemNew"),
PageType = typeof(NewDatabasePage),
Destination = destinationFrame,
Parameter = referenceFrame,
SymbolIcon = Symbol.Add
},
new MainMenuItemVm
{
Title = "Save" , PageType = typeof(SaveDatabasePage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Save,
IsSelected = isDatabaseOpen, IsEnabled = isDatabaseOpen
},
new MainMenuItemVm {
Title = "Recent" , PageType = typeof(RecentDatabasesPage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Copy,
IsSelected = (app.Database == null || app.Database.Status == DatabaseHelper.DatabaseStatus.Closed) && mru.Entries.Count > 0, IsEnabled = mru.Entries.Count > 0
Title = resource.GetResourceValue("MainMenuItemSave"),
PageType = typeof(SaveDatabasePage),
Destination = destinationFrame,
Parameter = referenceFrame,
SymbolIcon = Symbol.Save,
IsSelected = isDatabaseOpen,
IsEnabled = isDatabaseOpen
},
new MainMenuItemVm
{
Title = "About" , PageType = typeof(AboutPage), Destination = destinationFrame, SymbolIcon = Symbol.Help
Title = resource.GetResourceValue("MainMenuItemRecent"),
PageType = typeof(RecentDatabasesPage),
Destination = destinationFrame,
Parameter = referenceFrame,
SymbolIcon = Symbol.Copy,
IsSelected =
(database == null || database.Status == (int) DatabaseService.DatabaseStatus.Closed) &&
recent.EntryCount > 0,
IsEnabled = recent.EntryCount > 0
},
new MainMenuItemVm
{
Title = resource.GetResourceValue("MainMenuItemSettings"),
PageType = typeof(SettingsPage),
Destination = referenceFrame,
SymbolIcon = Symbol.Setting
},
new MainMenuItemVm
{
Title = resource.GetResourceValue("MainMenuItemAbout"),
PageType = typeof(AboutPage),
Destination = destinationFrame,
SymbolIcon = Symbol.Help
},
new MainMenuItemVm
{
Title = resource.GetResourceValue("MainMenuItemDonate"),
PageType = typeof(DonatePage),
Destination = destinationFrame,
SymbolIcon = Symbol.Shop
}
};
// Auto-select the Recent Items menu item if the conditions are met
SelectedItem = mainMenuItems.FirstOrDefault(m => m.IsSelected);
if (app.Database != null && app.Database.Status == DatabaseHelper.DatabaseStatus.Opened)
// Add currently opened database to the menu
if (database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opened)
mainMenuItems.Add(new MainMenuItemVm
{
Title = app.Database.Name,
Title = database.Name,
PageType = typeof(GroupDetailPage),
Destination = referenceFrame,
Parameter = app.Database.RootGroup,
Group = 1,
Parameter = database.RootGroup,
Group = "Databases",
SymbolIcon = Symbol.ProtectedDocument
});

View File

@@ -1,50 +1,44 @@
using System.ComponentModel;
using Windows.Storage;
using Windows.Storage.AccessCache;
using Windows.Storage;
using Windows.UI.Xaml;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.Services;
namespace ModernKeePass.ViewModels
{
public class OpenVm: INotifyPropertyChanged
public class OpenVm: NotifyPropertyChangedBase
{
public bool ShowPasswordBox
{
get { return ((App)Application.Current).Database.Status == DatabaseHelper.DatabaseStatus.Opening; }
}
public bool ShowPasswordBox => _database?.Status == (int) DatabaseService.DatabaseStatus.Opening;
public string Name
{
get { return ((App)Application.Current).Database.Name; }
}
public string Name => _database?.Name;
public OpenVm()
{
var app = Application.Current as App;
if (app?.Database == null || app.Database.Status != DatabaseHelper.DatabaseStatus.Opening) return;
OpenFile(app.Database.DatabaseFile);
}
private readonly IDatabase _database;
public event PropertyChangedEventHandler PropertyChanged;
public OpenVm() : this((Application.Current as App)?.Database) { }
protected void NotifyPropertyChanged(string propertyName)
public OpenVm(IDatabase database)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
_database = database;
if (database == null || database.Status != (int) DatabaseService.DatabaseStatus.Opening) return;
OpenFile(database.DatabaseFile);
}
public void OpenFile(StorageFile file)
{
var database = ((App)Application.Current).Database;
database.DatabaseFile = file;
NotifyPropertyChanged("Name");
NotifyPropertyChanged("ShowPasswordBox");
AddToRecentList(file);
OpenFile(file, new RecentService());
}
private void AddToRecentList(StorageFile file)
public void OpenFile(StorageFile file, IRecent recent)
{
var mru = StorageApplicationPermissions.MostRecentlyUsedList;
mru.Add(file, file.DisplayName);
_database.DatabaseFile = file;
OnPropertyChanged("Name");
OnPropertyChanged("ShowPasswordBox");
AddToRecentList(file, recent);
}
private void AddToRecentList(StorageFile file, IRecent recent)
{
recent.Add(file, file.DisplayName);
}
}
}

View File

@@ -1,17 +1,17 @@
using System;
using System.Collections.ObjectModel;
using Windows.Storage.AccessCache;
using System.Collections.ObjectModel;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.Services;
namespace ModernKeePass.ViewModels
{
public class RecentVm : NotifyPropertyChangedBase, IHasSelectableObject
{
private readonly IRecent _recent;
private ISelectableModel _selectedItem;
private ObservableCollection<RecentItemVm> _recentItems = new ObservableCollection<RecentItemVm>();
private ObservableCollection<IRecentItem> _recentItems = new ObservableCollection<IRecentItem>();
public ObservableCollection<RecentItemVm> RecentItems
public ObservableCollection<IRecentItem> RecentItems
{
get { return _recentItems; }
set { SetProperty(ref _recentItems, value); }
@@ -35,25 +35,21 @@ namespace ModernKeePass.ViewModels
}
}
public RecentVm()
public RecentVm() : this (new RecentService())
{ }
public RecentVm(IRecent recent)
{
// TODO: opening the files actually changes the MRU order
var mru = StorageApplicationPermissions.MostRecentlyUsedList;
foreach (var entry in mru.Entries)
{
try
{
var file = mru.GetFileAsync(entry.Token).GetAwaiter().GetResult();
RecentItems.Add(new RecentItemVm(entry, file));
}
catch (Exception)
{
mru.Remove(entry.Token);
}
}
_recent = recent;
RecentItems = _recent.GetAllFiles();
if (RecentItems.Count > 0)
SelectedItem = RecentItems[0];
SelectedItem = RecentItems[0] as RecentItemVm;
}
public void ClearAll()
{
_recent.ClearAll();
RecentItems.Clear();
}
}
}

View File

@@ -1,20 +1,28 @@
using Windows.Storage;
using Windows.UI.Xaml;
using ModernKeePass.Interfaces;
namespace ModernKeePass.ViewModels
{
public class SaveVm
{
public void Save(bool close = true)
private readonly IDatabase _database;
public SaveVm() : this((Application.Current as App)?.Database) { }
public SaveVm(IDatabase database)
{
var app = (App)Application.Current;
if (close && app.Database.Save()) app.Database.Close();
_database = database;
}
internal void Save(StorageFile file)
public void Save(bool close = true)
{
var app = (App)Application.Current;
app.Database.Save(file);
_database.Save();
if (close) _database.Close();
}
public void Save(StorageFile file)
{
_database.Save(file);
}
}
}

View File

@@ -1,17 +1,26 @@
using System.Collections.ObjectModel;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.Pages;
using ModernKeePass.Views;
using ModernKeePass.Services;
namespace ModernKeePass.ViewModels
{
public class SettingsVM : NotifyPropertyChangedBase, IHasSelectableObject
public class SettingsVm : NotifyPropertyChangedBase, IHasSelectableObject
{
private ListMenuItemVm _selectedItem;
public ObservableCollection<ListMenuItemVm> MenuItems { get; set; }
private IOrderedEnumerable<IGrouping<string, ListMenuItemVm>> _menuItems;
public IOrderedEnumerable<IGrouping<string, ListMenuItemVm>> MenuItems
{
get { return _menuItems; }
set { SetProperty(ref _menuItems, value); }
}
public ISelectableModel SelectedItem
{
get { return _selectedItem; }
@@ -32,15 +41,40 @@ namespace ModernKeePass.ViewModels
}
}
public SettingsVM()
public SettingsVm() : this((Application.Current as App)?.Database, new ResourcesService()) { }
public SettingsVm(IDatabase database, IResource resource)
{
MenuItems = new ObservableCollection<ListMenuItemVm>
var menuItems = new ObservableCollection<ListMenuItemVm>
{
new ListMenuItemVm { Title = "Database", SymbolIcon = Symbol.Setting, PageType = typeof(SettingsDatabasePage), IsSelected = true },
new ListMenuItemVm { Title = "Security", SymbolIcon = Symbol.Permissions, PageType = typeof(SettingsSecurityPage) },
//new ListMenuItemVm { Title = "General", SymbolIcon = Symbol.Edit, PageType = typeof(SettingsGeneralPage) }
new ListMenuItemVm
{
Title = resource.GetResourceValue("SettingsMenuItemNew"),
Group = resource.GetResourceValue("SettingsMenuGroupApplication"),
SymbolIcon = Symbol.Add,
PageType = typeof(SettingsNewDatabasePage),
IsSelected = true
},
new ListMenuItemVm
{
Title = resource.GetResourceValue("SettingsMenuItemGeneral"),
Group = resource.GetResourceValue("SettingsMenuGroupDatabase"),
SymbolIcon = Symbol.Setting,
PageType = typeof(SettingsDatabasePage),
IsEnabled = database?.Status == 2
},
new ListMenuItemVm
{
Title = resource.GetResourceValue("SettingsMenuItemSecurity"),
Group = resource.GetResourceValue("SettingsMenuGroupDatabase"),
SymbolIcon = Symbol.Permissions,
PageType = typeof(SettingsSecurityPage),
IsEnabled = database?.Status == 2
}
};
SelectedItem = MenuItems.FirstOrDefault(m => m.IsSelected);
SelectedItem = menuItems.FirstOrDefault(m => m.IsSelected);
MenuItems = from item in menuItems group item by item.Group into grp orderby grp.Key select grp;
}
}
}

View File

@@ -6,7 +6,7 @@ using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Pages.BasePages
namespace ModernKeePass.Views.BasePages
{
public class LayoutAwarePageBase: Page
{
@@ -69,17 +69,17 @@ namespace ModernKeePass.Pages.BasePages
{
// When this is a new page, select the first item automatically unless logical page
// navigation is being used (see the logical page navigation #region below.)
if (!UsingLogicalPageNavigation() && ListViewSource.View != null)
if (!UsingLogicalPageNavigation())
{
ListViewSource.View.MoveCurrentToFirst();
ListViewSource.View?.MoveCurrentToFirst();
}
}
else
{
// Restore the previously saved state associated with this page
if (e.PageState.ContainsKey("SelectedItem") && ListViewSource.View != null)
if (e.PageState.ContainsKey("SelectedItem"))
{
ListViewSource.View.MoveCurrentTo(e.PageState["SelectedItem"]);
ListViewSource.View?.MoveCurrentTo(e.PageState["SelectedItem"]);
}
}
}

View File

@@ -10,8 +10,9 @@
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:ModernKeePass.Actions"
x:Name="PageRoot"
x:Class="ModernKeePass.Pages.EntryDetailPage"
mc:Ignorable="d">
x:Class="ModernKeePass.Views.EntryDetailPage"
mc:Ignorable="d"
SizeChanged="EntryDetailPage_OnSizeChanged">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
@@ -337,21 +338,21 @@
</interactivity:Interaction.Behaviors>
<StackPanel>
<TextBlock>
<Run Text="Password Length: "/>
<Run x:Uid="PasswordGeneratorLength" />
<Run Text="{Binding PasswordLength}" />
</TextBlock>
<Slider Value="{Binding PasswordLength, Mode=TwoWay}" Margin="0,-10,0,-20" />
<CheckBox IsChecked="{Binding UpperCasePatternSelected, Mode=TwoWay}" Content="Upper case (A, B, C, ...)"/>
<CheckBox IsChecked="{Binding LowerCasePatternSelected, Mode=TwoWay}" Content="Lower case (a, b, c, ...)"/>
<CheckBox IsChecked="{Binding DigitsPatternSelected, Mode=TwoWay}" Content="Digits (0, 1, 2, ...)"/>
<CheckBox IsChecked="{Binding MinusPatternSelected, Mode=TwoWay}" Content="Minus (-)"/>
<CheckBox IsChecked="{Binding UnderscorePatternSelected, Mode=TwoWay}" Content="Underscore (_)"/>
<CheckBox IsChecked="{Binding SpacePatternSelected, Mode=TwoWay}" Content="Space ( )"/>
<CheckBox IsChecked="{Binding SpecialPatternSelected, Mode=TwoWay}" Content="Special (!, $, %, ...)"/>
<CheckBox IsChecked="{Binding BracketsPatternSelected, Mode=TwoWay}" Content="Brackets ([], {}, (), ...)"/>
<TextBlock Text="Also add these characters:" Margin="0,5,0,0"/>
<CheckBox IsChecked="{Binding UpperCasePatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorUpper" />
<CheckBox IsChecked="{Binding LowerCasePatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorLower" />
<CheckBox IsChecked="{Binding DigitsPatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorDigits" />
<CheckBox IsChecked="{Binding MinusPatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorMinus" />
<CheckBox IsChecked="{Binding UnderscorePatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorUnderscore" />
<CheckBox IsChecked="{Binding SpacePatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorSpace" />
<CheckBox IsChecked="{Binding SpecialPatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorSpecial" />
<CheckBox IsChecked="{Binding BracketsPatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorBrackets" />
<TextBlock x:Uid="PasswordGeneratorAlso" Margin="0,5,0,0"/>
<TextBox Text="{Binding CustomChars, Mode=TwoWay}" />
<Button Content="Generate">
<Button x:Uid="PasswordGeneratorButton">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction TargetObject="{Binding}" MethodName="GeneratePassword"/>
@@ -361,6 +362,9 @@
</StackPanel>
</Flyout>
</Button.Flyout>
<ToolTipService.ToolTip>
<ToolTip x:Uid="PasswordGeneratorTooltip" />
</ToolTipService.ToolTip>
</Button>
</Grid>
</ControlTemplate>
@@ -374,7 +378,14 @@
<Page.BottomAppBar>
<CommandBar x:Name="CommandBar" VerticalAlignment="Center">
<CommandBar.SecondaryCommands>
<AppBarButton Icon="Save" Label="Save">
<AppBarButton Icon="Home" x:Uid="AppBarHome">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.MainPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Save" x:Uid="AppBarSave">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction TargetObject="{Binding}" MethodName="Save"/>
@@ -382,29 +393,29 @@
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Setting" Label="Settings">
<AppBarButton Icon="Setting" x:Uid="AppBarSettings">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Pages.SettingsPage" />
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.SettingsPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
</CommandBar.SecondaryCommands>
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
<AppBarToggleButton Icon="Edit" x:Uid="AppBarEdit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarToggleButton>
<AppBarButton Icon="Undo" Label="Restore" Visibility="{Binding ParentGroup.IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding PreviousGroup, Converter={StaticResource NullToBooleanConverter}}" Click="RestoreButton_Click">
<AppBarButton Icon="Undo" x:Uid="AppBarRestore" Visibility="{Binding ParentGroup.IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding PreviousGroup, Converter={StaticResource NullToBooleanConverter}}" Click="RestoreButton_Click">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction MethodName="UndoDelete" TargetObject="{Binding}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Delete" Label="Delete" Click="DeleteButton_Click" />
<AppBarButton Icon="Delete" x:Uid="AppBarDelete" Click="DeleteButton_Click" />
</CommandBar>
</Page.BottomAppBar>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
@@ -418,7 +429,7 @@
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="stackPanel" Grid.Row="1" Margin="20,0,0,0" >
<StackPanel Grid.Row="1" Margin="20,0,0,0" >
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="0,20,0,0"/>
@@ -427,17 +438,17 @@
<Setter Property="Margin" Value="0,20,0,0"/>
</Style>
</StackPanel.Resources>
<TextBlock TextWrapping="Wrap" Text="User name or login" FontSize="18"/>
<local:TextBoxWithButton HorizontalAlignment="Left" Text="{Binding UserName, Mode=TwoWay}" Width="350" Height="32" Style="{StaticResource TextBoxWithButtonStyle}" ButtonSymbol="&#xE16F;">
<TextBlock x:Uid="EntryLogin" TextWrapping="Wrap" FontSize="18"/>
<local:TextBoxWithButton HorizontalAlignment="Left" Text="{Binding UserName, Mode=TwoWay}" Width="350" Height="32" Style="{StaticResource TextBoxWithButtonStyle}" ButtonSymbol="&#xE16F;" ButtonTooltip="Copy">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{Binding UserName}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</local:TextBoxWithButton>
<TextBlock TextWrapping="Wrap" Text="Password" FontSize="18"/>
<TextBlock x:Uid="EntryPassword" TextWrapping="Wrap" FontSize="18"/>
<PasswordBox HorizontalAlignment="Left" Password="{Binding Password, Mode=TwoWay}" Width="350" Height="32" IsPasswordRevealButtonEnabled="True" Visibility="{Binding IsRevealPassword, Converter={StaticResource InverseBooleanToVisibilityConverter}}" Style="{StaticResource PasswordBoxWithButtonStyle}" />
<local:TextBoxWithButton HorizontalAlignment="Left" Text="{Binding Password, Mode=TwoWay}" Width="350" Height="32" Visibility="{Binding IsRevealPassword, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource TextBoxWithButtonStyle}" ButtonSymbol="&#xE16F;">
<local:TextBoxWithButton HorizontalAlignment="Left" Text="{Binding Password, Mode=TwoWay}" Width="350" Height="32" Visibility="{Binding IsRevealPassword, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource TextBoxWithButtonStyle}" ButtonSymbol="&#xE16F;" ButtonTooltip="Copy">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{Binding Password}" />
@@ -445,21 +456,27 @@
</interactivity:Interaction.Behaviors>
</local:TextBoxWithButton>
<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 HorizontalAlignment="Left" Margin="-3,0,0,0" Content="Show password" 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"/>
<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;" />
<TextBlock TextWrapping="Wrap" Text="Notes" 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" />
<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" />
<CheckBox FontSize="18" IsChecked="{Binding HasExpirationDate, Mode=TwoWay}" Content="Expiration date"/>
<StackPanel Orientation="Horizontal" IsHitTestVisible="{Binding HasExpirationDate}">
<SymbolIcon Symbol="Important" Foreground="DarkRed" Visibility="{Binding HasExpired, Converter={StaticResource BooleanToVisibilityConverter}}">
<CheckBox x:Uid="EntryExpirationDate" FontSize="18" IsChecked="{Binding HasExpirationDate, Mode=TwoWay}" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<SymbolIcon Grid.Column="0" Symbol="Important" Foreground="DarkRed" Visibility="{Binding HasExpired, Converter={StaticResource BooleanToVisibilityConverter}}">
<ToolTipService.ToolTip>
<ToolTip Content="Password has expired" />
<ToolTip x:Uid="EntryExpirationTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<StackPanel Grid.Column="1" x:Name="ExpirationDatePanel" Orientation="Horizontal" Visibility="{Binding HasExpirationDate, Converter={StaticResource BooleanToVisibilityConverter}}">
<DatePicker Margin="0,0,20,0" Date="{Binding ExpiryDate, Mode=TwoWay}" />
<TimePicker Time="{Binding ExpiryTime, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</StackPanel>
<!-- Bouton Précédent et titre de la page -->
@@ -467,14 +484,11 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="400"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
x:Name="BackButton"
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Height="50"
Width="50"
VerticalAlignment="Center"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
@@ -503,5 +517,23 @@
<TextBlock FontSize="12" Text="{Binding Path}" />
</StackPanel>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Large">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ExpirationDatePanel" Storyboard.TargetProperty="Orientation">
<DiscreteObjectKeyFrame KeyTime="0" Value="Horizontal"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Small">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ExpirationDatePanel" Storyboard.TargetProperty="Orientation">
<DiscreteObjectKeyFrame KeyTime="0" Value="Vertical"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</Page>

View File

@@ -1,14 +1,14 @@
using System;
using Windows.ApplicationModel.DataTransfer;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.Services;
using ModernKeePass.ViewModels;
// Pour en savoir plus sur le modèle d'élément Page Détail de l'élément, consultez la page http://go.microsoft.com/fwlink/?LinkId=234232
namespace ModernKeePass.Pages
namespace ModernKeePass.Views
{
/// <summary>
/// Page affichant les détails d'un élément au sein d'un groupe, offrant la possibilité de
@@ -71,14 +71,16 @@ namespace ModernKeePass.Pages
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
var isRecycleBinEnabled = ((App)Application.Current).Database.RecycleBinEnabled && !Model.ParentGroup.IsSelected;
var message = isRecycleBinEnabled
? "Are you sure you want to send this entry to the recycle bin?"
: "Are you sure you want to delete this entry?";
var text = isRecycleBinEnabled ? "Item moved to the Recycle bin" : "Item permanently removed";
MessageDialogHelper.ShowDeleteConfirmationDialog("Delete", message, a =>
var resource = new ResourcesService();
var message = Model.IsRecycleOnDelete
? resource.GetResourceValue("EntryRecyclingConfirmation")
: resource.GetResourceValue("EntryDeletingConfirmation");
var text = Model.IsRecycleOnDelete ? resource.GetResourceValue("EntryRecycled") : resource.GetResourceValue("EntryDeleted");
MessageDialogHelper.ShowActionDialog(resource.GetResourceValue("EntityDeleteTitle"), message,
resource.GetResourceValue("EntityDeleteActionButton"),
resource.GetResourceValue("EntityDeleteCancelButton"), a =>
{
ToastNotificationHelper.ShowMovedToast(Model, "Deleting", text);
ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityDeleting"), text);
Model.MarkForDelete();
if (Frame.CanGoBack) Frame.GoBack();
});
@@ -86,7 +88,8 @@ namespace ModernKeePass.Pages
private void RestoreButton_Click(object sender, RoutedEventArgs e)
{
ToastNotificationHelper.ShowMovedToast(Model, "Restored", "Item returned to its original group");
var resource = new ResourcesService();
ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityRestoredTitle"), resource.GetResourceValue("EntryRestored"));
if (Frame.CanGoBack) Frame.GoBack();
}
@@ -97,10 +100,15 @@ namespace ModernKeePass.Pages
var uri = new Uri(UrlTextBox.Text);
await Windows.System.Launcher.LaunchUriAsync(uri);
}
catch
catch (Exception ex)
{
// TODO: Show some error
}
MessageDialogHelper.ShowErrorDialog(ex);
}
}
private void EntryDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
VisualStateManager.GoToState(this, e.NewSize.Width < 700 ? "Small" : "Large", true);
}
}
}

View File

@@ -0,0 +1,393 @@
<Page
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"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:ModernKeePass.Actions"
xmlns:controls="using:ModernKeePass.Controls"
xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors"
x:Name="PageRoot"
x:Class="ModernKeePass.Views.GroupDetailPage"
mc:Ignorable="d"
SizeChanged="GroupDetailPage_OnSizeChanged">
<Page.Resources>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<converters:BooleanToFontStyleConverter x:Key="BooleanToFontStyleConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<converters:NullToBooleanConverter x:Key="NullToBooleanConverter"/>
</Page.Resources>
<Page.DataContext>
<viewModels:GroupVm />
</Page.DataContext>
<Page.BottomAppBar>
<CommandBar x:Name="CommandBar" VerticalAlignment="Center">
<CommandBar.SecondaryCommands>
<AppBarButton Icon="Home" x:Uid="AppBarHome">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.MainPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Save" x:Uid="AppBarSave">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction TargetObject="{Binding}" MethodName="Save"/>
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Setting" x:Uid="AppBarSettings">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.SettingsPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
</CommandBar.SecondaryCommands>
<AppBarButton Icon="Sort" x:Uid="AppBarSort" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}">
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="AppBarSortEntries">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction MethodName="SortEntries" TargetObject="{Binding}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="AppBarSortGroups">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction MethodName="SortGroups" TargetObject="{Binding}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
</MenuFlyout>
</Button.Flyout>
</AppBarButton>
<AppBarToggleButton Icon="Edit" x:Uid="AppBarEdit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarToggleButton>
<AppBarButton Icon="Undo" x:Uid="AppBarRestore" Visibility="{Binding ShowRestore, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding PreviousGroup, Converter={StaticResource NullToBooleanConverter}}" Click="RestoreButton_Click">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction MethodName="UndoDelete" TargetObject="{Binding}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Delete" x:Uid="AppBarDelete" IsEnabled="{Binding IsNotRoot}" Click="DeleteButton_Click" />
</CommandBar>
</Page.BottomAppBar>
<Grid>
<Grid.Resources>
<CollectionViewSource
x:Name="GroupsViewSource"
Source="{Binding Groups}" />
<CollectionViewSource
x:Name="EntriesViewSource"
Source="{Binding Entries}" />
<CollectionViewSource
x:Name="EntriesZoomedOutViewSource"
Source="{Binding EntriesZoomedOut}" IsSourceGrouped="True"/>
</Grid.Resources>
<Grid.Background>
<StaticResource ResourceKey="ApplicationPageBackgroundThemeBrush"/>
</Grid.Background>
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" x:Name="LeftListViewColumn" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListView
Grid.Column="0"
x:Name="LeftListView"
SelectionChanged="groups_SelectionChanged"
IsSwipeEnabled="false"
IsSynchronizedWithCurrentItem="False"
DataContext="{Binding DataContext, ElementName=PageRoot}"
RequestedTheme="Dark"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}">
<ListView.Resources>
<DataTemplate x:Name="GroupOtherItem">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding Name}" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock Text="{Binding Name}" x:Name="GroupTextBlock" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" FontStyle="{Binding IsSelected, Converter={StaticResource BooleanToFontStyleConverter}}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Name="GroupFirstItem">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0">
<ToolTipService.ToolTip>
<ToolTip x:Uid="GroupNewItemTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock x:Name="GroupTextBlock" x:Uid="GroupNewItemTextBox" FontWeight="SemiBold" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" />
</StackPanel>
</DataTemplate>
</ListView.Resources>
<ListView.ItemsSource>
<Binding Source="{StaticResource GroupsViewSource}"/>
</ListView.ItemsSource>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="AllowDrop" Value="True"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.HeaderTemplate>
<DataTemplate>
<ToggleButton Style="{StaticResource HamburgerToggleButton}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked">
<core:ChangePropertyAction PropertyName="Width" Value="Auto" TargetObject="{Binding ElementName=LeftListViewColumn}"/>
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<core:ChangePropertyAction PropertyName="Width" Value="50" TargetObject="{Binding ElementName=LeftListViewColumn}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ToggleButton>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplateSelector>
<templateSelectors:FirstItemDataTemplateSelector
FirstItem="{StaticResource GroupFirstItem}"
OtherItem="{StaticResource GroupOtherItem}" />
</ListView.ItemTemplateSelector>
</ListView>
<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">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Add"></SymbolIcon>
<TextBlock x:Uid="GroupCreateEntry" VerticalAlignment="Center" Margin="10,0,0,0" />
</StackPanel>
</HyperlinkButton>
<SemanticZoom Grid.Column="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" Margin="20,60,0,0">
<SemanticZoom.ZoomedInView>
<!-- Horizontal scrolling grid -->
<GridView
x:Name="GridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Entries"
TabIndex="1"
SelectionChanged="entries_SelectionChanged"
IsSynchronizedWithCurrentItem="False"
BorderBrush="{StaticResource ListViewItemSelectedBackgroundThemeBrush}"
AllowDrop="True"
CanReorderItems="True"
CanDragItems="True"
DragItemsStarting="GridView_DragItemsStarting">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="False">
<actions:SetupFocusAction TargetObject="{Binding ElementName=GridView}" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
<GridView.ItemTemplate>
<DataTemplate>
<Grid Height="110" Width="480" x:Name="EntryGrid" Background="{Binding BackgroundColor, ConverterParameter={StaticResource AppBarBackgroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<SymbolIcon Grid.Column="0" Symbol="{Binding IconSymbol}" Width="100" Height="100" RenderTransformOrigin="0.5,0.5" >
<SymbolIcon.RenderTransform>
<CompositeTransform ScaleX="2" TranslateX="0" TranslateY="0" ScaleY="2"/>
</SymbolIcon.RenderTransform>
</SymbolIcon>
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,10,0,0" >
<TextBlock x:Name="NameTextBlock" Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
<TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
<TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsSource>
<Binding Source="{StaticResource EntriesViewSource}"/>
</GridView.ItemsSource>
<GridView.DataContext>
<viewModels:EntryVm/>
</GridView.DataContext>
<GridView.ItemContainerStyle>
<Style TargetType = "GridViewItem" >
<Setter Property = "Template" >
<Setter.Value>
<ControlTemplate TargetType = "GridViewItem" >
<GridViewItemPresenter
SelectionCheckMarkVisualEnabled="True"
CheckHintBrush="{ThemeResource ListViewItemCheckHintThemeBrush}"
CheckSelectingBrush="{ThemeResource ListViewItemCheckSelectingThemeBrush}"
CheckBrush="{ThemeResource ListViewItemCheckThemeBrush}"
DragBackground="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}"
DragForeground="{ThemeResource ListViewItemDragForegroundThemeBrush}"
FocusBorderBrush="{ThemeResource ListViewItemFocusBorderThemeBrush}"
PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}"
PointerOverBackground="{ThemeResource ListViewItemPointerOverBackgroundThemeBrush}"
SelectedBorderThickness="{ThemeResource GridViewItemCompactSelectedBorderThemeThickness}"
SelectedBackground="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}"
SelectedForeground="{ThemeResource ListViewItemSelectedForegroundThemeBrush}"
SelectedPointerOverBackground="{ThemeResource ListViewItemSelectedPointerOverBackgroundThemeBrush}"
SelectedPointerOverBorderBrush="{ThemeResource ListViewItemSelectedPointerOverBorderThemeBrush}"
DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
PointerOverBackgroundMargin="1"
ContentMargin="4" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GridView.ItemContainerStyle>
</GridView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<ListView
ItemsSource="{Binding Source={StaticResource EntriesZoomedOutViewSource}}"
IsSwipeEnabled="false"
SelectionChanged="groups_SelectionChanged"
IsSynchronizedWithCurrentItem="False">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="LightGray" Margin="0,0,10,0" HorizontalAlignment="Left">
<TextBlock Text="{Binding Key}" Width="40" Foreground="Black" Margin="30" Style="{StaticResource HeaderTextBlockStyle}"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</Grid>
<!-- Back button and page title -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Height="50"
Width="50"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<StackPanel Grid.Column="1" >
<TextBox
x:Name="TitleTextBox"
Text="{Binding Name, Mode=TwoWay}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
Background="Transparent"
IsHitTestVisible="{Binding IsEditMode}"
BorderThickness="0"
FontSize="20"
FontWeight="SemiBold"
TextWrapping="NoWrap"
VerticalAlignment="Center"
x:Uid="GroupTitle">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="True">
<actions:SetupFocusAction TargetObject="{Binding ElementName=TitleTextBox}" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<TextBlock FontSize="12" Text="{Binding Path}" />
</StackPanel>
<Button Grid.Column="2" x:Name="SearchButton" Style="{StaticResource NoBorderButtonStyle}" Height="50">
<SymbolIcon Symbol="Find" />
<Button.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" />-->
<SearchBox x:Uid="GroupSearch" Width="350" Background="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" BorderThickness="0" FontSize="18" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" VerticalContentAlignment="Center" />
</Flyout>
</Button.Flyout>
</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" />-->
<SearchBox Grid.Column="2" x:Uid="GroupSearch" x:Name="SearchBox" Width="350" Background="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" BorderThickness="0" FontSize="18" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" VerticalContentAlignment="Center" />
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DragDropGroup">
<VisualState x:Name="Dragging">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GridView" Storyboard.TargetProperty="BorderThickness">
<DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Dropped">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GridView" Storyboard.TargetProperty="BorderThickness">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SearchGroup">
<VisualState x:Name="Small">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchBox" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Large">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchBox" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</Page>

View File

@@ -1,16 +1,18 @@
using System;
using System.Linq;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.Events;
using ModernKeePass.Services;
using ModernKeePass.ViewModels;
// The Group Detail Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234229
namespace ModernKeePass.Pages
namespace ModernKeePass.Views
{
/// <summary>
/// A page that displays an overview of a single group, including a preview of the items
@@ -99,9 +101,6 @@ namespace ModernKeePass.Pages
{
case -1:
return;
case 0:
entry = Model.AddNewEntry();
break;
default:
entry = GridView.SelectedItem as EntryVm;
break;
@@ -111,14 +110,16 @@ namespace ModernKeePass.Pages
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
var isRecycleBinEnabled = ((App)Application.Current).Database.RecycleBinEnabled && !Model.IsSelected && !Model.ParentGroup.IsSelected;
var message = isRecycleBinEnabled
? "Are you sure you want to send the whole group and all its entries to the recycle bin?"
: "Are you sure you want to delete the whole group and all its entries?";
var text = isRecycleBinEnabled ? "Item moved to the Recycle bin" : "Item permanently removed";
MessageDialogHelper.ShowDeleteConfirmationDialog("Delete", message, a =>
var resource = new ResourcesService();
var message = Model.IsRecycleOnDelete
? resource.GetResourceValue("GroupRecyclingConfirmation")
: resource.GetResourceValue("GroupDeletingConfirmation");
var text = Model.IsRecycleOnDelete ? resource.GetResourceValue("GroupRecycled") : resource.GetResourceValue("GroupDeleted");
MessageDialogHelper.ShowActionDialog(resource.GetResourceValue("EntityDeleteTitle"), message,
resource.GetResourceValue("EntityDeleteActionButton"),
resource.GetResourceValue("EntityDeleteCancelButton"), a =>
{
ToastNotificationHelper.ShowMovedToast(Model, "Deleting", text);
ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityDeleting"), text);
Model.MarkForDelete();
if (Frame.CanGoBack) Frame.GoBack();
});
@@ -126,7 +127,9 @@ namespace ModernKeePass.Pages
private void RestoreButton_Click(object sender, RoutedEventArgs e)
{
ToastNotificationHelper.ShowMovedToast(Model, "Restored", "Item returned to its original group");
var resource = new ResourcesService();
ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityRestoredTitle"),
resource.GetResourceValue("GroupRestored"));
if (Frame.CanGoBack) Frame.GoBack();
}
@@ -138,11 +141,21 @@ namespace ModernKeePass.Pages
e.DestinationItem.Item = e.SourceItem.Item;
}
}
private void CreateEntry_ButtonClick(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(EntryDetailPage), Model.AddNewEntry());
}
private void GridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
e.Cancel = !Model.IsEditMode;
e.Data.RequestedOperation = DataPackageOperation.Move;
}
private void SearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
var imageUri = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx://Assets/Logo.scale-80.png"));
var results = Model.Entries.Skip(1).Where(e => e.Name.IndexOf(args.QueryText, StringComparison.OrdinalIgnoreCase) >= 0).Take(5);
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);
@@ -151,10 +164,16 @@ namespace ModernKeePass.Pages
private void SearchBox_OnResultSuggestionChosen(SearchBox sender, SearchBoxResultSuggestionChosenEventArgs args)
{
var entry = Model.Entries.Skip(1).FirstOrDefault(e => e.Id == args.Tag);
var entry = Model.Entries.FirstOrDefault(e => e.Id == args.Tag);
Frame.Navigate(typeof(EntryDetailPage), entry);
}
private void GroupDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
VisualStateManager.GoToState(this, e.NewSize.Width < 700 ? "Small" : "Large", true);
}
#endregion
}
}

View File

@@ -5,8 +5,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:controls="using:ModernKeePass.Controls"
xmlns:basePages="using:ModernKeePass.Pages.BasePages"
x:Class="ModernKeePass.Pages.MainPage"
xmlns:basePages="using:ModernKeePass.Views.BasePages"
x:Class="ModernKeePass.Views.MainPage"
x:Name="PageRoot"
mc:Ignorable="d">
<Page.Resources>
@@ -61,11 +61,10 @@
Grid.Column="0"
Grid.Row="1"
x:Name="MenuListView"
SelectionChanged="MenuListView_SelectionChanged"
SelectionChanged="ListView_SelectionChanged"
Background="{ThemeResource AppBarBackgroundThemeBrush}"
ItemsSource="{Binding Source={StaticResource MenuItemsSource}}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="False"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<controls:ListViewWithDisable.ItemTemplate>
<DataTemplate>

View File

@@ -4,7 +4,7 @@ using ModernKeePass.ViewModels;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Pages
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
@@ -20,18 +20,18 @@ namespace ModernKeePass.Pages
ListViewSource = MenuItemsSource;
}
private void MenuListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
private new void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView_SelectionChanged(sender, e);
base.ListView_SelectionChanged(sender, e);
var selectedItem = Model.SelectedItem as MainMenuItemVm;
selectedItem?.Destination.Navigate(selectedItem.PageType, selectedItem.Parameter);
if (selectedItem == null) MenuFrame.Navigate(typeof(WelcomePage));
else selectedItem.Destination.Navigate(selectedItem.PageType, selectedItem.Parameter);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
DataContext = new MainVm(Frame, MenuFrame);
if (Model.SelectedItem == null) MenuFrame.Navigate(typeof(WelcomePage));
}
}
}

View File

@@ -4,7 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ModernKeePass.ViewModels"
x:Class="ModernKeePass.Pages.AboutPage"
x:Class="ModernKeePass.Views.AboutPage"
mc:Ignorable="d">
<Page.DataContext>
@@ -18,21 +18,21 @@
<Run Text="{Binding Version}" />
</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
<Run Text="A modern password manager for the Windows Store"/>
<Run x:Uid="AboutDesc" />
</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
<Run Text="Homepage: "/>
<Run x:Uid="AboutHomepage" />
<Hyperlink NavigateUri="https://github.com/wismna/ModernKeePass">
<Run Text="https://github.com/wismna/ModernKeePass"/>
</Hyperlink></TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="10,0,0,0">
<Run Text="Credits:"/>
<Run x:Uid="AboutCreditsLabel" />
</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
<Run Text="Dominik Reichl for the KeePass application and file format"/>
<Run x:Uid="AboutCredits1" />
</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
<Run Text="David Lechner for his PCL adapatation of the KeePass Library and his correlated tests"/>
<Run x:Uid="AboutCredits2" />
</TextBlock>
</StackPanel>
</Page>

View File

@@ -1,6 +1,6 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Pages
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.

View File

@@ -0,0 +1,30 @@
<Page
x:Class="ModernKeePass.Views.DonatePage"
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"
xmlns:converters="using:ModernKeePass.Converters"
mc:Ignorable="d">
<Page.DataContext>
<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>

View File

@@ -0,0 +1,71 @@
using System;
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
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class DonatePage
{
public DonateVm Model => DataContext as DonateVm;
public DonatePage()
{
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

@@ -1,5 +1,5 @@
<Page
x:Class="ModernKeePass.Pages.NewDatabasePage"
x:Class="ModernKeePass.Views.NewDatabasePage"
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"
@@ -9,6 +9,7 @@
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:userControls="using:ModernKeePass.Views.UserControls"
mc:Ignorable="d">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
@@ -18,20 +19,22 @@
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<HyperlinkButton Content="Create new..." Click="ButtonBase_OnClick" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30">Create a new password database to the location of your chosing.</TextBlock>
<HyperlinkButton x:Uid="NewCreateButton" Click="ButtonBase_OnClick" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" x:Uid="NewCreateDesc" />
<Border HorizontalAlignment="Left" BorderThickness="1" BorderBrush="AliceBlue" Width="550" Visibility="{Binding ShowPasswordBox, Converter={StaticResource BooleanToVisibilityConverter}}">
<StackPanel Margin="25,0,25,0">
<TextBlock Text="{Binding Name}" />
<local:CompositeKeyUserControl CreateNew="True" >
<userControls:CompositeKeyUserControl CreateNew="True" x:Uid="CompositeKeyNewButton">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ValidationChecked">
<core:NavigateToPageAction TargetPage="ModernKeePass.Pages.GroupDetailPage" />
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.GroupDetailPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</local:CompositeKeyUserControl>
</userControls:CompositeKeyUserControl>
</StackPanel>
</Border>
</StackPanel>
</Page>

View File

@@ -2,22 +2,17 @@
using System.Collections.Generic;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Events;
using ModernKeePass.ViewModels;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Pages
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 NewDatabasePage
{
private Frame _mainFrame;
public NewVm Model => (NewVm)DataContext;
public NewDatabasePage()
@@ -25,12 +20,6 @@ namespace ModernKeePass.Pages
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
_mainFrame = e.Parameter as Frame;
}
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var savePicker = new FileSavePicker

View File

@@ -7,7 +7,8 @@
xmlns:local="using:ModernKeePass.Controls"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
x:Class="ModernKeePass.Pages.OpenDatabasePage"
xmlns:userControls="using:ModernKeePass.Views.UserControls"
x:Class="ModernKeePass.Views.OpenDatabasePage"
mc:Ignorable="d">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
@@ -17,20 +18,20 @@
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<HyperlinkButton Content="Browse files..." Click="ButtonBase_OnClick" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30">Open an existing password database from your PC.</TextBlock>
<HyperlinkButton Content="From Url..." IsEnabled="False" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30">Open an existing password database from an Internet location (not yet implemented).</TextBlock>
<HyperlinkButton x:Uid="OpenBrowseButton" Click="ButtonBase_OnClick" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" x:Uid="OpenBrowseDesc" />
<HyperlinkButton x:Uid="OpenUrlButton" IsEnabled="False" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" x:Uid="OpenUrlDesc" />
<Border HorizontalAlignment="Left" BorderThickness="1" BorderBrush="AliceBlue" Width="550" Visibility="{Binding ShowPasswordBox, Converter={StaticResource BooleanToVisibilityConverter}}">
<StackPanel Margin="25,0,25,0">
<TextBlock Text="{Binding Name}" />
<local:CompositeKeyUserControl>
<userControls:CompositeKeyUserControl x:Uid="CompositeKeyOpenButton">
<interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="ValidationChecked">
<Core:NavigateToPageAction TargetPage="ModernKeePass.Pages.GroupDetailPage" />
<Core:NavigateToPageAction TargetPage="ModernKeePass.Views.GroupDetailPage" />
</Core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</local:CompositeKeyUserControl>
</userControls:CompositeKeyUserControl>
</StackPanel>
</Border>
</StackPanel>

View File

@@ -3,12 +3,11 @@ using Windows.Storage.Pickers;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Events;
using ModernKeePass.ViewModels;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Pages
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.

View File

@@ -0,0 +1,70 @@
<Page
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"
xmlns:local="using:ModernKeePass.Controls"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:userControls="using:ModernKeePass.Views.UserControls"
x:Class="ModernKeePass.Views.RecentDatabasesPage"
mc:Ignorable="d">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<CollectionViewSource x:Name="RecentItemsSource" Source="{Binding RecentItems}" />
</Page.Resources>
<Page.DataContext>
<viewModels:RecentVm/>
</Page.DataContext>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<HyperlinkButton Grid.Row="0" HorizontalAlignment="Right">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Cancel" />
<TextBlock x:Uid="RecentClear" VerticalAlignment="Center" Margin="10,0,0,0" />
</StackPanel>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction TargetObject="{Binding}" MethodName="ClearAll" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</HyperlinkButton>
<ListView Grid.Row="1"
ItemsSource="{Binding Source={StaticResource RecentItemsSource}}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="10,0,10,0">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Name}" Padding="5,0,0,0" />
<TextBlock Grid.Row="1" Text="{Binding Path}" Padding="5,0,0,0" FontSize="10" />
<userControls:CompositeKeyUserControl Grid.Row="2" x:Name="DatabaseUserControl" x:Uid="CompositeKeyOpenButton" HorizontalAlignment="Stretch" MinWidth="400" Margin="0,10,0,0" Visibility="{Binding IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ValidationChecking">
<core:CallMethodAction TargetObject="{Binding}" MethodName="OpenDatabaseFile" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="ValidationChecked">
<core:CallMethodAction TargetObject="{Binding}" MethodName="UpdateAccessTime" />
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.GroupDetailPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</userControls:CompositeKeyUserControl>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>

View File

@@ -1,27 +1,15 @@
// Pour en savoir plus sur le modèle d'élément Page vierge, consultez la page http://go.microsoft.com/fwlink/?LinkId=234238
using System;
using Windows.UI.Xaml;
using ModernKeePass.ViewModels;
namespace ModernKeePass.Pages
namespace ModernKeePass.Views
{
/// <summary>
/// Une page vide peut être utilisée seule ou constituer une page de destination au sein d'un frame.
/// </summary>
public sealed partial class RecentDatabasesPage
{
public RecentVm Model => (RecentVm)DataContext;
public RecentDatabasesPage()
{
InitializeComponent();
}
private void OpenDatabaseUserControl_OnValidationChecking(object sender, EventArgs e)
{
var app = (App)Application.Current;
app.Database.DatabaseFile = ((RecentItemVm)Model.SelectedItem).DatabaseFile;
}
}
}

View File

@@ -4,16 +4,16 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ModernKeePass.ViewModels"
x:Class="ModernKeePass.Pages.SaveDatabasePage"
x:Class="ModernKeePass.Views.SaveDatabasePage"
mc:Ignorable="d">
<Page.DataContext>
<viewModels:SaveVm/>
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<HyperlinkButton Content="Save and close" Click="SaveButton_OnClick" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30">This will save and close the currently opened database.</TextBlock>
<HyperlinkButton Content="Save as..." Click="SaveAsButton_OnClick" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30">This will save the currently opened database as a new file and leave it open.</TextBlock>
<HyperlinkButton x:Uid="SaveButton" Click="SaveButton_OnClick" />
<TextBlock x:Uid="SaveDesc" Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" />
<HyperlinkButton x:Uid="SaveAsButton" Click="SaveAsButton_OnClick" />
<TextBlock x:Uid="SaveAsDesc" Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" />
</StackPanel>
</Page>

View File

@@ -8,7 +8,7 @@ using ModernKeePass.ViewModels;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Pages
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
@@ -31,7 +31,7 @@ namespace ModernKeePass.Pages
private void SaveButton_OnClick(object sender, RoutedEventArgs e)
{
Model.Save();
_mainFrame.Navigate(typeof(MainPage));
_mainFrame.Navigate(typeof(Views.MainPage));
}
private async void SaveAsButton_OnClick(object sender, RoutedEventArgs e)
@@ -47,7 +47,7 @@ namespace ModernKeePass.Pages
if (file == null) return;
Model.Save(file);
_mainFrame.Navigate(typeof(MainPage));
_mainFrame.Navigate(typeof(Views.MainPage));
}
}
}

View File

@@ -1,5 +1,5 @@
<Page
x:Class="ModernKeePass.Pages.WelcomePage"
x:Class="ModernKeePass.Views.WelcomePage"
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"
@@ -9,11 +9,11 @@
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" Margin="0,2,0,10">
<SymbolIcon Symbol="Back" Margin="0,7,40,0" />
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}">Have an existing password database? Open it here.</TextBlock>
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}" x:Uid="WelcomeOpen" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,6,0,10">
<SymbolIcon Symbol="Back" Margin="0,7,40,0" />
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}">Want to create a new password database? Do it here.</TextBlock>
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}" x:Uid="WelcomeNew" />
</StackPanel>
</StackPanel>
</Page>

View File

@@ -1,13 +1,13 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Pages
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 SettingsSecurityPage
public sealed partial class WelcomePage
{
public SettingsSecurityPage()
public WelcomePage()
{
InitializeComponent();
}

View File

@@ -3,18 +3,17 @@
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:basePages="using:ModernKeePass.Pages.BasePages"
xmlns:controls="using:ModernKeePass.Controls"
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:basePages="using:ModernKeePass.Views.BasePages"
x:Name="PageRoot"
x:Class="ModernKeePass.Pages.SettingsPage"
x:Class="ModernKeePass.Views.SettingsPage"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource x:Name="MenuItemsSource" Source="{Binding MenuItems}" >
</CollectionViewSource>
<CollectionViewSource x:Name="MenuItemsSource" Source="{Binding MenuItems}" IsSourceGrouped="True" />
</Page.Resources>
<Page.DataContext>
<viewModels:SettingsVM />
<viewModels:SettingsVm />
</Page.DataContext>
<Page.Background>
@@ -50,13 +49,13 @@
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<TextBlock x:Name="TitleTextBox" Text="Settings" Grid.Column="1" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock x:Name="TitleTextBox" x:Uid="SettingsTitle" Grid.Column="1" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<controls:ListViewWithDisable
Grid.Column="0"
Grid.Row="1"
x:Name="MenuListView"
SelectionChanged="MenuListView_SelectionChanged"
SelectionChanged="ListView_SelectionChanged"
Background="{ThemeResource AppBarBackgroundThemeBrush}"
ItemsSource="{Binding Source={StaticResource MenuItemsSource}}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
@@ -74,9 +73,12 @@
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="DarkGray" Margin="20,0,0,0">
<StackPanel Margin="20,0,0,0">
<TextBlock Text="{Binding Key}" />
<Grid Background="DarkGray">
<Border Height="1" Width="300" HorizontalAlignment="Stretch"/>
</Grid>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>

View File

@@ -3,7 +3,7 @@ using ModernKeePass.ViewModels;
// The Split Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234234
namespace ModernKeePass.Pages
namespace ModernKeePass.Views
{
/// <summary>
/// A page that displays a group title, a list of items within the group, and details for
@@ -11,7 +11,7 @@ namespace ModernKeePass.Pages
/// </summary>
public sealed partial class SettingsPage
{
public new SettingsVM Model => (SettingsVM)DataContext;
public new SettingsVm Model => (SettingsVm)DataContext;
public SettingsPage()
{
@@ -20,12 +20,11 @@ namespace ModernKeePass.Pages
ListViewSource = MenuItemsSource;
}
private void MenuListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
private new void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView_SelectionChanged(sender, e);
base.ListView_SelectionChanged(sender, e);
var selectedItem = Model.SelectedItem as ListMenuItemVm;
if (selectedItem == null) return;
MenuFrame?.Navigate(selectedItem.PageType);
MenuFrame?.Navigate(selectedItem == null ? typeof(SettingsWelcomePage) : selectedItem.PageType);
}
}
}

View File

@@ -1,10 +1,11 @@
<Page
x:Class="ModernKeePass.Pages.SettingsDatabasePage"
x:Class="ModernKeePass.Views.SettingsDatabasePage"
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"
xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource x:Name="RecycleBinGroups" Source="{Binding Groups}" />
@@ -17,13 +18,27 @@
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleSwitch Header="Recycle bin" OffContent="Disabled" OnContent="Enabled" IsOn="{Binding HasRecycleBin, Mode=TwoWay}" />
<ComboBox ItemsSource="{Binding Source={StaticResource RecycleBinGroups}}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" IsEnabled="{Binding HasRecycleBin}" />
<TextBlock Text="Encryption Algorithm" FontSize="14" Margin="5,20,0,10" />
<ToggleSwitch x:Uid="SettingsDatabaseRecycleBin" IsOn="{Binding HasRecycleBin, Mode=TwoWay}" />
<ComboBox ItemsSource="{Binding Source={StaticResource RecycleBinGroups}}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" IsEnabled="{Binding HasRecycleBin}">
<ComboBox.Resources>
<DataTemplate x:Name="GroupFirstItem">
<TextBlock x:Uid="GroupNewItemTextBox" />
</DataTemplate>
<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" />
<ComboBox ItemsSource="{Binding Source={StaticResource Ciphers}}" SelectedIndex="{Binding CipherIndex, Mode=TwoWay}" />
<TextBlock Text="Compression Algorithm" FontSize="14" Margin="5,20,0,10" />
<TextBlock x:Uid="SettingsDatabaseCompression" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />
<ComboBox ItemsSource="{Binding Source={StaticResource Compressions}}" SelectedItem="{Binding CompressionName, Mode=TwoWay}" />
<TextBlock Text="Key Derivation Algorithm" FontSize="14" Margin="5,20,0,10" />
<TextBlock x:Uid="SettingsDatabaseKdf" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />
<ComboBox ItemsSource="{Binding Source={StaticResource KeyDerivations}}" SelectedItem="{Binding KeyDerivationName, Mode=TwoWay}" />
</StackPanel>
</Page>

View File

@@ -1,6 +1,6 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Pages
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.

View File

@@ -0,0 +1,40 @@
<Page
x:Class="ModernKeePass.Views.SettingsNewDatabasePage"
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.Resources>
<CollectionViewSource x:Name="KeyDerivations" Source="{Binding FileFormats}" />
</Page.Resources>
<Page.DataContext>
<viewModels:SettingsNewVm />
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Uid="SettingsNewDatabaseDesc" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,0,0,10"/>
<ToggleSwitch x:Uid="SettingsNewDatabaseSample" IsOn="{Binding IsCreateSample, Mode=TwoWay}" />
<TextBlock x:Uid="SettingsNewDatabaseKdf" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="1" ItemsSource="{Binding Source={StaticResource KeyDerivations}}" SelectedItem="{Binding FileFormatVersion, Mode=TwoWay}" />
<Button Grid.Column="2" Style="{StaticResource TextBlockButtonStyle}">
<SymbolIcon Symbol="Help" RenderTransformOrigin="0.5,0.5" >
<SymbolIcon.RenderTransform>
<CompositeTransform ScaleX="0.7" ScaleY="0.7"/>
</SymbolIcon.RenderTransform>
</SymbolIcon>
<Button.Flyout>
<Flyout>
<TextBlock x:Uid="SettingsNewDatabaseKdfMoreInfo" TextWrapping="WrapWholeWords" MaxWidth="400" />
</Flyout>
</Button.Flyout>
</Button>
</Grid>
</StackPanel>
</Page>

View File

@@ -0,0 +1,15 @@
// Pour en savoir plus sur le modèle d'élément Page vierge, consultez la page http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views
{
/// <summary>
/// Une page vide peut être utilisée seule ou constituer une page de destination au sein d'un frame.
/// </summary>
public sealed partial class SettingsNewDatabasePage
{
public SettingsNewDatabasePage()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,20 @@
<Page
x:Class="ModernKeePass.Views.SettingsSecurityPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ModernKeePass.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:userControls="using:ModernKeePass.Views.UserControls"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Uid="SettingsSecurityTitle" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,0,0,0" />
<TextBlock TextWrapping="WrapWholeWords" Margin="5,0,0,0">
<Run x:Uid="SettingsSecurityDesc1" />
<Run x:Uid="SettingsSecurityDesc2" FontWeight="SemiBold" />
<Run x:Uid="SettingsSecurityDesc3" />
</TextBlock>
<userControls:CompositeKeyUserControl Margin="0,20,0,0" UpdateKey="True" x:Uid="SettingsSecurityUpdateButton" ValidationChecked="CompositeKeyUserControl_OnValidationChecked" />
</StackPanel>
</Page>

View File

@@ -0,0 +1,23 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
using ModernKeePass.Common;
using ModernKeePass.Events;
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 SettingsSecurityPage
{
public SettingsSecurityPage()
{
InitializeComponent();
}
private void CompositeKeyUserControl_OnValidationChecked(object sender, PasswordEventArgs e)
{
ToastNotificationHelper.ShowGenericToast("Composite key", "Database successfully updated.");
}
}
}

View File

@@ -0,0 +1,14 @@
<Page
x:Class="ModernKeePass.Views.SettingsWelcomePage"
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"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock FontSize="24" VerticalAlignment="Center" Margin="10,-70,0,0" x:Uid="SettingsWelcomeTitle" />
<TextBlock VerticalAlignment="Center" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,0,0,0" x:Uid="SettingsWelcomeDesc" />
<TextBlock VerticalAlignment="Center" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,0,0,0" x:Uid="SettingsWelcomeHowto" />
</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 SettingsWelcomePage
{
public SettingsWelcomePage()
{
InitializeComponent();
}
}
}

View File

@@ -1,5 +1,5 @@
<UserControl x:Name="UserControl"
x:Class="ModernKeePass.Controls.CompositeKeyUserControl"
x:Class="ModernKeePass.Views.UserControls.CompositeKeyUserControl"
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"
@@ -9,15 +9,14 @@
xmlns:actions="using:ModernKeePass.Actions"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:viewModels="using:ModernKeePass.ViewModels"
mc:Ignorable="d"
d:DesignHeight="120"
d:DesignWidth="550" >
mc:Ignorable="d" >
<UserControl.Resources>
<SolidColorBrush x:Key="ErrorColorBrush" Color="Red"/>
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter"/>
<converters:DoubleToSolidColorBrushConverter x:Key="DoubleToForegroungBrushConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:DiscreteIntToSolidColorBrushConverter x:Key="DiscreteIntToSolidColorBrushConverter"/>
<converters:EmptyStringToVisibilityConverter x:Key="EmptyStringToVisibilityConverter"/>
</UserControl.Resources>
<Grid x:Name="Grid">
<!-- DataContext is not set at the root of the control because of issues happening when displaying it -->
@@ -31,10 +30,11 @@
<Grid.RowDefinitions>
<RowDefinition Height="45" />
<RowDefinition Height="40" />
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" IsChecked="{Binding HasPassword, Mode=TwoWay}" />
<PasswordBox Grid.Row="0" Grid.Column="1" x:Name="PasswordBox" Password="{Binding Password, Mode=TwoWay}" Height="30" IsEnabled="{Binding HasPassword}" IsPasswordRevealButtonEnabled="True" KeyDown="PasswordBox_KeyDown" PlaceholderText="Password" BorderBrush="{Binding StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" >
<PasswordBox Grid.Row="0" Grid.Column="1" x:Name="PasswordBox" x:Uid="CompositeKeyPassword" Password="{Binding Password, Mode=TwoWay}" Height="30" IsEnabled="{Binding HasPassword}" IsPasswordRevealButtonEnabled="True" KeyDown="PasswordBox_KeyDown" BorderBrush="{Binding StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" >
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding HasPassword}" Value="True">
<actions:SetupFocusAction TargetObject="{Binding ElementName=PasswordBox}" />
@@ -48,8 +48,14 @@
Visibility="{Binding ShowComplexityIndicator, ElementName=UserControl, Converter={StaticResource BooleanToVisibilityConverter}}" />
<CheckBox Grid.Row="1" Grid.Column="0" IsChecked="{Binding HasKeyFile, Mode=TwoWay}" />
<HyperlinkButton Grid.Row="1" Grid.Column="1" Margin="-15,0,0,0" Content="{Binding KeyFileText}" IsEnabled="{Binding HasKeyFile}" Click="KeyFileButton_Click" />
<Button Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Content="Create new key" Visibility="{Binding ShowComplexityIndicator, ElementName=UserControl, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding HasKeyFile}" Click="CreateKeyFileButton_Click" />
<Button Grid.Column="0" Grid.Row="2" Content="OK" Click="OpenButton_OnClick" Background="{ThemeResource ListViewItemSelectedPointerOverBorderThemeBrush}" Foreground="{ThemeResource TextBoxBackgroundThemeBrush}" IsEnabled="{Binding IsValid}" />
<TextBlock Grid.Column="1" Grid.Row="2" Height="28" FontSize="14" FontWeight="Light" HorizontalAlignment="Right" Text="{Binding Status}" Foreground="{Binding StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" />
<HyperlinkButton Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Visibility="{Binding ShowComplexityIndicator, ElementName=UserControl, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding HasKeyFile}" Click="CreateKeyFileButton_Click">
<SymbolIcon Symbol="Add">
<ToolTipService.ToolTip>
<ToolTip x:Uid="CompositeKeyNewKeyFileTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
</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}" />
</Grid>
</UserControl>

View File

@@ -5,11 +5,13 @@ using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
using ModernKeePass.Events;
using ModernKeePass.Extensions;
using ModernKeePass.Services;
using ModernKeePass.ViewModels;
// Pour en savoir plus sur le modèle d'élément Contrôle utilisateur, consultez la page http://go.microsoft.com/fwlink/?LinkId=234236
namespace ModernKeePass.Controls
namespace ModernKeePass.Views.UserControls
{
public sealed partial class CompositeKeyUserControl
{
@@ -39,6 +41,19 @@ namespace ModernKeePass.Controls
typeof(CompositeKeyUserControl),
new PropertyMetadata(false, (o, args) => { }));
public string ButtonLabel
{
get { return (string)GetValue(ButtonLabelProperty); }
set { SetValue(ButtonLabelProperty, value); }
}
public static readonly DependencyProperty ButtonLabelProperty =
DependencyProperty.Register(
"ButtonLabel",
typeof(string),
typeof(CompositeKeyUserControl),
new PropertyMetadata("OK", (o, args) => { }));
public bool ShowComplexityIndicator => CreateNew || UpdateKey;
public CompositeKeyUserControl()
@@ -51,20 +66,31 @@ namespace ModernKeePass.Controls
public event PasswordCheckedEventHandler ValidationChecked;
public delegate void PasswordCheckedEventHandler(object sender, PasswordEventArgs e);
private void OpenButton_OnClick(object sender, RoutedEventArgs e)
private async void OpenButton_OnClick(object sender, RoutedEventArgs e)
{
ValidationChecking?.Invoke(this, new EventArgs());
if (UpdateKey) Model.UpdateKey();
else if (Model.OpenDatabase(CreateNew))
if (UpdateKey)
{
Model.UpdateKey();
ValidationChecked?.Invoke(this, new PasswordEventArgs(Model.RootGroup));
}
else
{
var resource = new ResourcesService();
var oldLabel = ButtonLabel;
ButtonLabel = resource.GetResourceValue("CompositeKeyOpening");
if (await Dispatcher.RunTaskAsync(async () => await Model.OpenDatabase(CreateNew)))
{
ValidationChecked?.Invoke(this, new PasswordEventArgs(Model.RootGroup));
}
ButtonLabel = oldLabel;
}
}
private void PasswordBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Enter) OpenButton_OnClick(null, null);
if (e.Key == VirtualKey.Enter && Model.IsValid) OpenButton_OnClick(null, null);
}
private async void KeyFileButton_Click(object sender, RoutedEventArgs e)

View File

@@ -0,0 +1,6 @@
Tired of trying to remember your numerous passwords? Worried that using the same password everywhere is a security risk?
ModernKeePass is free, open-source and user friendly yet secure password manager, based on proven and certified technology from KeePass 2.x and fully compatible with it.
You can safely store and generate all your passwords in an encrypted, secure database that you can put anywhere (local computer/tablet, cloud, USB key...)
Even though security is an important matter, this app aims at being simple to use. Suggestions welcome!
Works in Windows 10, 8.1 and RT.

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1 @@
Entry page with password generator

View File

@@ -0,0 +1 @@
Filter your entries to quickly find the one you're looking for

View File

@@ -0,0 +1 @@
Group view, with subgroups and entries

View File

@@ -0,0 +1 @@
Create new databases, with a password and/or with a new or existing key file

View File

@@ -0,0 +1 @@
Open database with password and key files

View File

@@ -0,0 +1 @@
Open recent files

View File

@@ -0,0 +1 @@
See everything quickly with the semantic zoom

View File

@@ -0,0 +1 @@
Application settings

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