diff --git a/WordPress/Classes/AddSiteViewController.h b/WordPress/Classes/AddSiteViewController.h deleted file mode 100644 index b5cf5b53b530..000000000000 --- a/WordPress/Classes/AddSiteViewController.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * AddSiteViewController.h - * - * Copyright (c) 2013 WordPress. All rights reserved. - * - * Licensed under GNU General Public License 2.0. - * Some rights reserved. See license.txt - */ - -#import -#import "EditSiteViewController.h" - -@interface AddSiteViewController : EditSiteViewController - -@end diff --git a/WordPress/Classes/AddSiteViewController.m b/WordPress/Classes/AddSiteViewController.m deleted file mode 100644 index 1c4da5128b44..000000000000 --- a/WordPress/Classes/AddSiteViewController.m +++ /dev/null @@ -1,176 +0,0 @@ -/* - * AddSiteViewController.m - * - * Copyright (c) 2013 WordPress. All rights reserved. - * - * Licensed under GNU General Public License 2.0. - * Some rights reserved. See license.txt - */ - -#import "AddSiteViewController.h" -#import "AddUsersBlogsViewController.h" -#import "WordPressComApi.h" -#import "JetpackSettingsViewController.h" -#import "WPAccount.h" -#import -#import "ContextManager.h" - -@interface EditSiteViewController (PrivateMethods) - -- (void)validationDidFail:(id)wrong; - -@end - -@implementation AddSiteViewController - -CGSize const AddSiteLogoSize = { 320.0, 70.0 }; - -- (void)viewDidLoad { - [super viewDidLoad]; - - [WPStyleGuide configureColorsForView:self.view andTableView:self.tableView]; - - UIImageView *logoImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"logo_wporg"]]; - logoImage.frame = CGRectMake(0.0f, 0.0f, AddSiteLogoSize.width, AddSiteLogoSize.height); - logoImage.autoresizingMask = UIViewAutoresizingFlexibleWidth; - logoImage.contentMode = UIViewContentModeCenter; - self.tableView.tableHeaderView = logoImage; - - self.saveButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Add", @"Add button to add a site from Settings.") style:[WPStyleGuide barButtonStyleForDone] target:self action:@selector(save:)]; - self.navigationItem.rightBarButtonItem = self.saveButton; - - self.navigationItem.title = NSLocalizedString(@"Add Blog", @""); -} - -- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { - return nil; -} - -- (void)validationSuccess:(NSString *)xmlrpc { - DDLogInfo(@"hasSubsites: %@", self.subsites); - - if ([self.subsites count] > 0) { - // If the user has entered the URL of a site they own on a MultiSite install, - // assume they want to add that specific site. - NSDictionary *subsite = nil; - if ([self.subsites count] > 1) { - if (self.blogId) { - subsite = [[self.subsites filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"blogid = %@", self.blogId]] lastObject]; - } - if (!subsite) { - subsite = [[self.subsites filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"xmlrpc = %@", xmlrpc]] lastObject]; - } - } - - if (subsite == nil) { - subsite = [self.subsites objectAtIndex:0]; - } - - if ([self.subsites count] > 1 && [[subsite objectForKey:@"blogid"] isEqualToString:@"1"]) { - [self displayAddUsersBlogsForXmlRpc:xmlrpc]; - } else { - if (self.isSiteDotCom) { - xmlrpc = [subsite objectForKey:@"xmlrpc"]; - } - [self createBlogWithXmlRpc:xmlrpc andBlogDetails:subsite]; - [self synchronizeNewlyAddedBlog]; - } - } else { - NSError *error = [NSError errorWithDomain:@"WordPress" code:0 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Sorry, you credentials were good but you don't seem to have access to any blogs", @"")}]; - [self validationDidFail:error]; - } - [self.navigationItem setHidesBackButton:NO animated:NO]; - self.saveButton.enabled = YES; -} - -- (void)displayAddUsersBlogsForXmlRpc:(NSString *)xmlrpc -{ - WPAccount *account = [WPAccount createOrUpdateSelfHostedAccountWithXmlrpc:xmlrpc username:self.username andPassword:self.password withContext:[[ContextManager sharedInstance] mainContext]]; - - AddUsersBlogsViewController *addUsersBlogsView = [[AddUsersBlogsViewController alloc] initWithAccount:account]; - addUsersBlogsView.isWPcom = NO; - addUsersBlogsView.usersBlogs = self.subsites; - addUsersBlogsView.url = xmlrpc; - addUsersBlogsView.geolocationEnabled = self.geolocationEnabled; - [self.navigationController pushViewController:addUsersBlogsView animated:YES]; -} - -- (void)createBlogWithXmlRpc:(NSString *)xmlrpc andBlogDetails:(NSDictionary *)blogDetails -{ - NSAssert(blogDetails != nil, nil); - - WPAccount *account = [WPAccount createOrUpdateSelfHostedAccountWithXmlrpc:xmlrpc username:self.username andPassword:self.password withContext:[[ContextManager sharedInstance] mainContext]]; - - NSMutableDictionary *newBlog = [NSMutableDictionary dictionaryWithDictionary:blogDetails]; - [newBlog setObject:xmlrpc forKey:@"xmlrpc"]; - - self.blog = [account findOrCreateBlogFromDictionary:newBlog withContext:account.managedObjectContext]; - self.blog.geolocationEnabled = self.geolocationEnabled; - [self.blog dataSave]; -} - -- (void)synchronizeNewlyAddedBlog -{ - void (^successBlock)() = ^{ - [[WordPressComApi sharedApi] syncPushNotificationInfo]; - if (![self.blog isWPcom] && [self.blog hasJetpack]) { - [self connectToJetpack]; - } else { - [self dismiss]; - } - }; - void (^failureBlock)(NSError*) = ^(NSError * error) { - [SVProgressHUD dismiss]; - }; - [SVProgressHUD showWithStatus:NSLocalizedString(@"Reading blog options", @"") maskType:SVProgressHUDMaskTypeBlack]; - [self.blog syncBlogWithSuccess:successBlock failure:failureBlock]; -} - -- (void)connectToJetpack -{ - NSString *wpcomUsername = [[WPAccount defaultWordPressComAccount] username]; - NSString *wpcomPassword = [[WPAccount defaultWordPressComAccount] password]; - if ((wpcomUsername != nil) && (wpcomPassword != nil)) { - // Try with a known WordPress.com username first - [SVProgressHUD showWithStatus:NSLocalizedString(@"Connecting to Jetpack", @"") maskType:SVProgressHUDMaskTypeBlack]; - [self.blog validateJetpackUsername:wpcomUsername - password:wpcomPassword - success:^{ [self dismiss]; } - failure:^(NSError *error) { [self showJetpackAuthentication]; } - ]; - } else { - [self showJetpackAuthentication]; - } -} - -- (void)dismiss { - [SVProgressHUD dismiss]; - if (IS_IPAD) { - [self dismissViewControllerAnimated:YES completion:nil]; - } - else { - [self.navigationController popToRootViewControllerAnimated:YES]; - } - [[NSNotificationCenter defaultCenter] postNotificationName:@"BlogsRefreshNotification" object:nil]; -} - -- (void)showJetpackAuthentication { - [SVProgressHUD dismiss]; - JetpackSettingsViewController *jetpackSettingsViewController = [[JetpackSettingsViewController alloc] initWithBlog:self.blog]; - jetpackSettingsViewController.canBeSkipped = YES; - jetpackSettingsViewController.showFullScreen = YES; - [jetpackSettingsViewController setCompletionBlock:^(BOOL didAuthenticate) { - [self.navigationController dismissViewControllerAnimated:YES completion:^{ - [self dismiss]; - }]; - }]; - [self.navigationController presentViewController:jetpackSettingsViewController animated:YES completion:nil]; -} - -- (BOOL)canEditUsernameAndURL -{ - return YES; -} - -@end - diff --git a/WordPress/Classes/AddUsersBlogsViewController.h b/WordPress/Classes/AddUsersBlogsViewController.h deleted file mode 100644 index 1d7a6f85a6b9..000000000000 --- a/WordPress/Classes/AddUsersBlogsViewController.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * AddUsersBlogsViewController.h - * - * Copyright (c) 2013 WordPress. All rights reserved. - * - * Licensed under GNU General Public License 2.0. - * Some rights reserved. See license.txt - */ - - -@class WPAccount; - -@interface AddUsersBlogsViewController : UIViewController - -@property (nonatomic, strong) NSArray *usersBlogs; -@property (nonatomic, assign) BOOL isWPcom; -@property (nonatomic, strong) NSString *url; -@property (nonatomic, assign) BOOL geolocationEnabled; - -- (AddUsersBlogsViewController *)initWithAccount:(WPAccount *)account; - -@end diff --git a/WordPress/Classes/AddUsersBlogsViewController.m b/WordPress/Classes/AddUsersBlogsViewController.m deleted file mode 100644 index 5880053983e8..000000000000 --- a/WordPress/Classes/AddUsersBlogsViewController.m +++ /dev/null @@ -1,567 +0,0 @@ -/* - * AddUsersBlogsViewController.m - * - * Copyright (c) 2013 WordPress. All rights reserved. - * - * Licensed under GNU General Public License 2.0. - * Some rights reserved. See license.txt - */ - -#import -#import "AddUsersBlogsViewController.h" -#import "CreateWPComBlogViewController.h" -#import "NSString+XMLExtensions.h" -#import "WordPressComApi.h" -#import "ReachabilityUtils.h" -#import "UIImageView+Gravatar.h" -#import "WPAccount.h" -#import "SupportViewController.h" -#import "ContextManager.h" -#import "WPTableViewCell.h" -#import "WordPressAppDelegate.h" -#import "Blog.h" -#import "WPcomLoginViewController.h" - -@interface AddUsersBlogsViewController () - -@property (nonatomic, strong) UIView *noblogsView; -@property (nonatomic, weak) IBOutlet UIToolbar *toolbar; -@property (nonatomic, weak) WPAccount *account; -@property (nonatomic, assign) BOOL hideBackButton, hideSignInButton; -@property (nonatomic, assign) BOOL hasCompletedGetUsersBlogs; -@property (nonatomic, strong) NSMutableArray *selectedBlogs; -@property (nonatomic, weak) IBOutlet UITableView *tableView; -@property (nonatomic, weak) IBOutlet UIBarButtonItem *buttonAddSelected, *buttonSelectAll, *topAddSelectedButton; -@property (nonatomic, strong) UIAlertView *failureAlertView; - -@end - -@implementation AddUsersBlogsViewController - -- (AddUsersBlogsViewController *)initWithAccount:(WPAccount *)account { - self = [super init]; - if (self) { - _account = account; - } - return self; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - _failureAlertView.delegate = nil; -} - -- (void)viewDidLoad { - DDLogInfo(@"%@ %@", self, NSStringFromSelector(_cmd)); - [super viewDidLoad]; - - self.navigationItem.title = NSLocalizedString(@"Select Blogs", @""); - self.selectedBlogs = [NSMutableArray array]; - - [WPStyleGuide configureColorsForView:self.view andTableView:self.tableView]; - - if (IS_IOS7) { - self.toolbar.barTintColor = [WPStyleGuide littleEddieGrey]; - } - NSShadow *shadow = [[NSShadow alloc] init]; - [_buttonSelectAll setTitleTextAttributes:@{ - NSFontAttributeName: [WPStyleGuide regularTextFont], - NSForegroundColorAttributeName : [UIColor whiteColor], - NSShadowAttributeName: shadow} - forState:UIControlStateNormal]; - - [_buttonAddSelected setTitleTextAttributes:@{ - NSFontAttributeName: [WPStyleGuide regularTextFont], - NSForegroundColorAttributeName : [UIColor whiteColor], - NSShadowAttributeName: shadow} - forState:UIControlStateNormal]; - [_buttonAddSelected setTitleTextAttributes:@{ - NSFontAttributeName: [WPStyleGuide regularTextFont], - NSForegroundColorAttributeName : [UIColor grayColor], - NSShadowAttributeName: shadow} - forState:UIControlStateDisabled]; - - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cancelAddWPcomBlogs) - name:@"didCancelWPcomLogin" object:nil]; -} - -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - - [self.navigationController setNavigationBarHidden:NO animated:animated]; - - if (self.hideBackButton) { - [self.navigationItem setHidesBackButton:YES animated:NO]; - } - - if (self.usersBlogs.count == 0) { - _buttonSelectAll.enabled = NO; - } - - if (self.isWPcom && (![WordPressAppDelegate sharedWordPressApplicationDelegate].isWPcomAuthenticated)) { - WPcomLoginViewController *wpComLogin = [[WPcomLoginViewController alloc] initWithStyle:UITableViewStyleGrouped]; - [self.navigationController presentViewController:wpComLogin animated:YES completion:nil]; - } - - if (IS_IPAD) { - UIBarButtonItem *topAddSelectedButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Add Selected", @"") - style:[WPStyleGuide barButtonStyleForDone] - target:self - action:@selector(saveSelectedBlogs:)]; - self.navigationItem.rightBarButtonItem = topAddSelectedButton; - self.topAddSelectedButton = self.navigationItem.rightBarButtonItem; - self.topAddSelectedButton.enabled = NO; - } - - self.buttonAddSelected.title = NSLocalizedString(@"Add Selected", @""); - self.buttonSelectAll.title = NSLocalizedString(@"Select All", @""); - self.buttonAddSelected.enabled = NO; - - [self checkAddSelectedButtonStatus]; -} - -- (void)viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - - if (self.isWPcom) { - if (!self.usersBlogs && ([[NSUserDefaults standardUserDefaults] objectForKey:@"WPcomUsersBlogs"] != nil)) { - self.usersBlogs = [[NSUserDefaults standardUserDefaults] objectForKey:@"WPcomUsersBlogs"]; - } else if (!self.usersBlogs) { - [self refreshBlogs]; - } else if ([self.usersBlogs count] == 0){ - [self refreshBlogs]; //Maybe just returning from creating a blog - [self hideNoBlogsView]; - } - } - else if (!self.usersBlogs) { - [self refreshBlogs]; - } -} - -#pragma mark - Table view data source - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return 1; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return self.usersBlogs.count; -} - -- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { - CGRect footerFrame = CGRectMake(0, 0, self.view.frame.size.width, 50); - UIView *footerView = [[UIView alloc] initWithFrame:footerFrame]; - CGRect footerSpinnerFrame = CGRectMake(0, 26.0f, 20, 20); - CGRect footerTextFrame = CGRectMake(0, 0, self.view.frame.size.width, 20); - if (self.usersBlogs.count == 0 && !self.hasCompletedGetUsersBlogs) { - UIActivityIndicatorView *footerSpinner = [[UIActivityIndicatorView alloc] initWithFrame:footerSpinnerFrame]; - footerSpinner.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; - [footerSpinner startAnimating]; - footerSpinner.center = CGPointMake(self.view.center.x, footerSpinner.center.y); - [footerView addSubview:footerSpinner]; - - UILabel *footerText = [[UILabel alloc] initWithFrame:footerTextFrame]; - footerText.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin; - footerText.textAlignment = NSTextAlignmentCenter; - footerText.backgroundColor = [UIColor clearColor]; - footerText.textColor = [UIColor darkGrayColor]; - footerText.text = NSLocalizedString(@"Loading blogs...", @""); - [footerView addSubview:footerText]; - } else if (self.usersBlogs.count == 0 && self.hasCompletedGetUsersBlogs) { - if (!self.isWPcom) { - UILabel *footerText = [[UILabel alloc] initWithFrame:CGRectMake(110, 0, 200, 20)]; - footerText.backgroundColor = [UIColor clearColor]; - footerText.textColor = [UIColor darkGrayColor]; - footerText.text = NSLocalizedString(@"No blogs found.", @""); - [footerView addSubview:footerText]; - } else { - //User has no blogs at WPCom but has signed in successfully, lets finish and take them to the reader - [[NSNotificationCenter defaultCenter] postNotificationName:@"BlogsRefreshNotification" object:nil]; - } - } - return footerView; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { - if (section == 0 && self.usersBlogs.count == 0) { - return 60; - } else { - return 0; - } -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - static NSString *CellIdentifier = @"Cell"; - - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; - if (cell == nil) { - cell = [[WPTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; - } - - cell.textLabel.textAlignment = NSTextAlignmentLeft; - - NSDictionary *blog = self.usersBlogs[indexPath.row]; - if ([self.selectedBlogs containsObject:[blog valueForKey:@"blogid"]]) { - cell.accessoryType = UITableViewCellAccessoryCheckmark; - } else { - cell.accessoryType = UITableViewCellAccessoryNone; - } - - cell.textLabel.text = [blog valueForKey:@"blogName"]; - if (!cell.textLabel.text || [cell.textLabel.text isEqualToString:@""]) { - cell.textLabel.text = [blog valueForKey:@"url"]; - } - NSURL *blogURL = [NSURL URLWithString:[blog valueForKey:@"url"]]; - [cell.imageView setImageWithBlavatarUrl:[blogURL host] isWPcom:self.isWPcom]; - - if (indexPath.row == 0) { - [self maskImageView:cell.imageView corner:UIRectCornerTopLeft]; - } else if (indexPath.row == ([self.tableView numberOfRowsInSection:indexPath.section] -1)) { - [self maskImageView:cell.imageView corner:UIRectCornerBottomLeft]; - } else { - cell.imageView.layer.mask = nil; - } - [WPStyleGuide configureTableViewCell:cell]; - - return cell; -} - -#pragma mark - -#pragma mark Table view delegate - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - NSDictionary *selectedBlog = self.usersBlogs[indexPath.row]; - if (![self.selectedBlogs containsObject:[selectedBlog valueForKey:@"blogid"]]) { - [self.selectedBlogs addObject:[selectedBlog valueForKey:@"blogid"]]; - } else { - NSInteger indexToRemove = -1; - NSInteger count = 0; - for (NSString *blogID in self.selectedBlogs) { - if ([blogID isEqual:[selectedBlog valueForKey:@"blogid"]]) { - indexToRemove = count; - break; - } - count++; - } - if (indexToRemove > -1) { - [self.selectedBlogs removeObjectAtIndex:indexToRemove]; - } - } - [tableView reloadData]; - - if (self.selectedBlogs.count == self.usersBlogs.count) { - [self selectAllBlogs:self]; - } else if (_selectedBlogs.count == 0) { - [self deselectAllBlogs:self]; - } - - [self checkAddSelectedButtonStatus]; - - [tableView deselectRowAtIndexPath:indexPath animated:YES]; -} - -#pragma mark - -#pragma mark Custom methods - -- (void)maskImageView:(UIImageView *)imageView corner:(UIRectCorner)corner { - CGRect frame = CGRectMake(0.0, 0.0, 43.0, 43.0); - UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:frame - byRoundingCorners:corner cornerRadii:CGSizeMake(7.0f, 7.0f)]; - CAShapeLayer *maskLayer = [CAShapeLayer layer]; - maskLayer.frame = frame; - maskLayer.path = path.CGPath; - imageView.layer.mask = maskLayer; -} - -- (NSArray *)usersBlogs { - return [_usersBlogs filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { - NSNumber *hidden = [evaluatedObject objectForKey:@"hidden"]; - return (!hidden || [hidden boolValue]); - }]]; -} - -- (void)selectAllBlogs:(id)sender { - [self.selectedBlogs removeAllObjects]; - - for (NSDictionary *blog in self.usersBlogs) { - [self.selectedBlogs addObject:[blog valueForKey:@"blogid"]]; - } - [self.tableView reloadData]; - self.buttonSelectAll.title = NSLocalizedString(@"Deselect All", @""); - self.buttonSelectAll.action = @selector(deselectAllBlogs:); - [self checkAddSelectedButtonStatus]; -} - -- (void)deselectAllBlogs:(id)sender { - [self.selectedBlogs removeAllObjects]; - [self.tableView reloadData]; - self.buttonSelectAll.title = NSLocalizedString(@"Select All", @""); - self.buttonSelectAll.action = @selector(selectAllBlogs:); - [self checkAddSelectedButtonStatus]; -} - -- (void)refreshBlogs { - if (![ReachabilityUtils isInternetReachable]) { - __weak AddUsersBlogsViewController *weakSelf = self; - [ReachabilityUtils showAlertNoInternetConnectionWithRetryBlock:^{ - [weakSelf refreshBlogs]; - }]; - self.hasCompletedGetUsersBlogs = YES; - [self.tableView reloadData]; - return; - } - - NSURL *xmlrpc; - NSString *username, *password; - if (self.isWPcom) { - xmlrpc = [NSURL URLWithString:@"https://wordpress.com/xmlrpc.php"]; - WPAccount *account = [WPAccount defaultWordPressComAccount]; - username = account.username; - password = account.password; - } else { - xmlrpc = [NSURL URLWithString:_url]; - username = self.account.username; - password = self.account.password; - } - - WPXMLRPCClient *api = [WPXMLRPCClient clientWithXMLRPCEndpoint:xmlrpc]; - [api callMethod:@"wp.getUsersBlogs" - parameters:[NSArray arrayWithObjects:username, password, nil] - success:^(AFHTTPRequestOperation *operation, id responseObject) { - self.usersBlogs = responseObject; - self.hasCompletedGetUsersBlogs = YES; - if (self.usersBlogs.count > 0) { - self.buttonSelectAll.enabled = YES; - [self.usersBlogs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - NSString *title = [obj valueForKey:@"blogName"]; - title = [title stringByDecodingXMLCharacters]; - [obj setValue:title forKey:@"blogName"]; - }]; - self.usersBlogs = [self.usersBlogs sortedArrayUsingComparator:^(id obj1, id obj2){ - NSString *title1 = [obj1 valueForKey:@"blogName"]; - NSString *title2 = [obj2 valueForKey:@"blogName"]; - return [title1 localizedCaseInsensitiveCompare:title2]; - }]; - - if (self.usersBlogs.count > 1) { - [self hideNoBlogsView]; - [self.tableView reloadData]; - } else { - [self.selectedBlogs removeAllObjects]; - for (NSDictionary *blog in self.usersBlogs) { - [self.selectedBlogs addObject:[blog valueForKey:@"blogid"]]; - } - [self saveSelectedBlogs]; - } - } else { - // User blogs count == 0. Prompt the user to create a blog. - [self showNoBlogsView]; - [self.tableView reloadData]; - - } - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - DDLogError(@"Failed getting user blogs: %@", [error localizedDescription]); - [self hideNoBlogsView]; - self.hasCompletedGetUsersBlogs = YES; - [self.tableView reloadData]; - if (self.failureAlertView == nil) { - self.failureAlertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Sorry, can't log in", @"") - message:[error localizedDescription] - delegate:self - cancelButtonTitle:NSLocalizedString(@"Need Help?", @"") - otherButtonTitles:NSLocalizedString(@"OK", @""), nil]; - self.failureAlertView.tag = 1; - [self.failureAlertView show]; - } - }]; -} - -- (UIView *)noblogsView { - if (_noblogsView) { - return _noblogsView; - } - - CGFloat width = 282.0f; - CGFloat height = 160.0f; - CGFloat x = (self.view.frame.size.width / 2.0f) - (width / 2.0f); - CGFloat y = (self.view.frame.size.height / 2.0f) - (height / 2.0f); - _noblogsView = [[UIView alloc] initWithFrame:CGRectMake(x, y, width, height)]; - _noblogsView.backgroundColor = [UIColor clearColor]; - - _noblogsView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | - UIViewAutoresizingFlexibleRightMargin | - UIViewAutoresizingFlexibleTopMargin | - UIViewAutoresizingFlexibleBottomMargin; - - UIColor *textColor = [UIColor colorWithRed:33.0f/255.0f green:33.0f/255.0f blue:33.0f/255.0f alpha:1.0]; - UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero]; - label.backgroundColor = [UIColor clearColor]; - label.numberOfLines = 0; - label.lineBreakMode = NSLineBreakByWordWrapping; - label.font = [UIFont fontWithName:@"Georgia" size:16.0f]; - label.shadowOffset = CGSizeMake(0.0f, 1.0f); - label.textColor = textColor; - label.shadowColor = [UIColor whiteColor]; - label.textAlignment = NSTextAlignmentCenter; - - if ([[WPAccount defaultWordPressComAccount] username]) { - label.text = NSLocalizedString(@"You do not seem to have any blogs. Would you like to create one now?", @""); - } else { - label.text = NSLocalizedString(@"You do not seem to have any blogs.", @""); - } - - label.frame = CGRectMake(0.0, 0.0, width, 38.0); - [_noblogsView addSubview:label]; - - if ([[WPAccount defaultWordPressComAccount] username]) { - width = 282.0f; - height = 44.0f; - x = (_noblogsView.frame.size.width / 2.0f) - (width / 2.0f); - y = label.frame.size.height + 10.0f; - - UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; - button.frame = CGRectMake(x, y, width, height); - button.titleLabel.font = [UIFont fontWithName:@"Helvetica-Bold" size:15.0]; - [button setTitleColor:textColor forState:UIControlStateNormal]; - [button setTitleShadowColor:[UIColor whiteColor] forState:UIControlStateNormal]; - button.titleLabel.shadowOffset = CGSizeMake(0.0f, 1.0f); - [button setImage:[UIImage imageNamed:@"welcome_button_asterisk.png"] forState:UIControlStateNormal]; - [button setContentEdgeInsets:UIEdgeInsetsMake(0.0f, 15.0f, 0.0f, 0.0f)]; - [button setTitleEdgeInsets:UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 0.0f)]; - [button setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft]; - [button setBackgroundImage:[UIImage imageNamed:@"welcome_button_bg_full"] forState:UIControlStateNormal]; - [button setBackgroundImage:[UIImage imageNamed:@"welcome_button_bg_full_highlighted.png"] forState:UIControlStateHighlighted]; - [button setTitle:NSLocalizedString(@"Create WordPress.com Blog", @"") forState:UIControlStateNormal]; - [button addTarget:self action:@selector(handleCreateBlogTapped:) forControlEvents:UIControlEventTouchUpInside]; - - [_noblogsView addSubview:button]; - } - return _noblogsView; -} - -- (void)showNoBlogsView { - [self.view addSubview:self.noblogsView]; - - self.buttonSelectAll.enabled = NO; - self.noblogsView.alpha = 0.0; - self.noblogsView.hidden = NO; - _hideSignInButton = YES; - [self.tableView reloadData]; - - [UIView animateWithDuration:0.3f animations:^{ - self.noblogsView.alpha = 1.0f; - }]; -} - -- (void)hideNoBlogsView { - _hideSignInButton = NO; - [self.tableView reloadData]; - - if (!self.noblogsView) { - return; - } - - self.noblogsView.hidden = YES; - self.buttonSelectAll.enabled = YES; -} - -- (void)handleCreateBlogTapped:(id)sender { - CreateWPComBlogViewController *viewController = [[CreateWPComBlogViewController alloc] initWithStyle:UITableViewStyleGrouped]; - viewController.delegate = self; - [self.navigationController pushViewController:viewController animated:YES]; -} - -#pragma mark - UIAlertViewDelegate - -- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { - if (buttonIndex == 0) { - SupportViewController *supportViewController = [[SupportViewController alloc] init]; - - if (IS_IPAD) { - [self.navigationController pushViewController:supportViewController animated:YES]; - } else { - UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:supportViewController]; - navController.modalPresentationStyle = UIModalPresentationFormSheet; - navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical; - navController.navigationBar.translucent = NO; - [self presentViewController:navController animated:YES completion:nil]; - } - } - - if (self.failureAlertView == alertView) { - self.failureAlertView = nil; - } -} - -- (IBAction)saveSelectedBlogs:(id)sender { - [self saveSelectedBlogs]; -} - -- (void)saveSelectedBlogs { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"refreshCommentsRequired"]; - - NSManagedObjectContext *backgroundMOC = [[ContextManager sharedInstance] backgroundContext]; - [backgroundMOC performBlock:^{ - for (NSDictionary *blog in self.usersBlogs) { - if ([self.selectedBlogs containsObject:[blog valueForKey:@"blogid"]]) { - [self createBlog:blog withContext:backgroundMOC]; - } - } - - [[ContextManager sharedInstance] saveContext:backgroundMOC]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [self.navigationController popToRootViewControllerAnimated:YES]; - [[WordPressComApi sharedApi] syncPushNotificationInfo]; - }); - }]; -} - -- (void)createBlog:(NSDictionary *)blogInfo withContext:(NSManagedObjectContext *)context { - DDLogInfo(@"creating blog: %@", blogInfo); - - Blog *blog = [_account findOrCreateBlogFromDictionary:blogInfo withContext:context]; - blog.geolocationEnabled = self.geolocationEnabled; - - [blog syncBlogWithSuccess:nil failure:nil]; -} - -- (void)checkAddSelectedButtonStatus { - //disable the 'Add Selected' button if they have selected 0 blogs, trac #521 - if (self.selectedBlogs.count == 0) { - self.buttonAddSelected.enabled = NO; - if (IS_IPAD) { - self.topAddSelectedButton.enabled = NO; - } - // iOS 7 Beta 6 doesn't seem to be respecting the title text attributes for UIControlStateDisabled - // so we have to engage in this hack until apple fixes it. - [self.buttonAddSelected setTitleTextAttributes:@{ - NSFontAttributeName: [WPStyleGuide regularTextFont], - NSForegroundColorAttributeName : [WPStyleGuide whisperGrey ], - NSShadowAttributeName: [[NSShadow alloc] init]} - forState:UIControlStateNormal]; - } else { - self.buttonAddSelected.enabled = YES; - if (IS_IPAD) { - self.topAddSelectedButton.enabled = YES; - } - // iOS 7 Beta 6 doesn't seem to be respecting the title text attributes for UIControlStateDisabled - // so we have to engage in this hack until apple fixes it. - [self.buttonAddSelected setTitleTextAttributes:@{ - NSFontAttributeName: [WPStyleGuide regularTextFont], - NSForegroundColorAttributeName : [UIColor whiteColor], - NSShadowAttributeName: [[NSShadow alloc] init]} - forState:UIControlStateNormal]; - } - -} - -#pragma mark - CreateWPComBlogViewControllerDelegate - -- (void)createdBlogWithDetails:(NSDictionary *)blogDetails -{ - [self.navigationController popToRootViewControllerAnimated:YES]; -} - -@end diff --git a/WordPress/Classes/Blog+Jetpack.m b/WordPress/Classes/Blog+Jetpack.m index 91c44a546cce..0927d2786136 100644 --- a/WordPress/Classes/Blog+Jetpack.m +++ b/WordPress/Classes/Blog+Jetpack.m @@ -130,7 +130,7 @@ - (void)removeJetpackCredentials { - (void)saveJetpackUsername:(NSString *)username andPassword:(NSString *)password { NSAssert(![self isWPcom], @"Blog+Jetpack doesn't support WordPress.com blogs"); - WPAccount *account = [WPAccount createOrUpdateWordPressComAccountWithUsername:username andPassword:password withContext:self.managedObjectContext]; + WPAccount *account = [WPAccount createOrUpdateWordPressComAccountWithUsername:username password:password authToken:nil context:self.managedObjectContext]; self.jetpackAccount = account; } diff --git a/WordPress/Classes/Blog.h b/WordPress/Classes/Blog.h index 0c725eab64ee..5ff0f3d738ba 100644 --- a/WordPress/Classes/Blog.h +++ b/WordPress/Classes/Blog.h @@ -12,8 +12,6 @@ #import "Reachability.h" -#define BlogChangedNotification @"BlogChangedNotification" - @class WPAccount; @interface Blog : NSManagedObject @@ -21,7 +19,7 @@ @property (nonatomic, strong) NSNumber *blogID; @property (nonatomic, strong) NSString *blogName, *xmlrpc, *apiKey; @property (weak, readonly) NSString *blavatarUrl; -@property (nonatomic, strong) NSNumber *isAdmin, *hasOlderPosts, *hasOlderPages; +@property (nonatomic, strong) NSNumber *hasOlderPosts, *hasOlderPages; @property (nonatomic, strong) NSSet *posts; @property (nonatomic, strong) NSSet *categories; @property (nonatomic, strong) NSSet *comments; @@ -34,8 +32,9 @@ @property (nonatomic, strong) NSDate *lastStatsSync; @property (nonatomic, strong) NSString *lastUpdateWarning; @property (nonatomic, assign) BOOL geolocationEnabled; +@property (nonatomic, assign) BOOL visible; @property (nonatomic, weak) NSNumber *isActivated; -@property (nonatomic, strong) NSDictionary *options; //we can store an NSArray or an NSDictionary as a transformable attribute... +@property (nonatomic, strong) NSDictionary *options; //we can store an NSArray or an NSDictionary as a transformable attribute... @property (nonatomic, strong) NSDictionary *postFormats; @property (nonatomic, strong) WPAccount *account; @property (nonatomic, strong) WPAccount *jetpackAccount; diff --git a/WordPress/Classes/Blog.m b/WordPress/Classes/Blog.m index 68475aebee14..5ec7e7f41f7f 100644 --- a/WordPress/Classes/Blog.m +++ b/WordPress/Classes/Blog.m @@ -33,11 +33,11 @@ @implementation Blog { } @dynamic blogID, blogName, url, xmlrpc, apiKey; -@dynamic isAdmin, hasOlderPosts, hasOlderPages; +@dynamic hasOlderPosts, hasOlderPages; @dynamic posts, categories, comments; @dynamic lastPostsSync, lastStatsSync, lastPagesSync, lastCommentsSync, lastUpdateWarning; @synthesize isSyncingPosts, isSyncingPages, isSyncingComments; -@dynamic geolocationEnabled, options, postFormats, isActivated; +@dynamic geolocationEnabled, options, postFormats, isActivated, visible; @dynamic account; @dynamic jetpackAccount; @@ -222,11 +222,7 @@ - (NSArray *)sortedPostFormatNames { } - (BOOL)isWPcom { - if ([[self getOptionValue:@"wordpress.com"] boolValue]) { - return YES; - } - NSRange range = [self.xmlrpc rangeOfString:@"wordpress.com"]; - return (range.location != NSNotFound); + return self.account.isWpcom; } //WP.COM private blog. diff --git a/WordPress/Classes/BlogListViewController.m b/WordPress/Classes/BlogListViewController.m index 9f06959f39ae..3f98b2078d68 100644 --- a/WordPress/Classes/BlogListViewController.m +++ b/WordPress/Classes/BlogListViewController.m @@ -11,11 +11,12 @@ #import "UIImageView+Gravatar.h" #import "WordPressComApi.h" #import "SettingsViewController.h" -#import "WelcomeViewController.h" +#import "LoginViewController.h" #import "BlogDetailsViewController.h" #import "WPTableViewCell.h" #import "ContextManager.h" #import "Blog.h" +#import "WPAccount.h" CGFloat const blavatarImageSize = 50.f; NSString * const WPBlogListRestorationID = @"WPBlogListID"; @@ -23,7 +24,7 @@ @interface BlogListViewController () @property (nonatomic, strong) NSFetchedResultsController *resultsController; @property (nonatomic, strong) UIBarButtonItem *settingsButton; - +@property (nonatomic) BOOL sectionDeletedByController; @end @implementation BlogListViewController @@ -69,7 +70,6 @@ - (NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier i - (void)viewDidLoad { [super viewDidLoad]; - self.navigationItem.leftBarButtonItem = self.editButtonItem; self.settingsButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Settings", nil) style:UIBarButtonItemStylePlain target:self @@ -91,22 +91,17 @@ - (void)viewDidLoad { } [WPStyleGuide configureColorsForView:self.view andTableView:self.tableView]; - - // Uncomment the following line to preserve selection between presentations. - // self.clearsSelectionOnViewWillAppear = NO; - // Uncomment the following line to display an Edit button in the navigation bar for this view controller. - // self.navigationItem.rightBarButtonItem = self.editButtonItem; + self.navigationItem.leftBarButtonItem = self.editButtonItem; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; + [self.navigationController setNavigationBarHidden:NO animated:animated]; self.resultsController.delegate = self; [self.resultsController performFetch:nil]; [self.tableView reloadData]; - - self.editButtonItem.enabled = ([self numSites] > 0); } - (void)viewWillDisappear:(BOOL)animated { @@ -118,6 +113,9 @@ - (NSUInteger)numSites { return [[self.resultsController fetchedObjects] count]; } +- (BOOL)hasDotComAndSelfHosted { + return ([[self.resultsController sections] count] > 1); +} #pragma mark - Actions @@ -137,17 +135,16 @@ - (void)showSettings:(id)sender { #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return 1; + // Adding an extra section for "Add Site" + return [self.resultsController.sections count] + 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - NSInteger numRows = [self numSites]; - - // Plus an extra row for 'Add a Site' (when not editing) - if (![tableView isEditing]) - numRows += 1; - - return numRows; + if (section == [self sectionForAddSite]) { + return tableView.isEditing ? 0 : 1; + } + id sectionInfo = [[self.resultsController sections] objectAtIndex:section]; + return sectionInfo.numberOfObjects; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath @@ -164,54 +161,88 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N return cell; } +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + if (![self hasDotComAndSelfHosted]) { + return nil; + } + + if (section < [self sectionForAddSite]) { + return [[self.resultsController sectionIndexTitles] objectAtIndex:section]; + } + + return nil; +} + - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath { return NSLocalizedString(@"Remove", @"Button label when removing a blog"); } - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { - return indexPath.row != [self rowForAddSite]; + return indexPath.section == [self sectionForSelfHosted]; } -- (void)setEditing:(BOOL)editing animated:(BOOL)animated { - [super setEditing:editing animated:animated]; +- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { - // And the "Add a Site" row too - NSArray *rowsToChange = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:[self rowForAddSite] inSection:0]]; - - UITableViewRowAnimation rowAnimation = animated ? UITableViewRowAnimationFade : UITableViewRowAnimationNone; - [self.tableView beginUpdates]; - if (editing) { - [self.tableView deleteRowsAtIndexPaths:rowsToChange withRowAnimation:rowAnimation]; - [self.navigationItem setRightBarButtonItem:nil animated:animated]; + if (indexPath.section == [self sectionForSelfHosted] && tableView.editing ) { + return UITableViewCellEditingStyleDelete; } else { - [self.tableView insertRowsAtIndexPaths:rowsToChange withRowAnimation:rowAnimation]; - [self.navigationItem setRightBarButtonItem:self.settingsButton animated:animated]; + return UITableViewCellEditingStyleNone; } - [self.tableView endUpdates]; } - - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { [WPMobileStats trackEventForWPCom:StatsEventSettingsRemovedBlog]; - Blog *blog = [self.resultsController objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]]; - [blog remove]; + Blog *blog = [self.resultsController objectAtIndexPath:indexPath]; + if (blog.isWPcom) { + DDLogWarn(@"Tried to remove a WordPress.com blog. This shouldn't happen but just in case, let's hide it"); + blog.visible = NO; + [blog dataSave]; + } else { + [blog remove]; + } // Count won't be updated yet; if this is the last site (count 1), exit editing mode if ([self numSites] == 1) { // Update the UI in the next run loop after the resultsController has updated // (otherwise row insertion/deletion logic won't work) dispatch_async(dispatch_get_main_queue(), ^{ - self.editButtonItem.enabled = NO; - [self setEditing:NO animated:YES]; + [self setEditing:NO animated:NO]; + + // No blogs and signed out, show NUX + if (![WPAccount defaultWordPressComAccount]) { + [[WordPressAppDelegate sharedWordPressApplicationDelegate] showWelcomeScreenIfNeededAnimated:YES]; + } }); } } } -- (NSInteger)rowForAddSite { - return [self numSites]; +- (NSInteger)sectionForAddSite { + return [self.resultsController.sections count]; +} + +- (NSInteger)sectionForDotCom { + + id sectionInfo = [[self.resultsController sections] objectAtIndex:0]; + if ([[sectionInfo name] isEqualToString:@"1"]) { + return 0; + } else { + return -1; + } +} + +- (NSInteger)sectionForSelfHosted { + + NSInteger sectionsCount = [self.resultsController sections].count; + if (sectionsCount > 1) { + return 1; + } else if (sectionsCount > 0 && [[[[self.resultsController sections] objectAtIndex:0] name] isEqualToString:@"0"]) { + return 0; + } else { + return -1; + } } - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { @@ -219,49 +250,54 @@ - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPa cell.accessoryType = UITableViewCellAccessoryNone; cell.accessoryView = nil; - if (indexPath.row < [self rowForAddSite]) { - Blog *blog = [self.resultsController objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]]; - if ([blog.blogName length] != 0) { - cell.textLabel.text = blog.blogName; - } else { - cell.textLabel.text = blog.url; - } - - [cell.imageView setImageWithBlavatarUrl:blog.blavatarUrl isWPcom:blog.isWPcom]; - cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; - -// if (indexPath.row == 0) { -// [self maskImageView:cell.imageView corner:UIRectCornerTopLeft]; -// } else if (indexPath.row == ([self.tableView numberOfRowsInSection:indexPath.section] -1)) { -// [self maskImageView:cell.imageView corner:UIRectCornerBottomLeft]; -// } else { -// cell.imageView.layer.mask = NULL; -// } - } else { + if (indexPath.section == [self sectionForAddSite]) { cell.textLabel.text = NSLocalizedString(@"Add a Site", @""); cell.selectionStyle = UITableViewCellSelectionStyleBlue; - cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; - + // To align the label, create and add a blank image UIGraphicsBeginImageContextWithOptions(CGSizeMake(blavatarImageSize, blavatarImageSize), NO, 0.0); UIImage *blank = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [cell.imageView setImage:blank]; + } else { + + Blog *blog = [self.resultsController objectAtIndexPath:indexPath]; + if ([blog.blogName length] != 0) { + cell.textLabel.text = blog.blogName; + } else { + cell.textLabel.text = blog.url; + } + + [cell.imageView setImageWithBlavatarUrl:blog.blavatarUrl isWPcom:blog.isWPcom]; + if ([self.tableView isEditing] && blog.isWPcom) { + UISwitch *visibilitySwitch = [UISwitch new]; + visibilitySwitch.on = blog.visible; + visibilitySwitch.tag = indexPath.row; + [visibilitySwitch addTarget:self action:@selector(visibilitySwitchAction:) forControlEvents:UIControlEventValueChanged]; + cell.accessoryView = visibilitySwitch; + + // Make textLabel light gray if blog is not-visible + if (!visibilitySwitch.on) { + [cell.textLabel setTextColor:[WPStyleGuide readGrey]]; + } + + } else { + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + } } } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { - // No top margin on iPhone - if (IS_IPHONE) - return 1; - - return 40; + return [self hasDotComAndSelfHosted] ? 40.0 : 20.0; +} +- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { + return section == [self sectionForAddSite] ? UITableViewAutomaticDimension : 1.0; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; - if (indexPath.row < [self rowForAddSite]) { + if (indexPath.section < [self sectionForAddSite]) { [WPMobileStats trackEventForWPCom:StatsEventSettingsClickedEditBlog]; Blog *blog = [self.resultsController objectAtIndexPath:indexPath]; @@ -271,10 +307,17 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath [self.navigationController pushViewController:blogDetailsViewController animated:YES]; } else { [WPMobileStats trackEventForWPCom:StatsEventSettingsClickedAddBlog]; - - WelcomeViewController *welcomeViewController = [[WelcomeViewController alloc] initWithStyle:UITableViewStyleGrouped]; - welcomeViewController.title = NSLocalizedString(@"Add a Site", nil); - [self.navigationController pushViewController:welcomeViewController animated:YES]; + + LoginViewController *loginViewController = [[LoginViewController alloc] init]; + if (![WPAccount defaultWordPressComAccount]) { + + loginViewController.prefersSelfHosted = YES; + } + loginViewController.dismissBlock = ^{ + [self dismissViewControllerAnimated:YES completion:nil]; + }; + UINavigationController *loginNavigationController = [[UINavigationController alloc] initWithRootViewController:loginViewController]; + [self presentViewController:loginNavigationController animated:YES completion:nil]; } } @@ -282,6 +325,38 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa return 54; } +- (void)setEditing:(BOOL)editing animated:(BOOL)animated { + [super setEditing:editing animated:animated]; + + // Animate view to editing mode + __block UIView *snapshot; + if (animated) { + snapshot = [self.view snapshotViewAfterScreenUpdates:NO]; + snapshot.frame = [self.view convertRect:self.view.frame fromView:self.view.superview]; + [self.view addSubview:snapshot]; + } + + // Update results controller to show hidden blogs + [self updateFetchRequest]; + + if (animated) { + [UIView animateWithDuration:0.2 animations:^{ + snapshot.alpha = 0.0; + } completion:^(BOOL finished) { + [snapshot removeFromSuperview]; + snapshot = nil; + }]; + } +} + +- (void)visibilitySwitchAction:(id)sender { + UISwitch *switcher = (UISwitch *)sender; + Blog *blog = [self.resultsController objectAtIndexPath:[NSIndexPath indexPathForRow:switcher.tag inSection:0]]; + if (switcher.on != blog.visible) { + blog.visible = switcher.on; + [blog dataSave]; + } +} #pragma mark - #pragma mark NSFetchedResultsController @@ -293,14 +368,13 @@ - (NSFetchedResultsController *)resultsController { NSManagedObjectContext *moc = [[ContextManager sharedInstance] mainContext]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Blog"]; - fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"blogName" ascending:YES]]; + [fetchRequest setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"account.isWpcom" ascending:NO], [NSSortDescriptor sortDescriptorWithKey:@"blogName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]]]; + [fetchRequest setPredicate:[self fetchRequestPredicate]]; - // For some reasons, the cache sometimes gets corrupted - // Since we don't really use sections we skip the cache here _resultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc - sectionNameKeyPath:nil + sectionNameKeyPath:@"isWPcom" cacheName:nil]; _resultsController.delegate = self; @@ -312,12 +386,48 @@ - (NSFetchedResultsController *)resultsController { return _resultsController; } +- (NSPredicate *)fetchRequestPredicate { + if ([self.tableView isEditing]) { + return nil; + } else { + return [NSPredicate predicateWithFormat:@"visible = YES"]; + } +} + +- (void)updateFetchRequest { + self.resultsController.fetchRequest.predicate = [self fetchRequestPredicate]; + + NSError *error = nil; + if (![self.resultsController performFetch:&error]) { + DDLogError(@"Couldn't fetch blogs: %@", [error localizedDescription]); + } + + [self.tableView reloadData]; +} + - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { [self.tableView beginUpdates]; } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.tableView endUpdates]; + + if (self.sectionDeletedByController) { + /* + This covers the corner case when the only self hosted blog is removed and + there's a WordPress.com account. + + Since we only show the section title if there are multiple blog sections, + the section header wouldn't change when the section count changed, and it + would still display the wordpress.com header. + + It's not a big deal but it wouldn't be consistent with future appearances + of the same view. + */ + + [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade]; + self.sectionDeletedByController = NO; + } } - (void)controller:(NSFetchedResultsController *)controller @@ -354,5 +464,27 @@ - (void)controller:(NSFetchedResultsController *)controller } } +- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { + switch (type) { + case NSFetchedResultsChangeInsert: + [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; + break; + + case NSFetchedResultsChangeDelete: + [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; + self.sectionDeletedByController = YES; + break; + + default: + break; + } +} + +- (NSString *)controller:(NSFetchedResultsController *)controller sectionIndexTitleForSectionName:(NSString *)sectionName { + if ([sectionName isEqualToString:@"1"]) { + return [NSString stringWithFormat:NSLocalizedString(@"%@'s blogs", @"Section header for WordPress.com blogs"), [[WPAccount defaultWordPressComAccount] username]]; + } + return NSLocalizedString(@"Self Hosted", @"Section header for self hosted blogs"); +} @end diff --git a/WordPress/Classes/CreateAccountAndBlogViewController.h b/WordPress/Classes/CreateAccountAndBlogViewController.h index 3417a85e50a0..ced56dabebfe 100644 --- a/WordPress/Classes/CreateAccountAndBlogViewController.h +++ b/WordPress/Classes/CreateAccountAndBlogViewController.h @@ -10,6 +10,4 @@ @interface CreateAccountAndBlogViewController : UIViewController -@property (nonatomic, copy) void(^onCreatedUser)(NSString *, NSString *); - @end diff --git a/WordPress/Classes/CreateAccountAndBlogViewController.m b/WordPress/Classes/CreateAccountAndBlogViewController.m index 508c68d3ea9b..ed6650a8d2e2 100644 --- a/WordPress/Classes/CreateAccountAndBlogViewController.m +++ b/WordPress/Classes/CreateAccountAndBlogViewController.m @@ -23,6 +23,9 @@ #import "WPWebViewController.h" #import "WPStyleGuide.h" #import "UILabel+SuggestSize.h" +#import "WPAccount.h" +#import "Blog.h" +#import "WordPressComOAuthClient.h" @interface CreateAccountAndBlogViewController ()< UITextFieldDelegate, @@ -52,6 +55,8 @@ @interface CreateAccountAndBlogViewController ()< CGFloat _viewHeight; NSDictionary *_currentLanguage; + + WPAccount *_account; } @end @@ -664,61 +669,76 @@ - (void)createUserAndSite [operation didSucceed]; }; void (^createUserFailure)(NSError *) = ^(NSError *error) { + DDLogError(@"Failed creating user: %@", error); [operation didFail]; [self setAuthenticating:NO]; [self displayRemoteError:error]; }; - - [[WordPressComApi sharedApi] createWPComAccountWithEmail:_emailField.text - andUsername:_usernameField.text - andPassword:_passwordField.text - success:createUserSuccess - failure:createUserFailure]; - + + [[WordPressComApi anonymousApi] createWPComAccountWithEmail:_emailField.text + andUsername:_usernameField.text + andPassword:_passwordField.text + success:createUserSuccess + failure:createUserFailure]; + }]; WPAsyncBlockOperation *userSignIn = [WPAsyncBlockOperation operationWithBlock:^(WPAsyncBlockOperation *operation){ - void (^signInSuccess)(void) = ^{ + void (^signInSuccess)(NSString *authToken) = ^(NSString *authToken){ + _account = [WPAccount createOrUpdateWordPressComAccountWithUsername:_usernameField.text password:_passwordField.text authToken:authToken]; + if (![WPAccount defaultWordPressComAccount]) { + [WPAccount setDefaultWordPressComAccount:_account]; + } [operation didSucceed]; }; void (^signInFailure)(NSError *) = ^(NSError *error) { + DDLogError(@"Failed signing in user: %@", error); // We've hit a strange failure at this point, the user has been created successfully but for some reason // we are unable to sign in and proceed [operation didFail]; [self setAuthenticating:NO]; [self displayRemoteError:error]; }; - - [[WordPressComApi sharedApi] signInWithUsername:_usernameField.text - password:_passwordField.text - success:signInSuccess - failure:signInFailure]; + + + WordPressComOAuthClient *client = [WordPressComOAuthClient client]; + [client authenticateWithUsername:_usernameField.text + password:_passwordField.text + success:signInSuccess + failure:signInFailure]; }]; - + WPAsyncBlockOperation *blogCreation = [WPAsyncBlockOperation operationWithBlock:^(WPAsyncBlockOperation *operation){ void (^createBlogSuccess)(id) = ^(id responseObject){ [WPMobileStats trackEventForSelfHostedAndWPCom:StatsEventNUXCreateAccountCreatedAccount]; [operation didSucceed]; - [self setAuthenticating:NO]; - if (self.onCreatedUser) { - self.onCreatedUser(_usernameField.text, _passwordField.text); + + NSMutableDictionary *blogOptions = [[responseObject dictionaryForKey:@"blog_details"] mutableCopy]; + if ([blogOptions objectForKey:@"blogname"]) { + [blogOptions setObject:[blogOptions objectForKey:@"blogname"] forKey:@"blogName"]; + [blogOptions removeObjectForKey:@"blogname"]; } + Blog *blog = [_account findOrCreateBlogFromDictionary:blogOptions withContext:_account.managedObjectContext]; + [blog dataSave]; + [blog syncBlogWithSuccess:nil failure:nil]; + [self setAuthenticating:NO]; + [self dismissViewControllerAnimated:YES completion:nil]; }; void (^createBlogFailure)(NSError *error) = ^(NSError *error) { + DDLogError(@"Failed creating blog: %@", error); [self setAuthenticating:NO]; [operation didFail]; [self displayRemoteError:error]; }; - - NSNumber *languageId = [_currentLanguage objectForKey:@"lang_id"]; - [[WordPressComApi sharedApi] createWPComBlogWithUrl:[self getSiteAddressWithoutWordPressDotCom] - andBlogTitle:[self generateSiteTitleFromUsername:_usernameField.text] - andLanguageId:languageId - andBlogVisibility:WordPressComApiBlogVisibilityPublic - success:createBlogSuccess - failure:createBlogFailure]; + NSNumber *languageId = [_currentLanguage objectForKey:@"lang_id"]; + [[_account restApi] createWPComBlogWithUrl:[self getSiteAddressWithoutWordPressDotCom] + andBlogTitle:[self generateSiteTitleFromUsername:_usernameField.text] + andLanguageId:languageId + andBlogVisibility:WordPressComApiBlogVisibilityPublic + success:createBlogSuccess + failure:createBlogFailure]; }]; - + [blogCreation addDependency:userSignIn]; [userSignIn addDependency:userCreation]; [userCreation addDependency:siteValidation]; diff --git a/WordPress/Classes/CreateWPComAccountViewController.h b/WordPress/Classes/CreateWPComAccountViewController.h deleted file mode 100644 index ec364ceb1834..000000000000 --- a/WordPress/Classes/CreateWPComAccountViewController.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// CreateWPComAccountViewController.h -// WordPress -// -// Created by Sendhil Panchadsaram on 4/5/13. -// Copyright (c) 2013 WordPress. All rights reserved. -// - -#import - -@protocol CreateWPComAccountViewControllerDelegate; -@interface CreateWPComAccountViewController : UITableViewController - -@property (nonatomic, weak) id delegate; - -@end - -@protocol CreateWPComAccountViewControllerDelegate - -- (void)createdAndSignedInAccountWithUserName:(NSString *)userName; -- (void)createdAccountWithUserName:(NSString *)userName; - -@end diff --git a/WordPress/Classes/CreateWPComBlogViewController.h b/WordPress/Classes/CreateWPComBlogViewController.h deleted file mode 100644 index 08378436c68e..000000000000 --- a/WordPress/Classes/CreateWPComBlogViewController.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// CreateWPComBlogViewController.h -// WordPress -// -// Created by Sendhil Panchadsaram on 4/10/13. -// Copyright (c) 2013 WordPress. All rights reserved. -// - -#import - -@protocol CreateWPComBlogViewControllerDelegate; -@interface CreateWPComBlogViewController : UITableViewController - -@property (nonatomic, weak) id delegate; - -@end - -@protocol CreateWPComBlogViewControllerDelegate - -- (void)createdBlogWithDetails:(NSDictionary *)blogDetails; - -@end \ No newline at end of file diff --git a/WordPress/Classes/CreateWPComBlogViewController.m b/WordPress/Classes/CreateWPComBlogViewController.m deleted file mode 100644 index d6814b2a86f8..000000000000 --- a/WordPress/Classes/CreateWPComBlogViewController.m +++ /dev/null @@ -1,394 +0,0 @@ -// -// CreateWPComBlogViewController.m -// WordPress -// -// Created by Sendhil Panchadsaram on 4/10/13. -// Copyright (c) 2013 WordPress. All rights reserved. -// - -#import "CreateWPComBlogViewController.h" -#import "SelectWPComLanguageViewController.h" -#import "SelectWPComBlogVisibilityViewController.h" -#import "UITableViewTextFieldCell.h" -#import "UITableViewActivityCell.h" -#import "WordPressComApi.h" -#import "WPComLanguages.h" -#import "WordPressAppDelegate.h" -#import "ReachabilityUtils.h" -#import "WPAccount.h" -#import "WPTableViewSectionFooterView.h" -#import "Blog.h" - -@interface CreateWPComBlogViewController () < - SelectWPComBlogVisibilityViewControllerDelegate, - UITextFieldDelegate> { - - UITableViewTextFieldCell *_blogUrlCell; - UITableViewTextFieldCell *_blogTitleCell; - UITableViewCell *_blogVisibilityCell; - UITableViewCell *_localeCell; - - UITextField *_blogUrlTextField; - UITextField *_blogTitleTextField; - - NSString *_buttonText; - NSString *_footerText; - - BOOL _geolocationEnabled; - BOOL _isCreatingBlog; - BOOL _userPressedBackButton; - - NSDictionary *_currentLanguage; - WordPressComApiBlogVisibility _blogVisibility; -} - -@end - -@implementation CreateWPComBlogViewController - -CGSize const CreateBlogHeaderSize = { 320.0, 70.0 }; - -NSUInteger const CreateBlogBlogUrlFieldTag = 1; - -- (id)initWithStyle:(UITableViewStyle)style -{ - self = [super initWithStyle:style]; - - if (self) { - _currentLanguage = [WPComLanguages currentLanguage]; - _blogVisibility = WordPressComApiBlogVisibilityPublic; - _geolocationEnabled = YES; - } - - return self; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - [WPStyleGuide configureColorsForView:self.view andTableView:self.tableView]; - - _footerText = @" "; - _buttonText = NSLocalizedString(@"Create WordPress.com Blog", @""); - self.navigationItem.title = NSLocalizedString(@"Create Blog", @""); - - UIImageView *logoImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"logo_wpcom"]]; - logoImage.frame = CGRectMake(0.0f, 0.0f, CreateBlogHeaderSize.width, CreateBlogHeaderSize.height); - logoImage.autoresizingMask = UIViewAutoresizingFlexibleWidth; - logoImage.contentMode = UIViewContentModeCenter; - self.tableView.tableHeaderView = logoImage; -} - -- (void)didMoveToParentViewController:(UIViewController *)parent -{ - // User has pressed back button so make sure the user does not see a strange message - // or encounters strange behavior as a result of a failed or successful attempt to create an account. - if (parent == nil) { - self.delegate = nil; - _userPressedBackButton = YES; - } -} - -#pragma mark - Table view data source - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return 2; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - if (section == 0) - return 5; - else - return 1; -} - -- (NSString *)titleForFooterInSection:(NSInteger)section { - if(section == 0) - return _footerText; - else - return @""; -} - -- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section -{ - WPTableViewSectionFooterView *header = [[WPTableViewSectionFooterView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 0)]; - header.title = [self titleForFooterInSection:section]; - return header; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section -{ - NSString *title = [self titleForFooterInSection:section]; - return [WPTableViewSectionFooterView heightForTitle:title andWidth:CGRectGetWidth(self.view.bounds)]; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - UITableViewCell *cell = nil; - - if(indexPath.section == 1) { - UITableViewActivityCell *activityCell = nil; - NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"UITableViewActivityCell" owner:nil options:nil]; - for(id currentObject in topLevelObjects) - { - if([currentObject isKindOfClass:[UITableViewActivityCell class]]) - { - activityCell = (UITableViewActivityCell *)currentObject; - break; - } - } - - if(_isCreatingBlog) { - [activityCell.spinner startAnimating]; - _buttonText = NSLocalizedString(@"Creating Blog...", nil); - } else { - [activityCell.spinner stopAnimating]; - _buttonText = NSLocalizedString(@"Create WordPress.com Blog", nil); - } - - activityCell.textLabel.text = _buttonText; - if (_isCreatingBlog) { - activityCell.selectionStyle = UITableViewCellSelectionStyleNone; - } else { - activityCell.selectionStyle = UITableViewCellSelectionStyleBlue; - } - - activityCell.textLabel.font = [WPStyleGuide tableviewTextFont]; - activityCell.textLabel.textColor = [WPStyleGuide tableViewActionColor]; - cell = activityCell; - } else { - if (indexPath.row == 0) { - if (_blogUrlCell == nil) { - _blogUrlCell = [[UITableViewTextFieldCell alloc] initWithStyle:UITableViewCellStyleDefault - reuseIdentifier:@"TextCell"]; - } - _blogUrlCell.textLabel.text = NSLocalizedString(@"Blog URL", nil); - _blogUrlTextField = _blogUrlCell.textField; - _blogUrlTextField.tag = CreateBlogBlogUrlFieldTag; - _blogUrlTextField.placeholder = NSLocalizedString(@"myblog.wordpress.com", @""); - _blogUrlTextField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 1)]; - _blogUrlTextField.leftViewMode = UITextFieldViewModeAlways; - _blogUrlTextField.keyboardType = UIKeyboardTypeURL; - _blogUrlTextField.delegate = self; - [self styleTextFieldCell:_blogUrlCell]; - cell = _blogUrlCell; - } else if (indexPath.row == 1) { - if (_blogTitleCell == nil) { - _blogTitleCell = [[UITableViewTextFieldCell alloc] initWithStyle:UITableViewCellStyleDefault - reuseIdentifier:@"TextCell"]; - } - _blogTitleCell.textLabel.text = NSLocalizedString(@"Site Title", @"Label for site title field in create an account process"); - _blogTitleTextField = _blogTitleCell.textField; - _blogTitleTextField.placeholder = NSLocalizedString(@"My Site", @"Placeholder for site title field in create an account process"); - _blogTitleTextField.leftViewMode = UITextFieldViewModeAlways; - _blogTitleTextField.delegate = self; - [self styleTextFieldCell:_blogTitleCell]; - cell = _blogTitleCell; - } else if (indexPath.row == 2) { - if (_localeCell == nil) { - _localeCell = [[WPTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 - reuseIdentifier:@"LocaleCell"]; - } - _localeCell.textLabel.text = @"Language"; - _localeCell.detailTextLabel.text = [_currentLanguage objectForKey:@"name"]; - _localeCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; - [WPStyleGuide configureTableViewCell:_localeCell]; - cell = _localeCell; - } else if (indexPath.row == 3) { - if (_blogVisibilityCell == nil) { - _blogVisibilityCell = [[WPTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 - reuseIdentifier:@"VisibilityCell"]; - } - _blogVisibilityCell.textLabel.text = @"Blog Visibility"; - _blogVisibilityCell.detailTextLabel.text = [self textForCurrentBlogVisibility]; - _blogVisibilityCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; - [WPStyleGuide configureTableViewCell:_blogVisibilityCell]; - cell = _blogVisibilityCell; - } else if (indexPath.row == 4) { - UITableViewCell *geolocationCell = [tableView dequeueReusableCellWithIdentifier:@"GeolocationCell"]; - if(geolocationCell == nil) { - geolocationCell = [[WPTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"GeolocationCell"]; - geolocationCell.accessoryView = [[UISwitch alloc] init]; - } - UISwitch *geolocationSwitch = (UISwitch *)geolocationCell.accessoryView; - geolocationCell.textLabel.text = NSLocalizedString(@"Geotagging", nil); - geolocationCell.selectionStyle = UITableViewCellSelectionStyleNone; - geolocationSwitch.on = _geolocationEnabled; - [geolocationSwitch addTarget:self action:@selector(toggleGeolocation:) forControlEvents:UIControlEventValueChanged]; - [WPStyleGuide configureTableViewCell:geolocationCell]; - cell = geolocationCell; - } - } - - return cell; - -} - -#pragma mark - Table view delegate - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - [tableView deselectRowAtIndexPath:indexPath animated:YES]; - - if (_isCreatingBlog) - return; - - if (indexPath.section == 0) { - if (indexPath.row == 2) { - SelectWPComLanguageViewController *selectLanguageViewController = [[SelectWPComLanguageViewController alloc] initWithStyle:UITableViewStyleGrouped]; - selectLanguageViewController.currentlySelectedLanguageId = [[_currentLanguage objectForKey:@"lang_id"] intValue]; - selectLanguageViewController.didSelectLanguage = ^(NSDictionary *language){ - _currentLanguage = language; - [self.tableView reloadData]; - }; - [self.navigationController pushViewController:selectLanguageViewController animated:YES]; - } else if (indexPath.row == 3) { - SelectWPComBlogVisibilityViewController *selectedVisibilityViewController = [[SelectWPComBlogVisibilityViewController alloc] initWithStyle:UITableViewStyleGrouped]; - selectedVisibilityViewController.currentBlogVisibility = _blogVisibility; - selectedVisibilityViewController.delegate = self; - [self.navigationController pushViewController:selectedVisibilityViewController animated:YES]; - } - } else { - [self clickedCreateBlog]; - } -} - -#pragma mark - UITextField delegate methods - -- (BOOL)textFieldShouldReturn:(UITextField *)textField { - [textField resignFirstResponder]; - - switch (textField.tag) { - case CreateBlogBlogUrlFieldTag: - [_blogTitleTextField becomeFirstResponder]; - break; - default: - break; - } - - return YES; -} - -#pragma mark - SelectWPComBlogVisibilityViewControllerDelegate - -- (void)selectWPComBlogVisibilityViewController:(SelectWPComBlogVisibilityViewController *)viewController didSelectBlogVisibilitySetting:(WordPressComApiBlogVisibility)visibility -{ - _blogVisibility = visibility; - [self.tableView reloadData]; -} - -#pragma mark - Private Methods - -- (void)toggleGeolocation:(id)sender -{ - UISwitch *geolocationSwitch = (UISwitch *)sender; - _geolocationEnabled = geolocationSwitch.on; -} - -- (void)clickedCreateBlog -{ - [self.view endEditing:YES]; - - if (![self areFieldsValid]) { - [self displayErrorMessage]; - return; - } - - if (![ReachabilityUtils isInternetReachable]) { - [ReachabilityUtils showAlertNoInternetConnection]; - return; - } - - _isCreatingBlog = YES; - [self.tableView reloadData]; - [[WordPressComApi sharedApi] createWPComBlogWithUrl:_blogUrlTextField.text andBlogTitle:_blogTitleTextField.text andLanguageId:[_currentLanguage objectForKey:@"lang_id"] andBlogVisibility:_blogVisibility success:^(id responseObject){ - NSDictionary *blogDetails = [responseObject dictionaryForKey:@"blog_details"]; - [self createBlog:blogDetails]; - [self.delegate createdBlogWithDetails:blogDetails]; - } failure:^(NSError *error){ - if (!_userPressedBackButton) { - _isCreatingBlog = NO; - [self.tableView reloadData]; - [self displayCreationError:error]; - } - }]; -} - -- (BOOL)areFieldsValid -{ - BOOL areFieldsFilled = [[_blogTitleTextField.text trim] length] != 0 && [[_blogUrlTextField.text trim] length] != 0; - BOOL urlDoesNotHavePeriod = [_blogUrlTextField.text rangeOfString:@"."].location == NSNotFound; - - return areFieldsFilled && urlDoesNotHavePeriod; -} - -- (void)displayErrorMessage -{ - NSString *errorMessage; - - if ([[_blogUrlTextField.text trim] length] == 0) { - errorMessage = NSLocalizedString(@"Blog address is required.", nil); - } else if ([_blogUrlTextField.text rangeOfString:@"."].location != NSNotFound) { - errorMessage = NSLocalizedString(@"Blog url cannot contain a period", nil); - } else if ([[_blogTitleTextField.text trim] length] == 0) { - errorMessage = NSLocalizedString(@"Must set a blog title", nil); - } - - if (errorMessage != nil) { - _footerText = errorMessage; - [self.tableView reloadData]; - } -} - -// TODO : Figure out where to put this so we aren't duplicating code with AddUsersBlogViewController -- (void)createBlog:(NSDictionary *)blogInfo { - NSMutableDictionary *newBlog = [NSMutableDictionary dictionary]; - [newBlog setObject:[blogInfo objectForKey:@"blogname"] forKey:@"blogName"]; - [newBlog setObject:[blogInfo objectForKey:@"blogid"] forKey:@"blogid"]; - [newBlog setObject:[blogInfo objectForKey:@"url"] forKey:@"url"]; - [newBlog setObject:[blogInfo objectForKey:@"xmlrpc"] forKey:@"xmlrpc"]; - [newBlog setObject:@(YES) forKey:@"isAdmin"]; - - WPAccount *account = [WPAccount defaultWordPressComAccount]; - - Blog *blog = [account findOrCreateBlogFromDictionary:newBlog withContext:account.managedObjectContext]; - blog.geolocationEnabled = _geolocationEnabled; - [blog dataSave]; - [blog syncBlogWithSuccess:^{ - if( ! [blog isWPcom] ) - [[WordPressComApi sharedApi] syncPushNotificationInfo]; - } - failure:nil]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"BlogsRefreshNotification" object:nil]; - - [[WordPressComApi sharedApi] syncPushNotificationInfo]; -} - -- (void)displayCreationError:(NSError *)error -{ - NSString *errorMessage = [error.userInfo objectForKey:WordPressComApiErrorMessageKey]; - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Error", nil) message:errorMessage delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil, nil]; - [alertView show]; -} - -- (NSString *)textForCurrentBlogVisibility -{ - if (_blogVisibility == WordPressComApiBlogVisibilityPublic) { - return NSLocalizedString(@"Public", nil); - } else if (_blogVisibility == WordPressComApiComBlogVisibilityPrivate) { - return NSLocalizedString(@"Private", nil); - } else { - return NSLocalizedString(@"Hidden", nil); - } -} - -- (void)styleTextFieldCell:(UITableViewTextFieldCell *)cell -{ - [WPStyleGuide configureTableViewCell:cell]; - cell.textField.font = [WPStyleGuide tableviewTextFont]; -} - -@end diff --git a/WordPress/Classes/LoginViewController.h b/WordPress/Classes/LoginViewController.h index 9b9efdb699bc..d819ea3d1e9b 100644 --- a/WordPress/Classes/LoginViewController.h +++ b/WordPress/Classes/LoginViewController.h @@ -9,5 +9,7 @@ #import @interface LoginViewController : UIViewController - +@property (nonatomic, assign) BOOL onlyDotComAllowed; +@property (nonatomic, assign) BOOL prefersSelfHosted; +@property (nonatomic, copy) void (^dismissBlock)(); @end diff --git a/WordPress/Classes/LoginViewController.m b/WordPress/Classes/LoginViewController.m index 5407e6c56798..577aa8a53e9d 100644 --- a/WordPress/Classes/LoginViewController.m +++ b/WordPress/Classes/LoginViewController.m @@ -11,21 +11,22 @@ #import "UIView+FormSheetHelpers.h" #import "LoginViewController.h" #import "CreateAccountAndBlogViewController.h" -#import "NewAddUsersBlogViewController.h" #import "AboutViewController.h" #import "SupportViewController.h" #import "WPNUXMainButton.h" #import "WPNUXPrimaryButton.h" #import "WPNUXSecondaryButton.h" #import "WPWalkthroughTextField.h" -#import "WordPressComApi.h" +#import "WordPressComOAuthClient.h" #import "WPWebViewController.h" #import "Blog+Jetpack.h" #import "JetpackSettingsViewController.h" #import "WPWalkthroughOverlayView.h" #import "ReachabilityUtils.h" #import "WPNUXUtility.h" +#import "WPNUXBackButton.h" #import "WPAccount.h" +#import "Note.h" @interface LoginViewController () < UITextFieldDelegate> { @@ -40,6 +41,8 @@ @interface LoginViewController () < WPWalkthroughTextField *_passwordText; WPWalkthroughTextField *_siteUrlText; WPNUXMainButton *_signInButton; + WPNUXSecondaryButton *_cancelButton; + UILabel *_statusLabel; // Measurements @@ -82,7 +85,10 @@ - (void)viewDidLoad _viewHeight = [self.view formSheetViewHeight]; self.view.backgroundColor = [WPNUXUtility backgroundColor]; - _userIsDotCom = YES; + _userIsDotCom = self.onlyDotComAllowed || !self.prefersSelfHosted; + if ([WPAccount defaultWordPressComAccount]) { + _userIsDotCom = NO; + } [self addMainView]; [self initializeView]; @@ -128,13 +134,13 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField { - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { - _signInButton.enabled = [self areDotComFieldsFilled]; + _signInButton.enabled = [self isSignInEnabled]; return YES; } - (BOOL)textFieldShouldEndEditing:(UITextField *)textField { - _signInButton.enabled = [self areDotComFieldsFilled]; + _signInButton.enabled = [self isSignInEnabled]; return YES; } @@ -142,6 +148,7 @@ - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRang { BOOL isUsernameFilled = [self isUsernameFilled]; BOOL isPasswordFilled = [self isPasswordFilled]; + BOOL isSiteUrlFilled = [self isSiteUrlFilled]; NSMutableString *updatedString = [[NSMutableString alloc] initWithString:textField.text]; [updatedString replaceCharactersInRange:range withString:string]; @@ -150,8 +157,10 @@ - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRang isUsernameFilled = updatedStringHasContent; } else if (textField == _passwordText) { isPasswordFilled = updatedStringHasContent; + } else if (textField == _siteUrlText) { + isSiteUrlFilled = updatedStringHasContent; } - _signInButton.enabled = isUsernameFilled && isPasswordFilled; + _signInButton.enabled = isUsernameFilled && isPasswordFilled && (_userIsDotCom || isSiteUrlFilled); return YES; } @@ -256,21 +265,14 @@ - (void)helpButtonAction:(id)sender [self.navigationController presentViewController:nc animated:YES completion:nil]; } -- (void)clickedSkipToCreate:(id)sender -{ - [WPMobileStats trackEventForSelfHostedAndWPCom:StatsEventNUXFirstWalkthroughClickedSkipToCreateAccount]; - [self showCreateAccountView]; -} - -- (void)clickedCreateAccount:(UITapGestureRecognizer *)tapGestureRecognizer +- (void)skipToCreateAction:(id)sender { [WPMobileStats trackEventForSelfHostedAndWPCom:StatsEventNUXFirstWalkthroughClickedCreateAccount]; [self showCreateAccountView]; } -- (void)clickedBackground:(UITapGestureRecognizer *)tapGestureRecognizer +- (void)backgroundTapGestureAction:(UITapGestureRecognizer *)tapGestureRecognizer { - [self.view endEditing:YES]; // The info button is a little hard to hit so this adds a little buffer around it @@ -300,8 +302,7 @@ - (void)signInButtonAction:(id)sender [self signIn]; } -- (void)toggleSignInformAction:(id)sender { - +- (void)toggleSignInFormAction:(id)sender { _userIsDotCom = !_userIsDotCom; // Controls are layed out in initializeView. Calling this method in an animation block will animate the controls to their new positions. @@ -311,6 +312,12 @@ - (void)toggleSignInformAction:(id)sender { }]; } +- (void)cancelButtonAction:(id)sender { + if (self.dismissBlock) { + self.dismissBlock(); + } +} + #pragma mark - Private Methods - (void)addMainView @@ -320,7 +327,7 @@ - (void)addMainView _mainView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; [self.view addSubview:_mainView]; - UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickedBackground:)]; + UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backgroundTapGestureAction:)]; gestureRecognizer.numberOfTapsRequired = 1; gestureRecognizer.cancelsTouchesInView = YES; [_mainView addGestureRecognizer:gestureRecognizer]; @@ -402,13 +409,29 @@ - (void)addControls // Add Sign In Button if (_signInButton == nil) { _signInButton = [[WPNUXMainButton alloc] init]; - [_signInButton setTitle:NSLocalizedString(@"Sign In", nil) forState:UIControlStateNormal]; [_signInButton addTarget:self action:@selector(signInButtonAction:) forControlEvents:UIControlEventTouchUpInside]; _signInButton.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin; [_mainView addSubview:_signInButton]; _signInButton.enabled = NO; } + NSString *signInTitle; + if (_userIsDotCom) { + signInTitle = NSLocalizedString(@"Sign In", nil); + } else { + signInTitle = NSLocalizedString(@"Add Site", nil); + } + [_signInButton setTitle:signInTitle forState:UIControlStateNormal]; + + // Add Cancel Button + if (self.dismissBlock && _cancelButton == nil) { + _cancelButton = [[WPNUXSecondaryButton alloc] init]; + [_cancelButton setTitle:NSLocalizedString(@"Cancel", nil) forState:UIControlStateNormal]; + [_cancelButton addTarget:self action:@selector(cancelButtonAction:) forControlEvents:UIControlEventTouchUpInside]; + [_cancelButton sizeToFit]; + [self.view addSubview:_cancelButton]; + } + // Add status label if (_statusLabel == nil) { _statusLabel = [[UILabel alloc] init]; @@ -423,20 +446,34 @@ - (void)addControls // Add Account type toggle if (_toggleSignInForm == nil) { _toggleSignInForm = [[WPNUXSecondaryButton alloc] init]; - [_toggleSignInForm addTarget:self action:@selector(toggleSignInformAction:) forControlEvents:UIControlEventTouchUpInside]; + [_toggleSignInForm addTarget:self action:@selector(toggleSignInFormAction:) forControlEvents:UIControlEventTouchUpInside]; _toggleSignInForm.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin; [_mainView addSubview:_toggleSignInForm]; } - NSString *toggleTitle = _userIsDotCom ? @"Add Self-Hosted Site" : @"Sign in to WordPress.com"; - [_toggleSignInForm setTitle:toggleTitle forState:UIControlStateNormal]; - - // Add Skip to Create Account Button - if (_skipToCreateAccount == nil) { - _skipToCreateAccount = [[WPNUXSecondaryButton alloc] init]; - [_skipToCreateAccount setTitle:NSLocalizedString(@"Create Account", nil) forState:UIControlStateNormal]; - [_skipToCreateAccount addTarget:self action:@selector(clickedSkipToCreate:) forControlEvents:UIControlEventTouchUpInside]; - _skipToCreateAccount.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin; - [_mainView addSubview:_skipToCreateAccount]; + if (!self.onlyDotComAllowed && ![WPAccount defaultWordPressComAccount]) { + // Add Account type toggle + if (_toggleSignInForm == nil) { + _toggleSignInForm = [[WPNUXSecondaryButton alloc] init]; + [_toggleSignInForm addTarget:self action:@selector(toggleSignInFormAction:) forControlEvents:UIControlEventTouchUpInside]; + [_mainView addSubview:_toggleSignInForm]; + } + NSString *toggleTitle; + if (_userIsDotCom) { + toggleTitle = NSLocalizedString(@"Add Self-Hosted Site", nil); + } else { + toggleTitle = NSLocalizedString(@"Sign in to WordPress.com", nil); + } + [_toggleSignInForm setTitle:toggleTitle forState:UIControlStateNormal]; + } + + if (![WPAccount defaultWordPressComAccount]) { + // Add Skip to Create Account Button + if (_skipToCreateAccount == nil) { + _skipToCreateAccount = [[WPNUXSecondaryButton alloc] init]; + [_skipToCreateAccount setTitle:NSLocalizedString(@"Create Account", nil) forState:UIControlStateNormal]; + [_skipToCreateAccount addTarget:self action:@selector(skipToCreateAction:) forControlEvents:UIControlEventTouchUpInside]; + [_mainView addSubview:_skipToCreateAccount]; + } } } @@ -447,12 +484,19 @@ - (void)layoutControls UIImage *infoButtonImage = [UIImage imageNamed:@"btn-about"]; y = 0; - if (IS_IOS7 && IS_IPHONE) { + if (IS_IPHONE) { y = GeneralWalkthroughiOS7StatusBarOffset; } _helpButton.frame = CGRectMake(viewWidth - infoButtonImage.size.width, y, infoButtonImage.size.width, infoButtonImage.size.height); - + // Layout Cancel Button + x = viewWidth - CGRectGetWidth(_cancelButton.frame); + y = 0.5 * GeneralWalkthroughStandardOffset; + if (IS_IPHONE) { + y += GeneralWalkthroughiOS7StatusBarOffset; + } + _cancelButton.frame = CGRectMake(x, y, CGRectGetWidth(_cancelButton.frame), CGRectGetHeight(_cancelButton.frame)); + CGFloat heightOfControls = CGRectGetHeight(_icon.frame) + GeneralWalkthroughStandardOffset + (_userIsDotCom ? 2 : 3) * GeneralWalkthroughTextFieldHeight + GeneralWalkthroughStandardOffset + GeneralWalkthroughButtonHeight; CGFloat startingYForCenteredControls = floorf((_viewHeight - 2 * GeneralWalkthroughSecondaryButtonHeight - heightOfControls)/2.0); @@ -506,12 +550,6 @@ - (void)dismiss - (void)showCreateAccountView { CreateAccountAndBlogViewController *createAccountViewController = [[CreateAccountAndBlogViewController alloc] init]; - createAccountViewController.onCreatedUser = ^(NSString *username, NSString *password) { - _usernameText.text = username; - _passwordText.text = password; - _userIsDotCom = YES; - [self showAddUsersBlogsForWPCom]; - }; [self.navigationController pushViewController:createAccountViewController animated:YES]; } @@ -528,6 +566,7 @@ - (void)showJetpackAuthentication } else { [WPMobileStats trackEventForSelfHostedAndWPCom:StatsEventNUXFirstWalkthroughUserSkippedConnectingToJetpack]; } + [self dismiss]; }]; [self.navigationController pushViewController:jetpackSettingsViewController animated:YES]; @@ -598,6 +637,16 @@ - (BOOL)isPasswordFilled return [[_passwordText.text trim] length] != 0; } +- (BOOL)isSiteUrlFilled +{ + return [[_siteUrlText.text trim] length] != 0; +} + +- (BOOL)isSignInEnabled +{ + return _userIsDotCom ? [self areDotComFieldsFilled] : [self areSelfHostedFieldsFilled]; +} + - (BOOL)areDotComFieldsFilled { return [self isUsernameFilled] && [self isPasswordFilled]; @@ -605,7 +654,7 @@ - (BOOL)areDotComFieldsFilled - (BOOL)areSelfHostedFieldsFilled { - return [self areDotComFieldsFilled] && [[_siteUrlText.text trim] length] != 0; + return [self areDotComFieldsFilled] && [self isSiteUrlFilled]; } - (BOOL)hasUserOnlyEnteredValuesForDotCom @@ -613,11 +662,6 @@ - (BOOL)hasUserOnlyEnteredValuesForDotCom return [self areDotComFieldsFilled] && ![self areSelfHostedFieldsFilled]; } -- (BOOL)areFieldsFilled -{ - return [[_usernameText.text trim] length] != 0 && [[_passwordText.text trim] length] != 0 && [[_siteUrlText.text trim] length] != 0; -} - - (BOOL)isUrlValid { NSURL *siteURL = [NSURL URLWithString:_siteUrlText.text]; @@ -638,6 +682,7 @@ - (void)setAuthenticating:(BOOL)authenticating withStatusMessage:(NSString *)sta _signInButton.enabled = !authenticating; _toggleSignInForm.hidden = authenticating; _skipToCreateAccount.hidden = authenticating; + _cancelButton.enabled = !authenticating; [_signInButton showActivityIndicator:authenticating]; } @@ -673,7 +718,8 @@ - (void)signIn _dotComSiteUrl = [siteUrl objectForKey:@"value"]; [self signInForWPComForUsername:username andPassword:password]; } else { - [self signInForSelfHostedForUsername:username password:password options:options andApi:api]; + NSString *xmlrpc = [xmlRPCURL absoluteString]; + [self createSelfHostedAccountAndBlogWithUsername:username password:password xmlrpc:xmlrpc options:options]; } } failure:^(NSError *error){ [self setAuthenticating:NO withStatusMessage:nil]; @@ -694,39 +740,59 @@ - (void)signInForWPComForUsername:(NSString *)username andPassword:(NSString *)p [self setAuthenticating:YES withStatusMessage:NSLocalizedString(@"Connecting to WordPress.com", nil)]; - void (^loginSuccessBlock)(void) = ^{ - [self setAuthenticating:NO withStatusMessage:nil]; - _userIsDotCom = YES; - [self showAddUsersBlogsForWPCom]; - }; - - void (^loginFailBlock)(NSError *) = ^(NSError *error){ - // User shouldn't get here because the getOptions call should fail, but in the unlikely case they do throw up an error message. + WordPressComOAuthClient *client = [WordPressComOAuthClient client]; + [client authenticateWithUsername:username + password:password + success:^(NSString *authToken) { + [self setAuthenticating:NO withStatusMessage:nil]; + _userIsDotCom = YES; + [self createWordPressComAccountForUsername:username password:password authToken:authToken]; + } failure:^(NSError *error) { + [self setAuthenticating:NO withStatusMessage:nil]; + [self displayRemoteError:error]; + }]; +} + +- (void)createWordPressComAccountForUsername:(NSString *)username password:(NSString *)password authToken:(NSString *)authToken +{ + [self setAuthenticating:YES withStatusMessage:NSLocalizedString(@"Getting account information", nil)]; + WPAccount *account = [WPAccount createOrUpdateWordPressComAccountWithUsername:username password:password authToken:authToken]; + if (![WPAccount defaultWordPressComAccount]) { + [WPAccount setDefaultWordPressComAccount:account]; + } + [account syncBlogsWithSuccess:^{ [self setAuthenticating:NO withStatusMessage:nil]; - DDLogError(@"Login failed with username %@ : %@", username, error); - [self displayGenericErrorMessage:NSLocalizedString(@"Please try entering your login details again.", nil)]; - }; - - [[WordPressComApi sharedApi] signInWithUsername:username - password:password - success:loginSuccessBlock - failure:loginFailBlock]; - -} - -- (void)signInForSelfHostedForUsername:(NSString *)username password:(NSString *)password options:(NSDictionary *)options andApi:(WordPressXMLRPCApi *)api -{ - [WPMobileStats trackEventForSelfHostedAndWPCom:StatsEventNUXFirstWalkthroughSignedInForSelfHosted]; - - [self setAuthenticating:YES withStatusMessage:NSLocalizedString(@"Reading blog options", nil)]; - - [api getBlogsWithSuccess:^(NSArray *blogs) { - _blogs = blogs; - [self handleGetBlogsSuccess:[api.xmlrpc absoluteString]]; + [self dismiss]; } failure:^(NSError *error) { [self setAuthenticating:NO withStatusMessage:nil]; [self displayRemoteError:error]; }]; + [account.restApi getNotificationsSince:nil success:nil failure:nil]; +} + +- (void)createSelfHostedAccountAndBlogWithUsername:(NSString *)username password:(NSString *)password xmlrpc:(NSString *)xmlrpc options:(NSDictionary *)options +{ + WPAccount *account = [WPAccount createOrUpdateSelfHostedAccountWithXmlrpc:xmlrpc username:username andPassword:password]; + NSString *blogName = [options stringForKeyPath:@"blog_title.value"]; + NSString *url = [options stringForKeyPath:@"home_url.value"]; + NSMutableDictionary *blogDetails = [NSMutableDictionary dictionaryWithObject:xmlrpc forKey:@"xmlrpc"]; + if (blogName) { + [blogDetails setObject:blogName forKey:@"blogName"]; + } + if (url) { + [blogDetails setObject:url forKey:@"url"]; + } + _blog = [account findOrCreateBlogFromDictionary:blogDetails withContext:account.managedObjectContext]; + _blog.options = options; + [_blog dataSave]; + [WPMobileStats trackEventForSelfHostedAndWPCom:StatsEventNUXFirstWalkthroughUserSignedInToBlogWithJetpack]; + [_blog syncBlogWithSuccess:nil failure:nil]; + + if ([_blog hasJetpack]) { + [self showJetpackAuthentication]; + } else { + [self dismiss]; + } } - (void)handleGuessXMLRPCURLFailure:(NSError *)error @@ -752,32 +818,6 @@ - (void)handleGuessXMLRPCURLFailure:(NSError *)error } } -- (void)handleGetBlogsSuccess:(NSString *)xmlRPCUrl { - if ([_blogs count] > 0) { - // If the user has entered the URL of a site they own on a MultiSite install, - // assume they want to add that specific site. - NSDictionary *subsite = nil; - if ([_blogs count] > 1) { - subsite = [[_blogs filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"xmlrpc = %@", xmlRPCUrl]] lastObject]; - } - - if (subsite == nil) { - subsite = [_blogs objectAtIndex:0]; - } - - if ([_blogs count] > 1 && [[subsite objectForKey:@"blogid"] isEqualToString:@"1"]) { - [self setAuthenticating:NO withStatusMessage:nil]; - [self showAddUsersBlogsForSelfHosted:xmlRPCUrl]; - } else { - [self createBlogWithXmlRpc:xmlRPCUrl andBlogDetails:subsite]; - [self synchronizeNewlyAddedBlog]; - } - } else { - NSError *error = [NSError errorWithDomain:@"WordPress" code:0 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Sorry, you credentials were good but you don't seem to have access to any blogs", nil)}]; - [self displayRemoteError:error]; - } -} - - (void)displayRemoteError:(NSError *)error { DDLogError(@"%@", error); NSString *message = [error localizedDescription]; @@ -804,88 +844,6 @@ - (void)displayRemoteError:(NSError *)error { } } -- (NewAddUsersBlogViewController *)addUsersBlogViewController:(NSString *)xmlRPCUrl -{ - BOOL isWPCom = (xmlRPCUrl == nil); - NewAddUsersBlogViewController *vc = [[NewAddUsersBlogViewController alloc] init]; - vc.account = [self createAccountWithUsername:_usernameText.text andPassword:_passwordText.text isWPCom:isWPCom xmlRPCUrl:xmlRPCUrl]; - vc.blogAdditionCompleted = ^(NewAddUsersBlogViewController * viewController){ - [self dismiss]; - }; - vc.onNoBlogsLoaded = ^(NewAddUsersBlogViewController *viewController) { - [self dismiss]; - }; - vc.onErrorLoading = ^(NewAddUsersBlogViewController *viewController, NSError *error) { - DDLogError(@"There was an error loading blogs after sign in"); - [self.navigationController popViewControllerAnimated:YES]; - [self displayGenericErrorMessage:[error localizedDescription]]; - }; - - return vc; -} - -- (void)showAddUsersBlogsForSelfHosted:(NSString *)xmlRPCUrl -{ - NewAddUsersBlogViewController *vc = [self addUsersBlogViewController:xmlRPCUrl]; - - [self.navigationController pushViewController:vc animated:YES]; -} - -- (void)showAddUsersBlogsForWPCom -{ - NewAddUsersBlogViewController *vc = [self addUsersBlogViewController:nil]; - - NSString *siteUrl = [_siteUrlText.text trim]; - if ([siteUrl length] != 0) { - vc.siteUrl = siteUrl; - } else if ([_dotComSiteUrl length] != 0) { - vc.siteUrl = _dotComSiteUrl; - } - - [self.navigationController pushViewController:vc animated:YES]; -} - -- (void)createBlogWithXmlRpc:(NSString *)xmlRPCUrl andBlogDetails:(NSDictionary *)blogDetails -{ - NSParameterAssert(blogDetails != nil); - - WPAccount *account = [self createAccountWithUsername:_usernameText.text andPassword:_passwordText.text isWPCom:NO xmlRPCUrl:xmlRPCUrl]; - - NSMutableDictionary *newBlog = [NSMutableDictionary dictionaryWithDictionary:blogDetails]; - [newBlog setObject:xmlRPCUrl forKey:@"xmlrpc"]; - - _blog = [account findOrCreateBlogFromDictionary:newBlog withContext:account.managedObjectContext]; - [_blog.managedObjectContext obtainPermanentIDsForObjects:@[_blog] error:nil]; - [_blog dataSave]; -} - -- (WPAccount *)createAccountWithUsername:(NSString *)username andPassword:(NSString *)password isWPCom:(BOOL)isWPCom xmlRPCUrl:(NSString *)xmlRPCUrl { - WPAccount *account; - if (isWPCom) { - account = [WPAccount createOrUpdateWordPressComAccountWithUsername:username andPassword:password]; - } else { - account = [WPAccount createOrUpdateSelfHostedAccountWithXmlrpc:xmlRPCUrl username:username andPassword:password]; - } - return account; -} - -- (void)synchronizeNewlyAddedBlog -{ - void (^successBlock)() = ^{ - [[WordPressComApi sharedApi] syncPushNotificationInfo]; - [self setAuthenticating:NO withStatusMessage:nil]; - [WPMobileStats trackEventForSelfHostedAndWPCom:StatsEventNUXFirstWalkthroughUserSignedInToBlogWithJetpack]; - if ([_blog hasJetpack]) { - [self showJetpackAuthentication]; - } else { - [self dismiss]; - } - }; - void (^failureBlock)(NSError*) = ^(NSError * error) { - [self setAuthenticating:NO withStatusMessage:nil]; }; - [_blog syncBlogWithSuccess:successBlock failure:failureBlock]; -} - - (void)keyboardWillShow:(NSNotification *)notification { NSDictionary *keyboardInfo = notification.userInfo; diff --git a/WordPress/Classes/NewAddUsersBlogViewController.h b/WordPress/Classes/NewAddUsersBlogViewController.h deleted file mode 100644 index 22f08f762de0..000000000000 --- a/WordPress/Classes/NewAddUsersBlogViewController.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// NewAddUsersBlogViewController.h -// WordPress -// -// Created by Sendhil Panchadsaram on 5/2/13. -// Copyright (c) 2013 WordPress. All rights reserved. -// - -#import - -@class WPAccount; -@interface NewAddUsersBlogViewController : UIViewController - -@property (nonatomic, assign) BOOL autoAddSingleBlog; -@property (nonatomic, strong) NSString *siteUrl; -@property (nonatomic, strong) WPAccount *account; - -@property (nonatomic, copy ) void (^blogAdditionCompleted)(NewAddUsersBlogViewController *); -@property (nonatomic, copy ) void (^onNoBlogsLoaded)(NewAddUsersBlogViewController *); -@property (nonatomic, copy ) void (^onErrorLoading)(NewAddUsersBlogViewController *, NSError *); - -@end diff --git a/WordPress/Classes/NewAddUsersBlogViewController.m b/WordPress/Classes/NewAddUsersBlogViewController.m deleted file mode 100644 index 7ec7d316bd2c..000000000000 --- a/WordPress/Classes/NewAddUsersBlogViewController.m +++ /dev/null @@ -1,456 +0,0 @@ -// -// NewAddUsersBlogViewController.m -// WordPress -// -// Created by Sendhil Panchadsaram on 5/2/13. -// Copyright (c) 2013 WordPress. All rights reserved. -// - -#import -#import -#import "WordPressAppDelegate.h" -#import "UIView+FormSheetHelpers.h" -#import "NewAddUsersBlogViewController.h" -#import "WPNUXPrimaryButton.h" -#import "WPNUXSecondaryButton.h" -#import "AddUsersBlogCell.h" -#import "NSString+XMLExtensions.h" -#import "WordPressComApi.h" -#import "Blog.h" -#import "WPNUXUtility.h" -#import "WPAccount.h" -#import "ContextManager.h" -#import "UILabel+SuggestSize.h" - -@interface NewAddUsersBlogViewController () < - UITableViewDelegate, - UITableViewDataSource> { - NSArray *_usersBlogs; - NSMutableArray *_selectedBlogs; - WPNUXSecondaryButton *_selectAllButton; - WPNUXPrimaryButton *_addSelectedButton; - UIView *_mainView; - - CGFloat _viewWidth; - CGFloat _viewHeight; -} - -@property (nonatomic, strong) UITableView *tableView; - -@end - -@implementation NewAddUsersBlogViewController - -CGFloat const AddUsersBlogHeaderHeight = 164.0; -CGFloat const AddUsersBlogStandardOffset = 16.0; -CGFloat const AddUsersBlogTitleVerticalOffset = 23.0; -CGFloat const AddUsersBlogMaxTextWidth = 289.0; -CGFloat const AddUsersBlogBottomBackgroundHeight = 64; - - -- (id)init -{ - self = [super init]; - if (self) { - _selectedBlogs = [[NSMutableArray alloc] init]; - _autoAddSingleBlog = YES; - } - return self; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - [WPMobileStats trackEventForSelfHostedAndWPCom:StatsEventAddBlogsOpened]; - - _viewWidth = [self.view formSheetViewWidth]; - _viewHeight = [self.view formSheetViewHeight]; - - [self addMainView]; - [self addTableView]; - [self addBottomPanel]; -} - -- (void)viewDidAppear:(BOOL)animated -{ - [super viewDidAppear:animated]; - [self refreshBlogs]; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - return [super shouldAutorotateToInterfaceOrientation:interfaceOrientation]; -} - -- (NSUInteger)supportedInterfaceOrientations { - if (IS_IPHONE) - return UIInterfaceOrientationMaskPortrait; - - return UIInterfaceOrientationMaskAll; -} - -#pragma mark - Table view data source - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return 1; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - return [_usersBlogs count]; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - static NSString *CellIdentifier = @"Cell"; - AddUsersBlogCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; - if (cell == nil) { - cell = [[AddUsersBlogCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; - } - - cell.selectionStyle = UITableViewCellSelectionStyleNone; - cell.isWPCom = self.account.isWpcom; - - NSDictionary *blogData = [_usersBlogs objectAtIndex:indexPath.row]; - cell.showTopSeparator = indexPath.row == 0; - cell.title = [self getCellTitleForIndexPath:indexPath]; - cell.blavatarUrl = [blogData objectForKey:@"url"]; - cell.selected = [_selectedBlogs containsObject:[blogData objectForKey:@"blogid"]]; - - return cell; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - return [AddUsersBlogCell rowHeightWithText:[self getCellTitleForIndexPath:indexPath]]; -} - -#pragma mark - Table view delegate - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - NSDictionary *blogData = [_usersBlogs objectAtIndex:indexPath.row]; - NSString *blogId = [blogData objectForKey:@"blogid"]; - if ([_selectedBlogs containsObject:blogId]) { - [_selectedBlogs removeObject:blogId]; - } else { - [_selectedBlogs addObject:blogId]; - } - [tableView reloadData]; - [self toggleButtons]; -} - -#pragma mark - Private Methods - -- (void)addMainView -{ - _mainView = [[UIView alloc] initWithFrame:self.view.bounds]; - [self.view addSubview:_mainView]; - _mainView.userInteractionEnabled = NO; -} - -- (void)addTableView -{ - CGRect tableViewFrame = CGRectMake(0, 0, _viewWidth, _viewHeight); - tableViewFrame.size.height -= AddUsersBlogBottomBackgroundHeight; - - self.tableView = [[UITableView alloc] initWithFrame:tableViewFrame style:UITableViewStylePlain]; - self.tableView.backgroundColor = [UIColor clearColor]; - self.tableView.delegate = self; - self.tableView.dataSource = self; - self.view.backgroundColor = [WPNUXUtility backgroundColor]; - self.tableView.tableHeaderView = [self headerView]; - self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; - [self.view addSubview:self.tableView]; -} - -- (void)addBottomPanel -{ - UIView *bottomPanel = [[UIView alloc] init]; - bottomPanel.backgroundColor = [WPNUXUtility bottomPanelBackgroundColor]; - bottomPanel.frame = CGRectMake(0, CGRectGetMaxY(self.tableView.frame), _viewWidth, AddUsersBlogBottomBackgroundHeight); - [self.view addSubview:bottomPanel]; - - UIView *bottomPanelLine = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _viewWidth, 1)]; - bottomPanelLine.backgroundColor = [WPNUXUtility bottomPanelLineColor]; - [bottomPanel addSubview:bottomPanelLine]; - - _selectAllButton = [[WPNUXSecondaryButton alloc] init]; - [_selectAllButton setTitle:NSLocalizedString(@"Select All", nil) forState:UIControlStateNormal]; - _selectAllButton.titleLabel.adjustsFontSizeToFitWidth = YES; - [_selectAllButton sizeToFit]; - [_selectAllButton addTarget:self action:@selector(selectAllBlogs) forControlEvents:UIControlEventTouchUpInside]; - [bottomPanel addSubview:_selectAllButton]; - - CGFloat selectAllWidth = CGRectGetWidth(_selectAllButton.frame); - // This part is to ensure that we have the larger of the two widths for the two text options for this button - // so that regardless of what text is in the button it will fit - [_selectAllButton setTitle:NSLocalizedString(@"Deselect All", nil) forState:UIControlStateNormal]; - [_selectAllButton sizeToFit]; - CGFloat deselectAllWidth = CGRectGetWidth(_selectAllButton.frame); - [_selectAllButton setTitle:NSLocalizedString(@"Select All", nil) forState:UIControlStateNormal]; - CGRect selectAllFrame = _selectAllButton.frame; - selectAllFrame.size.width = selectAllWidth > deselectAllWidth ? selectAllWidth : deselectAllWidth; - - _addSelectedButton = [[WPNUXPrimaryButton alloc] init]; - [_addSelectedButton setTitle:NSLocalizedString(@"Add Selected", nil) forState:UIControlStateNormal]; - _addSelectedButton.titleLabel.adjustsFontSizeToFitWidth = YES; - // Calculate the space with the largest possible text width before setting the text back to normal. We calculate this - // ahead of time so that way we don't have flickering as the text changes result in the button size changing. This also - // ensures we don't have to re-layout the button as the text changes as well. - [_addSelectedButton setTitle:[NSString stringWithFormat:@"%@ (100)", NSLocalizedString(@"Add Selected", nil)] forState:UIControlStateNormal]; - [_addSelectedButton sizeToFit]; - [_addSelectedButton setTitle:[NSString stringWithFormat:@"%@ (1)", NSLocalizedString(@"Add Selected", nil)] forState:UIControlStateNormal]; - [_addSelectedButton addTarget:self action:@selector(createBlogs) forControlEvents:UIControlEventTouchUpInside]; - [bottomPanel addSubview:_addSelectedButton]; - - // For some locales, these strings can be long so we try to balance the widths. - CGFloat availableWidth = _viewWidth - 3 * AddUsersBlogStandardOffset; - CGFloat widthRatio = availableWidth / (CGRectGetWidth(_addSelectedButton.frame) + CGRectGetWidth(selectAllFrame)); - - CGFloat maxWidth = CGRectGetWidth(_selectAllButton.frame) * widthRatio; - CGFloat x,y; - x = AddUsersBlogStandardOffset; - y = AddUsersBlogStandardOffset; - _selectAllButton.frame = CGRectIntegral(CGRectMake(x, y, MIN(CGRectGetWidth(_selectAllButton.frame), maxWidth), CGRectGetHeight(_selectAllButton.frame))); - - maxWidth = CGRectGetWidth(_addSelectedButton.frame) * widthRatio; - CGFloat addSelectedButtonWidth = MIN(CGRectGetWidth(_addSelectedButton.frame), maxWidth); - x = _viewWidth - addSelectedButtonWidth - AddUsersBlogStandardOffset; - y = AddUsersBlogStandardOffset; - _addSelectedButton.frame = CGRectIntegral(CGRectMake(x, y, addSelectedButtonWidth, CGRectGetHeight(_addSelectedButton.frame))); -} - -- (UIView *)headerView -{ - CGFloat x, y; - UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _viewWidth, AddUsersBlogHeaderHeight)]; - headerView.backgroundColor = [UIColor clearColor]; - - UILabel *title = [[UILabel alloc] init]; - title.backgroundColor = [UIColor clearColor]; - title.textAlignment = NSTextAlignmentCenter; - title.lineBreakMode = NSLineBreakByWordWrapping; - title.font = [UIFont fontWithName:@"OpenSans-Light" size:29.0]; - title.text = NSLocalizedString(@"Select the sites you want to add", nil); - title.textColor = [UIColor whiteColor]; - title.numberOfLines = 0; - CGSize titleSize = [title suggestedSizeForWidth:AddUsersBlogMaxTextWidth]; - x = (_viewWidth - titleSize.width)/2.0; - y = CGRectGetHeight(headerView.frame) - titleSize.height - AddUsersBlogTitleVerticalOffset; - title.frame = CGRectMake(x, y, titleSize.width, titleSize.height); - [headerView addSubview:title]; - - return headerView; -} - -- (UIView *)checkmarkAccessoryView -{ - UIImage *image = [UIImage imageNamed:@"addBlogsSelectedImage"]; - UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; - return imageView; -} - -- (void)refreshBlogs -{ - NSURL *xmlrpc = [NSURL URLWithString:self.account.xmlrpc]; - NSString *username = self.account.username; - NSString *password = self.account.password; - - [self.tableView reloadData]; - [SVProgressHUD showWithStatus:NSLocalizedString(@"Loading sites...", nil) maskType:SVProgressHUDMaskTypeBlack]; - - WPXMLRPCClient *api = [WPXMLRPCClient clientWithXMLRPCEndpoint:xmlrpc]; - [api callMethod:@"wp.getUsersBlogs" - parameters:[NSArray arrayWithObjects:username, password, nil] - success:^(AFHTTPRequestOperation *operation, id responseObject) { - [SVProgressHUD dismiss]; - [self storeUsersVisibleBlogs:responseObject]; - - if (_usersBlogs.count == 0) { - if (self.onNoBlogsLoaded) { - self.onNoBlogsLoaded(self); - } - } else { - [self selectAppropriateBlog]; - - if(_usersBlogs.count == 1 && self.autoAddSingleBlog) { - [self selectAllBlogs]; - [self createBlogs]; - } - } - - [self toggleButtons]; - [self.tableView reloadData]; - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - [SVProgressHUD dismiss]; - [self.tableView reloadData]; - DDLogError(@"Failed getting user blogs: %@", [error localizedDescription]); - if (self.onErrorLoading) { - self.onErrorLoading(self, error); - } - }]; -} - -- (void)selectAppropriateBlog -{ - if (self.siteUrl == nil) { - [self selectFirstBlog]; - } else { - // This strips out any leading http:// or https:// making for an easier string match. - NSString *desiredBlogUrl = [[NSURL URLWithString:self.siteUrl] absoluteString]; - - __block BOOL blogFound = NO; - __block NSUInteger indexOfBlog; - [_usersBlogs enumerateObjectsUsingBlock:^(id blogInfo, NSUInteger index, BOOL *stop){ - NSString *blogUrl = [blogInfo objectForKey:@"url"]; - if ([blogUrl rangeOfString:desiredBlogUrl options:NSCaseInsensitiveSearch].location != NSNotFound) { - blogFound = YES; - [_selectedBlogs addObject:[blogInfo objectForKey:@"blogid"]]; - indexOfBlog = index; - *stop = YES; - } - }]; - - if (!blogFound) { - [self selectFirstBlog]; - } else { - // Let's make sure the blog we selected is at the top of the list the user sees. - NSMutableArray *rearrangedUsersBlogs = [NSMutableArray arrayWithArray:_usersBlogs]; - NSDictionary *selectedBlogInfo = [rearrangedUsersBlogs objectAtIndex:indexOfBlog]; - [rearrangedUsersBlogs removeObjectAtIndex:indexOfBlog]; - [rearrangedUsersBlogs insertObject:selectedBlogInfo atIndex:0]; - _usersBlogs = rearrangedUsersBlogs; - } - } -} - -- (void)selectFirstBlog -{ - NSString *firstBlogId = [[_usersBlogs objectAtIndex:0] objectForKey:@"blogid"]; - if (![_selectedBlogs containsObject:firstBlogId]) { - [_selectedBlogs addObject:firstBlogId]; - } -} - -- (void)storeUsersVisibleBlogs:(NSArray *)blogs -{ - _usersBlogs = [blogs filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { - NSNumber *hidden = [evaluatedObject objectForKey:@"hidden"]; - return ((hidden == nil) || [hidden boolValue]); - }]]; - [_usersBlogs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - NSString *title = [obj valueForKey:@"blogName"]; - title = [title stringByDecodingXMLCharacters]; - [obj setValue:title forKey:@"blogName"]; - }]; - _usersBlogs = [_usersBlogs sortedArrayUsingComparator:^(id obj1, id obj2){ - NSString *title1 = [obj1 valueForKey:@"blogName"]; - NSString *title2 = [obj2 valueForKey:@"blogName"]; - return [title1 localizedCaseInsensitiveCompare:title2]; - }]; -} - -- (void)createBlogs -{ - NSDictionary *properties = @{@"number_of_blogs": [NSNumber numberWithInt:[_selectedBlogs count]]}; - [WPMobileStats trackEventForSelfHostedAndWPCom:StatsEventAddBlogsClickedAddSelected properties:properties]; - - _addSelectedButton.enabled = NO; - - NSManagedObjectContext *context = [[ContextManager sharedInstance] backgroundContext]; - [context performBlock:^{ - for (NSDictionary *blog in _usersBlogs) { - if([_selectedBlogs containsObject:[blog valueForKey:@"blogid"]]) { - [self createBlog:blog withContext:context]; - } - } - - [[ContextManager sharedInstance] saveContext:context]; - - dispatch_async(dispatch_get_main_queue(), ^{ - if (self.blogAdditionCompleted) { - self.blogAdditionCompleted(self); - } - [[WordPressComApi sharedApi] syncPushNotificationInfo]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"BlogsRefreshNotification" object:nil]; - }); - }]; -} - -- (void)createBlog:(NSDictionary *)blogInfo withContext:(NSManagedObjectContext *)context -{ - DDLogInfo(@"creating blog: %@", blogInfo); - Blog *blog = [_account findOrCreateBlogFromDictionary:blogInfo withContext:context]; - blog.geolocationEnabled = YES; - - [context obtainPermanentIDsForObjects:@[blog] error:nil]; - [blog syncBlogWithSuccess:nil failure:nil]; -} - -- (void)toggleButtons -{ - _addSelectedButton.enabled = [_selectedBlogs count] > 0; - [_addSelectedButton setTitle:[NSString stringWithFormat:@"%@ (%d)", NSLocalizedString(@"Add Selected", nil), [_selectedBlogs count]] forState:UIControlStateNormal]; - _selectAllButton.enabled = [_usersBlogs count] != 0; - if ([_selectedBlogs count] == [_usersBlogs count]) { - [self setupDeselectAllButton]; - } else { - [self setupSelectAllButton]; - } -} - -- (void)selectAllBlogs -{ - [WPMobileStats trackEventForSelfHostedAndWPCom:StatsEventAddBlogsClickedSelectAll]; - - [self setupDeselectAllButton]; - - [_selectedBlogs removeAllObjects]; - for (NSDictionary *blogData in _usersBlogs) { - NSString *blogId = [blogData objectForKey:@"blogid"]; - [_selectedBlogs addObject:blogId]; - } - - [self toggleButtons]; - [self.tableView reloadData]; -} - -- (void)deselectAllBlogs -{ - [WPMobileStats trackEventForSelfHostedAndWPCom:StatsEventAddBlogsClickedDeselectAll]; - - [self setupSelectAllButton]; - - [_selectedBlogs removeAllObjects]; - - [self toggleButtons]; - [self.tableView reloadData]; -} - -- (void)setupSelectAllButton -{ - [_selectAllButton removeTarget:self action:@selector(deselectAllBlogs) forControlEvents:UIControlEventTouchUpInside]; - [_selectAllButton addTarget:self action:@selector(selectAllBlogs) forControlEvents:UIControlEventTouchUpInside]; - [_selectAllButton setTitle:NSLocalizedString(@"Select All", nil) forState:UIControlStateNormal]; -} - -- (void)setupDeselectAllButton -{ - [_selectAllButton removeTarget:self action:@selector(selectAllBlogs) forControlEvents:UIControlEventTouchUpInside]; - [_selectAllButton addTarget:self action:@selector(deselectAllBlogs) forControlEvents:UIControlEventTouchUpInside]; - [_selectAllButton setTitle:NSLocalizedString(@"Deselect All", nil) forState:UIControlStateNormal]; -} - -- (NSString *)getCellTitleForIndexPath:(NSIndexPath *)indexPath -{ - NSDictionary *blogData = [_usersBlogs objectAtIndex:indexPath.row]; - if ([[[blogData objectForKey:@"blogName"] trim] length] == 0) - return [blogData objectForKey:@"url"]; - else - return [blogData objectForKey:@"blogName"]; -} - -@end diff --git a/WordPress/Classes/NotificationsManager.m b/WordPress/Classes/NotificationsManager.m index 1b613231c4ea..4dba94d76d27 100644 --- a/WordPress/Classes/NotificationsManager.m +++ b/WordPress/Classes/NotificationsManager.m @@ -19,8 +19,7 @@ @implementation NotificationsManager + (void)registerForPushNotifications { - WordPressAppDelegate *appDelegate = [WordPressAppDelegate sharedWordPressApplicationDelegate]; - if (appDelegate.isWPcomAuthenticated) { + if ([WPAccount defaultWordPressComAccount]) { [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | diff --git a/WordPress/Classes/ReaderUsersBlogsViewController.m b/WordPress/Classes/ReaderUsersBlogsViewController.m index 2b8d110c21fb..266817af6913 100644 --- a/WordPress/Classes/ReaderUsersBlogsViewController.m +++ b/WordPress/Classes/ReaderUsersBlogsViewController.m @@ -10,6 +10,8 @@ #import "AddUsersBlogCell.h" #import "WordPressAppDelegate.h" #import "WPNUXUtility.h" +#import "WPAccount.h" +#import "Blog.h" @interface ReaderUsersBlogsViewController () @@ -17,7 +19,6 @@ @interface ReaderUsersBlogsViewController ())delegate { - (id)init { self = [super init]; if (self) { - self.blogs = [[NSUserDefaults standardUserDefaults] arrayForKey:@"wpcom_users_blogs"]; + self.blogs = [[WPAccount defaultWordPressComAccount] visibleBlogs]; self.primaryBlogId = [[NSUserDefaults standardUserDefaults] objectForKey:@"wpcom_users_prefered_blog_id"]; } return self; @@ -83,12 +84,11 @@ - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interface #pragma mark - Instance Methods -- (NSString *)getCellTitleForIndexPath:(NSIndexPath *)indexPath { - NSDictionary *dict = [_blogs objectAtIndex:indexPath.row]; - if ([[[dict objectForKey:@"blogName"] trim] length] == 0) { - return [dict objectForKey:@"url"]; +- (NSString *)cellTitleForBlog:(Blog *)blog { + if ([[blog.blogName trim] length] == 0) { + return blog.hostURL; } else { - return [dict objectForKey:@"blogName"]; + return blog.blogName; } } @@ -121,24 +121,25 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell.isWPCom = YES; } - NSDictionary *dict = [_blogs objectAtIndex:indexPath.row]; + Blog *blog = _blogs[indexPath.row]; cell.showTopSeparator = ( indexPath.row == 0 ) ? YES : NO; - cell.title = [self getCellTitleForIndexPath:indexPath]; - cell.blavatarUrl = [dict objectForKey:@"url"]; + cell.title = [self cellTitleForBlog:blog]; + cell.blavatarUrl = [blog blavatarUrl]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - return [AddUsersBlogCell rowHeightWithText:[self getCellTitleForIndexPath:indexPath]]; + Blog *blog = _blogs[indexPath.row]; + return [AddUsersBlogCell rowHeightWithText:[self cellTitleForBlog:blog]]; } #pragma mark - Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - NSDictionary *dict = [_blogs objectAtIndex:indexPath.row]; + NSDictionary *dict = _blogs[indexPath.row]; [self.delegate userDidSelectBlog:dict]; [self dismissViewControllerAnimated:YES completion:nil]; diff --git a/WordPress/Classes/SelectWPComBlogVisibilityViewController.h b/WordPress/Classes/SelectWPComBlogVisibilityViewController.h deleted file mode 100644 index c0f03f207a4a..000000000000 --- a/WordPress/Classes/SelectWPComBlogVisibilityViewController.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// SelectWPComBlogVisibilityViewController.h -// WordPress -// -// Created by Sendhil Panchadsaram on 4/16/13. -// Copyright (c) 2013 WordPress. All rights reserved. -// - -#import -#import "WordPressComApi.h" - -@protocol SelectWPComBlogVisibilityViewControllerDelegate; -@interface SelectWPComBlogVisibilityViewController : UITableViewController - -@property (nonatomic, assign) WordPressComApiBlogVisibility currentBlogVisibility; -@property (nonatomic, weak) id delegate; - -@end - -@protocol SelectWPComBlogVisibilityViewControllerDelegate - -- (void)selectWPComBlogVisibilityViewController:(SelectWPComBlogVisibilityViewController *)viewController didSelectBlogVisibilitySetting:(WordPressComApiBlogVisibility)visibility; - -@end diff --git a/WordPress/Classes/SelectWPComBlogVisibilityViewController.m b/WordPress/Classes/SelectWPComBlogVisibilityViewController.m deleted file mode 100644 index c499c82ebc71..000000000000 --- a/WordPress/Classes/SelectWPComBlogVisibilityViewController.m +++ /dev/null @@ -1,109 +0,0 @@ -// -// SelectWPComBlogVisibilityViewController.m -// WordPress -// -// Created by Sendhil Panchadsaram on 4/16/13. -// Copyright (c) 2013 WordPress. All rights reserved. -// - -#import "SelectWPComBlogVisibilityViewController.h" -#import "WPTableViewSectionFooterView.h" - -@interface SelectWPComBlogVisibilityViewController () { - NSArray *_visibilityOptions; -} - -@end - -@implementation SelectWPComBlogVisibilityViewController - -- (id)initWithStyle:(UITableViewStyle)style -{ - self = [super initWithStyle:style]; - - if (self) { - _visibilityOptions = @[ - NSLocalizedString(@"Public", nil), - NSLocalizedString(@"Hidden", nil), - NSLocalizedString(@"Private", nil)]; - _currentBlogVisibility = WordPressComApiBlogVisibilityPublic; - } - - return self; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - self.navigationItem.title = NSLocalizedString(@"Blog Visibility", nil); -} - -#pragma mark - Table view data source - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - // Return the number of rows in the section. - return [_visibilityOptions count]; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - static NSString *CellIdentifier = @"Cell"; - - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; - if (cell == nil) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; - } - - if (indexPath.row == self.currentBlogVisibility) { - cell.accessoryType = UITableViewCellAccessoryCheckmark; - } else { - cell.accessoryType = UITableViewCellAccessoryNone; - } - cell.textLabel.text = [_visibilityOptions objectAtIndex:indexPath.row]; - - return cell; -} - - -#pragma mark - Table view delegate - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - WordPressComApiBlogVisibility visibility; - if (indexPath.row == 0) { - visibility = WordPressComApiBlogVisibilityPublic; - } else if (indexPath.row == 1) { - visibility = WordPressComApiBlogVisibilityHidden; - } else { - visibility = WordPressComApiComBlogVisibilityPrivate; - } - [self.delegate selectWPComBlogVisibilityViewController:self didSelectBlogVisibilitySetting:visibility]; - [self.navigationController popViewControllerAnimated:YES]; -} - -#pragma mark - Private Methods - -- (NSString *)titleForFooterInSection:(NSInteger)section { - NSMutableString *text = [[NSMutableString alloc] init]; - [text appendFormat:@"%@ - %@\n\n", NSLocalizedString(@"Public", nil), NSLocalizedString(@"Blog_Visibility_Public_Description", nil)]; - [text appendFormat:@"%@ - %@\n\n", NSLocalizedString(@"Hidden", nil), NSLocalizedString(@"Blog_Visibility_Hidden_Description", nil)]; - [text appendFormat:@"%@ - %@", NSLocalizedString(@"Private", nil), NSLocalizedString(@"Blog_Visibility_Private_Description", nil)]; - - return text; -} - -- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section -{ - WPTableViewSectionFooterView *header = [[WPTableViewSectionFooterView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 0)]; - header.title = [self titleForFooterInSection:section]; - return header; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section -{ - NSString *title = [self titleForFooterInSection:section]; - return [WPTableViewSectionFooterView heightForTitle:title andWidth:CGRectGetWidth(self.view.bounds)]; -} - -@end diff --git a/WordPress/Classes/SettingsViewController.m b/WordPress/Classes/SettingsViewController.m index db47cca5d61c..1dd2c6938508 100644 --- a/WordPress/Classes/SettingsViewController.m +++ b/WordPress/Classes/SettingsViewController.m @@ -29,15 +29,15 @@ */ #import "SettingsViewController.h" -#import "WPcomLoginViewController.h" #import "WordPressComApi.h" #import "AboutViewController.h" #import "SettingsPageViewController.h" #import "NotificationSettingsViewController.h" +#import "Blog+Jetpack.h" +#import "LoginViewController.h" #import "SupportViewController.h" #import "WPAccount.h" #import "WPTableViewSectionHeaderView.h" -#import "AddUsersBlogsViewController.h" #import "SupportViewController.h" #import "ContextManager.h" @@ -50,7 +50,7 @@ CGFloat const blavatarImageViewSize = 43.f; -@interface SettingsViewController () +@interface SettingsViewController () @property (nonatomic, strong) NSArray *mediaSettingsArray; @property (nonatomic, strong) UIBarButtonItem *doneButton; @@ -70,22 +70,18 @@ - (void)viewDidLoad { self.doneButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Done", @"") style:[WPStyleGuide barButtonStyleForBordered] target:self action:@selector(dismiss)]; self.navigationItem.rightBarButtonItem = self.doneButton; - [[NSNotificationCenter defaultCenter] addObserverForName:WordPressComApiDidLoginNotification object:nil queue:nil usingBlock:^(NSNotification *note) { + [[NSNotificationCenter defaultCenter] addObserverForName:WPAccountDefaultWordPressComAccountChangedNotification object:nil queue:nil usingBlock:^(NSNotification *note) { NSMutableIndexSet *sections = [NSMutableIndexSet indexSet]; [sections addIndex:SettingsSectionWpcom]; [self.tableView reloadSections:sections withRowAnimation:UITableViewRowAnimationFade]; }]; - [[NSNotificationCenter defaultCenter] addObserverForName:WordPressComApiDidLogoutNotification object:nil queue:nil usingBlock:^(NSNotification *note) { - NSMutableIndexSet *sections = [NSMutableIndexSet indexSet]; - [sections addIndex:SettingsSectionWpcom]; - [self.tableView reloadSections:sections withRowAnimation:UITableViewRowAnimationFade]; - }]; - + [WPStyleGuide configureColorsForView:self.view andTableView:self.tableView]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; + [self.navigationController setNavigationBarHidden:NO animated:animated]; [self.tableView reloadData]; } @@ -156,7 +152,6 @@ - (BOOL)supportsNotifications { return nil != [[NSUserDefaults standardUserDefaults] objectForKey:kApnsDeviceTokenPrefKey]; } - #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { @@ -165,32 +160,28 @@ - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // The Sign Out row in Wpcom section can change, so identify it dynamically - (NSInteger)rowForSignOut { - return [self supportsNotifications] ? 2 : 1; + NSInteger rowForSignOut = 1; + if ([self supportsNotifications]) { + rowForSignOut += 1; + } + return rowForSignOut; } - (NSInteger)rowForNotifications { - return [self supportsNotifications] ? 1 : -1; + if ([self supportsNotifications]) { + return 1; + } + return -1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - int numWpcomRows = 0; - switch (section) { case SettingsSectionWpcom: - numWpcomRows = 1; - if ([[WordPressComApi sharedApi] hasCredentials]) { - // Allow notifications management? - if ([self supportsNotifications]) { - numWpcomRows += 1; - } - } - - // Show a Sign Out row? - if ([[WPAccount defaultWordPressComAccount] username]) { - numWpcomRows += 1; + if ([WPAccount defaultWordPressComAccount]) { + return [self rowForSignOut] + 1; + } else { + return 1; } - - return numWpcomRows; case SettingsSectionMedia: return [self.mediaSettingsArray count]; @@ -236,22 +227,26 @@ - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPa cell.accessoryView = nil; if (indexPath.section == SettingsSectionWpcom) { - if ([[WordPressComApi sharedApi] hasCredentials]) { + if ([WPAccount defaultWordPressComAccount]) { if (indexPath.row == 0) { cell.textLabel.text = NSLocalizedString(@"Username", @""); cell.detailTextLabel.text = [[WPAccount defaultWordPressComAccount] username]; cell.detailTextLabel.textColor = [UIColor UIColorFromHex:0x888888]; cell.selectionStyle = UITableViewCellSelectionStyleNone; + cell.accessibilityIdentifier = @"wpcom-username"; } else if (indexPath.row == [self rowForNotifications]) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.textLabel.text = NSLocalizedString(@"Manage Notifications", @""); + cell.accessibilityIdentifier = @"wpcom-manage-notifications"; } else { cell.textLabel.textAlignment = NSTextAlignmentCenter; cell.textLabel.text = NSLocalizedString(@"Sign Out", @"Sign out from WordPress.com"); + cell.accessibilityIdentifier = @"wpcom-sign-out"; } } else { cell.textLabel.textAlignment = NSTextAlignmentCenter; cell.textLabel.text = NSLocalizedString(@"Sign In", @"Sign in to WordPress.com"); + cell.accessibilityIdentifier = @"wpcom-sign-in"; cell.selectionStyle = UITableViewCellSelectionStyleBlue; } @@ -301,7 +296,7 @@ - (UITableViewCell *)cellForIndexPath:(NSIndexPath *)indexPath { switch (indexPath.section) { case SettingsSectionWpcom: - if ([[WordPressComApi sharedApi] hasCredentials] && indexPath.row == 0) { + if ([WPAccount defaultWordPressComAccount] && indexPath.row == 0) { cellIdentifier = @"WpcomUsernameCell"; cellStyle = UITableViewCellStyleValue1; } else { @@ -358,7 +353,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath [tableView deselectRowAtIndexPath:indexPath animated:YES]; if (indexPath.section == SettingsSectionWpcom) { - if ([[WordPressComApi sharedApi] hasCredentials]) { + if ([WPAccount defaultWordPressComAccount]) { if (indexPath.row == [self rowForSignOut]) { [WPMobileStats trackEventForWPCom:StatsEventSettingsClickedSignOutOfDotCom]; @@ -380,9 +375,12 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath } } else { [WPMobileStats trackEventForWPCom:StatsEventSettingsClickedSignIntoDotCom]; - - WPcomLoginViewController *loginViewController = [[WPcomLoginViewController alloc] initWithStyle:UITableViewStyleGrouped]; - loginViewController.delegate = self; + + LoginViewController *loginViewController = [[LoginViewController alloc] init]; + loginViewController.onlyDotComAllowed = YES; + loginViewController.dismissBlock = ^{ + [self.navigationController popToViewController:self animated:YES]; + }; [self.navigationController pushViewController:loginViewController animated:YES]; } @@ -413,30 +411,14 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath } } - -#pragma mark - WPComLoginViewControllerDelegate - -- (void)loginController:(WPcomLoginViewController *)loginController didAuthenticateWithAccount:(WPAccount *)account { - [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:SettingsSectionWpcom] withRowAnimation:UITableViewRowAnimationFade]; - AddUsersBlogsViewController *addUsersBlogsView = [[AddUsersBlogsViewController alloc] initWithAccount:[WPAccount defaultWordPressComAccount]]; - addUsersBlogsView.isWPcom = YES; - [self.navigationController pushViewController:addUsersBlogsView animated:YES]; -} - - -- (void)loginControllerDidDismiss:(WPcomLoginViewController *)loginController { - [self.navigationController popToRootViewControllerAnimated:YES]; -} - - -#pragma mark - Action Sheet Delegate Methods +#pragma mark - +#pragma mark Action Sheet Delegate Methods - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex == 0) { [WPMobileStats trackEventForWPCom:StatsEventSettingsSignedOutOfDotCom]; // Sign out - [[WordPressComApi sharedApi] signOut]; //Signout first, then remove the account [WPAccount removeDefaultWordPressComAccount]; [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:SettingsSectionWpcom] withRowAnimation:UITableViewRowAnimationFade]; diff --git a/WordPress/Classes/WPAccount.h b/WordPress/Classes/WPAccount.h index 8a23a390b17a..b69769cec77a 100644 --- a/WordPress/Classes/WPAccount.h +++ b/WordPress/Classes/WPAccount.h @@ -9,6 +9,10 @@ #import #import +#import + +#import "WordPressComApi.h" + @class Blog; extern NSString * const WPAccountDefaultWordPressComAccountChangedNotification; @@ -24,6 +28,7 @@ extern NSString * const WPAccountDefaultWordPressComAccountChangedNotification; @property (nonatomic, readonly) BOOL isWpcom; @property (nonatomic, retain) NSSet *blogs; @property (nonatomic, retain) NSSet *jetpackBlogs; +@property (nonatomic, readonly) NSArray *visibleBlogs; /** The account's password @@ -75,18 +80,31 @@ extern NSString * const WPAccountDefaultWordPressComAccountChangedNotification; @param username the WordPress.com account's username @param password the WordPress.com account's password + @param authToken the OAuth2 token returned by signIntoWordPressDotComWithUsername:password:success:failure: + @param context the NSManagedObjectContext used to create or update the account @return a WordPress.com `WPAccount` object for the given `username` */ + (WPAccount *)createOrUpdateWordPressComAccountWithUsername:(NSString *)username - andPassword:(NSString *)password - withContext:(NSManagedObjectContext *)context; + password:(NSString *)password + authToken:(NSString *)authToken + context:(NSManagedObjectContext *)context; /** - Same as above but defaults to a background context + Creates a new WordPress.com account or updates the password if there is a matching account + + There can only be one WordPress.com account per username, so if one already exists for the given `username` its password is updated + Uses a background managed object context. + + @param username the WordPress.com account's username + @param password the WordPress.com account's password + @param authToken the OAuth2 token returned by signIntoWordPressDotComWithUsername:password:success:failure: + @return a WordPress.com `WPAccount` object for the given `username` + @see createOrUpdateWordPressComAccountWithUsername:password:authToken:context: */ + (WPAccount *)createOrUpdateWordPressComAccountWithUsername:(NSString *)username - andPassword:(NSString *)password; + password:(NSString *)password + authToken:(NSString *)authToken; /** Creates a new self hosted account or updates the password if there is a matching account @@ -96,6 +114,7 @@ extern NSString * const WPAccountDefaultWordPressComAccountChangedNotification; @param xmlrpc the account XML-RPC endpoint @param username the account's username @param password the account's password + @param context the NSManagedObjectContext used to create or update the account @return a `WPAccount` object for the given `xmlrpc` endpoint and `username` */ + (WPAccount *)createOrUpdateSelfHostedAccountWithXmlrpc:(NSString *)xmlrpc @@ -103,8 +122,16 @@ extern NSString * const WPAccountDefaultWordPressComAccountChangedNotification; andPassword:(NSString *)password withContext:(NSManagedObjectContext *)context; /** - Same as above but defaults to a background context + Creates a new self hosted account or updates the password if there is a matching account + + There can only be one account per XML-RPC endpoint and username, so if one already exists its password is updated + Uses a background managed object context. + + @param xmlrpc the account XML-RPC endpoint + @param username the account's username + @param password the account's password + @return a `WPAccount` object for the given `xmlrpc` endpoint and `username` */ + (WPAccount *)createOrUpdateSelfHostedAccountWithXmlrpc:(NSString *)xmlrpc username:(NSString *)username @@ -119,11 +146,27 @@ extern NSString * const WPAccountDefaultWordPressComAccountChangedNotification; If a there is an existing blog with the same `url`, it is returned as-is. - @param blogInfo a dictionary containing `url`, `blogName`, `xmlrpc`, `blogid`, and `isAdmin`; as returned by `wp.getUsersBlogs` + @param blogInfo a dictionary containing `url`, `blogName`, `xmlrpc`, and `blogid`; as returned by `wp.getUsersBlogs` @return the newly created blog */ - (Blog *)findOrCreateBlogFromDictionary:(NSDictionary *)blogInfo withContext:(NSManagedObjectContext*)context; +- (void)syncBlogsWithSuccess:(void (^)())success failure:(void (^)(NSError *error))failure; + +///------------------ +/// @name API Helpers +///------------------ + +/** + A WordPressComApi object if the account is a WordPress.com account. Otherwise, it returns `nil` + */ +@property (nonatomic, readonly) WordPressComApi *restApi; + +/** + A WordPressXMLRPCApi object configured for the XML-RPC endpoint + */ +@property (nonatomic, readonly) WordPressXMLRPCApi *xmlrpcApi; + @end @interface WPAccount (CoreDataGeneratedAccessors) diff --git a/WordPress/Classes/WPAccount.m b/WordPress/Classes/WPAccount.m index 07efd208a7cc..5de42e571b4d 100644 --- a/WordPress/Classes/WPAccount.m +++ b/WordPress/Classes/WPAccount.m @@ -17,6 +17,7 @@ static NSString * const DefaultDotcomAccountDefaultsKey = @"AccountDefaultDotcom"; static NSString * const DotcomXmlrpcKey = @"https://wordpress.com/xmlrpc.php"; +static NSString * const OauthTokenServiceName = @"public-api.wordpress.com"; static WPAccount *__defaultDotcomAccount = nil; NSString * const WPAccountDefaultWordPressComAccountChangedNotification = @"WPAccountDefaultWordPressComAccountChangedNotification"; @@ -24,10 +25,14 @@ @interface WPAccount () @property (nonatomic, retain) NSString *xmlrpc; @property (nonatomic, retain) NSString *username; +@property (nonatomic, retain) NSString *authToken; @property (nonatomic) BOOL isWpcom; @end -@implementation WPAccount +@implementation WPAccount { + WordPressComApi *_restApi; + WordPressXMLRPCApi *_xmlrpcApi; +} @dynamic xmlrpc; @dynamic username; @@ -92,7 +97,9 @@ + (void)removeDefaultWordPressComAccount { - (void)prepareForDeletion { // Invoked automatically by the Core Data framework when the receiver is about to be deleted. if (__defaultDotcomAccount == self) { - [[WordPressComApi sharedApi] cancelAllHTTPOperationsWithMethod:nil path:nil]; + [[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]; [[NSNotificationCenter defaultCenter] postNotificationName:WPAccountDefaultWordPressComAccountChangedNotification object:nil]; @@ -101,16 +108,14 @@ - (void)prepareForDeletion { #pragma mark - Account creation -+ (WPAccount *)createOrUpdateWordPressComAccountWithUsername:(NSString *)username andPassword:(NSString *)password { - return [WPAccount createOrUpdateWordPressComAccountWithUsername:username andPassword:password withContext:[[ContextManager sharedInstance] backgroundContext]]; ++ (WPAccount *)createOrUpdateWordPressComAccountWithUsername:(NSString *)username password:(NSString *)password authToken:(NSString *)authToken { + return [WPAccount createOrUpdateWordPressComAccountWithUsername:username password:password authToken:authToken context:[[ContextManager sharedInstance] backgroundContext]]; } -+ (WPAccount *)createOrUpdateWordPressComAccountWithUsername:(NSString *)username andPassword:(NSString *)password withContext:(NSManagedObjectContext *)context { ++ (WPAccount *)createOrUpdateWordPressComAccountWithUsername:(NSString *)username password:(NSString *)password authToken:(NSString *)authToken context:(NSManagedObjectContext *)context { WPAccount *account = [self createOrUpdateSelfHostedAccountWithXmlrpc:DotcomXmlrpcKey username:username andPassword:password withContext:context]; account.isWpcom = YES; - if (__defaultDotcomAccount == nil) { - [self setDefaultWordPressComAccount:account]; - } + account.authToken = authToken; return account; } @@ -164,12 +169,41 @@ - (Blog *)findOrCreateBlogFromDictionary:(NSDictionary *)blogInfo withContext:(N blog.blogID = [NSNumber numberWithInt:[[blogInfo objectForKey:@"blogid"] intValue]]; blog.blogName = [[blogInfo objectForKey:@"blogName"] stringByDecodingXMLCharacters]; blog.xmlrpc = [blogInfo objectForKey:@"xmlrpc"]; - blog.isAdmin = [NSNumber numberWithInt:[[blogInfo objectForKey:@"isAdmin"] intValue]]; + + DDLogInfo(@"Created blog: %@", blog); }]; return blog; } +- (void)syncBlogsWithSuccess:(void (^)())success failure:(void (^)(NSError *error))failure { + WPFLogMethod(); + [self.xmlrpcApi getBlogsWithSuccess:^(NSArray *blogs) { + [self mergeBlogs:blogs withCompletion:success]; + } failure:^(NSError *error) { + DDLogError(@"Error syncing blogs: %@", error); + if (failure) { + failure(error); + } + }]; +} + +- (void)mergeBlogs:(NSArray *)blogs withCompletion:(void (^)())completion { + NSManagedObjectContext *backgroundMOC = [[ContextManager sharedInstance] backgroundContext]; + + NSManagedObjectID *accountID = self.objectID; + [backgroundMOC performBlock:^{ + WPAccount *account = (WPAccount *)[backgroundMOC objectWithID:accountID]; + for (NSDictionary *blog in blogs) { + [account findOrCreateBlogFromDictionary:blog withContext:backgroundMOC]; + } + [[ContextManager sharedInstance] saveContext:backgroundMOC]; + if (completion != nil) { + dispatch_async(dispatch_get_main_queue(), completion); + } + }]; +} + #pragma mark - Custom accessors - (NSString *)password { @@ -183,7 +217,56 @@ - (void)setPassword:(NSString *)password { forServiceName:self.xmlrpc updateExisting:YES error:nil]; + } else { + [SFHFKeychainUtils deleteItemForUsername:self.username + andServiceName:self.xmlrpc + error:nil]; + } +} + +- (NSString *)authToken { + return [SFHFKeychainUtils getPasswordForUsername:self.username andServiceName:OauthTokenServiceName error:nil]; +} + +- (void)setAuthToken:(NSString *)authToken { + if (authToken) { + [SFHFKeychainUtils storeUsername:self.username + andPassword:authToken + forServiceName:OauthTokenServiceName + updateExisting:YES + error:nil]; + } else { + [SFHFKeychainUtils deleteItemForUsername:self.username + andServiceName:OauthTokenServiceName + error:nil]; + } +} + +- (NSArray *)visibleBlogs { + NSSet *visibleBlogs = [self.blogs filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"visible = YES"]]; + NSArray *sortedBlogs = [visibleBlogs sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"blogName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]]]; + return sortedBlogs; +} + +#pragma mark - API Helpers + +- (WordPressComApi *)restApi +{ + if (!self.isWpcom) { + return nil; + } + + if (!_restApi) { + _restApi = [[WordPressComApi alloc] initWithOAuthToken:self.authToken]; + } + return _restApi; +} + +- (WordPressXMLRPCApi *)xmlrpcApi { + if (!_xmlrpcApi) { + _xmlrpcApi = [WordPressXMLRPCApi apiWithXMLRPCEndpoint:[NSURL URLWithString:self.xmlrpc] username:self.username password:self.password]; } + return _xmlrpcApi; } @end diff --git a/WordPress/Classes/WPError.m b/WordPress/Classes/WPError.m index 028d972d1df2..4ca7b0a4329a 100644 --- a/WordPress/Classes/WPError.m +++ b/WordPress/Classes/WPError.m @@ -65,12 +65,7 @@ + (void)showAlertWithError:(NSError *)error title:(NSString *)title { } else if ([error.domain isEqualToString:WordPressComApiErrorDomain]) { DDLogError(@"wp.com API error: %@: %@", [error.userInfo objectForKey:WordPressComApiErrorCodeKey], [error localizedDescription]); if (error.code == WordPressComApiErrorInvalidToken || error.code == WordPressComApiErrorAuthorizationRequired) { - if ([[WPAccount defaultWordPressComAccount] password] == nil) { - [WPcomLoginViewController presentLoginScreen]; - } - [[WordPressComApi sharedApi] refreshTokenWithSuccess:nil failure:^(NSError *error) { - [WPcomLoginViewController presentLoginScreen]; - }]; + [WPcomLoginViewController presentLoginScreen]; return; } } diff --git a/WordPress/Classes/WPMobileStats.h b/WordPress/Classes/WPMobileStats.h index cc4fce7909bf..ebeadf37cf72 100644 --- a/WordPress/Classes/WPMobileStats.h +++ b/WordPress/Classes/WPMobileStats.h @@ -169,11 +169,6 @@ extern NSString *const StatsEventManageNotificationsDisabledBlogNotifications; extern NSString *const StatsEventQuickPhotoOpened; extern NSString *const StatsEventQuickPhotoPosted; -// Welcome View Controller -extern NSString *const StatsEventWelcomeViewControllerClickedAddSelfHostedBlog; -extern NSString *const StatsEventWelcomeViewControllerClickedAddWordpressDotComBlog; -extern NSString *const StatsEventWelcomeViewControllerClickedCreateWordpressDotComBlog; - // NUX Related extern NSString *const StatsEventNUXFirstWalkthroughOpened; extern NSString *const StatsEventNUXFirstWalkthroughClickedSkipToCreateAccount; diff --git a/WordPress/Classes/WPMobileStats.m b/WordPress/Classes/WPMobileStats.m index c4e53382fc66..c68327e3bd68 100644 --- a/WordPress/Classes/WPMobileStats.m +++ b/WordPress/Classes/WPMobileStats.m @@ -179,11 +179,6 @@ NSString *const StatsEventQuickPhotoOpened = @"Quick Photo - Opened"; NSString *const StatsEventQuickPhotoPosted = @"Quick Photo - Posted"; -// Welcome View Controller -NSString *const StatsEventWelcomeViewControllerClickedAddSelfHostedBlog = @"Welcome View Controller - Add Self Hosted Blog"; -NSString *const StatsEventWelcomeViewControllerClickedAddWordpressDotComBlog = @"Welcome View Controller - Add Wordpress.com Blog"; -NSString *const StatsEventWelcomeViewControllerClickedCreateWordpressDotComBlog = @"Welcome View Controller - Create Wordpress.com Blog"; - // NUX First Walkthrough NSString *const StatsEventNUXFirstWalkthroughOpened = @"NUX - First Walkthrough - Opened"; NSString *const StatsEventNUXFirstWalkthroughClickedSkipToCreateAccount = @"NUX - First Walkthrough - Skipped to Create Account"; diff --git a/WordPress/Classes/WPcomLoginViewController.m b/WordPress/Classes/WPcomLoginViewController.m index 92ae71f876c2..6d4c1fa871b2 100644 --- a/WordPress/Classes/WPcomLoginViewController.m +++ b/WordPress/Classes/WPcomLoginViewController.m @@ -342,7 +342,8 @@ - (void)signIn:(id)sender { [self.wpComApi signInWithUsername:username password:password success:^{ - WPAccount *account = [WPAccount createOrUpdateWordPressComAccountWithUsername:username andPassword:password]; + 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]; diff --git a/WordPress/Classes/WelcomeViewController.h b/WordPress/Classes/WelcomeViewController.h deleted file mode 100644 index a6255d0b7104..000000000000 --- a/WordPress/Classes/WelcomeViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// WelcomeViewController.h -// WordPress -// -// Created by Sendhil Panchadsaram on 9/4/13. -// Copyright (c) 2013 WordPress. All rights reserved. -// - -#import - -@interface WelcomeViewController : UITableViewController - -@end diff --git a/WordPress/Classes/WelcomeViewController.m b/WordPress/Classes/WelcomeViewController.m deleted file mode 100644 index e692a6d39679..000000000000 --- a/WordPress/Classes/WelcomeViewController.m +++ /dev/null @@ -1,205 +0,0 @@ -// -// WelcomeViewController.m -// WordPress -// -// Created by Sendhil Panchadsaram on 9/4/13. -// Copyright (c) 2013 WordPress. All rights reserved. -// - -#import "WelcomeViewController.h" -#import "WordPressAppDelegate.h" -#import "AddSiteViewController.h" -#import "WPcomLoginViewController.h" -#import "CreateWPComAccountViewController.h" -#import "CreateWPComBlogViewController.h" -#import "AddUsersBlogsViewController.h" -#import "WordPressComApi.h" -#import "WPAccount.h" -#import "WPTableViewSectionHeaderView.h" - -@interface WelcomeViewController () < - WPcomLoginViewControllerDelegate, - CreateWPComAccountViewControllerDelegate, - CreateWPComBlogViewControllerDelegate> { - NSArray *_buttonTitles; - NSArray *_sectionHeaderTitles; - WordPressAppDelegate *__weak _appDelegate; -} - -@end - -@implementation WelcomeViewController - -- (id)initWithStyle:(UITableViewStyle)style -{ - self = [super initWithStyle:style]; - if (self) { - _buttonTitles = @[@[NSLocalizedString(@"Add Self-Hosted Site", nil), NSLocalizedString(@"Add WordPress.com Site", nil)], @[NSLocalizedString(@"Create WordPress.com Site", nil)]]; - _sectionHeaderTitles = @[NSLocalizedString(@"Add an existing Site:", nil), NSLocalizedString(@"Start a new Site:", nil)]; - } - return self; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - _appDelegate = (WordPressAppDelegate *)[[UIApplication sharedApplication] delegate]; - - [WPStyleGuide configureColorsForView:self.view andTableView:self.tableView]; - - [self.tableView registerClass:[WPTableViewCell class] forCellReuseIdentifier:@"Cell"]; -} - -#pragma mark - Table view data source - -- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { - WPTableViewSectionHeaderView *header = [[WPTableViewSectionHeaderView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 0)]; - header.title = [self titleForHeaderInSection:section]; - return header; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section -{ - NSString *title = [self titleForHeaderInSection:section]; - return [WPTableViewSectionHeaderView heightForTitle:title andWidth:CGRectGetWidth(self.view.bounds)]; -} - -- (NSString *)titleForHeaderInSection:(NSInteger)section -{ - return _sectionHeaderTitles[section]; -} - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - // Return the number of sections. - return [_buttonTitles count]; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - // Return the number of rows in the section. - return [_buttonTitles[section] count]; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - static NSString *CellIdentifier = @"Cell"; - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; - - cell.textLabel.text = _buttonTitles[indexPath.section][indexPath.row]; - cell.textLabel.textAlignment = NSTextAlignmentCenter; - cell.textLabel.font = [WPStyleGuide tableviewTextFont]; - cell.textLabel.textColor = [WPStyleGuide tableViewActionColor]; - - return cell; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - if ([self isIndexPathForAddSelfHostedBlog:indexPath]) { - [self handleAddSelfHostedBlog]; - } else if ([self isIndexPathForAddWordPressDotComBlog:indexPath]) { - [self handleAddWordPressDotComBlog]; - } else if ([self isIndexPathForCreateWordPressDotComBlog:indexPath]) { - [self handleCreateWordPressDotComBlog]; - } -} - -#pragma mark - Private Methods - -- (BOOL)isIndexPathForAddSelfHostedBlog:(NSIndexPath *)indexPath -{ - return indexPath.section == 0 && indexPath.row == 0; -} - -- (BOOL)isIndexPathForAddWordPressDotComBlog:(NSIndexPath *)indexPath -{ - return indexPath.section == 0 && indexPath.row == 1; -} - -- (BOOL)isIndexPathForCreateWordPressDotComBlog:(NSIndexPath *)indexPath -{ - return indexPath.section == 1 && indexPath.row == 0; -} - -- (void)handleAddSelfHostedBlog -{ - [WPMobileStats trackEventForWPCom:StatsEventWelcomeViewControllerClickedAddSelfHostedBlog]; - - AddSiteViewController *addSiteView = [[AddSiteViewController alloc] initWithNibName:nil bundle:nil]; - [self.navigationController pushViewController:addSiteView animated:YES]; -} - - -- (void)handleAddWordPressDotComBlog -{ - [WPMobileStats trackEventForWPCom:StatsEventWelcomeViewControllerClickedAddWordpressDotComBlog]; - - if(_appDelegate.isWPcomAuthenticated) { - AddUsersBlogsViewController *addUsersBlogsView = [[AddUsersBlogsViewController alloc] initWithAccount:[WPAccount defaultWordPressComAccount]]; - addUsersBlogsView.isWPcom = YES; - [self.navigationController pushViewController:addUsersBlogsView animated:YES]; - } - else { - WPcomLoginViewController *wpLoginView = [[WPcomLoginViewController alloc] initWithStyle:UITableViewStyleGrouped]; - wpLoginView.delegate = self; - [self.navigationController pushViewController:wpLoginView animated:YES]; - } -} - -- (void)handleCreateWordPressDotComBlog -{ - [WPMobileStats trackEventForWPCom:StatsEventWelcomeViewControllerClickedCreateWordpressDotComBlog]; - - if ([WordPressComApi sharedApi].hasCredentials) { - CreateWPComBlogViewController *viewController = [[CreateWPComBlogViewController alloc] initWithStyle:UITableViewStyleGrouped]; - viewController.delegate = self; - [self.navigationController pushViewController:viewController animated:YES]; - } else { - CreateWPComAccountViewController *viewController = [[CreateWPComAccountViewController alloc] initWithStyle:UITableViewStyleGrouped]; - viewController.delegate = self; - [self.navigationController pushViewController:viewController animated:YES]; - } -} - -#pragma mark - WPcomLoginViewControllerDelegate - -- (void)loginControllerDidDismiss:(WPcomLoginViewController *)loginController { - [self.navigationController popViewControllerAnimated:YES]; -} - - -- (void)loginController:(WPcomLoginViewController *)loginController didAuthenticateWithAccount:(WPAccount *)account { - [self.navigationController popViewControllerAnimated:NO]; - [self handleAddWordPressDotComBlog]; -} - -#pragma mark - CreateWPComAccountViewControllerDelegate - -- (void)createdAndSignedInAccountWithUserName:(NSString *)userName -{ - [self.navigationController popViewControllerAnimated:NO]; - [self handleAddWordPressDotComBlog]; -} - -- (void)createdAccountWithUserName:(NSString *)userName -{ - // In this case the user was able to create an account but for some reason was unable to sign in. - // Just present the login controller in this case with the data prefilled and give the user the chance to sign in again - [self.navigationController popViewControllerAnimated:NO]; - WPcomLoginViewController *wpLoginView = [[WPcomLoginViewController alloc] initWithStyle:UITableViewStyleGrouped]; - wpLoginView.delegate = self; - wpLoginView.predefinedUsername = userName; - [self.navigationController pushViewController:wpLoginView animated:YES]; -} - -#pragma mark - CreateWPComBlogViewControllerDelegate - -- (void)createdBlogWithDetails:(NSDictionary *)blogDetails -{ - [self.navigationController popToRootViewControllerAnimated:YES]; -} - - -@end diff --git a/WordPress/Classes/WordPress.xcdatamodeld/WordPress 13.xcdatamodel/contents b/WordPress/Classes/WordPress.xcdatamodeld/WordPress 13.xcdatamodel/contents index 6f6608cc6250..35b54e25219e 100644 --- a/WordPress/Classes/WordPress.xcdatamodeld/WordPress 13.xcdatamodel/contents +++ b/WordPress/Classes/WordPress.xcdatamodeld/WordPress 13.xcdatamodel/contents @@ -119,9 +119,6 @@ - - - @@ -150,6 +147,7 @@ + diff --git a/WordPress/Classes/WordPressAppDelegate.h b/WordPress/Classes/WordPressAppDelegate.h index 01bb1e5f3b17..225d37548b94 100644 --- a/WordPress/Classes/WordPressAppDelegate.h +++ b/WordPress/Classes/WordPressAppDelegate.h @@ -26,6 +26,8 @@ + (WordPressAppDelegate *)sharedWordPressApplicationDelegate; +- (void)showWelcomeScreenIfNeededAnimated:(BOOL)animated; + ///-------------------- /// @name Global Alerts ///-------------------- diff --git a/WordPress/Classes/WordPressAppDelegate.m b/WordPress/Classes/WordPressAppDelegate.m index 4387687dd8f1..ec3ffd2e57d6 100644 --- a/WordPress/Classes/WordPressAppDelegate.m +++ b/WordPress/Classes/WordPressAppDelegate.m @@ -32,6 +32,7 @@ #import #import #import "ContextManager.h" +#import "ReaderPost.h" #import #import @@ -98,7 +99,7 @@ - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions: self.window.rootViewController = self.tabBarController; [self.window makeKeyAndVisible]; - [self showWelcomeScreenIfNeeded]; + [self showWelcomeScreenIfNeededAnimated:NO]; // Push notifications [NotificationsManager registerForPushNotifications]; @@ -266,7 +267,7 @@ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(N #pragma mark - Custom methods -- (void)showWelcomeScreenIfNeeded { +- (void)showWelcomeScreenIfNeededAnimated:(BOOL)animated { if ([self noBlogsAndNoWordPressDotComAccount]) { [WordPressAppDelegate wipeAllKeychainItems]; @@ -275,7 +276,11 @@ - (void)showWelcomeScreenIfNeeded { aNavigationController.navigationBar.translucent = NO; aNavigationController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; aNavigationController.modalPresentationStyle = UIModalPresentationFormSheet; - + + UIViewController *presenter = self.window.rootViewController; + if (presenter.presentedViewController) { + [presenter dismissViewControllerAnimated:animated completion:nil]; + } [self.window.rootViewController presentViewController:aNavigationController animated:NO completion:nil]; } } @@ -572,23 +577,18 @@ - (void)configureCrashlytics { [Crashlytics startWithAPIKey:[WordPressComApiCredentials crashlyticsApiKey]]; [[Crashlytics sharedInstance] setDelegate:self]; - BOOL hasCredentials = [[WordPressComApi sharedApi] hasCredentials]; + BOOL hasCredentials = ([WPAccount defaultWordPressComAccount] != nil); [self setCommonCrashlyticsParameters]; if (hasCredentials && [[WPAccount defaultWordPressComAccount] username] != nil) { [Crashlytics setUserName:[[WPAccount defaultWordPressComAccount] username]]; } - void (^wpcomLoggedInBlock)(NSNotification *) = ^(NSNotification *note) { + void (^accountChangedBlock)(NSNotification *) = ^(NSNotification *note) { [Crashlytics setUserName:[[WPAccount defaultWordPressComAccount] username]]; [self setCommonCrashlyticsParameters]; }; - void (^wpcomLoggedOutBlock)(NSNotification *) = ^(NSNotification *note) { - [Crashlytics setUserName:nil]; - [self setCommonCrashlyticsParameters]; - }; - [[NSNotificationCenter defaultCenter] addObserverForName:WordPressComApiDidLoginNotification object:nil queue:nil usingBlock:wpcomLoggedInBlock]; - [[NSNotificationCenter defaultCenter] addObserverForName:WordPressComApiDidLogoutNotification object:nil queue:nil usingBlock:wpcomLoggedOutBlock]; + [[NSNotificationCenter defaultCenter] addObserverForName:WPAccountDefaultWordPressComAccountChangedNotification object:nil queue:nil usingBlock:accountChangedBlock]; } - (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id)crash @@ -604,8 +604,9 @@ - (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecu - (void)setCommonCrashlyticsParameters { - [Crashlytics setObjectValue:[NSNumber numberWithBool:[[WordPressComApi sharedApi] hasCredentials]] forKey:@"logged_in"]; - [Crashlytics setObjectValue:@([[WordPressComApi sharedApi] hasCredentials]) forKey:@"connected_to_dotcom"]; + BOOL loggedIn = [WPAccount defaultWordPressComAccount] != nil; + [Crashlytics setObjectValue:@(loggedIn) forKey:@"logged_in"]; + [Crashlytics setObjectValue:@(loggedIn) forKey:@"connected_to_dotcom"]; [Crashlytics setObjectValue:@([Blog countWithContext:[[ContextManager sharedInstance] mainContext]]) forKey:@"number_of_blogs"]; } @@ -1003,8 +1004,7 @@ - (NSString *) getLogFilesContentWithMaxSize:(NSInteger)maxSize { - (void)toggleExtraDebuggingIfNeeded { if (!_listeningForBlogChanges) { _listeningForBlogChanges = YES; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleLogoutOrBlogsChangedNotification:) name:BlogChangedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleLogoutOrBlogsChangedNotification:) name:WordPressComApiDidLogoutNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDefaultAccountChangedNotification:) name:WPAccountDefaultWordPressComAccountChangedNotification object:nil]; } int num_blogs = [Blog countWithContext:[[ContextManager sharedInstance] mainContext]]; @@ -1038,8 +1038,14 @@ - (void)toggleExtraDebuggingIfNeeded { } } -- (void)handleLogoutOrBlogsChangedNotification:(NSNotification *)notification { +- (void)handleDefaultAccountChangedNotification:(NSNotification *)notification { [self toggleExtraDebuggingIfNeeded]; + [NotificationsManager registerForPushNotifications]; + [self showWelcomeScreenIfNeededAnimated:NO]; + // If the notification object is not nil, then it's a login + if (notification.object) { + [ReaderPost fetchPostsWithCompletionHandler:nil]; + } } @end diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 8eddd68a0e54..6fe4be756536 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -526,7 +526,6 @@ 83610AAA11F4AD2C00421116 /* WPcomLoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83610AA811F4AD2C00421116 /* WPcomLoginViewController.m */; }; 8362C1041201E7CE00599347 /* WebSignupViewController-iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8362C1031201E7CE00599347 /* WebSignupViewController-iPad.xib */; }; 8362C54612027BC300599347 /* AddUsersBlogsViewController~ipad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8362C54512027BC300599347 /* AddUsersBlogsViewController~ipad.xib */; }; - 8369FF9111F4F3BF003106C7 /* AddUsersBlogsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8369FF8F11F4F3BF003106C7 /* AddUsersBlogsViewController.m */; }; 8369FF9211F4F3BF003106C7 /* AddUsersBlogsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8369FF9011F4F3BF003106C7 /* AddUsersBlogsViewController.xib */; }; 8370D10A11FA499A009D650F /* UITableViewActivityCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 8370D10911FA499A009D650F /* UITableViewActivityCell.m */; }; 8370D10C11FA4A1B009D650F /* UITableViewActivityCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8370D10B11FA4A1B009D650F /* UITableViewActivityCell.xib */; }; @@ -536,7 +535,6 @@ 8370D11F11FA4CD5009D650F /* logo_wporg.png in Resources */ = {isa = PBXBuildFile; fileRef = 8370D11B11FA4CD5009D650F /* logo_wporg.png */; }; 8370D12011FA4CD5009D650F /* logo_wpcom@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8370D11C11FA4CD5009D650F /* logo_wpcom@2x.png */; }; 8370D12111FA4CD5009D650F /* logo_wpcom.png in Resources */ = {isa = PBXBuildFile; fileRef = 8370D11D11FA4CD5009D650F /* logo_wpcom.png */; }; - 8370D1BD11FA6295009D650F /* AddSiteViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8370D1BB11FA6295009D650F /* AddSiteViewController.m */; }; 8370D1BE11FA6295009D650F /* AddSiteViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8370D1BC11FA6295009D650F /* AddSiteViewController.xib */; }; 837576C511305C67002EAA27 /* hasLocation.png in Resources */ = {isa = PBXBuildFile; fileRef = 837576C211305C67002EAA27 /* hasLocation.png */; }; 8383178812270E980047B476 /* PostMediaViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8383178612270E980047B476 /* PostMediaViewController.m */; }; @@ -562,7 +560,6 @@ 850DE07017CDAF92003A1057 /* icon-comments-approve-active@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 850DE06817CDAF92003A1057 /* icon-comments-approve-active@2x.png */; }; 850DE07117CDAF92003A1057 /* icon-comments-approve-active.png in Resources */ = {isa = PBXBuildFile; fileRef = 850DE06917CDAF92003A1057 /* icon-comments-approve-active.png */; }; 85149741171E13DF00B87F3F /* WPAsyncBlockOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 85149740171E13DF00B87F3F /* WPAsyncBlockOperation.m */; }; - 85149747171E671000B87F3F /* SelectWPComBlogVisibilityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 85149746171E671000B87F3F /* SelectWPComBlogVisibilityViewController.m */; }; 8516972C169D42F4006C5DED /* WPToast.m in Sources */ = {isa = PBXBuildFile; fileRef = 8516972B169D42F4006C5DED /* WPToast.m */; }; 851734431798C64700A30E27 /* NSURL+Util.m in Sources */ = {isa = PBXBuildFile; fileRef = 851734421798C64700A30E27 /* NSURL+Util.m */; }; 8524551717DE4DBE00D22F98 /* icon-chevron.png in Resources */ = {isa = PBXBuildFile; fileRef = 8524551517DE4DBE00D22F98 /* icon-chevron.png */; }; @@ -636,8 +633,6 @@ 85495F6717C6FACF00871A7A /* icon-comments-edit.png in Resources */ = {isa = PBXBuildFile; fileRef = 85495F5717C6FACF00871A7A /* icon-comments-edit.png */; }; 85495F6817C6FACF00871A7A /* icon-comments-edit-active@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 85495F5817C6FACF00871A7A /* icon-comments-edit-active@2x.png */; }; 85495F6917C6FACF00871A7A /* icon-comments-edit-active.png in Resources */ = {isa = PBXBuildFile; fileRef = 85495F5917C6FACF00871A7A /* icon-comments-edit-active.png */; }; - 854E6B8D17D7BEBC00CCCFDD /* WelcomeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 854E6B8C17D7BEBC00CCCFDD /* WelcomeViewController.m */; }; - 8566BC95170F84BC003C6BCF /* CreateWPComAccountViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8566BC93170F84BC003C6BCF /* CreateWPComAccountViewController.m */; }; 857F55B517CEA01A00E154E1 /* WPKeyboardToolbarBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 857F55B417CEA01A00E154E1 /* WPKeyboardToolbarBase.m */; }; 857F55B817CEA2B200E154E1 /* WPKeyboardToolbarWithoutGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 857F55B717CEA2B200E154E1 /* WPKeyboardToolbarWithoutGradient.m */; }; 857F55CB17CEC5E100E154E1 /* toggleButtonMain-ios7@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 857F55C317CEC5E100E154E1 /* toggleButtonMain-ios7@2x.png */; }; @@ -688,10 +683,8 @@ 85C9172717CECAA6001ECEC3 /* icon-reader-topics-active@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 85C9172317CECAA6001ECEC3 /* icon-reader-topics-active@2x.png */; }; 85C9172817CECAA6001ECEC3 /* icon-reader-topics.png in Resources */ = {isa = PBXBuildFile; fileRef = 85C9172417CECAA6001ECEC3 /* icon-reader-topics.png */; }; 85C9172917CECAA6001ECEC3 /* icon-reader-topics@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 85C9172517CECAA6001ECEC3 /* icon-reader-topics@2x.png */; }; - 85D08A5E17332C4A00E2BBCA /* NewAddUsersBlogViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 85D08A5D17332C4A00E2BBCA /* NewAddUsersBlogViewController.m */; }; 85D08A7117342ECE00E2BBCA /* AddUsersBlogCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 85D08A7017342ECE00E2BBCA /* AddUsersBlogCell.m */; }; 85D2415817D0701A00AF090E /* UIViewController+iOS7StyleChanges.m in Sources */ = {isa = PBXBuildFile; fileRef = 85D2415717D0701A00AF090E /* UIViewController+iOS7StyleChanges.m */; }; - 85D805521715F4EA0075EEAC /* CreateWPComBlogViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 85D805511715F4EA0075EEAC /* CreateWPComBlogViewController.m */; }; 85D80558171630B30075EEAC /* DotCom-Languages.plist in Resources */ = {isa = PBXBuildFile; fileRef = 85D80557171630B30075EEAC /* DotCom-Languages.plist */; }; 85D8055D171631F10075EEAC /* SelectWPComLanguageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 85D8055C171631F10075EEAC /* SelectWPComLanguageViewController.m */; }; 85E105861731A597001071A3 /* WPWalkthroughOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 85E105851731A597001071A3 /* WPWalkthroughOverlayView.m */; }; @@ -780,6 +773,7 @@ CCEF153114C9EA050001176D /* WPWebAppViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CCEF153014C9EA050001176D /* WPWebAppViewController.m */; }; CEBD3EAB0FF1BA3B00C1396E /* Blog.m in Sources */ = {isa = PBXBuildFile; fileRef = CEBD3EAA0FF1BA3B00C1396E /* Blog.m */; }; E100C6BB1741473000AE48D8 /* WordPress-11-12.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = E100C6BA1741472F00AE48D8 /* WordPress-11-12.xcmappingmodel */; }; + E10675C8183F82E900E5CE5C /* SettingsViewControllerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E10675C7183F82E900E5CE5C /* SettingsViewControllerTest.m */; }; E10675CA183FA78E00E5CE5C /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E10675C9183FA78E00E5CE5C /* XCTest.framework */; }; E10A2E9B134E8AD3007643F9 /* PostAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 833AF25A114575A50016DE8F /* PostAnnotation.m */; }; E10B3652158F2D3F00419A93 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E10B3651158F2D3F00419A93 /* QuartzCore.framework */; }; @@ -836,6 +830,7 @@ E15618FD16DB8677006532C4 /* UIKitTestHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = E15618FC16DB8677006532C4 /* UIKitTestHelper.m */; }; E15618FF16DBA983006532C4 /* xmlrpc-response-newpost.xml in Resources */ = {isa = PBXBuildFile; fileRef = E15618FE16DBA983006532C4 /* xmlrpc-response-newpost.xml */; }; E156190116DBABDE006532C4 /* xmlrpc-response-getpost.xml in Resources */ = {isa = PBXBuildFile; fileRef = E156190016DBABDE006532C4 /* xmlrpc-response-getpost.xml */; }; + E1634519183B733B005E967F /* WordPressComOAuthClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E1634518183B733B005E967F /* WordPressComOAuthClient.m */; }; E16A0C3212E769A10049EA80 /* table_sep.png in Resources */ = {isa = PBXBuildFile; fileRef = E16A0C3012E769A10049EA80 /* table_sep.png */; }; E16A0C3312E769A10049EA80 /* table_sep@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E16A0C3112E769A10049EA80 /* table_sep@2x.png */; }; E16AB92E14D978240047A2E5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; @@ -1572,8 +1567,6 @@ 83610AA811F4AD2C00421116 /* WPcomLoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WPcomLoginViewController.m; sourceTree = ""; }; 8362C1031201E7CE00599347 /* WebSignupViewController-iPad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "WebSignupViewController-iPad.xib"; path = "Resources-iPad/WebSignupViewController-iPad.xib"; sourceTree = ""; }; 8362C54512027BC300599347 /* AddUsersBlogsViewController~ipad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "AddUsersBlogsViewController~ipad.xib"; path = "Resources-iPad/AddUsersBlogsViewController~ipad.xib"; sourceTree = ""; }; - 8369FF8E11F4F3BF003106C7 /* AddUsersBlogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddUsersBlogsViewController.h; sourceTree = ""; }; - 8369FF8F11F4F3BF003106C7 /* AddUsersBlogsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AddUsersBlogsViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 8369FF9011F4F3BF003106C7 /* AddUsersBlogsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = AddUsersBlogsViewController.xib; path = Resources/AddUsersBlogsViewController.xib; sourceTree = ""; }; 8370D10811FA499A009D650F /* UITableViewActivityCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UITableViewActivityCell.h; sourceTree = ""; }; 8370D10911FA499A009D650F /* UITableViewActivityCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UITableViewActivityCell.m; sourceTree = ""; }; @@ -1584,8 +1577,6 @@ 8370D11B11FA4CD5009D650F /* logo_wporg.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = logo_wporg.png; path = Resources/Images/logo_wporg.png; sourceTree = ""; }; 8370D11C11FA4CD5009D650F /* logo_wpcom@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "logo_wpcom@2x.png"; path = "Resources/Images/logo_wpcom@2x.png"; sourceTree = ""; }; 8370D11D11FA4CD5009D650F /* logo_wpcom.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = logo_wpcom.png; path = Resources/Images/logo_wpcom.png; sourceTree = ""; }; - 8370D1BA11FA6295009D650F /* AddSiteViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddSiteViewController.h; sourceTree = ""; }; - 8370D1BB11FA6295009D650F /* AddSiteViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AddSiteViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 8370D1BC11FA6295009D650F /* AddSiteViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = AddSiteViewController.xib; path = Resources/AddSiteViewController.xib; sourceTree = ""; }; 837576C211305C67002EAA27 /* hasLocation.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = hasLocation.png; path = Resources/Images/hasLocation.png; sourceTree = ""; }; 8383178512270E980047B476 /* PostMediaViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostMediaViewController.h; sourceTree = ""; }; @@ -1619,8 +1610,6 @@ 850DE06917CDAF92003A1057 /* icon-comments-approve-active.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-comments-approve-active.png"; sourceTree = ""; }; 8514973F171E13DF00B87F3F /* WPAsyncBlockOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WPAsyncBlockOperation.h; sourceTree = ""; }; 85149740171E13DF00B87F3F /* WPAsyncBlockOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WPAsyncBlockOperation.m; sourceTree = ""; }; - 85149745171E671000B87F3F /* SelectWPComBlogVisibilityViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SelectWPComBlogVisibilityViewController.h; sourceTree = ""; }; - 85149746171E671000B87F3F /* SelectWPComBlogVisibilityViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SelectWPComBlogVisibilityViewController.m; sourceTree = ""; }; 8516972A169D42F4006C5DED /* WPToast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WPToast.h; sourceTree = ""; }; 8516972B169D42F4006C5DED /* WPToast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WPToast.m; sourceTree = ""; }; 851734411798C64700A30E27 /* NSURL+Util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURL+Util.h"; sourceTree = ""; }; @@ -1703,10 +1692,6 @@ 85495F5717C6FACF00871A7A /* icon-comments-edit.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-comments-edit.png"; sourceTree = ""; }; 85495F5817C6FACF00871A7A /* icon-comments-edit-active@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-comments-edit-active@2x.png"; sourceTree = ""; }; 85495F5917C6FACF00871A7A /* icon-comments-edit-active.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-comments-edit-active.png"; sourceTree = ""; }; - 854E6B8B17D7BEBC00CCCFDD /* WelcomeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WelcomeViewController.h; sourceTree = ""; }; - 854E6B8C17D7BEBC00CCCFDD /* WelcomeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WelcomeViewController.m; sourceTree = ""; }; - 8566BC92170F84BC003C6BCF /* CreateWPComAccountViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CreateWPComAccountViewController.h; sourceTree = ""; }; - 8566BC93170F84BC003C6BCF /* CreateWPComAccountViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CreateWPComAccountViewController.m; sourceTree = ""; }; 857F55B317CEA01900E154E1 /* WPKeyboardToolbarBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WPKeyboardToolbarBase.h; sourceTree = ""; }; 857F55B417CEA01A00E154E1 /* WPKeyboardToolbarBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WPKeyboardToolbarBase.m; sourceTree = ""; }; 857F55B617CEA2B200E154E1 /* WPKeyboardToolbarWithoutGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WPKeyboardToolbarWithoutGradient.h; sourceTree = ""; }; @@ -1769,14 +1754,10 @@ 85C9172317CECAA6001ECEC3 /* icon-reader-topics-active@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-reader-topics-active@2x.png"; sourceTree = ""; }; 85C9172417CECAA6001ECEC3 /* icon-reader-topics.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-reader-topics.png"; sourceTree = ""; }; 85C9172517CECAA6001ECEC3 /* icon-reader-topics@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-reader-topics@2x.png"; sourceTree = ""; }; - 85D08A5C17332C4A00E2BBCA /* NewAddUsersBlogViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NewAddUsersBlogViewController.h; sourceTree = ""; }; - 85D08A5D17332C4A00E2BBCA /* NewAddUsersBlogViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NewAddUsersBlogViewController.m; sourceTree = ""; }; 85D08A6F17342ECE00E2BBCA /* AddUsersBlogCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddUsersBlogCell.h; sourceTree = ""; }; 85D08A7017342ECE00E2BBCA /* AddUsersBlogCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddUsersBlogCell.m; sourceTree = ""; }; 85D2415617D0701A00AF090E /* UIViewController+iOS7StyleChanges.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+iOS7StyleChanges.h"; sourceTree = ""; }; 85D2415717D0701A00AF090E /* UIViewController+iOS7StyleChanges.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+iOS7StyleChanges.m"; sourceTree = ""; }; - 85D805501715F4EA0075EEAC /* CreateWPComBlogViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CreateWPComBlogViewController.h; sourceTree = ""; }; - 85D805511715F4EA0075EEAC /* CreateWPComBlogViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CreateWPComBlogViewController.m; sourceTree = ""; }; 85D80557171630B30075EEAC /* DotCom-Languages.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "DotCom-Languages.plist"; sourceTree = ""; }; 85D8055B171631F10075EEAC /* SelectWPComLanguageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SelectWPComLanguageViewController.h; sourceTree = ""; }; 85D8055C171631F10075EEAC /* SelectWPComLanguageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SelectWPComLanguageViewController.m; sourceTree = ""; }; @@ -1912,6 +1893,7 @@ E100C6BA1741472F00AE48D8 /* WordPress-11-12.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = "WordPress-11-12.xcmappingmodel"; sourceTree = ""; }; E105E9CD1726955600C0D9E7 /* WPAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WPAccount.h; sourceTree = ""; }; E105E9CE1726955600C0D9E7 /* WPAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WPAccount.m; sourceTree = ""; }; + E10675C7183F82E900E5CE5C /* SettingsViewControllerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsViewControllerTest.m; sourceTree = ""; }; E10675C9183FA78E00E5CE5C /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; E10B3651158F2D3F00419A93 /* QuartzCore.framework */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; E10B3653158F2D4500419A93 /* UIKit.framework */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -1992,6 +1974,8 @@ E15618FC16DB8677006532C4 /* UIKitTestHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIKitTestHelper.m; sourceTree = ""; }; E15618FE16DBA983006532C4 /* xmlrpc-response-newpost.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "xmlrpc-response-newpost.xml"; sourceTree = ""; }; E156190016DBABDE006532C4 /* xmlrpc-response-getpost.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "xmlrpc-response-getpost.xml"; sourceTree = ""; }; + E1634517183B733B005E967F /* WordPressComOAuthClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WordPressComOAuthClient.h; sourceTree = ""; }; + E1634518183B733B005E967F /* WordPressComOAuthClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WordPressComOAuthClient.m; sourceTree = ""; }; E167745A1377F24300EE44DD /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; E167745B1377F25500EE44DD /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; E167745C1377F26400EE44DD /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; @@ -2898,8 +2882,6 @@ 85EC44D31739826A00686604 /* CreateAccountAndBlogViewController.m */, 858DE40D1730384F000AC628 /* LoginViewController.h */, 858DE40E1730384F000AC628 /* LoginViewController.m */, - 85D08A5C17332C4A00E2BBCA /* NewAddUsersBlogViewController.h */, - 85D08A5D17332C4A00E2BBCA /* NewAddUsersBlogViewController.m */, 85B6F7501742DAE800CE7F3A /* WPNUXBackButton.h */, 85B6F7511742DAE800CE7F3A /* WPNUXBackButton.m */, 85B6F74D1742DA1D00CE7F3A /* WPNUXMainButton.h */, @@ -3262,22 +3244,10 @@ 462F4E0918369F0B0028D2F8 /* BlogListViewController.m */, 83610AA711F4AD2C00421116 /* WPcomLoginViewController.h */, 83610AA811F4AD2C00421116 /* WPcomLoginViewController.m */, - 8370D1BA11FA6295009D650F /* AddSiteViewController.h */, - 8370D1BB11FA6295009D650F /* AddSiteViewController.m */, 83FEFC7311FF6C5A0078B462 /* EditSiteViewController.h */, 83FEFC7411FF6C5A0078B462 /* EditSiteViewController.m */, - 8369FF8E11F4F3BF003106C7 /* AddUsersBlogsViewController.h */, - 8369FF8F11F4F3BF003106C7 /* AddUsersBlogsViewController.m */, - 8566BC92170F84BC003C6BCF /* CreateWPComAccountViewController.h */, - 8566BC93170F84BC003C6BCF /* CreateWPComAccountViewController.m */, - 85D805501715F4EA0075EEAC /* CreateWPComBlogViewController.h */, - 85D805511715F4EA0075EEAC /* CreateWPComBlogViewController.m */, 85D8055B171631F10075EEAC /* SelectWPComLanguageViewController.h */, 85D8055C171631F10075EEAC /* SelectWPComLanguageViewController.m */, - 85149745171E671000B87F3F /* SelectWPComBlogVisibilityViewController.h */, - 85149746171E671000B87F3F /* SelectWPComBlogVisibilityViewController.m */, - 854E6B8B17D7BEBC00CCCFDD /* WelcomeViewController.h */, - 854E6B8C17D7BEBC00CCCFDD /* WelcomeViewController.m */, ); name = Blog; sourceTree = ""; @@ -3848,6 +3818,8 @@ E13EB7A4157D230000885780 /* WordPressComApi.m */, E1756DD41694560100D9EC00 /* WordPressComApiCredentials.h */, E1756DD51694560100D9EC00 /* WordPressComApiCredentials.m */, + E1634517183B733B005E967F /* WordPressComOAuthClient.h */, + E1634518183B733B005E967F /* WordPressComOAuthClient.m */, ); path = WordPressApi; sourceTree = ""; @@ -3866,6 +3838,7 @@ E1239B7C176A2EE600D37220 /* AccountTest.m */, E18C1C4F176A3A880002F8AC /* AccountMigrationTest.m */, E1E4CE071773AB4200430844 /* WPTableImageSourceTest.m */, + E10675C7183F82E900E5CE5C /* SettingsViewControllerTest.m */, ); name = Tests; sourceTree = ""; @@ -4978,10 +4951,8 @@ E125443C12BF5A7200D87A0A /* WordPress.xcdatamodeld in Sources */, 8350E49611D2C71E00A7B073 /* Media.m in Sources */, 83610AAA11F4AD2C00421116 /* WPcomLoginViewController.m in Sources */, - 8369FF9111F4F3BF003106C7 /* AddUsersBlogsViewController.m in Sources */, 8370D10A11FA499A009D650F /* UITableViewActivityCell.m in Sources */, 93C486501810442200A24725 /* SupportViewController.m in Sources */, - 8370D1BD11FA6295009D650F /* AddSiteViewController.m in Sources */, 83FEFC7611FF6C5A0078B462 /* EditSiteViewController.m in Sources */, 838C672E1210C3C300B09CA3 /* Post.m in Sources */, 8383178812270E980047B476 /* PostMediaViewController.m in Sources */, @@ -5025,6 +4996,7 @@ 5D2B80D11592447200161F6E /* QuickPhotoButtonView.m in Sources */, 852F4E8C17BED193005ED539 /* PostSettingsSelectionViewController.m in Sources */, CC6B3BE715A33E16005A4523 /* WPFriendFinderNudgeView.m in Sources */, + E1634519183B733B005E967F /* WordPressComOAuthClient.m in Sources */, FD75DDAD15B021C80043F12C /* UIViewController+Rotation.m in Sources */, CC0E20AE15B87DA100D3468B /* WPWebBridge.m in Sources */, 5D97C2F315CAF8D8009B44DD /* UINavigationController+KeyboardFix.m in Sources */, @@ -5039,7 +5011,6 @@ CC0199E9165C17020073A966 /* NotificationsCommentDetailViewController.m in Sources */, 372AD15516682BA100F21BC1 /* NotificationsFollowDetailViewController.m in Sources */, 379DA051166E938E001A43CC /* NotificationsFollowTableViewCell.m in Sources */, - 854E6B8D17D7BEBC00CCCFDD /* WelcomeViewController.m in Sources */, CC669B1F1672C305009E16F8 /* FollowButton.m in Sources */, CC669B231672C328009E16F8 /* NoteCommentCell.m in Sources */, 93740DCB17D8F86700C41B2F /* WPAlertView.m in Sources */, @@ -5065,16 +5036,12 @@ E1D0D84716D3D2EA00E33F4C /* PocketActivity.m in Sources */, E15051CB16CA5DDB00D3DDDC /* Blog+Jetpack.m in Sources */, 85325A5917BC42E5008E87D1 /* NewPostTableViewCell.m in Sources */, - 8566BC95170F84BC003C6BCF /* CreateWPComAccountViewController.m in Sources */, - 85D805521715F4EA0075EEAC /* CreateWPComBlogViewController.m in Sources */, 85D8055D171631F10075EEAC /* SelectWPComLanguageViewController.m in Sources */, 8525398B171761D9003F6B32 /* WPComLanguages.m in Sources */, 85149741171E13DF00B87F3F /* WPAsyncBlockOperation.m in Sources */, - 85149747171E671000B87F3F /* SelectWPComBlogVisibilityViewController.m in Sources */, 858DE40F1730384F000AC628 /* LoginViewController.m in Sources */, 85C720B11730CEFA00460645 /* WPWalkthroughTextField.m in Sources */, 85E105861731A597001071A3 /* WPWalkthroughOverlayView.m in Sources */, - 85D08A5E17332C4A00E2BBCA /* NewAddUsersBlogViewController.m in Sources */, 85D08A7117342ECE00E2BBCA /* AddUsersBlogCell.m in Sources */, 85EC44D11738C24500686604 /* UIView+FormSheetHelpers.m in Sources */, 85EC44D41739826A00686604 /* CreateAccountAndBlogViewController.m in Sources */, @@ -5135,6 +5102,7 @@ E183ECA016B2164900C2EB11 /* EditPostViewControllerTest.m in Sources */, E150520C16CAC5C400D3DDDC /* BlogJetpackTest.m in Sources */, E150520F16CAC75A00D3DDDC /* CoreDataTestHelper.m in Sources */, + E10675C8183F82E900E5CE5C /* SettingsViewControllerTest.m in Sources */, E131CB5E16CAD659004B0314 /* AsyncTestHelper.m in Sources */, E15618FD16DB8677006532C4 /* UIKitTestHelper.m in Sources */, E18C1C50176A3A880002F8AC /* AccountMigrationTest.m in Sources */, diff --git a/WordPress/WordPressApi/WordPressComApi.h b/WordPress/WordPressApi/WordPressComApi.h index c7328fa99c68..3750e360a84d 100644 --- a/WordPress/WordPressApi/WordPressComApi.h +++ b/WordPress/WordPressApi/WordPressComApi.h @@ -39,7 +39,14 @@ extern NSString *const WordPressComApiErrorMessageKey; @property (nonatomic,readonly,strong) NSString *password; @property (nonatomic, readonly, strong) NSString *authToken; -+ (WordPressComApi *)sharedApi; ++ (WordPressComApi *)sharedApi DEPRECATED_MSG_ATTRIBUTE("Use [[WPAccount defaultWordPressComAccount] restApi] instead"); +/** + Returns an API without an associated user + + Use this only for things that don't require an account, like signup or logged out reader + */ ++ (WordPressComApi *)anonymousApi; +- (id)initWithOAuthToken:(NSString *)authToken; ///------------------------- /// @name Account management diff --git a/WordPress/WordPressApi/WordPressComApi.m b/WordPress/WordPressApi/WordPressComApi.m index 0cec5c227363..560cc2f499b0 100644 --- a/WordPress/WordPressApi/WordPressComApi.m +++ b/WordPress/WordPressApi/WordPressComApi.m @@ -17,6 +17,7 @@ #import #import #import "UIDevice+WordPressIdentifier.h" +#import "WPAccount.h" #import "ContextManager.h" #import @@ -85,47 +86,32 @@ - (void)clearWpcomCookies; @implementation WordPressComApi + (WordPressComApi *)sharedApi { - static WordPressComApi *_sharedApi = nil; + DDLogWarn(@"Called obsolete [WordPressComApi sharedApi]"); + return [[WPAccount defaultWordPressComAccount] restApi]; +} + ++ (WordPressComApi *)anonymousApi { + static WordPressComApi *_anonymousApi = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ - NSString *username = [[NSUserDefaults standardUserDefaults] objectForKey:@"wpcom_username_preference"]; - DDLogVerbose(@"Initializing API with username '%@'", username); - NSString *password = nil; - NSString *authToken = nil; - if (username) { - NSError *error = nil; - password = [SFHFKeychainUtils getPasswordForUsername:username - andServiceName:kWPcomXMLRPCUrl - error:&error]; - if (error) { - DDLogError(@"Error getting WordPress.com password: %@", error); - } else { - DDLogVerbose(@"Found password for API: %@", password ? @"YES" : @"NO"); - } - authToken = [SFHFKeychainUtils getPasswordForUsername:username - andServiceName:WordPressComApiOauthServiceName - error:&error]; - if (error) { - DDLogError(@"Error getting WordPress.com OAuth2 token: %@", error); - } else { - DDLogVerbose(@"Found OAuth2 token for API: %@", authToken.length > 0 ? @"YES" : @"NO"); - } - } - _sharedApi = [[self alloc] initWithBaseURL:[NSURL URLWithString:WordPressComApiClientEndpointURL] ]; - _sharedApi.username = username; - _sharedApi.password = password; - [_sharedApi registerHTTPOperationClass:[WPJSONRequestOperation class]]; - [_sharedApi setDefaultHeader:@"User-Agent" value:[[WordPressAppDelegate sharedWordPressApplicationDelegate] applicationUserAgent]]; - if (authToken) { - _sharedApi.authToken = authToken; - } else if (username && password) { - [_sharedApi signInWithUsername:username password:password success:nil failure:nil]; - } - -// [_sharedApi checkForNewUnseenNotifications]; + DDLogVerbose(@"Initializing anonymous API"); + _anonymousApi = [[self alloc] initWithBaseURL:[NSURL URLWithString:WordPressComApiClientEndpointURL] ]; + [_anonymousApi registerHTTPOperationClass:[WPJSONRequestOperation class]]; + [_anonymousApi setDefaultHeader:@"User-Agent" value:[[WordPressAppDelegate sharedWordPressApplicationDelegate] applicationUserAgent]]; }); - return _sharedApi; + return _anonymousApi; +} + +- (id)initWithOAuthToken:(NSString *)authToken { + self = [super initWithBaseURL:[NSURL URLWithString:WordPressComApiClientEndpointURL]]; + if (self) { + _authToken = authToken; + [self setAuthorizationHeaderWithToken:_authToken]; + [self registerHTTPOperationClass:[WPJSONRequestOperation class]]; + [self setDefaultHeader:@"User-Agent" value:[[WordPressAppDelegate sharedWordPressApplicationDelegate] applicationUserAgent]]; + } + return self; } #pragma mark - Account management diff --git a/WordPress/WordPressApi/WordPressComOAuthClient.h b/WordPress/WordPressApi/WordPressComOAuthClient.h new file mode 100644 index 000000000000..0134fceebd69 --- /dev/null +++ b/WordPress/WordPressApi/WordPressComOAuthClient.h @@ -0,0 +1,41 @@ +// +// WordPressComOAuthClient.h +// WordPress +// +// Created by Jorge Bernal on 19/11/13. +// Copyright (c) 2013 WordPress. All rights reserved. +// + +#import "AFHTTPClient.h" + +extern NSString * const WordPressComOAuthErrorDomain; + +typedef NS_ENUM(NSUInteger, WordPressComOAuthError) { + WordPressComOAuthErrorUnknown, + WordPressComOAuthErrorInvalidClient, + WordPressComOAuthErrorUnsupportedGrantType, + WordPressComOAuthErrorInvalidRequest, +}; + +/** + `WordPressComOAuthClient` encapsulates the pattern of authenticating against WordPress.com OAuth2 service. + + Right now it requires a special client id and secret, so this probably won't work for you + + @see https://developer.wordpress.com/docs/oauth2/ + */ +@interface WordPressComOAuthClient : AFHTTPClient + ++ (WordPressComOAuthClient *)client; + +/** + Authenticates on WordPress.com + + @param username the account's username. + @param password the account's password. + @param success block to be called if authentication was successful. The OAuth2 token is passed as a parameter. + @param failure block to be called if authentication failed. The error object is passed as a parameter. + */ +- (void)authenticateWithUsername:(NSString *)username password:(NSString *)password success:(void (^)(NSString *authToken))success failure:(void (^)(NSError *error))failure; + +@end diff --git a/WordPress/WordPressApi/WordPressComOAuthClient.m b/WordPress/WordPressApi/WordPressComOAuthClient.m new file mode 100644 index 000000000000..c4130e734b5b --- /dev/null +++ b/WordPress/WordPressApi/WordPressComOAuthClient.m @@ -0,0 +1,84 @@ +// +// WordPressComOAuthClient.m +// WordPress +// +// Created by Jorge Bernal on 19/11/13. +// Copyright (c) 2013 WordPress. All rights reserved. +// + +#import "WordPressComOAuthClient.h" +#import "WordPressComApiCredentials.h" + +NSString * const WordPressComOAuthErrorDomain = @"WordPressComOAuthError"; + +static NSString * const WordPressComOAuthBaseUrl = @"https://public-api.wordpress.com/oauth2"; +static NSString * const WordPressComOAuthRedirectUrl = @"http://wordpress.com/"; + +@implementation WordPressComOAuthClient + ++ (WordPressComOAuthClient *)client { + WordPressComOAuthClient *client = [[WordPressComOAuthClient alloc] initWithBaseURL:[NSURL URLWithString:WordPressComOAuthBaseUrl]]; + [client registerHTTPOperationClass:[AFJSONRequestOperation class]]; + [client setDefaultHeader:@"Accept" value:@"application/json"]; + return client; +} + +- (void)authenticateWithUsername:(NSString *)username password:(NSString *)password success:(void (^)(NSString *authToken))success failure:(void (^)(NSError *error))failure { + NSDictionary *parameters = @{ + @"username": username, + @"password": password, + @"grant_type": @"password", + @"client_id": [WordPressComApiCredentials client], + @"client_secret": [WordPressComApiCredentials secret], + }; + [self postPath:@"token" + parameters:parameters + success:^(AFHTTPRequestOperation *operation, id responseObject) { + DDLogVerbose(@"Received OAuth2 response: %@", responseObject); + NSString *authToken = [responseObject stringForKey:@"access_token"]; + if (success) { + success(authToken); + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + error = [self processError:error forOperation:operation]; + DDLogError(@"Error receiving OAuth2 token: %@", error); + if (failure) { + failure(error); + } + }]; +} + +- (NSError *)processError:(NSError *)error forOperation:(AFHTTPRequestOperation *)operation { + if (operation.response.statusCode >= 400 && operation.response.statusCode < 500) { + // Bad request, look for errors in the JSON response + NSDictionary *response = nil; + if ([operation isKindOfClass:[AFJSONRequestOperation class]]) { + AFJSONRequestOperation *jsonOperation = (AFJSONRequestOperation *)operation; + response = jsonOperation.responseJSON; + } + if (response) { + NSString *errorCode = [response stringForKey:@"error"]; + NSString *errorDescription = [response stringForKey:@"error_description"]; + + NSInteger code = WordPressComOAuthErrorUnknown; + /* + Possible errors: + - invalid_client: client_id is missing or wrong, it shouldn't happen + - unsupported_grant_type: client_id doesn't support password grants + - invalid_request: a required field is missing/malformed + - invalid_request: authentication failed + */ + if ([errorCode isEqualToString:@"invalid_client"]) { + code = WordPressComOAuthErrorInvalidClient; + } else if ([errorCode isEqualToString:@"unsupported_grant_type"]) { + code = WordPressComOAuthErrorUnsupportedGrantType; + } else if ([errorCode isEqualToString:@"invalid_request"]) { + code = WordPressComOAuthErrorInvalidRequest; + } + return [NSError errorWithDomain:WordPressComOAuthErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: errorDescription}]; + } + } + return error; +} + +@end diff --git a/WordPress/WordPressTest/AccountTest.m b/WordPress/WordPressTest/AccountTest.m index 4bcbc7850750..5279f80b3b2b 100644 --- a/WordPress/WordPressTest/AccountTest.m +++ b/WordPress/WordPressTest/AccountTest.m @@ -31,13 +31,15 @@ - (void)tearDown [[CoreDataTestHelper sharedHelper] reset]; } -- (void)testNewAccountSetsDefaultAccount +- (void)testNewAccountDoesntSetDefaultAccount { XCTAssertNil([WPAccount defaultWordPressComAccount]); - WPAccount *_account = [WPAccount createOrUpdateWordPressComAccountWithUsername:@"user" andPassword:@"pass"]; + WPAccount *_account = [WPAccount createOrUpdateWordPressComAccountWithUsername:@"user" password:@"pass" authToken:@"token"]; + XCTAssertNil([WPAccount defaultWordPressComAccount]); + [WPAccount setDefaultWordPressComAccount:_account]; XCTAssertNotNil([WPAccount defaultWordPressComAccount]); XCTAssertEqualObjects([WPAccount defaultWordPressComAccount], _account); - WPAccount *_account2 = [WPAccount createOrUpdateWordPressComAccountWithUsername:@"user" andPassword:@"pass"]; + WPAccount *_account2 = [WPAccount createOrUpdateWordPressComAccountWithUsername:@"user" password:@"pass" authToken:@"token"]; XCTAssertNotNil(_account2); XCTAssertEqualObjects([WPAccount defaultWordPressComAccount], _account); } diff --git a/WordPress/WordPressTest/CoreDataTestHelper.m b/WordPress/WordPressTest/CoreDataTestHelper.m index 2a1dbf0e46e6..791b27dbd332 100644 --- a/WordPress/WordPressTest/CoreDataTestHelper.m +++ b/WordPress/WordPressTest/CoreDataTestHelper.m @@ -149,6 +149,7 @@ - (NSPersistentStoreCoordinator *)persistentStoreCoordinator { _coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; NSError *error; + [[NSFileManager defaultManager] removeItemAtURL:[self storeURL] error:nil]; NSPersistentStore *store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self storeURL] options:nil error:&error]; NSAssert(store != nil, @"Can't initialize core data storage"); } diff --git a/WordPress/WordPressTest/EditPostViewControllerTest.m b/WordPress/WordPressTest/EditPostViewControllerTest.m index 4af7fedde3c9..af6b19e52861 100644 --- a/WordPress/WordPressTest/EditPostViewControllerTest.m +++ b/WordPress/WordPressTest/EditPostViewControllerTest.m @@ -31,7 +31,6 @@ - (void)setUp { @"url": @"http://test.blog/", @"xmlrpc": @"http://test.blog/xmlrpc.php", @"blogName": @"A test blog", - @"isAdmin": @YES }; [[CoreDataTestHelper sharedHelper] registerDefaultContext]; _account = [WPAccount createOrUpdateSelfHostedAccountWithXmlrpc:blogDict[@"xmlrpc"] username:@"test" andPassword:@"test"]; diff --git a/WordPress/WordPressTest/SettingsViewControllerTest.m b/WordPress/WordPressTest/SettingsViewControllerTest.m new file mode 100644 index 000000000000..b66928c4ba88 --- /dev/null +++ b/WordPress/WordPressTest/SettingsViewControllerTest.m @@ -0,0 +1,171 @@ +// +// SettingsViewControllerTest.m +// WordPress +// +// Created by Jorge Bernal on 22/11/13. +// Copyright (c) 2013 WordPress. All rights reserved. +// + +#import +#import "CoreDataTestHelper.h" +#import "WPAccount.h" +#import "Blog.h" +#import "Constants.h" +#import "SettingsViewController.h" + +@interface SettingsViewControllerTest : XCTestCase + +@end + +@implementation SettingsViewControllerTest + +- (void)setUp +{ + [super setUp]; + // Put setup code here; it will be run once, before the first test case. + [[CoreDataTestHelper sharedHelper] registerDefaultContext]; +} + +- (void)tearDown +{ + // Put teardown code here; it will be run once, after the last test case. + [super tearDown]; + [[CoreDataTestHelper sharedHelper] reset]; +} + +- (void)testWpcomSection +{ + [WPAccount removeDefaultWordPressComAccount]; + [[NSUserDefaults standardUserDefaults] removeObjectForKey:kApnsDeviceTokenPrefKey]; + SettingsViewController *controller = [self settingsViewController]; + [self present:controller]; + + UITableView *table = controller.tableView; + UITableViewCell *cell = [self tableView:table cellForRow:0]; + + /* + Signed out + + - Sign In + */ + XCTAssertEqual(1, [table numberOfRowsInSection:0]); + XCTAssertEqualObjects(@"wpcom-sign-in", cell.accessibilityIdentifier); + + + // Sign In + WPAccount *account = [WPAccount createOrUpdateWordPressComAccountWithUsername:@"jacksparrow" password:@"piratesobrave" authToken:@"sevenseas"]; + [WPAccount setDefaultWordPressComAccount:account]; + + /* + Signed In, Notifications disabled, 1 blog + + - Username jacksparrow + - Sign Out + */ + XCTAssertEqual(2, [table numberOfRowsInSection:0]); + cell = [self tableView:table cellForRow:0]; + XCTAssertEqualObjects(@"wpcom-username", cell.accessibilityIdentifier); + cell = [self tableView:table cellForRow:1]; + XCTAssertEqualObjects(@"wpcom-sign-out", cell.accessibilityIdentifier); + + [[NSUserDefaults standardUserDefaults] setObject:@"aFakeAPNSToken" forKey:kApnsDeviceTokenPrefKey]; + [table reloadData]; + + /* + Signed In, Notifications enabled, 0 blogs + + - Username jacksparrow + - Manage Notifications + - Sign Out + */ + XCTAssertEqual(3, [table numberOfRowsInSection:0]); + cell = [self tableView:table cellForRow:0]; + XCTAssertEqualObjects(@"wpcom-username", cell.accessibilityIdentifier); + cell = [self tableView:table cellForRow:1]; + XCTAssertEqualObjects(@"wpcom-manage-notifications", cell.accessibilityIdentifier); + cell = [self tableView:table cellForRow:2]; + XCTAssertEqualObjects(@"wpcom-sign-out", cell.accessibilityIdentifier); + + Blog *blog = [account findOrCreateBlogFromDictionary:@{@"url": @"blog1.com"} withContext:account.managedObjectContext]; + [blog dataSave]; + [table reloadData]; + + /* + Signed In, Notifications enabled, 1 blogs + + - Username jacksparrow + - Manage Blogs + - Manage Notifications + - Sign Out + */ + XCTAssertEqual(4, [table numberOfRowsInSection:0]); + cell = [self tableView:table cellForRow:0]; + XCTAssertEqualObjects(@"wpcom-username", cell.accessibilityIdentifier); + cell = [self tableView:table cellForRow:1]; + XCTAssertEqualObjects(@"wpcom-manage-blogs", cell.accessibilityIdentifier); + cell = [self tableView:table cellForRow:2]; + XCTAssertEqualObjects(@"wpcom-manage-notifications", cell.accessibilityIdentifier); + cell = [self tableView:table cellForRow:3]; + XCTAssertEqualObjects(@"wpcom-sign-out", cell.accessibilityIdentifier); + + blog = [account findOrCreateBlogFromDictionary:@{@"url": @"blog2.com"} withContext:account.managedObjectContext]; + [blog dataSave]; + [table reloadData]; + + /* + Signed In, Notifications enabled, 2 blogs + + - Username jacksparrow + - Manage Blogs + - Manage Notifications + - Sign Out + */ + XCTAssertEqual(4, [table numberOfRowsInSection:0]); + cell = [self tableView:table cellForRow:0]; + XCTAssertEqualObjects(@"wpcom-username", cell.accessibilityIdentifier); + cell = [self tableView:table cellForRow:1]; + XCTAssertEqualObjects(@"wpcom-manage-blogs", cell.accessibilityIdentifier); + cell = [self tableView:table cellForRow:2]; + XCTAssertEqualObjects(@"wpcom-manage-notifications", cell.accessibilityIdentifier); + cell = [self tableView:table cellForRow:3]; + XCTAssertEqualObjects(@"wpcom-sign-out", cell.accessibilityIdentifier); + + [[NSUserDefaults standardUserDefaults] removeObjectForKey:kApnsDeviceTokenPrefKey]; + [table reloadData]; + + /* + Signed In, Notifications disabled, 2 blogs + + - Username jacksparrow + - Manage Blogs + - Sign Out + */ + XCTAssertEqual(3, [table numberOfRowsInSection:0]); + cell = [self tableView:table cellForRow:0]; + XCTAssertEqualObjects(@"wpcom-username", cell.accessibilityIdentifier); + cell = [self tableView:table cellForRow:1]; + XCTAssertEqualObjects(@"wpcom-manage-blogs", cell.accessibilityIdentifier); + cell = [self tableView:table cellForRow:2]; + XCTAssertEqualObjects(@"wpcom-sign-out", cell.accessibilityIdentifier); +} + +- (SettingsViewController *)settingsViewController { + SettingsViewController *controller = [SettingsViewController new]; + // Force view load + [controller view]; + return controller; +} + +- (void)present:(UIViewController *)controller { + [[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:controller animated:NO completion:nil]; +} + +- (void)dismiss:(UIViewController *)controller { + [[[[[UIApplication sharedApplication] delegate] window] rootViewController] dismissViewControllerAnimated:NO completion:nil]; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRow:(NSInteger)row { + return [tableView.dataSource tableView:tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]]; +} + +@end