diff --git a/WordPress/Classes/Blog+Jetpack.m b/WordPress/Classes/Blog+Jetpack.m index 636008932c1b..899f413ff879 100644 --- a/WordPress/Classes/Blog+Jetpack.m +++ b/WordPress/Classes/Blog+Jetpack.m @@ -10,6 +10,7 @@ #import "Blog+Jetpack.h" #import "WPAccount.h" +#import "Note.h" #import "WordPressAppDelegate.h" #import "ContextManager.h" #import "WordPressComOAuthClient.h" @@ -147,7 +148,7 @@ - (void)saveJetpackUsername:(NSString *)username andPassword:(NSString *)passwor // Sadly we don't care if this succeeds or not [account syncBlogsWithSuccess:nil failure:nil]; - [account.restApi getNotificationsSince:nil success:nil failure:nil]; + [Note fetchNewNotificationsWithSuccess:nil failure:nil]; } } failure:^(NSError *error) { DDLogError(@"Error while obtaining OAuth2 token after enabling JetPack: %@", error); diff --git a/WordPress/Classes/Blog.m b/WordPress/Classes/Blog.m index 522a4a88ef74..f44165b6f68c 100644 --- a/WordPress/Classes/Blog.m +++ b/WordPress/Classes/Blog.m @@ -541,7 +541,7 @@ - (WPXMLRPCClient *)api { // Enable compression for wp.com only, as some self hosted have connection issues if (self.isWPcom) { [_api setDefaultHeader:@"gzip, deflate" value:@"Accept-Encoding"]; - [_api setAuthorizationHeaderWithToken:[WordPressComApi sharedApi].authToken]; + [_api setAuthorizationHeaderWithToken:self.account.authToken]; } } return _api; diff --git a/WordPress/Classes/BlogListViewController.m b/WordPress/Classes/BlogListViewController.m index b528ca371320..c433f1bc80f0 100644 --- a/WordPress/Classes/BlogListViewController.m +++ b/WordPress/Classes/BlogListViewController.m @@ -82,9 +82,6 @@ - (void)viewDidLoad { target:self action:@selector(showSettings:)]; self.navigationItem.rightBarButtonItem = self.settingsButton; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wordPressComApiDidLogin:) name:WordPressComApiDidLoginNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wordPressComApiDidLogout:) name:WordPressComApiDidLogoutNotification object:nil]; // Remove one-pixel gap resulting from a top-aligned grouped table view if (IS_IPHONE) { diff --git a/WordPress/Classes/BlogSelectorViewController.m b/WordPress/Classes/BlogSelectorViewController.m index 749b010443e3..a31ee812a23f 100644 --- a/WordPress/Classes/BlogSelectorViewController.m +++ b/WordPress/Classes/BlogSelectorViewController.m @@ -60,8 +60,7 @@ - (void)viewDidLoad { self.navigationItem.leftBarButtonItem = cancelButtonItem; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wordPressComApiDidLogin:) name:WordPressComApiDidLoginNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wordPressComApiDidLogout:) name:WordPressComApiDidLogoutNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wordPressComAccountChanged) name:WPAccountDefaultWordPressComAccountChangedNotification object:nil]; // Remove one-pixel gap resulting from a top-aligned grouped table view if (IS_IPHONE) { @@ -100,15 +99,10 @@ - (BOOL)hasDotComAndSelfHosted { #pragma mark - Notifications -- (void)wordPressComApiDidLogin:(NSNotification *)notification { +- (void)wordPressComAccountChanged { [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade]; } -- (void)wordPressComApiDidLogout:(NSNotification *)notification { - [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade]; -} - - #pragma mark - Actions - (IBAction)cancelButtonTapped:(id)sender { diff --git a/WordPress/Classes/BlogToAccount.m b/WordPress/Classes/BlogToAccount.m index 4717f42ea2fd..aec8e08bc010 100644 --- a/WordPress/Classes/BlogToAccount.m +++ b/WordPress/Classes/BlogToAccount.m @@ -12,6 +12,7 @@ #import "WPAccount.h" static NSString * const DefaultDotcomAccountDefaultsKey = @"AccountDefaultDotcom"; +static NSString * const WPComXMLRPCUrl = @"https://wordpress.com/xmlrpc.php"; @implementation BlogToAccount { NSString *_defaultWpcomUsername; diff --git a/WordPress/Classes/BlogToJetpackAccount.m b/WordPress/Classes/BlogToJetpackAccount.m index 695b6a9fdb03..515bd26fd8bf 100644 --- a/WordPress/Classes/BlogToJetpackAccount.m +++ b/WordPress/Classes/BlogToJetpackAccount.m @@ -11,6 +11,7 @@ #import "WPAccount.h" static NSString * const BlogJetpackKeychainPrefix = @"jetpackblog-"; +static NSString * const WPComXMLRPCUrl = @"https://wordpress.com/xmlrpc.php"; @implementation BlogToJetpackAccount diff --git a/WordPress/Classes/Constants.h b/WordPress/Classes/Constants.h index 471e465f9e1e..2fc1faad6613 100644 --- a/WordPress/Classes/Constants.h +++ b/WordPress/Classes/Constants.h @@ -9,4 +9,4 @@ static NSString *const kMobileReaderURL = @"https://en.wordpress.com/reader/mobi static NSString *const kMobileReaderDetailURL = @"https://en.wordpress.com/reader/mobile/v2/?template=details"; static NSString *const kMobileReaderFFURL = @"https://en.wordpress.com/reader/mobile/v2/?template=friendfinder"; -static NSString *const kXML_RPC_ERROR_OCCURS = @"kXML_RPC_ERROR_OCCURS"; \ No newline at end of file +static NSString *const kXML_RPC_ERROR_OCCURS = @"kXML_RPC_ERROR_OCCURS"; diff --git a/WordPress/Classes/EditSiteViewController.m b/WordPress/Classes/EditSiteViewController.m index e1a6f605396f..2e2d1b1d7430 100644 --- a/WordPress/Classes/EditSiteViewController.m +++ b/WordPress/Classes/EditSiteViewController.m @@ -15,6 +15,7 @@ #import "ReachabilityUtils.h" #import "WPAccount.h" #import "WPTableViewSectionHeaderView.h" +#import "NotificationsManager.h" #import #import #import @@ -69,7 +70,7 @@ - (void)viewDidLoad { _notificationPreferences = [[[NSUserDefaults standardUserDefaults] objectForKey:@"notification_preferences"] mutableCopy]; if (!_notificationPreferences) { - [[WordPressComApi sharedApi] fetchNotificationSettings:^{ + [NotificationsManager fetchNotificationSettingsWithSuccess:^{ [self reloadNotificationSettings]; } failure:^(NSError *error) { [WPError showAlertWithTitle:NSLocalizedString(@"Error", @"") message:error.localizedDescription]; @@ -375,8 +376,8 @@ - (void)configureTextField:(UITextField *)textField asPassword:(BOOL)asPassword - (BOOL)canTogglePushNotifications { return self.blog && ([self.blog isWPcom] || [self.blog hasJetpack]) && - [[WordPressComApi sharedApi] hasCredentials] && - [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsDeviceToken] != nil; + [[[WPAccount defaultWordPressComAccount] restApi] hasCredentials] && + [NotificationsManager deviceRegisteredForPushNotifications]; } - (void)toggleGeolocation:(id)sender { @@ -408,7 +409,7 @@ - (void)togglePushNotifications:(id)sender { [[NSUserDefaults standardUserDefaults] setValue:_notificationPreferences forKey:@"notification_preferences"]; // Send these settings optimistically since they're low-impact (not ideal but works for now) - [[WordPressComApi sharedApi] saveNotificationSettings:nil failure:nil]; + [NotificationsManager saveNotificationSettings]; return; } } diff --git a/WordPress/Classes/FollowButton.m b/WordPress/Classes/FollowButton.m index ad48c7e9ee3c..dc828f330835 100644 --- a/WordPress/Classes/FollowButton.m +++ b/WordPress/Classes/FollowButton.m @@ -8,6 +8,7 @@ #import "FollowButton.h" #import "NSString+XMLExtensions.h" +#import "WPToast.h" @interface FollowButton () @property (nonatomic, strong) UIButton *button; @@ -82,6 +83,11 @@ - (void)toggleFollowState:(id)sender { [self.user followBlog:[self.siteID intValue] isFollowing:isFollowing success:^(AFHTTPRequestOperation *operation, id followResponse){ BOOL following = [[followResponse objectForKey:@"is_following"] intValue] == 1; self.followState = following ? FollowButtonStateFollowing : FollowButtonStateNotFollowing; + + NSString *message = following ? NSLocalizedString(@"Unfollowed", @"User unfollowed a blog") : NSLocalizedString(@"Followed", @"User followed a blog"); + NSString *imageName = [NSString stringWithFormat:@"action_icon_%@", (following) ? @"unfollowed" : @"followed"]; + [WPToast showToastWithMessage:message andImage:[UIImage imageNamed:imageName]]; + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { // flip the button back since it failed self.followState = isFollowing ? FollowButtonStateNotFollowing : FollowButtonStateFollowing; diff --git a/WordPress/Classes/JetpackSettingsViewController.m b/WordPress/Classes/JetpackSettingsViewController.m index c810e341b8f2..1356c9802bdf 100644 --- a/WordPress/Classes/JetpackSettingsViewController.m +++ b/WordPress/Classes/JetpackSettingsViewController.m @@ -16,6 +16,7 @@ #import "WPWalkthroughTextField.h" #import "WPNUXSecondaryButton.h" #import "UILabel+SuggestSize.h" +#import "WordPressComOAuthClient.h" CGFloat const JetpackiOS7StatusBarOffset = 20.0; CGFloat const JetpackStandardOffset = 16; @@ -305,6 +306,14 @@ - (void)saveAction:(id)sender { [_blog validateJetpackUsername:_usernameField.text password:_passwordField.text success:^{ + if (![[[WPAccount defaultWordPressComAccount] restApi] hasCredentials]) { + [[WordPressComOAuthClient client] authenticateWithUsername:_usernameField.text password:_passwordField.text success:^(NSString *authToken) { + WPAccount *account = [WPAccount createOrUpdateWordPressComAccountWithUsername:_usernameField.text password:_passwordField.text authToken:authToken]; + [WPAccount setDefaultWordPressComAccount:account]; + } failure:^(NSError *error) { + DDLogWarn(@"Unabled to obtain OAuth token for account credentials provided for Jetpack blog. %@", error); + }]; + } [self setAuthenticating:NO]; if (self.completionBlock) { self.completionBlock(YES); diff --git a/WordPress/Classes/LoginViewController.m b/WordPress/Classes/LoginViewController.m index 5eebe0dcc48a..3a2a02062c85 100644 --- a/WordPress/Classes/LoginViewController.m +++ b/WordPress/Classes/LoginViewController.m @@ -773,7 +773,7 @@ - (void)createWordPressComAccountForUsername:(NSString *)username password:(NSSt [self setAuthenticating:NO withStatusMessage:nil]; [self displayRemoteError:error]; }]; - [account.restApi getNotificationsSince:nil success:nil failure:nil]; + [Note fetchNewNotificationsWithSuccess:nil failure:nil]; } - (void)createSelfHostedAccountAndBlogWithUsername:(NSString *)username password:(NSString *)password xmlrpc:(NSString *)xmlrpc options:(NSDictionary *)options diff --git a/WordPress/Classes/Note.h b/WordPress/Classes/Note.h index 4bc6cf2bb621..9e0f3b5f3cbf 100644 --- a/WordPress/Classes/Note.h +++ b/WordPress/Classes/Note.h @@ -30,10 +30,8 @@ - (BOOL)isUnread; - (void)syncAttributes:(NSDictionary *)data; -- (NSDictionary *)getNoteData; -+ (void)syncNotesWithResponse:(NSArray *)notesData; -+ (void)refreshUnreadNotesWithContext:(NSManagedObjectContext *)context; ++ (void)mergeNewNotes:(NSArray *)notesData; /** Remove old notes from Core Data storage @@ -43,6 +41,16 @@ @param context The context which contains the notes to delete. */ + (void)pruneOldNotesBefore:(NSNumber *)timestamp withContext:(NSManagedObjectContext *)context; -+ (void)getNewNotificationswithContext:(NSManagedObjectContext *)context success:(void (^)(BOOL hasNewNotes))success failure:(void (^)(NSError *error))failure; + +@end + +@interface Note (WordPressComApi) + ++ (void)fetchNewNotificationsWithSuccess:(void (^)(BOOL hasNewNotes))success failure:(void (^)(NSError *error))failure; ++ (void)refreshUnreadNotesWithContext:(NSManagedObjectContext *)context; ++ (void)fetchNotificationsSince:(NSNumber *)timestamp success:(void (^)())success failure:(void (^)(NSError *error))failure; ++ (void)fetchNotificationsBefore:(NSNumber *)timestamp success:(void (^)())success failure:(void (^)(NSError *error))failure; +- (void)refreshNoteDataWithSuccess:(void (^)())success failure:(void (^)(NSError *error))failure; +- (void)markAsReadWithSuccess:(void (^)())success failure:(void (^)(NSError *error))failure; @end diff --git a/WordPress/Classes/Note.m b/WordPress/Classes/Note.m index 412b5a9ecdc3..5a3a86c399bb 100644 --- a/WordPress/Classes/Note.m +++ b/WordPress/Classes/Note.m @@ -50,7 +50,7 @@ @implementation Note @synthesize commentText = _commentText, noteData = _noteData; -+ (void)syncNotesWithResponse:(NSArray *)notesData { ++ (void)mergeNewNotes:(NSArray *)notesData { NSManagedObjectContext *context = [[ContextManager sharedInstance] backgroundContext]; [context performBlock:^{ NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Note"]; @@ -81,15 +81,6 @@ + (void)syncNotesWithResponse:(NSArray *)notesData { }]; } -+ (void)refreshUnreadNotesWithContext:(NSManagedObjectContext *)context { - NSFetchRequest *request = [[ContextManager sharedInstance].managedObjectModel fetchRequestTemplateForName:@"UnreadNotes"]; - NSError *error = nil; - NSArray *notes = [context executeFetchRequest:request error:&error]; - if ([notes count] > 0) { - [[WordPressComApi sharedApi] refreshNotifications:notes fields:@"id,unread" success:nil failure:nil]; - } -} - + (void)pruneOldNotesBefore:(NSNumber *)timestamp withContext:(NSManagedObjectContext *)context { NSError *error; @@ -128,21 +119,6 @@ + (void)pruneOldNotesBefore:(NSNumber *)timestamp withContext:(NSManagedObjectCo [context save:&error]; } -+ (void)getNewNotificationswithContext:(NSManagedObjectContext *)context success:(void (^)(BOOL hasNewNotes))success failure:(void (^)(NSError *error))failure { - NSNumber *timestamp = [self lastNoteTimestampWithContext:context]; - - [[WordPressComApi sharedApi] getNotificationsSince:timestamp success:^(AFHTTPRequestOperation *operation, id responseObject) { - NSArray *notes = [responseObject arrayForKey:@"notes"]; - if (success) { - success([notes count] > 0); - } - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - if (failure) { - failure(error); - } - }]; -} - + (NSNumber *)lastNoteTimestampWithContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Note"]; request.resultType = NSDictionaryResultType; @@ -158,10 +134,6 @@ + (NSNumber *)lastNoteTimestampWithContext:(NSManagedObjectContext *)context { return timestamp; } -- (NSDictionary *)getNoteData { - return self.noteData; -} - - (void)syncAttributes:(NSDictionary *)noteData { self.payload = [NSJSONSerialization dataWithJSONObject:noteData options:0 error:nil]; self.noteData = [NSJSONSerialization JSONObjectWithData:self.payload options:NSJSONReadingMutableContainers error:nil]; @@ -251,3 +223,85 @@ - (void)parseComment { } @end + +@implementation Note (WordPressComApi) + ++ (void)fetchNewNotificationsWithSuccess:(void (^)(BOOL hasNewNotes))success failure:(void (^)(NSError *error))failure { + NSNumber *timestamp = [self lastNoteTimestampWithContext:[ContextManager sharedInstance].backgroundContext]; + + [[[WPAccount defaultWordPressComAccount] restApi] fetchNotificationsSince:timestamp success:^(NSArray *notes) { + [Note mergeNewNotes:notes]; + if (success) { + success([notes count] > 0); + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if (failure) { + failure(error); + } + }]; +} + ++ (void)refreshUnreadNotesWithContext:(NSManagedObjectContext *)context { + NSFetchRequest *request = [[ContextManager sharedInstance].managedObjectModel fetchRequestTemplateForName:@"UnreadNotes"]; + NSError *error = nil; + NSArray *notes = [context executeFetchRequest:request error:&error]; + if ([notes count] > 0) { + [[[WPAccount defaultWordPressComAccount] restApi] refreshNotifications:notes fields:@"id,unread" success:nil failure:nil]; + } +} + ++ (void)fetchNotificationsBefore:(NSNumber *)timestamp success:(void (^)())success failure:(void (^)(NSError *))failure { + [[[WPAccount defaultWordPressComAccount] restApi] fetchNotificationsBefore:timestamp success:^(NSArray *notes) { + [self mergeNewNotes:notes]; + if (success) { + success(); + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if (failure) { + failure(error); + } + }]; +} + ++ (void)fetchNotificationsSince:(NSNumber *)timestamp success:(void (^)())success failure:(void (^)(NSError *))failure { + [[[WPAccount defaultWordPressComAccount] restApi] fetchNotificationsSince:timestamp success:^(NSArray *notes) { + [self mergeNewNotes:notes]; + if (success) { + success(); + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if (failure) { + failure(error); + } + }]; +} + +- (void)refreshNoteDataWithSuccess:(void (^)())success failure:(void (^)(NSError *))failure { + [[[WPAccount defaultWordPressComAccount] restApi] refreshNotifications:@[self.noteID] fields:nil success:^(NSArray *updatedNotes){ + if ([updatedNotes count] > 0 && ![self isDeleted] && self.managedObjectContext) { + [self syncAttributes:updatedNotes[0]]; + } + [[ContextManager sharedInstance] saveContext:self.managedObjectContext]; + if (success) { + success(); + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if (failure) { + failure(error); + } + }]; +} + +- (void)markAsReadWithSuccess:(void (^)())success failure:(void (^)(NSError *))failure { + [[[WPAccount defaultWordPressComAccount] restApi] markNoteAsRead:self.noteID success:^(AFHTTPRequestOperation *operation, id responseObject) { + if (success) { + success(); + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if (failure) { + failure(error); + } + }]; +} + +@end diff --git a/WordPress/Classes/NotificationSettingsViewController.m b/WordPress/Classes/NotificationSettingsViewController.m index 598b51300bc2..b3c16893a1d5 100644 --- a/WordPress/Classes/NotificationSettingsViewController.m +++ b/WordPress/Classes/NotificationSettingsViewController.m @@ -15,6 +15,8 @@ #import "NSString+XMLExtensions.h" #import "DateUtils.h" #import "WPTableViewSectionHeaderView.h" +#import "WPAccount.h" +#import "NotificationsManager.h" @interface NotificationSettingsViewController () @@ -74,10 +76,10 @@ - (void)viewWillAppear:(BOOL)animated { } - (void)getNotificationSettings { - [[WordPressComApi sharedApi] fetchNotificationSettings:^{ - [self notificationsDidFinishRefreshingWithError: nil]; + [NotificationsManager fetchNotificationSettingsWithSuccess:^{ + [self notificationsDidFinishRefreshingWithError:nil]; } failure:^(NSError *error) { - [self notificationsDidFinishRefreshingWithError: error]; + [self notificationsDidFinishRefreshingWithError:error]; }]; } @@ -251,8 +253,9 @@ - (void)dismiss { - (void)viewWillDisappear:(BOOL)animated { self.navigationController.toolbarHidden = YES; - if (hasChanges) - [[WordPressComApi sharedApi] saveNotificationSettings:nil failure:nil]; + if (hasChanges){ + [NotificationsManager saveNotificationSettings]; + } [super viewWillDisappear:animated]; } diff --git a/WordPress/Classes/NotificationsCommentDetailViewController.m b/WordPress/Classes/NotificationsCommentDetailViewController.m index 8fb90cadc24e..dfe510824bef 100644 --- a/WordPress/Classes/NotificationsCommentDetailViewController.m +++ b/WordPress/Classes/NotificationsCommentDetailViewController.m @@ -399,14 +399,14 @@ - (IBAction)moderateComment:(id)sender { [spinner startAnimating]; NSString *path = [NSString stringWithFormat:@"/rest/v1%@", [commentAction valueForKeyPath:@"params.rest_path"]]; + [[[WPAccount defaultWordPressComAccount] restApi] postPath:path parameters:[commentAction valueForKeyPath:@"params.rest_body"] success:^(AFHTTPRequestOperation *operation, id responseObject) { NSDictionary *response = (NSDictionary *)responseObject; if (response) { - NSArray *noteArray = [NSArray arrayWithObject:_note]; - [[[WPAccount defaultWordPressComAccount] restApi] refreshNotifications:noteArray fields:nil success:^(AFHTTPRequestOperation *operation, id refreshResponseObject) { + [_note refreshNoteDataWithSuccess:^{ [spinner stopAnimating]; [self displayNote]; - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + } failure:^(NSError *error) { [spinner stopAnimating]; [self displayNote]; }]; diff --git a/WordPress/Classes/NotificationsFollowDetailViewController.m b/WordPress/Classes/NotificationsFollowDetailViewController.m index 41cc73a2c3a6..d6f9860de2fc 100644 --- a/WordPress/Classes/NotificationsFollowDetailViewController.m +++ b/WordPress/Classes/NotificationsFollowDetailViewController.m @@ -16,6 +16,7 @@ #import "WPWebViewController.h" #import #import "WPAccount.h" +#import "WPToast.h" #import "Note.h" @interface NotificationsFollowDetailViewController () @@ -48,7 +49,7 @@ - (void)viewDidLoad [super viewDidLoad]; if (_note) { - _noteData = [[[_note getNoteData] objectForKey:@"body"] objectForKey:@"items"]; + _noteData = [[[_note noteData] objectForKey:@"body"] objectForKey:@"items"]; } [_postTitleView.layer setMasksToBounds:NO]; @@ -57,7 +58,7 @@ - (void)viewDidLoad [_postTitleView.layer setShadowRadius:2.0f]; [_postTitleView.layer setShadowOpacity:0.3f]; - NSString *headerText = [[[_note getNoteData] objectForKey:@"body"] objectForKey:@"header_text"]; + NSString *headerText = [[[_note noteData] objectForKey:@"body"] objectForKey:@"header_text"]; if (headerText) { UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 40.0f)]; [headerLabel setBackgroundColor:[WPStyleGuide itsEverywhereGrey]]; @@ -68,7 +69,7 @@ - (void)viewDidLoad [self.tableView setTableHeaderView:headerLabel]; [self.view bringSubviewToFront:_postTitleView]; - NSString *headerLink = [[[_note getNoteData] objectForKey:@"body"] objectForKey:@"header_link"]; + NSString *headerLink = [[[_note noteData] objectForKey:@"body"] objectForKey:@"header_link"]; if (headerLink && [headerLink isKindOfClass:[NSString class]]) { NSURL *postURL = [NSURL URLWithString:headerLink]; if (postURL) { @@ -88,7 +89,7 @@ - (void)viewDidLoad } - NSString *footerText = [[[_note getNoteData] objectForKey:@"body"] objectForKey:@"footer_text"]; + NSString *footerText = [[[_note noteData] objectForKey:@"body"] objectForKey:@"footer_text"]; if (footerText && ![footerText isEqualToString:@""]) { _hasFooter = YES; } @@ -211,7 +212,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell.textLabel.textColor = [WPStyleGuide newKidOnTheBlockBlue]; cell.textLabel.font = [WPStyleGuide regularTextFont]; } - NSString *footerText = [[[_note getNoteData] objectForKey:@"body"] objectForKey:@"footer_text"]; + NSString *footerText = [[[_note noteData] objectForKey:@"body"] objectForKey:@"footer_text"]; cell.textLabel.text = footerText; return cell; } @@ -244,7 +245,7 @@ - (void)followBlog:(id)sender { NSUInteger blogID = [[noteDetails objectForKey:@"site_id"] intValue]; if (blogID) { - [[WordPressComApi sharedApi] followBlog:blogID isFollowing:isFollowing success:^(AFHTTPRequestOperation *operation, id responseObject) { + [[[WPAccount defaultWordPressComAccount] restApi] followBlog:blogID isFollowing:isFollowing success:^(AFHTTPRequestOperation *operation, id responseObject) { NSDictionary *followResponse = (NSDictionary *)responseObject; if (followResponse && [[followResponse objectForKey:@"success"] intValue] == 1) { if ([[followResponse objectForKey:@"is_following"] intValue] == 1) { @@ -258,6 +259,11 @@ - (void)followBlog:(id)sender { } else { [cell setFollowing:isFollowing]; } + + NSString *message = isFollowing ? NSLocalizedString(@"Unfollowed", @"User unfollowed a blog") : NSLocalizedString(@"Followed", @"User followed a blog"); + NSString *imageName = [NSString stringWithFormat:@"action_icon_%@", (isFollowing) ? @"unfollowed" : @"followed"]; + [WPToast showToastWithMessage:message andImage:[UIImage imageNamed:imageName]]; + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { [cell setFollowing: isFollowing]; DDLogVerbose(@"[Rest API] ! %@", [error localizedDescription]); @@ -266,7 +272,7 @@ - (void)followBlog:(id)sender { } - (IBAction)viewPostTitle:(id)sender { - [self loadWebViewWithURL:[[[_note getNoteData] objectForKey:@"body"] objectForKey:@"header_link"]]; + [self loadWebViewWithURL:[[[_note noteData] objectForKey:@"body"] objectForKey:@"header_link"]]; } - (void)loadWebViewWithURL: (NSString*)url { @@ -320,7 +326,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath [self.tableView deselectRowAtIndexPath:indexPath animated:NO]; } } else { - [self loadWebViewWithURL:[[[_note getNoteData] objectForKey:@"body"] objectForKey:@"footer_link"]]; + [self loadWebViewWithURL:[[[_note noteData] objectForKey:@"body"] objectForKey:@"footer_link"]]; } } diff --git a/WordPress/Classes/NotificationsManager.h b/WordPress/Classes/NotificationsManager.h index 84c61074b71e..e47f52ae2a01 100644 --- a/WordPress/Classes/NotificationsManager.h +++ b/WordPress/Classes/NotificationsManager.h @@ -7,6 +7,10 @@ * Some rights reserved. See license.txt */ +/** + The persisted device token + */ +extern NSString *const NotificationsDeviceToken; extern NSString *const NotificationsDeviceToken; @@ -40,6 +44,18 @@ extern NSString *const NotificationsDeviceToken; */ + (void)unregisterDeviceToken; +/** + Returns whether the device is currently registered for remote notifications + + @return YES if the device is registered for WordPress.com notifications + @return NO if not + */ ++ (BOOL)deviceRegisteredForPushNotifications; + +///---------------------------- +/// @name Notification Handling +///---------------------------- + /** Handle the notification received, and call the completion handler for background work @@ -57,4 +73,18 @@ extern NSString *const NotificationsDeviceToken; */ + (void)handleNotificationForApplicationLaunch:(NSDictionary *)launchOptions; +///-------------------------------------- +/// @name WordPress.com Notifications API +///-------------------------------------- + +/** + Sends the current settings to WordPress.com + */ ++ (void)saveNotificationSettings; + +/** + Fetch the current WordPress.com settings + */ ++ (void)fetchNotificationSettingsWithSuccess:(void (^)())success failure:(void (^)(NSError *error))failure; + @end diff --git a/WordPress/Classes/NotificationsManager.m b/WordPress/Classes/NotificationsManager.m index 3951ed921fba..17a5be9739d4 100644 --- a/WordPress/Classes/NotificationsManager.m +++ b/WordPress/Classes/NotificationsManager.m @@ -16,6 +16,7 @@ #import #import "ContextManager.h" +static NSString *const NotificationsPreferencesKey = @"notification_preferences"; NSString *const NotificationsDeviceToken = @"apnsDeviceToken"; @implementation NotificationsManager @@ -36,19 +37,19 @@ + (void)registerForPushNotifications { #pragma mark - Device token registration + (void)registerDeviceToken:(NSData *)deviceToken { - NSString *myToken = [[[[deviceToken description] + NSString *newToken = [[[[deviceToken description] stringByReplacingOccurrencesOfString: @"<" withString: @""] stringByReplacingOccurrencesOfString: @">" withString: @""] stringByReplacingOccurrencesOfString: @" " withString: @""]; - DDLogInfo(@"Device token received in didRegisterForRemoteNotificationsWithDeviceToken: %@", myToken); + DDLogInfo(@"Device token received in didRegisterForRemoteNotificationsWithDeviceToken: %@", newToken); // Store the token NSString *previousToken = [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsDeviceToken]; - if (![previousToken isEqualToString:myToken]) { - DDLogInfo(@"Device Token has changed! OLD Value %@, NEW value %@", previousToken, myToken); - [[NSUserDefaults standardUserDefaults] setObject:myToken forKey:NotificationsDeviceToken]; - [[WordPressComApi sharedApi] syncPushNotificationInfo]; + if (![previousToken isEqualToString:newToken]) { + DDLogInfo(@"Device Token has changed! OLD Value %@, NEW value %@", previousToken, newToken); + [[NSUserDefaults standardUserDefaults] setObject:newToken forKey:NotificationsDeviceToken]; + [self syncPushNotificationInfo]; } } @@ -59,37 +60,16 @@ + (void)registrationDidFail:(NSError *)error { + (void)unregisterDeviceToken { NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsDeviceToken]; - if (nil == token) { - return; - } - - if (![[WordPressComApi sharedApi] hasCredentials]) { - return; - } - - WPAccount *account = [WPAccount defaultWordPressComAccount]; - if (account) { - NSArray *parameters = @[account.username, - account.password, - token, - [[UIDevice currentDevice] wordpressIdentifier], - @"apple", - @NO, // Sandbox parameter - deprecated - WordPressComApiPushAppId - ]; - - WPXMLRPCClient *api = [[WPXMLRPCClient alloc] initWithXMLRPCEndpoint:[NSURL URLWithString:WPComXMLRPCUrl]]; - [api setAuthorizationHeaderWithToken:[[WordPressComApi sharedApi] authToken]]; - [api callMethod:@"wpcom.mobile_push_unregister_token" - parameters:parameters - success:^(AFHTTPRequestOperation *operation, id responseObject) { - DDLogInfo(@"Unregistered token %@", token); - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - DDLogError(@"Couldn't unregister token: %@", [error localizedDescription]); - }]; - } + [[[WPAccount defaultWordPressComAccount] restApi] unregisterForPushNotificationsWithDeviceToken:token success:^{ + DDLogInfo(@"Unregistered token %@", token); + } failure:^(NSError *error){ + DDLogError(@"Couldn't unregister token: %@", [error localizedDescription]); + }]; } ++ (BOOL)deviceRegisteredForPushNotifications { + return [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsDeviceToken] != nil; +} #pragma mark - Notification handling @@ -98,10 +78,9 @@ + (void)handleNotification:(NSDictionary*)userInfo forState:(UIApplicationState) switch (state) { case UIApplicationStateActive: - [[WordPressComApi sharedApi] checkForNewUnseenNotifications]; - [[WordPressComApi sharedApi] syncPushNotificationInfo]; + [self syncPushNotificationInfo]; break; - + case UIApplicationStateInactive: [WPMobileStats recordAppOpenedForEvent:StatsEventAppOpenedDueToPushNotification]; [[WordPressAppDelegate sharedWordPressApplicationDelegate] showNotificationsTab]; @@ -112,7 +91,7 @@ + (void)handleNotification:(NSDictionary*)userInfo forState:(UIApplicationState) [[WordPressAppDelegate sharedWordPressApplicationDelegate] showNotificationsTab]; if (completionHandler) { - [Note getNewNotificationswithContext:[[ContextManager sharedInstance] mainContext] success:^(BOOL hasNewNotes) { + [Note fetchNewNotificationsWithSuccess:^(BOOL hasNewNotes) { DDLogInfo(@"notification fetch completion handler completed with new notes: %@", hasNewNotes ? @"YES" : @"NO"); if (hasNewNotes) { completionHandler(UIBackgroundFetchResultNewData); @@ -140,4 +119,92 @@ + (void)handleNotificationForApplicationLaunch:(NSDictionary *)launchOptions { } } + +#pragma mark - WordPress.com XML RPC API + ++ (NSDictionary *)notificationSettingsDictionary { + if (![[[WPAccount defaultWordPressComAccount] restApi] hasCredentials]) { + return nil; + } + + NSDictionary *notificationPreferences = [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsPreferencesKey]; + if (!notificationPreferences) + return nil; + + NSMutableArray *notificationPrefArray = [[notificationPreferences allKeys] mutableCopy]; + if ([notificationPrefArray indexOfObject:@"muted_blogs"] != NSNotFound) { + [notificationPrefArray removeObjectAtIndex:[notificationPrefArray indexOfObject:@"muted_blogs"]]; + } + + // Build the dictionary to send in the API call + NSMutableDictionary *updatedSettings = [[NSMutableDictionary alloc] init]; + for (int i = 0; i < [notificationPrefArray count]; i++) { + NSDictionary *updatedSetting = [notificationPreferences objectForKey:[notificationPrefArray objectAtIndex:i]]; + [updatedSettings setValue:[updatedSetting objectForKey:@"value"] forKey:[notificationPrefArray objectAtIndex:i]]; + } + + //Check and send 'mute_until' value + NSMutableDictionary *muteDictionary = [notificationPreferences objectForKey:@"mute_until"]; + if(muteDictionary != nil && [muteDictionary objectForKey:@"value"] != nil) { + [updatedSettings setValue:[muteDictionary objectForKey:@"value"] forKey:@"mute_until"]; + } else { + [updatedSettings setValue:@"0" forKey:@"mute_until"]; + } + + NSArray *blogsArray = [[notificationPreferences objectForKey:@"muted_blogs"] objectForKey:@"value"]; + NSMutableArray *mutedBlogsArray = [[NSMutableArray alloc] init]; + for (int i=0; i < [blogsArray count]; i++) { + NSDictionary *userBlog = [blogsArray objectAtIndex:i]; + if ([[userBlog objectForKey:@"value"] intValue] == 1) { + [mutedBlogsArray addObject:userBlog]; + } + } + + if ([mutedBlogsArray count] > 0) { + [updatedSettings setValue:mutedBlogsArray forKey:@"muted_blogs"]; + } + + if ([updatedSettings count] == 0) { + return nil; + } + + return updatedSettings; +} + ++ (void)saveNotificationSettings { + NSDictionary *settings = [NotificationsManager notificationSettingsDictionary]; + NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsDeviceToken]; + [[[WPAccount defaultWordPressComAccount] restApi] saveNotificationSettings:settings deviceToken:token success:^{ + DDLogInfo(@"Notification settings successfully sent to WP.com\n Settings: %@", settings); + } failure:^(NSError *error){ + DDLogError(@"Failed to update notification settings on WP.com %@", error.localizedDescription); + }]; +} + ++ (void)fetchNotificationSettingsWithSuccess:(void (^)())success failure:(void (^)(NSError *))failure { + NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsDeviceToken]; + [[[WPAccount defaultWordPressComAccount] restApi] fetchNotificationSettingsWithDeviceToken:token success:^(NSDictionary *settings) { + [[NSUserDefaults standardUserDefaults] setObject:settings forKey:NotificationsPreferencesKey]; + DDLogInfo(@"Received notification settings %@", settings); + if (success) { + success(); + } + } failure:^(NSError *error) { + DDLogError(@"Failed to fetch notification settings %@", error.localizedDescription); + if (failure) { + failure(error); + } + }]; +} + ++ (void)syncPushNotificationInfo { + NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsDeviceToken]; + [[[WPAccount defaultWordPressComAccount] restApi] syncPushNotificationInfoWithDeviceToken:token success:^(NSDictionary *settings) { + [[NSUserDefaults standardUserDefaults] setObject:settings forKey:NotificationsPreferencesKey]; + DDLogInfo(@"Synched push notification token and received settings %@", settings); + } failure:^(NSError *error) { + DDLogError(@"Failed to receive supported notification list: %@", [error localizedDescription]); + }]; +} + @end diff --git a/WordPress/Classes/NotificationsViewController.m b/WordPress/Classes/NotificationsViewController.m index 790ededbd470..ce8c0975907a 100644 --- a/WordPress/Classes/NotificationsViewController.m +++ b/WordPress/Classes/NotificationsViewController.m @@ -28,7 +28,6 @@ @interface NotificationsViewController () { } @property (nonatomic, strong) id authListener; -@property (nonatomic, strong) WordPressComApi *user; @property (nonatomic, assign) BOOL isPushingViewController; @end @@ -40,7 +39,6 @@ - (id)init { self = [super init]; if (self) { self.title = NSLocalizedString(@"Notifications", @"Notifications View Controller title"); - self.user = [WordPressComApi sharedApi]; } return self; } @@ -86,8 +84,7 @@ - (void)didTapNoResultsView:(WPNoResultsView *)noResultsView [self.navigationController pushViewController:webViewController animated:YES]; } -- (BOOL)showJetpackConnectMessage -{ +- (BOOL)showJetpackConnectMessage { return [WPAccount defaultWordPressComAccount] == nil; } @@ -147,7 +144,7 @@ - (void)updateSyncDate { NSArray *notes = self.resultsController.fetchedObjects; if ([notes count] > 0) { Note *note = [notes objectAtIndex:0]; - [self.user updateNoteLastSeenTime:note.timestamp success:nil failure:nil]; + [[[WPAccount defaultWordPressComAccount] restApi] updateNoteLastSeenTime:note.timestamp success:nil failure:nil]; } NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; @@ -220,19 +217,17 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } - [self.user markNoteAsRead:note.noteID success:^(AFHTTPRequestOperation *operation, id responseObject) { - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + [note markAsReadWithSuccess:nil failure:^(NSError *error){ note.unread = [NSNumber numberWithInt:1]; }]; } } - (BOOL)noteHasDetailView:(Note *)note { - if ([note isComment]) return YES; - NSDictionary *noteBody = [[note getNoteData] objectForKey:@"body"]; + NSDictionary *noteBody = [[note noteData] objectForKey:@"body"]; if (noteBody) { NSString *noteTemplate = [noteBody objectForKey:@"template"]; if ([noteTemplate isEqualToString:@"single-line-list"] || [noteTemplate isEqualToString:@"multi-line-list"]) @@ -269,7 +264,7 @@ - (void)configureCell:(NewNotificationsTableViewCell *)cell atIndexPath:(NSIndex } - (BOOL)userCanRefresh { - return self.user != nil; + return [WPAccount defaultWordPressComAccount] != nil; } - (void)syncItemsViaUserInteraction:(BOOL)userInteraction success:(void (^)())success failure:(void (^)(NSError *error))failure { @@ -286,16 +281,12 @@ - (void)syncItemsViaUserInteraction:(BOOL)userInteraction success:(void (^)())su timestamp = nil; } - [self.user getNotificationsSince:timestamp success:^(AFHTTPRequestOperation *operation, id responseObject) { + [Note fetchNotificationsSince:timestamp success:^{ [self updateSyncDate]; if (success) { success(); } - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - if (failure) { - failure(error); - } - }]; + } failure:failure]; } - (BOOL)hasMoreContent { @@ -323,15 +314,13 @@ - (void)loadMoreWithSuccess:(void (^)())success failure:(void (^)(NSError *))fai _retrievingNotifications = YES; - [self.user getNotificationsBefore:lastNote.timestamp success:^(AFHTTPRequestOperation *operation, id responseObject) { + [Note fetchNotificationsBefore:lastNote.timestamp success:^{ _retrievingNotifications = NO; - if (success) { success(); } - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + } failure:^(NSError *error) { _retrievingNotifications = NO; - if (failure) { failure(error); } diff --git a/WordPress/Classes/ReaderCommentPublisher.m b/WordPress/Classes/ReaderCommentPublisher.m index 25c885009946..42776a545495 100644 --- a/WordPress/Classes/ReaderCommentPublisher.m +++ b/WordPress/Classes/ReaderCommentPublisher.m @@ -9,6 +9,7 @@ #import "ReaderPost.h" #import "ReaderComment.h" #import "WPToast.h" +#import "WPAccount.h" @interface ReaderCommentPublisher () @@ -119,7 +120,7 @@ - (void)publishComment:(NSString *)commentText { self.composeView.enabled = NO; NSDictionary *params = @{@"content":str}; - [[WordPressComApi sharedApi] postPath:[self pathForContext] parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { + [[[WPAccount defaultWordPressComAccount] restApi] postPath:[self pathForContext] parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { [self.composeView clearText]; self.composeView.enabled = YES; diff --git a/WordPress/Classes/ReaderPost.m b/WordPress/Classes/ReaderPost.m index 29732e4b7052..33a8d39545f1 100644 --- a/WordPress/Classes/ReaderPost.m +++ b/WordPress/Classes/ReaderPost.m @@ -536,7 +536,7 @@ - (void)toggleLikedWithSuccess:(void (^)())success failure:(void (^)(NSError *er path = [NSString stringWithFormat:@"sites/%@/posts/%@/likes/mine/delete", self.siteID, self.postID]; } - [[WordPressComApi sharedApi] postPath:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { + [[[WPAccount defaultWordPressComAccount] restApi] postPath:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { [self save]; if(success) { @@ -568,7 +568,7 @@ - (void)toggleFollowingWithSuccess:(void (^)())success failure:(void (^)(NSError path = [NSString stringWithFormat:@"sites/%@/follows/mine/delete", self.siteID]; } - [[WordPressComApi sharedApi] postPath:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { + [[[WPAccount defaultWordPressComAccount] restApi] postPath:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { [self save]; if(success) { @@ -593,7 +593,7 @@ - (void)reblogPostToSite:(id)site note:(NSString *)note success:(void (^)())succ } NSString *path = [NSString stringWithFormat:@"sites/%@/posts/%@/reblogs/new", self.siteID, self.postID]; - [[WordPressComApi sharedApi] postPath:path parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { + [[[WPAccount defaultWordPressComAccount] restApi] postPath:path parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { NSDictionary *dict = (NSDictionary *)responseObject; self.isReblogged = [dict numberForKey:@"is_reblogged"]; diff --git a/WordPress/Classes/ReaderPostsViewController.m b/WordPress/Classes/ReaderPostsViewController.m index 6b2674d0b787..cdd11ce4aa73 100644 --- a/WordPress/Classes/ReaderPostsViewController.m +++ b/WordPress/Classes/ReaderPostsViewController.m @@ -148,8 +148,7 @@ - (void)viewDidLoad { } // Sync content as soon as login or creation occurs - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didLogin:) name:WordPressComApiDidLoginNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didLogout:) name:WordPressComApiDidLogoutNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeAccount:) name:WPAccountDefaultWordPressComAccountChangedNotification object:nil]; self.inlineComposeView = [[InlineComposeView alloc] initWithFrame:CGRectZero]; @@ -909,13 +908,13 @@ - (void)readerTopicDidChange:(NSNotification *)notification { } } -- (void)didLogin:(NSNotification *)notification { - [self syncItems]; -} - -- (void)didLogout:(NSNotification *)notification { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:ReaderLastSyncDateKey]; - [NSUserDefaults resetStandardUserDefaults]; +- (void)didChangeAccount:(NSNotification *)notification { + if ([WPAccount defaultWordPressComAccount]) { + [self syncItems]; + } else { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:ReaderLastSyncDateKey]; + [NSUserDefaults resetStandardUserDefaults]; + } } @@ -962,7 +961,7 @@ - (void)fetchBlogsAndPrimaryBlog { [[NSUserDefaults standardUserDefaults] setObject:usersBlogs forKey:@"wpcom_users_blogs"]; - [[WordPressComApi sharedApi] getPath:@"me" + [[[WPAccount defaultWordPressComAccount] restApi] getPath:@"me" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { if ([usersBlogs count] < 1) diff --git a/WordPress/Classes/SettingsViewController.m b/WordPress/Classes/SettingsViewController.m index 378a75e3615c..04eb470a3eb4 100644 --- a/WordPress/Classes/SettingsViewController.m +++ b/WordPress/Classes/SettingsViewController.m @@ -140,9 +140,6 @@ - (void)dismiss { [self dismissViewControllerAnimated:YES completion:nil]; } -- (BOOL)supportsNotifications { - return nil != [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsDeviceToken]; -} #pragma mark - Table view data source @@ -153,14 +150,14 @@ - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // The Sign Out row in Wpcom section can change, so identify it dynamically - (NSInteger)rowForSignOut { NSInteger rowForSignOut = 1; - if ([self supportsNotifications]) { + if ([NotificationsManager deviceRegisteredForPushNotifications]) { rowForSignOut += 1; } return rowForSignOut; } - (NSInteger)rowForNotifications { - if ([self supportsNotifications]) { + if ([NotificationsManager deviceRegisteredForPushNotifications]) { return 1; } return -1; @@ -326,7 +323,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N [self configureCell:cell atIndexPath:indexPath]; BOOL isSignInCell = NO; - if (![[WordPressComApi sharedApi] hasCredentials]) { + if (![[[WPAccount defaultWordPressComAccount] restApi] hasCredentials]) { isSignInCell = indexPath.section == SettingsSectionWpcom && indexPath.row == 0; } diff --git a/WordPress/Classes/StatsWebViewController.m b/WordPress/Classes/StatsWebViewController.m index 8fc9be76ed9f..7089972e108f 100644 --- a/WordPress/Classes/StatsWebViewController.m +++ b/WordPress/Classes/StatsWebViewController.m @@ -7,7 +7,7 @@ #import "StatsWebViewController.h" #import "Blog+Jetpack.h" #import "WordPressAppDelegate.h" -#import "WordPressComApi.h" +#import "WPAccount.h" #import "AFHTTPClient.h" #import "AFHTTPRequestOperation.h" #import "WPWebViewController.h" @@ -29,15 +29,6 @@ @interface StatsWebViewController () { @property (nonatomic, strong) AFHTTPRequestOperation *authRequest; @property (assign) BOOL authed; -+ (NSString *)lastAuthedName; -+ (void)setLastAuthedName:(NSString *)str; -+ (void)handleLogoutNotification:(NSNotification *)notification; - -- (void)clearCookies; -- (void)showAuthFailed; -- (void)showBlogSettings; -- (void)handleRefreshedWithOutValidRequest:(NSNotification *)notification; - @end @implementation StatsWebViewController @@ -71,11 +62,11 @@ + (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)ide } + (void)load { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleLogoutNotification:) name:WordPressComApiDidLogoutNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAccountChangeNotification:) name:WPAccountDefaultWordPressComAccountChangedNotification object:nil]; } -+ (void)handleLogoutNotification:(NSNotification *)notification { - [self setLastAuthedName:nil]; ++ (void)handleAccountChangeNotification:(NSNotification *)notification { + [self setLastAuthedName:[WPAccount defaultWordPressComAccount].username]; } + (NSString *)lastAuthedName { diff --git a/WordPress/Classes/WPAccount.h b/WordPress/Classes/WPAccount.h index 4418c3f54581..e26d8b76f4ce 100644 --- a/WordPress/Classes/WPAccount.h +++ b/WordPress/Classes/WPAccount.h @@ -16,7 +16,6 @@ @class Blog; extern NSString *const WPAccountDefaultWordPressComAccountChangedNotification; -extern NSString *const WPComXMLRPCUrl; @interface WPAccount : NSManagedObject @@ -38,6 +37,11 @@ extern NSString *const WPComXMLRPCUrl; */ @property (nonatomic, retain) NSString *password; +/** + The OAuth2 auth token for WordPress.com accounts + */ +@property (nonatomic, readonly) NSString *authToken; + ///------------------------------------ /// @name Default WordPress.com account ///------------------------------------ diff --git a/WordPress/Classes/WPAccount.m b/WordPress/Classes/WPAccount.m index 25225606097d..98625970ac1f 100644 --- a/WordPress/Classes/WPAccount.m +++ b/WordPress/Classes/WPAccount.m @@ -13,12 +13,15 @@ #import "WordPressComApi.h" #import "SFHFKeychainUtils.h" #import "ContextManager.h" - +#import +#import "NotificationsManager.h" +#import "WordPressComOAuthClient.h" static NSString * const DefaultDotcomAccountDefaultsKey = @"AccountDefaultDotcom"; -static NSString * const OauthTokenServiceName = @"public-api.wordpress.com"; +static NSString * const WordPressDotcomXMLRPCKey = @"https://wordpress.com/xmlrpc.php"; + static WPAccount *__defaultDotcomAccount = nil; -NSString * const WPComXMLRPCUrl = @"https://wordpress.com/xmlrpc.php"; + NSString * const WPAccountDefaultWordPressComAccountChangedNotification = @"WPAccountDefaultWordPressComAccountChangedNotification"; @@ -70,6 +73,8 @@ + (WPAccount *)defaultWordPressComAccount { + (void)setDefaultWordPressComAccount:(WPAccount *)account { NSAssert(account.isWpcom, @"account should be a wordpress.com account"); + NSAssert(account.authToken.length > 0, @"Account should have an authToken for WP.com"); + // Make sure the account is on the main context __defaultDotcomAccount = (WPAccount *)[[[ContextManager sharedInstance] mainContext] existingObjectWithID:account.objectID error:nil]; // When the account object hasn't been saved yet, its objectID is temporary @@ -81,6 +86,10 @@ + (void)setDefaultWordPressComAccount:(WPAccount *)account { [[NSUserDefaults standardUserDefaults] setURL:accountURL forKey:DefaultDotcomAccountDefaultsKey]; [[NSUserDefaults standardUserDefaults] synchronize]; [[NSNotificationCenter defaultCenter] postNotificationName:WPAccountDefaultWordPressComAccountChangedNotification object:account]; + + [SFHFKeychainUtils storeUsername:account.username andPassword:account.authToken forServiceName:WordPressComOAuthKeychainServiceName updateExisting:YES error:nil]; + + [NotificationsManager registerForPushNotifications]; } + (void)removeDefaultWordPressComAccount { @@ -88,30 +97,45 @@ + (void)removeDefaultWordPressComAccount { } + (void)removeDefaultWordPressComAccountWithContext:(NSManagedObjectContext *)context { - WPAccount *defaultAccount = __defaultDotcomAccount; - if (!defaultAccount) { + if (!__defaultDotcomAccount) { return; } + + [NotificationsManager unregisterDeviceToken]; + + [[NSUserDefaults standardUserDefaults] removeObjectForKey:DefaultDotcomAccountDefaultsKey]; + NSManagedObjectID *accountObjectID = __defaultDotcomAccount.objectID; + __defaultDotcomAccount = nil; + [context performBlock:^{ - WPAccount *account = (WPAccount *)[context objectWithID:defaultAccount.objectID]; + WPAccount *account = (WPAccount *)[context objectWithID:accountObjectID]; [context deleteObject:account]; [[ContextManager sharedInstance] saveContext:context]; }]; - __defaultDotcomAccount = nil; } - (void)prepareForDeletion { - // Invoked automatically by the Core Data framework when the receiver is about to be deleted. - if (__defaultDotcomAccount == self) { - [[self restApi] cancelAllHTTPOperationsWithMethod:nil path:nil]; - // FIXME: this is temporary until we move all the cleanup out of WordPressComApi - [[self restApi] signOut]; - __defaultDotcomAccount = nil; - [[NSUserDefaults standardUserDefaults] removeObjectForKey:DefaultDotcomAccountDefaultsKey]; + [[self restApi] cancelAllHTTPOperationsWithMethod:nil path:nil]; + [[self restApi] reset]; + + // Clear keychain entries + NSError *error; + [SFHFKeychainUtils deleteItemForUsername:self.username andServiceName:@"WordPress.com" error:&error]; + [SFHFKeychainUtils deleteItemForUsername:self.username andServiceName:WordPressComOAuthKeychainServiceName error:&error]; + self.password = nil; + self.authToken = nil; + + [WordPressAppDelegate sharedWordPressApplicationDelegate].isWPcomAuthenticated = NO; + + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"wpcom_username_preference"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:WPAccountDefaultWordPressComAccountChangedNotification object:nil]; - } + }); } + #pragma mark - Account creation + (WPAccount *)createOrUpdateWordPressComAccountWithUsername:(NSString *)username password:(NSString *)password authToken:(NSString *)authToken { @@ -119,7 +143,7 @@ + (WPAccount *)createOrUpdateWordPressComAccountWithUsername:(NSString *)usernam } + (WPAccount *)createOrUpdateWordPressComAccountWithUsername:(NSString *)username password:(NSString *)password authToken:(NSString *)authToken context:(NSManagedObjectContext *)context { - WPAccount *account = [self createOrUpdateSelfHostedAccountWithXmlrpc:WPComXMLRPCUrl username:username andPassword:password withContext:context]; + WPAccount *account = [self createOrUpdateSelfHostedAccountWithXmlrpc:WordPressDotcomXMLRPCKey username:username andPassword:password withContext:context]; [account.managedObjectContext performBlockAndWait:^{ account.isWpcom = YES; account.authToken = authToken; @@ -233,19 +257,19 @@ - (void)setPassword:(NSString *)password { } - (NSString *)authToken { - return [SFHFKeychainUtils getPasswordForUsername:self.username andServiceName:OauthTokenServiceName error:nil]; + return [SFHFKeychainUtils getPasswordForUsername:self.username andServiceName:WordPressComOAuthKeychainServiceName error:nil]; } - (void)setAuthToken:(NSString *)authToken { if (authToken) { [SFHFKeychainUtils storeUsername:self.username andPassword:authToken - forServiceName:OauthTokenServiceName + forServiceName:WordPressComOAuthKeychainServiceName updateExisting:YES error:nil]; } else { [SFHFKeychainUtils deleteItemForUsername:self.username - andServiceName:OauthTokenServiceName + andServiceName:WordPressComOAuthKeychainServiceName error:nil]; } } diff --git a/WordPress/Classes/WPMobileStats.m b/WordPress/Classes/WPMobileStats.m index 72e53f343e1b..a665e2527d80 100644 --- a/WordPress/Classes/WPMobileStats.m +++ b/WordPress/Classes/WPMobileStats.m @@ -256,16 +256,17 @@ + (void)initializeStats // Tracking session count will help us isolate users who just installed the app NSUInteger sessionCount = [[[[Mixpanel sharedInstance] currentSuperProperties] objectForKey:@"session_count"] intValue]; sessionCount++; - + + WPAccount *account = [WPAccount defaultWordPressComAccount]; NSDictionary *properties = @{ @"platform": @"iOS", @"session_count": @(sessionCount), - @"connected_to_dotcom": @([[WordPressComApi sharedApi] hasCredentials]), + @"connected_to_dotcom": @(account != nil), @"number_of_blogs" : @([Blog countWithContext:[[ContextManager sharedInstance] mainContext]]) }; [[Mixpanel sharedInstance] registerSuperProperties:properties]; - NSString *username = [[WPAccount defaultWordPressComAccount] username]; - if ([[WordPressComApi sharedApi] hasCredentials] && [username length] > 0) { + NSString *username = account.username; + if (account && [username length] > 0) { [[Mixpanel sharedInstance] identify:username]; [[Mixpanel sharedInstance].people increment:@"Application Opened" by:@(1)]; [[Mixpanel sharedInstance].people set:@{ @"$username": username, @"$first_name" : username }]; @@ -387,7 +388,7 @@ + (void)incrementSuperProperty:(NSString *)property - (BOOL)connectedToWordPressDotCom { - return [[WordPressComApi sharedApi] hasCredentials]; + return [[[WPAccount defaultWordPressComAccount] restApi] hasCredentials]; } - (void)trackEventForSelfHostedAndWPCom:(NSString *)event diff --git a/WordPress/Classes/WPcomLoginViewController.m b/WordPress/Classes/WPcomLoginViewController.m index a0d9fb50d829..ba56f0fc89de 100644 --- a/WordPress/Classes/WPcomLoginViewController.m +++ b/WordPress/Classes/WPcomLoginViewController.m @@ -12,7 +12,7 @@ #import "UITableViewTextFieldCell.h" #import "WPTableViewActivityCell.h" #import "WPAccount.h" -#import "WordPressComApi.h" +#import "WordPressComOAuthClient.h" #import "ReachabilityUtils.h" #import "WPTableViewSectionFooterView.h" @@ -23,7 +23,6 @@ @interface WPcomLoginViewController () { @property (nonatomic, assign) BOOL dismissWhenFinished; @property (nonatomic, strong) NSString *footerText, *buttonText; @property (nonatomic, assign) BOOL isSigningIn; -@property (nonatomic, strong) WordPressComApi *wpComApi; - (void)signIn:(id)sender; @end @@ -32,7 +31,6 @@ @implementation WPcomLoginViewController @synthesize footerText, buttonText, isSigningIn, isCancellable, predefinedUsername; @synthesize delegate; -@synthesize wpComApi = _wpComApi; + (void)presentLoginScreen { UIViewController *rootViewController = [[[UIApplication sharedApplication] keyWindow] rootViewController]; @@ -55,7 +53,6 @@ - (void)viewDidLoad { [WPStyleGuide configureColorsForView:self.view andTableView:self.tableView]; - self.wpComApi = [WordPressComApi sharedApi]; self.footerText = @" "; self.buttonText = NSLocalizedString(@"Sign In", @""); self.navigationItem.title = NSLocalizedString(@"Sign In", @""); @@ -339,25 +336,26 @@ - (void)signIn:(id)sender { } else { isSigningIn = YES; self.footerText = @" "; - [self.wpComApi signInWithUsername:username - password:password - success:^{ - WPAccount *account = [WPAccount createOrUpdateWordPressComAccountWithUsername:username password:password authToken:nil]; - [WPAccount setDefaultWordPressComAccount:account]; - [loginController.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:1]] withRowAnimation:UITableViewRowAnimationNone]; - if (loginController.delegate) { - [loginController.delegate loginController:loginController didAuthenticateWithAccount:account]; - } - if (self.dismissWhenFinished) { - [self dismissViewControllerAnimated:YES completion:nil]; - } - } failure:^(NSError *error) { - DDLogError(@"Login failed with username %@: %@", username, error); - loginController.footerText = NSLocalizedString(@"Sign in failed. Please try again.", @""); - loginController.buttonText = NSLocalizedString(@"Sign In", @""); - loginController.isSigningIn = NO; - [loginController.tableView reloadData]; - }]; + + WordPressComOAuthClient *client = [WordPressComOAuthClient client]; + [client authenticateWithUsername:username + password:password success:^(NSString *authToken) { + WPAccount *account = [WPAccount createOrUpdateWordPressComAccountWithUsername:username password:password authToken:authToken]; + [WPAccount setDefaultWordPressComAccount:account]; + [loginController.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:1]] withRowAnimation:UITableViewRowAnimationNone]; + if (loginController.delegate) { + [loginController.delegate loginController:loginController didAuthenticateWithAccount:account]; + } + if (self.dismissWhenFinished) { + [self dismissViewControllerAnimated:YES completion:nil]; + } + } failure:^(NSError *error) { + DDLogError(@"Login failed with username %@: %@", username, error); + loginController.footerText = NSLocalizedString(@"Sign in failed. Please try again.", @""); + loginController.buttonText = NSLocalizedString(@"Sign In", @""); + loginController.isSigningIn = NO; + [loginController.tableView reloadData]; + }]; } [self.tableView reloadData]; } diff --git a/WordPress/Classes/WordPressAppDelegate.m b/WordPress/Classes/WordPressAppDelegate.m index daeecd75c83f..ed3b70a6f1bf 100644 --- a/WordPress/Classes/WordPressAppDelegate.m +++ b/WordPress/Classes/WordPressAppDelegate.m @@ -252,9 +252,8 @@ - (void)applicationDidBecomeActive:(UIApplication *)application { [WPMobileStats recordAppOpenedForEvent:StatsEventAppOpened]; - // Clear notifications badge and update server + // Clear notifications badge [UIApplication sharedApplication].applicationIconBadgeNumber = 0; - [[WordPressComApi sharedApi] syncPushNotificationInfo]; } - (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder @@ -824,7 +823,7 @@ - (void)setupReachability { - (void)checkWPcomAuthentication { // Temporarily set the is authenticated flag based upon if we have a WP.com OAuth2 token // TODO :: Move this BOOL to a method on the WordPressComApi along with checkWPcomAuthentication - BOOL tempIsAuthenticated = [[WordPressComApi sharedApi] authToken].length > 0; + BOOL tempIsAuthenticated = [[[WPAccount defaultWordPressComAccount] restApi] authToken].length > 0; self.isWPcomAuthenticated = tempIsAuthenticated; NSString *authURL = @"https://wordpress.com/xmlrpc.php"; @@ -832,7 +831,7 @@ - (void)checkWPcomAuthentication { WPAccount *account = [WPAccount defaultWordPressComAccount]; if (account) { WPXMLRPCClient *client = [WPXMLRPCClient clientWithXMLRPCEndpoint:[NSURL URLWithString:authURL]]; - [client setAuthorizationHeaderWithToken:[[WordPressComApi sharedApi] authToken]]; + [client setAuthorizationHeaderWithToken:[[[WPAccount defaultWordPressComAccount] restApi] authToken]]; [client callMethod:@"wp.getUsersBlogs" parameters:[NSArray arrayWithObjects:account.username, account.password, nil] success:^(AFHTTPRequestOperation *operation, id responseObject) { @@ -842,7 +841,7 @@ - (void)checkWPcomAuthentication { if ([error.domain isEqualToString:@"WPXMLRPCFaultError"] || ([error.domain isEqualToString:@"XMLRPC"] && error.code == 403)) { self.isWPcomAuthenticated = NO; - [[WordPressComApi sharedApi] invalidateOAuth2Token]; + [[[WPAccount defaultWordPressComAccount] restApi] invalidateOAuth2Token]; } DDLogError(@"Error authenticating %@ with WordPress.com: %@", account.username, [error description]); diff --git a/WordPress/WordPressApi/WordPressComApi.h b/WordPress/WordPressApi/WordPressComApi.h index 3bf3dd5cf876..5cd3c915bdc7 100644 --- a/WordPress/WordPressApi/WordPressComApi.h +++ b/WordPress/WordPressApi/WordPressComApi.h @@ -9,9 +9,6 @@ #import #import -#define WordPressComApiDidLoginNotification @"WordPressComApiDidLogin" -#define WordPressComApiDidLogoutNotification @"WordPressComApiDidLogout" - typedef void (^WordPressComApiRestSuccessResponseBlock)(AFHTTPRequestOperation *operation, id responseObject); typedef void (^WordPressComApiRestSuccessFailureBlock)(AFHTTPRequestOperation *operation, NSError *error); @@ -35,11 +32,10 @@ extern NSString *const WordPressComApiErrorMessageKey; extern NSString *const WordPressComApiPushAppId; @interface WordPressComApi : AFHTTPClient -@property (nonatomic,readonly,strong) NSString *username; -@property (nonatomic,readonly,strong) NSString *password; +@property (nonatomic, readonly, strong) NSString *username; +@property (nonatomic, readonly, strong) NSString *password; @property (nonatomic, readonly, strong) NSString *authToken; -+ (WordPressComApi *)sharedApi; // DEPRECATED_MSG_ATTRIBUTE("Use [[WPAccount defaultWordPressComAccount] restApi] instead"); /** Returns an API without an associated user @@ -48,15 +44,21 @@ extern NSString *const WordPressComApiPushAppId; + (WordPressComApi *)anonymousApi; - (id)initWithOAuthToken:(NSString *)authToken; +/** + Reset the API instance + + @discussion Clears authorization headers, cookies, + and sets `authToken`, `username`, and `password` to nil. + */ +- (void)reset; + ///------------------------- /// @name Account management ///------------------------- -- (void)signInWithUsername:(NSString *)username password:(NSString *)password success:(void (^)())success failure:(void (^)(NSError *error))failure; -- (void)refreshTokenWithSuccess:(void (^)())success failure:(void (^)(NSError *error))failure; -- (void)signInWithToken:(NSString *)token DEPRECATED_ATTRIBUTE; -- (void)signOut; + - (BOOL)hasCredentials; + // Wipe the OAuth2 token - (void)invalidateOAuth2Token; - (void)validateWPComAccountWithEmail:(NSString *)email andUsername:(NSString *)username andPassword:(NSString *)password success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure; @@ -64,55 +66,51 @@ extern NSString *const WordPressComApiPushAppId; - (void)validateWPComBlogWithUrl:(NSString *)blogUrl andBlogTitle:(NSString *)blogTitle andLanguageId:(NSNumber *)languageId success:(void (^)(id))success failure:(void (^)(NSError *))failure; - (void)createWPComBlogWithUrl:(NSString *)blogUrl andBlogTitle:(NSString *)blogTitle andLanguageId:(NSNumber *)languageId andBlogVisibility:(WordPressComApiBlogVisibility)visibility success:(void (^)(id))success failure:(void (^)(NSError *))failure; -///--------------------------- -/// @name Transitional methods -///--------------------------- - -/** - Reloads `self.username` and `self.password` from the defaults dictionary and keychain - - Since WordPressComApi uses tokens now, this shouldn't be necessary and will be removed in the future - */ -- (void)updateCredentailsFromStore; ///-------------------- /// @name Notifications ///-------------------- -- (void)saveNotificationSettings:(void (^)())success +- (void)saveNotificationSettings:(NSDictionary *)settings + deviceToken:(NSString *)token success:(void (^)())success failure:(void (^)(NSError *error))failure; -- (void)fetchNotificationSettings:(void (^)())success - failure:(void (^)(NSError *error))failure; +- (void)fetchNotificationSettingsWithDeviceToken:(NSString *)token + success:(void (^)(NSDictionary *settings))success + failure:(void (^)(NSError *error))failure; -- (void)syncPushNotificationInfo; +- (void)unregisterForPushNotificationsWithDeviceToken:(NSString *)token + success:(void (^)())success failure:(void (^)(NSError *error))failure; +- (void)syncPushNotificationInfoWithDeviceToken:(NSString *)token + success:(void (^)(NSDictionary *settings))success + failure:(void (^)(NSError *error))failure; /* * Queries the REST Api for unread notes and determines if the user has * seen them using the response's last_seen_time timestamp. * - * If we have unseen notes we post a WordPressComApiUnseenNotesNotification */ -- (void)checkForNewUnseenNotifications; +- (void)fetchNewUnseenNotificationsWithSuccess:(void (^)(NSArray *notes))success + failure:(void (^)(NSError *error))failure; -- (void)checkNotificationsSuccess:(WordPressComApiRestSuccessResponseBlock)success +- (void)fetchRecentNotificationsWithSuccess:(void (^)(NSArray *notes))success failure:(WordPressComApiRestSuccessFailureBlock)failure; -- (void)getNotificationsBefore:(NSNumber *)timestamp - success:(WordPressComApiRestSuccessResponseBlock)success +- (void)fetchNotificationsBefore:(NSNumber *)timestamp + success:(void (^)(NSArray *notes))success failure:(WordPressComApiRestSuccessFailureBlock)failure; -- (void)getNotificationsSince:(NSNumber *)timestamp - success:(WordPressComApiRestSuccessResponseBlock)success +- (void)fetchNotificationsSince:(NSNumber *)timestamp + success:(void (^)(NSArray *notes))success failure:(WordPressComApiRestSuccessFailureBlock)failure; -- (void)getNotificationsWithParameters:(NSDictionary *)parameters - success:(WordPressComApiRestSuccessResponseBlock)success +- (void)fetchNotificationsWithParameters:(NSDictionary *)parameters + success:(void (^)(NSArray *notes))success failure:(WordPressComApiRestSuccessFailureBlock)failure; -- (void)refreshNotifications:(NSArray *)notes +- (void)refreshNotifications:(NSArray *)noteIDs fields:(NSString *)fields - success:(WordPressComApiRestSuccessResponseBlock)success + success:(void (^)(NSArray *notes))success failure:(WordPressComApiRestSuccessFailureBlock)failure; - (void)markNoteAsRead:(NSString *)noteID @@ -123,10 +121,19 @@ extern NSString *const WordPressComApiPushAppId; success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure; -- (void)followBlog:(NSUInteger)blogID isFollowing:(bool)following +///------------- +/// @name Reader +///------------- + +- (void)followBlog:(NSUInteger)blogID isFollowing:(BOOL)following success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure; + +///--------------- +/// @name Comments +///--------------- + - (void)moderateComment:(NSUInteger)blogID forCommentID:(NSUInteger)commentID withStatus:(NSString *)commentStatus success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure; diff --git a/WordPress/WordPressApi/WordPressComApi.m b/WordPress/WordPressApi/WordPressComApi.m index ca7cf3214cd2..4e635577f9fe 100644 --- a/WordPress/WordPressApi/WordPressComApi.m +++ b/WordPress/WordPressApi/WordPressComApi.m @@ -8,28 +8,17 @@ #import "WordPressComApi.h" #import "WordPressComApiCredentials.h" -#import "SFHFKeychainUtils.h" -#import "WordPressAppDelegate.h" -#import "Note.h" #import "NSString+Helpers.h" -#import "WPToast.h" #import #import #import "UIDevice+WordPressIdentifier.h" -#import "WPAccount.h" -#import "ContextManager.h" #import +#import "WordPressAppDelegate.h" #import "NotificationsManager.h" - static NSString *const WordPressComApiClientEndpointURL = @"https://public-api.wordpress.com/rest/v1/"; static NSString *const WordPressComApiOauthBaseUrl = @"https://public-api.wordpress.com/oauth2"; -static NSString *const WordPressComApiOauthServiceName = @"public-api.wordpress.com"; -static NSString *const WordPressComApiOauthRedirectUrl = @"http://wordpress.com/"; -static NSString *const WordPressComApiNotificationFields = @"id,type,unread,body,subject,timestamp"; -static NSString *const WordPressComApiUnseenNotesNotification = @"WordPressComUnseenNotes"; -static NSString *const WordPressComApiNotesUserInfoKey = @"notes"; -static NSString *const WordPressComApiUnseenNoteCountInfoKey = @"note_count"; +NSString *const WordPressComApiNotificationFields = @"id,type,unread,body,subject,timestamp"; static NSString *const WordPressComApiLoginUrl = @"https://wordpress.com/wp-login.php"; static NSString *const WordPressComXMLRPCUrl = @"https://wordpress.com/xmlrpc.php"; NSString *const WordPressComApiErrorDomain = @"com.wordpress.api"; @@ -95,11 +84,6 @@ - (void)clearWpcomCookies; @implementation WordPressComApi -+ (WordPressComApi *)sharedApi { - DDLogWarn(@"Called obsolete [WordPressComApi sharedApi]"); - return [[WPAccount defaultWordPressComAccount] restApi]; -} - + (WordPressComApi *)anonymousApi { static WordPressComApi *_anonymousApi = nil; static dispatch_once_t oncePredicate; @@ -126,112 +110,15 @@ - (id)initWithOAuthToken:(NSString *)authToken { #pragma mark - Account management -- (void)signInWithUsername:(NSString *)username password:(NSString *)password success:(void (^)())success failure:(void (^)(NSError *error))failure { - NSAssert(username != nil, @"username is nil"); - NSAssert(password != nil, @"password is nil"); - if (self.username && ![username isEqualToString:self.username]) { - [self signOut]; // Only one account supported for now - } - self.username = username; - self.password = password; - - void (^successBlock)(AFHTTPRequestOperation *,id) = ^(AFHTTPRequestOperation *operation, id responseObject) { - /* - responseObject should look like: - { - "access_token": "YOUR_API_TOKEN", - "blog_id": "blog id", - "blog_url": "blog url", - "token_type": "bearer" - } - */ - NSString *accessToken; - if ([responseObject respondsToSelector:@selector(objectForKey:)]) { - accessToken = [responseObject objectForKey:@"access_token"]; - } - if (accessToken == nil) { - //FIXME: this error message is crappy. Understand the posible reasons why responseObject is not what we expected and return a proper error - NSString *localizedDescription = NSLocalizedString(@"Error authenticating", @""); - NSError *error = [NSError errorWithDomain:WordPressComApiErrorDomain code:WordPressComApiErrorNoAccessToken userInfo:@{NSLocalizedDescriptionKey: localizedDescription}]; - if (failure) { - failure(error); - } - return; - } - self.authToken = accessToken; - NSError *error = nil; - [SFHFKeychainUtils storeUsername:self.username andPassword:self.password forServiceName:WPComXMLRPCUrl updateExisting:YES error:&error]; - if (error) { - if (failure) { - failure(error); - } - } else { - [[NSUserDefaults standardUserDefaults] setObject:self.username forKey:@"wpcom_username_preference"]; - [[NSUserDefaults standardUserDefaults] synchronize]; - [WordPressAppDelegate sharedWordPressApplicationDelegate].isWPcomAuthenticated = YES; - // [NotificationsManager registerForPushNotifications]; - [[NSNotificationCenter defaultCenter] postNotificationName:WordPressComApiDidLoginNotification object:self.username]; - if (success) success(); - } - }; - AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:WordPressComApiOauthBaseUrl]]; - [client registerHTTPOperationClass:[WPJSONRequestOperation class]]; - [client setDefaultHeader:@"User-Agent" value:[[WordPressAppDelegate sharedWordPressApplicationDelegate] applicationUserAgent]]; - NSDictionary *params = @{ - @"client_id": [WordPressComApi WordPressAppId], - @"redirect_uri": WordPressComApiOauthRedirectUrl, - @"client_secret": [WordPressComApi WordPressAppSecret], - @"grant_type": @"password", - @"username": username, - @"password": password - }; - - [self postPath:@"/oauth2/token" - parameters:params - success:successBlock - failure:^(AFHTTPRequestOperation *operation, NSError *error) { - self.password = nil; - if (operation.response.statusCode != 400) { - [WPError showNetworkingAlertWithError:error]; - } - if (failure) failure(error); - }]; -} - -- (void)refreshTokenWithSuccess:(void (^)())success failure:(void (^)(NSError *error))failure { - if (self.username == nil || self.password == nil) { - return; - } - [self signInWithUsername:self.username password:self.password success:success failure:failure]; -} - -- (void)signInWithToken:(NSString *)token { - self.authToken = token; -} - -- (void)signOut { +- (void)reset { DDLogMethod(); - NSError *error = nil; - -// [NotificationsManager unregisterDeviceToken]; - [SFHFKeychainUtils deleteItemForUsername:self.username andServiceName:@"WordPress.com" error:&error]; - [SFHFKeychainUtils deleteItemForUsername:self.username andServiceName:WPComXMLRPCUrl error:&error]; - - [WordPressAppDelegate sharedWordPressApplicationDelegate].isWPcomAuthenticated = NO; - [[NSUserDefaults standardUserDefaults] removeObjectForKey:NotificationsDeviceToken]; //Remove the token from Preferences, otherwise the token is never sent to the server on the next login - [SFHFKeychainUtils deleteItemForUsername:self.username andServiceName:WordPressComApiOauthServiceName error:&error]; - [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"wpcom_username_preference"]; - [[NSUserDefaults standardUserDefaults] synchronize]; self.authToken = nil; self.username = nil; self.password = nil; - [self clearAuthorizationHeader]; - + [self clearWpcomCookies]; - - // Notify the world - [[NSNotificationCenter defaultCenter] postNotificationName:WordPressComApiDidLogoutNotification object:nil]; + [self clearAuthorizationHeader]; } - (BOOL)hasCredentials { @@ -379,22 +266,6 @@ - (void)createWPComBlogWithUrl:(NSString *)blogUrl andBlogTitle:(NSString *)blog [self postPath:@"sites/new" parameters:params success:successBlock failure:failureBlock]; } - -#pragma mark - Transitional methods - -- (void)updateCredentailsFromStore { - self.username = [[NSUserDefaults standardUserDefaults] objectForKey:@"wpcom_username_preference"]; - NSError *error = nil; - self.password = [SFHFKeychainUtils getPasswordForUsername:self.username - andServiceName:WPComXMLRPCUrl - error:&error]; - [self clearWpcomCookies]; - [[NSNotificationCenter defaultCenter] postNotificationName:WordPressComApiDidLogoutNotification object:nil]; - [WordPressAppDelegate sharedWordPressApplicationDelegate].isWPcomAuthenticated = YES; -// [NotificationsManager registerForPushNotifications]; - [[NSNotificationCenter defaultCenter] postNotificationName:WordPressComApiDidLoginNotification object:self.username]; -} - - (void)clearWpcomCookies { NSArray *wpcomCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; for (NSHTTPCookie *cookie in wpcomCookies) { @@ -407,80 +278,41 @@ - (void)clearWpcomCookies { #pragma mark - Notifications -- (void)saveNotificationSettings:(void (^)())success +- (void)saveNotificationSettings:(NSDictionary *)settings deviceToken:(NSString *)token + success:(void (^)())success failure:(void (^)(NSError *error))failure { - NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsDeviceToken]; - if( nil == token ) return; //no apns token available - - if(![[WordPressComApi sharedApi] hasCredentials]) + if (nil == token) return; - NSDictionary *notificationPreferences = [[NSUserDefaults standardUserDefaults] objectForKey:@"notification_preferences"]; - if (!notificationPreferences) - return; - - NSMutableArray *notificationPrefArray = [[notificationPreferences allKeys] mutableCopy]; - if ([notificationPrefArray indexOfObject:@"muted_blogs"] != NSNotFound) - [notificationPrefArray removeObjectAtIndex:[notificationPrefArray indexOfObject:@"muted_blogs"]]; - - // Build the dictionary to send in the API call - NSMutableDictionary *updatedSettings = [[NSMutableDictionary alloc] init]; - for (int i = 0; i < [notificationPrefArray count]; i++) { - NSDictionary *updatedSetting = [notificationPreferences objectForKey:[notificationPrefArray objectAtIndex:i]]; - [updatedSettings setValue:[updatedSetting objectForKey:@"value"] forKey:[notificationPrefArray objectAtIndex:i]]; - } - - //Check and send 'mute_until' value - NSMutableDictionary *muteDictionary = [notificationPreferences objectForKey:@"mute_until"]; - if(muteDictionary != nil && [muteDictionary objectForKey:@"value"] != nil) { - [updatedSettings setValue:[muteDictionary objectForKey:@"value"] forKey:@"mute_until"]; - } else { - [updatedSettings setValue:@"0" forKey:@"mute_until"]; - } - - NSArray *blogsArray = [[notificationPreferences objectForKey:@"muted_blogs"] objectForKey:@"value"]; - NSMutableArray *mutedBlogsArray = [[NSMutableArray alloc] init]; - for (int i=0; i < [blogsArray count]; i++) { - NSDictionary *userBlog = [blogsArray objectAtIndex:i]; - if ([[userBlog objectForKey:@"value"] intValue] == 1) { - [mutedBlogsArray addObject:userBlog]; - } - } - - if ([mutedBlogsArray count] > 0) - [updatedSettings setValue:mutedBlogsArray forKey:@"muted_blogs"]; - - if ([updatedSettings count] == 0) - return; - NSArray *parameters = @[[self usernameForXmlrpc], [self passwordForXmlrpc], - updatedSettings, + settings, token, @"apple", WordPressComApiPushAppId ]; - WPXMLRPCClient *api = [[WPXMLRPCClient alloc] initWithXMLRPCEndpoint:[NSURL URLWithString:WPComXMLRPCUrl]]; + + WPXMLRPCClient *api = [[WPXMLRPCClient alloc] initWithXMLRPCEndpoint:[NSURL URLWithString:WordPressComXMLRPCUrl]]; [api setAuthorizationHeaderWithToken:self.authToken]; - //Update supported notifications dictionary [api callMethod:@"wpcom.set_mobile_push_notification_settings" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { - // Hooray! - if (success) + if (success) { success(); + } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - if (failure) + if (failure) { failure(error); + } }]; } -- (void)fetchNotificationSettings:(void (^)())success failure:(void (^)(NSError *error))failure { - NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsDeviceToken]; - if( nil == token ) return; //no apns token available +- (void)fetchNotificationSettingsWithDeviceToken:(NSString *)token success:(void (^)(NSDictionary *settings))success failure:(void (^)(NSError *error))failure { + if( nil == token ) + return; - if(![[WordPressComApi sharedApi] hasCredentials]) + if (![self hasCredentials]) return; NSArray *parameters = @[[self usernameForXmlrpc], @@ -490,32 +322,65 @@ - (void)fetchNotificationSettings:(void (^)())success failure:(void (^)(NSError WordPressComApiPushAppId ]; - WPXMLRPCClient *api = [[WPXMLRPCClient alloc] initWithXMLRPCEndpoint:[NSURL URLWithString:WPComXMLRPCUrl]]; + WPXMLRPCClient *api = [[WPXMLRPCClient alloc] initWithXMLRPCEndpoint:[NSURL URLWithString:WordPressComXMLRPCUrl]]; [api setAuthorizationHeaderWithToken:self.authToken]; [api callMethod:@"wpcom.get_mobile_push_notification_settings" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { - NSDictionary *supportedNotifications = (NSDictionary *)responseObject; - [[NSUserDefaults standardUserDefaults] setObject:supportedNotifications forKey:@"notification_preferences"]; - if (success) - success(); + if (success) { + success(responseObject); + } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - if (failure) + if (failure) { failure(error); + } }]; } -- (void)syncPushNotificationInfo { - NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:NotificationsDeviceToken]; - if ( nil == token ) return; //no apns token available - - if (![[WordPressComApi sharedApi] hasCredentials]) { +- (void)unregisterForPushNotificationsWithDeviceToken:(NSString *)token + success:(void (^)())success failure:(void (^)(NSError *error))failure { + if (nil == token) { return; } + + NSArray *parameters = @[[self usernameForXmlrpc], + [self passwordForXmlrpc], + token, + @"apple", + @NO, // Sandbox parameter - deprecated + WordPressComApiPushAppId + ]; + WPXMLRPCClient *api = [[WPXMLRPCClient alloc] initWithXMLRPCEndpoint:[NSURL URLWithString:WordPressComXMLRPCUrl]]; + [api setAuthorizationHeaderWithToken:self.authToken]; + [api callMethod:@"wpcom.mobile_push_unregister_token" + parameters:parameters + success:^(AFHTTPRequestOperation *operation, id responseObject) { + DDLogInfo(@"Unregistered token %@", token); + if (success) { + success(); + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + DDLogError(@"Couldn't unregister token: %@", [error localizedDescription]); + if (failure) { + failure(error); + } + }]; +} + +- (void)syncPushNotificationInfoWithDeviceToken:(NSString *)token + success:(void (^)(NSDictionary *settings))success + failure:(void (^)(NSError *error))failure { + if (nil == token) { + return; + } + + if (![self hasCredentials]) + return; + // Send a multicall for register the token and retrieval of push notification settings NSMutableArray *operations = [NSMutableArray arrayWithCapacity:2]; - WPXMLRPCClient *api = [[WPXMLRPCClient alloc] initWithXMLRPCEndpoint:[NSURL URLWithString:WPComXMLRPCUrl]]; + WPXMLRPCClient *api = [[WPXMLRPCClient alloc] initWithXMLRPCEndpoint:[NSURL URLWithString:WordPressComXMLRPCUrl]]; [api setAuthorizationHeaderWithToken:self.authToken]; @@ -536,9 +401,11 @@ - (void)syncPushNotificationInfo { tokenOptions ]; WPXMLRPCRequest *tokenRequest = [api XMLRPCRequestWithMethod:@"wpcom.mobile_push_register_token" parameters:parameters]; - WPXMLRPCRequestOperation *tokenOperation = [api XMLRPCRequestOperationWithRequest:tokenRequest success:^(AFHTTPRequestOperation *operation, id responseObject) { + WPXMLRPCRequestOperation *tokenOperation = [api XMLRPCRequestOperationWithRequest:tokenRequest success:^(AFHTTPRequestOperation *op, id response){ } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - DDLogError(@"Token registration failed: %@", error); + if (failure) { + failure(error); + } }]; [operations addObject:tokenOperation]; @@ -551,146 +418,131 @@ - (void)syncPushNotificationInfo { ]; WPXMLRPCRequest *settingsRequest = [api XMLRPCRequestWithMethod:@"wpcom.get_mobile_push_notification_settings" parameters:settingsParameters]; WPXMLRPCRequestOperation *settingsOperation = [api XMLRPCRequestOperationWithRequest:settingsRequest success:^(AFHTTPRequestOperation *operation, id responseObject) { - NSDictionary *supportedNotifications = (NSDictionary *)responseObject; - [[NSUserDefaults standardUserDefaults] setObject:supportedNotifications forKey:@"notification_preferences"]; + if (success) { + success(responseObject); + } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - DDLogError(@"Syncing push notification info failed: %@", error); + if (failure) { + failure(error); + } }]; [operations addObject:settingsOperation]; - AFHTTPRequestOperation *combinedOperation = [api combinedHTTPRequestOperationWithOperations:operations success:^(AFHTTPRequestOperation *operation, id responseObject) {} failure:^(AFHTTPRequestOperation *operation, NSError *error) {}]; + AFHTTPRequestOperation *combinedOperation = [api combinedHTTPRequestOperationWithOperations:operations success:nil failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if (failure) { + failure(error); + } + }]; [api enqueueHTTPRequestOperation:combinedOperation]; } -- (void)checkForNewUnseenNotifications { - NSDictionary *params = @{ @"unread":@"true", @"number":@"20", @"num_note_items":@"20", @"fields" : WordPressComApiNotificationFields }; +- (void)fetchNewUnseenNotificationsWithSuccess:(void (^)(NSArray *notes))success failure:(void (^)(NSError *error))failure { + NSDictionary *params = @{@"unread": @"true", + @"number": @"20", + @"num_note_items": @"20", + @"fields": WordPressComApiNotificationFields}; [self getPath:@"notifications" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { - NSNumber *last_seen_time = [responseObject objectForKey:@"last_seen_time"]; + NSNumber *lastSeenTime = [responseObject objectForKey:@"last_seen_time"]; NSArray *notes = [responseObject objectForKey:@"notes"]; if ([notes count] > 0) { NSMutableArray *unseenNotes = [[NSMutableArray alloc] initWithCapacity:[notes count]]; [notes enumerateObjectsUsingBlock:^(id noteData, NSUInteger idx, BOOL *stop) { NSNumber *timestamp = [noteData objectForKey:@"timestamp"]; - if ([timestamp compare:last_seen_time] == NSOrderedDescending) { + if ([timestamp compare:lastSeenTime] == NSOrderedDescending) { [unseenNotes addObject:noteData]; } }]; - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc postNotificationName:WordPressComApiUnseenNotesNotification - object:self - userInfo:@{ - WordPressComApiNotesUserInfoKey : unseenNotes, - WordPressComApiUnseenNoteCountInfoKey : [NSNumber numberWithInteger:[unseenNotes count]] - }]; + + if (success) { + success([NSArray arrayWithArray:unseenNotes]); + } } - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - DDLogError(@"Checking for unseen notes failed: %@", error); + if (failure) { + failure(error); + } }]; } -- (void)checkNotificationsSuccess:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure { - [self getNotificationsBefore:nil success:success failure:failure]; +- (void)fetchRecentNotificationsWithSuccess:(void (^)(NSArray *))success failure:(WordPressComApiRestSuccessFailureBlock)failure { + [self fetchNotificationsWithParameters:nil success:success failure:failure]; } -- (void)getNotificationsSince:(NSNumber *)timestamp success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure { +- (void)fetchNotificationsSince:(NSNumber *)timestamp + success:(void (^)(NSArray *notes))success + failure:(WordPressComApiRestSuccessFailureBlock)failure { NSDictionary *parameters; if (timestamp != nil) { - parameters = @{ @"since" : timestamp }; + parameters = @{@"since": timestamp}; } - [self getNotificationsWithParameters:parameters success:success failure:failure]; - + [self fetchNotificationsWithParameters:parameters success:success failure:failure]; } -- (void)getNotificationsBefore:(NSNumber *)timestamp success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure { +- (void)fetchNotificationsBefore:(NSNumber *)timestamp + success:(void (^)(NSArray *notes))success + failure:(WordPressComApiRestSuccessFailureBlock)failure { NSDictionary *parameters; if (timestamp != nil) { - parameters = @{ @"before" : timestamp }; + parameters = @{@"before": timestamp}; } - [self getNotificationsWithParameters:parameters success:success failure:failure]; + [self fetchNotificationsWithParameters:parameters success:success failure:failure]; } -- (void)getNotificationsWithParameters:(NSDictionary *)parameters success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure { +- (void)fetchNotificationsWithParameters:(NSDictionary *)parameters success:(void (^)(NSArray *notes))success failure:(WordPressComApiRestSuccessFailureBlock)failure { NSMutableDictionary *requestParameters = [NSMutableDictionary dictionaryWithDictionary:parameters]; - [requestParameters setObject:WordPressComApiNotificationFields forKey:@"fields"]; [requestParameters setObject:[NSNumber numberWithInt:20] forKey:@"number"]; [requestParameters setObject:[NSNumber numberWithInt:20] forKey:@"num_note_items"]; - // TODO: Check for unread notifications and notify with the number of unread notifications - [self getPath:@"notifications/" parameters:requestParameters success:^(AFHTTPRequestOperation *operation, id responseObject){ - [Note syncNotesWithResponse:[responseObject objectForKey:@"notes"]]; - if (success != nil ) success( operation, responseObject ); - + if (success) { + success(responseObject[@"notes"]); + } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - if (failure) failure(operation, error); + if (failure) { + failure(operation, error); + } }]; } -- (void)refreshNotifications:(NSArray *)notes fields:(NSString *)fields success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure { - // No notes? Then there's nothing to sync - if ([notes count] == 0) { +- (void)refreshNotifications:(NSArray *)noteIDs fields:(NSString *)fields success:(void (^)(NSArray *notes))success failure:(WordPressComApiRestSuccessFailureBlock)failure { + if ([noteIDs count] == 0) { return; } - NSMutableArray *noteIDs = [[NSMutableArray alloc] initWithCapacity:[notes count]]; - [notes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - [noteIDs addObject:[(Note *)obj noteID]]; - }]; + if (fields == nil) { fields = WordPressComApiNotificationFields; } + NSDictionary *params = @{ - @"fields" : fields, - @"ids" : noteIDs + @"fields": fields, + @"ids": noteIDs }; - NSManagedObjectContext *context = [(Note *)[notes objectAtIndex:0] managedObjectContext]; [self getPath:@"notifications/" parameters:params success:^(AFHTTPRequestOperation *operation, id response){ - NSError *error; - NSArray *notesData = [response objectForKey:@"notes"]; - for (int i=0; i < [notes count]; i++) { - if ([notesData count] > i) { - Note *note = [notes objectAtIndex:i]; - if (![note isDeleted] && [note managedObjectContext]) { - [note syncAttributes:[notesData objectAtIndex:i]]; - } - } + if (success) { + success(response[@"notes"]); } - if(![context save:&error]){ - NSLog(@"Unable to update note: %@", error); - } - if (success != nil) success(operation, response); - } failure:failure ]; + } failure:failure]; } - (void)markNoteAsRead:(NSString *)noteID success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure { - NSDictionary *params = @{ @"counts" : @{ noteID : @"1" } }; - [self postPath:@"notifications/read" parameters:params success:success failure:failure]; - } - (void)updateNoteLastSeenTime:(NSNumber *)timestamp success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure { - [self postPath:@"notifications/seen" parameters:@{ @"time" : timestamp } success:success failure:failure]; - } -- (void)followBlog:(NSUInteger)blogID isFollowing:(bool)following success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure { - +- (void)followBlog:(NSUInteger)blogID isFollowing:(BOOL)following success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure { NSString *followPath = [NSString stringWithFormat: @"sites/%d/follows/new", blogID]; - if (following) + if (following) { followPath = [followPath stringByReplacingOccurrencesOfString:@"new" withString:@"mine/delete"]; - - NSString *message = following ? NSLocalizedString(@"Unfollowed", @"User unfollowed a blog") : NSLocalizedString(@"Followed", @"User followed a blog"); - NSString *imageName = [NSString stringWithFormat:@"action_icon_%@", (following) ? @"unfollowed" : @"followed"]; - [WPToast showToastWithMessage:message andImage:[UIImage imageNamed:imageName]]; - + } [self postPath:followPath parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject){ @@ -700,9 +552,7 @@ - (void)followBlog:(NSUInteger)blogID isFollowing:(bool)following success:(WordP } - (void)moderateComment:(NSUInteger)blogID forCommentID:(NSUInteger)commentID withStatus:(NSString *)commentStatus success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure { - NSString *commentPath = [NSString stringWithFormat: @"sites/%d/comments/%d", blogID, commentID]; - [self postPath:commentPath parameters:@{ @"status" : commentStatus } success:success @@ -710,9 +560,7 @@ - (void)moderateComment:(NSUInteger)blogID forCommentID:(NSUInteger)commentID wi } - (void)replyToComment:(NSUInteger)blogID forCommentID:(NSUInteger)commentID withReply:(NSString *)reply success:(WordPressComApiRestSuccessResponseBlock)success failure:(WordPressComApiRestSuccessFailureBlock)failure { - NSString *replyPath = [NSString stringWithFormat: @"sites/%d/comments/%d/replies/new", blogID, commentID]; - [self postPath:replyPath parameters:@{ @"content" : reply } success:success @@ -738,26 +586,6 @@ - (NSString *)passwordForXmlrpc { } /* HACK ENDS */ -#pragma mark - Oauth methods - -- (void)setAuthToken:(NSString *)authToken { - _authToken = authToken; - NSError *error; - if (_authToken) { - [self setAuthorizationHeaderWithToken:authToken]; - [SFHFKeychainUtils storeUsername:self.username - andPassword:authToken - forServiceName:WordPressComApiOauthServiceName - updateExisting:YES - error:&error]; - } else { - [self clearAuthorizationHeader]; - [SFHFKeychainUtils deleteItemForUsername:self.username - andServiceName:WordPressComApiOauthServiceName - error:&error]; - } -} - - (void)setAuthorizationHeaderWithToken:(NSString *)token { [self setDefaultHeader:@"Authorization" value:[NSString stringWithFormat:@"Bearer %@", token]]; } diff --git a/WordPress/WordPressApi/WordPressComOAuthClient.h b/WordPress/WordPressApi/WordPressComOAuthClient.h index 0134fceebd69..a800ab1864fd 100644 --- a/WordPress/WordPressApi/WordPressComOAuthClient.h +++ b/WordPress/WordPressApi/WordPressComOAuthClient.h @@ -9,6 +9,7 @@ #import "AFHTTPClient.h" extern NSString * const WordPressComOAuthErrorDomain; +extern NSString * const WordPressComOAuthKeychainServiceName; typedef NS_ENUM(NSUInteger, WordPressComOAuthError) { WordPressComOAuthErrorUnknown, diff --git a/WordPress/WordPressApi/WordPressComOAuthClient.m b/WordPress/WordPressApi/WordPressComOAuthClient.m index c4130e734b5b..2fefbef39a6a 100644 --- a/WordPress/WordPressApi/WordPressComOAuthClient.m +++ b/WordPress/WordPressApi/WordPressComOAuthClient.m @@ -10,10 +10,11 @@ #import "WordPressComApiCredentials.h" NSString * const WordPressComOAuthErrorDomain = @"WordPressComOAuthError"; - +NSString * const WordPressComOAuthKeychainServiceName = @"public-api.wordpress.com"; static NSString * const WordPressComOAuthBaseUrl = @"https://public-api.wordpress.com/oauth2"; static NSString * const WordPressComOAuthRedirectUrl = @"http://wordpress.com/"; + @implementation WordPressComOAuthClient + (WordPressComOAuthClient *)client {