2
0

Pearl & API update.

This commit is contained in:
Maarten Billemont
2020-01-14 13:59:32 -05:00
parent 22796663dc
commit 91b89aaf39
17 changed files with 575 additions and 561 deletions

View File

@@ -172,23 +172,24 @@
if (![site.questions count])
[self setMultiple:NO animated:YES];
else
[PearlAlert showAlertWithTitle:@"Remove Site Questions?" message:
else {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Remove Site Questions?" message:
@"Do you want to remove the questions you have configured for this site?"
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site_ = [self siteInContext:context];
NSOrderedSet *questions = [site_.questions copy];
for (MPSiteQuestionEntity *question in questions)
[context deleteObject:question];
[context saveToStore];
[self setMultiple:NO animated:YES];
}];
} cancelTitle:@"Cancel" otherTitles:@"Remove Questions", nil];
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Remove Questions" style:UIAlertActionStyleDestructive handler:
^(UIAlertAction *_Nonnull action) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site_ = [self siteInContext:context];
NSOrderedSet *questions = [site_.questions copy];
for (MPSiteQuestionEntity *question in questions)
[context deleteObject:question];
[context saveToStore];
[self setMultiple:NO animated:YES];
}];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
}
}
}
@@ -197,9 +198,9 @@
if (!self.multiple) {
NSObject *answer = [site resolveSiteAnswerUsingKey:[MPiOSAppDelegate get].key];
body = strf( @"Master Password generated the following security answer for your site: %@\n\n"
@"%@\n"
@"\n\nYou should use this as the answer to each security question the site asks you.\n"
@"Do not share this answer with others!", site.name, answer );
@"%@\n"
@"\n\nYou should use this as the answer to each security question the site asks you.\n"
@"Do not share this answer with others!", site.name, answer );
}
else {
NSMutableString *bodyBuilder = [NSMutableString string];
@@ -209,7 +210,7 @@
[bodyBuilder appendFormat:@"For question: '%@', use answer: %@\n", question.keyword, answer];
}
[bodyBuilder appendFormat:@"\n\nUse the answer for the matching security question.\n"
@"Do not share this answer with others!"];
@"Do not share this answer with others!"];
body = bodyBuilder;
}
@@ -225,7 +226,7 @@
- (void)copyAnswer:(NSString *)answer {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
if (@available(iOS 10.0, *)) {
if (@available( iOS 10.0, * )) {
[pasteboard setItems:@[ @{ UIPasteboardTypeAutomatic: answer } ]
options:@{
UIPasteboardOptionLocalOnly : @NO,

View File

@@ -228,9 +228,9 @@ const long MPAvatarAdd = 10000;
case MPAvatarModeLowered: {
[self.avatarSizeConstraint updateConstant:
self.avatarImageView.image.size.height * (self.visibility * 0.3f + 0.7f)];
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
[self.avatarRaisedConstraint withPriority:UILayoutPriorityDefaultLow];
[self.avatarToTopConstraint withPriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint withPriority:UILayoutPriorityDefaultLow];
self.nameContainer.visible = YES;
self.nameContainer.backgroundColor = [UIColor clearColor];
self.avatarImageView.visible = YES;
@@ -239,9 +239,9 @@ const long MPAvatarAdd = 10000;
case MPAvatarModeRaisedButInactive: {
[self.avatarSizeConstraint updateConstant:
self.avatarImageView.image.size.height * (self.visibility * 0.7f + 0.3f)];
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
[self.avatarRaisedConstraint withPriority:UILayoutPriorityDefaultHigh];
[self.avatarToTopConstraint withPriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint withPriority:UILayoutPriorityDefaultLow];
self.nameContainer.visible = YES;
self.nameContainer.backgroundColor = [UIColor clearColor];
self.avatarImageView.visible = NO;
@@ -250,9 +250,9 @@ const long MPAvatarAdd = 10000;
case MPAvatarModeRaisedAndActive: {
[self.avatarSizeConstraint updateConstant:
self.avatarImageView.image.size.height * (self.visibility * 0.7f + 0.3f)];
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
[self.avatarRaisedConstraint withPriority:UILayoutPriorityDefaultHigh];
[self.avatarToTopConstraint withPriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint withPriority:UILayoutPriorityDefaultHigh];
self.nameContainer.visible = YES;
self.nameContainer.backgroundColor = [UIColor blackColor];
self.avatarImageView.visible = YES;
@@ -261,9 +261,9 @@ const long MPAvatarAdd = 10000;
case MPAvatarModeRaisedAndHidden: {
[self.avatarSizeConstraint updateConstant:
self.avatarImageView.image.size.height * (self.visibility * 0.7f + 0.3f)];
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
[self.avatarRaisedConstraint withPriority:UILayoutPriorityDefaultHigh];
[self.avatarToTopConstraint withPriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint withPriority:UILayoutPriorityDefaultHigh];
self.nameContainer.visible = NO;
self.nameContainer.backgroundColor = [UIColor blackColor];
self.avatarImageView.visible = NO;
@@ -271,9 +271,9 @@ const long MPAvatarAdd = 10000;
}
case MPAvatarModeRaisedAndMinimized: {
[self.avatarSizeConstraint updateConstant:36];
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh + 2];
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
[self.avatarRaisedConstraint withPriority:UILayoutPriorityDefaultLow];
[self.avatarToTopConstraint withPriority:UILayoutPriorityDefaultHigh + 2];
[self.nameToCenterConstraint withPriority:UILayoutPriorityDefaultHigh];
self.nameContainer.visible = NO;
self.nameContainer.backgroundColor = [UIColor blackColor];
self.avatarImageView.visible = YES;

View File

@@ -56,16 +56,15 @@
BOOL traceEnabled = (BOOL)self.levelControl.selectedSegmentIndex;
if (traceEnabled) {
[PearlAlert showAlertWithTitle:@"Enable Trace Mode?" message:
@"Trace mode will log the internal operation of the application.\n"
@"Unless you're looking for the cause of a problem, you should leave this off to save memory."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[MPiOSConfig get].traceMode = @YES;
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Enable Trace", nil];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Enable Trace Mode?" message:
@"Trace mode will log the internal operation of the application.\n"
@"Unless you're looking for the cause of a problem, you should leave this off to save memory."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Enable Trace" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[MPiOSConfig get].traceMode = @YES;
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
}
else
[MPiOSConfig get].traceMode = @NO;
@@ -79,13 +78,14 @@
- (IBAction)mail:(UIBarButtonItem *)sender {
if ([[MPiOSConfig get].traceMode boolValue]) {
[PearlAlert showAlertWithTitle:@"Hiding Trace Messages" message:
@"Trace-level log messages will not be mailed. "
@"These messages contain sensitive and personal information."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
[[MPiOSAppDelegate get] openFeedbackWithLogs:YES forVC:self];
} cancelTitle:[PearlStrings get].commonButtonOkay otherTitles:nil];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Hiding Trace Messages" message:
@"Trace-level log messages will not be mailed. "
@"These messages contain sensitive and personal information."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
[[MPiOSAppDelegate get] openFeedbackWithLogs:YES forVC:self];
}]];
[self presentViewController:controller animated:YES completion:nil];
}
else
[[MPiOSAppDelegate get] openFeedbackWithLogs:YES forVC:self];

View File

@@ -41,7 +41,7 @@
[UIView animateWithDuration:0.6f delay:0 usingSpringWithDamping:0.75f initialSpringVelocity:1
options:UIViewAnimationOptionCurveEaseOut animations:^{
[[passwordsVC.popdownToTopConstraint updatePriority:1] layoutIfNeeded];
[[passwordsVC.popdownToTopConstraint withPriority:1] layoutIfNeeded];
} completion:^(BOOL finished) {
[popdownVC didMoveToParentViewController:passwordsVC];
@@ -63,7 +63,7 @@
[popdownVC willMoveToParentViewController:nil];
[UIView animateWithDuration:0.4f delay:0 options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionOverrideInheritedDuration
animations:^{
[[passwordsVC.popdownToTopConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded];
[[passwordsVC.popdownToTopConstraint withPriority:UILayoutPriorityDefaultHigh] layoutIfNeeded];
} completion:^(BOOL finished) {
[popdownVC.view removeFromSuperview];
[popdownVC removeFromParentViewController];

View File

@@ -114,11 +114,13 @@
if (cell == self.checkInconsistencies)
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
if ([[MPiOSAppDelegate get] findAndFixInconsistenciesSaveInContext:context] == MPFixableResultNoProblems)
[PearlAlert showAlertWithTitle:@"No Inconsistencies" message:
if ([[MPiOSAppDelegate get] findAndFixInconsistenciesSaveInContext:context] == MPFixableResultNoProblems) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"No Inconsistencies" message:
@"No inconsistencies were detected in your sites."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay otherTitles:nil];
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleDefault handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
}
}];
[tableView deselectRowAtIndexPath:indexPath animated:YES];

View File

@@ -261,19 +261,20 @@
if (!site)
return;
[PearlSheet showSheetWithTitle:strf( @"Delete %@?", site.name ) viewStyle:UIActionSheetStyleAutomatic
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex])
return;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site_ = [self siteInContext:context];
if (site_) {
[context deleteObject:site_];
[context saveToStore];
}
}];
} cancelTitle:@"Cancel" destructiveTitle:@"Delete Site" otherTitles:nil];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:strf( @"Delete %@?", site.name ) message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
[controller addAction:[UIAlertAction actionWithTitle:@"Delete Site" style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull action) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site_ = [self siteInContext:context];
if (site_) {
[context deleteObject:site_];
[context saveToStore];
}
}];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[UIApp.keyWindow.rootViewController presentViewController:controller animated:YES completion:nil];
}
- (IBAction)doChangeType:(UIButton *)sender {
@@ -281,29 +282,24 @@
[self setMode:MPPasswordCellModePassword animated:YES];
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
[PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic
initSheet:^(UIActionSheet *sheet) {
for (NSNumber *typeNumber in [mainSite.algorithm allTypes]) {
MPResultType type = (MPResultType)[typeNumber unsignedIntegerValue];
NSString *typeName = [mainSite.algorithm nameOfType:type];
if (type == mainSite.type)
[sheet addButtonWithTitle:strf( @"● %@", typeName )];
else
[sheet addButtonWithTitle:typeName];
}
} tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex])
return;
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Change Password Type" message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
for (NSNumber *typeNumber in [mainSite.algorithm allTypes]) {
MPResultType type = (MPResultType)[typeNumber unsignedIntegerValue];
NSString *typeName = [mainSite.algorithm nameOfType:type];
NSString *title = type == mainSite.type? strf( @"● %@", typeName ): typeName;
MPResultType type = (MPResultType)[[mainSite.algorithm allTypes][buttonIndex] unsignedIntegerValue]?:
mainSite.user.defaultType?: mainSite.algorithm.defaultType;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site = [self siteInContext:context];
site = [[MPiOSAppDelegate get] changeSite:site saveInContext:context toType:type];
[self setSite:site animated:YES];
}];
} cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:nil];
[controller addAction:[UIAlertAction actionWithTitle:title style:UIAlertActionStyleDefault handler:
^(UIAlertAction *_Nonnull action) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site = [self siteInContext:context];
site = [[MPiOSAppDelegate get] changeSite:site saveInContext:context toType:type];
[self setSite:site animated:YES];
}];
}]];
}
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[UIApp.keyWindow.rootViewController presentViewController:controller animated:YES completion:nil];
}
- (IBAction)doEdit:(UIButton *)sender {
@@ -370,24 +366,23 @@
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
MPSiteEntity *mainSite = [self siteInContext:mainContext];
[PearlAlert showAlertWithTitle:@"Login Page" message:nil
viewStyle:UIAlertViewStylePlainTextInput
initAlert:^(UIAlertView *alert, UITextField *firstField) {
firstField.placeholder = strf( @"Login URL for %@", mainSite.name );
firstField.text = mainSite.url;
}
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == alert.cancelButtonIndex)
return;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site = [self siteInContext:context];
NSURL *url = [NSURL URLWithString:[alert textFieldAtIndex:0].text];
site.url = [url.host? url: nil absoluteString];
[context saveToStore];
}];
}
cancelTitle:@"Cancel" otherTitles:@"Save", nil];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Login Page" message:nil
preferredStyle:UIAlertControllerStyleAlert];
[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = strf( @"Login URL for %@", mainSite.name );
textField.text = mainSite.url;
}];
[controller addAction:[UIAlertAction actionWithTitle:@"Save" style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site = [self siteInContext:context];
NSURL *url = [NSURL URLWithString:controller.textFields.firstObject.text];
site.url = [url.host? url: nil absoluteString];
[context saveToStore];
}];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[UIApp.keyWindow.rootViewController presentViewController:controller animated:YES completion:nil];
}];
}
@@ -446,15 +441,11 @@
if (self.transientSite) {
[[UIResponder findFirstResponder] resignFirstResponder];
[PearlAlert showAlertWithTitle:@"Create Site"
message:strf( @"Remember site named:\n%@", self.transientSite )
viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex]) {
self.contentButton.selected = NO;
return;
}
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Create Site" message:
strf( @"Remember site named:\n%@", self.transientSite )
preferredStyle:UIAlertControllerStyleActionSheet];
[controller addAction:[UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:
^(UIAlertAction *_Nonnull action) {
[[MPiOSAppDelegate get]
addSiteNamed:self.transientSite completion:^(MPSiteEntity *site, NSManagedObjectContext *context) {
[self copyContentOfSite:site saveInContext:context];
@@ -465,7 +456,11 @@
}];
} );
}];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
self.contentButton.selected = NO;
}]];
[UIApp.keyWindow.rootViewController presentViewController:controller animated:YES completion:nil];
return;
}
@@ -677,7 +672,7 @@
[self.window endEditing:YES];
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
if (@available(iOS 10.0, *)) {
if (@available( iOS 10.0, * )) {
[pasteboard setItems:@[ @{ UIPasteboardTypeAutomatic: password } ]
options:@{
UIPasteboardOptionLocalOnly : @NO,

View File

@@ -447,8 +447,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
_active = active;
[UIView animateWithDuration:animated? 0.4f: 0 animations:^{
[self.navigationBarToTopConstraint updatePriority:active? 1: UILayoutPriorityDefaultHigh];
[self.sitesToBottomConstraint updatePriority:active? 1: UILayoutPriorityDefaultHigh];
[self.navigationBarToTopConstraint withPriority:active? 1: UILayoutPriorityDefaultHigh];
[self.sitesToBottomConstraint withPriority:active? 1: UILayoutPriorityDefaultHigh];
[self.view layoutIfNeeded];
} completion:completion];
}

View File

@@ -146,15 +146,14 @@ PearlEnum( MPDevelopmentFuelConsumption,
- (IBAction)restorePurchases:(id)sender {
[PearlAlert showAlertWithTitle:@"Restore Previous Purchases" message:
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Restore Previous Purchases" message:
@"This will check with Apple to find and activate any purchases you made from other devices."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[[MPAppDelegate_Shared get] restoreCompletedTransactions];
} cancelTitle:@"Cancel" otherTitles:@"Find Purchases", nil];
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Find Purchases" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[MPAppDelegate_Shared get] restoreCompletedTransactions];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
}
- (IBAction)sendThanks:(id)sender {

View File

@@ -432,33 +432,27 @@ referenceSizeForFooterInSection:(NSInteger)section {
return;
NSManagedObjectID *userID = user.permanentObjectID;
[PearlSheet showSheetWithTitle:user.name
viewStyle:UIActionSheetStyleBlackTranslucent
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex])
return;
if (buttonIndex == [sheet destructiveButtonIndex]) {
// Delete User
[PearlAlert showParentalGateWithTitle:@"Deleting User" message:
@"The user and its sites will be deleted.\nPlease confirm by solving:"
completion:^(BOOL continuing) {
if (continuing)
[self deleteUser:userID];
}];
return;
}
if (buttonIndex == [sheet firstOtherButtonIndex])
// Reset Password
[PearlAlert showParentalGateWithTitle:@"Resetting User" message:
@"The user's master password will be reset.\nPlease confirm by solving:"
completion:^(BOOL continuing) {
if (continuing)
[self resetUser:userID avatar:avatarCell];
}];
} cancelTitle:[PearlStrings get].commonButtonCancel
destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:user.name message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[controller addAction:[UIAlertAction actionWithTitle:@"Delete User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Deleting User" message:
@"The user and its sites will be deleted." preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Delete User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[self deleteUser:userID];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Reset Password" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Resetting User" message:
@"The user's master password will be reset." preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Reset User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[self resetUser:userID avatar:avatarCell];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
}
}

View File

@@ -110,20 +110,24 @@
case MPFixableResultNoProblems:
break;
case MPFixableResultProblemsFixed:
[PearlAlert showAlertWithTitle:@"Inconsistencies Fixed" message:
case MPFixableResultProblemsFixed: {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Inconsistencies Fixed" message:
@"Some inconsistencies were detected in your sites.\n"
@"All issues were fixed."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay otherTitles:nil];
@"All issues were fixed."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
break;
case MPFixableResultProblemsNotFixed:
[PearlAlert showAlertWithTitle:@"Inconsistencies Found" message:
}
case MPFixableResultProblemsNotFixed: {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Inconsistencies Found" message:
@"Some inconsistencies were detected in your sites.\n"
@"Not all issues could be fixed. Try signing in to each user or checking the logs."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay otherTitles:nil];
@"Not all issues could be fixed. Try signing in to each user or checking the logs."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
break;
}
}
} );
@@ -133,12 +137,14 @@
} );
NSString *latestFeatures = [MPStoreViewController latestStoreFeatures];
if (latestFeatures)
[PearlAlert showAlertWithTitle:@"New Features" message:
if (latestFeatures) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"New Features" message:
strf( @"The following features are now available in the store:\n\n%@•••\n\n"
@"Find the store from the user pulldown after logging in.", latestFeatures )
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:nil
cancelTitle:@"Thanks" otherTitles:nil];
@"Find the store from the user pulldown after logging in.", latestFeatures )
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
}
}
@catch (id exception) {
err( @"During Post-Startup: %@", exception );
@@ -161,14 +167,22 @@
MPError( error, @"While reading imported sites from %@.", url );
if (!importedSitesData) {
[PearlAlert showError:strf( @"Master Password couldn't read the import sites.\n\n%@",
[error localizedDescription]?: error )];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Error" message:
strf( @"Master Password couldn't read the import sites.\n\n%@",
(id)[error localizedDescription]?: error )
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
return;
}
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
if (!importedSitesString) {
[PearlAlert showError:@"Master Password couldn't understand the import file."];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Error" message:
@"Master Password couldn't understand the import file."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
return;
}
@@ -190,33 +204,45 @@
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
[self importSites:importData askImportPassword:^NSString *(NSString *userName) {
return PearlAwait( ^(void (^setResult)(id)) {
[PearlAlert showAlertWithTitle:strf( @"Importing Sites For\n%@", userName )
message:@"Enter the master password used to create this export file."
viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
if (buttonIndex_ == [alert_ cancelButtonIndex])
setResult( nil );
else
setResult( [alert_ textFieldAtIndex:0].text );
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:strf( @"Importing Sites For\n%@", userName ) message:
@"Enter the master password used to create this export file."
preferredStyle:UIAlertControllerStyleAlert];
[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
}];
[controller addAction:[UIAlertAction actionWithTitle:@"Import" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
setResult( controller.textFields.firstObject.text );
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
setResult( nil );
}]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
} );
} askUserPassword:^NSString *(NSString *userName) {
return PearlAwait( (id)^(void (^setResult)(id)) {
[PearlAlert showAlertWithTitle:strf( @"Master Password For\n%@", userName )
message:@"Enter the current master password for this user."
viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
if (buttonIndex_ == [alert_ cancelButtonIndex])
setResult( nil );
else
setResult( [alert_ textFieldAtIndex:0].text );
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:strf( @"Master Password For\n%@", userName ) message:
@"Enter the current master password for this user."
preferredStyle:UIAlertControllerStyleAlert];
[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
}];
[controller addAction:[UIAlertAction actionWithTitle:@"Import" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
setResult( controller.textFields.firstObject.text );
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
setResult( nil );
}]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
} );
} result:^(NSError *error) {
[activityOverlay cancelOverlayAnimated:YES];
if (error && !(error.domain == NSCocoaErrorDomain && error.code == NSUserCancelledError))
[PearlAlert showError:error.localizedDescription];
if (error && !(error.domain == NSCocoaErrorDomain && error.code == NSUserCancelledError)) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Error" message:[error localizedDescription]
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
}
}];
}
@@ -237,17 +263,17 @@
PearlNotMainQueue( ^{
NSString *importData = [UIPasteboard generalPasteboard].string;
MPMarshalInfo *importInfo = mpw_marshal_read_info( importData.UTF8String );
if (importInfo->format != MPMarshalFormatNone)
[PearlAlert showAlertWithTitle:@"Import Sites?" message:
if (importInfo->format != MPMarshalFormatNone) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Import Sites?" message:
@"We've detected Master Password import sites on your pasteboard, would you like to import them?"
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[self importSites:importData];
[UIPasteboard generalPasteboard].string = @"";
} cancelTitle:@"No" otherTitles:@"Import Sites", nil];
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Import Sites" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self importSites:importData];
[UIPasteboard generalPasteboard].string = @"";
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"No" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
}
mpw_marshal_info_free( &importInfo );
} );
@@ -314,26 +340,29 @@
- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController {
if (![PearlEMail canSendMail])
[PearlAlert showAlertWithTitle:@"Feedback"
message:
@"Have a question, comment, issue or just saying thanks?\n\n"
@"We'd love to hear what you think!\n"
@"masterpassword@lyndir.com"
viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay
otherTitles:nil];
else if (logs)
[PearlAlert showAlertWithTitle:@"Feedback"
message:
@"Have a question, comment, issue or just saying thanks?\n\n"
@"If you're having trouble, it may help us if you can first reproduce the problem "
@"and then include log files in your message."
viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
[self openFeedbackWithLogs:(buttonIndex_ == [alert_ firstOtherButtonIndex]) forVC:viewController];
} cancelTitle:nil otherTitles:@"Include Logs", @"No Logs", nil];
if (![PearlEMail canSendMail]) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Feedback" message:
@"Have a question, comment, issue or just saying thanks?\n\n"
@"We'd love to hear what you think!\n"
@"masterpassword@lyndir.com"
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
}
else if (logs) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Feedback" message:
@"Have a question, comment, issue or just saying thanks?\n\n"
@"If you're having trouble, it may help us if you can first reproduce the problem "
@"and then include log files in your message."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Include Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self openFeedbackWithLogs:YES forVC:viewController];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"No Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self openFeedbackWithLogs:NO forVC:viewController];
}]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
}
else
[self openFeedbackWithLogs:NO forVC:viewController];
}
@@ -349,9 +378,9 @@
subject:strf( @"Feedback for Master Password [%@]",
[[PearlKeyChain deviceIdentifier] stringByDeletingMatchesOf:@"-.*"] )
body:strf( @"\n\n\n"
@"--\n"
@"%@"
@"Master Password %@, build %@",
@"--\n"
@"%@"
@"Master Password %@, build %@",
userName? ([userName stringByAppendingString:@"\n"]): @"",
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion )
@@ -372,173 +401,163 @@
static dispatch_once_t once = 0;
dispatch_once( &once, ^{
[PearlAlert showAlertWithTitle:@"Failed To Load Sites" message:
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Failed To Load Sites" message:
@"Master Password was unable to open your sites history.\n"
@"This may be due to corruption. You can either reset Master Password and "
@"recreate your user, or E-Mail us your logs and leave your corrupt store as-is for now."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
if (buttonIndex == [alert firstOtherButtonIndex])
[self openFeedbackWithLogs:YES forVC:nil];
if (buttonIndex == [alert firstOtherButtonIndex] + 1)
[self deleteAndResetStore];
} cancelTitle:@"Ignore" otherTitles:@"E-Mail Logs", @"Reset", nil];
@"This may be due to corruption. You can either reset Master Password and "
@"recreate your user, or E-Mail us your logs and leave your corrupt store as-is for now."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"E-Mail Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self openFeedbackWithLogs:YES forVC:nil];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Reset" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self deleteAndResetStore];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Ignore" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
} );
}
- (void)showExportForVC:(UIViewController *)viewController {
[PearlAlert showAlertWithTitle:@"Exporting Your Sites"
message:@"An export is great for keeping a "
@"backup list of your accounts.\n\n"
@"When the file is ready, you will be "
@"able to mail it to yourself.\n"
@"You can open it with a text editor or "
@"with Master Password if you need to "
@"restore your list of sites."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex != [alert cancelButtonIndex])
[PearlAlert showAlertWithTitle:@"Show Passwords?"
message:@"Would you like to make all your passwords "
@"visible in the export file?\n\n"
@"A safe export will include all sites "
@"but make their passwords invisible.\n"
@"It is great as a backup and remains "
@"safe when fallen in the wrong hands."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 0)
// Safe Export
[self showExportRevealPasswords:NO forVC:viewController];
if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 1)
// Show Passwords
[self showExportRevealPasswords:YES forVC:viewController];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Safe Export", @"Show Passwords",
nil];
} cancelTitle:@"Cancel" otherTitles:@"Export Sites", nil];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Exporting Your Sites" message:
@"An export is great for keeping a backup list of your accounts.\n\n"
@"When the file is ready, you will be able to mail it to yourself.\n"
@"You can open it with a text editor or with Master Password if you need to restore your list of sites."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Export Sites" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
UIAlertController *controller_ = [UIAlertController alertControllerWithTitle:@"Show Passwords?" message:
@"Would you like to make all your passwords visible in the export file?\n\n"
@"A safe export will include all sites but make their passwords invisible.\n"
@"It is great as a backup and remains safe when fallen in the wrong hands."
preferredStyle:UIAlertControllerStyleAlert];
[controller_ addAction:[UIAlertAction actionWithTitle:@"Safe Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self showExportRevealPasswords:NO forVC:viewController];
}]];
[controller_ addAction:[UIAlertAction actionWithTitle:@"Show Passwords" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self showExportRevealPasswords:YES forVC:viewController];
}]];
[controller_ addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller_ animated:YES completion:nil];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
}
- (void)showExportRevealPasswords:(BOOL)revealPasswords forVC:(UIViewController *)viewController {
if (![PearlEMail canSendMail]) {
[PearlAlert showAlertWithTitle:@"Cannot Send Mail"
message:
@"Your device is not yet set up for sending mail.\n"
@"Close Master Password, go into Settings and add a Mail account."
viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay
otherTitles:nil];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Cannot Send Mail" message:
@"Your device is not yet set up for sending mail.\n"
@"Close Master Password, go into Settings and add a Mail account."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
return;
}
[self exportSitesRevealPasswords:revealPasswords askExportPassword:^NSString *(NSString *userName) {
return PearlAwait( ^(void (^setResult)(id)) {
[PearlAlert showAlertWithTitle:strf( @"Master Password For:\n%@", userName )
message:@"Enter the user's master password to create an export file."
viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
if (buttonIndex_ == [alert_ cancelButtonIndex])
setResult( nil );
else
setResult( [alert_ textFieldAtIndex:0].text );
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Export", nil];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName ) message:
@"Enter the user's master password to create an export file."
preferredStyle:UIAlertControllerStyleAlert];
[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
}];
[controller addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
setResult( controller.textFields.firstObject.text );
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
setResult( nil );
}]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
} );
} result:^(NSString *mpsites, NSError *error) {
if (!mpsites || error) {
MPError( error, @"Failed to export mpsites." );
[PearlAlert showAlertWithTitle:@"Export Error"
message:error.localizedDescription
viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay
otherTitles:nil];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Export Error" message:[error localizedDescription]
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
return;
}
[PearlSheet showSheetWithTitle:@"Export Destination" viewStyle:UIActionSheetStyleBlackTranslucent initSheet:nil
tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex])
return;
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
NSString *exportFileName = strf( @"%@ (%@).mpsites",
[self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
NSString *exportFileName = strf( @"%@ (%@).mpsites",
[self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Export Destination" message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
[controller addAction:[UIAlertAction actionWithTitle:@"Send As E-Mail" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSString *message;
if (revealPasswords)
message = strf( @"Export of Master Password sites with passwords included.\n\n"
@"REMINDER: Make sure nobody else sees this file! Passwords are visible!\n\n\n"
@"--\n"
@"%@\n"
@"Master Password %@, build %@",
[self activeUserForMainThread].name,
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
else
message = strf( @"Backup of Master Password sites.\n\n\n"
@"--\n"
@"%@\n"
@"Master Password %@, build %@",
[self activeUserForMainThread].name,
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
if (buttonIndex == [sheet firstOtherButtonIndex]) {
NSString *message;
if (revealPasswords)
message = strf( @"Export of Master Password sites with passwords included.\n\n"
@"REMINDER: Make sure nobody else sees this file! Passwords are visible!\n\n\n"
@"--\n"
@"%@\n"
@"Master Password %@, build %@",
[self activeUserForMainThread].name,
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
else
message = strf( @"Backup of Master Password sites.\n\n\n"
@"--\n"
@"%@\n"
@"Master Password %@, build %@",
[self activeUserForMainThread].name,
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
attachments:[[PearlEMailAttachment alloc]
initWithContent:[mpsites dataUsingEncoding:NSUTF8StringEncoding]
mimeType:@"text/plain" fileName:exportFileName],
nil];
return;
}
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask] lastObject];
NSURL *exportURL = [[applicationSupportURL
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
URLByAppendingPathComponent:exportFileName isDirectory:NO];
NSError *writeError = nil;
if (![[mpsites dataUsingEncoding:NSUTF8StringEncoding]
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&writeError])
MPError( writeError, @"Failed to write export data to URL %@.", exportURL );
else {
self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
self.interactionController.delegate = self;
[self.interactionController presentOpenInMenuFromRect:CGRectZero inView:viewController.view animated:YES];
}
} cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:@"Send As E-Mail", @"Share / Airdrop", nil];
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
attachments:[[PearlEMailAttachment alloc]
initWithContent:[mpsites dataUsingEncoding:NSUTF8StringEncoding]
mimeType:@"text/plain" fileName:exportFileName],
nil];
return;
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Share / Airdrop" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask] lastObject];
NSURL *exportURL = [[applicationSupportURL
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
URLByAppendingPathComponent:exportFileName isDirectory:NO];
NSError *writeError = nil;
if (![[mpsites dataUsingEncoding:NSUTF8StringEncoding]
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&writeError])
MPError( writeError, @"Failed to write export data to URL %@.", exportURL );
else {
self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
self.interactionController.delegate = self;
[self.interactionController presentOpenInMenuFromRect:CGRectZero inView:viewController.view animated:YES];
}
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
}];
}
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
[PearlAlert showAlertWithTitle:@"Changing Master Password"
message:
@"If you continue, you'll be able to set a new master password.\n\n"
@"Changing your master password will cause all your generated passwords to change!\n"
@"Changing the master password back to the old one will cause your passwords to revert as well."
viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Changing Master Password" message:
@"If you continue, you'll be able to set a new master password.\n\n"
@"Changing your master password will cause all your generated passwords to change!\n"
@"Changing the master password back to the old one will cause your passwords to revert as well."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[moc performBlockAndWait:^{
inf( @"Clearing keyID for user: %@.", user.userID );
user.keyID = nil;
[self forgetSavedKeyFor:user];
[moc saveToStore];
}];
[moc performBlockAndWait:^{
inf( @"Clearing keyID for user: %@.", user.userID );
user.keyID = nil;
[self forgetSavedKeyFor:user];
[moc saveToStore];
}];
[self signOutAnimated:YES];
if (didReset)
didReset();
}
cancelTitle:[PearlStrings get].commonButtonAbort
otherTitles:[PearlStrings get].commonButtonContinue, nil];
[self signOutAnimated:YES];
if (didReset)
didReset();
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
}
#pragma mark - UIDocumentInteractionControllerDelegate

View File

@@ -1,133 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>M. Password</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>mpsites</string>
</array>
<key>CFBundleTypeIconFiles</key>
<array>
<string>Icon-Small</string>
</array>
<key>CFBundleTypeName</key>
<string>Master Password sites</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.lyndir.masterpassword.sites</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>[auto]</string>
<key>CFBundleVersion</key>
<string>[auto]</string>
<key>Fabric</key>
<dict>
<key>APIKey</key>
<string>0d10c90776f5ef5acd01ddbeaca9a6cba4814560</string>
<key>Kits</key>
<array>
<dict>
<key>KitInfo</key>
<dict/>
<key>KitName</key>
<string>Crashlytics</string>
</dict>
</array>
</dict>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>© 2011-2018</string>
<key>UIAppFonts</key>
<array>
<string>Exo2.0-Bold.otf</string>
<string>Exo2.0-ExtraBold.otf</string>
<string>Exo2.0-Regular.otf</string>
<string>Exo2.0-Thin.otf</string>
<string>SourceCodePro-Black.otf</string>
<string>SourceCodePro-Regular.otf</string>
<string>SourceCodePro-ExtraLight.otf</string>
</array>
<key>UIStatusBarHidden</key>
<true/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDefault</string>
<key>UIStatusBarTintParameters</key>
<dict>
<key>UINavigationBar</key>
<dict>
<key>Style</key>
<string>UIBarStyleDefault</string>
<key>TintColor</key>
<dict>
<key>Blue</key>
<real>0.42745098039215684</real>
<key>Green</key>
<real>0.39215686274509803</real>
<key>Red</key>
<real>0.37254901960784315</real>
</dict>
<key>Translucent</key>
<false/>
</dict>
</dict>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.utf8-plain-text</string>
</array>
<key>UTTypeDescription</key>
<string>Master Password sites</string>
<key>UTTypeIdentifier</key>
<string>com.lyndir.masterpassword.sites</string>
<key>UTTypeSize320IconFile</key>
<string>Icon-320.png</string>
<key>UTTypeSize64IconFile</key>
<string>Icon-64.png</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>mpsites</string>
</array>
</dict>
</dict>
</array>
</dict>
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>M. Password</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>mpsites</string>
</array>
<key>CFBundleTypeIconFiles</key>
<array>
<string>Icon-Small</string>
</array>
<key>CFBundleTypeName</key>
<string>Master Password sites</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.lyndir.masterpassword.sites</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>[auto]</string>
<key>CFBundleVersion</key>
<string>[auto]</string>
<key>Fabric</key>
<dict>
<key>APIKey</key>
<string>0d10c90776f5ef5acd01ddbeaca9a6cba4814560</string>
<key>Kits</key>
<array>
<dict>
<key>KitInfo</key>
<dict/>
<key>KitName</key>
<string>Crashlytics</string>
</dict>
</array>
</dict>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>© 2011-2018</string>
<key>UIAppFonts</key>
<array>
<string>Exo2.0-Bold.otf</string>
<string>Exo2.0-ExtraBold.otf</string>
<string>Exo2.0-Regular.otf</string>
<string>Exo2.0-Thin.otf</string>
<string>SourceCodePro-Black.otf</string>
<string>SourceCodePro-Regular.otf</string>
<string>SourceCodePro-ExtraLight.otf</string>
</array>
<key>UIMainStoryboardFile</key>
<string>Storyboard</string>
<key>UIStatusBarHidden</key>
<true/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDefault</string>
<key>UIStatusBarTintParameters</key>
<dict>
<key>UINavigationBar</key>
<dict>
<key>Style</key>
<string>UIBarStyleDefault</string>
<key>TintColor</key>
<dict>
<key>Blue</key>
<real>0.42745098039215684</real>
<key>Green</key>
<real>0.39215686274509803</real>
<key>Red</key>
<real>0.37254901960784315</real>
</dict>
<key>Translucent</key>
<false/>
</dict>
</dict>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.utf8-plain-text</string>
</array>
<key>UTTypeDescription</key>
<string>Master Password sites</string>
<key>UTTypeIdentifier</key>
<string>com.lyndir.masterpassword.sites</string>
<key>UTTypeSize320IconFile</key>
<string>Icon-320.png</string>
<key>UTTypeSize64IconFile</key>
<string>Icon-64.png</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>mpsites</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>