77 Commits
V1.1 ... V1.4

Author SHA1 Message Date
6bb37d9e70 Removed QueryString.Net package and replaced it with Data.Json
Version 1.4
2017-11-08 14:42:42 +01:00
66fd87124b Entries now have working expiration dates
About page redone (with working hyperlink)
WIP on how to display that info on the group detail page
2017-11-08 14:42:41 +01:00
7bb78fd374 WIP Entry expiration dates 2017-11-08 14:42:41 +01:00
dc62cedb74 Toast notifications and undo mechanism now works! (for group and entries) 2017-11-08 14:42:41 +01:00
bg45
bc6e7d0097 WIP Toasts - bis 2017-11-08 14:42:41 +01:00
01ed1bc9c1 WIP toast notifications
WIP layout and color changes
2017-11-08 14:42:41 +01:00
bg45
19008cdad2 Change password reveal button style in Entry page 2017-11-08 14:42:41 +01:00
8a312bec71 Implemented password generator in Entry form 2017-11-08 14:42:41 +01:00
2b8d37057c Added password complexity indicator on new database
Database password control now inside a Border element
Save page also has a title
2017-11-08 14:42:41 +01:00
5497e6fc00 Bug correction when canceling open file dialog
New About page
Main menu pages now have titles
2017-11-08 14:42:40 +01:00
86064af3a2 Package information updated 2017-11-08 14:42:40 +01:00
3265244f06 Update project version and manifest versions 2017-11-08 14:42:40 +01:00
2698070328 Creating groups and entries now navigates to the related detail page, in edit mode
Code cleanup
2017-11-08 14:42:40 +01:00
5638b59fda Corrected Entry password not synchronized bug
Created a new welcome page to be shown on first launch
Added descriptive text in main menu pages
2017-11-08 14:42:40 +01:00
9f94dd55c2 Added ViewModel property in code behind of all pages
Workaround a bug in Entry password reveal when set for the first time
2017-11-08 14:42:40 +01:00
0ded991673 Code cleanup 2017-11-08 14:42:40 +01:00
cddda7adcd Created TextBox with Forward button style for Entry Url
Created PasswordBox with always visible Forward button for Open Database user control
2017-11-08 14:42:40 +01:00
69a63f4b2e Minor code cleanup 2017-11-08 14:42:39 +01:00
10b6330ac6 Added Save As function 2017-11-08 14:42:39 +01:00
676365460c Create new database works! 2017-11-08 14:42:39 +01:00
acb5bcc230 Recent items list auto removes non existing files
Choosing a recent list item does not change the database file anymore
2017-11-08 14:42:39 +01:00
bg45
f1737ca9f7 Minor changes 2017-11-08 14:42:39 +01:00
2f1355104e WIP New database (and open and recent...) 2017-11-08 14:42:39 +01:00
97b1475100 Refactor in file open mechanisms: file is passed to user control
Confirmation dialogs on group and entry delete
2017-11-08 14:42:39 +01:00
454e074c44 Search shows entries results
Selecting a result goes to the Entry Page
Auto-save on quit disabled
2017-11-08 14:42:39 +01:00
951172e36f Removed search stackpanel 2017-11-08 14:42:39 +01:00
f5fd3effaf WIP Search 2017-11-08 14:42:39 +01:00
ec4f2e7d88 Open file from Explorer with association works
Major code refactor in Open, Save and Recent pages and VM
Main page automatically opens a sub page depending on context
2017-11-08 14:42:38 +01:00
98ecb0b8a1 Better Entry URL go to button
Migrated code from code behind pages to view models
Auto select Recents if there are any recent items
WIP auto focus on password box when opening database
2017-11-08 14:42:38 +01:00
bg45
f2794f8055 WIP Text with button for Url in Entry 2017-11-08 14:42:38 +01:00
bg45
dfb5ec9683 Semantic Zoom now has grouped zoomed out view
New button to open browser to Entry Url
Entries are now sorted alphabetically
2017-11-08 14:42:38 +01:00
3d033417ad Groups left collapsed menu now has a tooltip
Groups design changes: border now only around icon
Entry password reveal checkbox now works with XAML and converters
2017-11-08 14:42:38 +01:00
f22ca4c46f Finally (!) removed right margin in Group Detail page left menu 2017-11-08 14:42:38 +01:00
11fb82573b Edit mode works in Groups and Entries 2017-11-08 14:42:38 +01:00
4aa3b17398 Some layout enhancements
Changed controls to use static or theme resources
Switch to light theme (mainly for testing)
2017-11-08 14:42:38 +01:00
c611f5a8a2 Replaced arrow buttons with a hamburger icon in groups menu
Created data triggers with Blend to handle item data template changes
2017-11-08 14:42:38 +01:00
013108f2ec Major redesign in the Group Detail page:
- groups and entries are inverted
- groups are in a collapsable menu (to be improved!)
2017-11-08 14:42:37 +01:00
616d922145 New color to brush converter
New boolean to visibility converter
Base class to handle property changes notifications for all View Models
Template selector to handle a different first item in listviews or gridviews
2017-11-08 14:42:37 +01:00
bg45
95771878fc No changes, just sync 2017-11-08 14:42:37 +01:00
267d9f25c2 Implement add and delete entries and groups
New command bar
Some layout changes
Some refactoring
2017-11-08 14:42:37 +01:00
2bcc4fde60 Use latest Nuget package for lib with fixed version 2017-11-08 14:42:37 +01:00
30838d0e00 Code refactor and simplfication
ModernKeePassLib assembly info updated and version fixed
2017-11-08 14:42:37 +01:00
bg45
6d69dd4d15 Finished Password User Control
Recent and Open now use this control
2017-11-08 14:42:37 +01:00
bg45
324553c58c Creation of a UserControl to handle password input 2017-11-08 14:42:37 +01:00
bg45
1ca3f29da0 Implementing Recent Files WIP
Code refactoring
Entry coloring
2017-11-08 14:42:37 +01:00
817f25e8a8 Yet again some dll reference changes 2017-11-08 14:42:36 +01:00
bg45
056e1624be Update Lib dll version reference 2017-11-08 14:42:36 +01:00
b5c04d524d Added icons to main menu
Added icons to entries and groups with a mapping
Created a Converter to handle pluralization
Several UI enhancements
2017-11-08 14:42:36 +01:00
1582060466 Add currently opened database to main menu
Save and close reloads the page
Design enhancements
Some refactoring
2017-11-08 14:42:36 +01:00
caaf34918e Main page now uses Frame to change views when selecting items
Backgrounds unified
Menu items can be disabled thanks to custom ListView control
2017-11-08 14:42:36 +01:00
bg45
3a045dbb16 Update project reference to ModernKeePassLib 2017-11-08 14:42:36 +01:00
40256be135 Deleted AppPackages 2017-11-08 14:42:36 +01:00
26e9b78b3b Minor update 2017-09-26 18:18:00 +02:00
a76b0ff350 Remove app packages from source control 2017-09-26 18:01:21 +02:00
7e0f21f88f ModernKeePass now targets nuget package from VSTS 2017-09-26 17:24:19 +02:00
d78e75f449 Correct nuspec for Validation package version 2017-09-26 17:15:22 +02:00
93be4cf01c Remove unnecessary references to Bcl build targets 2017-09-26 17:10:44 +02:00
ec037b97fd Include Bcl.Build dependency 2017-09-26 16:44:42 +02:00
2b469d764d Deleted project.lock.json 2017-09-26 13:40:12 +00:00
cfd61a87e8 Removed unused projects 2017-09-26 15:39:50 +02:00
1faa26473d ModernKeePassLib namespace restoration 2017-09-26 15:38:58 +02:00
27aaa8023a Write-mode is finally working!!!
Lib uses BouncyCastle crypto
2017-09-26 14:32:15 +02:00
22ea657885 Added unit tests (all passing unfortunately)
UI improvements
Write mode still doesn't work
2017-09-25 18:34:27 +02:00
34996da19d Read-mode working
Write-mode does not create exceptions but still doesn't work
2017-09-25 14:50:47 +02:00
bg45
3bf8015280 Lib fully migrated to Standard
CryptographicEngine.DeriveKeyMaterial exception...
2017-09-23 18:30:04 -04:00
bg45
32e629231c Switched to .Net Standard (yet again...)
Changed every PCL call to WinRt
WIP on CompositeKey
2017-09-23 09:48:31 -04:00
bg45
9d78d59a15 WIP KeePassLibPCL 2017-09-23 09:42:48 -04:00
668afbe817 WIP KeePassLibPCL - problem with awaitables 2017-09-22 18:48:09 +02:00
a43bc20eb3 ModernKeePassLib custom PCL version 2017-09-22 15:40:24 +02:00
bg45
baba70e56d WIP saving 2017-09-21 03:11:00 -04:00
c4ac244270 WIP saving 2017-09-20 18:45:37 +02:00
668ffcb107 Better home page layout 2017-09-20 18:19:00 +02:00
a7cdd248c9 Updated .gitignore 2017-09-19 16:18:23 +00:00
f976a2cc86 Deleted packages 2017-09-19 16:18:05 +00:00
bg45
5d271d4133 WIP sqving - removed some useless async await 2017-09-19 02:53:29 -04:00
bg45
25d1564a8d WIP saving 2017-09-18 15:04:21 -04:00
00897307a4 Re-enabled write mode in KeePassLib
Set database as part of App (so, Singleton)
Changed main page view model
WIP and testing saving mechanism
2017-09-18 18:40:43 +02:00
599 changed files with 10499 additions and 232302 deletions

5
.gitignore vendored
View File

@@ -33,4 +33,7 @@ _ReSharper*/
Translation/TrlUtil.vshost.exe.manifest
*.nupkg
.vs/
/UpgradeLog.htm
/UpgradeLog*.htm
packages/
project.lock.json
AppPackages/

View File

@@ -5,7 +5,12 @@ VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePass", "ModernKeePass\ModernKeePass.csproj", "{A0CFC681-769B-405A-8482-0CDEE595A91F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePassLib", "ModernKeePassLib\ModernKeePassLib.csproj", "{A207789D-9020-401B-9D0A-D0D2CFF721BD}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePassLib", "ModernKeePassLib\ModernKeePassLib.csproj", "{2E710089-9559-4967-846C-E763DD1F3ACB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePassLib.Test", "ModernKeePassLib.Test\ModernKeePassLib.Test.csproj", "{067456C0-086C-46A8-B37F-1405717B7BFC}"
ProjectSection(ProjectDependencies) = postProject
{2E710089-9559-4967-846C-E763DD1F3ACB} = {2E710089-9559-4967-846C-E763DD1F3ACB}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -43,22 +48,38 @@ Global
{A0CFC681-769B-405A-8482-0CDEE595A91F}.Release|x86.ActiveCfg = Release|x86
{A0CFC681-769B-405A-8482-0CDEE595A91F}.Release|x86.Build.0 = Release|x86
{A0CFC681-769B-405A-8482-0CDEE595A91F}.Release|x86.Deploy.0 = Release|x86
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Debug|ARM.ActiveCfg = Debug|ARM
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Debug|ARM.Build.0 = Debug|ARM
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Debug|x64.ActiveCfg = Debug|x64
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Debug|x64.Build.0 = Debug|x64
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Debug|x86.ActiveCfg = Debug|x86
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Debug|x86.Build.0 = Debug|x86
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Release|Any CPU.Build.0 = Release|Any CPU
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Release|ARM.ActiveCfg = Release|ARM
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Release|ARM.Build.0 = Release|ARM
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Release|x64.ActiveCfg = Release|x64
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Release|x64.Build.0 = Release|x64
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Release|x86.ActiveCfg = Release|x86
{A207789D-9020-401B-9D0A-D0D2CFF721BD}.Release|x86.Build.0 = Release|x86
{2E710089-9559-4967-846C-E763DD1F3ACB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Debug|ARM.ActiveCfg = Debug|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Debug|ARM.Build.0 = Debug|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Debug|x64.ActiveCfg = Debug|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Debug|x64.Build.0 = Debug|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Debug|x86.ActiveCfg = Debug|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Debug|x86.Build.0 = Debug|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Release|Any CPU.Build.0 = Release|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Release|ARM.ActiveCfg = Release|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Release|ARM.Build.0 = Release|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Release|x64.ActiveCfg = Release|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Release|x64.Build.0 = Release|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Release|x86.ActiveCfg = Release|Any CPU
{2E710089-9559-4967-846C-E763DD1F3ACB}.Release|x86.Build.0 = Release|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Debug|ARM.ActiveCfg = Debug|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Debug|ARM.Build.0 = Debug|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Debug|x64.ActiveCfg = Debug|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Debug|x64.Build.0 = Debug|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Debug|x86.ActiveCfg = Debug|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Debug|x86.Build.0 = Debug|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Release|Any CPU.Build.0 = Release|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Release|ARM.ActiveCfg = Release|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Release|ARM.Build.0 = Release|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Release|x64.ActiveCfg = Release|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Release|x64.Build.0 = Release|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Release|x86.ActiveCfg = Release|Any CPU
{067456C0-086C-46A8-B37F-1405717B7BFC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -0,0 +1,23 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Microsoft.Xaml.Interactivity;
namespace ModernKeePass.Actions
{
public class SetupFocusAction : DependencyObject, IAction
{
public Control TargetObject
{
get { return (Control)GetValue(TargetObjectProperty); }
set { SetValue(TargetObjectProperty, value); }
}
public static readonly DependencyProperty TargetObjectProperty =
DependencyProperty.Register("TargetObject", typeof(Control), typeof(SetupFocusAction), new PropertyMetadata(0));
public object Execute(object sender, object parameter)
{
return TargetObject?.Focus(FocusState.Programmatic);
}
}
}

View File

@@ -2,6 +2,14 @@
x:Class="ModernKeePass.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ModernKeePass">
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/TextBoxWithButtonStyle.xaml" />
<ResourceDictionary Source="Styles/HamburgerButtonStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -1,19 +1,14 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Data.Json;
using Windows.Storage;
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;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227
@@ -24,16 +19,20 @@ namespace ModernKeePass
/// </summary>
sealed partial class App : Application
{
public DatabaseHelper Database { get; set; } = new DatabaseHelper();
public Dictionary<string, IPwEntity> PendingDeleteEntities = new Dictionary<string, IPwEntity>();
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
InitializeComponent();
Suspending += OnSuspending;
}
#region Event Handlers
/// <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.
@@ -41,24 +40,33 @@ namespace ModernKeePass
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
OnLaunchOrActivated(e);
}
protected override void OnActivated(IActivatedEventArgs args)
{
OnLaunchOrActivated(args);
}
private void OnLaunchOrActivated(IActivatedEventArgs e)
{
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
this.DebugSettings.EnableFrameRateCounter = true;
DebugSettings.EnableFrameRateCounter = true;
}
#endif
Frame rootFrame = Window.Current.Content as Frame;
var rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame = new Frame {Language = Windows.Globalization.ApplicationLanguages.Languages[0]};
// Set the default language
rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];
rootFrame.NavigationFailed += OnNavigationFailed;
@@ -71,13 +79,30 @@ namespace ModernKeePass
Window.Current.Content = rootFrame;
}
if (e is LaunchActivatedEventArgs)
{
var lauchActivatedEventArgs = (LaunchActivatedEventArgs) e;
if (rootFrame.Content == null)
{
// 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(MainPage), e.Arguments);
rootFrame.Navigate(typeof(MainPage), lauchActivatedEventArgs.Arguments);
}
else
{
// App is "launched" via the Toast Activation event
UndoEntityDelete(lauchActivatedEventArgs.Arguments);
}
}
// This is only available on Windows 10...
/*else if (e is ToastNotificationActivatedEventArgs)
{
var toastActivationArgs = e as ToastNotificationActivatedEventArgs;
// Parse the query string (using QueryString.NET)
UndoEntityDelete(QueryString.Parse(toastActivationArgs.Argument));
}*/
// Ensure the current window is active
Window.Current.Activate();
}
@@ -102,8 +127,32 @@ namespace ModernKeePass
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
Database.Save();
deferral.Complete();
}
/// <summary>
/// Invoked when application is launched from opening a file in Windows Explorer
/// </summary>
/// <param name="args">Details about the file being opened</param>
protected override void OnFileActivated(FileActivatedEventArgs args)
{
base.OnFileActivated(args);
var rootFrame = new Frame();
Database.DatabaseFile = args.Files[0] as StorageFile;
rootFrame.Navigate(typeof(MainPage), args);
Window.Current.Content = rootFrame;
Window.Current.Activate();
}
#endregion
private void UndoEntityDelete(string arguments)
{
if (arguments == null) return;
var args = JsonObject.Parse(arguments);
var entity = PendingDeleteEntities[args["entityId"].GetString()];
PendingDeleteEntities.Remove(args["entityId"].GetString());
entity.UndoDelete();
}
}
}

View File

@@ -0,0 +1,4 @@
MainPackage=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.4.0.19_AnyCPU.appx
SymbolPackage=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\AppPackages\ModernKeePass_1.4.0.19_Test\ModernKeePass_1.4.0.19_AnyCPU.appxsym
ResourcePack=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.4.0.19_scale-140.appx
ResourcePack=C:\Users\GBE\Source\Repos\ModernKeePass\ModernKeePass\bin\Release\ModernKeePass_1.4.0.19_scale-180.appx

View File

@@ -0,0 +1,86 @@
using System;
using Windows.Storage;
using ModernKeePass.ViewModels;
using ModernKeePassLib;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Keys;
using ModernKeePassLib.Serialization;
namespace ModernKeePass.Common
{
public class DatabaseHelper
{
public enum DatabaseStatus
{
Closed = 0,
Opening = 1,
Opened = 2
}
private PwDatabase _pwDatabase = new PwDatabase();
private StorageFile _databaseFile;
public GroupVm RootGroup { get; set; }
public DatabaseStatus Status { get; private set; } = DatabaseStatus.Closed;
public string Name => DatabaseFile?.Name;
public StorageFile DatabaseFile
{
get { return _databaseFile; }
set
{
_databaseFile = value;
Status = DatabaseStatus.Opening;
}
}
public string Open(string password, bool createNew = false)
{
var key = new CompositeKey();
try
{
key.AddUserKey(new KcpPassword(password));
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);
}
}
catch (ArgumentNullException)
{
return "Password cannot be empty";
}
catch (InvalidCompositeKeyException)
{
return "Wrong password";
}
catch (Exception ex)
{
return ex.Message;
}
return string.Empty;
}
internal void Save(StorageFile file)
{
DatabaseFile = file;
_pwDatabase.SaveAs(IOConnectionInfo.FromFile(DatabaseFile), true, new NullStatusLogger());
Status = DatabaseStatus.Opened;
}
public void Save()
{
if (_pwDatabase != null && _pwDatabase.IsOpen)
_pwDatabase.Save(new NullStatusLogger());
}
public void Close()
{
_pwDatabase?.Close();
Status = DatabaseStatus.Closed;
}
}
}

View File

@@ -0,0 +1,28 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace ModernKeePass.Common
{
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T property, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(property, value))
{
return false;
}
property = value;
OnPropertyChanged(propertyName);
return true;
}
}
}

View File

@@ -46,6 +46,14 @@ namespace ModernKeePass.Common
this.Add(item.Key, item.Value);
}
public void AddRange(IEnumerable<KeyValuePair<string, object>> values)
{
foreach (var value in values)
{
Add(value);
}
}
public bool Remove(string key)
{
if (this._dictionary.Remove(key))

View File

@@ -0,0 +1,99 @@
using System;
using Windows.Data.Json;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
using Windows.UI.Xaml;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Common
{
public static class ToastNotificationHelper
{
public static /*async*/ void ShowUndoToast(string entityType, IPwEntity entity)
{
// This is for Windows 10
// Construct the visuals of the toast
/*var visual = new ToastVisual
{
BindingGeneric = new ToastBindingGeneric
{
Children =
{
new AdaptiveText
{
Text = $"{entityType} {entity.Name} deleted."
}
}
}
};
// Construct the actions for the toast (inputs and buttons)
var actions = new ToastActionsCustom
{
Buttons =
{
new ToastButton("Undo", new QueryString
{
{ "action", "undo" },
{ "entityType", entityType },
{ "entityId", entity.Id }
}.ToString())
}
};
// Now we can construct the final toast content
var toastContent = new ToastContent
{
Visual = visual,
Actions = actions,
// Arguments when the user taps body of toast
Launch = new QueryString()
{
{ "action", "undo" },
{ "entityType", "group" },
{ "entityId", entity.Id }
}.ToString()
};
// And create the toast notification
var toastXml = new XmlDocument();
toastXml.LoadXml(toastContent.GetContent());
var toast = new ToastNotification(toastXml) {ExpirationTime = DateTime.Now.AddSeconds(5)};
toast.Dismissed += Toast_Dismissed;
*/
var notificationXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var toastElements = notificationXml.GetElementsByTagName("text");
toastElements[0].AppendChild(notificationXml.CreateTextNode($"{entityType} {entity.Name} deleted"));
toastElements[1].AppendChild(notificationXml.CreateTextNode("Click me to undo"));
var toastNode = notificationXml.SelectSingleNode("/toast");
var launch = new JsonObject
{
{"entityType", JsonValue.CreateStringValue(entityType)},
{"entityId", JsonValue.CreateStringValue(entity.Id)}
};
((XmlElement)toastNode)?.SetAttribute("launch", launch.Stringify());
var toast = new ToastNotification(notificationXml)
{
ExpirationTime = DateTime.Now.AddSeconds(5)
};
toast.Dismissed += Toast_Dismissed;
ToastNotificationManager.CreateToastNotifier().Show(toast);
}
private static void Toast_Dismissed(ToastNotification sender, ToastDismissedEventArgs args)
{
var toastNode = sender.Content.SelectSingleNode("/toast");
if (toastNode == null) return;
var launchArguments = JsonObject.Parse(((XmlElement)toastNode).GetAttribute("launch"));
var app = (App)Application.Current;
var entity = app.PendingDeleteEntities[launchArguments["entityId"].GetString()];
app.PendingDeleteEntities.Remove(launchArguments["entityId"].GetString());
entity.CommitDelete();
}
}
}

View File

@@ -0,0 +1,18 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace ModernKeePass.Controls
{
public class FirstItemDataTemplateSelector: DataTemplateSelector
{
public DataTemplate FirstItem { get; set; }
public DataTemplate OtherItem { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var itemsControl = ItemsControl.ItemsControlFromItemContainer(container);
var returnTemplate = itemsControl.IndexFromContainer(container) == 0 ? FirstItem : OtherItem;
return returnTemplate;
}
}
}

View File

@@ -0,0 +1,20 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Controls
{
public class ListViewWithDisable: ListView
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var container = element as ListViewItem;
var binaryItem = item as IIsEnabled;
if (container == null || binaryItem == null) return;
container.IsEnabled = binaryItem.IsEnabled;
container.IsHitTestVisible = binaryItem.IsEnabled;
}
}
}

View File

@@ -0,0 +1,331 @@
<UserControl x:Name="UserControl"
x:Class="ModernKeePass.Controls.OpenDatabaseUserControl"
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"
d:DesignHeight="60"
d:DesignWidth="335" Loaded="UserControl_Loaded">
<UserControl.Resources>
<Style TargetType="PasswordBox" x:Name="PasswordBoxWithButtonStyle">
<Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}" />
<Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}" />
<Setter Property="Foreground" Value="{ThemeResource TextBoxForegroundThemeBrush}" />
<Setter Property="Background" Value="{ThemeResource TextBoxBackgroundThemeBrush}" />
<Setter Property="SelectionHighlightColor" Value="{ThemeResource TextSelectionHighlightColorThemeBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource TextBoxBorderThemeBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
<Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Grid>
<Grid.Resources>
<Style x:Name="RevealButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="BorderElement"
BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}"
BorderThickness="{TemplateBinding BorderThickness}"/>
<Border x:Name="BackgroundElement"
Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}"
Margin="{TemplateBinding BorderThickness}">
<TextBlock x:Name="GlyphElement"
Foreground="{ThemeResource TextBoxButtonForegroundThemeBrush}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontStyle="Normal"
Text="&#xE052;"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
AutomationProperties.AccessibilityView="Raw"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Name="GotoButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" >
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="BorderElement"
BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}"
BorderThickness="{TemplateBinding BorderThickness}"/>
<Border x:Name="BackgroundElement"
Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}"
Margin="{TemplateBinding BorderThickness}">
<TextBlock x:Name="GlyphElement"
Foreground="{ThemeResource TextBoxButtonForegroundThemeBrush}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontStyle="Normal"
Text="&#xE111;"
Padding="4,0,4,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
AutomationProperties.AccessibilityView="Raw"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlBackgroundThemeOpacity}" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlBorderThemeOpacity}" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlPointerOverBackgroundThemeOpacity}" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlPointerOverBorderThemeOpacity}" />
</Storyboard>
</VisualState>
<VisualState x:Name="Focused" />
</VisualStateGroup>
<VisualStateGroup x:Name="ButtonStates">
<VisualState x:Name="ButtonVisible">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RevealButton"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="ButtonCollapsed" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border x:Name="BackgroundElement"
Grid.Row="1"
Background="{TemplateBinding Background}"
Margin="{TemplateBinding BorderThickness}"
Grid.ColumnSpan="3"
Grid.RowSpan="1"/>
<Border x:Name="BorderElement"
Grid.Row="1"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Grid.ColumnSpan="3"
Grid.RowSpan="1"/>
<ContentPresenter x:Name="HeaderContentPresenter"
Grid.Row="0"
Foreground="{ThemeResource TextBoxForegroundHeaderThemeBrush}"
Margin="0,4,0,4"
Grid.ColumnSpan="3"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontWeight="Semilight" />
<ScrollViewer x:Name="ContentElement"
Grid.Row="1"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsTabStop="False"
ZoomMode="Disabled"
AutomationProperties.AccessibilityView="Raw"/>
<ContentControl x:Name="PlaceholderTextContentPresenter"
Grid.Row="1"
Foreground="{ThemeResource TextBoxPlaceholderTextThemeBrush}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsTabStop="False"
Grid.ColumnSpan="3"
Content="{TemplateBinding PlaceholderText}"
IsHitTestVisible="False"/>
<Button x:Name="RevealButton"
Grid.Row="1"
Style="{StaticResource RevealButtonStyle}"
BorderThickness="{TemplateBinding BorderThickness}"
IsTabStop="False"
Grid.Column="1"
Visibility="Collapsed"
FontSize="{TemplateBinding FontSize}"
VerticalAlignment="Stretch"/>
<Button x:Name="GotoButton"
Grid.Row="1"
Style="{StaticResource GotoButtonStyle}"
BorderThickness="{TemplateBinding BorderThickness}"
IsTabStop="False"
Grid.Column="2"
Visibility="Visible"
FontSize="{TemplateBinding FontSize}"
VerticalAlignment="Stretch"
Click="OpenButton_OnClick" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<StackPanel>
<PasswordBox x:Name="PasswordBox" Password="{Binding Password, ElementName=UserControl, Mode=TwoWay}" Width="300" IsPasswordRevealButtonEnabled="True" KeyDown="PasswordBox_KeyDown" PlaceholderText="Password" Style="{StaticResource PasswordBoxWithButtonStyle}"/>
<TextBlock x:Name="StatusTextBlock" Height="32" Width="auto" Foreground="#FFBF6969" FontSize="16" FontWeight="Bold" />
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,72 @@
using System;
using System.Threading.Tasks;
using Windows.System;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
using ModernKeePass.Common;
using ModernKeePass.Events;
using ModernKeePassLib.Cryptography;
// 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
{
public sealed partial class OpenDatabaseUserControl
{
public bool CreateNew
{
get { return (bool)GetValue(CreateNewProperty); }
set { SetValue(CreateNewProperty, value); }
}
public static readonly DependencyProperty CreateNewProperty =
DependencyProperty.Register(
"CreateNew",
typeof(bool),
typeof(OpenDatabaseUserControl),
new PropertyMetadata(false, (o, args) => { }));
public string Password
{
get { return (string)GetValue(PasswordProperty); }
set { SetValue(PasswordProperty, value); }
}
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.Register(
"Password",
typeof(string),
typeof(OpenDatabaseUserControl),
new PropertyMetadata(string.Empty, (o, args) => { }));
public OpenDatabaseUserControl()
{
InitializeComponent();
}
public event PasswordCheckingEventHandler ValidationChecking;
public delegate void PasswordCheckingEventHandler(object sender, EventArgs e);
public event PasswordCheckedEventHandler ValidationChecked;
public delegate void PasswordCheckedEventHandler(object sender, PasswordEventArgs e);
private void OpenButton_OnClick(object sender, RoutedEventArgs e)
{
ValidationChecking?.Invoke(this, new EventArgs());
var app = (App)Application.Current;
StatusTextBlock.Text = app.Database.Open(PasswordBox.Password, CreateNew);
if (app.Database.Status == DatabaseHelper.DatabaseStatus.Opened)
ValidationChecked?.Invoke(this, new PasswordEventArgs(app.Database.RootGroup));
}
private void PasswordBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Enter) OpenButton_OnClick(null, null);
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(
() => Dispatcher.RunAsync(CoreDispatcherPriority.Low,
() => PasswordBox.Focus(FocusState.Programmatic)));
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace ModernKeePass.Controls
{
public class TextBoxWithButton : TextBox
{
public event EventHandler<RoutedEventArgs> GotoClick;
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
var extraButton = GetTemplateChild("GotoButton") as Button;
if (extraButton != null)
{
extraButton.Click += (sender, e) => GotoClick?.Invoke(sender, e);
}
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
namespace ModernKeePass.Converters
{
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var boolean = value is bool ? (bool) value : false;
return boolean ? Visibility.Visible : Visibility.Collapsed;
}
// No need to implement this
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
var visibility = value is Visibility ? (Visibility) value : Visibility.Visible;
switch (visibility)
{
case Visibility.Visible: return true;
case Visibility.Collapsed: return false;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Drawing;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
namespace ModernKeePass.Converters
{
public class ColorToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var color = value is Color ? (Color?) value : Color.Empty;
if (color == Color.Empty && parameter is SolidColorBrush) return (SolidColorBrush) parameter;
return new SolidColorBrush(Windows.UI.Color.FromArgb(
color.Value.A,
color.Value.R,
color.Value.G,
color.Value.B));
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using Windows.UI;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
namespace ModernKeePass.Converters
{
public class DoubleToForegroungBrushComplexityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
try
{
var currentValue = (double) value;
var maxValue = double.Parse(parameter as string);
var green = System.Convert.ToByte(currentValue / maxValue * byte.MaxValue);
var red = (byte) (byte.MaxValue - green);
return new SolidColorBrush(Color.FromArgb(255, red, green, 0));
}
catch (OverflowException)
{
return new SolidColorBrush(Color.FromArgb(255, 0, byte.MaxValue, 0));
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
namespace ModernKeePass.Converters
{
public class InverseBooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var boolean = value is bool ? (bool)value : false;
return boolean ? Visibility.Collapsed : Visibility.Visible;
}
// No need to implement this
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
var visibility = value is Visibility ? (Visibility)value : Visibility.Visible;
switch (visibility)
{
case Visibility.Visible: return false;
case Visibility.Collapsed: return true;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using Windows.UI.Xaml.Data;
namespace ModernKeePass.Converters
{
public class PluralizationConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var pluralizationOptionString = parameter as string;
var pluralizationOptions = pluralizationOptionString?.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
if (pluralizationOptions == null || pluralizationOptions.Length != 2) return string.Empty;
var count = value is int ? (int) value : 0;
var text = count == 1 ? pluralizationOptions[0] : pluralizationOptions[1];
return $"{count} {text}";
}
// No need to implement this
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using Windows.UI.Xaml.Data;
namespace ModernKeePass.Converters
{
public class ProgressBarLegalValuesConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var legalValuesOptionString = parameter as string;
var legalValuesOptions = legalValuesOptionString?.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
if (legalValuesOptions == null || legalValuesOptions.Length != 2) return 0;
var minValue = double.Parse(legalValuesOptions[0]);
var maxValue = double.Parse(legalValuesOptions[1]);
var count = value is double ? (double)value : 0;
if (count > maxValue) return maxValue;
if (count < minValue) return minValue;
return count;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using Windows.UI.Xaml.Data;
namespace ModernKeePass.Converters
{
public class TextToWidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var fontSize = double.Parse(parameter as string);
var text = value as string;
return text?.Length * fontSize ?? 0;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using ModernKeePass.ViewModels;
namespace ModernKeePass.Events
{
public class PasswordEventArgs: EventArgs
{
public GroupVm RootGroup { get; set; }
public PasswordEventArgs(GroupVm groupVm)
{
RootGroup = groupVm;
}
}
}

View File

@@ -0,0 +1,7 @@
namespace ModernKeePass.Interfaces
{
public interface IIsEnabled
{
bool IsEnabled { get; }
}
}

View File

@@ -0,0 +1,17 @@
using Windows.UI.Xaml.Controls;
using ModernKeePass.ViewModels;
namespace ModernKeePass.Interfaces
{
public interface IPwEntity
{
GroupVm ParentGroup { get; }
Symbol IconSymbol { get; }
string Id { get; }
string Name { get; set; }
bool IsEditMode { get; }
void CommitDelete();
void UndoDelete();
}
}

View File

@@ -1,41 +1,66 @@
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ModernKeePass"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ViewModels="using:ModernKeePass.ViewModels"
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:controls="using:ModernKeePass.Controls"
x:Class="ModernKeePass.MainPage"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
Background="{StaticResource ApplicationPageBackgroundThemeBrush}" >
<Page.DataContext>
<ViewModels:DatabaseVm/>
<viewModels:MainVm />
</Page.DataContext>
<Page.Resources>
<CollectionViewSource
x:Name="MenuItemsSource"
Source="{Binding MainMenuItems}"
IsSourceGrouped="True" />
</Page.Resources>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Column="0" Grid.Row="0" x:Name="selectButton" Content="Select password database" Click="Button_Click" HorizontalAlignment="Center" />
<Grid Grid.Column="0" Grid.Row="2" HorizontalAlignment="Center" Height="auto" VerticalAlignment="Top" Width="auto" Visibility="{Binding Visibility}" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" x:Name="passwordTextBlock" HorizontalAlignment="Left" TextWrapping="Wrap" Text="Password" VerticalAlignment="Center" Height="auto" Width="auto" FontSize="16" Margin="10,7,0,6" />
<PasswordBox Grid.Column="1" Grid.Row="0" x:Name="passwordBox" VerticalAlignment="Top" HorizontalAlignment="Right" Width="130" Password="{Binding Password, Mode=TwoWay}" IsPasswordRevealButtonEnabled="True" Margin="0,0,10,0"/>
<Button Grid.Column="1" Grid.Row="1" x:Name="openBbutton" Content="Open" HorizontalAlignment="Right" VerticalAlignment="Top" Click="openBbutton_Click" Margin="0,0,7,0" Width="auto"/>
<TextBlock Grid.Column="1" Grid.Row="2" x:Name="errorTextBlock" TextWrapping="Wrap" Text="{Binding ErrorMessage}" HorizontalAlignment="Right" Height="auto" Width="auto" Foreground="#FF9E0909" Margin="0,0,10,0"/>
<controls:ListViewWithDisable Grid.Column="0"
x:Name="MenuListView"
RequestedTheme="Dark"
SelectionChanged="ListView_SelectionChanged"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
ItemsSource="{Binding Source={StaticResource MenuItemsSource}}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="False">
<ListView.Header>
<TextBlock Text="ModernKeePass" FontWeight="Bold" FontSize="36" Margin="20" />
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding SymbolIcon}" />
<TextBlock Text="{Binding Title}" Margin="10,5,0,0" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="20,5,0,0" />
<Setter Property="Margin" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="DarkGray" Margin="20,0,0,0">
<Border Height="1" Width="240" HorizontalAlignment="Stretch"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</controls:ListViewWithDisable>
<Frame Grid.Column="2" Name="MenuFrame" Width="auto" Margin="0,60,0,0" />
</Grid>
</Page>

View File

@@ -1,7 +1,5 @@
using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Pages;
using ModernKeePass.ViewModels;
@@ -12,38 +10,25 @@ namespace ModernKeePass
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
public sealed partial class MainPage
{
public MainVm Model => (MainVm)DataContext;
public MainPage()
{
this.InitializeComponent();
InitializeComponent();
}
private async void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.List;
picker.SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add(".kdbx");
var file = await picker.PickSingleFileAsync();
if (file != null)
{
// Application now has read/write access to the picked file
DataContext = new DatabaseVm(file);
}
else
{
}
base.OnNavigatedTo(e);
DataContext = new MainVm(Frame, MenuFrame);
if (Model.SelectedItem == null) MenuFrame.Navigate(typeof(WelcomePage));
}
private void openBbutton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var database = DataContext as DatabaseVm;
database.Open();
if (database.IsOpen)
Frame.Navigate(typeof(GroupDetailPage), database.RootGroup);
Model.SelectedItem?.Destination.Navigate(Model.SelectedItem.PageType, Model.SelectedItem.Parameter);
}
}
}

View File

@@ -0,0 +1,82 @@
using Windows.UI.Xaml.Controls;
using ModernKeePassLib;
namespace ModernKeePass.Mappings
{
public static class PwIconToSegoeMapping
{
public static Symbol GetSymbolFromIcon(PwIcon icon)
{
switch (icon)
{
case PwIcon.Key: return Symbol.Permissions;
case PwIcon.World: return Symbol.World;
case PwIcon.Warning: return Symbol.Important;
case PwIcon.WorldComputer:
case PwIcon.DriveWindows:
case PwIcon.NetworkServer: return Symbol.MapDrive;
//case PwIcon.MarkedDirectory: return Symbol.;
case PwIcon.UserCommunication: return Symbol.ContactInfo;
//case PwIcon.Parts: return Symbol.;
case PwIcon.Notepad: return Symbol.Document;
//case PwIcon.WorldScoket: return Symbol.;
case PwIcon.Identity: return Symbol.Contact2;
//case PwIcon.PaperReady: return Symbol.;
case PwIcon.Digicam: return Symbol.Camera;
case PwIcon.IRCommunication: return Symbol.View;
case PwIcon.Energy: return Symbol.ZeroBars;
case PwIcon.Scanner: return Symbol.Scan;
case PwIcon.CDRom: return Symbol.Rotate;
case PwIcon.Monitor: return Symbol.Caption;
case PwIcon.EMailBox:
case PwIcon.EMail: return Symbol.Mail;
case PwIcon.Configuration: return Symbol.Setting;
case PwIcon.ClipboardReady: return Symbol.Paste;
case PwIcon.PaperNew: return Symbol.Page2;
case PwIcon.Screen: return Symbol.GoToStart;
case PwIcon.EnergyCareful: return Symbol.FourBars;
case PwIcon.Disk: return Symbol.Save;
//case PwIcon.Drive: return Symbol.;
//case PwIcon.PaperQ: return Symbol.;
//case PwIcon.TerminalEncrypted: return Symbol.;
//case PwIcon.Console: return Symbol.;
//case PwIcon.Printer: return Symbol.;
case PwIcon.ProgramIcons: return Symbol.GoToStart;
//case PwIcon.Run: return Symbol.;
case PwIcon.Settings:
case PwIcon.Tool: return Symbol.Repair;
//case PwIcon.Archive: return Symbol.;
case PwIcon.Count: return Symbol.MapDrive;
case PwIcon.Clock: return Symbol.Clock;
case PwIcon.EMailSearch: return Symbol.Find;
case PwIcon.PaperFlag: return Symbol.Flag;
//case PwIcon.Memory: return Symbol.;
case PwIcon.TrashBin: return Symbol.Delete;
case PwIcon.Expired: return Symbol.Cancel;
case PwIcon.Info: return Symbol.Help;
//case PwIcon.Package: return Symbol.;
case PwIcon.Folder:
case PwIcon.FolderOpen:
case PwIcon.FolderPackage: return Symbol.Folder;
//case PwIcon.LockOpen: return Symbol.;
case PwIcon.PaperLocked: return Symbol.ProtectedDocument;
case PwIcon.Checked: return Symbol.Accept;
case PwIcon.Pen: return Symbol.Edit;
case PwIcon.Thumbnail: return Symbol.BrowsePhotos;
case PwIcon.Book: return Symbol.Library;
case PwIcon.List: return Symbol.List;
case PwIcon.UserKey: return Symbol.ContactPresence;
case PwIcon.Home: return Symbol.Home;
case PwIcon.Star: return Symbol.OutlineStar;
//case PwIcon.Tux: return Symbol.;
//case PwIcon.Feather: return Symbol.;
//case PwIcon.Apple: return Symbol.;
//case PwIcon.Wiki: return Symbol.;
//case PwIcon.Money: return Symbol.;
case PwIcon.Certificate: return Symbol.PreviewLink;
case PwIcon.BlackBerry: return Symbol.CellPhone;
default: return Symbol.More;
}
}
}
}

View File

@@ -107,26 +107,71 @@
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Compile Include="Actions\SetupFocusAction.cs" />
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Common\DatabaseHelper.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="Common\ToastNotificationHelper.cs" />
<Compile Include="Controls\FirstItemDataTemplateSelector.cs" />
<Compile Include="Controls\ListViewWithDisable.cs" />
<Compile Include="Controls\OpenDatabaseUserControl.xaml.cs">
<DependentUpon>OpenDatabaseUserControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TextBoxWithButton.cs" />
<Compile Include="Converters\BooleanToVisibilityConverter.cs" />
<Compile Include="Converters\ColorToBrushConverter.cs" />
<Compile Include="Converters\DoubleToForegroungBrushComplexityConverter.cs" />
<Compile Include="Converters\InverseBooleanToVisibilityConverter.cs" />
<Compile Include="Converters\PluralizationConverter.cs" />
<Compile Include="Converters\ProgressBarLegalValuesConverter.cs" />
<Compile Include="Converters\TextToWidthConverter.cs" />
<Compile Include="Events\PasswordEventArgs.cs" />
<Compile Include="Interfaces\IIsEnabled.cs" />
<Compile Include="Interfaces\IPwEntity.cs" />
<Compile Include="MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
<Compile Include="Mappings\PwIconToSegoeMapping.cs" />
<Compile Include="Pages\AboutPage.xaml.cs">
<DependentUpon>AboutPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\NewDatabasePage.xaml.cs">
<DependentUpon>NewDatabasePage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\WelcomePage.xaml.cs">
<DependentUpon>WelcomePage.xaml</DependentUpon>
</Compile>
<Compile Include="ViewModels\Items\MainMenuItemVm.cs" />
<Compile Include="ViewModels\Items\RecentItemVm.cs" />
<Compile Include="Pages\EntryDetailPage.xaml.cs">
<DependentUpon>EntryDetailPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\GroupDetailPage.xaml.cs">
<DependentUpon>GroupDetailPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\OpenDatabasePage.xaml.cs">
<DependentUpon>OpenDatabasePage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\RecentDatabasesPage.xaml.cs">
<DependentUpon>RecentDatabasesPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\SaveDatabasePage.xaml.cs">
<DependentUpon>SaveDatabasePage.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ViewModels\DatabaseVm.cs" />
<Compile Include="ViewModels\EntryVm.cs" />
<Compile Include="ViewModels\GroupVm.cs" />
<Compile Include="ViewModels\MainVm.cs" />
<Compile Include="ViewModels\NewVm.cs" />
<Compile Include="ViewModels\OpenVm.cs" />
<Compile Include="ViewModels\RecentVm.cs" />
<Compile Include="ViewModels\SaveVm.cs" />
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
@@ -134,7 +179,9 @@
</AppxManifest>
<None Include="ModernKeePass_StoreKey.pfx" />
<None Include="ModernKeePass_TemporaryKey.pfx" />
<None Include="packages.config" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<None Include="Package.StoreAssociation.xml" />
@@ -144,10 +191,18 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="Controls\OpenDatabaseUserControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MainPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Pages\AboutPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\EntryDetailPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -156,18 +211,68 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\NewDatabasePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\OpenDatabasePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\RecentDatabasesPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\SaveDatabasePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\WelcomePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\HamburgerButtonStyle.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Page>
<Page Include="Styles\TextBoxWithButtonStyle.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Page>
</ItemGroup>
<ItemGroup>
<Reference Include="ModernKeePassLib, Version=2.19.0.15913, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ModernKeePassLib.2.19.2950\lib\netstandard1.2\ModernKeePassLib.dll</HintPath>
<Reference Include="BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942, processorArchitecture=MSIL">
<HintPath>..\packages\Portable.BouncyCastle.1.8.1.3\lib\netstandard1.0\BouncyCastle.Crypto.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Toolkit.Uwp.Notifications, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Toolkit.Uwp.Notifications.2.0.0\lib\dotnet\Microsoft.Toolkit.Uwp.Notifications.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ModernKeePassLib, Version=2.28.1.4000, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ModernKeePassLib.2.28.4000\lib\netstandard1.2\ModernKeePassLib.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Splat.2.0.0\lib\Portable-Win81+Wpa81\Splat.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\win8\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Validation, Version=2.4.0.0, Culture=neutral, PublicKeyToken=2fc06f0d701809a7, processorArchitecture=MSIL">
<HintPath>..\packages\Validation.2.4.15\lib\portable-net45+win8+wp8+wpa81\Validation.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<SDKReference Include="BehaviorsXamlSDKManaged, Version=12.0">
<Name>BehaviorsXamlSDKManaged</Name>
</SDKReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Content Include="Assets\Logo.scale-100.png" />
<Content Include="Assets\Logo.scale-140.png" />
@@ -220,7 +325,9 @@
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\NETStandard.Library.2.0.0\build\NETStandard.Library.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NETStandard.Library.2.0.0\build\NETStandard.Library.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
</Target>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -0,0 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp70</s:String>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=viewmodels_005Citems/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

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.1.0.2" />
<Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.4.0.19" />
<Properties>
<DisplayName>ModernKeePass</DisplayName>
<PublisherDisplayName>wismna</PublisherDisplayName>
@@ -15,7 +15,7 @@
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="ModernKeePass.App">
<m2:VisualElements DisplayName="ModernKeePass" Square150x150Logo="Assets\ModernKeePass-Logo.png" Square30x30Logo="Assets\ModernKeePass-SmallLogo.png" Description="A port of the KeePass application for the Windows store. For now, it only features read-only capabilites and only opens password protected databases." ForegroundText="light" BackgroundColor="#464646" ToastCapable="true">
<m2:VisualElements DisplayName="ModernKeePass" Square150x150Logo="Assets\ModernKeePass-Logo.png" Square30x30Logo="Assets\ModernKeePass-SmallLogo.png" Description="A port of the KeePass application for the Windows store. You can create, open and edit KeePass 2.x password databases in a modern fashion." ForegroundText="light" BackgroundColor="#464646" ToastCapable="true">
<m2:DefaultTile Square310x310Logo="Assets\Square310x310Logo.png" Wide310x150Logo="Assets\Wide310x150Logo.png" Square70x70Logo="Assets\Square70x70Logo.png">
</m2:DefaultTile>
<m2:SplashScreen Image="Assets\ModernKeePass-SplashScreen.png" />
@@ -28,6 +28,22 @@
</SupportedFileTypes>
</FileOpenPicker>
</Extension>
<Extension Category="windows.fileTypeAssociation">
<FileTypeAssociation Name="kdbx">
<DisplayName>KeePass 2.x database</DisplayName>
<EditFlags OpenIsSafe="true" />
<SupportedFileTypes>
<FileType ContentType="application/xml">.kdbx</FileType>
</SupportedFileTypes>
</FileTypeAssociation>
</Extension>
<Extension Category="windows.fileSavePicker">
<FileSavePicker>
<SupportedFileTypes>
<FileType>.kdbx</FileType>
</SupportedFileTypes>
</FileSavePicker>
</Extension>
</Extensions>
</Application>
</Applications>

View File

@@ -0,0 +1,18 @@
<Page
x:Class="ModernKeePass.Pages.AboutPage"
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 Style="{StaticResource HeaderTextBlockStyle}" Margin="0,-20,0,20">About</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}">ModernKeePass version 1.4</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">A modern password manager for the Windows Store</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">Homepage: <Hyperlink NavigateUri="https://github.com/wismna/ModernKeePass">https://github.com/wismna/ModernKeePass</Hyperlink></TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}">Credits:</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">Dominik Reichl for the KeePass application and file format</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">ArtjomP for his PCL adapatation of the KeePass Library</TextBlock>
</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.Pages
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class AboutPage
{
public AboutPage()
{
InitializeComponent();
}
}
}

View File

@@ -1,17 +1,354 @@
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ModernKeePass.Pages"
xmlns:common="using:ModernKeePass.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ViewModels="using:ModernKeePass.ViewModels"
x:Name="pageRoot"
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:local="using:ModernKeePass.Controls"
x:Name="PageRoot"
x:Class="ModernKeePass.Pages.EntryDetailPage"
mc:Ignorable="d">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<Style TargetType="PasswordBox" x:Name="PasswordBoxWithButtonStyle">
<Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}" />
<Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}" />
<Setter Property="Foreground" Value="{ThemeResource TextBoxForegroundThemeBrush}" />
<Setter Property="Background" Value="{ThemeResource TextBoxBackgroundThemeBrush}" />
<Setter Property="SelectionHighlightColor" Value="{ThemeResource TextSelectionHighlightColorThemeBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource TextBoxBorderThemeBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
<Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Grid>
<Grid.Resources>
<Style x:Name="RevealButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="BorderElement"
BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}"
BorderThickness="{TemplateBinding BorderThickness}"/>
<Border x:Name="BackgroundElement"
Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}"
Margin="{TemplateBinding BorderThickness}">
<TextBlock x:Name="GlyphElement"
Foreground="{ThemeResource TextBoxButtonForegroundThemeBrush}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontStyle="Normal"
Text="&#xE052;"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
AutomationProperties.AccessibilityView="Raw"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Name="GeneratorButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" >
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="BorderElement"
BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}"
BorderThickness="{TemplateBinding BorderThickness}"/>
<Border x:Name="BackgroundElement"
Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}"
Margin="{TemplateBinding BorderThickness}">
<TextBlock x:Name="GlyphElement"
Foreground="{ThemeResource TextBoxButtonForegroundThemeBrush}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontStyle="Normal"
Text="&#xE15E;"
Padding="4,0,4,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
AutomationProperties.AccessibilityView="Raw"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlBackgroundThemeOpacity}" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlBorderThemeOpacity}" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlPointerOverBackgroundThemeOpacity}" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlPointerOverBorderThemeOpacity}" />
</Storyboard>
</VisualState>
<VisualState x:Name="Focused" />
</VisualStateGroup>
<VisualStateGroup x:Name="ButtonStates">
<VisualState x:Name="ButtonVisible">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RevealButton"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="ButtonCollapsed" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border x:Name="BackgroundElement"
Grid.Row="1"
Background="{TemplateBinding Background}"
Margin="{TemplateBinding BorderThickness}"
Grid.ColumnSpan="3"
Grid.RowSpan="1"/>
<Border x:Name="BorderElement"
Grid.Row="1"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Grid.ColumnSpan="3"
Grid.RowSpan="1"/>
<ContentPresenter x:Name="HeaderContentPresenter"
Grid.Row="0"
Foreground="{ThemeResource TextBoxForegroundHeaderThemeBrush}"
Margin="0,4,0,4"
Grid.ColumnSpan="3"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontWeight="Semilight" />
<ScrollViewer x:Name="ContentElement"
Grid.Row="1"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsTabStop="False"
ZoomMode="Disabled"
AutomationProperties.AccessibilityView="Raw"/>
<ContentControl x:Name="PlaceholderTextContentPresenter"
Grid.Row="1"
Foreground="{ThemeResource TextBoxPlaceholderTextThemeBrush}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsTabStop="False"
Grid.ColumnSpan="3"
Content="{TemplateBinding PlaceholderText}"
IsHitTestVisible="False"/>
<Button x:Name="RevealButton"
Grid.Row="1"
Style="{StaticResource RevealButtonStyle}"
BorderThickness="{TemplateBinding BorderThickness}"
IsTabStop="False"
Grid.Column="1"
Visibility="Collapsed"
FontSize="{TemplateBinding FontSize}"
VerticalAlignment="Stretch"/>
<Button x:Name="GeneratorButton"
Grid.Row="1"
Style="{StaticResource GeneratorButtonStyle}"
BorderThickness="{TemplateBinding BorderThickness}"
IsTabStop="False"
Grid.Column="2"
Visibility="Visible"
FontSize="{TemplateBinding FontSize}"
VerticalAlignment="Stretch" >
<Button.Flyout>
<Flyout>
<StackPanel>
<TextBlock>Password Length:</TextBlock>
<Slider Value="{Binding PasswordLength, Mode=TwoWay}" Margin="0,-10,0,-20" />
<CheckBox IsChecked="{Binding UpperCasePatternSelected, Mode=TwoWay}">Upper case (A, B, C, ...)</CheckBox>
<CheckBox IsChecked="{Binding LowerCasePatternSelected, Mode=TwoWay}">Lower case (a, b, c, ...)</CheckBox>
<CheckBox IsChecked="{Binding DigitsPatternSelected, Mode=TwoWay}">Digits (0, 1, 2, ...)</CheckBox>
<CheckBox IsChecked="{Binding MinusPatternSelected, Mode=TwoWay}">Minus (-)</CheckBox>
<CheckBox IsChecked="{Binding UnderscorePatternSelected, Mode=TwoWay}">Underscore (_)</CheckBox>
<CheckBox IsChecked="{Binding SpacePatternSelected, Mode=TwoWay}">Space ( )</CheckBox>
<CheckBox IsChecked="{Binding SpecialPatternSelected, Mode=TwoWay}">Special (!, $, %, ...)</CheckBox>
<CheckBox IsChecked="{Binding BracketsPatternSelected, Mode=TwoWay}">Brackets ([], {}, (), ...)</CheckBox>
<Button Click="PasswordGenerationButton_Click">Generate</Button>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Page.DataContext>
<ViewModels:EntryVm/>
<viewModels:EntryVm/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
@@ -25,44 +362,65 @@
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="1" x:Name="contentRegion">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="userTextBlock" Grid.Row="0" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="User name or login" VerticalAlignment="Top" FontSize="18"/>
<TextBox x:Name="userTextBox" Grid.Row="1" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding UserName, Mode=TwoWay}" VerticalAlignment="Top" Width="250" AllowDrop="True"/>
<TextBlock x:Name="passwordTextBlock" Grid.Row="2" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Password" VerticalAlignment="Top" FontSize="18"/>
<PasswordBox x:Name="passwordBox" Grid.Row="3" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Password="{Binding Password, Mode=TwoWay}" IsPasswordRevealButtonEnabled="True" Width="250" AllowDrop="True"/>
<TextBox x:Name="passwordTextBox" Grid.Row="3" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding Password, Mode=TwoWay}" VerticalAlignment="Top" Width="250" FontSize="14,667" MaxLength="256" AllowDrop="True" Visibility="Collapsed"/>
<CheckBox x:Name="checkBox" Grid.Row="4" Content="Show password" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Checked="checkBox_Checked" Unchecked="checkBox_Unchecked"/>
<TextBlock x:Name="urlTextBlock" Grid.Row="5" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="URL" VerticalAlignment="Top" FontSize="18"/>
<TextBox x:Name="urlTextBox" Grid.Row="6" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding URL, Mode=TwoWay}" VerticalAlignment="Top" Width="250" FontSize="14,667" MaxLength="256" AllowDrop="True"/>
<TextBlock x:Name="notesTextBlock" Grid.Row="7" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Notes" VerticalAlignment="Top" FontSize="18"/>
<TextBox x:Name="notesTextBox" Grid.Row="8" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding Notes, Mode=TwoWay}" VerticalAlignment="Top" Width="250" Height="32" IsSpellCheckEnabled="True" AllowDrop="True"/>
</Grid>
<StackPanel Grid.Row="1" Margin="20,0,0,0" >
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="0,20,0,0"/>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Margin" Value="0,20,0,0"/>
</Style>
</StackPanel.Resources>
<TextBlock TextWrapping="Wrap" Text="User name or login" FontSize="18"/>
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding UserName, Mode=TwoWay}" Width="350" Height="32" />
<TextBlock TextWrapping="Wrap" Text="Password" 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}" />
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Password, Mode=TwoWay}" Width="350" Height="32" Visibility="{Binding IsRevealPassword, Converter={StaticResource BooleanToVisibilityConverter}}" />
<CheckBox HorizontalAlignment="Left" Margin="-3,0,0,0" Content="Show password" 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}" GotoClick="UrlButton_Click" />
<TextBlock TextWrapping="Wrap" Text="Notes" 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}">Expiration date</CheckBox>
<StackPanel Orientation="Horizontal" IsHitTestVisible="{Binding HasExpirationDate}">
<SymbolIcon Symbol="Important" Foreground="DarkRed" Visibility="{Binding HasExpired, Converter={StaticResource BooleanToVisibilityConverter}}">
<ToolTipService.ToolTip>
<ToolTip Content="Password has expired" />
</ToolTipService.ToolTip>
</SymbolIcon>
<DatePicker Margin="0,0,20,0" Date="{Binding ExpiryDate, Mode=TwoWay}" ></DatePicker>
<TimePicker Time="{Binding ExpiryTime, Mode=TwoWay}"></TimePicker>
</StackPanel>
</StackPanel>
<!-- Bouton Précédent et titre de la page -->
<Grid>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
<Button Margin="39,0,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
VerticalAlignment="Center"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"/>
<TextBlock x:Name="pageTitle" Text="{Binding Title}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
<TextBox
Grid.Column="1"
x:Name="TitleTextBox"
Text="{Binding Name, Mode=TwoWay}"
Style="{StaticResource HeaderTextBoxStyle}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
IsHitTestVisible="{Binding IsEditMode}"
TextWrapping="NoWrap"
VerticalAlignment="Center"
Margin="0,0,30,0"
PlaceholderText="New entry name..."/>
<CommandBar Grid.Column="2" Background="Transparent" IsOpen="True" VerticalAlignment="Center" Margin="0,20,0,0">
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}" />
<AppBarButton Icon="Delete" Label="Delete" Click="AppBarButton_Click" />
</CommandBar>
</Grid>
</Grid>
</Page>

View File

@@ -1,19 +1,13 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using ModernKeePass.Common;
using Windows.Foundation;
using Windows.Foundation.Collections;
using System.Threading.Tasks;
using Windows.UI.Core;
using Windows.UI.Popups;
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;
using ModernKeePass.Common;
using ModernKeePass.ViewModels;
using ModernKeePassLib.Cryptography.PasswordGenerator;
// 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
@@ -23,24 +17,23 @@ namespace ModernKeePass.Pages
/// Page affichant les détails d'un élément au sein d'un groupe, offrant la possibilité de
/// consulter les autres éléments qui appartiennent au même groupe.
/// </summary>
public sealed partial class EntryDetailPage : Page
public sealed partial class EntryDetailPage
{
private NavigationHelper navigationHelper;
public EntryVm Model => (EntryVm) DataContext;
/// <summary>
/// NavigationHelper est utilisé sur chaque page pour faciliter la navigation et
/// gestion de la durée de vie des processus
/// </summary>
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
public NavigationHelper NavigationHelper => navigationHelper;
public EntryDetailPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
InitializeComponent();
navigationHelper = new NavigationHelper(this);
navigationHelper.LoadState += navigationHelper_LoadState;
}
/// <summary>
@@ -51,17 +44,10 @@ namespace ModernKeePass.Pages
/// Source de l'événement ; en général <see cref="Common.NavigationHelper"/>
/// </param>
/// <param name="e">Données d'événement qui fournissent le paramètre de navigation transmis à
/// <see cref="Frame.Navigate(Type, Object)"/> lors de la requête initiale de cette page et
/// <see cref="Frame.Navigate(Type, object)"/> lors de la requête initiale de cette page et
/// un dictionnaire d'état conservé par cette page durant une session
/// antérieure. L'état n'aura pas la valeur Null lors de la première visite de la page.</param>
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
object navigationParameter;
if (e.PageState != null && e.PageState.ContainsKey("SelectedItem"))
{
navigationParameter = e.PageState["SelectedItem"];
}
}
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e) {}
#region Inscription de NavigationHelper
@@ -77,10 +63,12 @@ namespace ModernKeePass.Pages
protected override void OnNavigatedTo(NavigationEventArgs e)
{
navigationHelper.OnNavigatedTo(e);
if (e.Parameter is EntryVm)
{
DataContext = e.Parameter as EntryVm;
}
if (!(e.Parameter is EntryVm)) return;
DataContext = (EntryVm)e.Parameter;
if (Model.IsEditMode)
Task.Factory.StartNew(
() => Dispatcher.RunAsync(CoreDispatcherPriority.Low,
() => TitleTextBox.Focus(FocusState.Programmatic)));
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
@@ -90,16 +78,48 @@ namespace ModernKeePass.Pages
#endregion
private void checkBox_Checked(object sender, RoutedEventArgs e)
private async void AppBarButton_Click(object sender, RoutedEventArgs e)
{
passwordBox.Visibility = Visibility.Collapsed;
passwordTextBox.Visibility = Visibility.Visible;
// Create the message dialog and set its content
var messageDialog = new MessageDialog("Are you sure you want to delete this entry?");
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
messageDialog.Commands.Add(new UICommand("Delete", delete =>
{
ToastNotificationHelper.ShowUndoToast("Entry", Model);
Model.MarkForDelete();
if (Frame.CanGoBack) Frame.GoBack();
}));
messageDialog.Commands.Add(new UICommand("Cancel"));
// Set the command that will be invoked by default
messageDialog.DefaultCommandIndex = 1;
// Set the command to be invoked when escape is pressed
messageDialog.CancelCommandIndex = 1;
// Show the message dialog
await messageDialog.ShowAsync();
}
private void checkBox_Unchecked(object sender, RoutedEventArgs e)
private async void UrlButton_Click(object sender, RoutedEventArgs e)
{
passwordBox.Visibility = Visibility.Visible;
passwordTextBox.Visibility = Visibility.Collapsed;
try
{
var uri = new Uri(UrlTextBox.Text);
await Windows.System.Launcher.LaunchUriAsync(uri);
}
catch
{
// TODO: Show some error
}
}
private void PasswordGenerationButton_Click(object sender, RoutedEventArgs e)
{
Model.GeneratePassword();
/*var button = (Button)sender;
button?.Flyout?.Hide();*/
}
}
}

View File

@@ -1,19 +1,39 @@
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ModernKeePass.Pages"
xmlns:common="using:ModernKeePass.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ViewModels="using:ModernKeePass.ViewModels"
x:Name="pageRoot"
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:local="using:ModernKeePass.Controls"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
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:PluralizationConverter x:Key="PluralizationConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:TextToWidthConverter x:Key="TextToWidthConverter"/>
</Page.Resources>
<Page.DataContext>
<ViewModels:GroupVm />
<viewModels:GroupVm />
</Page.DataContext>
<Grid>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<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.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
@@ -23,83 +43,206 @@
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.Resources>
<CollectionViewSource
x:Name="groupsViewSource"
Source="{Binding Groups}"/>
<CollectionViewSource
x:Name="entriesViewSource"
Source="{Binding Entries}"/>
</Grid.Resources>
<!-- Horizontal scrolling grid -->
<SemanticZoom Grid.Row="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" >
<SemanticZoom.ZoomedInView>
<GridView
x:Name="groupsGridView"
x:Name="GridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Groups"
AutomationProperties.Name="Entries"
TabIndex="1"
Grid.RowSpan="2"
Padding="120,126,120,50"
ItemsSource="{Binding Source={StaticResource groupsViewSource}}"
IsSwipeEnabled="false"
SelectionChanged="groupsGridView_SelectionChanged"
SelectedIndex="-1" >
<GridView.ItemTemplate>
<DataTemplate>
<Grid Height="110" Width="480" Margin="10">
SelectionChanged="entries_SelectionChanged"
IsSynchronizedWithCurrentItem="False">
<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>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
<Image Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
<TextBlock Text="{Binding EntryCount}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60"/>
<TextBlock Text="{Binding GroupCount}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60"/>
</StackPanel>
<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>
<TextBlock Grid.Column="1" Text="{Binding Name}" FontWeight="Bold" Style="{ThemeResource TitleTextBlockStyle}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="13,0,0,5"/>
</Grid>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.Header>
<StackPanel Width="480" Margin="0,4,14,0">
<ListView x:Name="entriesListView" Height="100" ItemsSource="{Binding Source={StaticResource entriesViewSource}}" Margin="10,0,0,0" SelectionChanged="entriesListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<DataTemplate x:Name="GroupOtherItem">
<Grid Height="110" Width="480" x:Name="EntryGrid" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0"/>
<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}}"/>
<!--<Line Visibility="{Binding HasExpired, Converter={StaticResource BooleanToVisibilityConverter}}" Margin="0,-10,0,0" Stretch="Fill" Stroke="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}" StrokeThickness="1" X1="1" Width="{Binding Name, Converter={StaticResource TextToWidthConverter}, ConverterParameter=7}" HorizontalAlignment="Left" VerticalAlignment="Center" />-->
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
<!--<TextBlock Text="{Binding EntryCount, ConverterParameter=entry\,entries, Converter={StaticResource PluralizationConverter}}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
<TextBlock Text="{Binding GroupCount, ConverterParameter=group\,groups, Converter={StaticResource PluralizationConverter}}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />-->
<TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
<TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</GridView.Header>
</GridView.Resources>
<GridView.ItemsSource>
<Binding Source="{StaticResource EntriesViewSource}"/>
</GridView.ItemsSource>
<GridView.DataContext>
<viewModels:EntryVm/>
</GridView.DataContext>
<GridView.ItemTemplateSelector>
<local: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.Header>
<ListView
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="Collapsed">
<SymbolIcon Symbol="{Binding IconSymbol}">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding Name}" />
</ToolTipService.ToolTip>
</SymbolIcon>
</DataTemplate>
<DataTemplate x:Name="Expanded">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconSymbol}" />
<TextBlock Text="{Binding Name}" FontWeight="{Binding FontWeight}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" />
<!--<TextBlock Text="{Binding EntryCount}" HorizontalAlignment="Right" VerticalAlignment="Center" />-->
</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="20,0,0,0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.HeaderTemplate>
<DataTemplate>
<ToggleButton IsChecked="{Binding IsLeftPaneOpen, Mode=TwoWay}" Style="{StaticResource HamburgerToggleButton}" Margin="0" />
</DataTemplate>
</ListView.HeaderTemplate>
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsLeftPaneOpen}" Value="True">
<core:ChangePropertyAction PropertyName="ItemTemplate" Value="{StaticResource Expanded}"/>
</core:DataTriggerBehavior>
<core:DataTriggerBehavior Binding="{Binding IsLeftPaneOpen}" Value="False">
<core:ChangePropertyAction PropertyName="ItemTemplate" Value="{StaticResource Collapsed}"/>
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
</GridView.Header>
</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}" Foreground="Black" Margin="30" Style="{StaticResource HeaderTextBlockStyle}"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
<!-- Back button and page title -->
<Grid>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="300"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
<Button Margin="39,0,39,0"
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
VerticalAlignment="Center"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"/>
<TextBlock x:Name="pageTitle" Text="{Binding Path=Name}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
<TextBox
Grid.Column="1"
x:Name="TitleTextBox"
Text="{Binding Name, Mode=TwoWay}"
Style="{StaticResource HeaderTextBoxStyle}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
IsHitTestVisible="{Binding IsEditMode}"
TextWrapping="NoWrap"
VerticalAlignment="Center"
Margin="0,0,30,0"
PlaceholderText="New group name..."/>
<CommandBar Grid.Column="2" Background="Transparent" IsOpen="True" VerticalAlignment="Center" Margin="0,20,0,0">
<AppBarButton Icon="Find" Label="Search">
<AppBarButton.Flyout>
<Flyout>
<SearchBox PlaceholderText="Search..." Width="350" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" />
</Flyout>
</AppBarButton.Flyout>
</AppBarButton>
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}" />
<AppBarButton Icon="Delete" Label="Delete" IsEnabled="{Binding IsNotRoot}" Click="DeleteButton_Click" />
</CommandBar>
</Grid>
</Grid>
</Page>

View File

@@ -1,6 +1,13 @@
using ModernKeePass.Common;
using System;
using System.Linq;
using System.Threading.Tasks;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.ViewModels;
// The Group Detail Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234229
@@ -11,25 +18,20 @@ namespace ModernKeePass.Pages
/// A page that displays an overview of a single group, including a preview of the items
/// within the group.
/// </summary>
public sealed partial class GroupDetailPage : Page
public sealed partial class GroupDetailPage
{
private NavigationHelper navigationHelper;
/// <summary>
/// NavigationHelper is used on each page to aid in navigation and
/// process lifetime management
/// </summary>
public NavigationHelper NavigationHelper
{
get { return navigationHelper; }
}
public NavigationHelper NavigationHelper { get; }
public GroupVm Model => (GroupVm)DataContext;
public GroupDetailPage()
{
InitializeComponent();
navigationHelper = new NavigationHelper(this);
navigationHelper.LoadState += navigationHelper_LoadState;
NavigationHelper = new NavigationHelper(this);
NavigationHelper.LoadState += navigationHelper_LoadState;
}
/// <summary>
@@ -40,12 +42,10 @@ namespace ModernKeePass.Pages
/// The source of the event; typically <see cref="Common.NavigationHelper"/>
/// </param>
/// <param name="e">Event data that provides both the navigation parameter passed to
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested and
/// <see cref="Frame.Navigate(Type, object)"/> when this page was initially requested and
/// a dictionary of state preserved by this page during an earlier
/// session. The state will be null the first time a page is visited.</param>
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
}
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e) {}
#region NavigationHelper registration
@@ -60,34 +60,110 @@ namespace ModernKeePass.Pages
///
protected override void OnNavigatedTo(NavigationEventArgs e)
{
navigationHelper.OnNavigatedTo(e);
NavigationHelper.OnNavigatedTo(e);
if (e.Parameter is GroupVm)
{
DataContext = e.Parameter as GroupVm;
groupsGridView.SelectedIndex = -1;
entriesListView.SelectedIndex = -1;
}
if (!(e.Parameter is GroupVm)) return;
DataContext = (GroupVm) e.Parameter;
if (Model.IsEditMode)
Task.Factory.StartNew(
() => Dispatcher.RunAsync(CoreDispatcherPriority.Low,
() => TitleTextBox.Focus(FocusState.Programmatic)));
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
navigationHelper.OnNavigatedFrom(e);
NavigationHelper.OnNavigatedFrom(e);
}
#endregion
private void groupsGridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
#region Event Handlers
private void groups_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var gridView = sender as GridView;
Frame.Navigate(typeof(GroupDetailPage), gridView.SelectedItem as GroupVm);
GroupVm group;
switch (LeftListView.SelectedIndex)
{
case -1:
return;
case 0:
group = Model.CreateNewGroup();
break;
default:
group = LeftListView.SelectedItem as GroupVm;
break;
}
Frame.Navigate(typeof(GroupDetailPage), group);
}
private void entriesListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
private void entries_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listView = sender as ListView;
Frame.Navigate(typeof(EntryDetailPage), listView.SelectedItem as EntryVm);
EntryVm entry;
switch (GridView.SelectedIndex)
{
case -1:
return;
case 0:
entry = Model.CreateNewEntry();
break;
default:
entry = GridView.SelectedItem as EntryVm;
break;
}
Frame.Navigate(typeof(EntryDetailPage), entry);
}
private async void DeleteButton_Click(object sender, RoutedEventArgs e)
{
// Create the message dialog and set its content
var messageDialog = new MessageDialog("Are you sure you want to delete the whole group and all its entries?");
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
messageDialog.Commands.Add(new UICommand("Delete", delete =>
{
ToastNotificationHelper.ShowUndoToast("Group", Model);
Model.MarkForDelete();
if (Frame.CanGoBack) Frame.GoBack();
}));
messageDialog.Commands.Add(new UICommand("Cancel"));
// Set the command that will be invoked by default
messageDialog.DefaultCommandIndex = 1;
// Set the command to be invoked when escape is pressed
messageDialog.CancelCommandIndex = 1;
// Show the message dialog
await messageDialog.ShowAsync();
}
private void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)
{
// We need to synchronize the two lists (zoomed-in and zoomed-out) because the source is different
if (e.IsSourceZoomedInView == false)
{
e.DestinationItem.Item = e.SourceItem.Item;
}
}
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);
foreach (var result in results)
{
args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Name, result.ParentGroup.Name, result.Id, imageUri, string.Empty);
}
}
private void SearchBox_OnResultSuggestionChosen(SearchBox sender, SearchBoxResultSuggestionChosenEventArgs args)
{
var entry = Model.Entries.Skip(1).FirstOrDefault(e => e.Id == args.Tag);
Frame.Navigate(typeof(EntryDetailPage), entry);
}
#endregion
}
}

View File

@@ -0,0 +1,33 @@
<Page
x:Class="ModernKeePass.Pages.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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="using:ModernKeePass.Controls"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:viewModels="using:ModernKeePass.ViewModels"
mc:Ignorable="d">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter"/>
<converters:DoubleToForegroungBrushComplexityConverter x:Key="DoubleToForegroungBrushComplexityConverter"/>
</Page.Resources>
<Page.DataContext>
<viewModels:NewVm />
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Style="{StaticResource HeaderTextBlockStyle}" Margin="0,-20,0,20">New</TextBlock>
<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>
<Border HorizontalAlignment="Left" BorderThickness="1" BorderBrush="AliceBlue" Width="350" Visibility="{Binding ShowPasswordBox, Converter={StaticResource BooleanToVisibilityConverter}}">
<StackPanel>
<TextBlock Margin="25,10,0,10" Text="{Binding Name}" />
<local:OpenDatabaseUserControl Password="{Binding Password, Mode=TwoWay}" CreateNew="True" ValidationChecked="PasswordUserControl_PasswordChecked" />
<TextBlock Margin="25,0,0,10">Password complexity</TextBlock>
<ProgressBar Margin="25,0,0,10" Value="{Binding PasswordComplexityIndicator, ConverterParameter=0\,128, Converter={StaticResource ProgressBarLegalValuesConverter}}" Maximum="128" Width="300" HorizontalAlignment="Left" Foreground="{Binding PasswordComplexityIndicator, ConverterParameter=128, Converter={StaticResource DoubleToForegroungBrushComplexityConverter}}" />
</StackPanel>
</Border>
</StackPanel>
</Page>

View File

@@ -0,0 +1,53 @@
using System;
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
{
/// <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()
{
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
{
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
SuggestedFileName = "New Database"
};
savePicker.FileTypeChoices.Add("KeePass 2.x database", new List<string> { ".kdbx" });
var file = await savePicker.PickSaveFileAsync();
if (file == null) return;
Model.OpenFile(file);
}
private void PasswordUserControl_PasswordChecked(object sender, PasswordEventArgs e)
{
_mainFrame.Navigate(typeof(GroupDetailPage), e.RootGroup);
}
}
}

View File

@@ -0,0 +1,31 @@
<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"
x:Class="ModernKeePass.Pages.OpenDatabasePage"
mc:Ignorable="d">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Page.Resources>
<Page.DataContext>
<viewModels:OpenVm/>
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Style="{StaticResource HeaderTextBlockStyle}" Margin="0,-20,0,20">Open</TextBlock>
<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>
<Border HorizontalAlignment="Left" BorderThickness="1" BorderBrush="AliceBlue" Width="350" Visibility="{Binding ShowPasswordBox, Converter={StaticResource BooleanToVisibilityConverter}}">
<StackPanel>
<TextBlock Margin="25,10,0,10" Text="{Binding Name}" />
<local:OpenDatabaseUserControl ValidationChecked="PasswordUserControl_PasswordChecked" />
</StackPanel>
</Border>
</StackPanel>
</Page>

View File

@@ -0,0 +1,54 @@
using System;
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
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class OpenDatabasePage
{
private Frame _mainFrame;
public OpenVm Model => (OpenVm)DataContext;
public OpenDatabasePage()
{
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 picker =
new FileOpenPicker
{
ViewMode = PickerViewMode.List,
SuggestedStartLocation = PickerLocationId.DocumentsLibrary
};
picker.FileTypeFilter.Add(".kdbx");
// Application now has read/write access to the picked file
var file = await picker.PickSingleFileAsync();
if (file == null) return;
Model.OpenFile(file);
}
private void PasswordUserControl_PasswordChecked(object sender, PasswordEventArgs e)
{
_mainFrame.Navigate(typeof(GroupDetailPage), e.RootGroup);
}
}
}

View File

@@ -0,0 +1,40 @@
<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"
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>
<StackPanel Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Style="{StaticResource HeaderTextBlockStyle}" Margin="0,-20,0,20">Recent</TextBlock>
<ListView
ItemsSource="{Binding Source={StaticResource RecentItemsSource}}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10,0,10,0">
<TextBlock Text="{Binding Name}" Width="350" Padding="5,0,0,0" />
<TextBlock Text="{Binding Path}" Width="350" Padding="5,0,0,0" FontSize="10" />
<local:OpenDatabaseUserControl Margin="0,10,0,0" Visibility="{Binding IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}" ValidationChecking="OpenDatabaseUserControl_OnValidationChecking" ValidationChecked="PasswordUserControl_PasswordChecked" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
</StackPanel>
</Page>

View File

@@ -0,0 +1,43 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Events;
using ModernKeePass.ViewModels;
// 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.Pages
{
/// <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
{
private Frame _mainFrame;
public RecentVm Model => (RecentVm)DataContext;
public RecentDatabasesPage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
_mainFrame = e.Parameter as Frame;
}
private void PasswordUserControl_PasswordChecked(object sender, PasswordEventArgs e)
{
_mainFrame.Navigate(typeof(GroupDetailPage), e.RootGroup);
}
private void OpenDatabaseUserControl_OnValidationChecking(object sender, EventArgs e)
{
var app = (App)Application.Current;
app.Database.DatabaseFile = Model.SelectedItem.DatabaseFile;
}
}
}

View File

@@ -0,0 +1,20 @@
<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"
x:Class="ModernKeePass.Pages.SaveDatabasePage"
mc:Ignorable="d">
<Page.DataContext>
<viewModels:SaveVm/>
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Style="{StaticResource HeaderTextBlockStyle}" Margin="0,-20,0,20">Save</TextBlock>
<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>
</StackPanel>
</Page>

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.ViewModels;
// 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 SaveDatabasePage
{
private Frame _mainFrame;
public SaveVm Model => (SaveVm)DataContext;
public SaveDatabasePage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
_mainFrame = e.Parameter as Frame;
}
private void SaveButton_OnClick(object sender, RoutedEventArgs e)
{
Model.Save();
_mainFrame.Navigate(typeof(MainPage));
}
private async void SaveAsButton_OnClick(object sender, RoutedEventArgs e)
{
var savePicker = new FileSavePicker
{
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
SuggestedFileName = "New Database"
};
savePicker.FileTypeChoices.Add("KeePass 2.x database", new List<string> { ".kdbx" });
var file = await savePicker.PickSaveFileAsync();
if (file == null) return;
Model.Save(file);
_mainFrame.Navigate(typeof(MainPage));
}
}
}

View File

@@ -0,0 +1,20 @@
<Page
x:Class="ModernKeePass.Pages.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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Style="{StaticResource HeaderTextBlockStyle}" Margin="0,-20,0,0">Welcome</TextBlock>
<StackPanel Orientation="Horizontal" Margin="0,20,0,10">
<SymbolIcon Symbol="Back" Margin="-30,7,40,0" />
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}">Have an existing password database? Open it here.</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Back" Margin="-30,7,40,0" />
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}">Want to create a new password database? Do it here.</TextBlock>
</StackPanel>
</StackPanel>
</Page>

View File

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

@@ -6,9 +6,9 @@ using System.Runtime.InteropServices;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ModernKeePass")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyDescription("A port of KeePass 2.x to Modern UI as a Windows Store application")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyCompany("wismna")]
[assembly: AssemblyProduct("ModernKeePass")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
@@ -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.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.4.0.0")]
[assembly: AssemblyFileVersion("1.4.0.0")]
[assembly: ComVisible(false)]

View File

@@ -0,0 +1,48 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="ToggleButton" x:Name="HamburgerToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<ContentControl>
<Grid Background="Transparent" Margin="0" Width="77" Height="77">
<Canvas x:Name="hampurger_menu" HorizontalAlignment="Center" Height="17" UseLayoutRounding="False" VerticalAlignment="Center" Width="28">
<Canvas x:Name="Layer_1" Height="17" Canvas.Left="0" Width="28" Margin="0" RenderTransformOrigin="0.5,0.5">
<Canvas.RenderTransform>
<CompositeTransform/>
</Canvas.RenderTransform>
<Canvas.Projection>
<PlaneProjection/>
</Canvas.Projection>
<Path x:Name="path" Data="M0,12.997 L30,12.997" Height="3" Stretch="Fill" StrokeThickness="3" Width="28" Stroke="{ThemeResource DefaultTextForegroundThemeBrush}" StrokeStartLineCap="Square" StrokeEndLineCap="Square" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<CompositeTransform/>
</Path.RenderTransform>
</Path>
<Path Data="M0,12.997 L30,12.997" Height="3" Stretch="Fill" StrokeThickness="3" Width="28" Stroke="{ThemeResource DefaultTextForegroundThemeBrush}" StrokeStartLineCap="Square" StrokeEndLineCap="Square" Canvas.Top="7"/>
<Path x:Name="path1" Data="M0,12.997 L30,12.997" Height="3" Stretch="Fill" StrokeThickness="3" Width="28" Stroke="{ThemeResource DefaultTextForegroundThemeBrush}" StrokeStartLineCap="Square" StrokeEndLineCap="Square" Canvas.Top="14" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<CompositeTransform/>
</Path.RenderTransform>
</Path>
</Canvas>
</Canvas>
</Grid>
</ContentControl>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Margin" Value="20" />
</Style>
<Style x:Key="HeaderTextBoxStyle" TargetType="TextBox">
<Setter Property="FontSize" Value="56"/>
<Setter Property="FontWeight" Value="Light"/>
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Background" Value="Transparent" />
<!--<Setter Property="LineHeight" Value="40"/>-->
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,229 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBox" x:Name="TextBoxWithButtonStyle">
<Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}" />
<Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}" />
<Setter Property="Foreground" Value="{ThemeResource TextBoxForegroundThemeBrush}" />
<Setter Property="Background" Value="{ThemeResource TextBoxBackgroundThemeBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource TextBoxBorderThemeBrush}" />
<Setter Property="SelectionHighlightColor" Value="{ThemeResource TextSelectionHighlightColorThemeBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
<Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Grid>
<Grid.Resources>
<Style x:Name="GotoButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="BorderElement"
BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}"
BorderThickness="{TemplateBinding BorderThickness}"/>
<Border x:Name="BackgroundElement"
Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}"
Margin="{TemplateBinding BorderThickness}">
<TextBlock x:Name="GlyphElement"
Foreground="{ThemeResource TextBoxButtonForegroundThemeBrush}"
Padding="4,0,4,0"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontStyle="Normal"
Text="&#xE111;"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
AutomationProperties.AccessibilityView="Raw"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlBackgroundThemeOpacity}" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlBorderThemeOpacity}" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlPointerOverBackgroundThemeOpacity}" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlPointerOverBorderThemeOpacity}" />
</Storyboard>
</VisualState>
<VisualState x:Name="Focused" />
</VisualStateGroup>
<VisualStateGroup x:Name="ButtonStates">
<VisualState x:Name="ButtonVisible">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GotoButton"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="ButtonCollapsed" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border x:Name="BackgroundElement"
Grid.Row="1"
Background="{TemplateBinding Background}"
Margin="{TemplateBinding BorderThickness}"
Grid.ColumnSpan="2"
Grid.RowSpan="1"/>
<Border x:Name="BorderElement"
Grid.Row="1"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Grid.ColumnSpan="2"
Grid.RowSpan="1"/>
<ContentPresenter x:Name="HeaderContentPresenter"
Grid.Row="0"
Foreground="{ThemeResource TextBoxForegroundHeaderThemeBrush}"
Margin="0,4,0,4"
Grid.ColumnSpan="2"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontWeight="Semilight" />
<ScrollViewer x:Name="ContentElement"
Grid.Row="1"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsTabStop="False"
AutomationProperties.AccessibilityView="Raw"
ZoomMode="Disabled" />
<ContentControl x:Name="PlaceholderTextContentPresenter"
Grid.Row="1"
Foreground="{ThemeResource TextBoxPlaceholderTextThemeBrush}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsTabStop="False"
Grid.ColumnSpan="2"
Content="{TemplateBinding PlaceholderText}"
IsHitTestVisible="False"/>
<Button x:Name="GotoButton"
Grid.Row="1"
Style="{StaticResource GotoButtonStyle}"
BorderThickness="{TemplateBinding BorderThickness}"
IsTabStop="False"
Grid.Column="1"
Visibility="Collapsed"
FontSize="{TemplateBinding FontSize}"
VerticalAlignment="Stretch"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -1,69 +0,0 @@
using System.ComponentModel;
using Windows.Storage;
using ModernKeePassLib;
using ModernKeePassLib.Keys;
using ModernKeePassLib.Serialization;
using ModernKeePassLib.Interfaces;
using Windows.UI.Xaml;
using System;
namespace ModernKeePass.ViewModels
{
public class DatabaseVm : INotifyPropertyChanged
{
private PwDatabase database = new PwDatabase();
private StorageFile databaseFile;
public string Password { get; set; }
public bool IsOpen { get; set; }
public Visibility Visibility { get; private set; }
public string ErrorMessage { get; set; }
public string Name { get; set; }
public GroupVm RootGroup { get; set; }
public DatabaseVm()
{
Visibility = Visibility.Collapsed;
}
public DatabaseVm(StorageFile databaseFile)
{
this.databaseFile = databaseFile;
Visibility = Visibility.Visible;
}
public async void Open()
{
var key = new CompositeKey();
try
{
key.AddUserKey(new KcpPassword(Password));
await database.Open(IOConnectionInfo.FromFile(databaseFile), key, new NullStatusLogger());
IsOpen = database.IsOpen;
Name = databaseFile.DisplayName;
RootGroup = new GroupVm (database.RootGroup);
}
catch (ArgumentNullException)
{
ErrorMessage = "Password cannot be empty";
NotifyPropertyChanged("ErrorMessage");
}
catch (InvalidCompositeKeyException)
{
ErrorMessage = "Wrong password";
NotifyPropertyChanged("ErrorMessage");
}
finally
{
// TODO: move this when implementing write mode
database.Close();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -1,26 +1,199 @@
using System.ComponentModel;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Interfaces;
using ModernKeePass.Mappings;
using ModernKeePassLib;
using ModernKeePassLib.Cryptography.PasswordGenerator;
using ModernKeePassLib.Security;
using System;
using Windows.UI.Xaml;
namespace ModernKeePass.ViewModels
{
public class EntryVm : INotifyPropertyChanged
public class EntryVm : INotifyPropertyChanged, IPwEntity
{
public string Title { get; private set; }
public string UserName { get; private set; }
public string Password { get; private set; }
public string URL { get; private set; }
public string Notes { get; private set; }
public GroupVm ParentGroup { get; }
public PwEntry Entry { get; }
public EntryVm() { }
public EntryVm(PwEntry entry)
public System.Drawing.Color? BackgroundColor => Entry?.BackgroundColor;
public System.Drawing.Color? ForegroundColor => Entry?.ForegroundColor;
public bool IsRevealPasswordEnabled => !string.IsNullOrEmpty(Password);
public double PasswordLength { get; set; } = 25;
public bool UpperCasePatternSelected { get; set; } = true;
public bool LowerCasePatternSelected { get; set; } = true;
public bool DigitsPatternSelected { get; set; } = true;
public bool MinusPatternSelected { get; set; }
public bool UnderscorePatternSelected { get; set; }
public bool SpacePatternSelected { get; set; }
public bool SpecialPatternSelected { get; set; }
public bool BracketsPatternSelected { get; set; }
public string CustomChars { get; set; } = string.Empty;
public string Name
{
Title = entry.Strings.GetSafe(PwDefs.TitleField).ReadString();
UserName = entry.Strings.GetSafe(PwDefs.UserNameField).ReadString();
Password = entry.Strings.GetSafe(PwDefs.PasswordField).ReadString();
URL = entry.Strings.GetSafe(PwDefs.UrlField).ReadString();
Notes = entry.Strings.GetSafe(PwDefs.NotesField).ReadString();
get
{
var title = GetEntryValue(PwDefs.TitleField);
return title == null ? "New entry" : title;
}
set { SetEntryValue(PwDefs.TitleField, value); }
}
public string Id => Entry.Uuid.ToHexString();
public string UserName
{
get { return GetEntryValue(PwDefs.UserNameField); }
set { SetEntryValue(PwDefs.UserNameField, value); }
}
public string Password
{
get { return GetEntryValue(PwDefs.PasswordField); }
set
{
SetEntryValue(PwDefs.PasswordField, value);
NotifyPropertyChanged("Password");
}
}
public string Url
{
get { return GetEntryValue(PwDefs.UrlField); }
set { SetEntryValue(PwDefs.UrlField, value); }
}
public string Notes
{
get { return GetEntryValue(PwDefs.NotesField); }
set { SetEntryValue(PwDefs.NotesField, value); }
}
public Symbol IconSymbol
{
get
{
if (Entry == null) return Symbol.Add;
if (HasExpired) return Symbol.Priority;
var result = PwIconToSegoeMapping.GetSymbolFromIcon(Entry.IconId);
return result == Symbol.More ? Symbol.Permissions : result;
}
}
public DateTimeOffset ExpiryDate
{
get { return new DateTimeOffset(Entry.ExpiryTime.Date); }
set { if (HasExpirationDate) Entry.ExpiryTime = value.DateTime; }
}
public TimeSpan ExpiryTime
{
get { return Entry.ExpiryTime.TimeOfDay; }
set { if (HasExpirationDate) Entry.ExpiryTime = Entry.ExpiryTime.Date.Add(value); }
}
public bool IsEditMode
{
get { return _isEditMode; }
set
{
_isEditMode = value;
NotifyPropertyChanged("IsEditMode");
}
}
public bool IsRevealPassword
{
get { return _isRevealPassword; }
set
{
_isRevealPassword = value;
NotifyPropertyChanged("IsRevealPassword");
}
}
public bool HasExpirationDate
{
get { return Entry.Expires; }
set
{
Entry.Expires = value;
NotifyPropertyChanged("HasExpirationDate");
}
}
public bool HasExpired
{
get { return HasExpirationDate && Entry.ExpiryTime < DateTime.Now; }
}
public event PropertyChangedEventHandler PropertyChanged;
private bool _isEditMode;
private bool _isRevealPassword;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public EntryVm() { }
public EntryVm(PwEntry entry, GroupVm parent)
{
Entry = entry;
ParentGroup = parent;
}
public void GeneratePassword()
{
var pwProfile = new PwProfile()
{
GeneratorType = PasswordGeneratorType.CharSet,
Length = (uint)PasswordLength,
CharSet = new PwCharSet()
};
if (UpperCasePatternSelected) pwProfile.CharSet.Add(PwCharSet.UpperCase);
if (LowerCasePatternSelected) pwProfile.CharSet.Add(PwCharSet.LowerCase);
if (DigitsPatternSelected) pwProfile.CharSet.Add(PwCharSet.Digits);
if (SpecialPatternSelected) pwProfile.CharSet.Add(PwCharSet.SpecialChars);
if (MinusPatternSelected) pwProfile.CharSet.Add('-');
if (UnderscorePatternSelected) pwProfile.CharSet.Add('_');
if (SpacePatternSelected) pwProfile.CharSet.Add(' ');
if (BracketsPatternSelected) pwProfile.CharSet.Add(PwCharSet.Brackets);
pwProfile.CharSet.Add(CustomChars);
ProtectedString password;
PwGenerator.Generate(out password, pwProfile, null, new CustomPwGeneratorPool());
Entry?.Strings.Set(PwDefs.PasswordField, password);
NotifyPropertyChanged("Password");
NotifyPropertyChanged("IsRevealPasswordEnabled");
}
private string GetEntryValue(string key)
{
return Entry?.Strings.GetSafe(key).ReadString();
}
private void SetEntryValue(string key, string newValue)
{
Entry?.Strings.Set(key, new ProtectedString(true, newValue));
}
public void MarkForDelete()
{
var app = (App)Application.Current;
app.PendingDeleteEntities.Add(Id, this);
ParentGroup.Entries.Remove(this);
}
public void CommitDelete()
{
Entry.ParentGroup.Entries.Remove(Entry);
}
public void UndoDelete()
{
ParentGroup.Entries.Add(this);
}
}
}

View File

@@ -1,50 +1,117 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Windows.UI.Text;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.Mappings;
using ModernKeePassLib;
using System;
namespace ModernKeePass.ViewModels
{
public class GroupVm : INotifyPropertyChanged
public class GroupVm : NotifyPropertyChangedBase, IPwEntity
{
public ObservableCollection<EntryVm> Entries { get; set; }
public ObservableCollection<GroupVm> Groups { get; set; }
public string Name { get; set; }
public GroupVm ParentGroup { get; }
public ObservableCollection<EntryVm> Entries { get; set; } = new ObservableCollection<EntryVm>();
public string EntryCount {
get
{
return $"{Entries?.Count} entries.";
}
}
public string GroupCount
public ObservableCollection<GroupVm> Groups { get; set; } = new ObservableCollection<GroupVm>();
public int EntryCount => Entries.Count() - 1;
public int GroupCount => Groups.Count - 1;
public bool IsNotRoot => ParentGroup != null;
public FontWeight FontWeight => _pwGroup == null ? FontWeights.Bold : FontWeights.Normal;
public string Id => _pwGroup.Uuid.ToHexString();
public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut
{
get
{
return $"{Groups?.Count} groups.";
return from e in Entries
where e.Entry != null
group e by e.Name.FirstOrDefault() into grp
orderby grp.Key
select grp;
}
}
public GroupVm()
public string Name
{
Name = "GroupName";
Entries = new ObservableCollection<EntryVm>();
Groups = new ObservableCollection<GroupVm>();
get { return _pwGroup == null ? "New group" : _pwGroup.Name; }
set { _pwGroup.Name = value; }
}
public GroupVm(PwGroup group)
public Symbol IconSymbol
{
Name = group.Name;
Entries = new ObservableCollection<EntryVm>(group.Entries.Select(e => new EntryVm(e)));
Groups = new ObservableCollection<GroupVm>(group.Groups.Select(g => new GroupVm(g)));
get
{
if (_pwGroup == null) return Symbol.Add;
var result = PwIconToSegoeMapping.GetSymbolFromIcon(_pwGroup.IconId);
return result == Symbol.More ? Symbol.Folder : result;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
public bool IsLeftPaneOpen
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
get { return _isLeftPaneOpen; }
set { SetProperty(ref _isLeftPaneOpen, value); }
}
public bool IsEditMode
{
get { return _isEditMode; }
set { SetProperty(ref _isEditMode, value); }
}
private readonly PwGroup _pwGroup;
private bool _isLeftPaneOpen;
private bool _isEditMode;
public GroupVm() {}
public GroupVm(PwGroup pwGroup, GroupVm parent)
{
_pwGroup = pwGroup;
ParentGroup = parent;
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)).OrderBy(g => g.Name));
Groups.Insert(0, new GroupVm ());
}
public GroupVm CreateNewGroup()
{
var pwGroup = new PwGroup(true, true, string.Empty, PwIcon.Folder);
_pwGroup.AddGroup(pwGroup, true);
var newGroup = new GroupVm(pwGroup, this) {IsEditMode = true};
Groups.Add(newGroup);
return newGroup;
}
public EntryVm CreateNewEntry()
{
var pwEntry = new PwEntry(true, true);
_pwGroup.AddEntry(pwEntry, true);
var newEntry = new EntryVm(pwEntry, this) {IsEditMode = true};
Entries.Add(newEntry);
return newEntry;
}
public void MarkForDelete()
{
var app = (App)Application.Current;
app.PendingDeleteEntities.Add(Id, this);
ParentGroup.Groups.Remove(this);
}
public void CommitDelete()
{
_pwGroup.ParentGroup.Groups.Remove(_pwGroup);
}
public void UndoDelete()
{
ParentGroup.Groups.Add(this);
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
namespace ModernKeePass.ViewModels
{
public class MainMenuItemVm: NotifyPropertyChangedBase, IIsEnabled
{
private bool _isSelected;
public string Title { get; set; }
public Type PageType { get; set; }
public object Parameter { get; set; }
public Frame Destination { get; set; }
public int Group { get; set; } = 0;
public Symbol SymbolIcon { get; set; }
public bool IsEnabled { get; set; } = true;
public bool IsSelected
{
get { return _isSelected; }
set { SetProperty(ref _isSelected, value); }
}
public override string ToString()
{
return Title;
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using Windows.Storage;
using ModernKeePass.Common;
using Windows.Storage.AccessCache;
namespace ModernKeePass.ViewModels
{
public class RecentItemVm: NotifyPropertyChangedBase
{
private bool _isSelected;
public RecentItemVm(AccessListEntry entry, StorageFile file)
{
Token = entry.Token;
Name = entry.Metadata;
DatabaseFile = file;
}
public StorageFile DatabaseFile { get; private set; }
public string Token { get; private set; }
public string Name { get; private set; } = "Recent file";
public string Path => DatabaseFile.Path;
public bool IsSelected
{
get { return _isSelected; }
internal set { SetProperty(ref _isSelected, value); }
}
}
}

View File

@@ -0,0 +1,91 @@
using System.Collections.ObjectModel;
using System.Linq;
using Windows.Storage.AccessCache;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
using ModernKeePass.Pages;
namespace ModernKeePass.ViewModels
{
public class MainVm : NotifyPropertyChangedBase
{
private IOrderedEnumerable<IGrouping<int, MainMenuItemVm>> _mainMenuItems;
private MainMenuItemVm _selectedItem;
public IOrderedEnumerable<IGrouping<int, MainMenuItemVm>> MainMenuItems
{
get { return _mainMenuItems; }
set { SetProperty(ref _mainMenuItems, value); }
}
public MainMenuItemVm SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem == value) return;
if (_selectedItem != null)
{
_selectedItem.IsSelected = false;
}
SetProperty(ref _selectedItem, value);
if (_selectedItem != null)
{
_selectedItem.IsSelected = true;
}
}
}
public MainVm() {}
public MainVm(Frame referenceFrame, Frame destinationFrame)
{
var app = (App)Application.Current;
var mru = StorageApplicationPermissions.MostRecentlyUsedList;
var isDatabaseOpen = app.Database != null && app.Database.Status == DatabaseHelper.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
},
new MainMenuItemVm
{
Title = "New" , 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
},
new MainMenuItemVm
{
Title = "About" , PageType = typeof(AboutPage), Destination = destinationFrame, SymbolIcon = Symbol.Help
}
};
// 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)
mainMenuItems.Add(new MainMenuItemVm
{
Title = app.Database.Name,
PageType = typeof(GroupDetailPage),
Destination = referenceFrame,
Parameter = app.Database.RootGroup,
Group = 1,
SymbolIcon = Symbol.ProtectedDocument
});
MainMenuItems = from item in mainMenuItems group item by item.Group into grp orderby grp.Key select grp;
}
}
}

View File

@@ -0,0 +1,21 @@
using ModernKeePassLib.Cryptography;
namespace ModernKeePass.ViewModels
{
public class NewVm : OpenVm
{
private string _password = string.Empty;
public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password.ToCharArray());
public string Password
{
get { return _password; }
set
{
_password = value;
NotifyPropertyChanged("PasswordComplexityIndicator");
}
}
}
}

View File

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

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.ObjectModel;
using Windows.Storage.AccessCache;
using ModernKeePass.Common;
namespace ModernKeePass.ViewModels
{
public class RecentVm : NotifyPropertyChangedBase
{
private RecentItemVm _selectedItem;
private ObservableCollection<RecentItemVm> _recentItems = new ObservableCollection<RecentItemVm>();
public ObservableCollection<RecentItemVm> RecentItems
{
get { return _recentItems; }
set { SetProperty(ref _recentItems, value); }
}
public RecentItemVm SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem == value) return;
if (_selectedItem != null)
{
_selectedItem.IsSelected = false;
}
SetProperty(ref _selectedItem, value);
if (_selectedItem == null) return;
_selectedItem.IsSelected = true;
}
}
public RecentVm()
{
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);
}
}
if (RecentItems.Count > 0)
SelectedItem = RecentItems[0];
}
}
}

View File

@@ -0,0 +1,22 @@
using Windows.Storage;
using Windows.UI.Xaml;
namespace ModernKeePass.ViewModels
{
public class SaveVm
{
public void Save(bool close = true)
{
var app = (App)Application.Current;
app.Database.Save();
if (!close) return;
app.Database.Close();
}
internal void Save(StorageFile file)
{
var app = (App)Application.Current;
app.Database.Save(file);
}
}
}

View File

@@ -2,10 +2,27 @@
<packages>
<package id="Microsoft.NETCore.Platforms" version="2.0.0" targetFramework="win81" />
<package id="Microsoft.NETCore.Portable.Compatibility" version="1.0.2" targetFramework="win81" />
<package id="ModernKeePassLib" version="2.19.2950" targetFramework="win81" />
<package id="Microsoft.Toolkit.Uwp.Notifications" version="2.0.0" targetFramework="win81" />
<package id="ModernKeePassLib" version="2.28.4000" targetFramework="win81" />
<package id="NETStandard.Library" version="2.0.0" targetFramework="win81" />
<package id="Portable.BouncyCastle" version="1.8.1.3" targetFramework="win81" />
<package id="Splat" version="2.0.0" targetFramework="win81" />
<package id="System.Collections" version="4.0.11" targetFramework="win81" />
<package id="System.Diagnostics.Debug" version="4.0.11" targetFramework="win81" />
<package id="System.Globalization" version="4.0.11" targetFramework="win81" />
<package id="System.IO" version="4.1.0" targetFramework="win81" />
<package id="System.Linq" version="4.1.0" targetFramework="win81" />
<package id="System.Net.Requests" version="4.3.0" targetFramework="win81" />
<package id="System.Reflection" version="4.1.0" targetFramework="win81" />
<package id="System.Reflection.Extensions" version="4.0.1" targetFramework="win81" />
<package id="System.Runtime" version="4.1.0" targetFramework="win81" />
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="win81" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="win81" />
<package id="System.Runtime.WindowsRuntime" version="4.3.0" targetFramework="win81" />
<package id="System.Text.Encoding" version="4.0.11" targetFramework="win81" />
<package id="System.Threading" version="4.0.11" targetFramework="win81" />
<package id="System.Threading.Tasks" version="4.0.11" targetFramework="win81" />
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="win81" />
<package id="System.Xml.XmlSerializer" version="4.3.0" targetFramework="win81" />
<package id="Validation" version="2.4.15" targetFramework="win81" />
</packages>

View File

@@ -0,0 +1,65 @@
using System;
using System.IO;
#if KeePassLib
using KeePassLib.Cryptography.Cipher;
#else
using ModernKeePassLib.Cryptography.Cipher;
#endif
using NUnit.Framework;
namespace ModernKeePassLib.Test.Shared.Cryptography.Cipher
{
[TestFixture ()]
public class StandardAesEngineTests
{
[Test ()]
public void TestEncryptStream ()
{
// Test vector (official ECB test vector #356)
var pbIV = new byte[16];
var pbTestKey = new byte[32];
var pbTestData = new byte[16];
var pbReferenceCT = new byte[16] {
0x75, 0xD1, 0x1B, 0x0E, 0x3A, 0x68, 0xC4, 0x22,
0x3D, 0x88, 0xDB, 0xF0, 0x17, 0x97, 0x7D, 0xD7
};
pbTestData[0] = 0x04;
var outStream = new MemoryStream (new byte[16]);
var aes = new StandardAesEngine ();
var inStream = aes.EncryptStream (outStream, pbTestKey, pbIV);
new BinaryWriter (inStream).Write (pbTestData);
Assert.That (outStream.Position, Is.EqualTo (16));
outStream.Position = 0;
var outBytes = new BinaryReader (outStream).ReadBytes (16);
Assert.That(outBytes, Is.EqualTo (pbReferenceCT));
}
[Test ()]
public void TestDecryptStream ()
{
// Test vector (official ECB test vector #356)
var pbIV = new byte[16];
var pbTestKey = new byte[32];
var pbTestData = new byte[16];
var pbReferenceCT = new byte[16] {
0x75, 0xD1, 0x1B, 0x0E, 0x3A, 0x68, 0xC4, 0x22,
0x3D, 0x88, 0xDB, 0xF0, 0x17, 0x97, 0x7D, 0xD7
};
pbTestData[0] = 0x04;
// Possible Mono Bug? This only works with size >= 48
var inStream = new MemoryStream (new byte[48]);
inStream.Write (pbReferenceCT, 0, pbReferenceCT.Length);
inStream.Position = 0;
var aes = new StandardAesEngine ();
var outStream = aes.DecryptStream (inStream, pbTestKey, pbIV);
var outBytes = new BinaryReader (outStream).ReadBytes (16);
Assert.That(outBytes, Is.EqualTo (pbTestData));
}
}
}

View File

@@ -0,0 +1,60 @@
using NUnit.Framework;
using System;
#if KeePassLib
using KeePassLib.Cryptography;
#else
using ModernKeePassLib.Cryptography;
#endif
namespace ModernKeePassLib.Test.Shared.Cryptography
{
[TestFixture ()]
public class CryptoRandomStreamTests
{
void TestGetRandomBytes(CryptoRandomStream stream)
{
const uint length = 16;
var bytes1 = stream.GetRandomBytes (length);
Assert.That (bytes1.Length, Is.EqualTo (length));
var bytes2 = stream.GetRandomBytes (length);
Assert.That (bytes2, Is.Not.EqualTo (bytes1));
}
[Test ()]
public void TestGetRandomBytesCrsAlgorithmSalsa20 ()
{
var stream = new CryptoRandomStream (CrsAlgorithm.Salsa20, new byte[16]);
TestGetRandomBytes (stream);
}
[Test ()]
public void TestGetRandomBytesCrsAlgorithmArcFourVariant ()
{
var stream = new CryptoRandomStream (CrsAlgorithm.ArcFourVariant, new byte[16]);
TestGetRandomBytes (stream);
}
void TestGetRandomInt64 (CryptoRandomStream stream)
{
var value1 = stream.GetRandomUInt64 ();
var value2 = stream.GetRandomUInt64 ();
Assert.That (value2, Is.Not.EqualTo (value1));
}
[Test ()]
public void TestGetRandomInt64AlgorithmSalsa20 ()
{
var stream = new CryptoRandomStream (CrsAlgorithm.Salsa20, new byte[16]);
TestGetRandomInt64 (stream);
}
[Test ()]
public void TestGetRandomInt64AlgorithmArcFourVariant ()
{
var stream = new CryptoRandomStream (CrsAlgorithm.ArcFourVariant, new byte[16]);
TestGetRandomInt64 (stream);
}
}
}

View File

@@ -0,0 +1,44 @@
using NUnit.Framework;
using System;
#if KeePassLib
using KeePassLib.Cryptography;
#else
using ModernKeePassLib.Cryptography;
#endif
namespace ModernKeePassLib.Test.Shared.Cryptography
{
[TestFixture ()]
public class CryptoRandomTests
{
[Test ()]
public void TestAddEntropy ()
{
// just making sure it does not throw an exception
CryptoRandom.Instance.AddEntropy (new byte[1]);
}
[Test ()]
public void TestGetRandomBytes ()
{
const int length = 32;
var bytes1 = CryptoRandom.Instance.GetRandomBytes (length);
Assert.That (bytes1.Length, Is.EqualTo (length));
var bytes2 = CryptoRandom.Instance.GetRandomBytes (length);
Assert.That (bytes2, Is.Not.EqualTo (bytes1));
}
[Test ()]
public void TestGeneratedBytesCount ()
{
const int length = 1;
CryptoRandom.Instance.GetRandomBytes (length);
var count1 = CryptoRandom.Instance.GeneratedBytesCount;
CryptoRandom.Instance.GetRandomBytes (length);
var count2 = CryptoRandom.Instance.GeneratedBytesCount;
Assert.That (count2, Is.GreaterThan (count1));
}
}
}

View File

@@ -0,0 +1,77 @@
using NUnit.Framework;
using System;
using System.IO;
using System.Text;
#if KeePassLib
using KeePassLib.Cryptography;
#else
using ModernKeePassLib.Cryptography;
#endif
namespace ModernKeePassLib.Test.Shared.Cryptography
{
[TestFixture ()]
public class HashingStreamExTests
{
const string data = "test";
// The expected hash includes the \n added by WriteLine
static readonly byte[] sha256HashOfData = {
0xf2, 0xca, 0x1b, 0xb6, 0xc7, 0xe9, 0x07, 0xd0,
0x6d, 0xaf, 0xe4, 0x68, 0x7e, 0x57, 0x9f, 0xce,
0x76, 0xb3, 0x7e, 0x4e, 0x93, 0xb7, 0x60, 0x50,
0x22, 0xda, 0x52, 0xe6, 0xcc, 0xc2, 0x6f, 0xd2
};
[Test ()]
public void TestRead ()
{
// if we use larger size, StreamReader will read past newline and cause bad hash
var bytes = new byte[data.Length + 1];
using (var ms = new MemoryStream (bytes)) {
using (var sw = new StreamWriter (ms)) {
// set NewLine to ensure we don't run into cross-platform issues on Windows
sw.NewLine = "\n";
sw.WriteLine (data);
}
}
using (var ms = new MemoryStream (bytes)) {
using (var hs = new HashingStreamEx (ms, false, null)) {
using (var sr = new StreamReader (hs)) {
var read = sr.ReadLine ();
Assert.That (read, Is.EqualTo (data));
}
// When the StreamReader is disposed, it calls Dispose on the
//HasingStreamEx, which computes the hash.
Assert.That (hs.Hash, Is.EqualTo (sha256HashOfData));
}
}
}
[Test ()]
public void TestWrite ()
{
var bytes = new byte[16];
using (var ms = new MemoryStream (bytes)) {
using (var hs = new HashingStreamEx (ms, true, null)) {
using (var sw = new StreamWriter (hs)) {
// set NewLine to ensure we don't run into cross-platform issues on Windows
sw.NewLine = "\n";
sw.WriteLine (data);
}
// When the StreamWriter is disposed, it calls Dispose on the
//HasingStreamEx, which computes the hash.
Assert.That (hs.Hash, Is.EqualTo (sha256HashOfData));
}
}
using (var ms = new MemoryStream (bytes)) {
using (var sr = new StreamReader (ms)) {
var read = sr.ReadLine ();
Assert.That (read, Is.EqualTo (data));
}
}
}
}
}

View File

@@ -0,0 +1,36 @@
using NUnit.Framework;
using System;
using System.Text;
#if KeePassLib
using KeePassLib.Cryptography;
#else
using ModernKeePassLib.Cryptography;
#endif
namespace ModernKeePassLib.Test.Shared.Cryptography
{
[TestFixture ()]
public class HmacOtpTests
{
// Using the test case from Appendix D of RFC 4226
const string secret = "12345678901234567890";
static readonly string[] expectedHOTP = new string[] {
"755224", "287082", "359152", "969429", "338314",
"254676", "287922", "162583", "399871", "520489"
};
[Test ()]
public void TestGenerate ()
{
var secretBytes = Encoding.UTF8.GetBytes (secret);
for (ulong i = 0; i < 10; i++) {
var hotp = HmacOtp.Generate (secretBytes, i, 6, false, -1);
Assert.That (hotp, Is.EqualTo (expectedHOTP[i]));
}
}
}
}

View File

@@ -0,0 +1,34 @@
using NUnit.Framework;
using System;
#if KeePassLib
using KeePassLib.Keys;
#else
using ModernKeePassLib.Keys;
#endif
namespace ModernKeePassLib.Test.Shared.Keys
{
[TestFixture ()]
public class CompositeKeyTests
{
[Test ()]
public void TestGenerateKey32 ()
{
var originalKey = new byte[32];
var expectedKey = new byte[32] {
0xF0, 0xED, 0x57, 0xD5, 0xF0, 0xDA, 0xF3, 0x47,
0x90, 0xD0, 0xDB, 0x43, 0x25, 0xC6, 0x81, 0x2C,
0x81, 0x6A, 0x0D, 0x94, 0x96, 0xA9, 0x03, 0xE1,
0x20, 0xD4, 0x3A, 0x3E, 0x45, 0xAD, 0x02, 0x65
};
const ulong rounds = 1;
var composite = new CompositeKey ();
var key = composite.GenerateKey32 (originalKey, rounds);
Assert.That (key, Is.Not.Null);
var keyData = key.ReadData ();
Assert.That (keyData, Is.EqualTo (expectedKey));
}
}
}

View File

@@ -0,0 +1,39 @@
using NUnit.Framework;
using System;
#if KeePassLib
using KeePassLib.Keys;
#else
using ModernKeePassLib.Keys;
#endif
namespace ModernKeePassLib.Test.Shared.Keys
{
[TestFixture ()]
public class KcpCustomKeyTests
{
static readonly byte[] testData = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
[Test ()]
public void TestConstruct ()
{
var expectedHash = new byte[32] {
0xAF, 0x55, 0x70, 0xF5, 0xA1, 0x81, 0x0B, 0x7A,
0xF7, 0x8C, 0xAF, 0x4B, 0xC7, 0x0A, 0x66, 0x0F,
0x0D, 0xF5, 0x1E, 0x42, 0xBA, 0xF9, 0x1D, 0x4D,
0xE5, 0xB2, 0x32, 0x8D, 0xE0, 0xE8, 0x3D, 0xFC
};
var key = new KcpCustomKey ("test1", testData, false);
var keyData = key.KeyData.ReadData ();
Assert.That (keyData, Is.EqualTo (testData));
key = new KcpCustomKey ("test2", testData, true);
keyData = key.KeyData.ReadData ();
Assert.That (keyData, Is.EqualTo (expectedHash));
}
}
}

View File

@@ -0,0 +1,76 @@
using NUnit.Framework;
using System;
using System.IO;
#if KeePassLib
using KeePassLib.Keys;
#else
using ModernKeePassLib.Keys;
#endif
namespace ModernKeePassLib.Test.Shared.Keys
{
[TestFixture ()]
public class KcpKeyFileTests
{
const string testCreateFile = "TestCreate.xml";
const string testKey = "0123456789";
const string expectedFileStart =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" +
"<KeyFile>\r\n" +
"\t<Meta>\r\n" +
"\t\t<Version>1.00</Version>\r\n" +
"\t</Meta>\r\n" +
"\t<Key>\r\n" +
"\t\t<Data>";
const string expectedFileEnd = "\t</Key>\r\n" +
"</KeyFile>\r\n";
[Test ()]
public void TestConstruct ()
{
var expectedKeyData = new byte[32] {
0xC1, 0xB1, 0x12, 0x77, 0x23, 0xB8, 0x99, 0xB8,
0xB9, 0x3B, 0x1B, 0xFF, 0x6C, 0xBE, 0xA1, 0x5B,
0x8B, 0x99, 0xAC, 0xBD, 0x99, 0x51, 0x85, 0x95,
0x31, 0xAA, 0x14, 0x3D, 0x95, 0xBF, 0x63, 0xFF
};
var fullPath = Path.Combine(Path.GetTempPath(), testCreateFile);
using (var fs = new FileStream(fullPath, FileMode.Create)) {
using (var sw = new StreamWriter(fs)) {
sw.Write (expectedFileStart);
sw.Write (testKey);
sw.Write (expectedFileEnd);
}
}
try {
var keyFile = new KcpKeyFile (fullPath);
var keyData = keyFile.KeyData.ReadData ();
Assert.That (keyData, Is.EqualTo (expectedKeyData));
} finally {
File.Delete (fullPath);
}
}
[Test ()]
public void TestCreate ()
{
var fullPath = Path.Combine(Path.GetTempPath(), testCreateFile);
File.Create(fullPath).Close();
KcpKeyFile.Create (fullPath, null);
try {
var fileContents = File.ReadAllText (fullPath);
Assert.That (fileContents.Length, Is.EqualTo (187));
Assert.That (fileContents, Does.StartWith (expectedFileStart));
Assert.That (fileContents, Does.EndWith (expectedFileEnd));
} finally {
File.Delete (fullPath);
}
}
}
}

View File

@@ -0,0 +1,33 @@
using NUnit.Framework;
using System;
#if KeePassLib
using KeePassLib.Keys;
#else
using ModernKeePassLib.Keys;
#endif
namespace ModernKeePassLib.Test.Shared.Keys
{
[TestFixture ()]
public class KcpPasswordTests
{
const string testPassword = "password";
[Test ()]
public void TestConstruct ()
{
var expectedHash = new byte[32] {
0x5E, 0x88, 0x48, 0x98, 0xDA, 0x28, 0x04, 0x71,
0x51, 0xD0, 0xE5, 0x6F, 0x8D, 0xC6, 0x29, 0x27,
0x73, 0x60, 0x3D, 0x0D, 0x6A, 0xAB, 0xBD, 0xD6,
0x2A, 0x11, 0xEF, 0x72, 0x1D, 0x15, 0x42, 0xD8
};
var key = new KcpPassword (testPassword);
var keyData = key.KeyData.ReadData ();
Assert.That (keyData, Is.EqualTo (expectedHash));
}
}
}

View File

@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{067456C0-086C-46A8-B37F-1405717B7BFC}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ModernKeePassLib.Test</RootNamespace>
<AssemblyName>ModernKeePassLib.Test</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Portable.BouncyCastle.1.8.1.1\lib\portable-net4+sl5+wp8+win8+wpa81\crypto.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Numerics" />
<Reference Include="Windows">
<HintPath>..\ModernKeePassLib\Libs\Windows.winmd</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Cryptography\Cipher\StandardAesEngineTests.cs" />
<Compile Include="Cryptography\CryptoRandomStreamTests.cs" />
<Compile Include="Cryptography\CryptoRandomTests.cs" />
<Compile Include="Cryptography\HashingStreamExTests.cs" />
<Compile Include="Cryptography\HmacOtpTests.cs" />
<Compile Include="Keys\CompositeKeyTests.cs" />
<Compile Include="Keys\KcpCustomKeyTests.cs" />
<Compile Include="Keys\KcpKeyFileTests.cs" />
<Compile Include="Keys\KcpPasswordTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Serialization\HashedBlockStreamTests.cs" />
<Compile Include="Serialization\KdbxFileTests.cs" />
<Compile Include="Utility\GfxUtilTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ModernKeePassLib\ModernKeePassLib.csproj">
<Project>{2e710089-9559-4967-846c-e763dd1f3acb}</Project>
<Name>ModernKeePassLib</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ModernKeePassLib.Test")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ModernKeePassLib.Test")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("067456c0-086c-46a8-b37f-1405717b7bfc")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// 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.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,71 @@
using NUnit.Framework;
using System;
using System.IO;
#if KeePassLib
using KeePassLib.Serialization;
#else
using ModernKeePassLib.Serialization;
#endif
namespace ModernKeePassLib.Test.Shared.Serialization
{
[TestFixture ()]
public class HashedBlockStreamTests
{
static readonly byte[] data = new byte[16];
static readonly byte[] hashStreamData = new byte[] {
// The first 4 bytes are an integer indicating the block index
0x00, 0x00, 0x00, 0x00,
// Then the SHA-256 hash of the data
0x37, 0x47, 0x08, 0xFF, 0xF7, 0x71, 0x9D, 0xD5,
0x97, 0x9E, 0xC8, 0x75, 0xD5, 0x6C, 0xD2, 0x28,
0x6F, 0x6D, 0x3C, 0xF7, 0xEC, 0x31, 0x7A, 0x3B,
0x25, 0x63, 0x2A, 0xAB, 0x28, 0xEC, 0x37, 0xBB,
// then an integer that is the length of the data
0x10, 0x00, 0x00, 0x00,
// and finally the data itself
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Next, a terminating block
0x01, 0x00, 0x00, 0x00,
// terminating block is indicated by a hash of all 0s...
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// ...and by a size of 0
0x00, 0x00, 0x00, 0x00
};
[Test ()]
public void TestRead ()
{
using (var ms = new MemoryStream (hashStreamData)) {
using (var hbs = new HashedBlockStream (ms, false)) {
using (var br = new BinaryReader(hbs)) {
var bytes = br.ReadBytes (data.Length);
Assert.That (bytes, Is.EqualTo (data));
Assert.That (() => br.ReadByte (), Throws.InstanceOf<EndOfStreamException> ());
}
}
}
}
[Test ()]
public void TestWrite ()
{
var buffer = new byte[hashStreamData.Length];
using (var ms = new MemoryStream (buffer)) {
using (var hbs = new HashedBlockStream (ms, true)) {
using (var bw = new BinaryWriter(hbs)) {
bw.Write (data);
}
}
Assert.That (buffer, Is.EqualTo (hashStreamData));
}
}
}
}

View File

@@ -0,0 +1,182 @@
using NUnit.Framework;
using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Windows.UI;
#if KeePassLib
using KeePassLib;
using KeePassLib.Keys;
using KeePassLib.Security;
using KeePassLib.Serialization;
using KeePassLib.Collections;
#else
using ModernKeePassLib;
using ModernKeePassLib.Keys;
using ModernKeePassLib.Security;
using ModernKeePassLib.Serialization;
using ModernKeePassLib.Collections;
#endif
namespace ModernKeePassLib.Test.Shared.Serialization
{
[TestFixture()]
public class KdbxFileTests
{
const string testLocalizedAppName = "My Localized App Name";
const string testDatabaseName = "My Database Name";
const string testDatabaseDescription = "My Database Description";
const string testDefaultUserName = "My Default User Name";
const string testColor = "#FF0000"; // Red
const string testRootGroupName = "My Root Group Name";
const string testRootGroupNotes = "My Root Group Notes";
const string testRootGroupDefaultAutoTypeSequence = "My Root Group Default Auto Type Sequence";
const string testDatabase = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n" +
"<KeePassFile>\r\n" +
"\t<Meta>\r\n" +
"\t\t<Generator>" + testLocalizedAppName + "</Generator>\r\n" +
"\t\t<DatabaseName>" + testDatabaseName + "</DatabaseName>\r\n" +
"\t\t<DatabaseNameChanged>2015-03-14T03:15:26Z</DatabaseNameChanged>\r\n" +
"\t\t<DatabaseDescription>" + testDatabaseDescription + "</DatabaseDescription>\r\n" +
"\t\t<DatabaseDescriptionChanged>2015-03-14T03:15:26Z</DatabaseDescriptionChanged>\r\n" +
"\t\t<DefaultUserName>" + testDefaultUserName + "</DefaultUserName>\r\n" +
"\t\t<DefaultUserNameChanged>2015-03-14T03:15:26Z</DefaultUserNameChanged>\r\n" +
"\t\t<MaintenanceHistoryDays>365</MaintenanceHistoryDays>\r\n" +
//"\t\t<Color>" + testColor + "</Color>\r\n" +
"\t\t<Color></Color>\r\n" +
"\t\t<MasterKeyChanged>2015-03-14T03:15:26Z</MasterKeyChanged>\r\n" +
"\t\t<MasterKeyChangeRec>-1</MasterKeyChangeRec>\r\n" +
"\t\t<MasterKeyChangeForce>-1</MasterKeyChangeForce>\r\n" +
"\t\t<MemoryProtection>\r\n" +
"\t\t\t<ProtectTitle>False</ProtectTitle>\r\n" +
"\t\t\t<ProtectUserName>False</ProtectUserName>\r\n" +
"\t\t\t<ProtectPassword>True</ProtectPassword>\r\n" +
"\t\t\t<ProtectURL>False</ProtectURL>\r\n" +
"\t\t\t<ProtectNotes>False</ProtectNotes>\r\n" +
"\t\t</MemoryProtection>\r\n" +
"\t\t<RecycleBinEnabled>True</RecycleBinEnabled>\r\n" +
"\t\t<RecycleBinUUID>AAAAAAAAAAAAAAAAAAAAAA==</RecycleBinUUID>\r\n" +
"\t\t<RecycleBinChanged>2015-03-14T03:15:26Z</RecycleBinChanged>\r\n" +
"\t\t<EntryTemplatesGroup>AAAAAAAAAAAAAAAAAAAAAA==</EntryTemplatesGroup>\r\n" +
"\t\t<EntryTemplatesGroupChanged>2015-03-14T03:15:26Z</EntryTemplatesGroupChanged>\r\n" +
"\t\t<HistoryMaxItems>10</HistoryMaxItems>\r\n" +
"\t\t<HistoryMaxSize>6291456</HistoryMaxSize>\r\n" +
"\t\t<LastSelectedGroup>AAAAAAAAAAAAAAAAAAAAAA==</LastSelectedGroup>\r\n" +
"\t\t<LastTopVisibleGroup>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleGroup>\r\n" +
"\t\t<Binaries />\r\n" +
"\t\t<CustomData />\r\n" +
"\t</Meta>\r\n" +
"\t<Root>\r\n" +
"\t\t<Group>\r\n" +
"\t\t\t<UUID>AAAAAAAAAAAAAAAAAAAAAA==</UUID>\r\n" +
"\t\t\t<Name>" + testRootGroupName + "</Name>\r\n" +
"\t\t\t<Notes>" + testRootGroupNotes + "</Notes>\r\n" +
"\t\t\t<IconID>49</IconID>\r\n" +
"\t\t\t<Times>\r\n" +
"\t\t\t\t<CreationTime>2015-03-14T03:15:26Z</CreationTime>\r\n" +
"\t\t\t\t<LastModificationTime>2015-03-14T03:15:26Z</LastModificationTime>\r\n" +
"\t\t\t\t<LastAccessTime>2015-03-14T03:15:26Z</LastAccessTime>\r\n" +
"\t\t\t\t<ExpiryTime>2015-03-14T03:15:26Z</ExpiryTime>\r\n" +
"\t\t\t\t<Expires>False</Expires>\r\n" +
"\t\t\t\t<UsageCount>0</UsageCount>\r\n" +
"\t\t\t\t<LocationChanged>2015-03-14T03:15:26Z</LocationChanged>\r\n" +
"\t\t\t</Times>\r\n" +
"\t\t\t<IsExpanded>True</IsExpanded>\r\n" +
"\t\t\t<DefaultAutoTypeSequence>" + testRootGroupDefaultAutoTypeSequence + "</DefaultAutoTypeSequence>\r\n" +
"\t\t\t<EnableAutoType>null</EnableAutoType>\r\n" +
"\t\t\t<EnableSearching>null</EnableSearching>\r\n" +
"\t\t\t<LastTopVisibleEntry>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleEntry>\r\n" +
"\t\t</Group>\r\n" +
"\t\t<DeletedObjects />\r\n" +
"\t</Root>\r\n" +
"</KeePassFile>";
const string testDate = "2015-03-14T03:15:26Z";
[Test()]
public void TestLoad()
{
var database = new PwDatabase();
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(testDatabase)))
{
var file = new KdbxFile(database);
file.Load(ms, KdbxFormat.PlainXml, null);
}
//Assert.That(database.Color.ToArgb(), Is.EqualTo(Color.Red.ToArgb()));
Assert.That(database.Compression, Is.EqualTo(PwCompressionAlgorithm.GZip));
//Assert.That (database.CustomData, Is.EqualTo ());
Assert.That(database.CustomIcons, Is.Empty);
}
[Test()]
public void TestSave()
{
var buffer = new byte[4096];
using (var ms = new MemoryStream(buffer))
{
var database = new PwDatabase();
database.New(new IOConnectionInfo(), new CompositeKey());
var date = DateTime.Parse(testDate);
PwDatabase.LocalizedAppName = testLocalizedAppName;
database.Name = testDatabaseName;
database.NameChanged = date;
database.Description = testDatabaseDescription;
database.DescriptionChanged = date;
database.DefaultUserName = testDefaultUserName;
database.DefaultUserNameChanged = date;
//database.Color = Color.Red;
database.MasterKeyChanged = date;
database.RecycleBinChanged = date;
database.EntryTemplatesGroupChanged = date;
database.RootGroup.Uuid = PwUuid.Zero;
database.RootGroup.Name = testRootGroupName;
database.RootGroup.Notes = testRootGroupNotes;
database.RootGroup.DefaultAutoTypeSequence = testRootGroupDefaultAutoTypeSequence;
database.RootGroup.CreationTime = date;
database.RootGroup.LastModificationTime = date;
database.RootGroup.LastAccessTime = date;
database.RootGroup.ExpiryTime = date;
database.RootGroup.LocationChanged = date;
var file = new KdbxFile(database);
file.Save(ms, null, KdbxFormat.PlainXml, null);
}
var fileContents = Encoding.UTF8.GetString(buffer).Replace("\0", "");
if (typeof(KdbxFile).Namespace.StartsWith("KeePassLib.")
&& Environment.OSVersion.Platform != PlatformID.Win32NT)
{
// Upstream KeePassLib does not specify line endings for XmlTextWriter,
// so it uses native line endings.
fileContents = fileContents.Replace("\n", "\r\n");
}
Assert.That(fileContents, Is.EqualTo(testDatabase));
}
[Test]
public void TestSearch()
{
var database = new PwDatabase();
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(testDatabase)))
{
var file = new KdbxFile(database);
file.Load(ms, KdbxFormat.PlainXml, null);
}
var sp = new SearchParameters()
{
SearchString = "sfsoiwsefsi"
};
var listStorage = new PwObjectList<PwEntry>();
database.RootGroup.SearchEntries(sp, listStorage);
Assert.AreEqual(0U, listStorage.UCount);
var entry = new PwEntry(true, true);
entry.Strings.Set("Title", new ProtectedString(false, "NaMe"));
database.RootGroup.AddEntry(entry, true);
sp.SearchString = "name";
database.RootGroup.SearchEntries(sp, listStorage);
Assert.AreEqual(1U, listStorage.UCount);
}
}
}

View File

@@ -0,0 +1,32 @@
using NUnit.Framework;
using System;
#if KeePassLib
using KeePassLib.Utility;
#else
using Splat;
using ModernKeePassLib.Utility;
#endif
namespace ModernKeePassLib.Test.Shared.Utility
{
[TestFixture ()]
public class GfxUtilTests
{
// 16x16 all white PNG file, base64 encoded
const string testImageData =
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAAsTAAA" +
"LEwEAmpwYAAAAB3RJTUUH3wMOFgIgmTCUMQAAABl0RVh0Q29tbWVudABDcmVhdG" +
"VkIHdpdGggR0lNUFeBDhcAAAAaSURBVCjPY/z//z8DKYCJgUQwqmFUw9DRAABVb" +
"QMdny4VogAAAABJRU5ErkJggg==";
[Test ()]
public void TestLoadImage ()
{
var testData = Convert.FromBase64String (testImageData);
var image = GfxUtil.LoadImage (testData);
Assert.That (image.Width, Is.EqualTo (16));
Assert.That (image.Height, Is.EqualTo (16));
}
}
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="PInvoke.BCrypt" publicKeyToken="9e300f9f87f04a7a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.5.0.0" newVersion="0.5.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="PInvoke.NCrypt" publicKeyToken="9e300f9f87f04a7a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.5.0.0" newVersion="0.5.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Validation" publicKeyToken="2fc06f0d701809a7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.4.0.0" newVersion="2.4.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.WindowsRuntime" publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.10.0" newVersion="4.0.10.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net462" />
<package id="NUnit" version="3.8.1" targetFramework="net452" />
<package id="Splat" version="2.0.0" targetFramework="net462" />
</packages>

View File

@@ -1,36 +0,0 @@
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################
#ignore thumbnails created by windows
Thumbs.db
#Ignore files build by Visual Studio
*.obj
*.exe
*.pdb
*.user
*.aps
*.pch
*.vspscc
*_i.c
*_p.c
*.ncb
*.suo
*.tlb
*.tlh
*.bak
*.cache
*.ilk
*.log
[Bb]in
[Dd]ebug*/
*.lib
*.sbr
obj/
[Rr]elease*/
_ReSharper*/
[Tt]est[Rr]esult*
Translation/TrlUtil.vshost.exe.manifest
*.nupkg
.vs/
/UpgradeLog.htm

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2012 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -194,13 +194,6 @@ namespace ModernKeePassLib.Collections
return true;
}
public void Add(AutoTypeAssociation a)
{
Debug.Assert(a != null); if(a == null) throw new ArgumentNullException("a");
m_lWindowAssocs.Add(a);
}
public AutoTypeAssociation GetAt(int iIndex)
{
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
@@ -209,6 +202,22 @@ namespace ModernKeePassLib.Collections
return m_lWindowAssocs[iIndex];
}
public void Add(AutoTypeAssociation a)
{
if(a == null) { Debug.Assert(false); throw new ArgumentNullException("a"); }
m_lWindowAssocs.Add(a);
}
public void Insert(int iIndex, AutoTypeAssociation a)
{
if((iIndex < 0) || (iIndex > m_lWindowAssocs.Count))
throw new ArgumentOutOfRangeException("iIndex");
if(a == null) { Debug.Assert(false); throw new ArgumentNullException("a"); }
m_lWindowAssocs.Insert(iIndex, a);
}
public void RemoveAt(int iIndex)
{
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
@@ -216,5 +225,20 @@ namespace ModernKeePassLib.Collections
m_lWindowAssocs.RemoveAt(iIndex);
}
// public void Sort()
// {
// m_lWindowAssocs.Sort(AutoTypeConfig.AssocCompareFn);
// }
// private static int AssocCompareFn(AutoTypeAssociation x,
// AutoTypeAssociation y)
// {
// if(x == null) { Debug.Assert(false); return ((y == null) ? 0 : -1); }
// if(y == null) { Debug.Assert(false); return 1; }
// int cn = x.WindowName.CompareTo(y.WindowName);
// if(cn != 0) return cn;
// return x.Sequence.CompareTo(y.Sequence);
// }
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2012 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@ using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
#if KeePassLibSD
using ModernKeePassLibSD;
using KeePassLibSD;
#endif
namespace ModernKeePassLib.Collections

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