// // AccountDetailsViewController.m // OpenStack // // Created by Mike Mayo on 10/7/10. // The OpenStack project is provided under the Apache 2.0 license. // #import "AccountDetailsViewController.h" #import "Provider.h" #import "RSTextFieldCell.h" #import "OpenStackAccount.h" #import "RootViewController.h" #import "OpenStackRequest.h" #import "UIViewController+Conveniences.h" #import "NSString+Conveniences.h" #import "ActivityIndicatorView.h" #import "OpenStackAppDelegate.h" #import "UIColor+MoreColors.h" #define kUsername 0 #define kAuthToken 1 #define kProviderName 0 #define kAuthEndpoint 1 @implementation AccountDetailsViewController @synthesize provider, rootViewController; @synthesize username, authToken; #pragma mark - View lifecycle - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait); } - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.title = @"Authentication"; providerSection = -1; authenticationSection = 0; getTokenSection = 1; [self addSaveButton]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (provider == nil) { customProvider = YES; providerSection = 0; authenticationSection = 1; getTokenSection = 2; [self.tableView reloadData]; } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [customProvider ? providerNameTextField : usernameTextField becomeFirstResponder]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [usernameTextField resignFirstResponder]; [authTokenTextField resignFirstResponder]; [providerNameTextField resignFirstResponder]; [apiEndpointTextField resignFirstResponder]; } #pragma mark - Memory management - (void)dealloc { [provider release]; [rootViewController release]; [username release]; [authToken release]; [super dealloc]; } #pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { if (customProvider) return 3; else return 2; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (section == getTokenSection) return 1; else return 2; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { if (section == authenticationSection) { if (customProvider) { return @"Login"; } else { return [NSString stringWithFormat:@"%@ Login", provider.name]; } } else if (section == providerSection) { return @"Provider Details"; } else { return @""; } } - (RSTextFieldCell *)textCell:(NSString *)labelText textField:(UITextField **)textField secure:(BOOL)secure returnKeyType:(UIReturnKeyType)returnKeyType { RSTextFieldCell *cell = (RSTextFieldCell *)[self.tableView dequeueReusableCellWithIdentifier:labelText]; if (cell == nil) { if ([labelText isEqualToString:@"API URL"]) cell = [[[RSTextFieldCell alloc] initCellWithFixedLabel:@"/v1" withStyle:UITableViewCellStyleValue1 reuseIdentifier:labelText] autorelease]; else cell = [[[RSTextFieldCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:labelText] autorelease]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.modalPresentationStyle = UIModalPresentationFormSheet; cell.textLabel.text = labelText; *textField = cell.textField; ((UITextField *)*textField).delegate = self; ((UITextField *)*textField).secureTextEntry = secure; ((UITextField *)*textField).returnKeyType = returnKeyType; } return cell; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = nil; if (indexPath.section == authenticationSection) { if (indexPath.row == kUsername) { cell = [self textCell:@"Username" textField:&usernameTextField secure:NO returnKeyType:UIReturnKeyNext]; if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { CGRect rect = usernameTextField.frame; CGFloat offset = 19.0; usernameTextField.frame = CGRectMake(rect.origin.x + offset, rect.origin.y, rect.size.width - offset, rect.size.height); } if (username) usernameTextField.text = username; } else if (indexPath.row == kAuthToken) { cell = [self textCell:@"Token" textField:&authTokenTextField secure:NO returnKeyType:UIReturnKeyDone]; if (authToken) authTokenTextField.text = authToken; } } else if (indexPath.section == providerSection) { if (indexPath.row == kProviderName) { cell = [self textCell:@"Name" textField:&providerNameTextField secure:NO returnKeyType:UIReturnKeyNext]; providerNameTextField.placeholder = @"E.g.: pithos"; } else if (indexPath.row == kAuthEndpoint) { cell = [self textCell:@"API URL" textField:&apiEndpointTextField secure:NO returnKeyType:UIReturnKeyNext]; apiEndpointTextField.placeholder = @"Type server's URL here"; } } else if (indexPath.section == getTokenSection) { static NSString *CellIdentifier = @"Cell"; cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } cell.textLabel.text = @"Get Token"; } return cell; } #pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.section == getTokenSection) { NSURL *loginURLPrefix = nil; if (customProvider) { if (!apiEndpointTextField.text || ![apiEndpointTextField.text isURL]) { [self alert:@"No API URL" message:@"Please enter an API Authentication URL"]; } else { loginURLPrefix = [[NSURL URLWithString:apiEndpointTextField.text] URLByAppendingPathComponent:@"login"]; } } else { loginURLPrefix = [[provider.authEndpointURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:@"login"]; } NSString *protocol = [[[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"] objectAtIndex:0] objectForKey:@"CFBundleURLSchemes"] objectAtIndex:0]; NSString *loginURL = [NSString stringWithFormat:@"%@?next=%@://login&force=", loginURLPrefix, protocol]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:loginURL]]; [tableView deselectRowAtIndexPath:indexPath animated:NO]; } } #pragma mark - UITextFieldDelegate - (BOOL)textFieldShouldReturn:(UITextField *)textField { if ([textField isEqual:providerNameTextField]) { [apiEndpointTextField becomeFirstResponder]; } else if ([textField isEqual:apiEndpointTextField]) { [usernameTextField becomeFirstResponder]; [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:kAuthToken inSection:authenticationSection] atScrollPosition:UITableViewScrollPositionNone animated:YES]; } else if ([textField isEqual:usernameTextField]) { [authTokenTextField becomeFirstResponder]; [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:kAuthToken inSection:authenticationSection] atScrollPosition:UITableViewScrollPositionNone animated:YES]; } else { [textField resignFirstResponder]; [self saveButtonPressed:self]; } return NO; } - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { NSString *result = [textField.text stringByReplacingCharactersInRange:range withString:string]; if ([textField isEqual:usernameTextField]) { self.username = result; } else if ([textField isEqual:authTokenTextField]) { self.authToken = result; } return YES; } #pragma mark - Actions - (void)setUsername:(NSString *)aUsername andAuthToken:(NSString *)anAuthToken { if (aUsername) self.username = aUsername; if (anAuthToken) self.authToken = anAuthToken; } #pragma mark - Button Handlers - (void)saveButtonPressed:(id)sender { self.navigationItem.rightBarButtonItem.enabled = NO; [self authenticate]; } #pragma mark - Authentication - (void)authenticate { if (customProvider && (!providerNameTextField.text || [providerNameTextField.text isEqualToString:@""])) { [self alert:nil message:@"Please enter a Provider Name."]; self.navigationItem.rightBarButtonItem.enabled = YES; [providerNameTextField becomeFirstResponder]; } else if (customProvider && (!apiEndpointTextField.text || ![apiEndpointTextField.text isURL])) { [self alert:nil message:@"Please enter an API Authentication URL."]; self.navigationItem.rightBarButtonItem.enabled = YES; [apiEndpointTextField becomeFirstResponder]; } else if (!usernameTextField.text || [usernameTextField.text isEqualToString:@""]) { [self alert:nil message:@"Please enter your Username."]; self.navigationItem.rightBarButtonItem.enabled = YES; [usernameTextField becomeFirstResponder]; } else if (!authTokenTextField.text || [authTokenTextField.text isEqualToString:@""]) { [self alert:nil message:@"Please enter your Token."]; self.navigationItem.rightBarButtonItem.enabled = YES; [authTokenTextField becomeFirstResponder]; } else { account = [[OpenStackAccount alloc] init]; if (customProvider) { account.provider = [[[Provider alloc] init] autorelease]; account.provider.name = providerNameTextField.text; account.provider.authEndpointURL = [[NSURL URLWithString:apiEndpointTextField.text] URLByAppendingPathComponent:@"v1"]; } else { account.provider = provider; } account.username = usernameTextField.text; account.authToken = authTokenTextField.text; account.hostURL = [account.provider.authEndpointURL URLByDeletingLastPathComponent]; __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Authenticating..." andAddToView:self.view]; OpenStackRequest *request = [OpenStackRequest authenticationRequest:account]; request.completionBlock = ^{ [activityIndicatorView removeFromSuperview]; if ([request isSuccess]) { NSString *storageURLString = [[request responseHeaders] objectForKey:@"X-Storage-Url"]; if (storageURLString) { account.filesURL = [NSURL URLWithString:storageURLString]; } else { account.filesURL = [[account.hostURL URLByAppendingPathComponent:@"v1"] URLByAppendingPathComponent:account.username]; } [account persist]; [rootViewController.tableView reloadData]; [self.navigationController dismissModalViewControllerAnimated:YES]; } else { self.navigationItem.rightBarButtonItem.enabled = YES; [self alert:@"Authentication Failure" message:@"Please check your Username and Token."]; } }; request.failedBlock = ^{ [activityIndicatorView removeFromSuperview]; self.navigationItem.rightBarButtonItem.enabled = YES; if ([request responseStatusCode] == 401) { [self alert:@"Authentication Failure" message:@"Please check your Username and Token."]; } else { [self failOnBadConnection]; } }; [request startAsynchronous]; } } @end