A few fixes to site import/export.
[FIXED] A few core data issues. [ADDED] Avatar to export file. [ADDED] Generated site counter to export file. [ADDED] Site login name to export file.
This commit is contained in:
parent
a4fe13842a
commit
c57bd5d5d3
2
External/UbiquityStoreManager
vendored
2
External/UbiquityStoreManager
vendored
@ -1 +1 @@
|
||||
Subproject commit 5e38f25f6e058cc52b8e41bcdc183b7966bb3ac7
|
||||
Subproject commit 99dcbccee742d2bd2e5701f6b4e001138956030a
|
@ -532,7 +532,8 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
saveInContext:(NSManagedObjectContext *)context {
|
||||
|
||||
// Compile patterns.
|
||||
static NSRegularExpression *headerPattern, *sitePattern;
|
||||
static NSRegularExpression *headerPattern;
|
||||
static NSArray *sitePatterns;
|
||||
NSError *error = nil;
|
||||
if (!headerPattern) {
|
||||
headerPattern = [[NSRegularExpression alloc]
|
||||
@ -543,12 +544,17 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
if (!sitePattern) {
|
||||
sitePattern = [[NSRegularExpression alloc]
|
||||
initWithPattern:@"^([^[:space:]]+)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)(:[[:digit:]]+)?[[:space:]]+([^\t]+)\t(.*)"
|
||||
options:(NSRegularExpressionOptions)0 error:&error];
|
||||
if (!sitePatterns) {
|
||||
sitePatterns = @[
|
||||
[[NSRegularExpression alloc] // Format 0
|
||||
initWithPattern:@"^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)? +([^\t]+)\t(.*)"
|
||||
options:(NSRegularExpressionOptions)0 error:&error],
|
||||
[[NSRegularExpression alloc] // Format 1
|
||||
initWithPattern:@"^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)?(:[[:digit:]]+)? +([^\t]*)\t *([^\t]+)\t(.*)"
|
||||
options:(NSRegularExpressionOptions)0 error:&error]
|
||||
];
|
||||
if (error) {
|
||||
err( @"Error loading the site pattern: %@", error );
|
||||
err( @"Error loading the site patterns: %@", error );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
@ -557,6 +563,8 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
inf( @"Importing sites." );
|
||||
__block MPUserEntity *user = nil;
|
||||
id<MPAlgorithm> importAlgorithm = nil;
|
||||
NSUInteger importFormat = 0;
|
||||
NSUInteger importAvatar = NSNotFound;
|
||||
NSString *importBundleVersion = nil, *importUserName = nil;
|
||||
NSData *importKeyID = nil;
|
||||
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
||||
@ -604,8 +612,8 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
|
||||
user = [users count]? [users lastObject]: nil;
|
||||
dbg( @"Found user: %@", [user debugDescription] );
|
||||
user = [users lastObject];
|
||||
dbg( @"Existing user? %@", [user debugDescription] );
|
||||
}
|
||||
if ([headerName isEqualToString:@"Key ID"])
|
||||
importKeyID = [headerValue decodeHex];
|
||||
@ -613,6 +621,15 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
importBundleVersion = headerValue;
|
||||
importAlgorithm = MPAlgorithmDefaultForBundleVersion( importBundleVersion );
|
||||
}
|
||||
if ([headerName isEqualToString:@"Format"]) {
|
||||
importFormat = [headerValue integerValue];
|
||||
if (importFormat >= [sitePatterns count]) {
|
||||
err( @"Unsupported import format: %d", importFormat );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
if ([headerName isEqualToString:@"Avatar"])
|
||||
importAvatar = [headerValue integerValue];
|
||||
if ([headerName isEqualToString:@"Passwords"]) {
|
||||
if ([headerValue isEqualToString:@"VISIBLE"])
|
||||
clearText = YES;
|
||||
@ -628,6 +645,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
continue;
|
||||
|
||||
// Site
|
||||
NSRegularExpression *sitePattern = sitePatterns[importFormat];
|
||||
if ([sitePattern numberOfMatchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] != 1) {
|
||||
err( @"Invalid site format in line: %@", importedSiteLine );
|
||||
@ -635,21 +653,45 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
}
|
||||
NSTextCheckingResult *siteElements = [[sitePattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
|
||||
NSString *lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
|
||||
NSString *uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
|
||||
NSString *type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]];
|
||||
NSString *version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]];
|
||||
NSString *lastUsed, *uses, *type, *version, *counter, *siteName, *loginName, *exportContent;
|
||||
switch (importFormat) {
|
||||
case 0:
|
||||
lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
|
||||
uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
|
||||
type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]];
|
||||
version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]];
|
||||
if ([version length])
|
||||
version = [version substringFromIndex:1]; // Strip the leading colon.
|
||||
NSString *name = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
|
||||
NSString *exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]];
|
||||
counter = @"";
|
||||
loginName = @"";
|
||||
siteName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
|
||||
exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]];
|
||||
break;
|
||||
case 1:
|
||||
lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
|
||||
uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
|
||||
type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]];
|
||||
version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]];
|
||||
if ([version length])
|
||||
version = [version substringFromIndex:1]; // Strip the leading colon.
|
||||
counter = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
|
||||
if ([counter length])
|
||||
counter = [counter substringFromIndex:1]; // Strip the leading colon.
|
||||
loginName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]];
|
||||
siteName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:7]];
|
||||
exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:8]];
|
||||
break;
|
||||
default:
|
||||
err( @"Unexpected import format: %d", importFormat );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
|
||||
// Find existing site.
|
||||
if (user) {
|
||||
elementFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", name, user];
|
||||
elementFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
|
||||
NSArray *existingSites = [context executeFetchRequest:elementFetchRequest error:&error];
|
||||
if (!existingSites) {
|
||||
err( @"Lookup of existing sites failed for site: %@, user: %@, error: %@", name, user.userID, error );
|
||||
err( @"Lookup of existing sites failed for site: %@, user: %@, error: %@", siteName, user.userID, error );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
if ([existingSites count]) {
|
||||
@ -657,9 +699,9 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
[elementsToDelete addObjectsFromArray:existingSites];
|
||||
}
|
||||
}
|
||||
[importedSiteElements addObject:@[ lastUsed, uses, type, version, name, exportContent ]];
|
||||
dbg( @"Will import site: lastUsed=%@, uses=%@, type=%@, version=%@, name=%@, exportContent=%@",
|
||||
lastUsed, uses, type, version, name, exportContent );
|
||||
[importedSiteElements addObject:@[ lastUsed, uses, type, version, counter, loginName, siteName, exportContent ]];
|
||||
dbg( @"Will import site: lastUsed=%@, uses=%@, type=%@, version=%@, counter=%@, loginName=%@, siteName=%@, exportContent=%@",
|
||||
lastUsed, uses, type, version, counter, loginName, siteName, exportContent );
|
||||
}
|
||||
|
||||
// Ask for confirmation to import these sites and the master password of the user.
|
||||
@ -689,10 +731,16 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
}];
|
||||
|
||||
// Make sure there is a user.
|
||||
if (!user) {
|
||||
if (user) {
|
||||
if (importAvatar != NSNotFound)
|
||||
user.avatar = importAvatar;
|
||||
dbg( @"Updating User: %@", [user debugDescription] );
|
||||
} else {
|
||||
user = [MPUserEntity insertNewObjectInContext:context];
|
||||
user.name = importUserName;
|
||||
user.keyID = importKeyID;
|
||||
if (importAvatar != NSNotFound)
|
||||
user.avatar = importAvatar;
|
||||
dbg( @"Created User: %@", [user debugDescription] );
|
||||
}
|
||||
|
||||
@ -702,13 +750,16 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
NSUInteger uses = (unsigned)[siteElements[1] integerValue];
|
||||
MPElementType type = (MPElementType)[siteElements[2] integerValue];
|
||||
NSUInteger version = (unsigned)[siteElements[3] integerValue];
|
||||
NSString *name = siteElements[4];
|
||||
NSString *exportContent = siteElements[5];
|
||||
NSUInteger counter = [siteElements[4] length]? (unsigned)[siteElements[4] integerValue]: NSNotFound;
|
||||
NSString *loginName = [siteElements[5] length]? siteElements[5]: nil;
|
||||
NSString *siteName = siteElements[6];
|
||||
NSString *exportContent = siteElements[7];
|
||||
|
||||
// Create new site.
|
||||
NSString *typeEntityName = [MPAlgorithmForVersion( version ) classNameOfType:type];
|
||||
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
|
||||
element.name = name;
|
||||
element.name = siteName;
|
||||
element.loginName = loginName;
|
||||
element.user = user;
|
||||
element.type = type;
|
||||
element.uses = uses;
|
||||
@ -720,6 +771,8 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
else
|
||||
[element.algorithm importProtectedContent:exportContent protectedByKey:importKey intoElement:element usingKey:userKey];
|
||||
}
|
||||
if ([element isKindOfClass:[MPElementGeneratedEntity class]] && counter != NSNotFound)
|
||||
((MPElementGeneratedEntity *)element).counter = counter;
|
||||
|
||||
dbg( @"Created Element: %@", [element debugDescription] );
|
||||
}
|
||||
@ -751,18 +804,20 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
[export appendFormat:@"# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n"];
|
||||
[export appendFormat:@"# \n"];
|
||||
[export appendFormat:@"##\n"];
|
||||
[export appendFormat:@"# Version: %@\n", [PearlInfoPlist get].CFBundleVersion];
|
||||
[export appendFormat:@"# User Name: %@\n", activeUser.name];
|
||||
[export appendFormat:@"# Avatar: %d\n", activeUser.avatar];
|
||||
[export appendFormat:@"# Key ID: %@\n", [activeUser.keyID encodeHex]];
|
||||
[export appendFormat:@"# Date: %@\n", [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]]];
|
||||
[export appendFormat:@"# Version: %@\n", [PearlInfoPlist get].CFBundleVersion];
|
||||
[export appendFormat:@"# Format: 1\n"];
|
||||
if (revealPasswords)
|
||||
[export appendFormat:@"# Passwords: VISIBLE\n"];
|
||||
else
|
||||
[export appendFormat:@"# Passwords: PROTECTED\n"];
|
||||
[export appendFormat:@"##\n"];
|
||||
[export appendFormat:@"#\n"];
|
||||
[export appendFormat:@"# Last Times Password Site\tSite\n"];
|
||||
[export appendFormat:@"# used used type name\tpassword\n"];
|
||||
[export appendFormat:@"# Last Times Password Login\t Site\tSite\n"];
|
||||
[export appendFormat:@"# used used type name\t name\tpassword\n"];
|
||||
|
||||
// Sites.
|
||||
for (MPElementEntity *element in activeUser.elements) {
|
||||
@ -770,9 +825,16 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
NSUInteger uses = element.uses;
|
||||
MPElementType type = element.type;
|
||||
NSUInteger version = element.version;
|
||||
NSString *name = element.name;
|
||||
NSUInteger counter = 0;
|
||||
NSString *loginName = element.loginName;
|
||||
NSString *siteName = element.name;
|
||||
NSString *content = nil;
|
||||
|
||||
// Generated-specific
|
||||
if ([element isKindOfClass:[MPElementGeneratedEntity class]])
|
||||
counter = ((MPElementGeneratedEntity *)element).counter;
|
||||
|
||||
|
||||
// Determine the content to export.
|
||||
if (!(type & MPElementFeatureDevicePrivate)) {
|
||||
if (revealPasswords)
|
||||
@ -781,10 +843,10 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
||||
content = [element.algorithm exportContentForElement:element usingKey:self.key];
|
||||
}
|
||||
|
||||
[export appendFormat:@"%@ %8ld %8s %20s\t%@\n",
|
||||
[export appendFormat:@"%@ %8ld %8s %25s\t%25s\t%@\n",
|
||||
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed], (long)uses,
|
||||
[strf( @"%lu:%lu", (long)type, (unsigned long)version ) UTF8String], [name UTF8String], content
|
||||
? content: @""];
|
||||
[strf( @"%lu:%lu:%lu", (long)type, (long)version, (long)counter ) UTF8String],
|
||||
[(loginName?: @"") UTF8String], [siteName UTF8String], content?: @""];
|
||||
}
|
||||
|
||||
MPCheckpoint( MPCheckpointSitesExported, @{
|
||||
|
@ -20,7 +20,7 @@
|
||||
@try {
|
||||
NSError *error = nil;
|
||||
if (!(success = [self save:&error]))
|
||||
err( @"While saving: %@", error );
|
||||
err( @"While saving: %@", [error fullDescription] );
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
success = NO;
|
||||
|
@ -36,6 +36,5 @@
|
||||
- (void)updatePasswords;
|
||||
|
||||
- (IBAction)dismissPopdown:(id)sender;
|
||||
- (IBAction)signOut:(id)sender;
|
||||
|
||||
@end
|
||||
|
@ -32,7 +32,8 @@
|
||||
@end
|
||||
|
||||
@implementation MPPasswordsViewController {
|
||||
__weak id _storeObserver;
|
||||
__weak id _storeChangingObserver;
|
||||
__weak id _storeChangedObserver;
|
||||
__weak id _mocObserver;
|
||||
NSArray *_notificationObservers;
|
||||
__weak UITapGestureRecognizer *_passwordsDismissRecognizer;
|
||||
@ -275,7 +276,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
||||
}],
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:MPSignedOutNotification object:nil
|
||||
queue:nil usingBlock:^(NSNotification *note) {
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
Strongify( self );
|
||||
|
||||
_fetchedResultsController = nil;
|
||||
@ -293,8 +294,8 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
||||
}];
|
||||
}],
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:MPCheckConfigNotification object:nil queue:[NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *note) {
|
||||
addObserverForName:MPCheckConfigNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
[self updateConfigKey:note.object];
|
||||
}],
|
||||
];
|
||||
@ -314,17 +315,25 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
||||
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
if (!_mocObserver && mainContext)
|
||||
_mocObserver = [[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
// Strongify(self);
|
||||
// [self updatePasswords];
|
||||
addObserverForName:NSManagedObjectContextDidSaveNotification object:mainContext
|
||||
queue:nil usingBlock:^(NSNotification *note) {
|
||||
if (![[MPiOSAppDelegate get] activeUserInContext:mainContext])
|
||||
[[MPiOSAppDelegate get] signOutAnimated:YES];
|
||||
}];
|
||||
if (!_storeObserver)
|
||||
_storeObserver = [[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:USMStoreDidChangeNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
if (!_storeChangingObserver)
|
||||
_storeChangingObserver = [[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:USMStoreWillChangeNotification object:nil
|
||||
queue:nil usingBlock:^(NSNotification *note) {
|
||||
Strongify( self );
|
||||
_fetchedResultsController = nil;
|
||||
if (self->_mocObserver)
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self->_mocObserver];
|
||||
}];
|
||||
if (!_storeChangedObserver)
|
||||
_storeChangedObserver = [[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:USMStoreDidChangeNotification object:nil
|
||||
queue:nil usingBlock:^(NSNotification *note) {
|
||||
Strongify( self );
|
||||
self->_fetchedResultsController = nil;
|
||||
[self updatePasswords];
|
||||
}];
|
||||
}
|
||||
@ -333,8 +342,10 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
||||
|
||||
if (_mocObserver)
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:_mocObserver];
|
||||
if (_storeObserver)
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:_storeObserver];
|
||||
if (_storeChangingObserver)
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:_storeChangingObserver];
|
||||
if (_storeChangedObserver)
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:_storeChangedObserver];
|
||||
}
|
||||
|
||||
- (void)updateConfigKey:(NSString *)key {
|
||||
@ -350,8 +361,8 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
||||
NSString *query = self.query;
|
||||
NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID;
|
||||
if (!activeUserOID) {
|
||||
self.passwordsSearchBar.text = nil;
|
||||
PearlMainQueue( ^{
|
||||
self.passwordsSearchBar.text = nil;
|
||||
[self.passwordCollectionView reloadData];
|
||||
[self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top ) animated:YES];
|
||||
} );
|
||||
@ -444,9 +455,4 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
||||
self.popdownToTopConstraint.priority = UILayoutPriorityDefaultHigh;
|
||||
}
|
||||
|
||||
- (IBAction)signOut:(id)sender {
|
||||
|
||||
[[MPiOSAppDelegate get] signOutAnimated:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -50,7 +50,8 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
||||
@end
|
||||
|
||||
@implementation MPUsersViewController {
|
||||
__weak id _storeObserver;
|
||||
__weak id _storeChangingObserver;
|
||||
__weak id _storeChangedObserver;
|
||||
__weak id _mocObserver;
|
||||
NSArray *_notificationObservers;
|
||||
NSString *_masterPasswordChoice;
|
||||
@ -382,6 +383,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
||||
|
||||
[context deleteObject:user_];
|
||||
[context saveToStore];
|
||||
[self reloadUsers]; // I do NOT understand why our ObjectsDidChangeNotification isn't firing on saveToStore.
|
||||
}];
|
||||
return;
|
||||
}
|
||||
@ -623,7 +625,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
||||
if (!_mocObserver && mainContext)
|
||||
_mocObserver = [[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
queue:nil usingBlock:^(NSNotification *note) {
|
||||
Strongify(self);
|
||||
NSSet *insertedObjects = note.userInfo[NSInsertedObjectsKey];
|
||||
NSSet *deletedObjects = note.userInfo[NSDeletedObjectsKey];
|
||||
@ -633,10 +635,18 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
||||
}]] count])
|
||||
[self reloadUsers];
|
||||
}];
|
||||
if (!_storeObserver)
|
||||
_storeObserver = [[NSNotificationCenter defaultCenter]
|
||||
if (!_storeChangingObserver)
|
||||
_storeChangingObserver = [[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:USMStoreWillChangeNotification object:nil
|
||||
queue:nil usingBlock:^(NSNotification *note) {
|
||||
Strongify(self);
|
||||
if (self->_mocObserver)
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self->_mocObserver];
|
||||
}];
|
||||
if (!_storeChangedObserver)
|
||||
_storeChangedObserver = [[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:USMStoreDidChangeNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
queue:nil usingBlock:^(NSNotification *note) {
|
||||
Strongify(self);
|
||||
[self reloadUsers];
|
||||
}];
|
||||
@ -646,8 +656,10 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
||||
|
||||
if (_mocObserver)
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:_mocObserver];
|
||||
if (_storeObserver)
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:_storeObserver];
|
||||
if (_storeChangingObserver)
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:_storeChangingObserver];
|
||||
if (_storeChangedObserver)
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:_storeChangedObserver];
|
||||
}
|
||||
|
||||
- (void)reloadUsers {
|
||||
|
@ -597,9 +597,14 @@
|
||||
if (buttonIndex == [alert cancelButtonIndex])
|
||||
return;
|
||||
|
||||
[[self storeManager] migrateCloudToLocal];
|
||||
if (buttonIndex == [alert firstOtherButtonIndex])
|
||||
[UIApp openURL:[NSURL URLWithString:
|
||||
@"http://support.lyndir.com/topic/486731-why-doesnt-the-mac-version-have-icloud-support/#comment-755394"]];
|
||||
|
||||
if (buttonIndex == [alert firstOtherButtonIndex] + 1)
|
||||
[MPiOSConfig get].iCloudEnabled = @NO;
|
||||
}
|
||||
cancelTitle:@"Ignore For Now" otherTitles:@"Disable iCloud", nil];
|
||||
cancelTitle:@"Ignore For Now" otherTitles:@"Why?", @"Disable iCloud", nil];
|
||||
}
|
||||
|
||||
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager failedLoadingStoreWithCause:(UbiquityStoreErrorCause)cause context:(id)context
|
||||
|
@ -150,7 +150,7 @@
|
||||
<action selector="changeAvatar:" destination="S8q-YF-Kt9" eventType="touchUpInside" id="kL5-zV-zbb"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qp1-nX-o4i" userLabel="Entry" customClass="UIView+Touches">
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qp1-nX-o4i" userLabel="Entry">
|
||||
<rect key="frame" x="20" y="255" width="280" height="68"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
@ -236,7 +236,7 @@
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="ignoreTouches" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XEP-O3-ayG" userLabel="Footer" customClass="UIView+Touches">
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XEP-O3-ayG" userLabel="Footer">
|
||||
<rect key="frame" x="0.0" y="477" width="320" height="71"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
@ -980,11 +980,11 @@
|
||||
<viewControllerLayoutGuide type="top" id="S9X-2T-e1e"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="c12-XI-Rv9"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="MSX-uI-cwS" userLabel="Root" customClass="UIView+Touches">
|
||||
<view key="view" contentMode="scaleToFill" id="MSX-uI-cwS" userLabel="Root">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tI8-OT-LrO" userLabel="Passwords Root" customClass="UIView+Touches">
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tI8-OT-LrO" userLabel="Passwords Root">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
|
Loading…
Reference in New Issue
Block a user