completed simple merge workflow. Made passwordInputController better reusabel

This commit is contained in:
michael starke
2017-09-01 16:41:33 +02:00
parent 716a822dc0
commit 70650fc20f
7 changed files with 113 additions and 49 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9059" systemVersion="15B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9059"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPIconSelectViewController">
@@ -26,31 +26,24 @@
<collectionView focusRingType="none" id="58">
<rect key="frame" x="0.0" y="0.0" width="380" height="280"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="primaryBackgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
<connections>
<outlet property="itemPrototype" destination="61" id="63"/>
</connections>
</collectionView>
</subviews>
<animations/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<animations/>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="59">
<rect key="frame" x="1" y="144" width="233" height="15"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="60">
<rect key="frame" x="234" y="1" width="15" height="143"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</scroller>
</scrollView>
<button verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="101">
<rect key="frame" x="263" y="18" width="117" height="25"/>
<animations/>
<buttonCell key="cell" type="roundTextured" title="Use Default Icon" bezelStyle="texturedRounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="102">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -61,7 +54,6 @@
</button>
<button verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="His-4A-hcY">
<rect key="frame" x="198" y="18" width="57" height="25"/>
<animations/>
<buttonCell key="cell" type="roundTextured" title="Cancel" bezelStyle="texturedRounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="1kM-cI-P1o">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -82,8 +74,7 @@
<constraint firstItem="101" firstAttribute="leading" secondItem="His-4A-hcY" secondAttribute="trailing" constant="8" id="EHs-2w-CJq"/>
<constraint firstItem="His-4A-hcY" firstAttribute="baseline" secondItem="101" secondAttribute="baseline" id="fq3-rS-gvQ"/>
</constraints>
<animations/>
<point key="canvasLocation" x="370" y="95"/>
<point key="canvasLocation" x="-26" y="-60"/>
</customView>
<collectionViewItem id="61">
<connections>
@@ -100,7 +91,6 @@
<constraint firstAttribute="width" constant="32" id="161"/>
<constraint firstAttribute="height" constant="32" id="162"/>
</constraints>
<animations/>
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="NSAddTemplate" imagePosition="only" alignment="center" imageScaling="proportionallyUpOrDown" inset="2" id="115">
<behavior key="behavior" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@@ -115,7 +105,6 @@
<constraint firstItem="114" firstAttribute="centerY" secondItem="113" secondAttribute="centerY" id="168"/>
<constraint firstItem="114" firstAttribute="centerX" secondItem="113" secondAttribute="centerX" id="169"/>
</constraints>
<animations/>
</customView>
</objects>
<resources>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -7,12 +7,14 @@
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPPasswordInputController">
<connections>
<outlet property="cancelButton" destination="2pb-ZG-spA" id="YrR-Yi-dnQ"/>
<outlet property="enablePasswordCheckBox" destination="d8O-Ha-rrS" id="2AI-e9-sph"/>
<outlet property="errorImageView" destination="262" id="294"/>
<outlet property="errorInfoTextField" destination="268" id="67b-K9-DZd"/>
<outlet property="keyPathControl" destination="241" id="261"/>
<outlet property="passwordTextField" destination="338" id="495"/>
<outlet property="togglePasswordButton" destination="408" id="493"/>
<outlet property="unlockButton" destination="2" id="ZRr-Ui-ExP"/>
<outlet property="view" destination="1" id="143"/>
</connections>
</customObject>
@@ -102,6 +104,19 @@ DQ
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="2pb-ZG-spA">
<rect key="frame" x="236" y="64" width="82" height="32"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="erj-mR-UyO">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<connections>
<action selector="_submit:" target="-2" id="aVF-1d-1Hq"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="2" secondAttribute="bottom" constant="20" symbolic="YES" id="122"/>
@@ -125,11 +140,13 @@ DQ
<constraint firstItem="408" firstAttribute="trailing" secondItem="486" secondAttribute="trailing" id="492"/>
<constraint firstItem="2" firstAttribute="trailing" secondItem="486" secondAttribute="trailing" id="496"/>
<constraint firstItem="408" firstAttribute="leading" secondItem="338" secondAttribute="trailing" constant="8" symbolic="YES" id="7qE-8F-QgB"/>
<constraint firstItem="2pb-ZG-spA" firstAttribute="baseline" secondItem="2" secondAttribute="baseline" id="9nK-MH-Ozs"/>
<constraint firstItem="338" firstAttribute="leading" secondItem="d8O-Ha-rrS" secondAttribute="trailing" constant="8" symbolic="YES" id="KYs-Ia-SVl"/>
<constraint firstItem="d8O-Ha-rrS" firstAttribute="centerY" secondItem="338" secondAttribute="centerY" id="kgB-jV-OGy"/>
<constraint firstItem="d8O-Ha-rrS" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="1" secondAttribute="leading" constant="20" symbolic="YES" id="vxq-YP-UhR"/>
<constraint firstItem="2" firstAttribute="leading" secondItem="2pb-ZG-spA" secondAttribute="trailing" constant="12" id="ytJ-5Z-5rT"/>
</constraints>
<point key="canvasLocation" x="91" y="140"/>
<point key="canvasLocation" x="-240" y="25"/>
</customView>
</objects>
<resources>

View File

@@ -151,7 +151,7 @@ FOUNDATION_EXPORT NSString *const MPDocumentGroupKey;
- (void)writeXMLToURL:(NSURL *)url;
- (void)readXMLfromURL:(NSURL *)url;
- (void)mergeWithContentsFromURL:(NSURL *)url;
- (void)mergeWithContentsFromURL:(NSURL *)url key:(KPKCompositeKey *)key;
/* Undoable Intiialization of elements */
- (KPKGroup *)createGroup:(KPKGroup *)parent;

View File

@@ -33,6 +33,7 @@
#import "MPTreeDelegate.h"
#import "MPTargetNodeResolving.h"
#import "MPErrorRecoveryAttempter.h"
#import "MPPasswordInputController.h"
#import "KeePassKit/KeePassKit.h"
@@ -334,7 +335,7 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGrou
}
if(strategy == MPFileChangeStrategyMerge) {
[self mergeWithContentsFromURL:self.fileURL];
[self mergeWithContentsFromURL:self.fileURL key:self.compositeKey];
}
else if(strategy == MPFileChangeStrategyUseOther) {
[self revertToContentsOfURL:self.fileURL ofType:self.fileType error:nil];
@@ -357,29 +358,53 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGrou
self.encryptedData = nil;
}
- (void)mergeWithContentsFromURL:(NSURL *)url {
/* TODO read file to check what format to use */
- (void)mergeWithContentsFromURL:(NSURL *)url key:(KPKCompositeKey *)key {
NSError *error;
KPKTree *otherTree = [[KPKTree alloc] initWithContentsOfUrl:url key:self.compositeKey error:&error];
if(!otherTree) {
if(error.code == KPKErrorPasswordAndOrKeyfileWrong) {
NSAlert *alert = [[NSAlert alloc] init];
alert.alertStyle = NSAlertStyleWarning;
alert.informativeText = NSLocalizedString(@"ALERT_MERGE_OTHER_KEY_CHANGED", @"");
[self presentError:error];
}
else {
[self presentError:error];
}
KPKTree *otherTree;
if(key) {
otherTree = [[KPKTree alloc] initWithContentsOfUrl:url key:key error:&error];
}
else {
if(otherTree) {
[self.tree syncronizeWithTree:otherTree options:KPKSynchronizationSynchronizeOption];
/* the key might have changed so update ours! */
//self.compositeKey = key;
NSUserNotification *notification = [[NSUserNotification alloc] init];
notification.title = NSApp.applicationName;
notification.informativeText = NSLocalizedString(@"AUTO_MERGE_NOTIFICATION_TEXT", @"");
notification.deliveryDate = NSDate.date;
[NSUserNotificationCenter.defaultUserNotificationCenter scheduleNotification:notification];
}
else {
if(!key || error.code == KPKErrorPasswordAndOrKeyfileWrong) {
NSWindow *sheet = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable
backing:NSBackingStoreBuffered
defer:NO];
MPPasswordInputController *passwordInputController = [[MPPasswordInputController alloc] init];
__weak MPDocument *welf = self;
[passwordInputController requestPasswordWithMessage:NSLocalizedString(@"EXTERN_CHANGE_OF_MASTERKEY", @"The master key was changed by an extrenal programm!")
cancelLabel:NSLocalizedString(@"ABORT_MERGE_KEEP_MINE", @"Button label to abort a merge on a file with changed master key!")
completionHandler:^BOOL(NSString *password, NSURL *keyURL, BOOL didCancel, NSError *__autoreleasing *error) {
[welf.windowForSheet endSheet:sheet returnCode:(didCancel ? NSModalResponseCancel : NSModalResponseOK)];
if(!didCancel) {
KPKCompositeKey *compositeKey = [[KPKCompositeKey alloc] initWithPassword:password key:keyURL];
[welf mergeWithContentsFromURL:url key:compositeKey];
}
// just return yes regardless since we will display the sheet again if needed!
return YES;
}];
sheet.contentViewController = passwordInputController;
[self.windowForSheet beginSheet:sheet completionHandler:^(NSModalResponse returnCode) { /* nothing to do, rest is done in other handler! */ }];
}
else {
/* unable to merge */
NSAlert *alert = [NSAlert alertWithError:error];
[alert beginSheetModalForWindow:self.windowForSheet completionHandler:^(NSModalResponse returnCode) {
// do nothing;
}];
}
}
}

View File

@@ -305,7 +305,7 @@ typedef void (^MPPasswordChangedBlock)(BOOL didChangePassword);
//openPanel.allowedFileTypes = @[(id)kUTTypeXML];
[openPanel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
if(result == NSFileHandlingPanelOKButton) {
[document mergeWithContentsFromURL:openPanel.URL];
[document mergeWithContentsFromURL:openPanel.URL key:document.compositeKey];
}
}];
}
@@ -324,7 +324,10 @@ typedef void (^MPPasswordChangedBlock)(BOOL didChangePassword);
}
[self _setContentViewController:self.passwordInputController];
__weak MPDocumentWindowController *welf = self;
[self.passwordInputController requestPasswordWithCompletionHandler:^BOOL(NSString *password, NSURL *keyURL, NSError *__autoreleasing *error) {
[self.passwordInputController requestPasswordWithCompletionHandler:^BOOL(NSString *password, NSURL *keyURL, BOOL cancel, NSError *__autoreleasing *error) {
if(cancel) {
return NO;
}
return [((MPDocument *)welf.document) unlockWithPassword:password keyFileURL:keyURL error:error];
}];
}

View File

@@ -12,8 +12,10 @@
@interface MPPasswordInputController : MPViewController
typedef BOOL (^passwordInputCompletionBlock)(NSString *password, NSURL *keyURL, NSError *__autoreleasing*error);
typedef BOOL (^passwordInputCompletionBlock)(NSString *password, NSURL *keyURL, BOOL didCancel, NSError *__autoreleasing*error);
- (void)requestPasswordWithCompletionHandler:(passwordInputCompletionBlock)completionHandler;
- (void)requestPasswordWithMessage:(NSString *)message cancelLabel:(NSString *)cancelLabel completionHandler:(passwordInputCompletionBlock)completionHandler;
@end

View File

@@ -26,6 +26,11 @@
@property (weak) IBOutlet NSTextField *errorInfoTextField;
@property (weak) IBOutlet NSButton *togglePasswordButton;
@property (weak) IBOutlet NSButton *enablePasswordCheckBox;
@property (weak) IBOutlet NSButton *unlockButton;
@property (weak) IBOutlet NSButton *cancelButton;
@property (copy) NSString *message;
@property (copy) NSString *cancelLabel;
@property (assign) BOOL showPassword;
@property (nonatomic, assign) BOOL enablePassword;
@@ -66,11 +71,17 @@
return self.passwordTextField;
}
- (void)requestPasswordWithCompletionHandler:(passwordInputCompletionBlock)completionHandler {
- (void)requestPasswordWithMessage:(NSString *)message cancelLabel:(NSString *)cancelLabel completionHandler:(passwordInputCompletionBlock)completionHandler {
self.completionHandler = completionHandler;
self.message = message;
self.cancelLabel = cancelLabel;
[self _reset];
}
- (void)requestPasswordWithCompletionHandler:(passwordInputCompletionBlock)completionHandler {
[self requestPasswordWithMessage:nil cancelLabel:nil completionHandler:completionHandler];
}
#pragma mark Properties
- (void)setEnablePassword:(BOOL)enablePassword {
if(_enablePassword != enablePassword) {
@@ -87,14 +98,24 @@
#pragma mark -
#pragma mark Private
- (IBAction)_submit:(id)sender {
if(self.completionHandler) {
/* No password is different than an empty password */
NSError *error = nil;
NSString *password = self.enablePassword ? self.passwordTextField.stringValue : nil;
if(!self.completionHandler(password, self.keyPathControl.URL, &error)) {
[self _showError:error];
[self.view.window shakeWindow:nil];
}
if(!self.completionHandler) {
return;
}
/* No password is different than an empty password */
NSError *error = nil;
NSString *password = self.enablePassword ? self.passwordTextField.stringValue : nil;
BOOL cancel = (sender == self.cancelButton);
BOOL result = self.completionHandler(password, self.keyPathControl.URL, cancel, &error);
if(cancel || result) {
return;
}
[self _showError:error];
/* do not shake if we are a sheet */
if(!self.view.window.isSheet) {
[self.view.window shakeWindow:nil];
}
}
@@ -112,8 +133,15 @@
self.showPassword = NO;
self.enablePassword = YES;
self.passwordTextField.stringValue = @"";
self.errorInfoTextField.hidden = YES;
self.errorImageView.hidden = YES;
self.errorInfoTextField.hidden = (nil == self.message);
if(self.message) {
self.errorInfoTextField.stringValue = self.message;
}
self.errorImageView.hidden = (nil == self.message);;
self.cancelButton.hidden = (nil == self.cancelLabel);
if(self.cancelLabel) {
self.cancelButton.stringValue = self.cancelLabel;
}
[self resetKeyFile:self];
}