2
0

Mac support for user handling and iCloud improvements.

[MOVED]     Extract user migration out of iOS specific codebase.
[UPDATED]   iCloud persistence manager.
[ADDED]     Mac: Hotkey for signing the user out.
[IMPROVED]  Mac: Menu item handling and usability.
This commit is contained in:
Maarten Billemont 2013-01-17 00:37:20 -05:00
parent 32f870406c
commit 4c19a29897
20 changed files with 520 additions and 273 deletions

@ -1 +1 @@
Subproject commit 8c7894a983f1c1e0795a6e3d5ee7a223a5e01a54
Subproject commit 20e2a1dec08fef1e99433f9a8b3a2310bb00717d

View File

@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
93D39C24C655D0FD48DCA1C3 /* menu-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 93D39E6D20434649192464E0 /* menu-icon.png */; };
93D39DEE2F9E55DAA206FD6E /* menu-icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 93D39F808A2C83E35C5C0411 /* menu-icon@2x.png */; };
DA34F0CC163B8C31006FFC95 /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = DA34F08D163B8C31006FFC95 /* NSArray+Indexing.h */; };
DA34F0CD163B8C31006FFC95 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = DA34F08E163B8C31006FFC95 /* NSArray+Indexing.m */; };
DA34F0CE163B8C31006FFC95 /* NSBundle+PearlMutableInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA34F08F163B8C31006FFC95 /* NSBundle+PearlMutableInfo.h */; };
@ -662,6 +664,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
93D39E6D20434649192464E0 /* menu-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu-icon.png"; sourceTree = "<group>"; };
93D39F808A2C83E35C5C0411 /* menu-icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu-icon@2x.png"; sourceTree = "<group>"; };
DA34F08D163B8C31006FFC95 /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
DA34F08E163B8C31006FFC95 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; };
DA34F08F163B8C31006FFC95 /* NSBundle+PearlMutableInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+PearlMutableInfo.h"; sourceTree = "<group>"; };
@ -2565,6 +2569,8 @@
DA600E9615057F10008E9AB6 /* Lock */,
DA600EA015057F10008E9AB6 /* logo-bare.png */,
DA600EA115057F10008E9AB6 /* Tooltips */,
93D39E6D20434649192464E0 /* menu-icon.png */,
93D39F808A2C83E35C5C0411 /* menu-icon@2x.png */,
);
path = Resources;
sourceTree = "<group>";
@ -5719,6 +5725,8 @@
DAF236BA163B24D5008AF5B5 /* MainMenu.xib in Resources */,
DAF236BD163B24D5008AF5B5 /* MasterPassword.entitlements in Resources */,
DAF236C0163B24D5008AF5B5 /* MPPasswordWindowController.xib in Resources */,
93D39C24C655D0FD48DCA1C3 /* menu-icon.png in Resources */,
93D39DEE2F9E55DAA206FD6E /* menu-icon@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -5935,7 +5943,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.7;
MACOSX_DEPLOYMENT_TARGET = 10.8;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
@ -5994,7 +6002,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.7;
MACOSX_DEPLOYMENT_TARGET = 10.8;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
SDKROOT = macosx;

View File

@ -56,12 +56,6 @@
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "-com.apple.coredata.ubiquity.logLevel 3"
isEnabled = "NO">
</CommandLineArgument>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -25,6 +25,7 @@
@required
- (NSUInteger)version;
- (void)migrateUser:(MPUserEntity *)user completion:(void(^)(BOOL userRequiresNewMigration))completion;
- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit;
- (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName;

View File

@ -31,6 +31,31 @@
return 0;
}
- (void)migrateUser:(MPUserEntity *)user completion:(void(^)(BOOL userRequiresNewMigration))completion {
BOOL didRequireExplicitMigration = user.requiresExplicitMigration;
[user.managedObjectContext performBlock:^void() {
NSError *error = nil;
NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])];
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d", MPAlgorithmDefaultVersion];
NSArray *migrationElements = [user.managedObjectContext executeFetchRequest:migrationRequest error:&error];
if (!migrationElements) {
err(@"While looking for elements to migrate: %@", error);
return;
}
if (didRequireExplicitMigration)
user.requiresExplicitMigration = NO;
for (MPElementEntity *migrationElement in migrationElements)
if (![migrationElement migrateExplicitly:NO])
user.requiresExplicitMigration = YES;
dispatch_async(dispatch_get_main_queue(), ^{
completion(!didRequireExplicitMigration && user.requiresExplicitMigration);
});
}];
}
- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit {
if (element.version != [self version] - 1)

View File

@ -20,6 +20,4 @@
+ (MPAppDelegate_Shared *)get;
- (NSURL *)applicationFilesDirectory;
@end

View File

@ -28,23 +28,6 @@
#endif
}
- (NSURL *)applicationFilesDirectory {
#if TARGET_OS_IPHONE
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
#else
NSURL *appSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *applicationFilesDirectory = [appSupportURL URLByAppendingPathComponent:@"com.lyndir.lhunath.MasterPassword"];
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtURL:applicationFilesDirectory withIntermediateDirectories:YES attributes:nil error:&error];
if (error)
err(@"Couldn't create application directory: %@, error occurred: %@", applicationFilesDirectory, error);
return applicationFilesDirectory;
#endif
}
- (MPUserEntity *)activeUser {
if (!self.activeUserID)

View File

@ -21,9 +21,7 @@ typedef enum {
@interface MPAppDelegate_Shared (Store)<UbiquityStoreManagerDelegate>
+ (NSManagedObjectContext *)managedObjectContextIfReady;
+ (NSManagedObjectModel *)managedObjectModel;
- (NSManagedObjectContext *)managedObjectContextIfReady;
- (NSManagedObjectModel *)managedObjectModel;
- (UbiquityStoreManager *)storeManager;
- (void)saveContext;

View File

@ -6,10 +6,15 @@
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import <objc/runtime.h>
#import "MPAppDelegate_Store.h"
#import
#import
@implementation MPAppDelegate_Shared (Store)
static char managedObjectContextKey;
#pragma mark - Core Data setup
+ (NSManagedObjectContext *)managedObjectContextIfReady {
@ -17,37 +22,21 @@
return [[self get] managedObjectContextIfReady];
}
+ (NSManagedObjectModel *)managedObjectModel {
return [[self get] managedObjectModel];
}
- (NSManagedObjectModel *)managedObjectModel {
static NSManagedObjectModel *managedObjectModel = nil;
if (managedObjectModel)
return managedObjectModel;
return managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
}
- (NSManagedObjectContext *)managedObjectContextIfReady {
if (![self storeManager].isReady)
return nil;
static NSManagedObjectContext *managedObjectContext = nil;
NSManagedObjectContext *managedObjectContext = objc_getAssociatedObject(self, &managedObjectContextKey);
if (!managedObjectContext) {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[managedObjectContext performBlockAndWait:^{
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
managedObjectContext.undoManager = [NSUndoManager new];
managedObjectContext.persistentStoreCoordinator = self.storeManager.persistentStoreCoordinator;
}];
objc_setAssociatedObject(self, &managedObjectContextKey, managedObjectContext, OBJC_ASSOCIATION_RETAIN);
}
[[self storeManager] persistentStoreCoordinator];
if (![self storeManager].isReady)
if (![managedObjectContext.persistentStoreCoordinator.persistentStores count])
// Store not available yet.
return nil;
return managedObjectContext;
@ -59,34 +48,86 @@
if (storeManager)
return storeManager;
storeManager = [[UbiquityStoreManager alloc] initWithManagedObjectModel:[self managedObjectModel]
localStoreURL:[[self applicationFilesDirectory] URLByAppendingPathComponent:@"MasterPassword.sqlite"]
containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil
containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
#if TARGET_OS_IPHONE
additionalStoreOptions:@{
NSPersistentStoreFileProtectionKey: NSFileProtectionComplete
}
additionalStoreOptions:@{
NSPersistentStoreFileProtectionKey : NSFileProtectionComplete
}];
#else
additionalStoreOptions:nil
additionalStoreOptions:nil];
#endif
];
storeManager.delegate = self;
#ifdef DEBUG
storeManager.hardResetEnabled = YES;
#endif
#if TARGET_OS_IPHONE
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification
object:[UIApplication sharedApplication] queue:nil
// Migrate old store to new store location.
NSNumber *cloudEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:@"iCloudEnabledKey"];
if (cloudEnabled) {
if ([cloudEnabled boolValue]) {
NSString *uuid = [[NSUserDefaults standardUserDefaults] stringForKey:@"LocalUUIDKey"];
NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"];
NSURL *oldCloudContentURL = [[cloudContainerURL URLByAppendingPathComponent:@"Data" isDirectory:YES]
URLByAppendingPathComponent:uuid isDirectory:YES];
NSURL *oldCloudStoreURL = [[[cloudContainerURL URLByAppendingPathComponent:@"Database.nosync" isDirectory:YES]
URLByAppendingPathComponent:uuid isDirectory:NO]
URLByAppendingPathExtension:@"sqlite"];
NSURL *newCloudContentURL = [storeManager URLForCloudContent];
NSURL *newCloudStoreURL = [storeManager URLForCloudStore];
if ([[NSFileManager defaultManager] fileExistsAtPath:oldCloudStoreURL.path isDirectory:NO] &&
![[NSFileManager defaultManager] fileExistsAtPath:newCloudStoreURL.path isDirectory:NO]) {
NSError *error = nil;
NSDictionary *options = @{
NSPersistentStoreUbiquitousContentNameKey : uuid,
NSPersistentStoreUbiquitousContentURLKey : oldCloudContentURL,
NSMigratePersistentStoresAutomaticallyOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES};
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil
URL:oldCloudStoreURL options:options error:&error];
if (oldStore)
[psc migratePersistentStore:oldStore toURL:newCloudStoreURL options:options withType:NSSQLiteStoreType error:&error];
if (error)
err(@"While migrating cloud store from %@ -> %@: %@", oldCloudStoreURL, newCloudStoreURL, error);
else {
[psc removePersistentStore:[psc.persistentStores lastObject] error:nil];
[[NSFileManager defaultManager] removeItemAtURL:oldCloudStoreURL error:nil];
}
}
} else {
NSURL *applicationFilesDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject];
NSURL *oldLocalStoreURL = [[applicationFilesDirectory URLByAppendingPathComponent:@"MasterPassword" isDirectory:NO]
URLByAppendingPathExtension:@"sqlite"];
NSURL *newLocalStoreURL = [storeManager URLForLocalStore];
if ([[NSFileManager defaultManager] fileExistsAtPath:oldLocalStoreURL.path isDirectory:NO] &&
![[NSFileManager defaultManager] fileExistsAtPath:newLocalStoreURL.path isDirectory:NO]) {
NSError *error = nil;
NSDictionary *options = @{
NSMigratePersistentStoresAutomaticallyOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES};
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil
URL:oldLocalStoreURL options:options error:&error];
if (oldStore)
[psc migratePersistentStore:oldStore toURL:newLocalStoreURL options:options withType:NSSQLiteStoreType error:&error];
if (error)
err(@"While migrating local store from %@ -> %@: %@", oldLocalStoreURL, newLocalStoreURL, error);
else {
[psc removePersistentStore:[psc.persistentStores lastObject] error:nil];
[[NSFileManager defaultManager] removeItemAtURL:oldLocalStoreURL error:nil];
}
}
}
}
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"iCloudEnabledKey"];
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification
object:storeManager queue:nil
usingBlock:^(NSNotification *note) {
[storeManager checkiCloudStatus];
objc_setAssociatedObject(self, &managedObjectContextKey, nil, OBJC_ASSOCIATION_RETAIN);
}];
#else
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillBecomeActiveNotification
object:[NSApplication sharedApplication] queue:nil
usingBlock:^(NSNotification *note) {
[storeManager checkiCloudStatus];
}];
#endif
#if TARGET_OS_IPHONE
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification
object:[UIApplication sharedApplication] queue:nil
@ -128,16 +169,16 @@
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToiCloud:(BOOL)iCloudEnabled {
// manager.iCloudEnabled is more reliable (eg. iOS' MPAppDelegate tampers with didSwitch a bit)
iCloudEnabled = manager.iCloudEnabled;
// manager.cloudEnabled is more reliable (eg. iOS' MPAppDelegate tampers with didSwitch a bit)
iCloudEnabled = manager.cloudEnabled;
inf(@"Using iCloud? %@", iCloudEnabled? @"YES": @"NO");
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:iCloudEnabled? MPCheckpointCloudEnabled: MPCheckpointCloudDisabled];
[TestFlight passCheckpoint:cloudEnabled? MPCheckpointCloudEnabled: MPCheckpointCloudDisabled];
#endif
#ifdef LOCALYTICS
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCloud attributes:@{
@"enabled": iCloudEnabled? @"YES": @"NO"
@"enabled": cloudEnabled? @"YES": @"NO"
}];
#endif
@ -162,7 +203,6 @@
switch (cause) {
case UbiquityStoreManagerErrorCauseDeleteStore:
case UbiquityStoreManagerErrorCauseDeleteLogs:
case UbiquityStoreManagerErrorCauseCreateStorePath:
case UbiquityStoreManagerErrorCauseClearStore:
break;
@ -178,13 +218,12 @@
#ifdef LOCALYTICS
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointLocalStoreReset attributes:nil];
#endif
manager.hardResetEnabled = YES;
[manager hardResetLocalStorage];
[manager deleteLocalStore];
Throw(@"Local store was reset, application must be restarted to use it.");
} else
// Try again.
[[self storeManager] persistentStoreCoordinator];
[manager persistentStoreCoordinator];
}
case UbiquityStoreManagerErrorCauseOpenCloudStore: {
wrn(@"iCloud store could not be opened: %@", error);
@ -198,13 +237,17 @@
#ifdef LOCALYTICS
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCloudStoreReset attributes:nil];
#endif
manager.hardResetEnabled = YES;
[manager hardResetCloudStorage];
[manager deleteCloudStore];
break;
} else
// Try again.
[[self storeManager] persistentStoreCoordinator];
[manager persistentStoreCoordinator];
}
case UbiquityStoreManagerErrorCauseMigrateLocalToCloudStore: {
wrn(@"Couldn't migrate local store to the cloud: %@", error);
wrn(@"Resetting the iCloud store.");
[manager deleteCloudStore];
};
}
}

View File

@ -9,8 +9,6 @@
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
#import "MPConfig.h"
#import "MPElementEntity.h"
#import <Carbon/Carbon.h>
@ -29,15 +27,19 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfour-char-constants"
static EventHotKeyID MPShowHotKey = {.signature = 'show', .id = 1};
static EventHotKeyID MPLockHotKey = {.signature = 'lock', .id = 1};
#pragma clang diagnostic pop
+ (void)initialize {
[MPMacConfig get];
static dispatch_once_t initialize;
dispatch_once(&initialize, ^{
[MPMacConfig get];
#ifdef DEBUG
[PearlLogger get].printLevel = PearlLogLevelTrace;
#endif
#ifdef DEBUG
[PearlLogger get].printLevel = PearlLogLevelTrace;
#endif
});
}
+ (MPAppDelegate *)get {
@ -57,6 +59,10 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[((__bridge MPAppDelegate *)userData) activate:nil];
return noErr;
}
if (hotKeyID.signature == MPLockHotKey.signature && hotKeyID.id == MPLockHotKey.id) {
[((__bridge MPAppDelegate *)userData) signOut:nil];
return noErr;
}
return eventNotHandledErr;
}
@ -65,19 +71,21 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (idx > 1)
[[self.usersItem submenu] removeItemAtIndex:(NSInteger)idx];
[[self.usersItem submenu] removeItem:obj];
}];
NSManagedObjectContext *moc = [MPAppDelegate managedObjectContextIfReady];
if (!moc) {
[self.createUserItem setEnabled:NO];
self.createUserItem.title = @"New User (Not ready)";
self.createUserItem.enabled = NO;
self.createUserItem.toolTip = @"Please wait until the app is fully loaded.";
[[self.usersItem.submenu addItemWithTitle:@"Loading..." action:NULL keyEquivalent:@""] setEnabled:NO];
[self.usersItem.submenu addItemWithTitle:@"Loading..." action:NULL keyEquivalent:@""].enabled = NO;
return;
}
[self.createUserItem setEnabled:YES];
self.createUserItem.title = @"New User";
self.createUserItem.enabled = YES;
self.createUserItem.toolTip = nil;
[moc performBlockAndWait:^{
@ -88,6 +96,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
users = [moc executeFetchRequest:fetchRequest error:&error];
if (!users)
err(@"Failed to load users: %@", error);
if (![users count]) {
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
noUsersItem.enabled = NO;
noUsersItem.toolTip = @"Use the iOS app to create users and make sure iCloud is enabled in its preferences as well. "
@"Then give iCloud some time to sync the new user to your Mac.";
}
for (MPUserEntity *user in users) {
NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector(selectUser:) keyEquivalent:@""];
@ -110,18 +125,17 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)showMenu {
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.savePasswordItem.state = [MPAppDelegate get].activeUser.saveKey? NSOnState: NSOffState;
if (!(self.showItem.enabled = ![self.passwordWindow.window isVisible]))
self.showItem.toolTip = @"Master Password is already showing.";
else
self.showItem.toolTip = nil;
[self updateMenuItems];
[self.statusItem popUpStatusItemMenu:self.statusMenu];
}
- (IBAction)activate:(id)sender {
if (!self.activeUser)
// No user, can't activate.
return;
if ([[NSApplication sharedApplication] isActive])
[self applicationDidBecomeActive:nil];
else
@ -131,7 +145,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (IBAction)togglePreference:(NSMenuItem *)sender {
if (sender == useICloudItem)
[self.storeManager useiCloudStore:sender.state == NSOffState alertUser:YES];
self.storeManager.cloudEnabled = (sender.state == NSOnState);
if (sender == rememberPasswordItem)
[MPConfig get].rememberLogin = [NSNumber numberWithBool:![[MPConfig get].rememberLogin boolValue]];
if (sender == savePasswordItem) {
@ -165,34 +179,24 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
// Setup delegates and listeners.
[MPConfig get].delegate = self;
__weak id weakSelf = self;
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
if (self.key) {
[self.lockItem setEnabled:YES];
self.lockItem.toolTip = nil;
[self.savePasswordItem setEnabled:YES];
self.savePasswordItem.toolTip = nil;
} else {
[self.lockItem setEnabled:NO];
self.lockItem.toolTip = @"Master Password is currently locked.";
[self.savePasswordItem setEnabled:NO];
self.savePasswordItem.toolTip = @"First unlock by selecting your user and showing the Master Password window.";
[self.passwordWindow close];
}
[weakSelf updateMenuItems];
} forKeyPath:@"key" options:NSKeyValueObservingOptionInitial context:nil];
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
[weakSelf updateMenuItems];
} forKeyPath:@"activeUser" options:NSKeyValueObservingOptionInitial context:nil];
// Initially, use iCloud.
if ([[MPConfig get].firstRun boolValue])
[[self storeManager] useiCloudStore:YES alertUser:YES];
[self storeManager].cloudEnabled = YES;
// Status item.
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
self.statusItem.title = @"•••";
self.statusItem.highlightMode = YES;
self.statusItem.target = self;
self.statusItem.action = @selector(showMenu);
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
self.statusItem.image = [NSImage imageNamed:@"menu-icon"];
self.statusItem.highlightMode = YES;
self.statusItem.target = self;
self.statusItem.action = @selector(showMenu);
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
[[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
@ -204,14 +208,15 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[MPMacConfig get].usedUserName = self.activeUser.name;
} forKeyPath:@"activeUser" options:0 context:nil];
[[NSNotificationCenter defaultCenter] addObserverForName:PersistentStoreDidChange object:nil queue:nil usingBlock:
^(NSNotification *note) {
[self updateUsers];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:PersistentStoreDidMergeChanges object:nil queue:nil usingBlock:
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
^(NSNotification *note) {
[self updateUsers];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidImportChangesNotification object:nil queue:nil
usingBlock:
^(NSNotification *note) {
[self updateUsers];
}];
[self updateUsers];
// Global hotkey.
@ -224,7 +229,61 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
err(@"Error installing application event handler: %d", status);
status = RegisterEventHotKey(35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef);
if (status != noErr)
err(@"Error registering hotkey: %d", status);
err(@"Error registering 'show' hotkey: %d", status);
status = RegisterEventHotKey(35 /* p */, controlKey + optionKey + cmdKey, MPLockHotKey, GetApplicationEventTarget(), 0, &hotKeyRef);
if (status != noErr)
err(@"Error registering 'lock' hotkey: %d", status);
}
- (void)updateMenuItems {
if (!(self.showItem.enabled = ![self.passwordWindow.window isVisible])) {
self.showItem.title = @"Show (Showing)";
self.showItem.toolTip = @"Master Password is already showing.";
} else if (!(self.showItem.enabled = (self.activeUser != nil))) {
self.showItem.title = @"Show (No user)";
self.showItem.toolTip = @"First select the user to show passwords for.";
} else {
self.showItem.title = @"Show";
self.showItem.toolTip = nil;
}
if (self.key) {
self.lockItem.title = @"Lock";
self.lockItem.enabled = YES;
self.lockItem.toolTip = nil;
} else {
self.lockItem.title = @"Lock (Locked)";
self.lockItem.enabled = NO;
self.lockItem.toolTip = @"Master Password is currently locked.";
}
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.savePasswordItem.state = [MPAppDelegate get].activeUser.saveKey? NSOnState: NSOffState;
if (!self.activeUser) {
self.savePasswordItem.title = @"Save Password (No user)";
self.savePasswordItem.enabled = NO;
self.savePasswordItem.toolTip = @"First select your user and unlock by showing the Master Password window.";
} else if (!self.key) {
self.savePasswordItem.title = @"Save Password (Locked)";
self.savePasswordItem.enabled = NO;
self.savePasswordItem.toolTip = @"First unlock by showing the Master Password window.";
} else {
self.savePasswordItem.title = @"Save Password";
self.savePasswordItem.enabled = YES;
self.savePasswordItem.toolTip = nil;
}
self.useICloudItem.state = [[MPMacConfig get].iCloud boolValue]? NSOnState: NSOffState;
if (!(self.useICloudItem.enabled = ![[MPMacConfig get].iCloud boolValue])) {
self.useICloudItem.title = @"Use iCloud (Required)";
self.useICloudItem.toolTip = @"iCloud is required in this version. Future versions will work without iCloud as well.";
}
else {
self.useICloudItem.title = @"Use iCloud (Required)";
self.useICloudItem.toolTip = nil;
}
}
- (void)applicationWillBecomeActive:(NSNotification *)notification {
@ -295,12 +354,8 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[super ubiquityStoreManager:manager didSwitchToiCloud:iCloudEnabled];
self.useICloudItem.state = iCloudEnabled? NSOnState: NSOffState;
if (!(self.useICloudItem.enabled = !iCloudEnabled))
self.useICloudItem.toolTip = @"iCloud is required in this version. Future versions will work without iCloud as well.";
else
self.useICloudItem.toolTip = nil;
[self updateMenuItems];
if (![[MPConfig get].iCloudDecided boolValue]) {
if (iCloudEnabled)
return;
@ -310,7 +365,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
informativeTextWithFormat:@"It is highly recommended you enable iCloud."] runModal]) {
case NSAlertDefaultReturn: {
[MPConfig get].iCloudDecided = @YES;
[manager useiCloudStore:YES alertUser:NO];
manager.cloudEnabled = YES;
break;
}

View File

@ -20,6 +20,7 @@
@property (nonatomic, weak) IBOutlet NSTextField *tipField;
@property (nonatomic, weak) IBOutlet NSView *contentContainer;
@property (nonatomic, weak) IBOutlet NSProgressIndicator *progressView;
@property (nonatomic, weak) IBOutlet NSTextField *userLabel;
- (void)unlock;

View File

@ -10,8 +10,6 @@
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
#import "MPElementEntity.h"
#import "MPElementGeneratedEntity.h"
@interface MPPasswordWindowController ()
@ -35,6 +33,19 @@
[self setContent:@""];
[self.tipField setStringValue:@""];
[[MPAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
[self.userLabel setStringValue:PearlString(@"%@'s password for:", [MPAppDelegate get].activeUser.name)];
} forKeyPath:@"activeUser" options:NSKeyValueObservingOptionInitial context:nil];
[[MPAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
if ([MPAppDelegate get].activeUser && [MPAppDelegate get].key)
[MPAlgorithmDefault migrateUser:[MPAppDelegate get].activeUser completion:^(BOOL userRequiresNewMigration) {
if (userRequiresNewMigration)
[NSAlert alertWithMessageText:@"Migration Needed" defaultButton:@"OK" alternateButton:nil otherButton:nil
informativeTextWithFormat:@"Certain sites require explicit migration to get updated to the latest version of the "
@"Master Password algorithm. For these sites, a migration button will appear. Migrating these sites will cause "
@"their passwords to change. You'll need to update your profile for that site with the new password."];
}];
} forKeyPath:@"key" options:NSKeyValueObservingOptionInitial context:nil];
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window queue:nil
usingBlock:^(NSNotification *note) {
if (!self.inProgress)
@ -57,6 +68,10 @@
if (shouldComplete)
[[[note userInfo] objectForKey:@"NSFieldEditor"] complete:nil];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:MPNotificationSignedOut object:nil queue:nil
usingBlock:^(NSNotification *note) {
[self.window close];
}];
[super windowDidLoad];
}
@ -98,8 +113,11 @@
@"If you log in with a different master password, your current sites will be unavailable.\n\n"
@"You can always change back to your current master password later.\n"
@"Your current sites and passwords will then become available again."] runModal]
== 1)
== 1) {
[MPAppDelegate get].activeUser.keyID = nil;
[[MPAppDelegate get] forgetSavedKeyFor:[MPAppDelegate get].activeUser];
[[MPAppDelegate get] signOutAnimated:YES];
}
break;
case NSAlertOtherReturn:
@ -126,6 +144,9 @@
});
});
}
default:
break;
}
}
@ -164,8 +185,8 @@
[[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
if ([[NSPasteboard generalPasteboard] setString:self.content forType:NSPasteboardTypeString]) {
self.tipField.alphaValue = 1;
[self.tipField setStringValue:@"Copied!"];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0f * NSEC_PER_SEC));
[self.tipField setStringValue:@"Copied! Hit ⎋ (ESC) to close window."];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.2f];
@ -197,18 +218,12 @@
_content = content;
NSShadow *shadow = [NSShadow new];
shadow.shadowColor = [NSColor colorWithDeviceWhite:0.0f alpha:0.6f];
shadow.shadowOffset = NSMakeSize(1.0f, -1.0f);
shadow.shadowBlurRadius = 1.2f;
NSMutableParagraphStyle *paragraph = [NSMutableParagraphStyle new];
paragraph.alignment = NSCenterTextAlignment;
[self.contentField setAttributedStringValue:
[[NSAttributedString alloc] initWithString:_content
attributes:[[NSMutableDictionary alloc] initWithObjectsAndKeys:
shadow, NSShadowAttributeName,
paragraph, NSParagraphStyleAttributeName,
nil]]];
}
@ -222,6 +237,8 @@
return NO;
}
dbg(@"element:\n%@", [result debugDescription]);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSString *description = [result.content description];
if (!description)
@ -229,7 +246,7 @@
dispatch_async(dispatch_get_main_queue(), ^{
[self setContent:description];
[self.tipField setStringValue:@"Hit enter to copy the password."];
[self.tipField setStringValue:@"Hit ⌤ (ENTER) to copy the password."];
self.tipField.alphaValue = 1;
});
});

View File

@ -40,7 +40,7 @@
<object class="NSWindowTemplate" id="45434518">
<int key="NSWindowStyleMask">287</int>
<int key="NSWindowBacking">2</int>
<string key="NSWindowRect">{{600, 530}, {480, 159}}</string>
<string key="NSWindowRect">{{600, 530}, {480, 200}}</string>
<int key="NSWTFlags">611845120</int>
<string key="NSWindowTitle">Master Password</string>
<string key="NSWindowClass">NSPanel</string>
@ -56,24 +56,25 @@
<reference key="NSNextResponder" ref="258451033"/>
<int key="NSvFlags">268</int>
<array class="NSMutableArray" key="NSSubviews">
<object class="NSTextField" id="49669222">
<object class="NSTextField" id="642967193">
<reference key="NSNextResponder" ref="1072816887"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{17, 20}, {446, 17}}</string>
<string key="NSFrame">{{131, 163}, {219, 17}}</string>
<reference key="NSSuperview" ref="1072816887"/>
<string key="NSReuseIdentifierKey">_NS:1505</string>
<reference key="NSNextKeyView" ref="402376051"/>
<string key="NSReuseIdentifierKey">_NS:1535</string>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="249851874">
<object class="NSTextFieldCell" key="NSCell" id="406294418">
<int key="NSCellFlags">68157504</int>
<int key="NSCellFlags2">138413056</int>
<string key="NSContents">Hit enter to copy the password.</string>
<int key="NSCellFlags2">272630784</int>
<string key="NSContents">Maarten Billemont's password for:</string>
<object class="NSFont" key="NSSupport" id="590895625">
<string key="NSName">LucidaGrande</string>
<double key="NSSize">13</double>
<int key="NSfFlags">1044</int>
</object>
<string key="NSCellIdentifier">_NS:1505</string>
<reference key="NSControlView" ref="49669222"/>
<string key="NSCellIdentifier">_NS:1535</string>
<reference key="NSControlView" ref="642967193"/>
<object class="NSColor" key="NSBackgroundColor" id="245864165">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
@ -83,6 +84,34 @@
<bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
</object>
</object>
<object class="NSColor" key="NSTextColor">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">controlTextColor</string>
<object class="NSColor" key="NSColor" id="714751679">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MAA</bytes>
</object>
</object>
</object>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
</object>
<object class="NSTextField" id="49669222">
<reference key="NSNextResponder" ref="1072816887"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{17, 20}, {446, 17}}</string>
<reference key="NSSuperview" ref="1072816887"/>
<reference key="NSNextKeyView" ref="104294954"/>
<string key="NSReuseIdentifierKey">_NS:1505</string>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="249851874">
<int key="NSCellFlags">68157504</int>
<int key="NSCellFlags2">138413056</int>
<string key="NSContents">Hit enter to copy the password.</string>
<reference key="NSSupport" ref="590895625"/>
<string key="NSCellIdentifier">_NS:1505</string>
<reference key="NSControlView" ref="49669222"/>
<reference key="NSBackgroundColor" ref="245864165"/>
<object class="NSColor" key="NSTextColor">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
@ -98,7 +127,7 @@
<object class="NSTextField" id="402376051">
<reference key="NSNextResponder" ref="1072816887"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{140, 117}, {200, 22}}</string>
<string key="NSFrame">{{140, 133}, {200, 22}}</string>
<reference key="NSSuperview" ref="1072816887"/>
<reference key="NSNextKeyView" ref="139778114"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
@ -106,7 +135,7 @@
<object class="NSTextFieldCell" key="NSCell" id="961966865">
<int key="NSCellFlags">-1804599231</int>
<int key="NSCellFlags2">138413120</int>
<string key="NSContents"/>
<string key="NSContents">apple.com</string>
<reference key="NSSupport" ref="590895625"/>
<string key="NSPlaceholderString">Site name</string>
<string key="NSCellIdentifier">_NS:9</string>
@ -122,10 +151,7 @@
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">textColor</string>
<object class="NSColor" key="NSColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MAA</bytes>
</object>
<reference key="NSColor" ref="714751679"/>
</object>
</object>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
@ -133,9 +159,18 @@
<object class="NSTextField" id="139778114">
<reference key="NSNextResponder" ref="1072816887"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{17, 45}, {446, 64}}</string>
<string key="NSFrame">{{17, 61}, {446, 64}}</string>
<reference key="NSSuperview" ref="1072816887"/>
<reference key="NSNextKeyView" ref="49669222"/>
<object class="NSShadow" key="NSViewShadow">
<double key="NSShadowHoriz">1</double>
<double key="NSShadowVert">1</double>
<double key="NSShadowBlurRadius">1</double>
<object class="NSColor" key="NSShadowColor" id="444840817">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MCAwLjYAA</bytes>
</object>
</object>
<string key="NSReuseIdentifierKey">_NS:9</string>
<string key="NSAntiCompressionPriority">{250, 750}</string>
<bool key="NSEnabled">YES</bool>
@ -160,24 +195,26 @@
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
</object>
</array>
<string key="NSFrameSize">{480, 159}</string>
<string key="NSFrameSize">{480, 200}</string>
<reference key="NSSuperview" ref="258451033"/>
<reference key="NSNextKeyView" ref="642967193"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<string key="NSClassName">NSView</string>
</object>
<object class="NSProgressIndicator" id="104294954">
<reference key="NSNextResponder" ref="258451033"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{224, 63}, {32, 32}}</string>
<string key="NSFrame">{{224, 84}, {32, 32}}</string>
<reference key="NSSuperview" ref="258451033"/>
<reference key="NSNextKeyView"/>
<string key="NSReuseIdentifierKey">_NS:945</string>
<int key="NSpiFlags">28682</int>
<double key="NSMaxValue">100</double>
</object>
</array>
<string key="NSFrameSize">{480, 159}</string>
<string key="NSFrameSize">{480, 200}</string>
<reference key="NSSuperview"/>
<reference key="NSNextKeyView"/>
<reference key="NSNextKeyView" ref="1072816887"/>
<bool key="NSViewIsLayerTreeHost">YES</bool>
<string key="NSReuseIdentifierKey">_NS:21</string>
</object>
@ -213,6 +250,38 @@
</object>
<int key="connectionID">215</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">userLabel</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="642967193"/>
</object>
<int key="connectionID">223</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">siteField</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="402376051"/>
</object>
<int key="connectionID">224</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">contentField</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="139778114"/>
</object>
<int key="connectionID">225</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">tipField</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="49669222"/>
</object>
<int key="connectionID">226</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">delegate</string>
@ -379,38 +448,6 @@
<int key="objectID">143</int>
<reference key="object" ref="1072816887"/>
<array class="NSMutableArray" key="children">
<object class="IBNSLayoutConstraint" id="645313537">
<reference key="firstItem" ref="402376051"/>
<int key="firstAttribute">3</int>
<int key="relation">0</int>
<reference key="secondItem" ref="1072816887"/>
<int key="secondAttribute">3</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="1072816887"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="1033518145">
<reference key="firstItem" ref="402376051"/>
<int key="firstAttribute">9</int>
<int key="relation">0</int>
<reference key="secondItem" ref="139778114"/>
<int key="secondAttribute">9</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">0.0</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="1072816887"/>
<int key="scoringType">6</int>
<float key="scoringTypeFloat">24</float>
<int key="contentType">2</int>
</object>
<object class="IBNSLayoutConstraint" id="314583816">
<reference key="firstItem" ref="1072816887"/>
<int key="firstAttribute">4</int>
@ -459,7 +496,71 @@
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="310034208">
<reference key="firstItem" ref="642967193"/>
<int key="firstAttribute">9</int>
<int key="relation">0</int>
<reference key="secondItem" ref="402376051"/>
<int key="secondAttribute">9</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">0.0</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="1072816887"/>
<int key="scoringType">6</int>
<float key="scoringTypeFloat">24</float>
<int key="contentType">2</int>
</object>
<object class="IBNSLayoutConstraint" id="645313537">
<reference key="firstItem" ref="642967193"/>
<int key="firstAttribute">3</int>
<int key="relation">0</int>
<reference key="secondItem" ref="1072816887"/>
<int key="secondAttribute">3</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">20</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="1072816887"/>
<int key="scoringType">8</int>
<float key="scoringTypeFloat">29</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="884917592">
<reference key="firstItem" ref="402376051"/>
<int key="firstAttribute">3</int>
<int key="relation">0</int>
<reference key="secondItem" ref="642967193"/>
<int key="secondAttribute">4</int>
<float key="multiplier">1</float>
<object class="IBNSLayoutSymbolicConstant" key="constant">
<double key="value">8</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="1072816887"/>
<int key="scoringType">6</int>
<float key="scoringTypeFloat">24</float>
<int key="contentType">3</int>
</object>
<object class="IBNSLayoutConstraint" id="1033518145">
<reference key="firstItem" ref="402376051"/>
<int key="firstAttribute">9</int>
<int key="relation">0</int>
<reference key="secondItem" ref="139778114"/>
<int key="secondAttribute">9</int>
<float key="multiplier">1</float>
<object class="IBLayoutConstant" key="constant">
<double key="value">0.0</double>
</object>
<float key="priority">1000</float>
<reference key="containingView" ref="1072816887"/>
<int key="scoringType">6</int>
<float key="scoringTypeFloat">24</float>
<int key="contentType">2</int>
</object>
<object class="IBNSLayoutConstraint" id="566883659">
<reference key="firstItem" ref="139778114"/>
<int key="firstAttribute">3</int>
<int key="relation">0</int>
@ -508,8 +609,9 @@
<int key="contentType">3</int>
</object>
<reference ref="49669222"/>
<reference ref="402376051"/>
<reference ref="642967193"/>
<reference ref="139778114"/>
<reference ref="402376051"/>
</array>
<reference key="parent" ref="258451033"/>
</object>
@ -629,20 +731,43 @@
<reference key="parent" ref="1072816887"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">210</int>
<reference key="object" ref="1033518145"/>
<int key="objectID">216</int>
<reference key="object" ref="642967193"/>
<array class="NSMutableArray" key="children">
<reference ref="406294418"/>
</array>
<reference key="parent" ref="1072816887"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">212</int>
<int key="objectID">217</int>
<reference key="object" ref="406294418"/>
<reference key="parent" ref="642967193"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">218</int>
<reference key="object" ref="645313537"/>
<reference key="parent" ref="1072816887"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">213</int>
<int key="objectID">219</int>
<reference key="object" ref="310034208"/>
<reference key="parent" ref="1072816887"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">221</int>
<reference key="object" ref="884917592"/>
<reference key="parent" ref="1072816887"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">222</int>
<reference key="object" ref="566883659"/>
<reference key="parent" ref="1072816887"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">210</int>
<reference key="object" ref="1033518145"/>
<reference key="parent" ref="1072816887"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
@ -652,12 +777,14 @@
<array class="NSMutableArray" key="143.IBNSViewMetadataConstraints">
<reference ref="865006730"/>
<reference ref="831384658"/>
<reference ref="566883659"/>
<reference ref="1033518145"/>
<reference ref="884917592"/>
<reference ref="645313537"/>
<reference ref="310034208"/>
<reference ref="63384401"/>
<reference ref="602857839"/>
<reference ref="314583816"/>
<reference ref="1033518145"/>
<reference ref="645313537"/>
</array>
<boolean value="NO" key="143.IBNSViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="143.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
@ -667,11 +794,19 @@
<string key="150.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="NO" key="181.IBNSViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="181.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<real value="1" key="181.IBViewIntegration.shadowBlurRadius"/>
<reference key="181.IBViewIntegration.shadowColor" ref="444840817"/>
<real value="1" key="181.IBViewIntegration.shadowOffsetHeight"/>
<real value="1" key="181.IBViewIntegration.shadowOffsetWidth"/>
<array class="NSMutableArray" key="182.IBNSViewMetadataConstraints">
<reference ref="102475933"/>
</array>
<boolean value="NO" key="182.IBNSViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="182.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<real value="0.0" key="182.IBViewIntegration.shadowBlurRadius"/>
<reference key="182.IBViewIntegration.shadowColor" ref="714751679"/>
<real value="0.0" key="182.IBViewIntegration.shadowOffsetHeight"/>
<real value="0.0" key="182.IBViewIntegration.shadowOffsetWidth"/>
<boolean value="NO" key="183.IBNSViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="183.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="184.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
@ -686,12 +821,17 @@
<string key="208.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="209.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="210.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="212.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="213.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="NO" key="216.IBNSViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="216.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="217.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="218.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="219.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES" key="22.IBNSWindowAutoPositionCentersHorizontal"/>
<boolean value="YES" key="22.IBNSWindowAutoPositionCentersVertical"/>
<string key="22.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="NO" key="22.NSWindowTemplate.visibleAtLaunch"/>
<string key="221.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="222.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<array key="23.IBNSViewMetadataConstraints">
<reference ref="216428540"/>
<reference ref="265452638"/>
@ -708,7 +848,7 @@
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">215</int>
<int key="maxID">230</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>

View File

@ -47,27 +47,19 @@
<object class="NSMenu" id="764588027">
<string key="NSTitle"/>
<array class="NSMutableArray" key="NSMenuItems">
<object class="NSMenuItem" id="846612332">
<reference key="NSMenu" ref="764588027"/>
<string key="NSTitle">Show</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<object class="NSCustomResource" key="NSOnImage" id="552246001">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSMenuCheckmark</string>
</object>
<object class="NSCustomResource" key="NSMixedImage" id="752047669">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSMenuMixedState</string>
</object>
</object>
<object class="NSMenuItem" id="11982480">
<reference key="NSMenu" ref="764588027"/>
<string key="NSTitle">Users</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<object class="NSCustomResource" key="NSOnImage" id="269450960">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSMenuCheckmark</string>
</object>
<object class="NSCustomResource" key="NSMixedImage" id="977440657">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSMenuMixedState</string>
</object>
<string key="NSAction">submenuAction:</string>
<object class="NSMenu" key="NSSubmenu" id="934187555">
<string key="NSTitle">Users</string>
@ -78,8 +70,8 @@
<string key="NSTitle">New User</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
</object>
<object class="NSMenuItem" id="925131766">
<reference key="NSMenu" ref="934187555"/>
@ -88,8 +80,8 @@
<string key="NSTitle"/>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
</object>
</array>
</object>
@ -99,8 +91,8 @@
<string key="NSTitle">Preferences</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
<string key="NSAction">submenuAction:</string>
<object class="NSMenu" key="NSSubmenu" id="800575174">
<string key="NSTitle">Preferences</string>
@ -110,8 +102,8 @@
<string key="NSTitle">Use iCloud</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
</object>
<object class="NSMenuItem" id="461686112">
<reference key="NSMenu" ref="800575174"/>
@ -119,8 +111,8 @@
<string key="NSTitle">Synchronize available sites from your iCloud account.</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
<object class="NSAttributedString" key="NSAttributedTitle">
<string key="NSString">Synchronize available sites from your iCloud account.</string>
<dictionary key="NSAttributes" id="583461090">
@ -141,8 +133,8 @@
<string key="NSTitle">Remember Password</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
</object>
<object class="NSMenuItem" id="907921953">
<reference key="NSMenu" ref="800575174"/>
@ -150,8 +142,8 @@
<string key="NSTitle">Remember the password while the application is running.</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
<object class="NSAttributedString" key="NSAttributedTitle">
<string key="NSString">Remember the password while the application is running.</string>
<reference key="NSAttributes" ref="583461090"/>
@ -162,8 +154,8 @@
<string key="NSTitle">Save Password</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
</object>
<object class="NSMenuItem" id="123831322">
<reference key="NSMenu" ref="800575174"/>
@ -171,8 +163,8 @@
<string key="NSTitle">Save the password in your keychain so you don't need to enter it again.</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
<object class="NSAttributedString" key="NSAttributedTitle">
<string key="NSString">Save the password in your keychain so you don't need to enter it again.</string>
<reference key="NSAttributes" ref="583461090"/>
@ -189,25 +181,35 @@
<string key="NSTitle"/>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
</object>
<object class="NSMenuItem" id="846612332">
<reference key="NSMenu" ref="764588027"/>
<string key="NSTitle">Show</string>
<string key="NSKeyEquiv">p</string>
<int key="NSKeyEquivModMask">1310720</int>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
</object>
<object class="NSMenuItem" id="229948989">
<reference key="NSMenu" ref="764588027"/>
<bool key="NSIsDisabled">YES</bool>
<string key="NSTitle">Lock</string>
<string key="NSKeyEquiv"/>
<string key="NSKeyEquiv">p</string>
<int key="NSKeyEquivModMask">1835008</int>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
</object>
<object class="NSMenuItem" id="291035877">
<reference key="NSMenu" ref="764588027"/>
<string key="NSTitle">Quit</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="552246001"/>
<reference key="NSMixedImage" ref="752047669"/>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
</object>
</array>
<bool key="NSNoAutoenable">YES</bool>
@ -392,9 +394,9 @@
<array class="NSMutableArray" key="children">
<reference ref="291035877"/>
<reference ref="466252869"/>
<reference ref="846612332"/>
<reference ref="229948989"/>
<reference ref="851296005"/>
<reference ref="846612332"/>
<reference ref="11982480"/>
</array>
<reference key="parent" ref="0"/>

View File

@ -178,30 +178,11 @@
}];
if ([MPAppDelegate get].activeUser)
[[MPAppDelegate get].managedObjectContextIfReady performBlock:^void() {
NSError *error = nil;
NSFetchRequest *migrationRequest = [NSFetchRequest
fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])];
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d", MPAlgorithmDefaultVersion];
NSArray *migrationElements = [[MPAppDelegate get].managedObjectContextIfReady executeFetchRequest:migrationRequest
error:&error];
if (!migrationElements) {
err(@"While looking for elements to migrate: %@", error);
return;
}
BOOL didRequireExplicitMigration = [MPAppDelegate_Shared get].activeUser.requiresExplicitMigration;
if (didRequireExplicitMigration)
[MPAppDelegate_Shared get].activeUser.requiresExplicitMigration = NO;
for (MPElementEntity *migrationElement in migrationElements)
if (![migrationElement migrateExplicitly:NO])
[MPAppDelegate_Shared get].activeUser.requiresExplicitMigration = YES;
if (!didRequireExplicitMigration && [MPAppDelegate_Shared get].activeUser.requiresExplicitMigration)
[MPAlgorithmDefault migrateUser:[MPAppDelegate get].activeUser completion:^(BOOL userRequiresNewMigration) {
if (userRequiresNewMigration)
[UIView animateWithDuration:0.3f animations:^{
self.outdatedAlertContainer.alpha = 1;
}];
}];
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Main"];
@ -794,6 +775,7 @@
- (void)didSelectElement:(MPElementEntity *)element {
inf(@"Selected: %@", element.name);
dbg(@"Element:\n%@", [element debugDescription]);
[self closeAlert];