#import "Provider.h"
#import "RSTextFieldCell.h"
#import "OpenStackAccount.h"
+#import "AccountManager.h"
+#import "APICallback.h"
#import "RootViewController.h"
#import "OpenStackRequest.h"
#import "UIViewController+Conveniences.h"
if (indexPath.section == authenticationSection) {
if (indexPath.row == kUsername) {
- cell = [self textCell:@"Username" textField:&usernameTextField secure:NO returnKeyType:UIReturnKeyNext];
+ cell = [self textCell:@"UUID" textField:&usernameTextField secure:NO returnKeyType:UIReturnKeyNext];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
CGRect rect = usernameTextField.frame;
CGFloat offset = 19.0;
loginURLPrefix = [[NSURL URLWithString:apiEndpointTextField.text] URLByAppendingPathComponent:@"login"];
}
} else {
- loginURLPrefix = [[provider.authEndpointURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:@"login"];
+ loginURLPrefix = provider.loginURL;
}
NSString *protocol = [[[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"] objectAtIndex:0] objectForKey:@"CFBundleURLSchemes"] objectAtIndex:0];
NSString *loginURL = [NSString stringWithFormat:@"%@?next=%@://login&force=", loginURLPrefix, protocol];
self.navigationItem.rightBarButtonItem.enabled = YES;
[apiEndpointTextField becomeFirstResponder];
} else if (!usernameTextField.text || [usernameTextField.text isEqualToString:@""]) {
- [self alert:nil message:@"Please enter your Username."];
+ [self alert:nil message:@"Please enter your UUID."];
self.navigationItem.rightBarButtonItem.enabled = YES;
[usernameTextField becomeFirstResponder];
} else if (!authTokenTextField.text || [authTokenTextField.text isEqualToString:@""]) {
if (customProvider) {
account.provider = [[[Provider alloc] init] autorelease];
account.provider.name = providerNameTextField.text;
- account.provider.authEndpointURL = [[NSURL URLWithString:apiEndpointTextField.text] URLByAppendingPathComponent:@"v1"];
+ account.provider.hostURL = [NSURL URLWithString:apiEndpointTextField.text];
+ account.provider.version = @"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];
+ [[account.manager authenticate]
+ success:^(OpenStackRequest *request) {
+ if ([request isSuccess]) {
+ NSString *storageURLString = [[request responseHeaders] objectForKey:@"X-Storage-Url"];
+ if (storageURLString) {
+ account.filesURL = [NSURL URLWithString:storageURLString];
+ } else {
+ account.filesURL = [account.provider.authEndpointURL URLByAppendingPathComponent:account.username];
+ }
+ [account persist];
+ [[account.manager userCatalogForDisplaynames:nil UUIDs:[NSArray arrayWithObject:account.username]]
+ success:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [rootViewController.tableView reloadData];
+ [self.navigationController dismissModalViewControllerAnimated:YES];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [rootViewController.tableView reloadData];
+ [self.navigationController dismissModalViewControllerAnimated:YES];
+ }];
} else {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
self.navigationItem.rightBarButtonItem.enabled = YES;
- [self alert:@"Authentication Failure" message:@"Please check your Username and Token."];
+ [self alert:@"Authentication Failure" message:@"Please check your UUID 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];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ self.navigationItem.rightBarButtonItem.enabled = YES;
+ if ([request responseStatusCode] == 401) {
+ [self alert:@"Authentication Failure" message:@"Please check your UUID and Token."];
+ } else {
+ [self failOnBadConnection];
+ }
+ }];
}
}
@property (nonatomic, retain) OpenStackAccount *account;
@property (nonatomic, retain) NSMutableDictionary *groups;
+@property (nonatomic, retain) NSMutableDictionary *metadata;
@end
@implementation AccountGroupsViewController
-@synthesize account, groups;
+@synthesize account, groups, metadata;
#pragma mark - View lifecycle
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationItem.title = @"Groups";
- groups = [[NSMutableDictionary alloc] init];
- metadata = [[NSMutableDictionary alloc] init];
+}
- __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading..."
+- (void)viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+ __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading groups..."
andAddToView:self.view];
[[self.account.manager getStorageAccountInfo]
success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- for (NSString *key in request.responseHeaders) {
- if ([key hasPrefix:@"X-Account-Group-"]) {
- NSString *groupUsers = [NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:key]];
- NSString *groupName = [NSString decodeFromPercentEscape:[key substringFromIndex:16]];
- [groups setObject:groupUsers forKey:groupName];
+ self.groups = [NSMutableDictionary dictionary];
+ self.metadata = [NSMutableDictionary dictionary];
+
+ for (NSString *headerName in request.responseHeaders) {
+ if ([headerName hasPrefix:@"X-Account-Group-"]) {
+ [groups setObject:[[NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:headerName]] componentsSeparatedByString:@","]
+ forKey:[NSString decodeFromPercentEscape:[headerName substringFromIndex:16]]];
+ } else if ([headerName hasPrefix:@"X-Account-Meta-"]) {
+ [metadata setObject:[NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:headerName]]
+ forKey:[NSString decodeFromPercentEscape:[headerName substringFromIndex:15]]];
}
}
- for (NSString *header in request.responseHeaders) {
- if ([header hasPrefix:@"X-Account-Meta-"]) {
- NSString *metadataKey = [NSString decodeFromPercentEscape:[header substringFromIndex:15]];
- NSString *metadataValue = [NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:header]];
- [metadata setObject:metadataValue forKey:metadataKey];
- }
+ NSMutableArray *UUIDs = [NSMutableArray array];
+ for (NSString *groupName in groups) {
+ [UUIDs addObjectsFromArray:[groups objectForKey:groupName]];
+ }
+ if (UUIDs.count) {
+ [[self.account.manager userCatalogForDisplaynames:nil UUIDs:UUIDs]
+ success:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView reloadData];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView reloadData];
+ [self alert:@"Failed to translate group UUIDs." request:request];
+ }];
+ } else {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView reloadData];
}
-
- [self.tableView reloadData];
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- [self alert:@"Error" message:@"Failed to get account information"];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self alert:@"Failed to get account information." request:request];
}];
}
+#pragma mark - Internal
+
+- (NSMutableArray *)groupUsersForGroupName:(NSString *)groupName {
+ NSArray *groupUUIDs = [groups objectForKey:groupName];
+ NSMutableArray *groupUsers = [NSMutableArray arrayWithCapacity:groupUUIDs.count];
+ for (NSString *UUID in groupUUIDs) {
+ [groupUsers addObject:[self.account displaynameForUUID:UUID safe:YES]];
+ }
+ return groupUsers;
+}
+
+- (NSMutableString *)groupUsersStringForGroupName:(NSString *)groupName {
+ NSMutableArray *groupUsers = [self groupUsersForGroupName:groupName];
+ NSMutableString *groupUsersString = [NSMutableString string];
+ for (NSUInteger index = 0; index < groupUsers.count; index++) {
+ if (index) {
+ [groupUsersString appendString:@","];
+ }
+ [groupUsersString appendString:[groupUsers objectAtIndex:index]];
+ }
+ return groupUsersString;
+}
+
#pragma mark - Memory management
- (void)dealloc {
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- return [groups count] + 1;
+ return (groups.count + 1);
}
- (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat result;
- if ([groups count] > 0 && indexPath.row < [groups count]) {
- NSString *groupName = [[groups allKeys] objectAtIndex:indexPath.row];
- NSString *groupUsers = [groups objectForKey:groupName];
- result = 22.0 + [[NSString stringWithFormat:@"%@", groupUsers] sizeWithFont:[UIFont systemFontOfSize:18.0]
+ if (indexPath.row < groups.count) {
+ NSString *groupName = [[[groups allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:indexPath.row];
+ result = 22.0 + [[self groupUsersStringForGroupName:groupName] sizeWithFont:[UIFont systemFontOfSize:18.0]
constrainedToSize:(([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) ?
CGSizeMake(596.0, 9000.0) :
CGSizeMake(280.0, 9000.0))
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
-
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
- if (indexPath.row < [groups count]) {
- NSString *groupName = [[groups allKeys] objectAtIndex:indexPath.row];
+ if (indexPath.row < groups.count) {
+ NSString *groupName = [[[groups allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:indexPath.row];
cell.textLabel.text = groupName;
- cell.detailTextLabel.text = [groups objectForKey:groupName];
+ cell.detailTextLabel.text = [self groupUsersStringForGroupName:groupName];
cell.detailTextLabel.numberOfLines = 0;
cell.detailTextLabel.lineBreakMode = UILineBreakModeCharacterWrap;
} else {
vc.account = self.account;
vc.metadata = metadata;
vc.groups = groups;
- if (indexPath.row < [groups count]) {
- NSString *groupName = [[groups allKeys] objectAtIndex:indexPath.row];
- NSString *groupUsers = [groups objectForKey:groupName];
-
+ if (indexPath.row < groups.count) {
+ NSString *groupName = [[[groups allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:indexPath.row];
vc.removeGroupEnabled = YES;
vc.groupName = groupName;
- vc.groupUsers = groupUsers;
vc.navigationItem.title = @"Edit Group";
} else {
vc.removeGroupEnabled = NO;
vc.groupName = @"";
- vc.groupUsers = @"";
vc.navigationItem.title = @"Add Group";
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
- self.navigationItem.title = self.account.username;
+ self.navigationItem.title = [self.account displaynameForUUID:self.account.username safe:YES];
account.shared = NO;
account.sharingAccount = nil;
// The OpenStack project is provided under the Apache 2.0 license.
//
-#import <Foundation/Foundation.h>
-
// this class performs API calls on accounts and broadcasts NSNotifications to any other
// object that chooses to observe the notification
ASINetworkQueue *queue;
}
-@property (nonatomic, retain) ASINetworkQueue *queue;
-
@property (nonatomic, assign) OpenStackAccount *account;
+@property (nonatomic, retain) ASINetworkQueue *queue;
-- (NSString *)notificationName:(NSString *)key identifier:(NSString *)identifier;
-- (void)notify:(NSString *)name request:(OpenStackRequest *)request;
-- (void)notify:(NSString *)name request:(OpenStackRequest *)request object:(id)object;
+- (APICallback *)userCatalogForDisplaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs;
-// object storage
+- (APICallback *)authenticate;
+- (APICallback *)getSharingAccounts;
- (APICallback *)getStorageAccountInfo;
-- (APICallback *)getSharingAccounts;
- (APICallback *)getContainers;
+- (APICallback *)writeAccountMetadata:(NSDictionary *)accountInfo;
+
+- (APICallback *)getContainerInfo:(Container *)container;
- (APICallback *)createContainer:(Container *)container;
- (APICallback *)deleteContainer:(Container *)container;
-
-- (void)getObjects:(Container *)container;
- (void)getObjects:(Container *)container afterMarker:(NSString *)marker objectsBuffer:(NSMutableDictionary *)objectsBuffer;
-- (APICallback *)getContainerInfo:(Container *)container;
+- (void)getObjects:(Container *)container;
+- (APICallback *)writeContainerPolicy:(Container *)container;
+
- (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object;
- (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object version:(NSString *)version;
- (APICallback *)getObjectVersionsList:(Container *)container object:(StorageObject *)object;
-- (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate;
-
-- (APICallback *)getObject:(Container *)container
- object:(StorageObject *)object
- downloadProgressDelegate:(id)downloadProgressDelegate
+- (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate
+ requestUserInfo:(NSDictionary *)requestUserInfo version:(NSString *)version;
+- (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate
requestUserInfo:(NSDictionary *)requestUserInfo;
-
-- (APICallback *)getObject:(Container *)container
- object:(StorageObject *)object
- downloadProgressDelegate:(id)downloadProgressDelegate
- requestUserInfo:(NSDictionary *)requestUserInfo
- version:(NSString *)version;
-
-
+- (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate;
- (APICallback *)writeObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate;
- (APICallback *)writeObjectMetadata:(Container *)container object:(StorageObject *)object;
- (APICallback *)deleteObject:(Container *)container object:(StorageObject *)object;
-// account actions
-
-- (APICallback *)writeAccountMetadata:(NSDictionary *)accountInfo;
-
-// container actions
-
-- (APICallback *)writeContainerPolicy:(Container *)container;
+- (NSString *)notificationName:(NSString *)key identifier:(NSString *)identifier;
+- (void)notify:(NSString *)name request:(OpenStackRequest *)request;
+- (void)notify:(NSString *)name request:(OpenStackRequest *)request object:(id)object;
@end
#import "Container.h"
#import "Folder.h"
#import "StorageObject.h"
-#import "GetContainersRequest.h"
#import "GetObjectsRequest.h"
#import "ASINetworkQueue.h"
#import "APICallback.h"
#import "JSON.h"
#import "OpenStackAppDelegate.h"
+#import "NSString+Conveniences.h"
@implementation AccountManager
- (APICallback *)callbackWithRequest:(id)request success:(APIResponseBlock)success failure:(APIResponseBlock)failure {
APICallback *callback = [[[APICallback alloc] initWithAccount:self.account request:request] autorelease];
((OpenStackRequest *)request).delegate = self;
- ((OpenStackRequest *)request).callback = callback;
-
+ ((OpenStackRequest *)request).callback = callback;
+
[request setCompletionBlock:^{
if ([request isSuccess]) {
success(request);
failure(request);
[request notify];
}];
- [request startAsynchronous];
+ [request startAsynchronous];
return callback;
}
return [self callbackWithRequest:request success:^(OpenStackRequest *request){} failure:^(OpenStackRequest *request){}];
}
-#pragma mark - Notification
-
-- (NSString *)notificationName:(NSString *)key identifier:(NSString *)identifier {
- return [NSString stringWithFormat:@"%@-%@-%@", key, self.account.uuid, identifier];
-}
-
-- (void)requestFinished:(OpenStackRequest *)request {
- NSString *notificationName = [request.userInfo objectForKey:@"notificationName"];
- if (!notificationName)
- return;
-
- id notificationObject = [request.userInfo objectForKey:@"notificationObject"];
- if ([request isSuccess]) {
- NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Succeeded", notificationName] object:notificationObject];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
- } else {
- NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Failed", notificationName] object:notificationObject userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
- }
+#pragma mark - API Calls
+#pragma mark User Catalog
+- (APICallback *)userCatalogForDisplaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs {
+ __block OpenStackRequest *request = [OpenStackRequest userCatalogRequest:self.account displaynames:displaynames UUIDs:UUIDs];
+ return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
+ NSDictionary *catalogs = [request catalogs];
+ NSDictionary *displaynameCatalog = [catalogs objectForKey:@"displayname_catalog"];
+ for (NSString *displayname in displaynameCatalog) {
+ [self.account.userCatalog setObject:displayname forKey:[displaynameCatalog objectForKey:displayname]];
+ }
+ if (UUIDs) {
+ NSDictionary *UUIDCatalog = [catalogs objectForKey:@"uuid_catalog"];
+ for (NSString *UUID in UUIDs) {
+ NSString *displayname = [UUIDCatalog objectForKey:UUID];
+ if (displayname) {
+ [self.account.userCatalog setObject:displayname forKey:UUID];
+ } else {
+ [self.account.userCatalog removeObjectForKey:UUID];
+ }
+ }
+ }
+ [self.account persist];
+ }];
}
-- (void)requestFailed:(OpenStackRequest *)request {
- NSString *notificationName = [request.userInfo objectForKey:@"notificationName"];
- if (!notificationName)
- return;
-
- id notificationObject = [request.userInfo objectForKey:@"notificationObject"];
- NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Failed", notificationName] object:notificationObject userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
-}
+#pragma mark Top
-- (void)notify:(NSString *)name request:(OpenStackRequest *)request {
- NSNotification *notification = [NSNotification notificationWithName:name object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
+- (APICallback *)authenticate {
+ __block OpenStackRequest *request = [OpenStackRequest authenticationRequest:self.account];
+ return [self callbackWithRequest:request];
}
-- (void)notify:(NSString *)name request:(OpenStackRequest *)request object:(id)object {
- NSNotification *notification = [NSNotification notificationWithName:name object:object userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
+- (APICallback *)getSharingAccounts {
+ __block OpenStackRequest *request = [OpenStackRequest getSharingAccountsRequest:self.account];
+ return [self callbackWithRequest:request];
}
-#pragma mark - API Calls
-
-#pragma mark Object Storage
+#pragma mark Account
- (APICallback *)getStorageAccountInfo {
__block OpenStackRequest *request = [OpenStackRequest getStorageAccountInfoRequest:self.account];
- return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
- self.account.containerCount = [[[request responseHeaders] objectForKey:@"X-Account-Container-Count"] intValue];
+ return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
NSString *bytesUsedString = [request.responseHeaders objectForKey:@"X-Account-Bytes-Used"];
self.account.bytesUsed = (bytesUsedString ?
[NSNumber numberWithUnsignedLongLong:strtoull([bytesUsedString UTF8String], NULL, 0)] : nil);
self.account.policyQuota = (policyQuotaString ?
[NSNumber numberWithUnsignedLongLong:strtoull([policyQuotaString UTF8String], NULL, 0)] : nil);
[self.account persist];
- self.account.containerCount = [self.account.containers count];
}];
}
-- (APICallback *)getSharingAccounts {
- __block OpenStackRequest *request = [OpenStackRequest getSharingAccountsRequest:self.account];
- return [self callbackWithRequest:request];
+- (APICallback *)getContainers {
+ __block OpenStackRequest *request = [OpenStackRequest getContainersRequest:self.account];
+ return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
+ self.account.containers = [request containers];
+ NSString *bytesUsedString = [request.responseHeaders objectForKey:@"X-Account-Bytes-Used"];
+ self.account.bytesUsed = (bytesUsedString ?
+ [NSNumber numberWithUnsignedLongLong:strtoull([bytesUsedString UTF8String], NULL, 0)] : nil);
+ NSString *policyQuotaString = [request.responseHeaders objectForKey:@"X-Account-Policy-Quota"];
+ self.account.policyQuota = (policyQuotaString ?
+ [NSNumber numberWithUnsignedLongLong:strtoull([policyQuotaString UTF8String], NULL, 0)] : nil);
+ [self.account persist];
+ }];
}
-- (APICallback *)getContainers {
- __block OpenStackRequest *request = [OpenStackRequest filesRequest:self.account method:@"GET" path:@""];
- return [self callbackWithRequest:request];
+- (APICallback *)writeAccountMetadata:(NSDictionary *)accountInfo {
+ __block OpenStackRequest *request = [OpenStackRequest writeAccountMetadataRequest:self.account withAccountInfo:accountInfo];
+ return [self callbackWithRequest:request];
+}
+
+#pragma mark Container
+
+- (APICallback *)getContainerInfo:(Container *)container {
+ __block OpenStackRequest *request = [OpenStackRequest getContainerInfoRequest:self.account container:container];
+ return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
+ container.metadata = [NSMutableDictionary dictionary];
+ for (NSString *headerName in request.responseHeaders) {
+ if ([headerName hasPrefix:@"X-Container-Meta-"]) {
+ [container.metadata setObject:[NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:headerName]]
+ forKey:[NSString decodeFromPercentEscape:[headerName substringFromIndex:17]]];
+ }
+ }
+ }];
}
- (APICallback *)createContainer:(Container *)container {
__block OpenStackRequest *request = [OpenStackRequest createContainerRequest:self.account container:container];
return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
-
- [self.account.containers setObject:container forKey:container.name];
+ [self.account.containers setObject:container forKey:container.name];
[self.account persist];
- self.account.containerCount = [self.account.containers count];
}];
}
- (APICallback *)deleteContainer:(Container *)container {
__block OpenStackRequest *request = [OpenStackRequest deleteContainerRequest:self.account container:container];
- return [self callbackWithRequest:request];
-
-}
-
-- (void)getObjects:(Container *)container {
- [self getObjects:container afterMarker:nil objectsBuffer:nil];
+ return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
+ }];
}
-- (void)getObjects:(Container *)container
- afterMarker:(NSString *)marker
- objectsBuffer:(NSMutableDictionary *)objectsBuffer {
- if (![self queue]) {
- [self setQueue:(ASINetworkQueue *)[[[NSOperationQueue alloc] init] autorelease]];
- }
+- (void)getObjects:(Container *)container afterMarker:(NSString *)marker objectsBuffer:(NSMutableDictionary *)objectsBuffer {
+ if (!self.queue) {
+ self.queue = [ASINetworkQueue queue];
+ self.queue.shouldCancelAllRequestsOnFailure = NO;
+ [self.queue go];
+ }
GetObjectsRequest *request = [GetObjectsRequest request:self.account container:container marker:marker objectsBuffer:objectsBuffer];
[queue addOperation:request];
}
-- (void)getObjectsSucceeded:(OpenStackRequest *)request {
- if ([request isSuccess]) {
- Container *container = [request.userInfo objectForKey:@"container"];
- NSMutableDictionary *objects = [request objects];
- container.rootFolder = [Folder folder];
- container.rootFolder.objects = objects;
- [self.account persist];
-
- NSNotification *notification = [NSNotification notificationWithName:@"getObjectsSucceeded" object:self.account userInfo:[NSDictionary dictionaryWithObject:container forKey:@"container"]];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
- } else {
- NSNotification *notification = [NSNotification notificationWithName:@"getObjectsFailed" object:self.account userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
- }
-}
-
-- (void)getObjectsFailed:(OpenStackRequest *)request {
- NSNotification *notification = [NSNotification notificationWithName:@"getObjectsFailed" object:self.account userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
+- (void)getObjects:(Container *)container {
+ [self getObjects:container afterMarker:nil objectsBuffer:nil];
}
-- (APICallback *)getContainerInfo:(Container *)container {
- __block OpenStackRequest *request = [OpenStackRequest getContainerInfoRequest:self.account container:container];
+- (APICallback *)writeContainerPolicy:(Container *)container {
+ __block OpenStackRequest *request = [OpenStackRequest writeContainerPolicyRequest:self.account container:container];
return [self callbackWithRequest:request];
}
-- (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object {
+#pragma mark Storage Object
+
+- (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object {
__block OpenStackRequest *request = [OpenStackRequest getObjectInfoRequest:self.account container:container object:object];
return [self callbackWithRequest:request];
}
- (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object version:(NSString *)version {
- if (!version)
+ if (!version) {
return [self getObjectInfo:container object:object];
+ }
__block OpenStackRequest *request = [OpenStackRequest getObjectInfoRequest:self.account container:container object:object version:version];
return [self callbackWithRequest:request];
return [self callbackWithRequest:request];
}
-- (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate {
- return [self getObject:container object:object downloadProgressDelegate:downloadProgressDelegate requestUserInfo:nil];
-}
-
-- (APICallback *)getObject:(Container *)container
- object:(StorageObject *)object
- downloadProgressDelegate:(id)downloadProgressDelegate
- requestUserInfo:(NSDictionary *)requestUserInfo {
-
- return [self getObject:container object:object downloadProgressDelegate:downloadProgressDelegate requestUserInfo:requestUserInfo version:nil];
-}
-
-- (APICallback *)getObject:(Container *)container
- object:(StorageObject *)object
- downloadProgressDelegate:(id)downloadProgressDelegate
- requestUserInfo:(NSDictionary *)requestUserInfo
- version:(NSString *)version{
+- (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate
+ requestUserInfo:(NSDictionary *)requestUserInfo version:(NSString *)version {
__block OpenStackRequest *request = [OpenStackRequest getObjectRequest:self.account container:container object:object version:version];
request.delegate = self;
request.downloadProgressDelegate = downloadProgressDelegate;
- request.showAccurateProgress = YES;
+ request.showAccurateProgress = YES;
if (requestUserInfo) {
request.userInfo = requestUserInfo;
}
OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
[app setObjectDownloadRequest:request forAccount:account container:container object:object];
return [self callbackWithRequest:request
- success:^(OpenStackRequest *request) {
- if (!object.hash)
- object.hash = [request.responseHeaders objectForKey:@"X-Object-Hash"];
- OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
- NSString *filePath = [app.cacheDirectoryPath stringByAppendingFormat:@"/%@.%@", object.hash, object.name.pathExtension];
- [[request responseData] writeToFile:filePath atomically:YES];
- [app setCacheFilePath:filePath forHash:object.hash];
- [app removeObjectDownloadRequestForAccount:account container:container object:object];
- }
- failure:^(OpenStackRequest *request) {
- OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
- [app removeObjectDownloadRequestForAccount:account container:container object:object];
- }];
+ success:^(OpenStackRequest *request) {
+ if (!object.hash) {
+ object.hash = [request.responseHeaders objectForKey:@"X-Object-Hash"];
+ }
+ OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+ NSString *filePath = [app.cacheDirectoryPath stringByAppendingFormat:@"/%@.%@", object.hash, object.name.pathExtension];
+ [[request responseData] writeToFile:filePath atomically:YES];
+ [app setCacheFilePath:filePath forHash:object.hash];
+ [app removeObjectDownloadRequestForAccount:account container:container object:object];
+ }
+ failure:^(OpenStackRequest *request) {
+ OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+ [app removeObjectDownloadRequestForAccount:account container:container object:object];
+ }];
+}
+
+- (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate
+ requestUserInfo:(NSDictionary *)requestUserInfo {
+ return [self getObject:container object:object downloadProgressDelegate:downloadProgressDelegate requestUserInfo:requestUserInfo version:nil];
+}
+
+- (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate {
+ return [self getObject:container object:object downloadProgressDelegate:downloadProgressDelegate requestUserInfo:nil version:nil];
}
- (APICallback *)writeObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate {
request.delegate = self;
request.uploadProgressDelegate = downloadProgressDelegate;
request.showAccurateProgress = YES;
-
+ return [self callbackWithRequest:request];
+}
+
+- (APICallback *)writeObjectMetadata:(Container *)container object:(StorageObject *)object {
+ __block OpenStackRequest *request = [OpenStackRequest writeObjectMetadataRequest:self.account container:container object:object];
return [self callbackWithRequest:request];
}
return [self callbackWithRequest:request];
}
-- (APICallback *)writeObjectMetadata:(Container *)container object:(StorageObject *)object {
- __block OpenStackRequest *request = [OpenStackRequest writeObjectMetadataRequest:self.account container:container object:object];
- return [self callbackWithRequest:request];
+#pragma mark - Notifications
+
+- (NSString *)notificationName:(NSString *)key identifier:(NSString *)identifier {
+ return [NSString stringWithFormat:@"%@-%@-%@", key, self.account.uuid, identifier];
}
-- (APICallback *)writeAccountMetadata:(NSDictionary *)accountInfo {
- __block OpenStackRequest *request = [OpenStackRequest writeAccountMetadataRequest:self.account withAccountInfo:accountInfo];
- return [self callbackWithRequest:request];
+- (void)notify:(NSString *)name request:(OpenStackRequest *)request {
+ NSNotification *notification = [NSNotification notificationWithName:name object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
}
-- (APICallback *)writeContainerPolicy:(Container *)container {
- __block OpenStackRequest *request = [OpenStackRequest writeContainerPolicyRequest:self.account container:container];
- return [self callbackWithRequest:request];
+- (void)notify:(NSString *)name request:(OpenStackRequest *)request object:(id)object {
+ NSNotification *notification = [NSNotification notificationWithName:name object:object userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
}
-#pragma mark - Memory Management
+#pragma mark - Request Delegates
-- (void)dealloc {
- [super dealloc];
+- (void)requestFinished:(OpenStackRequest *)request {
+ NSString *notificationName = [request.userInfo objectForKey:@"notificationName"];
+ if (!notificationName)
+ return;
+
+ id notificationObject = [request.userInfo objectForKey:@"notificationObject"];
+ if ([request isSuccess]) {
+ NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Succeeded", notificationName] object:notificationObject];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+ } else {
+ NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Failed", notificationName] object:notificationObject userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+ }
+}
+
+- (void)requestFailed:(OpenStackRequest *)request {
+ NSString *notificationName = [request.userInfo objectForKey:@"notificationName"];
+ if (!notificationName)
+ return;
+
+ id notificationObject = [request.userInfo objectForKey:@"notificationObject"];
+ NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Failed", notificationName] object:notificationObject userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+}
+
+#pragma mark - Observers
+
+- (void)getObjectsSucceeded:(OpenStackRequest *)request {
+ if ([request isSuccess]) {
+ Container *container = [request.userInfo objectForKey:@"container"];
+ NSMutableDictionary *objects = [request objects];
+ container.rootFolder = [Folder folder];
+ container.rootFolder.objects = objects;
+ [self.account persist];
+
+ NSNotification *notification = [NSNotification notificationWithName:@"getObjectsSucceeded" object:self.account userInfo:[NSDictionary dictionaryWithObject:container forKey:@"container"]];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+ } else {
+ NSNotification *notification = [NSNotification notificationWithName:@"getObjectsFailed" object:self.account userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+ }
+}
+
+- (void)getObjectsFailed:(OpenStackRequest *)request {
+ NSNotification *notification = [NSNotification notificationWithName:@"getObjectsFailed" object:self.account userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
}
@end
NSString *username;
NSString *authToken;
+ NSString *displayname;
}
@property (nonatomic, retain) OpenStackAccount *account;
@property (nonatomic, retain) NSString *username;
@property (nonatomic, retain) NSString *authToken;
+@property (nonatomic, retain) NSString *displayname;
- (void)setUsername:(NSString *)aUsername andAuthToken:(NSString *)anAuthToken;
- (void)saveButtonPressed:(id)sender;
#import "AccountSettingsViewController.h"
#import "OpenStackAccount.h"
+#import "AccountManager.h"
+#import "APICallback.h"
#import "ActivityIndicatorView.h"
#import "Provider.h"
#import "RSTextFieldCell.h"
#define kUsername 0
#define kAuthToken 1
+#define kDisplayname 2
@implementation AccountSettingsViewController
-@synthesize account, username, authToken;
+@synthesize account, username, authToken, displayname;
#pragma mark - View lifecycle
self.navigationItem.title = @"Authentication";
userDetailsSection = 0;
getTokenSection = 1;
+ displayname = [[self.account displaynameForUUID:self.account.username safe:NO] retain];
[self addSaveButton];
self.navigationItem.rightBarButtonItem.enabled = NO;
}
- (void)dealloc {
[username release];
[authToken release];
+ [displayname release];
[account release];
[super dealloc];
}
- (void) updateSaveButtonForUsername:(NSString *)checkUsername andAuthToken:(NSString *)checkAuthToken {
self.navigationItem.rightBarButtonItem.enabled = (checkUsername && checkUsername.length &&
- checkAuthToken &&checkAuthToken.length &&
+ checkAuthToken && checkAuthToken.length &&
(![checkUsername isEqualToString:account.username] ||
![checkAuthToken isEqualToString:account.authToken]));
}
- (void)setUsername:(NSString *)aUsername {
[username release];
username = [aUsername retain];
+ self.displayname = [self.account displaynameForUUID:username safe:NO];
[self updateSaveButtonForUsername:username andAuthToken:authTokenTextField.text];
}
[self updateSaveButtonForUsername:usernameTextField.text andAuthToken:authToken];
}
+- (void)setDisplayname:(NSString *)aDisplayname {
+ BOOL reloadDisplayname = (displayname && ![displayname isEqualToString:aDisplayname]) || (!displayname && aDisplayname);
+ [displayname release];
+ displayname = [aDisplayname retain];
+ if (reloadDisplayname) {
+ [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:kDisplayname inSection:userDetailsSection]]
+ withRowAnimation:UITableViewRowAnimationNone];
+ }
+}
+
#pragma mark - Actions
- (void)setUsername:(NSString *)aUsername andAuthToken:(NSString *)anAuthToken {
if (aUsername) {
[username release];
username = [aUsername retain];
+ self.displayname = [self.account displaynameForUUID:username safe:NO];
}
if (anAuthToken) {
[authToken release];
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == userDetailsSection)
- return 2;
+ return 3;
else
return 1;
}
[cell.textLabel setBackgroundColor:[UIColor clearColor]];
if (indexPath.row == kUsername) {
- cell.textLabel.text = @"Username";
+ cell.textLabel.text = @"UUID";
+ cell.userInteractionEnabled = YES;
+ cell.textField.delegate = self;
+ cell.textField.returnKeyType = UIReturnKeyNext;
+ cell.textField.text = (username ? username : self.account.username);
usernameTextField = cell.textField;
- usernameTextField.delegate = self;
- usernameTextField.secureTextEntry = NO;
- usernameTextField.returnKeyType = UIReturnKeyNext;
- if (username) {
- usernameTextField.text = username;
- } else {
- usernameTextField.text = self.account.username;
- }
} else if (indexPath.row == kAuthToken) {
cell.textLabel.text = @"Token";
+ cell.userInteractionEnabled = YES;
+ cell.textField.delegate = self;
+ cell.textField.returnKeyType = UIReturnKeyDone;
+ cell.textField.text = (authToken ? authToken : self.account.authToken);
authTokenTextField = cell.textField;
- authTokenTextField.secureTextEntry = NO;
- authTokenTextField.delegate = self;
- authTokenTextField.returnKeyType = UIReturnKeyDone;
- if (authToken) {
- authTokenTextField.text = authToken;
- } else {
- authTokenTextField.text = self.account.authToken;
- }
+ } else if (indexPath.row == kDisplayname) {
+ cell.textLabel.text = @"User";
+ cell.userInteractionEnabled = NO;
+ cell.textField.textColor = [UIColor grayColor];
+ cell.textField.delegate = nil;
+ cell.textField.text = displayname;
}
} else {
cell = (RSTextFieldCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == getTokenSection) {
NSString *protocol = [[[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"] objectAtIndex:0] objectForKey:@"CFBundleURLSchemes"] objectAtIndex:0];
- NSString *loginURL = [NSString stringWithFormat:@"%@?next=%@://login&force=", account.pithosLoginURLPrefix, protocol];
+ NSString *loginURL = [NSString stringWithFormat:@"%@?next=%@://login&force=", account.provider.loginURL, protocol];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:loginURL]];
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
- (void)authenticate {
if (!usernameTextField.text || [usernameTextField.text isEqualToString:@""]) {
- [self alert:nil message:@"Please enter your Username."];
+ [self alert:nil message:@"Please enter your UUID."];
self.navigationItem.rightBarButtonItem.enabled = YES;
[usernameTextField becomeFirstResponder];
} else if (!authTokenTextField.text || [authTokenTextField.text isEqualToString:@""]) {
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Authenticating..."
andAddToView:self.view];
- OpenStackRequest *request = [OpenStackRequest authenticationRequest:temporaryAccount];
- request.completionBlock = ^{
- [activityIndicatorView removeFromSuperview];
- if ([request isSuccess]) {
- account.username = request.account.username;
- account.authToken = request.account.authToken;
- 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];
- } 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];
+ [[temporaryAccount.manager authenticate]
+ success:^(OpenStackRequest *request) {
+ if ([request isSuccess]) {
+ account.username = request.account.username;
+ account.authToken = request.account.authToken;
+ NSString *storageURLString = [[request responseHeaders] objectForKey:@"X-Storage-Url"];
+ if (storageURLString) {
+ account.filesURL = [NSURL URLWithString:storageURLString];
+ } else {
+ account.filesURL = [account.provider.authEndpointURL URLByAppendingPathComponent:account.username];
+ }
+ [account persist];
+ [[account.manager userCatalogForDisplaynames:nil
+ UUIDs:[NSArray arrayWithObject:account.username]]
+ success:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ self.displayname = [account displaynameForUUID:account.username safe:NO];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ }];
+ } else {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ self.navigationItem.rightBarButtonItem.enabled = YES;
+ [self alert:@"Authentication Failure" message:@"Please check your UUID and Token."];
+ }
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ self.navigationItem.rightBarButtonItem.enabled = YES;
+ if ([request responseStatusCode] == 401) {
+ [self alert:@"Authentication Failure" message:@"Please check your UUID and Token."];
+ } else {
+ [self failOnBadConnection];
+ }
+ }];
}
}
@end
-
- (void)addToView:(UIView *)view scrollOffset:(CGFloat)offset;
- (void)addToView:(UIView *)view;
-- (void)removeFromSuperview;
+
+- (void)stopAnimatingAndRemoveFromSuperview;
@end
self.superview.userInteractionEnabled = NO;
}
+- (void)willMoveToSuperview:(UIView *)newSuperview {
+ self.superview.userInteractionEnabled = YES;
+ [super willMoveToSuperview:newSuperview];
+}
+
#pragma mark - Memory management
- (void)dealloc {
[self addToView:view scrollOffset:0.0];
}
-- (void)removeFromSuperview {
+- (void)stopAnimatingAndRemoveFromSuperview {
[self.spinner stopAnimating];
- [UIView animateWithDuration:kFadeTime animations:^{
- self.alpha = 0.0;
- } completion:^(BOOL finished) {
- self.superview.userInteractionEnabled = YES;
- [super removeFromSuperview];
- }];
+ [UIView transitionWithView:self
+ duration:kFadeTime
+ options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionTransitionNone
+ animations:^{
+ [self removeFromSuperview];
+ }
+ completion:nil];
}
@end
@class ContainersViewController, OpenStackAccount;
@interface AddContainerViewController : UITableViewController <UITextFieldDelegate> {
- UITextField *textField;
- ContainersViewController *containersViewController;
OpenStackAccount *account;
+ ContainersViewController *containersViewController;
+
+ UITextField *containerNameTextField;
}
-@property (nonatomic, retain) ContainersViewController *containersViewController;
@property (nonatomic, retain) OpenStackAccount *account;
+@property (nonatomic, retain) ContainersViewController *containersViewController;
+@property (nonatomic, retain) UITextField *containerNameTextField;
@end
@implementation AddContainerViewController
-@synthesize containersViewController, account;
+@synthesize account, containersViewController, containerNameTextField;
#pragma mark - View lifecycle
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
- [textField becomeFirstResponder];
+ [containerNameTextField becomeFirstResponder];
}
#pragma mark - Memory management
- (void)dealloc {
- [containersViewController release];
[account release];
+ [containersViewController release];
+ [containerNameTextField release];
[super dealloc];
}
+#pragma mark - Button handlers
+
+- (void)saveButtonPressed:(id)sender {
+ if ([containerNameTextField.text isEqualToString:@""]) {
+ [self alert:@"Invalid input" message:@"Container name cannot be empty."];
+ } else {
+ [containerNameTextField resignFirstResponder];
+ __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Creating container..."
+ andAddToView:self.view];
+ Container *container = [account.containers objectForKey:containerNameTextField.text];
+ if (!container) {
+ container = [[[Container alloc] init] autorelease];
+ container.name = containerNameTextField.text;
+ }
+ [[self.account.manager createContainer:container]
+ success:^(OpenStackRequest *request) {
+ [containersViewController.tableView reloadData];
+ containersViewController.refreshWhenAppeared = YES;
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self dismissModalViewControllerAnimated:YES];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self alert:@"There was a problem creating your container." request:request];
+ }];
+ }
+}
+
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (cell == nil) {
cell = [[[RSTextFieldCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier] autorelease];
cell.modalPresentationStyle = UIModalPresentationFormSheet;
- textField = cell.textField;
- textField.delegate = self;
- textField.clearButtonMode = UITextFieldViewModeWhileEditing;
+ cell.textLabel.text = @"Name";
+ cell.textField.delegate = self;
+ cell.textField.clearButtonMode = UITextFieldViewModeWhileEditing;
+ self.containerNameTextField = cell.textField;
}
- cell.textLabel.text = @"Name";
return cell;
}
return NO;
}
-#pragma mark - Button handlers
-
-- (void)saveButtonPressed:(id)sender {
- if ([textField.text isEqualToString:@""]) {
- [self alert:@"Container Name Required" message:@"Please enter a container name."];
- } else {
- [textField resignFirstResponder];
- __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Creating container..."
- andAddToView:self.view];
- Container *container = [[Container alloc] init];
- container.name = textField.text;
- [[self.account.manager createContainer:container]
- success:^(OpenStackRequest *request) {
- [self.containersViewController.tableView reloadData];
- [activityIndicatorView removeFromSuperview];
- [self dismissModalViewControllerAnimated:YES];
- [container release];
- }
- failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- [self alert:@"There was a problem creating your container." request:request];
- [container release];
- }];
- }
-}
-
@end
Container *container;
Folder *folder;
FolderViewController *folderViewController;
- id successObserver;
- id failureObserver;
BOOL hasPhotoLibrary;
BOOL hasCamera;
newFolder.name = [newFolder.name stringByAppendingString:@"/"];
newFolder.parent = folder;
newFolder.sharing = folder.sharing;
- BOOL currentFolderIsRoot = NO;
- if ([folderViewController.folder isEqual:container.rootFolder]) {
- currentFolderIsRoot = YES;
- }
+ BOOL currentFolderIsRoot = (folderViewController.folder == container.rootFolder);
[folder addFolder:newFolder];
folderViewController.folder = folderViewController.folder;
if (currentFolderIsRoot) {
container.count += 1;
container.rootFolder = folder;
- [self.account.containers setObject:container forKey:container.name];
}
- if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
- [folderViewController setDetailViewController];
-
allowOverwrite = NO;
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self dismissModalViewControllerAnimated:YES];
[newFolder release];
[object release];
}
failure:^(OpenStackRequest *request) {
allowOverwrite = NO;
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self alert:@"There was a problem creating this folder." request:request];
[object release];
}];
Container *container;
Folder *folder;
FolderViewController *folderViewController;
- id successObserver;
- id failureObserver;
}
@property (nonatomic, retain) OpenStackAccount *account;
Container *container;
Folder *folder;
FolderViewController *folderViewController;
- id successObserver;
- id failureObserver;
UIImage *image;
NSData *data;
}
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:1 inSection:kName]] withRowAnimation:UITableViewRowAnimationNone];
- [qualityActivityIndicatorView removeFromSuperview];
+ [qualityActivityIndicatorView stopAnimatingAndRemoveFromSuperview];
}
- (void)sliderFinished:(id)sender {
andAddToView:self.view];
[[self.account.manager writeObject:self.container object:object downloadProgressDelegate:activityIndicatorView.progressView]
success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
object.data = nil;
object.sharing = folder.sharing;
object.lastModified = [ComputeModel dateFromRFC1123String:[request.responseHeaders objectForKey:@"Date"]];
- BOOL currentFolderIsRoot = NO;
- if ([folderViewController.folder isEqual:container.rootFolder]) {
- currentFolderIsRoot = YES;
- }
+ BOOL currentFolderIsRoot = (folderViewController.folder == container.rootFolder);
[folder addObject:object];
folderViewController.folder = folderViewController.folder;
if (currentFolderIsRoot) {
container.count += 1;
container.rootFolder = folder;
- [self.account.containers setObject:container forKey:container.name];
}
- if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
- [folderViewController setDetailViewController];
allowOverwrite = NO;
- [self dismissModalViewControllerAnimated:YES];
+ [[self.account.manager getObjectInfo:self.container object:object]
+ success:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ object.hash = [request.responseHeaders objectForKey:@"X-Object-Hash"];
+ [self dismissModalViewControllerAnimated:YES];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self dismissModalViewControllerAnimated:YES];
+ }];
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
allowOverwrite = NO;
- [self alert:@"There was a problem uploading this file." request:request];
+ [self alert:@"There was a problem uploading the file." request:request];
}];
}
Container *container;
ContainersViewController *containersViewController;
FolderViewController *rootFolderViewController;
- id successObserver;
- id failureObserver;
- NSInteger deleteSection;
NSInteger policySection;
- NSIndexPath *selectedContainerIndexPath;
+ NSInteger deleteSection;
}
@property (nonatomic, retain) OpenStackAccount *account;
@property (nonatomic, retain) Container *container;
@property (nonatomic, retain) ContainersViewController *containersViewController;
@property (nonatomic, retain) FolderViewController *rootFolderViewController;
-@property (nonatomic, retain) NSIndexPath *selectedContainerIndexPath;
- (void)reloadMetadataSection;
@implementation ContainerDetailViewController
-@synthesize account, container, containersViewController, selectedContainerIndexPath, rootFolderViewController;
+@synthesize account, container, containersViewController, rootFolderViewController;
+
+#pragma mark - View lifecycle
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
-- (void)setBackgroundView {
- if (!self.container) {
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ if (!account.sharingAccount) {
+ policySection = 2;
+ deleteSection = 3;
+ } else {
+ policySection = -1;
+ deleteSection = -1;
+ }
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+ if (container) {
+ self.navigationItem.title = container.name;
+ } else {
UIView *viewContainer = [[UIView alloc] init];
viewContainer.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
viewContainer.backgroundColor = [UIColor iPadTableBackgroundColor];
}
}
-#pragma mark -
-#pragma mark View lifecycle
-
-- (void)viewDidLoad {
- [super viewDidLoad];
- if (!account.sharingAccount) {
- deleteSection = 3;
- policySection = 2;
- } else {
- deleteSection = -1;
- policySection = -1;
+- (void)viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+ if (container && ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)) {
+ [self reloadMetadataSection];
}
}
-- (void)viewWillAppear:(BOOL)animated {
- [super viewWillAppear:animated];
- if (container) {
- self.navigationItem.title = container.name;
- }
- [self setBackgroundView];
- [self.tableView reloadData];
+#pragma mark - Memory management
+
+- (void)dealloc {
+ [account release];
+ [container release];
+ [containersViewController release];
+ [rootFolderViewController release];
+ [super dealloc];
}
-- (void)viewDidAppear:(BOOL)animated {
- [super viewDidAppear:animated];
- if (self.container && !container.metadata && ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone))
- [self reloadMetadataSection];
+#pragma mark - Internal
+
+- (void)delete {
+ containersViewController.deletedContainer = container;
+ [containersViewController.navigationController popViewControllerAnimated:YES];
+ [containersViewController setDetailViewControllerForContainer:nil];
}
-- (void)viewWillDisappear:(BOOL)animated {
- [super viewWillDisappear:animated];
+#pragma mark - Actions
+
+- (void)reloadMetadataSection {
+ __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading metadata..."
+ andAddToView:self.view
+ scrollOffset:self.tableView.contentOffset.y];
+ [[self.account.manager getContainerInfo:container]
+ success:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kMetadata] withRowAnimation:UITableViewRowAnimationFade];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self alert:@"There was a problem getting the container's metadata." request:request];
+ }];
}
-#pragma mark -
-#pragma mark Table view data source
+#pragma mark - UITableViewDataSource
-- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
- if (section == deleteSection) {
- return @"Only empty containers can be deleted.";
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+ if (container) {
+ return (account.sharingAccount ? 2 : 4);
} else {
- return @"";
+ return 0;
}
}
-- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
- if (self.container) {
- if (account.sharingAccount)
- return 2;
- else
- return 4;
+- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
+ if (section == deleteSection) {
+ return @"Only empty containers can be deleted.";
} else {
- return 0;
+ return nil;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- // Return the number of rows in the section.
if (section == kOverview) {
return 2;
} else if (section == kMetadata) {
- if (account.sharingAccount)
- return [container.metadata count];
- else
- return 1 + [container.metadata count];
+ return (account.sharingAccount ? container.metadata.count : (container.metadata.count + 1));
} else if (section == policySection) {
- if (account.sharingAccount)
- return 2;
- else
- return 3;
+ return (account.sharingAccount ? 2 : 3);
} else {
return 1;
}
}
-// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == deleteSection) {
static NSString *CellIdentifier = @"DeleteCell";
cell.textLabel.textAlignment = UITextAlignmentCenter;
cell.textLabel.text = @"Delete Container";
}
- if ((self.container.count == 0) ||
- (self.container.rootFolder && !self.container.rootFolder.objectsAndFoldersCount)) {
+ if (!self.container.count || (self.container.rootFolder && !self.container.rootFolder.objectsAndFoldersCount)) {
cell.textLabel.textColor = [UIColor blackColor];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+ cell.userInteractionEnabled = YES;
} else {
cell.textLabel.textColor = [UIColor grayColor];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
- cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.detailTextLabel.backgroundColor = [UIColor clearColor];
cell.detailTextLabel.numberOfLines = 0;
- cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
+ cell.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation;
cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
}
cell.accessoryType = UITableViewCellAccessoryNone;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ cell.userInteractionEnabled = NO;
if (indexPath.section == kOverview) {
- cell.accessoryView = nil;
if (indexPath.row == 0) {
cell.textLabel.text = @"Name";
cell.detailTextLabel.text = container.name;
} else if (indexPath.row == 1) {
cell.textLabel.text = @"Size";
- cell.detailTextLabel.text = [container humanizedSize];
+ cell.detailTextLabel.text = [container osxStyleHumanizedSize];
}
} else if (indexPath.section == kMetadata) {
- if (account.sharingAccount) {
- cell.accessoryType = UITableViewCellAccessoryNone;
- cell.selectionStyle = UITableViewCellSelectionStyleNone;
- cell.userInteractionEnabled = NO;
- }
- else {
- cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+ if (!account.sharingAccount) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+ cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+ cell.userInteractionEnabled = YES;
}
- if (indexPath.row == [container.metadata count]) {
+ if (indexPath.row == container.metadata.count) {
cell.textLabel.text = @"Add Metadata";
- cell.detailTextLabel.text = @"";
+ cell.detailTextLabel.text = nil;
} else {
- NSString *key = [[container.metadata allKeys] objectAtIndex:indexPath.row];
- NSString *value = [container.metadata objectForKey:key];
- NSString *metadataKeyCellText = key;
- NSString *metadataValueCellText = value;
- if ([metadataKeyCellText length] > maxMetadataViewableLength) {
- metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
- metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
- }
- if ([metadataValueCellText length] > maxMetadataViewableLength) {
- metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
- metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
+ NSString *key = [[[container.metadata allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:indexPath.row];
+ if (key.length > maxMetadataViewableLength) {
+ cell.textLabel.text = [[key substringToIndex:(maxMetadataViewableLength - 3)] stringByAppendingString:@"..."];
+ } else {
+ cell.textLabel.text = key;
}
- cell.textLabel.text = metadataKeyCellText;
- cell.detailTextLabel.text = metadataValueCellText;
+ cell.detailTextLabel.text = [container.metadata objectForKey:key];
}
} else if (indexPath.section == policySection) {
- cell.selectionStyle = UITableViewCellSelectionStyleBlue;
if (indexPath.row == 0) {
cell.textLabel.text = @"Versioning";
cell.detailTextLabel.text = container.versioning;
- cell.accessoryType = UITableViewCellAccessoryNone;
- cell.selectionStyle = UITableViewCellSelectionStyleNone;
- }
- else if (indexPath.row == 1) {
+ } else if (indexPath.row == 1) {
cell.textLabel.text = @"Quota";
cell.detailTextLabel.text = [NSString stringWithFormat:@"%u", container.quota];
- cell.accessoryType = UITableViewCellAccessoryNone;
- cell.selectionStyle = UITableViewCellSelectionStyleNone;
- }
- else {
+ } else if (indexPath.row == 2) {
cell.textLabel.text = @"Edit policy";
- cell.detailTextLabel.text = @"";
+ cell.detailTextLabel.text = nil;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+ cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+ cell.userInteractionEnabled = YES;
}
}
return cell;
}
-#pragma mark -
-#pragma mark Table view delegate
+#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
-
if (indexPath.section == kMetadata) {
EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
- NSString *metadataKey;
- NSString *metadataValue;
-
- if (indexPath.row == [self.container.metadata count]) {
- metadataKey = @"";
- metadataValue = @"";
+ if (indexPath.row == container.metadata.count) {
+ vc.metadataKey = @"";
+ vc.metadataValue = @"";
vc.removeMetadataEnabled = FALSE;
vc.navigationItem.title = @"Add Metadata";
- }
- else {
- metadataKey = [[self.container.metadata allKeys] objectAtIndex:indexPath.row];
- metadataValue = [self.container.metadata objectForKey:metadataKey];
+ } else {
+ vc.metadataKey = [[[container.metadata allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:indexPath.row];
+ vc.metadataValue = [container.metadata objectForKey:vc.metadataKey];
vc.removeMetadataEnabled = TRUE;
vc.navigationItem.title = @"Edit Metadata";
}
object.metadata = container.metadata;
object.fullPath = @"";
- vc.metadataKey = metadataKey;
- vc.metadataValue = metadataValue;
vc.account = account;
vc.container = container;
vc.object = object;
[self.navigationController pushViewController:vc animated:YES];
[vc release];
- } else if (indexPath.section == policySection) {
- if (indexPath.row == 2) {
- EditPolicyViewController *vc = [[EditPolicyViewController alloc] initWithNibName:@"EditPolicyViewController" bundle:nil];
-
- vc.account = account;
- vc.container = container;
- vc.containerDetailViewController = self;
-
- [self.navigationController pushViewController:vc animated:YES];
- [vc release];
- }
- } else if (indexPath.section == deleteSection) {
- if (self.container.count == 0 || !self.container.rootFolder.objectsAndFoldersCount) {
- UIActionSheet *deleteActionSheet = [[[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this container? This operation cannot be undone."
- delegate:self
- cancelButtonTitle:@"Cancel"
- destructiveButtonTitle:@"Delete Container"
- otherButtonTitles:nil] autorelease];
- [deleteActionSheet showInView:self.view];
- }
+ } else if ((indexPath.section == policySection) && (indexPath.row == 2)) {
+ EditPolicyViewController *vc = [[EditPolicyViewController alloc] initWithNibName:@"EditPolicyViewController" bundle:nil];
+ vc.account = account;
+ vc.container = container;
+ vc.containerDetailViewController = self;
+ [self.navigationController pushViewController:vc animated:YES];
+ [vc release];
+ } else if ((indexPath.section == deleteSection) && (!self.container.count || (!self.container.rootFolder && !self.container.rootFolder.objectsAndFoldersCount))) {
+ UIActionSheet *deleteActionSheet = [[[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this container? This operation cannot be undone."
+ delegate:self
+ cancelButtonTitle:@"Cancel"
+ destructiveButtonTitle:@"Delete Container"
+ otherButtonTitles:nil] autorelease];
+ [deleteActionSheet showInView:self.view];
}
}
-#pragma mark -
-#pragma mark Action Sheet
-
-- (void)deleteContainerRow {
- if ([self.account.containers count] == 0) {
- [self.containersViewController.tableView reloadData];
- } else {
- [self.containersViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedContainerIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
- }
- [self.rootFolderViewController.navigationController popViewControllerAnimated:YES];
-}
+#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Deleting container..."
andAddToView:self.view
scrollOffset:self.tableView.contentOffset.y];
- [[self.account.manager deleteContainer:self.container]
+ [[self.account.manager deleteContainer:container]
success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- [self.account.containers removeObjectForKey:self.container.name];
- [self.account persist];
- if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
- [self.containersViewController.navigationController popViewControllerAnimated:YES];
- } else {
- [self.navigationController popViewControllerAnimated:YES];
- }
- [self deleteContainerRow];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self delete];
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
// 404 Not Found means it's not there, so we can show the user that it's deleted
- if ([request responseStatusCode] == 404) {
- [self.account.containers removeObjectForKey:container.name];
- [self.account persist];
+ if (request.responseStatusCode == 404) {
+ [self delete];
} else {
[self alert:@"There was a problem deleting this container." request:request];
}
[self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:deleteSection] animated:YES];
}
-
-#pragma mark -
-#pragma mark Memory management
-
-- (void)dealloc {
- [account release];
- [container release];
- [containersViewController release];
- [selectedContainerIndexPath release];
- [rootFolderViewController release];
- [super dealloc];
-}
-
-#pragma mark -
-#pragma mark Helper functions
-
-- (void)reloadMetadataSection {
- __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading metadata..."
- andAddToView:self.view
- scrollOffset:self.tableView.contentOffset.y];
- [[self.account.manager getContainerInfo:container]
- success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- container.metadata = [NSMutableDictionary dictionary];
- for (NSString *header in request.responseHeaders) {
- NSString *metadataKey;
- NSString *metadataValue;
- if ([header rangeOfString:@"X-Container-Meta-"].location != NSNotFound) {
- metadataKey = [NSString decodeFromPercentEscape:[header substringFromIndex:17]];
- metadataValue = [NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:header]];
- [container.metadata setObject:metadataValue forKey:metadataKey];
- }
- }
- NSIndexSet *metadataSections = [NSIndexSet indexSetWithIndex:kMetadata];
- [self.tableView reloadSections:metadataSections withRowAnimation:UITableViewRowAnimationFade];
- }
- failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- [self alert:@"There was a problem retrieving the container's metadata." request:request];
- }];
-}
-
@end
-
#import "OpenStackViewController.h"
-@class OpenStackAccount, AccountHomeViewController, ContainerDetailViewController;
+@class OpenStackAccount, AccountHomeViewController, ContainerDetailViewController, Container;
@interface ContainersViewController : OpenStackViewController <UITableViewDelegate, UITableViewDataSource> {
- IBOutlet UITableView *tableView;
OpenStackAccount *account;
-
- BOOL containersLoaded;
- NSString *accountUsageInfo;
AccountHomeViewController *accountHomeViewController;
+ BOOL refreshWhenAppeared;
+ Container *deletedContainer;
+
+ UITableView *tableView;
+
ContainerDetailViewController *containerDetailViewController;
}
-@property (nonatomic, retain) IBOutlet UITableView *tableView;
@property (nonatomic, retain) OpenStackAccount *account;
-@property (nonatomic, readonly) NSString *accountUsageInfo;
@property (nonatomic, assign) AccountHomeViewController *accountHomeViewController;
+@property (nonatomic, assign) BOOL refreshWhenAppeared;
+@property (nonatomic, retain) Container *deletedContainer;
+
+@property (nonatomic, retain) IBOutlet UITableView *tableView;
+
@property (nonatomic, retain) ContainerDetailViewController *containerDetailViewController;
- (IBAction)refreshButtonPressed:(id)sender;
+- (IBAction)homeButtonPressed:(id)sender;
+- (void)setDetailViewControllerForContainer:(Container *)container;
@end
@implementation ContainersViewController
-@synthesize tableView, account, accountUsageInfo, accountHomeViewController, containerDetailViewController;
+@synthesize account, accountHomeViewController, refreshWhenAppeared, deletedContainer;
+@synthesize tableView;
+@synthesize containerDetailViewController;
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @"Containers";
- [self addAddButton];
- [self addHomeButton];
+ [self addAddButton];
+ self.navigationItem.rightBarButtonItem.enabled = (!account.shared && !account.sharingAccount);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
-
- if ([self.account.containers count] == 0) {
- self.tableView.allowsSelection = NO;
- self.tableView.scrollEnabled = NO;
- [self.tableView reloadData];
- }
- if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
- if ([self.account.containers count] == 0 ) {
- NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
- [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
- [self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
- } else if ([self.tableView indexPathForSelectedRow].row == 0 && [self.tableView indexPathForSelectedRow].section == 0) {
- ContainerDetailViewController *vc = [[ContainerDetailViewController alloc] initWithNibName:@"ContainerDetailViewController" bundle:nil];
- [self presentPrimaryViewController:vc];
- [vc release];
+ if (!account.containers || !deletedContainer || ![account.containers objectForKey:deletedContainer.name]) {
+ // No containers loaded yet, or no deleted container to remove cell.
+ refreshWhenAppeared = (refreshWhenAppeared || !account.containers);
+ self.deletedContainer = nil;
+ }
+ if (account.shared) {
+ refreshWhenAppeared = YES;
+ }
+ if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && ![self.tableView indexPathForSelectedRow]) {
+ [self setDetailViewControllerForContainer:nil];
+ }
+ [self showToolbarInfoMessage:[self accountUsageInfo]];
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+ if (deletedContainer) {
+ self.deletedContainer = [account.containers objectForKey:deletedContainer.name];
+ if (deletedContainer) {
+ NSUInteger deletedContainerIndex = [account.pithosSortedContainers indexOfObject:deletedContainer];
+ if (deletedContainerIndex != NSNotFound) {
+ [account.containers removeObjectForKey:deletedContainer.name];
+ [account persist];
+ [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:deletedContainerIndex inSection:0]] withRowAnimation:UITableViewRowAnimationLeft];
+ }
}
+ refreshWhenAppeared = YES;
+ self.deletedContainer = nil;
+ }
+ if (refreshWhenAppeared) {
+ refreshWhenAppeared = NO;
+ [self refreshButtonPressed:nil];
}
-
- [self refreshButtonPressed:nil];
}
#pragma mark - Memory management
- (void)dealloc {
- [tableView release];
[account release];
+ [tableView release];
[containerDetailViewController release];
+ [deletedContainer release];
[super dealloc];
}
#pragma mark - Internal
-- (void)createContainerWithName:(NSString *)containerName {
- __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Creating container..."
- andAddToView:self.view];
-
- Container *container = [[Container alloc] init];
- container.name = containerName;
+- (void)createContainersWithNames:(NSArray *)containersNames activityIndicatorView:(ActivityIndicatorView *)previousActivityIndicatorView {
+ if (!containersNames.count)
+ return;
+ __block ActivityIndicatorView *activityIndicatorView = (previousActivityIndicatorView ?
+ previousActivityIndicatorView :
+ [ActivityIndicatorView activityIndicatorViewWithText:((containersNames.count == 1) ? @"Creating container..." : @"Creating containers...")
+ andAddToView:self.view]);
+ __block Container *container = [[Container alloc] init];
+ container.name = [containersNames objectAtIndex:0];
[[self.account.manager createContainer:container]
success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- [self.tableView reloadData];
+ [container release];
+ if (containersNames.count > 1) {
+ [self createContainersWithNames:[containersNames subarrayWithRange:NSMakeRange(1, containersNames.count - 1)]
+ activityIndicatorView:activityIndicatorView];
+ } else {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView reloadData];
+ }
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [container release];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self alert:@"There was a problem creating your container." request:request];
}];
- [container release];
}
-#pragma mark - Properties
-
- (NSString *)accountUsageInfo {
if (self.account.sharingAccount)
return nil;
}
}
+- (ContainerDetailViewController *)containerDetailViewControllerForContainer:(Container *)container {
+ if (containerDetailViewController && (containerDetailViewController.container == container)) {
+ return containerDetailViewController;
+ }
+
+ ContainerDetailViewController *cdvc = [[[ContainerDetailViewController alloc] initWithNibName:@"ContainerDetailViewController" bundle:nil] autorelease];
+ if (container) {
+ cdvc.account = account;
+ cdvc.container = container;
+ cdvc.containersViewController = self;
+ if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && (self.navigationController.topViewController != self)) {
+ cdvc.rootFolderViewController = [self.navigationController.viewControllers
+ objectAtIndex:([self.navigationController.viewControllers indexOfObject:self] + 1)];
+ }
+ }
+ return cdvc;
+}
+
+#pragma mark - Actions
+
+- (void)setDetailViewControllerForContainer:(Container *)container {
+ if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
+ ContainerDetailViewController *cdvc = [self containerDetailViewControllerForContainer:container];
+ [self presentPrimaryViewController:cdvc];
+ self.containerDetailViewController = cdvc;
+ }
+}
+
#pragma mark - Button Handlers
- (void)addButtonPressed:(id)sender {
[vc release];
}
-- (IBAction)homeButtonPressed:(id)sender {
- [self.navigationController popToViewController:accountHomeViewController animated:YES];
-}
-
- (void)refreshButtonPressed:(id)sender {
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading..."
andAddToView:self.view];
[[self.account.manager getContainers] success:^(OpenStackRequest *request) {
- self.account.containers = [request containers];
- self.account.containerCount = [self.account.containers count];
- NSString *bytesUsedString = [request.responseHeaders objectForKey:@"X-Account-Bytes-Used"];
- self.account.bytesUsed = (bytesUsedString ?
- [NSNumber numberWithUnsignedLongLong:strtoull([bytesUsedString UTF8String], NULL, 0)] : nil);
- NSString *policyQuotaString = [request.responseHeaders objectForKey:@"X-Account-Policy-Quota"];
- self.account.policyQuota = (policyQuotaString ?
- [NSNumber numberWithUnsignedLongLong:strtoull([policyQuotaString UTF8String], NULL, 0)] : nil);
-
- [self.account persist];
- containersLoaded = YES;
-
- [activityIndicatorView removeFromSuperview];
- [self showToolbarInfoMessage:self.accountUsageInfo];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self showToolbarInfoMessage:[self accountUsageInfo]];
[self.tableView reloadData];
if (!account.shared && !account.sharingAccount) {
- if (![self.account.containers objectForKey:@"pithos"])
- [self createContainerWithName:@"pithos"];
- if (![self.account.containers objectForKey:@"trash"])
- [self createContainerWithName:@"trash"];
+ NSMutableArray *containersNames = [NSMutableArray array];
+ if (![self.account.containers objectForKey:@"pithos"]) {
+ [containersNames addObject:@"pithos"];
+ }
+ if (![self.account.containers objectForKey:@"trash"]) {
+ [containersNames addObject:@"trash"];
+ }
+ [self createContainersWithNames:containersNames activityIndicatorView:nil];
}
} failure:^(OpenStackRequest *request) {
- containersLoaded = NO;
- [activityIndicatorView removeFromSuperview];
- [self showToolbarInfoMessage:self.accountUsageInfo];
- if (request.responseStatusCode != 0) {
- [self alert:@"There was a problem loading containers." request:request addAccountSettingsButton:YES];
- }
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self alert:@"There was a problem loading containers." request:request addAccountSettingsButton:YES];
}];
}
+- (void)homeButtonPressed:(id)sender {
+ [self.navigationController popToViewController:accountHomeViewController animated:YES];
+}
+
#pragma mark - UITableViewDataSource
-- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- if ([self.account.containers count] == 0) {
- self.tableView.allowsSelection = NO;
- self.tableView.scrollEnabled = NO;
+- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
+ if (!self.account.containers.count) {
+ aTableView.allowsSelection = NO;
+ aTableView.scrollEnabled = NO;
} else {
- self.tableView.allowsSelection = YES;
- self.tableView.scrollEnabled = YES;
+ aTableView.allowsSelection = YES;
+ aTableView.scrollEnabled = YES;
}
- if (!containersLoaded && [self.account.containers count] == 0) {
+ if (!self.account.containers) {
return 0;
} else {
- return MAX(1, [account.containers count]);
+ return MAX(1, self.account.containers.count);
}
+
}
- (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
- if ([account.containers count] == 0) {
+ if (!self.account.containers.count) {
return aTableView.frame.size.height;
} else {
return aTableView.rowHeight;
}
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
- if (containersLoaded && [self.account.containers count] == 0) {
- NSString *noContainersTitle = self.account.shared ? @"No shared containers" : @"No containers";
- NSString *noContainersSubtitle = self.account.shared ? @"" : @"Tap the + button to create a new container";
- self.navigationItem.rightBarButtonItem.enabled = !self.account.shared;
-
- return [self tableView:tableView
+ if (self.account.containers && !self.account.containers.count) {
+ return [self tableView:aTableView
emptyCellWithImage:[UIImage imageNamed:@"empty-containers.png"]
- title:noContainersTitle
- subtitle:noContainersSubtitle];
- } else {
+ title:(self.account.shared ? @"No shared containers" : @"No containers")
+ subtitle:(self.account.shared ? @"" : @"Tap the + button to create a new container")];
+ } else {
static NSString *CellIdentifier = @"Cell";
-
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
}
+
Container *container = [self.account.pithosSortedContainers objectAtIndex:indexPath.row];
cell.textLabel.text = container.name;
- if ([container.name isEqualToString:@"pithos"])
+ if ([container.name isEqualToString:@"pithos"] ) {
cell.imageView.image = [UIImage imageNamed:@"PithosContainerIcon.png"];
- else if ([container.name isEqualToString:@"trash"])
+ } else if ([container.name isEqualToString:@"trash"]) {
cell.imageView.image = [UIImage imageNamed:@"TrashIcon.png"];
- else
+ } else {
cell.imageView.image = [UIImage imageNamed:@"ContainerIcon.png"];
-
- if (!self.account.sharingAccount)
+ }
+ if (!self.account.sharingAccount) {
cell.detailTextLabel.text = [container osxStyleHumanizedSize];
+ }
return cell;
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
- Container *container = nil;
- if ([account.containers count] > 0) {
- container = [self.account.pithosSortedContainers objectAtIndex:indexPath.row];
-
- FolderViewController *vc = (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) ?
+ if (account.containers.count) {
+ Container *container = [self.account.pithosSortedContainers objectAtIndex:indexPath.row];
+ FolderViewController *fvc = (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) ?
[[FolderViewController alloc] initWithNibName:@"FolderViewController-iPad" bundle:nil] :
[[FolderViewController alloc] initWithNibName:@"FolderViewController" bundle:nil]);
- vc.account = self.account;
- vc.container = container;
- vc.containersViewController = self;
- vc.selectedContainerIndexPath = indexPath;
- [self.navigationController pushViewController:vc animated:YES];
- vc.folder = container.rootFolder;
- [vc release];
- [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
- }
-
- if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
- ContainerDetailViewController *vc = [[ContainerDetailViewController alloc] initWithNibName:@"ContainerDetailViewController" bundle:nil];
- vc.account = self.account;
- vc.container = container;
- vc.containersViewController = self;
- vc.selectedContainerIndexPath = indexPath;
- [self presentPrimaryViewController:vc];
- self.containerDetailViewController = vc;
- [vc release];
+ fvc.account = self.account;
+ fvc.container = container;
+ fvc.containersViewController = self;
+ [self.navigationController pushViewController:fvc animated:YES];
+ fvc.folder = container.rootFolder;
+ [fvc release];
+ [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
+
+ [self setDetailViewControllerForContainer:container];
}
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
- Container *container = nil;
- if ([account.containers count] > 0) {
- container = [self.account.pithosSortedContainers objectAtIndex:indexPath.row];
+ if (account.containers.count) {
+ Container *container = [self.account.pithosSortedContainers objectAtIndex:indexPath.row];
+ ContainerDetailViewController *cdvc = [self containerDetailViewControllerForContainer:container];
+ [self.navigationController pushViewController:cdvc animated:YES];
}
- ContainerDetailViewController *vc = [[ContainerDetailViewController alloc] initWithNibName:@"ContainerDetailViewController" bundle:nil];
- vc.account = self.account;
- vc.container = container;
- vc.containersViewController = self;
- vc.selectedContainerIndexPath = indexPath;
- [self.navigationController pushViewController:vc animated:YES];
- [vc release];
}
@end
<int key="IBUISystemItemIdentifier">5</int>
</object>
<object class="IBUIBarButtonItem" id="468797627">
+ <object class="NSCustomResource" key="IBUIImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">HomeFolderIcon.png</string>
+ </object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<float key="IBUIWidth">33</float>
<reference key="IBUIToolbar" ref="1006532466"/>
- <int key="IBUISystemItemIdentifier">6</int>
</object>
</object>
</object>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
+ <string key="label">homeButtonPressed:</string>
+ <reference key="source" ref="468797627"/>
+ <reference key="destination" ref="372490531"/>
+ </object>
+ <int key="connectionID">31</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">refreshButtonPressed:</string>
<reference key="source" ref="760150208"/>
<reference key="destination" ref="372490531"/>
<reference key="dict.values" ref="0"/>
</object>
<nil key="sourceID"/>
- <int key="maxID">30</int>
+ <int key="maxID">31</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<string key="className">ContainersViewController</string>
<string key="superclassName">OpenStackViewController</string>
<object class="NSMutableDictionary" key="actions">
- <string key="NS.key.0">refreshButtonPressed:</string>
- <string key="NS.object.0">id</string>
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>homeButtonPressed:</string>
+ <string>refreshButtonPressed:</string>
+ </object>
+ <object class="NSArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ </object>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
- <string key="NS.key.0">refreshButtonPressed:</string>
- <object class="IBActionInfo" key="NS.object.0">
- <string key="name">refreshButtonPressed:</string>
- <string key="candidateClassName">id</string>
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>homeButtonPressed:</string>
+ <string>refreshButtonPressed:</string>
+ </object>
+ <object class="NSArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">homeButtonPressed:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">refreshButtonPressed:</string>
+ <string key="candidateClassName">id</string>
+ </object>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+ <string key="NS.key.0">HomeFolderIcon.png</string>
+ <string key="NS.object.0">{22, 22}</string>
+ </object>
<string key="IBCocoaTouchPluginVersion">1929</string>
</data>
</archive>
@interface EditAccountGroupsViewController : UITableViewController <UITextFieldDelegate> {
OpenStackAccount *account;
NSString *groupName;
- NSString *groupUsers;
NSMutableDictionary *groups;
NSMutableDictionary *metadata;
BOOL removeGroupEnabled;
+ NSArray *groupUUIDs;
+ UITextField *groupNameTextField;
+ NSMutableDictionary *groupUsersTextFields;
+ NSInteger numberOfGroupUsersRows;
}
@property (nonatomic, retain) OpenStackAccount *account;
@property (nonatomic, retain) NSString *groupName;
-@property (nonatomic, retain) NSString *groupUsers;
@property (nonatomic, retain) NSMutableDictionary *groups;
@property (nonatomic, retain) NSMutableDictionary *metadata;
-@property (nonatomic) BOOL removeGroupEnabled;
+
+@property (nonatomic, assign) BOOL removeGroupEnabled;
+@property (nonatomic, retain) NSArray *groupUUIDs;
+@property (nonatomic, retain) UITextField *groupNameTextField;
+@property (nonatomic, retain) NSMutableDictionary *groupUsersTextFields;
+@property (nonatomic, assign) NSInteger numberOfGroupUsersRows;
@end
#import "OpenStackRequest.h"
#import "APICallback.h"
-#define kGroupDetails 0
-#define kSave 1
-#define kRemove 2
+#define kGroupName 0
+#define kGroupUsers 1
+#define kSave 2
+#define kRemove 3
@implementation EditAccountGroupsViewController
-@synthesize account;
-@synthesize groupName, groupUsers, removeGroupEnabled;
-@synthesize groups,metadata;
+@synthesize account, groupName, groups, metadata;
+@synthesize removeGroupEnabled, groupUUIDs, groupNameTextField, groupUsersTextFields, numberOfGroupUsersRows;
#pragma mark - View lifecycle
return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ self.groupUUIDs = [groups objectForKey:groupName];
+ if (!self.groupUUIDs) {
+ self.groupUUIDs = [NSArray array];
+ numberOfGroupUsersRows = 2;
+ self.groupUsersTextFields = [NSMutableDictionary dictionaryWithCapacity:1];
+ } else {
+ numberOfGroupUsersRows = groupUUIDs.count + 1;
+ self.groupUsersTextFields = [NSMutableDictionary dictionaryWithCapacity:groupUUIDs.count];
+ }
+}
+
#pragma mark - Memory management
- (void)dealloc {
- [groupName release];
- [groupUsers release];
[account release];
- [metadata release];
+ [groupName release];
[groups release];
+ [metadata release];
+ [groupUUIDs release];
+ [groupNameTextField release];
+ [groupUsersTextFields release];
[super dealloc];
}
#pragma mark - Internal
-- (BOOL)userInputIsValid:(NSString *)input fieldName:(NSString *)fieldName {
- if (![input length] || ![[input stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length]) {
- [self alert:@"Invalid input" message:[NSString stringWithFormat:@"%@ field cannot be empty", fieldName]];
+- (BOOL)groupNameIsValid:(NSString *)input {
+ if (!input.length) {
+ [self alert:@"Invalid input" message:@"Group name cannot be empty."];
return NO;
- }
- NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@"= "];
- if ([input rangeOfCharacterFromSet:set].location != NSNotFound) {
- [self alert:@"Invalid input" message:[NSString stringWithFormat:@"%@ field cannot contain '=' or whitespace characters", fieldName]];
+ } else if ([input rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"=,;"]].location != NSNotFound) {
+ [self alert:@"Invalid input" message:@"Group name cannot contain '=', ',' or ';'."];
return NO;
}
return YES;
}
+- (NSMutableDictionary *)groupsInfoForGroups:(NSMutableDictionary *)groupsDictionary {
+ NSMutableDictionary *groupsInfo = [NSMutableDictionary dictionaryWithCapacity:groupsDictionary.count];
+ for (NSString *groupUser in groupsDictionary) {
+ [groupsInfo setObject:[[groupsDictionary objectForKey:groupUser] componentsJoinedByString:@","]
+ forKey:groupUser];
+ }
+ return groupsInfo;
+}
+
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
- if (removeGroupEnabled)
- return 3;
- else
- return 2;
+ return (removeGroupEnabled ? 4 : 3);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- if (section == kGroupDetails)
- return 2;
- else
- return 1;
+ if (section == kGroupUsers) {
+ return numberOfGroupUsersRows;
+ }
+ return 1;
+}
+
+- (UITextField *)textFieldForCell:(UITableViewCell *)cell {
+ CGRect rect = CGRectInset(cell.contentView.bounds, 10.0, 10.0);
+ UITextField *textField = [[[UITextField alloc] initWithFrame:rect] autorelease];
+ textField.frame = rect;
+ textField.clearButtonMode = UITextFieldViewModeWhileEditing;
+ textField.backgroundColor = [UIColor clearColor];
+ textField.opaque = YES;
+ textField.autocorrectionType = UITextAutocorrectionTypeNo;
+ textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
+ textField.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ return textField;
+}
+
+- (UITextField *)textFieldForCell:(UITableViewCell *)cell withIndexPath:(NSIndexPath *)indexPath {
+ if (indexPath.section == kGroupName) {
+ if (!groupNameTextField) {
+ self.groupNameTextField = [self textFieldForCell:cell];
+ groupNameTextField.placeholder = @"Group name";
+ groupNameTextField.text = groupName;
+ groupNameTextField.delegate = self;
+ }
+ return groupNameTextField;
+ } else if (indexPath.section == kGroupUsers) {
+ UITextField *groupUserTextField = [groupUsersTextFields objectForKey:indexPath];
+ if (!groupUserTextField) {
+ groupUserTextField = [self textFieldForCell:cell];
+ groupUserTextField.placeholder = @"User";
+ NSString *groupUser = (indexPath.row < groupUUIDs.count) ? [self.account displaynameForUUID:[groupUUIDs objectAtIndex:indexPath.row] safe:YES] : nil;
+ groupUserTextField.text = (groupUser && groupUser.length) ? groupUser : nil;
+ groupUserTextField.tag = indexPath.row;
+ [groupUsersTextFields setObject:groupUserTextField forKey:indexPath];
+ groupUserTextField.delegate = self;
+ }
+ return groupUserTextField;
+ }
+ return nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
- static NSString *CellIdentifier = @"Cell";
+ if ((indexPath.section == kGroupName) || ((indexPath.section == kGroupUsers) && (indexPath.row < numberOfGroupUsersRows - 1))) {
+ static NSString *CellIdentifier = @"TextFieldCell";
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+ if (cell == nil) {
+ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+ }
+ cell.textLabel.text = nil;
+ cell.accessoryType = UITableViewCellAccessoryNone;
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ for (UIView *view in cell.contentView.subviews) {
+ [view removeFromSuperview];
+ }
+ UITextField *textField = [self textFieldForCell:cell withIndexPath:indexPath];
+ textField.returnKeyType = (((indexPath.section == kGroupUsers) && (indexPath.row == numberOfGroupUsersRows - 2)) ?
+ UIReturnKeyDefault : UIReturnKeyNext);
+ [cell.contentView addSubview:textField];
+ return cell;
+ }
+ static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
-
- cell.textLabel.text = @"";
cell.accessoryType = UITableViewCellAccessoryNone;
- if (indexPath.section == kGroupDetails) {
- UITextField *textField = nil;
- for (id subView in cell.contentView.subviews) {
- if ([subView isKindOfClass:[UITextField class]]) {
- textField = (UITextField *)subView;
- }
- }
-
- if (textField == nil) {
- CGRect bounds = [cell.contentView bounds];
- CGRect rect = CGRectInset(bounds, 10.0, 10.0);
- textField = [[UITextField alloc] initWithFrame:rect];
- [textField setFrame:rect];
- }
-
- [textField setClearButtonMode:UITextFieldViewModeWhileEditing];
- [textField setBackgroundColor:[UIColor clearColor]];
- [textField setOpaque:YES];
- [textField setAutocorrectionType:UITextAutocorrectionTypeNo];
- [textField setAutocapitalizationType:UITextAutocapitalizationTypeNone];
- [textField setDelegate:self];
- textField.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- if (indexPath.row == 0) {
- textField.returnKeyType = UIReturnKeyNext;
- textField.placeholder = @"Group name";
- textField.text = self.groupName;
- textField.tag = 0;
- } else if (indexPath.row == 1) {
- textField.returnKeyType = UIReturnKeyDefault;
- textField.placeholder = @"Group users";
- textField.text = self.groupUsers;
- textField.tag = 1;
- }
- cell.selectionStyle = UITableViewCellSelectionStyleNone;
- [cell.contentView addSubview:textField];
+ if ((indexPath.section == kGroupUsers) && (indexPath.row == numberOfGroupUsersRows - 1)) {
+ cell.textLabel.text = @"Add User";
+ cell.selectionStyle = UITableViewCellSelectionStyleBlue;
} else if (indexPath.section == kSave) {
- cell.textLabel.text = @"Save";
+ cell.textLabel.text = @"Save Group";
+ cell.selectionStyle = UITableViewCellSelectionStyleBlue;
} else if (indexPath.section == kRemove) {
- cell.textLabel.text = @"Remove";
+ cell.textLabel.text = @"Remove Group";
+ cell.selectionStyle = UITableViewCellSelectionStyleBlue;
}
return cell;
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
- NSIndexPath *keyCellIndexPath;
- NSIndexPath *valueCellIndexPath;
- UITableViewCell *cell;
-
- if (indexPath.section != kGroupDetails)
+ if (((indexPath.section == kGroupUsers) && (indexPath.row == numberOfGroupUsersRows - 1)) ||
+ (indexPath.section == kSave) || (indexPath.section == kRemove))
[self.view endEditing:YES];
-
- if (indexPath.section == kSave) {
- keyCellIndexPath = [NSIndexPath indexPathForRow:0 inSection:kGroupDetails];
- cell = [self.tableView cellForRowAtIndexPath:keyCellIndexPath];
- UITextField *textField = [[cell.contentView subviews] objectAtIndex:0];
- NSString *newGroupName = textField.text;
-
- if (![self userInputIsValid:newGroupName fieldName:@"Group name"]) {
- [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
- return;
- }
- valueCellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kGroupDetails];
- cell = [self.tableView cellForRowAtIndexPath:valueCellIndexPath];
- textField = [[cell.contentView subviews] objectAtIndex:0];
- NSString *newGroupUsers = textField.text;
-
- if (![self userInputIsValid:newGroupUsers fieldName:@"Group users"]) {
+ if ((indexPath.section == kGroupUsers) && (indexPath.row == numberOfGroupUsersRows - 1)) {
+ numberOfGroupUsersRows++;
+ [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+ [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kGroupUsers] withRowAnimation:UITableViewRowAnimationNone];
+ } else if (indexPath.section == kSave) {
+ if (![self groupNameIsValid:groupNameTextField.text]) {
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
- return;
+ } else {
+ NSMutableArray *newGroupUsers = [NSMutableArray arrayWithCapacity:groupUsersTextFields.count];
+ for (UITextField *groupUserTextField in [groupUsersTextFields allValues]) {
+ if (groupUserTextField.text && groupUserTextField.text.length)
+ [newGroupUsers addObject:groupUserTextField.text];
+ }
+ if (!newGroupUsers.count) {
+ [self alert:@"Cannot save group" message:@"Please input at least one user."];
+ [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+ } else {
+ __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Checking users..."
+ andAddToView:self.view];
+ [[self.account.manager userCatalogForDisplaynames:newGroupUsers UUIDs:nil]
+ success:^(OpenStackRequest *request) {
+ NSDictionary *displaynameCatalog = [request displaynameCatalog];
+ NSMutableArray *newGroupUUIDs = [NSMutableArray arrayWithCapacity:newGroupUsers.count];
+ for (NSString *groupUser in newGroupUsers) {
+ NSString *UUID = [displaynameCatalog objectForKey:groupUser];
+ if (!UUID) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+ [self alert:@"Invalid user" message:[NSString stringWithFormat:@"User '%@' doesn't exist.", groupUser]];
+ return;
+ }
+ [newGroupUUIDs addObject:UUID];
+ }
+ // Apply changes.
+ [groups removeObjectForKey:groupName];
+ [groups setObject:newGroupUUIDs forKey:groupNameTextField.text];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Saving group..."
+ andAddToView:self.view];
+ [[self.account.manager writeAccountMetadata:[NSDictionary dictionaryWithObjectsAndKeys:
+ [self groupsInfoForGroups:groups], @"groups",
+ metadata, @"metadata",
+ nil]]
+ success:^(OpenStackRequest *request) {
+ self.groupName = groupNameTextField.text;
+ self.groupUUIDs = newGroupUUIDs;
+ numberOfGroupUsersRows = groupUUIDs.count + 1;
+ self.groupUsersTextFields = [NSMutableDictionary dictionaryWithCapacity:groupUUIDs.count];
+ removeGroupEnabled = YES;
+ self.navigationItem.title = @"Edit Group";
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+ [self.tableView reloadData];
+ }
+ failure:^(OpenStackRequest *request) {
+ [groups removeObjectForKey:groupNameTextField.text];
+ if (groupUUIDs.count) {
+ [groups setObject:groupUUIDs forKey:groupName];
+ }
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+ [self alert:@"There was a problem saving the group." request:request];
+ }];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+ [self alert:@"There was a problem checking the users." request:request];
+ }];
+ }
}
-
- [groups removeObjectForKey:groupName];
- [groups setObject:newGroupUsers forKey:newGroupName];
- NSDictionary *accountInfo = [NSDictionary dictionaryWithObjectsAndKeys:groups, @"groups",
- metadata, @"metadata",
- nil
- ];
- __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Saving group..."
- andAddToView:self.view];
- [[self.account.manager writeAccountMetadata:accountInfo]
- success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- self.groupName = newGroupName;
- self.groupUsers = newGroupUsers;
- self.removeGroupEnabled = YES;
- self.navigationItem.title = @"Edit Group";
- [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
- [self.tableView reloadData];
- }
- failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- [groups removeObjectForKey:newGroupName];
- [groups setObject:self.groupUsers forKey:self.groupName];
- [self.tableView reloadData];
- [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
- [self alert:@"There was a problem saving the group information." request:request];
- }];
} else if (indexPath.section == kRemove) {
[groups removeObjectForKey:groupName];
- NSDictionary *accountInfo = [NSDictionary dictionaryWithObjectsAndKeys:groups, @"groups",
- metadata, @"metadata",
- nil
- ];
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Removing group..."
andAddToView:self.view];
- [[self.account.manager writeAccountMetadata:accountInfo]
+ [[self.account.manager writeAccountMetadata:[NSDictionary dictionaryWithObjectsAndKeys:
+ [self groupsInfoForGroups:groups], @"groups",
+ metadata, @"metadata",
+ nil]]
success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
self.groupName = @"";
- self.groupUsers = @"";
- self.removeGroupEnabled = NO;
- [self.tableView reloadData];
+ self.groupUUIDs = [NSArray array];
+ numberOfGroupUsersRows = 2;
+ self.groupUsersTextFields = [NSMutableDictionary dictionaryWithCapacity:1];
+ removeGroupEnabled = NO;
+ self.navigationItem.title = @"Add Group";
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+ [self.tableView reloadData];
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- [groups setObject:groupUsers forKey:groupName];
+ if (groupUUIDs.count) {
+ [groups setObject:groupUUIDs forKey:groupName];
+ }
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
- [self alert:@"There was a problem removing the group information." request:request];
+ [self alert:@"There was a problem removing the group." request:request];
}];
}
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
- if ([textField returnKeyType] == UIReturnKeyNext) {
- [self userInputIsValid:textField.text fieldName:@"Group name"];
-
- NSInteger nextTag = [textField tag] + 1;
- UIView *nextTextField = [self.tableView viewWithTag:nextTag];
- [nextTextField becomeFirstResponder];
- } else {
- [self userInputIsValid:textField.text fieldName:@"Group users"];
+ if (textField == groupNameTextField) {
+ if (![self groupNameIsValid:textField.text]) {
+ return NO;
+ }
+ [[groupUsersTextFields objectForKey:[NSIndexPath indexPathForRow:0 inSection:kGroupUsers]] becomeFirstResponder];
+ } else if (textField.tag == numberOfGroupUsersRows - 2) {
[textField resignFirstResponder];
- }
-
+ } else {
+ [[groupUsersTextFields objectForKey:[NSIndexPath indexPathForRow:(textField.tag + 1) inSection:kGroupUsers]] becomeFirstResponder];
+ }
return YES;
}
newFolder.sharing = folderViewController.folder.sharing;
newFolder.metadata = object.metadata;
[folderViewController.folder addFolder:newFolder];
- [activityIndicatorView removeFromSuperview];
+ // XXX increase container.count if in rootFolder?
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[newFolder release];
self.metadataKey = userInputMetaKey;
self.metadataValue = userInputMetaValue;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
[self.folderViewController refreshButtonPressed:nil];
} else {
- self.folderViewController.needsRefreshing = YES;
self.folderViewController.refreshWhenAppeared = YES;
}
}
failure:^(OpenStackRequest *request) {
[object.metadata removeObjectForKey:userInputMetaKey];
[object.metadata setObject:metadataValue forKey:metadataKey];
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
[self.tableView reloadData];
[self alert:@"There was a problem saving the metadata." request:request];
self.metadataValue = userInputMetaValue;
removeMetadataEnabled = YES;
[self.tableView reloadData];
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
if (objectIsContainer) {
container.metadata = object.metadata;
failure:^(OpenStackRequest *request) {
[object.metadata removeObjectForKey:userInputMetaKey];
[object.metadata setObject:metadataValue forKey:metadataKey];
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
[self alert:@"There was a problem saving the metadata." request:request];
}];
[object.metadata removeObjectForKey:metadataKey];
[[self.account.manager writeObjectMetadata:container object:object]
success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
self.metadataKey = @"";
self.metadataValue = @"";
}
failure:^(OpenStackRequest *request) {
[object.metadata setObject:metadataValue forKey:metadataKey];
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
[self alert:@"There was a problem saving the metadata." request:request];
// interpreted as representing official policies, either expressed
// or implied, of GRNET S.A.
-#import <UIKit/UIKit.h>
-
-@class FolderViewController, OpenStackRequest, OpenStackAccount, Container, StorageObject;
+@class FolderViewController, OpenStackAccount, Container, StorageObject;
@interface EditPermissionsViewController : UITableViewController <UITextFieldDelegate, UIAlertViewDelegate> {
- NSString *user;
- BOOL readPermissionSelected;
- BOOL writePermissionSelected;
- NSMutableDictionary *permissions;
-
OpenStackAccount *account;
Container *container;
StorageObject *object;
-
- NSString *oldPermissionsString;
- BOOL newPermissionsEntry;
+
+ NSString *permissionUser;
+ BOOL readPermissionSelected;
+ BOOL writePermissionSelected;
+ NSMutableDictionary *permissions;
+ NSString *currentUUID;
+ NSString *oldSharing;
BOOL removePermissionsEnabled;
BOOL objectIsFolder;
+
+ UITextField *userTextField;
+ UITextField *groupTextField;
+ UITableViewCell *readPermissionsCell;
+ UITableViewCell *writePermissionsCell;
}
@property (nonatomic, retain) OpenStackAccount *account;
@property (nonatomic, retain) Container *container;
@property (nonatomic, retain) StorageObject *object;
+@property (nonatomic, assign) FolderViewController *folderViewController;
-@property (nonatomic, retain) NSString *user;
-@property (nonatomic) BOOL readPermissionSelected;
-@property (nonatomic) BOOL writePermissionSelected;
-@property (nonatomic, assign) BOOL newPermissionsEntry;
-@property (nonatomic, assign) BOOL removePermissionsEnabled;
+@property (nonatomic, retain) NSString *permissionUser;
+@property (nonatomic, assign) BOOL readPermissionSelected;
+@property (nonatomic, assign) BOOL writePermissionSelected;
@property (nonatomic, retain) NSMutableDictionary *permissions;
-@property (nonatomic, retain) NSString *oldPermissionsString;
-@property (nonatomic, assign) FolderViewController *folderViewController;
+@property (nonatomic, retain) NSString *currentUUID;
+@property (nonatomic, retain) NSString *oldSharing;
+@property (nonatomic, assign) BOOL removePermissionsEnabled;
@property (nonatomic, assign) BOOL objectIsFolder;
+@property (nonatomic, retain) UITextField *userTextField;
+@property (nonatomic, retain) UITextField *groupTextField;
+@property (nonatomic, retain) UITableViewCell *readPermissionsCell;
+@property (nonatomic, retain) UITableViewCell *writePermissionsCell;
+
@end
@implementation EditPermissionsViewController
-@synthesize account, container, object;
-@synthesize user, readPermissionSelected, writePermissionSelected, permissions;
-@synthesize oldPermissionsString;
-@synthesize newPermissionsEntry, removePermissionsEnabled;
-@synthesize folderViewController, objectIsFolder;
+@synthesize account, container, object, folderViewController;
+@synthesize permissionUser, readPermissionSelected, writePermissionSelected, permissions;
+@synthesize currentUUID;
+@synthesize oldSharing, removePermissionsEnabled, objectIsFolder;
+@synthesize userTextField, groupTextField, readPermissionsCell, writePermissionsCell;
+
+#pragma mark - View lifecycle
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
+ return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
+}
+
+#pragma mark - Memory management
- (void)dealloc {
[account release];
[container release];
[object release];
- [user release];
+ [permissionUser release];
[permissions release];
- [oldPermissionsString release];
+ [oldSharing release];
+ [currentUUID release];
+ [userTextField release];
+ [groupTextField release];
+ [readPermissionsCell release];
+ [writePermissionsCell release];
[super dealloc];
}
-#pragma mark - View lifecycle
-
-- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
- return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
-}
-
#pragma mark - Internal
-- (NSString *)buildPermissionsString {
- NSString *readPermissionsString = @"";
- NSString *writePermissionsString = @"";
- for (NSString *aUser in [permissions allKeys]) {
- if ([[permissions objectForKey:aUser] isEqual:@"read"]) {
- if (!readPermissionsString.length) {
- readPermissionsString = [NSString stringWithFormat:@"read=%@", aUser];
- } else {
- readPermissionsString = [NSString stringWithFormat:@"%@,%@", readPermissionsString ,aUser];
- }
- } else if ([[permissions objectForKey:aUser] isEqual:@"write"]) {
- if (!writePermissionsString.length) {
- writePermissionsString = [NSString stringWithFormat:@"write=%@", aUser];
- } else {
- writePermissionsString = [NSString stringWithFormat:@"%@,%@", writePermissionsString, aUser];
- }
- }
+- (BOOL)userIsValid:(NSString *)input {
+ if (!input.length) {
+ [self alert:@"Invalid input" message:@"User cannot be empty."];
+ return NO;
}
- if (!writePermissionsString.length)
- return readPermissionsString;
- if (!readPermissionsString.length)
- return writePermissionsString;
- return [NSString stringWithFormat:@"%@;%@", readPermissionsString, writePermissionsString];
+ return YES;
}
-- (BOOL)userInputIsValid:(NSString *)input {
- if (!input.length || ![input stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length) {
- [self alert:@"Invalid input" message:@"User name cannot be empty"];
- return NO;
- }
- NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@"=,;"];
- if ([input rangeOfCharacterFromSet:set].location != NSNotFound) {
- [self alert:@"Invalid input" message:@"User name cannot contain '=', ',' or ';'"];
+- (BOOL)groupIsValid:(NSString *)input {
+ if (input && ([input rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"=,;"]].location != NSNotFound)) {
+ [self alert:@"Invalid input" message:@"Group cannot contain '=', ',' or ';'."];
return NO;
}
return YES;
}
-- (void)createNewFolder {
+- (void)applyPermissions {
+ [permissions removeObjectForKey:permissionUser];
+ NSString *newPermissionUser = ((groupTextField.text && groupTextField.text.length) ?
+ [NSString stringWithFormat:@"%@:%@", currentUUID, groupTextField.text] :
+ currentUUID);
+ [permissions setObject:(writePermissionSelected ? @"write" : @"read") forKey:newPermissionUser];
+ self.oldSharing = object.sharing;
+ [object setPermissions:permissions];
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Applying permissions..."
andAddToView:self.view];
+ [[self.account.manager writeObjectMetadata:container object:object]
+ success:^(OpenStackRequest *request) {
+ self.permissionUser = newPermissionUser;
+
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
+ if (!removePermissionsEnabled) {
+ removePermissionsEnabled = YES;
+ self.navigationItem.title = @"Edit Permission";
+ [self.tableView reloadData];
+ }
+
+ if (objectIsFolder || (account.shared && ![oldSharing isEqualToString:object.sharing])) {
+ if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
+ [self.folderViewController refreshButtonPressed:nil];
+ } else {
+ self.folderViewController.refreshWhenAppeared = YES;
+ }
+ }
+ }
+ failure:^(OpenStackRequest *request) {
+ object.sharing = self.oldSharing;
+ self.permissions = object.permissions;
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
+ [self alert:@"There was a problem applying the permissions." request:request];
+ }];
+}
+
+- (void)createNewFolder {
+ __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Creating folder..."
+ andAddToView:self.view];
+ object.contentType = @"application/directory";
+ object.data = [NSData data];
[[self.account.manager writeObject:self.container object:object downloadProgressDelegate:nil]
success:^(OpenStackRequest *request) {
Folder *newFolder = [[Folder alloc] init];
newFolder.parent = folderViewController.folder;
newFolder.sharing = folderViewController.folder.sharing;
[folderViewController.folder addFolder:newFolder];
- [activityIndicatorView removeFromSuperview];
+ // XXX increase container.count if in rootFolder?
[newFolder release];
- [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
- removePermissionsEnabled = YES;
- [self.tableView reloadData];
+
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
[self.folderViewController refreshButtonPressed:nil];
} else {
- self.folderViewController.needsRefreshing = YES;
self.folderViewController.refreshWhenAppeared = YES;
}
+ [self applyPermissions];
}
failure:^(OpenStackRequest *request) {
- object.sharing = self.oldPermissionsString;
- readPermissionSelected = !readPermissionSelected;
- writePermissionSelected = !writePermissionSelected;
-
- if (readPermissionSelected)
- [self.permissions setObject:@"read" forKey:user];
- else if (writePermissionSelected)
- [self.permissions setObject:@"write" forKey:user];
-
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
- [self.tableView reloadData];
- [self alert:@"There was a problem applying the permissions." request:request];
+ [self alert:@"There was a problem creating the folder." request:request];
}];
}
-#pragma mark - Table view data source
+
+#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return (removePermissionsEnabled ? 4 : 3);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- return ((section == kPermissions) ? 2 : 1);
+ if ((section == kUser) || (section == kPermissions)) {
+ return 2;
+ }
+ return 1;
+}
+
+- (UITextField *)textFieldForCell:(UITableViewCell *)cell {
+ CGRect rect = CGRectInset(cell.contentView.bounds, 10.0, 10.0);
+ UITextField *textField = [[[UITextField alloc] initWithFrame:rect] autorelease];
+ textField.frame = rect;
+ textField.clearButtonMode = UITextFieldViewModeWhileEditing;
+ textField.backgroundColor = [UIColor clearColor];
+ textField.opaque = YES;
+ textField.autocorrectionType = UITextAutocorrectionTypeNo;
+ textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
+ textField.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ return textField;
+}
+
+- (UITextField *)textFieldForCell:(UITableViewCell *)cell withIndexPath:(NSIndexPath *)indexPath {
+ if (indexPath.row == 0) {
+ if (!userTextField) {
+ self.userTextField = [self textFieldForCell:cell];
+ userTextField.returnKeyType = UIReturnKeyNext;
+ userTextField.placeholder = @"User";
+ NSRange rangeOfColumn = [permissionUser rangeOfString:@":"];
+ userTextField.text = [self.account displaynameForUUID:((rangeOfColumn.location == NSNotFound) ? permissionUser : [permissionUser substringToIndex:rangeOfColumn.location])
+ safe:YES];
+ userTextField.delegate = self;
+ }
+ return userTextField;
+ } else if (indexPath.row == 1) {
+ if (!groupTextField) {
+ self.groupTextField = [self textFieldForCell:cell];
+ groupTextField.returnKeyType = UIReturnKeyDefault;
+ groupTextField.placeholder = @"Group (optional)";
+ NSRange rangeOfColumn = [permissionUser rangeOfString:@":"];
+ groupTextField.text = ((rangeOfColumn.location == NSNotFound) || (rangeOfColumn.location == permissionUser.length - 1)) ? nil : [permissionUser substringFromIndex:(rangeOfColumn.location + 1)];
+ groupTextField.delegate = self;
+ }
+ return groupTextField;
+ }
+ return nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
- static NSString *CellIdentifier = @"Cell";
+ if (indexPath.section == kUser) {
+ static NSString *CellIdentifier = @"TextFieldCell";
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+ if (cell == nil) {
+ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+ }
+ cell.textLabel.text = nil;
+ cell.accessoryType = UITableViewCellAccessoryNone;
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ for (UIView *view in cell.contentView.subviews) {
+ [view removeFromSuperview];
+ }
+ [cell.contentView addSubview:[self textFieldForCell:cell withIndexPath:indexPath]];
+ return cell;
+ }
+ static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
- if (indexPath.section == kUser) {
- UITextField *textField = nil;
- for (id subView in cell.contentView.subviews) {
- if ([subView isKindOfClass:[UITextField class]]) {
- textField = (UITextField *)subView;
- }
- }
-
- if (textField == nil) {
- CGRect bounds = [cell.contentView bounds];
- CGRect rect = CGRectInset(bounds, 10.0, 10.0);
- textField = [[UITextField alloc] initWithFrame:rect];
- [textField setFrame:rect];
- }
-
- [textField setClearButtonMode:UITextFieldViewModeWhileEditing];
- [textField setBackgroundColor:[UIColor clearColor]];
- [textField setOpaque:YES];
- [textField setAutocorrectionType:UITextAutocorrectionTypeNo];
- [textField setAutocapitalizationType:UITextAutocapitalizationTypeNone];
- [textField setDelegate:self];
- textField.placeholder = @"User or User:Group";
- textField.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- textField.text = self.user;
-
- cell.selectionStyle = UITableViewCellSelectionStyleNone;
- [cell.contentView addSubview:textField];
- cell.selectionStyle = UITableViewCellSelectionStyleNone;
- } else if (indexPath.section == kPermissions) {
- cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ if (indexPath.section == kPermissions) {
if (indexPath.row == 0) {
cell.textLabel.text = @"Read Only";
- if (readPermissionSelected)
- cell.accessoryType = UITableViewCellAccessoryCheckmark;
- else
- cell.accessoryType = UITableViewCellAccessoryNone;
+ cell.accessoryType = (readPermissionSelected ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone);
+ self.readPermissionsCell = cell;
} else {
cell.textLabel.text = @"Read/Write";
- if (writePermissionSelected)
- cell.accessoryType = UITableViewCellAccessoryCheckmark;
- else
- cell.accessoryType = UITableViewCellAccessoryNone;
+ cell.accessoryType = (writePermissionSelected ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone);
+ self.writePermissionsCell = cell;
}
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
} else if (indexPath.section == kSavePermissions) {
cell.textLabel.text = @"Save";
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;
}
-#pragma mark - Table view delegate
+#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section != kUser)
[self.view endEditing:YES];
- NSString *newUserName;
- NSIndexPath *userCellIndexPath = [NSIndexPath indexPathForRow:0 inSection:kUser];
- UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:userCellIndexPath];
- UITextField *textField = [[cell.contentView subviews] objectAtIndex:0];
- newUserName = textField.text;
- if (![self userInputIsValid:newUserName]) {
- [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
- } else if (indexPath.section == kPermissions) {
- UITableViewCell *readPermissionsCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:kPermissions]];
- UITableViewCell *writePermissionsCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:kPermissions]];
-
- if (indexPath.row == 0) {
- if (!readPermissionSelected) {
- readPermissionSelected = YES;
- if (writePermissionSelected) {
- writePermissionSelected = FALSE;
- writePermissionsCell.accessoryType = UITableViewCellAccessoryNone;
- }
- readPermissionsCell.accessoryType = UITableViewCellAccessoryCheckmark;
+ if (indexPath.section == kPermissions) {
+ if ((indexPath.row == 0) && !readPermissionSelected) {
+ readPermissionSelected = YES;
+ if (writePermissionSelected) {
+ writePermissionSelected = FALSE;
+ writePermissionsCell.accessoryType = UITableViewCellAccessoryNone;
}
- } else if (indexPath.row == 1) {
- if (!writePermissionSelected) {
- writePermissionSelected = YES;
- if (readPermissionSelected) {
- readPermissionSelected = FALSE;
- readPermissionsCell.accessoryType = UITableViewCellAccessoryNone;
- }
- writePermissionsCell.accessoryType = UITableViewCellAccessoryCheckmark;
+ readPermissionsCell.accessoryType = UITableViewCellAccessoryCheckmark;
+ } else if ((indexPath.row == 1) && !writePermissionSelected) {
+ writePermissionSelected = YES;
+ if (readPermissionSelected) {
+ readPermissionSelected = FALSE;
+ readPermissionsCell.accessoryType = UITableViewCellAccessoryNone;
}
+ writePermissionsCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
} else if (indexPath.section == kSavePermissions) {
- if (!readPermissionSelected && !writePermissionSelected) {
- [self alert:@"Cannot save permissions" message:@"Please select permissions type"];
+ if (![self userIsValid:userTextField.text] || ![self groupIsValid:groupTextField.text]) {
+ [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+ } else if (!readPermissionSelected && !writePermissionSelected) {
+ [self alert:@"Cannot save permissions" message:@"Please select a permission type."];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
} else {
- [self.permissions removeObjectForKey:self.user];
- if (readPermissionSelected)
- [self.permissions setObject:@"read" forKey:newUserName];
- else if (writePermissionSelected)
- [self.permissions setObject:@"write" forKey:newUserName];
-
- self.user = newUserName;
- self.oldPermissionsString = object.sharing;
- object.sharing = [self buildPermissionsString];
-
- if (objectIsFolder && ![PithosUtilities isContentTypeDirectory:object.contentType]) {
- if ((([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) &&
- [folderViewController.parentFolderViewController.folder.objects objectForKey:object.name]) ||
- (([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad) &&
- [folderViewController.folder.objects objectForKey:object.name])) {
- NSString *alertMessage = [NSString stringWithFormat:@"In order to apply the changes in '%@', the object at the same path must be replaced. Continue?",object.name];
- NSString *alertTitle = @"Apply changes";
- UIAlertView *alert = [[UIAlertView alloc] initWithTitle:alertTitle
- message:alertMessage
- delegate:self
- cancelButtonTitle:@"Cancel"
- otherButtonTitles:@"OK", nil];
- [alert show];
- [alert release];
- } else {
- object.name = object.fullPath;
- object.contentType = @"application/directory";
- object.data = [NSData data];
- [self createNewFolder];
- }
- } else {
- __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Applying permissions..."
- andAddToView:self.view];
- [[self.account.manager writeObjectMetadata:container object:object]
- success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
- removePermissionsEnabled = YES;
- [self.tableView reloadData];
- if (objectIsFolder || (account.shared && ![oldPermissionsString isEqualToString:object.sharing])) {
- if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
- [self.folderViewController refreshButtonPressed:nil];
- }
- else {
- self.folderViewController.needsRefreshing = YES;
+ __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Checking user..."
+ andAddToView:self.view];
+ [[self.account.manager userCatalogForDisplaynames:[NSArray arrayWithObject:userTextField.text] UUIDs:nil]
+ success:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ self.currentUUID = [[request displaynameCatalog] objectForKey:userTextField.text];
+ if (currentUUID) {
+ if (objectIsFolder && ![PithosUtilities isContentTypeDirectory:object.contentType]) {
+ if ((([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) &&
+ [folderViewController.parentFolderViewController.folder.objects objectForKey:object.name]) ||
+ (([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad) &&
+ [folderViewController.folder.objects objectForKey:object.name])) {
+ UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:@"Apply changes"
+ message:[NSString stringWithFormat:@"In order to apply the changes to folder '%@', the file at the same path must be replaced. Continue?", object.name]
+ delegate:self
+ cancelButtonTitle:@"Cancel"
+ otherButtonTitles:@"OK", nil] autorelease];
+ [alert show];
+ } else {
+ [self createNewFolder];
}
- }
- }
- failure:^(OpenStackRequest *request) {
- object.sharing = self.oldPermissionsString;
- readPermissionSelected = !readPermissionSelected;
- writePermissionSelected = !writePermissionSelected;
-
- if (readPermissionSelected)
- [self.permissions setObject:@"read" forKey:user];
- else if (writePermissionSelected)
- [self.permissions setObject:@"write" forKey:user];
-
- [activityIndicatorView removeFromSuperview];
+ } else {
+ [self applyPermissions];
+ }
+ } else {
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
- [self.tableView reloadData];
- [self alert:@"There was a problem applying the permissions." request:request];
- }];
- }
+ [self alert:@"Invalid user" message:[NSString stringWithFormat:@"User '%@' doesn't exist.", userTextField.text]];
+ }
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+ [self alert:@"There was a problem checking the user." request:request];
+ }];
}
} else if (indexPath.section == kRemovePermissions) {
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Removing permissions..."
andAddToView:self.view];
- [permissions removeObjectForKey:user];
- self.oldPermissionsString = object.sharing;
- object.sharing = [self buildPermissionsString];
+ [permissions removeObjectForKey:permissionUser];
+ self.oldSharing = object.sharing;
+ [object setPermissions:permissions];
[[self.account.manager writeObjectMetadata:container object:object]
success:^(OpenStackRequest *request) {
- self.user = @"";
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+ self.permissionUser = @"";
self.readPermissionSelected = FALSE;
self.writePermissionSelected = FALSE;
removePermissionsEnabled = NO;
+ self.navigationItem.title = @"Add Permission";
[self.tableView reloadData];
- [activityIndicatorView removeFromSuperview];
- [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
- if (objectIsFolder || (account.shared && ![oldPermissionsString isEqualToString:object.sharing])) {
- self.folderViewController.needsRefreshing = YES;
- if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
+
+ if (objectIsFolder || (account.shared && ![oldSharing isEqualToString:object.sharing])) {
+ if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
[self.folderViewController refreshButtonPressed:nil];
- else
+ } else {
self.folderViewController.refreshWhenAppeared = YES;
+ }
}
}
failure:^(OpenStackRequest *request) {
- object.sharing = self.oldPermissionsString;
- if (readPermissionSelected)
- [self.permissions setObject:@"read" forKey:user];
- else if (writePermissionSelected)
- [self.permissions setObject:@"write" forKey:user];
-
- [activityIndicatorView removeFromSuperview];
+ object.sharing = self.oldSharing;
+ self.permissions = object.permissions;
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
- [self.tableView reloadData];
[self alert:@"There was a problem removing the permissions." request:request];
}];
}
}
-#pragma mark - Textfield delegate
+#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
- [self userInputIsValid:textField.text];
- [textField resignFirstResponder];
+ if (textField == userTextField) {
+ if (![self userIsValid:textField.text]) {
+ return NO;
+ }
+ [groupTextField becomeFirstResponder];
+ } else if (textField == groupTextField) {
+ if (![self groupIsValid:textField.text]) {
+ return NO;
+ }
+ [groupTextField resignFirstResponder];
+ }
return YES;
}
-#pragma mark - Alertview delegate
+#pragma mark - UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1) {
- object.name = object.fullPath;
- object.contentType = @"application/directory";
- object.data = [NSData data];
[self createNewFolder];
} else {
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
[self.tableView reloadData];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
[self.containerDetailViewController.containersViewController refreshButtonPressed:nil];
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
failure:^(OpenStackRequest *request) {
[self configVersioningVariables];
[self.tableView reloadData];
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
[self.tableView reloadData];
[self alert:@"There was a problem applying the policy." request:request];
// "Details" button
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
self.logEntryModalViewController.modalPresentationStyle = UIModalPresentationFormSheet;
- }
+ }
+ // XXX If the viewController is removed from the windows hierarchy, for a late request e.g.,
+ // this will do nothing and result in a warning.
[self.viewController presentModalViewController:self.logEntryModalViewController animated:YES];
} else if (buttonIndex == 2) {
// "Account Settings" button
- (id)initWithObject:(StorageObject *)anObject {
if ((self = [super init])) {
self.folderObject = anObject;
- self.objects = [NSMutableDictionary dictionary];
- self.folders = [NSMutableDictionary dictionary];
+ objects = [[NSMutableDictionary alloc] init];
+ folders = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)setObjects:(NSMutableDictionary *)objs {
if (![objects isEqualToDictionary:objs]) {
- [objects release];
NSMutableDictionary *folderedFiles = [NSMutableDictionary dictionary];
NSMutableDictionary *files = [NSMutableDictionary dictionary];
self.folders = [NSMutableDictionary dictionary];
// take the foldered files and recursively build the rest of the folder structure
for (NSString *folderName in [folderedFiles allKeys]) {
- Folder *folder;
- StorageObject *object = [objs objectForKey:folderName];
- if (object && [PithosUtilities isContentTypeDirectory:object.contentType]) {
- folder = [Folder folderWithObject:object];
- } else {
+ Folder *folder = [folders objectForKey:folderName];
+ if (!folder) {
folder = [Folder folder];
folder.name = folderName;
+ [self.folders setObject:folder forKey:folderName];
folder.metadata = [NSMutableDictionary dictionary];
}
folder.parent = self;
folder.objects = [folderedFiles objectForKey:folderName];
- [self.folders setObject:folder forKey:folder.name];
}
+ [objects release];
objects = [files retain];
[sortedContentsByName release];
sortedContentsByName = nil;
@property (nonatomic, retain) Container *container;
@property (nonatomic, retain) Folder *folder;
@property (nonatomic, retain) FolderViewController *folderViewController;
+@property (nonatomic, retain) NSMutableDictionary *permissions;
- (void)reloadMetadataSection;
@implementation FolderDetailViewController
-@synthesize account, container, folder, folderViewController;
+@synthesize account, container, folder, folderViewController, permissions;
-#pragma mark -
-#pragma mark Memory management
+#pragma mark - View lifecycle
-- (void)dealloc {
- [object release];
- [account release];
- [container release];
- [folderViewController release];
- [folder release];
- [permissions release];
- [super dealloc];
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
+ return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
-#pragma mark - View lifecycle
-
- (void)viewDidLoad {
[super viewDidLoad];
- permissions = [[NSMutableDictionary alloc] init];
- if (folder.sharing.length > 0) {
- NSArray *sharingArray = [folder.sharing componentsSeparatedByString:@";"];
- for (NSString *typeSpecificPermissions in sharingArray) {
- NSArray *array=[typeSpecificPermissions componentsSeparatedByString:@"="];
- NSString *permissionsType = [array objectAtIndex:0];
- if ([permissionsType hasPrefix:@" "])
- permissionsType = [permissionsType substringFromIndex:1];
-
- NSArray *users = [[array objectAtIndex:1] componentsSeparatedByString:@","];
- for (NSString *user in users) {
- [permissions setObject:permissionsType forKey:user];
- }
- }
- }
-
+ self.permissions = [folder.folderObject permissions];
folderIsReadOnly = NO;
- if (account.sharingAccount) {
- if ([permissions count] > 0) {
- folderIsReadOnly = [[permissions objectForKey:[account username]] isEqualToString:@"read"];
- }
+ if (account.sharingAccount && permissions.count) {
+ folderIsReadOnly = ![[permissions objectForKey:account.username] isEqualToString:@"write"];
}
object = [[StorageObject alloc] init];
[super viewDidAppear:animated];
if (folder.metadata == nil) {
[self reloadMetadataSection];
+ } else {
+ [self updatePermissionsUserCatalog];
}
}
folder.sharing = object.sharing;
}
-- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
- return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
+#pragma mark - Memory management
+
+- (void)dealloc {
+ [object release];
+ [account release];
+ [container release];
+ [folderViewController release];
+ [folder release];
+ [permissions release];
+ [super dealloc];
}
-#pragma mark - Table view data source
+#pragma mark - Internal
+
+- (void)updatePermissionsUserCatalog {
+ NSMutableArray *UUIDs = [NSMutableArray arrayWithCapacity:permissions.count];
+ for (NSString *user in [permissions allKeys]) {
+ NSRange rangeOfColumn = [user rangeOfString:@":"];
+ NSString *UUID = (rangeOfColumn.location == NSNotFound) ? user : [user substringToIndex:rangeOfColumn.location];
+ if (![UUID isEqualToString:@"*"]) {
+ [UUIDs addObject:UUID];
+ }
+ }
+ if (UUIDs.count) {
+ __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading permissions..."
+ andAddToView:self.view];
+ [[self.account.manager userCatalogForDisplaynames:nil UUIDs:UUIDs]
+ success:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kPermissions] withRowAnimation:UITableViewRowAnimationNone];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ }];
+ }
+}
+#pragma mark - Actions
+
+- (void)reloadMetadataSection {
+ __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading metadata..."
+ andAddToView:self.view];
+ [[self.account.manager getObjectInfo:container object:object version:nil]
+ success:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ folder.metadata = [NSMutableDictionary dictionary];
+ for (NSString *header in request.responseHeaders) {
+ NSString *metadataKey;
+ NSString *metadataValue;
+ if ([header rangeOfString:@"X-Object-Meta-"].location != NSNotFound) {
+ metadataKey = [NSString decodeFromPercentEscape:[header substringFromIndex:14]];
+ metadataValue = [NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:header]];
+ [folder.metadata setObject:metadataValue forKey:metadataKey];
+ }
+ }
+ object.metadata = folder.metadata;
+ [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kMetadata] withRowAnimation:UITableViewRowAnimationFade];
+ [self updatePermissionsUserCatalog];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self alert:@"There was a problem retrieving the object's metadata." request:request];
+ }];
+}
+
+#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
return (folder.lastModifiedString ? 3 : 2);
} else if (section == kPermissions) {
if (account.sharingAccount) {
- return [permissions count];
+ return permissions.count;
} else {
- return 1 + [permissions count];
+ return (permissions.count + 1);
}
} else if (section == kMetadata) {
if (folderIsReadOnly) {
return [folder.metadata count];
} else {
- return 1 + [folder.metadata count];
+ return ([folder.metadata count] + 1);
}
}
return 0;
return MAX(tableView.rowHeight, result);
}
-
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
cell.accessoryType = UITableViewCellAccessoryNone;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.userInteractionEnabled = NO;
- }
- else {
+ } else {
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.accessoryView = nil;
- if (indexPath.row == [permissions count]) {
+ if (indexPath.row == permissions.count) {
cell.textLabel.text = @"Share";
cell.detailTextLabel.text = @"";
- }
- else {
- NSString *user = [[permissions allKeys] objectAtIndex:indexPath.row];
- cell.textLabel.text = user;
- cell.detailTextLabel.text = [permissions objectForKey:user];
+ } else {
+ NSString *user = [[[permissions allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:indexPath.row];
+ NSRange rangeOfColumn = [user rangeOfString:@":"];
+ NSString *UUID = (rangeOfColumn.location == NSNotFound) ? user : [user substringToIndex:rangeOfColumn.location];
+ NSString *group = ((rangeOfColumn.location == NSNotFound) || (rangeOfColumn.location == user.length - 1)) ? nil : [user substringFromIndex:(rangeOfColumn.location + 1)];
+ NSMutableString *displayname = [NSMutableString stringWithString:[account displaynameForUUID:UUID safe:YES]];
+ if (group) {
+ [displayname appendFormat:@":%@", group];
+ }
+ cell.textLabel.text = displayname;
+
+ cell.detailTextLabel.numberOfLines = 1;
+ cell.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation;
+ cell.detailTextLabel.text = ([[permissions objectForKey:user] isEqualToString:@"write"] ? @"Read/Write" : @"Read Only");
}
}
return cell;
}
-
-#pragma mark - Table view delegate
+#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == kMetadata) {
EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
NSString *user;
- if (indexPath.row == [permissions count]) {
+ if (indexPath.row == permissions.count) {
user = @"";
vc.removePermissionsEnabled = NO;
- vc.navigationItem.title = @"Share";
+ vc.navigationItem.title = @"Add Permission";
} else {
- user = [[permissions allKeys] objectAtIndex:indexPath.row];
+ user = [[[permissions allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:indexPath.row];
NSString *userPermissions = [permissions objectForKey:user];
- if ([userPermissions rangeOfString:@"read"].location != NSNotFound)
+ if ([userPermissions rangeOfString:@"read"].location != NSNotFound) {
vc.readPermissionSelected = YES;
- else
+ } else {
vc.readPermissionSelected = NO;
-
- if ([userPermissions rangeOfString:@"write"].location != NSNotFound)
+ }
+ if ([userPermissions rangeOfString:@"write"].location != NSNotFound) {
vc.writePermissionSelected = YES;
- else
+ } else {
vc.writePermissionSelected = NO;
-
+ }
vc.removePermissionsEnabled = YES;
- vc.navigationItem.title = @"Edit Sharing";
+ vc.navigationItem.title = @"Edit Permission";
}
- vc.user = user;
+ vc.permissionUser = user;
vc.permissions = permissions;
vc.account = account;
vc.container = container;
}
}
-#pragma mark - Helper functions
-
-- (void)reloadMetadataSection {
- __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading metadata..."
- andAddToView:self.view];
- [[self.account.manager getObjectInfo:container object:object version:nil]
- success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- folder.metadata = [NSMutableDictionary dictionary];
- for (NSString *header in request.responseHeaders) {
- NSString *metadataKey;
- NSString *metadataValue;
- if ([header rangeOfString:@"X-Object-Meta-"].location != NSNotFound) {
- metadataKey = [NSString decodeFromPercentEscape:[header substringFromIndex:14]];
- metadataValue = [NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:header]];
- [folder.metadata setObject:metadataValue forKey:metadataKey];
- }
- }
- object.metadata = folder.metadata;
- NSIndexSet *metadataSections = [NSIndexSet indexSetWithIndex:kMetadata];
- [self.tableView reloadSections:metadataSections withRowAnimation:UITableViewRowAnimationFade];
- }
- failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- [self alert:@"There was a problem retrieving the object's metadata." request:request];
- }];
-}
-
@end
#import "OpenStackViewController.h"
#import "Folder.h"
-@class OpenStackAccount, Container, StorageObject, ContainersViewController, FolderDetailViewController, StorageObjectViewController;
+@class OpenStackAccount, Container, StorageObject, Folder, ContainersViewController, FolderDetailViewController, StorageObjectViewController;
@interface FolderViewController : OpenStackViewController <UITableViewDelegate, UITableViewDataSource, UISearchDisplayDelegate, UIActionSheetDelegate, UIDocumentInteractionControllerDelegate> {
OpenStackAccount *account;
Container *container;
Folder *folder;
NSString *name;
- id successObserver;
- id failureObserver;
-
ContainersViewController *containersViewController;
FolderViewController *parentFolderViewController;
- NSIndexPath *selectedContainerIndexPath;
- NSIndexPath *selectedFolderIndexPath;
- BOOL contentsLoaded;
- BOOL needsRefreshing;
- BOOL folderHasBeenRemoved;
BOOL refreshWhenAppeared;
- BOOL searchActiveOnRotation;
+ id deletedObject;
- IBOutlet UITableView *tableView;
- IBOutlet UIBarButtonItem *homeButton;
- IBOutlet UIBarButtonItem *refreshButton;
- IBOutlet UISearchBar *searchBar;
- IBOutlet UISearchDisplayController *searchDisplayController;
+ UISearchDisplayController *searchDisplayController;
+ UITableView *tableView;
+ UISearchBar *searchBar;
+ UIBarButtonItem *refreshButton;
+ UIBarButtonItem *sortTypeButton;
+ UIBarButtonItem *sortDirectionButton;
NSString *searchFilter;
-
- IBOutlet UIBarButtonItem *sortTypeButton;
- IBOutlet UIBarButtonItem *sortDirectionButton;
-
+ BOOL searchActiveOnRotation;
FolderSortType sortType;
FolderSortDirection sortNameDirection;
FolderSortDirection sortDateDirection;
+
+ id successObserver;
+ id failureObserver;
FolderDetailViewController *folderDetailVC;
StorageObjectViewController *selectedObjectViewController;
-
UIDocumentInteractionController *documentInteractionController;
}
@property (nonatomic, retain) Folder *folder;
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) ContainersViewController *containersViewController;
-@property (nonatomic, retain) NSIndexPath *selectedContainerIndexPath;
@property (nonatomic, retain) FolderViewController *parentFolderViewController;
-@property (nonatomic, retain) NSIndexPath *selectedFolderIndexPath;
-@property (nonatomic, assign) BOOL contentsLoaded;
-@property (nonatomic, assign) BOOL needsRefreshing;
-@property (nonatomic, assign) BOOL folderHasBeenRemoved;
@property (nonatomic, assign) BOOL refreshWhenAppeared;
-@property (nonatomic, assign) BOOL searchActiveOnRotation;
+@property (nonatomic, retain) id deletedObject;
+
+@property (nonatomic, retain) IBOutlet UISearchDisplayController *searchDisplayController;
@property (nonatomic, retain) IBOutlet UITableView *tableView;
-@property (nonatomic, retain) IBOutlet UIBarButtonItem *refreshButton;
@property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
-@property (nonatomic, retain) IBOutlet UISearchDisplayController *searchDisplayController;
-@property (nonatomic, retain) NSString *searchFilter;
+@property (nonatomic, retain) IBOutlet UIBarButtonItem *refreshButton;
@property (nonatomic, retain) IBOutlet UIBarButtonItem *sortTypeButton;
@property (nonatomic, retain) IBOutlet UIBarButtonItem *sortDirectionButton;
+
+@property (nonatomic, retain) NSString *searchFilter;
+@property (nonatomic, assign) BOOL searchActiveOnRotation;
@property (nonatomic, assign) FolderSortType sortType;
@property (nonatomic, assign) FolderSortDirection sortNameDirection;
@property (nonatomic, assign) FolderSortDirection sortDateDirection;
+
@property (nonatomic, assign) FolderDetailViewController *folderDetailVC;
@property (nonatomic, assign) StorageObjectViewController *selectedObjectViewController;
@property (nonatomic, retain) UIDocumentInteractionController *documentInteractionController;
-- (IBAction)homeButtonPressed:(id)sender;
- (IBAction)refreshButtonPressed:(id)sender;
- (IBAction)toggleSortType:(id)sender;
- (IBAction)toggleSortDirection:(id)sender;
+- (IBAction)homeButtonPressed:(id)sender;
- (void)reloadData;
- (void)setDetailViewController;
-- (void)deleteAnimatedObject:(StorageObject *)object;
+- (void)deleteAnimatedObject:(id)object;
@end
@implementation FolderViewController
-@synthesize account, container, folder, name, containersViewController, selectedContainerIndexPath, contentsLoaded, parentFolderViewController, selectedFolderIndexPath, tableView, needsRefreshing, folderHasBeenRemoved, refreshWhenAppeared, folderDetailVC, selectedObjectViewController, refreshButton;
-@synthesize searchBar, searchDisplayController, searchFilter, searchActiveOnRotation;
-@synthesize sortTypeButton, sortDirectionButton, sortType, sortNameDirection, sortDateDirection;
-@synthesize documentInteractionController;
+@synthesize account, container, folder, name, containersViewController, parentFolderViewController, refreshWhenAppeared, deletedObject;
+@synthesize searchDisplayController, tableView ,searchBar, refreshButton, sortTypeButton, sortDirectionButton;
+@synthesize searchFilter, searchActiveOnRotation, sortType, sortNameDirection, sortDateDirection;;
+@synthesize folderDetailVC, selectedObjectViewController, documentInteractionController;
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
[self addAddButton];
- [self addHomeButton];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
- if (self.name && self.name.length) {
- self.navigationItem.title = self.name;
- } else {
- self.navigationItem.title = self.container.name;
- }
+
+ self.navigationItem.title = (self.name.length ? self.name : self.container.name);
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
} else {
self.sortDirectionButton.title = (self.sortNameDirection == FolderSortNameDirectionZA) ? @"Z-A" : @"A-Z";
}
-
- [self reloadData];
+
+ if (!container.rootFolder || !deletedObject) {
+ self.deletedObject = nil;
+ [self reloadData];
+ } else if ([[deletedObject class] isEqual:[Folder class]]) {
+ Folder *deletedFolder = (Folder *)deletedObject;
+ if (![folder.folders objectForKey:deletedFolder.name] ||
+ ([[self searchResults] indexOfObject:[folder.folders objectForKey:deletedFolder.name]] == NSNotFound)) {
+ self.deletedObject = nil;
+ [self reloadData];
+ } else if (folder.objectsAndFoldersCount == 1) {
+ [self deleteAnimatedObject:deletedObject];
+ }
+ } else if ([[deletedObject class] isEqual:[StorageObject class]]) {
+ StorageObject *deletedStorageObject = (StorageObject *)deletedObject;
+ if (![folder.objects objectForKey:deletedStorageObject.name] ||
+ ([[self searchResults] indexOfObject:[folder.objects objectForKey:deletedStorageObject.name]] == NSNotFound)) {
+ self.deletedObject = nil;
+ [self reloadData];
+ } else if (folder.objectsAndFoldersCount == 1) {
+ [self deleteAnimatedObject:deletedObject];
+ }
+ } else {
+ self.deletedObject = nil;
+ }
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
- if (!self.folder) {
- if (folderHasBeenRemoved) {
- if (needsRefreshing)
- self.parentFolderViewController.needsRefreshing = YES;
- [self.navigationController popViewControllerAnimated:YES];
- } else {
- [self refreshButtonPressed:nil];
- }
- } else if (self.needsRefreshing) {
- self.parentFolderViewController.needsRefreshing = YES;
- if (refreshWhenAppeared) {
- [self refreshButtonPressed:nil];
- refreshWhenAppeared = NO;
- }
- if (account.shared && !folder.objectsAndFoldersCount) {
- [self.navigationController popViewControllerAnimated:YES];
- }
+
+ if (!container.rootFolder) {
+ // Container objects not fetched yet.
+ refreshWhenAppeared = YES;
+ } else if (!folder) {
+ // This controller's folder has been deleted.
+ parentFolderViewController.refreshWhenAppeared = YES;
+ [self.navigationController popViewControllerAnimated:YES];
+ return;
+ } else if (account.shared && !folder.objectsAndFoldersCount) {
+ // The shared folder is empty, probably after a delete.
+ [self.navigationController popViewControllerAnimated:YES];
+ return;
+ } else if (deletedObject) {
+ [self deleteAnimatedObject:deletedObject];
+ }
+
+ if (refreshWhenAppeared) {
+ refreshWhenAppeared = NO;
+ [self refreshButtonPressed:nil];
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
- if (![self.navigationController.viewControllers containsObject:self] &&
- self.parentViewController &&
- ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)) {
+ if (![self.navigationController.viewControllers containsObject:self] && self.parentViewController) {
[parentFolderViewController setDetailViewController];
}
}
[account release];
[container release];
[folder release];
+ [name release];
[containersViewController release];
- [selectedContainerIndexPath release];
[parentFolderViewController release];
- [selectedFolderIndexPath release];
- [refreshButton release];
- [searchBar release];
[searchDisplayController release];
- [searchFilter release];
+ [searchBar release];
+ [refreshButton release];
[sortTypeButton release];
[sortDirectionButton release];
+ [deletedObject release];
+ [searchFilter release];
[documentInteractionController release];
[super dealloc];
}
Folder *parentFolder = folderViewController.parentFolderViewController.folder;
folderViewController.folder = [parentFolder.folders objectForKey:folderViewController.folder.name];
- if (!folderViewController.folder)
- folderViewController.folderHasBeenRemoved = YES;
}
if (!self.folder || (account.shared && !folder.objectsAndFoldersCount)) {
- if (needsRefreshing && self.parentFolderViewController) {
- self.parentFolderViewController.needsRefreshing = YES;
- }
[self.navigationController popViewControllerAnimated:YES];
- } else if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && account.shared) {
+ } else if (account.shared) {
[self setDetailViewController];
}
}
-- (void)deleteFolderRow {
- [self.parentFolderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedFolderIndexPath]
- withRowAnimation:UITableViewRowAnimationLeft];
- if (self.parentFolderViewController.searchDisplayController.active &&
- self.parentFolderViewController.searchFilter && self.parentFolderViewController.searchFilter.length)
- [self.parentFolderViewController.searchDisplayController.searchResultsTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedFolderIndexPath]
- withRowAnimation:UITableViewRowAnimationLeft];
-}
-
-- (void)deleteObjectRow:(NSTimer *)timer {
- [self.folder removeObject:[timer.userInfo objectForKey:@"object"]];
- [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[timer.userInfo objectForKey:@"indexPath"]]
- withRowAnimation:UITableViewRowAnimationLeft];
- if (self.searchDisplayController.active && self.searchFilter && self.searchFilter.length)
- [self.searchDisplayController.searchResultsTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[timer.userInfo objectForKey:@"indexPath"]]
- withRowAnimation:UITableViewRowAnimationLeft];
- self.refreshButton.enabled = YES;
-}
+//- (void)deleteObjectRow:(NSTimer *)timer {
+// [self.folder removeObject:[timer.userInfo objectForKey:@"object"]];
+// if (folder == container.rootFolder) {
+// container.count -= 1;
+// }
+// [account persist];
+// [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[timer.userInfo objectForKey:@"indexPath"]]
+// withRowAnimation:UITableViewRowAnimationLeft];
+// if (self.searchDisplayController.active && self.searchFilter && self.searchFilter.length)
+// [self.searchDisplayController.searchResultsTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[timer.userInfo objectForKey:@"indexPath"]]
+// withRowAnimation:UITableViewRowAnimationLeft];
+// self.refreshButton.enabled = YES;
+//}
- (NSArray *)searchResults {
return [self.folder sortedContentsUsingFilter:self.searchFilter
sortDirection:((self.sortType == FolderSortTypeDate) ? self.sortDateDirection : self.sortNameDirection)];
}
+- (void)delete {
+ parentFolderViewController.deletedObject = folder;
+ [self.navigationController popViewControllerAnimated:YES];
+ [parentFolderViewController setDetailViewController];
+}
+
#pragma mark - Properties
- (void)setFolder:(Folder *)aFolder {
[folder release];
folder = [aFolder retain];
- [self showSearchBar:(self.contentsLoaded && self.folder && self.folder.objectsAndFoldersCount)];
+ [self showSearchBar:(self.container.rootFolder && self.folder && self.folder.objectsAndFoldersCount)];
if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && self.selectedObjectViewController && folder) {
StorageObject *newObject = [folder.objects objectForKey:self.selectedObjectViewController.object.name];
if (newObject)
}
- (void)setDetailViewController {
- self.selectedObjectViewController = nil;
- if ([self.folder isEqual:self.container.rootFolder]) {
- ContainerDetailViewController *vc = [[ContainerDetailViewController alloc] initWithNibName:@"ContainerDetailViewController" bundle:nil];
- vc.account = self.account;
- vc.container = self.container;
- vc.containersViewController = self.containersViewController;
- vc.rootFolderViewController = self;
- vc.selectedContainerIndexPath = self.selectedContainerIndexPath;
- [self presentPrimaryViewController:vc];
- self.containersViewController.containerDetailViewController = vc;
- [vc release];
- } else {
- FolderDetailViewController *vc = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
- vc.account = self.account;
- vc.container = self.container;
- vc.folder = self.folder;
- vc.folderViewController = self.parentFolderViewController;
- [self presentPrimaryViewController:vc];
- self.parentFolderViewController.folderDetailVC = vc;
- [vc release];
+ if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
+ self.selectedObjectViewController = nil;
+ if (self.folder) {
+ if ([self.folder isEqual:self.container.rootFolder]) {
+ [containersViewController setDetailViewControllerForContainer:container];
+ } else {
+ FolderDetailViewController *vc = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
+ vc.account = self.account;
+ vc.container = self.container;
+ vc.folder = self.folder;
+ vc.folderViewController = self.parentFolderViewController;
+ [self presentPrimaryViewController:vc];
+ self.parentFolderViewController.folderDetailVC = vc;
+ [vc release];
+ containersViewController.containerDetailViewController = nil;
+ }
+ }
}
}
-- (void)deleteAnimatedObject:(StorageObject *)object {
- NSIndexPath *deleteIndexPath = [NSIndexPath indexPathForRow:[[self searchResults] indexOfObject:object] inSection:0];
- [self.tableView selectRowAtIndexPath:deleteIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
- if (self.searchDisplayController.active && self.searchFilter && self.searchFilter.length)
- [self.searchDisplayController.searchResultsTableView selectRowAtIndexPath:deleteIndexPath
- animated:YES
- scrollPosition:UITableViewScrollPositionNone];
- NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:object, @"object", deleteIndexPath, @"indexPath", nil];
- [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteObjectRow:) userInfo:userInfo repeats:NO];
+//- (void)deleteAnimatedObject:(StorageObject *)object {
+// NSIndexPath *deleteIndexPath = [NSIndexPath indexPathForRow:[[self searchResults] indexOfObject:object] inSection:0];
+// [self.tableView selectRowAtIndexPath:deleteIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
+// if (self.searchDisplayController.active && self.searchFilter && self.searchFilter.length)
+// [self.searchDisplayController.searchResultsTableView selectRowAtIndexPath:deleteIndexPath
+// animated:YES
+// scrollPosition:UITableViewScrollPositionNone];
+// NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:object, @"object", deleteIndexPath, @"indexPath", nil];
+// [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteObjectRow:) userInfo:userInfo repeats:NO];
+//}
+
+- (void)deleteAnimatedObject:(id)object {
+ NSIndexPath *deletedObjectIndexPath = nil;
+ if ([[object class] isEqual:[Folder class]]) {
+ Folder *deletedFolder = [folder.folders objectForKey:[object name]];
+ if (deletedFolder) {
+ if (folder.objectsAndFoldersCount == 1) {
+ // If the deleted folder is the sole object of the folder, remove and reload to show the empty folder cell.
+ [folder removeFolder:deletedFolder];
+ if (folder == container.rootFolder) {
+ container.count -= 1;
+ }
+ [account persist];
+ self.folder = self.folder;
+ } else {
+ NSUInteger deletedFolderIndex = [[self searchResults] indexOfObject:deletedFolder];
+ if (deletedFolderIndex != NSNotFound) {
+ deletedObjectIndexPath = [NSIndexPath indexPathForRow:deletedFolderIndex inSection:0];
+ }
+ [folder removeFolder:deletedFolder];
+ if (folder == container.rootFolder) {
+ container.count -= 1;
+ }
+ [account persist];
+ }
+ }
+ } else if ([[object class] isEqual:[StorageObject class]]) {
+ StorageObject *deletedStorageObject = [folder.objects objectForKey:[object name]];
+ if (deletedStorageObject) {
+ if (folder.objectsAndFoldersCount == 1) {
+ // If the deleted storage object is the sole object of the folder, remove and reload to show the empty folder cell.
+ [folder removeObject:deletedStorageObject];
+ if (folder == container.rootFolder) {
+ container.count -= 1;
+ }
+ [account persist];
+ self.folder = self.folder;
+ } else {
+ NSUInteger deletedStorageObjectIndex = [[self searchResults] indexOfObject:deletedStorageObject];
+ if (deletedStorageObjectIndex != NSNotFound) {
+ deletedObjectIndexPath = [NSIndexPath indexPathForRow:deletedStorageObjectIndex inSection:0];
+ }
+ [folder removeObject:deletedStorageObject];
+ if (folder == container.rootFolder) {
+ container.count -= 1;
+ }
+ [account persist];
+ }
+ }
+ }
+ if (deletedObjectIndexPath) {
+ [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:deletedObjectIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
+ if (searchDisplayController.active && searchFilter && searchFilter.length) {
+ [searchDisplayController.searchResultsTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:deletedObjectIndexPath]
+ withRowAnimation:UITableViewRowAnimationLeft];
+ }
+ }
+ self.deletedObject = nil;
+ refreshWhenAppeared = NO;
+ [self refreshButtonPressed:nil];
}
- (IBAction)toggleSortType:(id)sender {
if (self.folder.objectsAndFoldersCount) {
// For the search results tableView this will be returned because search is allowed only when the folder is not empty.
return [[self searchResults] count];
- } else if (contentsLoaded) {
+ } else if (self.container.rootFolder) {
return 1;
} else {
return 0;
vc.account = self.account;
vc.container = self.container;
vc.name = [item name];
- vc.selectedFolderIndexPath = indexPath;
vc.parentFolderViewController = self;
vc.containersViewController = self.containersViewController;
[self.navigationController pushViewController:vc animated:YES];
- vc.contentsLoaded = YES;
vc.folder = item;
[aTableView deselectRowAtIndexPath:indexPath animated:YES];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
#pragma mark - Button Handlers
-- (IBAction)homeButtonPressed:(id)sender {
- [self.navigationController popToViewController:containersViewController.accountHomeViewController animated:YES];
+- (void)addButtonPressed:(id)sender {
+ AddObjectViewController *vc = [[AddObjectViewController alloc] initWithNibName:@"AddObjectViewController" bundle:nil];
+ vc.account = self.account;
+ vc.container = self.container;
+ vc.folder = self.folder;
+ vc.folderViewController = self;
+ if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
+ vc.modalPresentationStyle = UIModalPresentationFormSheet;
+ OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+ if (app.rootViewController.popoverController != nil) {
+ [app.rootViewController.popoverController dismissPopoverAnimated:YES];
+ }
+ }
+ [self presentModalViewControllerWithNavigation:vc animated:YES];
+ [vc release];
}
-- (IBAction)refreshButtonPressed:(id)sender {
+- (void)refreshButtonPressed:(id)sender {
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading..."
andAddToView:self.view];
[self.account.manager getObjects:self.container];
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification* notification)
{
- [activityIndicatorView removeFromSuperview];
- contentsLoaded = YES;
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self reloadFolderViewControllers];
- needsRefreshing = NO;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
if (self.selectedObjectViewController) {
[self.selectedObjectViewController reloadMetadataSection];
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification* notification)
{
- [activityIndicatorView removeFromSuperview];
- needsRefreshing = NO;
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self reloadData];
[self alert:@"Error" message:@"Failed to retrieve files from server."];
[[NSNotificationCenter defaultCenter] removeObserver:successObserver];
}];
}
-- (void)addButtonPressed:(id)sender {
- AddObjectViewController *vc = [[AddObjectViewController alloc] initWithNibName:@"AddObjectViewController" bundle:nil];
- vc.account = self.account;
- vc.container = self.container;
- vc.folder = self.folder;
- vc.folderViewController = self;
- if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
- vc.modalPresentationStyle = UIModalPresentationFormSheet;
- OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
- if (app.rootViewController.popoverController != nil) {
- [app.rootViewController.popoverController dismissPopoverAnimated:YES];
- }
- }
- [self presentModalViewControllerWithNavigation:vc animated:YES];
- [vc release];
+- (void)homeButtonPressed:(id)sender {
+ [self.navigationController popToViewController:containersViewController.accountHomeViewController animated:YES];
}
- (void)deleteButtonPressed:(id)sender {
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
- StorageObject *object = [[StorageObject alloc] init];
+ StorageObject *object = [[[StorageObject alloc] init] autorelease];
object.name = object.fullPath = [self.folder fullPath];
-
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Deleting folder..."
andAddToView:self.view
scrollOffset:self.tableView.contentOffset.y];
[[self.account.manager deleteObject:self.container object:object]
success:^(OpenStackRequest *request) {
- if (self.folder.parent) {
- [self.folder.parent removeFolder:self.folder];
- } else {
- [self.container.rootFolder removeFolder:self.folder];
- }
- [self.account persist];
-
- [activityIndicatorView removeFromSuperview];
- [self.navigationController popViewControllerAnimated:YES];
-
- if ((self.folder.parent && !self.folder.parent.objectsAndFoldersCount) ||
- (!self.folder.parent && !self.container.rootFolder.objectsAndFoldersCount)) {
- self.parentFolderViewController.folder = self.parentFolderViewController.folder;
- } else if ((self.folder.parent && self.folder.parent.objectsAndFoldersCount) ||
- (!self.folder.parent && self.container.rootFolder.objectsAndFoldersCount)) {
- [self.parentFolderViewController.tableView selectRowAtIndexPath:selectedFolderIndexPath
- animated:YES
- scrollPosition:UITableViewScrollPositionNone];
- if (self.parentFolderViewController.searchDisplayController.active &&
- self.parentFolderViewController.searchFilter && self.parentFolderViewController.searchFilter.length)
- [self.parentFolderViewController.searchDisplayController.searchResultsTableView selectRowAtIndexPath:selectedFolderIndexPath
- animated:YES
- scrollPosition:UITableViewScrollPositionNone];
- [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteFolderRow) userInfo:nil repeats:NO];
- }
- if (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && self.folder.parent)
- [self.parentFolderViewController setDetailViewController];
- [object release];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self delete];
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- [self alert:@"There was a problem deleting this folder." request:request];
- [object release];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ // 404 Not Found means it's not there, so we can show the user that it's deleted
+ if (request.responseStatusCode == 404) {
+ [self delete];
+ } else {
+ [self alert:@"There was a problem deleting this folder." request:request];
+ }
}];
}
}
<int key="NSvFlags">266</int>
<string key="NSFrame">{{0, 416}, {320, 44}}</string>
<reference key="NSSuperview" ref="228054471"/>
- <reference key="NSNextKeyView"/>
+ <reference key="NSWindow"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<reference key="IBUIToolbar" ref="599029925"/>
<int key="IBUISystemItemIdentifier">5</int>
</object>
+ <object class="IBUIBarButtonItem" id="901170948">
+ <object class="NSCustomResource" key="IBUIImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">HomeFolderIcon.png</string>
+ </object>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <float key="IBUIWidth">33</float>
+ <reference key="IBUIToolbar" ref="599029925"/>
+ </object>
</object>
</object>
<object class="IBUITableView" id="173989330">
<int key="NSvFlags">274</int>
<string key="NSFrameSize">{320, 416}</string>
<reference key="NSSuperview" ref="228054471"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="599029925"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
</object>
<string key="NSFrameSize">{320, 460}</string>
<reference key="NSSuperview"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="173989330"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<int key="NSvFlags">290</int>
<string key="NSFrameSize">{320, 44}</string>
<reference key="NSSuperview"/>
- <reference key="NSNextKeyView"/>
+ <reference key="NSWindow"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<int key="IBUIContentMode">3</int>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<int key="connectionID">63</int>
</object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchEventConnection" key="connection">
+ <string key="label">homeButtonPressed:</string>
+ <reference key="source" ref="901170948"/>
+ <reference key="destination" ref="372490531"/>
+ </object>
+ <int key="connectionID">66</int>
+ </object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
<reference ref="190609300"/>
<reference ref="438761365"/>
<reference ref="970966376"/>
+ <reference ref="901170948"/>
</object>
<reference key="parent" ref="228054471"/>
</object>
<reference key="object" ref="970966376"/>
<reference key="parent" ref="599029925"/>
</object>
+ <object class="IBObjectRecord">
+ <int key="objectID">65</int>
+ <reference key="object" ref="901170948"/>
+ <reference key="parent" ref="599029925"/>
+ </object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
<string>49.IBPluginDependency</string>
<string>58.IBPluginDependency</string>
<string>59.IBPluginDependency</string>
+ <string>65.IBPluginDependency</string>
<string>9.IBPluginDependency</string>
</object>
<object class="NSArray" key="dict.values">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</object>
</object>
<object class="NSMutableDictionary" key="unlocalizedProperties">
<reference key="dict.values" ref="0"/>
</object>
<nil key="sourceID"/>
- <int key="maxID">64</int>
+ <int key="maxID">66</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">FolderViewController</string>
+ <string key="superclassName">OpenStackViewController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>homeButtonPressed:</string>
+ <string>refreshButtonPressed:</string>
+ <string>toggleSortDirection:</string>
+ <string>toggleSortType:</string>
+ </object>
+ <object class="NSArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>homeButtonPressed:</string>
+ <string>refreshButtonPressed:</string>
+ <string>toggleSortDirection:</string>
+ <string>toggleSortType:</string>
+ </object>
+ <object class="NSArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">homeButtonPressed:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">refreshButtonPressed:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">toggleSortDirection:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">toggleSortType:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>refreshButton</string>
+ <string>searchBar</string>
+ <string>searchDisplayController</string>
+ <string>sortDirectionButton</string>
+ <string>sortTypeButton</string>
+ <string>tableView</string>
+ </object>
+ <object class="NSArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>UIBarButtonItem</string>
+ <string>UISearchBar</string>
+ <string>UISearchDisplayController</string>
+ <string>UIBarButtonItem</string>
+ <string>UIBarButtonItem</string>
+ <string>UITableView</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>refreshButton</string>
+ <string>searchBar</string>
+ <string>searchDisplayController</string>
+ <string>sortDirectionButton</string>
+ <string>sortTypeButton</string>
+ <string>tableView</string>
+ </object>
+ <object class="NSArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBToOneOutletInfo">
+ <string key="name">refreshButton</string>
+ <string key="candidateClassName">UIBarButtonItem</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">searchBar</string>
+ <string key="candidateClassName">UISearchBar</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">searchDisplayController</string>
+ <string key="candidateClassName">UISearchDisplayController</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">sortDirectionButton</string>
+ <string key="candidateClassName">UIBarButtonItem</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">sortTypeButton</string>
+ <string key="candidateClassName">UIBarButtonItem</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">tableView</string>
+ <string key="candidateClassName">UITableView</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/FolderViewController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">OpenStackViewController</string>
+ <string key="superclassName">UIViewController</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">toolbar</string>
+ <string key="NS.object.0">UIToolbar</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">toolbar</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">toolbar</string>
+ <string key="candidateClassName">UIToolbar</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/OpenStackViewController.h</string>
+ </object>
+ </object>
+ </object>
</object>
- <object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+ <string key="NS.key.0">HomeFolderIcon.png</string>
+ <string key="NS.object.0">{22, 22}</string>
+ </object>
<string key="IBCocoaTouchPluginVersion">1929</string>
</data>
</archive>
+++ /dev/null
-//
-// GetContainersRequest.h
-// OpenStack
-//
-// Created by Mike Mayo on 12/24/10.
-// The OpenStack project is provided under the Apache 2.0 license.
-//
-
-#import "OpenStackRequest.h"
-
-@class OpenStackAccount;
-
-@interface GetContainersRequest : OpenStackRequest {
-}
-
-+ (GetContainersRequest *)request:(OpenStackAccount *)account;
-
-@end
+++ /dev/null
-//
-// GetContainersRequest.m
-// OpenStack
-//
-// Created by Mike Mayo on 12/24/10.
-// The OpenStack project is provided under the Apache 2.0 license.
-//
-
-#import "GetContainersRequest.h"
-#import "OpenStackAccount.h"
-#import "Container.h"
-#import "AccountManager.h"
-
-@implementation GetContainersRequest
-
-+ (id)request:(OpenStackAccount *)account method:(NSString *)method url:(NSURL *)url {
- GetContainersRequest *request = [[[GetContainersRequest alloc] initWithURL:url] autorelease];
- request.account = account;
- [request setRequestMethod:method];
- [request addRequestHeader:@"X-Auth-Token" value:[account authToken]];
- [request addRequestHeader:@"Content-Type" value:@"application/json"];
- return request;
-}
-
-+ (id)filesRequest:(OpenStackAccount *)account method:(NSString *)method path:(NSString *)path {
- NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@?format=json%@",
- account.filesURL,
- path,
- account.shared ? @"&shared=" : @""]];
-
- return [GetContainersRequest request:account method:method url:url];
-}
-
-+ (GetContainersRequest *)request:(OpenStackAccount *)account {
- GetContainersRequest *request = [GetContainersRequest filesRequest:account method:@"GET" path:@""];
- request.account = account;
- [request setTimeOutSeconds:60];
- [request setNumberOfTimesToRetryOnTimeout:5];
- return request;
-}
-
-- (void)requestFinished {
-
- if ([self isSuccess]) {
- self.account.containers = [self containers];
- self.account.containerCount = [self.account.containers count];
- [self.account persist];
- [self.account.manager notify:@"getContainersSucceeded" request:self object:self.account];
- } else {
- [self.account.manager notify:@"getContainersFailed" request:self object:self.account];
- }
-
- [super requestFinished];
-}
-
-- (void)failWithError:(NSError *)theError {
- [self.account.manager notify:@"getContainersFailed" request:self object:self.account];
- [super failWithError:theError];
-}
-
-
-@end
@property (nonatomic, retain) Container *container;
-+ (GetObjectsRequest *)request:(OpenStackAccount *)account container:(Container *)container;
-+ (GetObjectsRequest *)request:(OpenStackAccount *)account
- container:(Container *)container
- marker:(NSString *)marker
- objectsBuffer:(NSMutableDictionary *)objectsBuffer;
++ (id)request:(OpenStackAccount *)account container:(Container *)container;
++ (id)request:(OpenStackAccount *)account container:(Container *)container
+ marker:(NSString *)marker objectsBuffer:(NSMutableDictionary *)objectsBuffer;
@end
@synthesize container;
+#pragma mark - Constructors
+
+ (id)request:(OpenStackAccount *)account method:(NSString *)method url:(NSURL *)url {
- GetObjectsRequest *request = [[[GetObjectsRequest alloc] initWithURL:url] autorelease];
+ GetObjectsRequest *request = [[[self
+ alloc] initWithURL:url] autorelease];
request.account = account;
- [request setRequestMethod:method];
- [request addRequestHeader:@"X-Auth-Token" value:[account authToken]];
+ request.requestMethod = method;
+ [request addRequestHeader:@"X-Auth-Token" value:account.authToken];
[request addRequestHeader:@"Content-Type" value:@"application/json"];
+ request.timeOutSeconds = 60;
+ request.numberOfTimesToRetryOnTimeout = 5;
return request;
}
+ (id)filesRequest:(OpenStackAccount *)account method:(NSString *)method path:(NSString *)path marker:(NSString *)marker {
- NSString *filesUrl;
NSString *urlString = [account.filesURL description];
if (account.sharingAccount) {
- NSRange authUserRange = [urlString rangeOfString:account.username];
- filesUrl = [NSString stringWithFormat:@"%@%@",
- [urlString substringToIndex:authUserRange.location],
- account.sharingAccount];
- } else
- filesUrl = urlString;
-
+ urlString = [NSString stringWithFormat:@"%@%@",
+ [urlString substringToIndex:[urlString rangeOfString:account.username].location],
+ account.sharingAccount];
+ }
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@?format=json%@%@",
- filesUrl,
+ urlString,
path,
- account.shared ? @"&shared=" : @"",
- marker ? [NSString stringWithFormat:@"&marker=%@", marker] : @""]];
-
- return [GetObjectsRequest request:account method:method url:url];
+ (account.shared ? @"&shared=" : @""),
+ (marker ? [NSString stringWithFormat:@"&marker=%@", marker] : @"")]];
+ return [self request:account method:method url:url];
}
+ (id)filesRequest:(OpenStackAccount *)account method:(NSString *)method path:(NSString *)path {
- return [GetObjectsRequest filesRequest:account method:method path:path marker:nil];
+ return [self filesRequest:account method:method path:path marker:nil];
}
-+ (GetObjectsRequest *)request:(OpenStackAccount *)account container:(Container *)container {
- return [GetObjectsRequest request:account container:container marker:nil objectsBuffer:nil];
++ (id)request:(OpenStackAccount *)account container:(Container *)container {
+ return [self request:account container:container marker:nil objectsBuffer:nil];
}
-+ (GetObjectsRequest *)request:(OpenStackAccount *)account
- container:(Container *)container
- marker:(NSString *)marker
- objectsBuffer:(NSMutableDictionary *)objectsBuffer
-{
- GetObjectsRequest *request = [GetObjectsRequest filesRequest:account method:@"GET"
- path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]
- marker:[NSString encodeToPercentEscape:marker]];
- request.account = account;
++ (id)request:(OpenStackAccount *)account container:(Container *)container
+ marker:(NSString *)marker objectsBuffer:(NSMutableDictionary *)objectsBuffer {
+ GetObjectsRequest *request = [self filesRequest:account
+ method:@"GET"
+ path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]
+ marker:[NSString encodeToPercentEscape:marker]];
request.container = container;
if (objectsBuffer == nil)
objectsBuffer = [NSMutableDictionary dictionary];
- [request setUserInfo:[NSMutableDictionary dictionaryWithObject:objectsBuffer forKey:@"objectsBuffer"]];
- [request setTimeOutSeconds:60];
- [request setNumberOfTimesToRetryOnTimeout:5];
+ request.userInfo = [NSMutableDictionary dictionaryWithObject:objectsBuffer forKey:@"objectsBuffer"];
return request;
}
+#pragma mark - ASIHTTPRequest Overrides
- (void)requestFinished {
if ([self isSuccess]) {
- Container *aContainer = self.container; //[self.userInfo objectForKey:@"container"];
+ Container *aContainer = self.container;
NSMutableDictionary *objects = [self objects];
NSMutableDictionary *objectsBuffer = [self.userInfo objectForKey:@"objectsBuffer"];
-
- SBJSON *parser = [[SBJSON alloc] init];
- NSArray *jsonObjects = [parser objectWithString:[self responseString]];
- NSUInteger numberOfObjectsInResponse = [jsonObjects count];
- [parser release];
-
[objectsBuffer addEntriesFromDictionary:objects];
- if (numberOfObjectsInResponse < 10000) {
+ if (objects.count < 10000) {
aContainer.rootFolder = [Folder folder];
aContainer.rootFolder.objects = objectsBuffer;
[self.account persist];
- NSNotification *notification = [NSNotification notificationWithName:@"getObjectsSucceeded" object:self.container userInfo:
- [NSDictionary dictionaryWithObjectsAndKeys:aContainer,@"container",
- self, @"request",nil]];
+ NSNotification *notification = [NSNotification notificationWithName:@"getObjectsSucceeded" object:self.container
+ userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
+ aContainer, @"container",
+ self, @"request",
+ nil]];
[[NSNotificationCenter defaultCenter] postNotification:notification];
} else {
+ SBJSON *parser = [[[SBJSON alloc] init] autorelease];
+ NSArray *jsonObjects = [parser objectWithString:[self responseString]];
NSString *marker = [[jsonObjects lastObject] objectForKey:@"name"];
[self.account.manager getObjects:self.container afterMarker:marker objectsBuffer:objectsBuffer];
}
[super failWithError:theError];
}
+#pragma mark - Memory Management
+
- (void)dealloc {
[container release];
[super dealloc];
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading..."
andAddToView:self.view];
- [[self.account.manager getObjectVersionsList:container object:object]
+ [[self.account.manager getObjectVersionsList:container object:object]
success:^(OpenStackRequest *request) {
- SBJSON *parser = [[SBJSON alloc] init];
- NSArray *versionsInJson = [[parser objectWithString:[request responseString]] objectForKey:@"versions"];
- for (NSArray *versionInfo in versionsInJson) {
- [versions addObject:[NSDictionary dictionaryWithObjectsAndKeys:[versionInfo objectAtIndex:0], @"versionID",
- [NSDate dateWithTimeIntervalSince1970:[[versionInfo objectAtIndex:1] doubleValue]], @"versionDate",
- nil]];
- }
- [parser release];
- [activityIndicatorView removeFromSuperview];
+ [versions addObjectsFromArray:[request versions]];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
versionsLoaded = YES;
[self.tableView reloadData];
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self alert:@"Couldn't get versions from server." request:request];
}];
}
NSString *versionID = [[versionDetails objectForKey:@"versionID"] description];
[[self.account.manager getObjectInfo:container object:object version:versionID]
success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
StorageObject *versionedObject = [[[StorageObject alloc] init] autorelease];
versionedObject.name = object.name;
versionedObject.fullPath = object.fullPath;
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self alert:[NSString stringWithFormat:@"Failed to get file info for version: %@", versionID] request:request];
}];
}
// The OpenStack project is provided under the Apache 2.0 license.
//
-#import <Foundation/Foundation.h>
-
@class Provider;
-
@class AccountManager;
@interface OpenStackAccount : NSObject <NSCoding, NSCopying> {
- @private
- BOOL shared;
- NSString *sharingAccount;
}
@property (nonatomic, assign) BOOL hasBeenRefreshed;
@property (nonatomic, retain) NSString *username;
@property (nonatomic, retain) NSString *apiKey;
@property (nonatomic, retain) NSString *authToken;
-@property (nonatomic, retain) NSURL *hostURL;
@property (nonatomic, retain) NSURL *filesURL;
-@property (nonatomic, readonly) NSURL *pithosPublicLinkURLPrefix;
-@property (nonatomic, readonly) NSURL *pithosLoginURLPrefix;
@property (nonatomic, retain) AccountManager *manager;
-@property (nonatomic, assign) NSInteger containerCount;
@property (nonatomic, retain) NSNumber *bytesUsed;
@property (nonatomic, retain) NSNumber *policyQuota;
@property (nonatomic, retain) NSMutableDictionary *containers;
@property (nonatomic, assign) BOOL flaggedForDelete;
@property (nonatomic, assign) BOOL shared;
@property (nonatomic, retain) NSString *sharingAccount;
+@property (nonatomic, retain) NSMutableDictionary *userCatalog;
+ (NSArray *)accounts;
-- (void)persist;
+ (void)persist:(NSArray *)accountArray;
+- (void)persist;
- (NSArray *)pithosSortedContainers;
+- (NSString *)displaynameForUUID:(NSString *)UUID safe:(BOOL)safe;
+- (NSString *)displaynameForUUID:(NSString *)UUID;
@end
@implementation OpenStackAccount
@synthesize uuid, provider, username, filesURL, manager,
- containerCount, bytesUsed, policyQuota, containers, hasBeenRefreshed, flaggedForDelete,
- shared, sharingAccount, pithosLoginURLPrefix, pithosPublicLinkURLPrefix, hostURL;
+ bytesUsed, policyQuota, containers, hasBeenRefreshed, flaggedForDelete,
+ shared, sharingAccount, userCatalog;
+ (void)initialize {
accounts = [[Archiver retrieve:@"accounts"] retain];
}
}
-- (NSArray *)pithosSortedContainers {
- NSMutableArray *pithosSortedContainers = [NSMutableArray array];
- if ([self.containers objectForKey:@"pithos"])
- [pithosSortedContainers addObject:[self.containers objectForKey:@"pithos"]];
- if ([self.containers objectForKey:@"trash"])
- [pithosSortedContainers addObject:[self.containers objectForKey:@"trash"]];
-
- NSMutableDictionary *otherContainers = [NSMutableDictionary dictionaryWithDictionary:self.containers];
- [otherContainers removeObjectForKey:@"pithos"];
- [otherContainers removeObjectForKey:@"trash"];
- [pithosSortedContainers addObjectsFromArray:[[otherContainers allValues] sortedArrayUsingSelector:@selector(compare:)]];
- return pithosSortedContainers;
-}
+#pragma mark - Constructors
-#pragma mark - Properties
-
-- (NSURL *)pithosPublicLinkURLPrefix {
- return self.hostURL;
-}
-
-- (NSURL *)pithosLoginURLPrefix {
- return [self.hostURL URLByAppendingPathComponent:@"login"];
-}
-
-- (void)setShared:(BOOL)aShared {
- if (shared != aShared) {
- [self.containers removeAllObjects];
+- (id)init {
+ if ((self = [super init])) {
+ uuid = [[NSString alloc] initWithString:[OpenStackAccount stringWithUUID]];
+
+ self.userCatalog = [NSMutableDictionary dictionary];
+
+ manager = [[AccountManager alloc] init];
+ manager.account = self;
}
- shared = aShared;
+ return self;
}
-- (void)setSharingAccount:(NSString *)aSharingAccount {
- if ((aSharingAccount && ![aSharingAccount isEqualToString:sharingAccount]) || (!aSharingAccount && sharingAccount)) {
- [self.containers removeAllObjects];
+
+- (id)initWithCoder:(NSCoder *)coder {
+ if ((self = [super init])) {
+ self.uuid = [self decode:coder key:@"uuid"];
+ self.provider = [self decode:coder key:@"provider"];
+ self.username = [self decode:coder key:@"username"];
+
+ self.filesURL = [self decode:coder key:@"filesURL"];
+ self.bytesUsed = [self decode:coder key:@"bytesUsed"];
+ self.policyQuota = [self decode:coder key:@"policyQuota"];
+
+// containers = [self decode:coder key:@"containers"];
+
+ self.userCatalog = [self decode:coder key:@"userCatalog"];
+ if (!userCatalog) {
+ self.userCatalog = [NSMutableDictionary dictionary];
+ }
+
+ manager = [[AccountManager alloc] init];
+ manager.account = self;
}
- [sharingAccount release];
- sharingAccount = [aSharingAccount retain];
+ return self;
}
#pragma mark - Serialization
- (id)copyWithZone:(NSZone *)zone {
- OpenStackAccount *copy = [[OpenStackAccount allocWithZone:zone] init];
+ OpenStackAccount *copy = [[[self class] allocWithZone:zone] init];
copy.uuid = self.uuid;
copy.provider = self.provider;
copy.username = self.username;
copy.authToken = self.authToken;
copy.filesURL = self.filesURL;
- copy.hostURL = self.hostURL;
- copy.containerCount = self.containerCount;
copy.bytesUsed = self.bytesUsed;
copy.policyQuota = self.policyQuota;
- manager = [[AccountManager alloc] init];
- manager.account = copy;
+
+ copy.userCatalog = [[self.userCatalog copy] autorelease];
+
+ copy.manager = [[[AccountManager alloc] init] autorelease];
+ copy.manager.account = copy;
return copy;
}
[coder encodeObject:username forKey:@"username"];
[coder encodeObject:filesURL forKey:@"filesURL"];
- [coder encodeObject:hostURL forKey:@"hostURL"];
- [coder encodeInt:containerCount forKey:@"containerCount"];
[coder encodeObject:bytesUsed forKey:@"bytesUsed"];
[coder encodeObject:policyQuota forKey:@"policyQuota"];
+
+// [coder encodeObject:containers forKey:@"containers"];
+
+ [coder encodeObject:userCatalog forKey:@"userCatalog"];
}
-- (id)decode:(NSCoder *)coder key:(NSString *)key {
+- (id)decode:(NSCoder *)coder key:(NSString *)key {
@try {
- return [[coder decodeObjectForKey:key] retain];
+ return [coder decodeObjectForKey:key];
}
@catch (NSException *exception) {
return nil;
}
}
-- (id)initWithCoder:(NSCoder *)coder {
- if ((self = [super init])) {
- uuid = [self decode:coder key:@"uuid"];
- provider = [self decode:coder key:@"provider"];
- username = [self decode:coder key:@"username"];
-
- filesURL = [self decode:coder key:@"filesURL"];
- hostURL = [self decode:coder key:@"hostURL"];
-
- containerCount = [coder decodeIntForKey:@"containerCount"];
+#pragma mark - Properties
- bytesUsed = [self decode:coder key:@"bytesUsed"];
- policyQuota = [self decode:coder key:@"policyQuota"];
-
- containers = [self decode:coder key:@"containers"];
-
- manager = [[AccountManager alloc] init];
- manager.account = self;
+- (void)setShared:(BOOL)aShared {
+ if (shared != aShared) {
+ self.containers = nil;
}
- return self;
+ shared = aShared;
}
-- (id)init {
- if ((self = [super init])) {
- uuid = [[NSString alloc] initWithString:[OpenStackAccount stringWithUUID]];
-
- manager = [[AccountManager alloc] init];
- manager.account = self;
+- (void)setSharingAccount:(NSString *)aSharingAccount {
+ if ((aSharingAccount && ![aSharingAccount isEqualToString:sharingAccount]) || (!aSharingAccount && sharingAccount)) {
+ self.containers = nil;
}
- return self;
+ [sharingAccount release];
+ sharingAccount = [aSharingAccount retain];
+}
+
+// the API key and auth token are stored in the Keychain, so overriding the
+// getter and setter to abstract the encryption away and make it easy to use
+
+- (NSString *)apiKeyKeychainKey {
+ return [NSString stringWithFormat:@"%@-apiKey", self.uuid];
}
+- (NSString *)apiKey {
+ return [Keychain getStringForKey:[self apiKeyKeychainKey]];
+}
+
+- (void)setApiKey:(NSString *)newAPIKey {
+ [Keychain setString:newAPIKey forKey:[self apiKeyKeychainKey]];
+}
+
+- (NSString *)authTokenKeychainKey {
+ return [NSString stringWithFormat:@"%@-authToken", self.uuid];
+}
+
+- (NSString *)authToken {
+ NSString *authToken = [Keychain getStringForKey:[self authTokenKeychainKey]];
+ if (!authToken) {
+ authToken = @"";
+ }
+ return authToken;
+}
+
+- (void)setAuthToken:(NSString *)newAuthToken {
+ [Keychain setString:newAuthToken forKey:[self authTokenKeychainKey]];
+}
+
+#pragma mark - Class Actions
+
+ (NSArray *)accounts {
if (accounts == nil) {
accounts = [[Archiver retrieve:@"accounts"] retain];
accounts = nil;
}
+#pragma mark - Actions
+
- (void)persist {
if (!flaggedForDelete) {
NSMutableArray *accountArr = [NSMutableArray arrayWithArray:[OpenStackAccount accounts]];
break;
}
}
-
+
if (!accountPresent) {
[accountArr insertObject:self atIndex:0];
}
- [Archiver persist:[NSArray arrayWithArray:accountArr] key:@"accounts"];
+ [Archiver persist:[NSArray arrayWithArray:accountArr] key:@"accounts"];
[accounts release];
accounts = nil;
}
}
-// the API key and auth token are stored in the Keychain, so overriding the
-// getter and setter to abstract the encryption away and make it easy to use
-
-- (NSString *)apiKeyKeychainKey {
- return [NSString stringWithFormat:@"%@-apiKey", self.uuid];
-}
-
-- (NSString *)apiKey {
- return [Keychain getStringForKey:[self apiKeyKeychainKey]];
-}
-
-- (void)setApiKey:(NSString *)newAPIKey {
- [Keychain setString:newAPIKey forKey:[self apiKeyKeychainKey]];
-}
-
-- (NSString *)authTokenKeychainKey {
- return [NSString stringWithFormat:@"%@-authToken", self.uuid];
+- (NSArray *)pithosSortedContainers {
+ NSMutableArray *pithosSortedContainers = [NSMutableArray array];
+ if ([self.containers objectForKey:@"pithos"])
+ [pithosSortedContainers addObject:[self.containers objectForKey:@"pithos"]];
+ if ([self.containers objectForKey:@"trash"])
+ [pithosSortedContainers addObject:[self.containers objectForKey:@"trash"]];
+
+ NSMutableDictionary *otherContainers = [NSMutableDictionary dictionaryWithDictionary:self.containers];
+ [otherContainers removeObjectForKey:@"pithos"];
+ [otherContainers removeObjectForKey:@"trash"];
+ [pithosSortedContainers addObjectsFromArray:[[otherContainers allValues] sortedArrayUsingSelector:@selector(compare:)]];
+ return pithosSortedContainers;
}
-- (NSString *)authToken {
- NSString *authToken = [Keychain getStringForKey:[self authTokenKeychainKey]];
- if (!authToken) {
- authToken = @"";
+- (NSString *)displaynameForUUID:(NSString *)UUID safe:(BOOL)safe {
+ NSString *displayName = [userCatalog objectForKey:UUID];
+ if (safe && !displayName) {
+ return UUID;
+ } else {
+ return displayName;
}
- return authToken;
}
-- (void)setAuthToken:(NSString *)newAuthToken {
- [Keychain setString:newAuthToken forKey:[self authTokenKeychainKey]];
+- (NSString *)displaynameForUUID:(NSString *)UUID {
+ return [self displaynameForUUID:UUID safe:NO];
}
#pragma mark - Memory Management
[manager release];
[provider release];
[username release];
- [hostURL release];
[filesURL release];
[containers release];
[sharingAccount release];
[bytesUsed release];
[policyQuota release];
+ [userCatalog release];
[super dealloc];
}
#pragma mark - Persistence
- (NSString *)cacheFilePathForHash:(NSString *)hash {
+ if (!hash)
+ return nil;
NSString *filePath = [self.cachedObjectsDictionary objectForKey:hash];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
@synchronized(self.cachedObjectsDictionary) {
#import "ASIHTTPRequest.h"
-#define kOpenStackPollingFrequency 20.0
-
@class OpenStackAccount, Container, StorageObject, APICallback, ErrorAlerter;
@interface OpenStackRequest : ASIHTTPRequest {
OpenStackAccount *account;
- BOOL retried;
- OpenStackRequest *retriedRequest;
ASIBasicBlock backupCompletionBlock;
ASIBasicBlock backupFailureBlock;
APICallback *callback;
- NSInteger retriedCount;
}
@property (nonatomic, retain) OpenStackAccount *account;
@property (nonatomic, retain) APICallback *callback;
-@property (nonatomic, assign) NSInteger retriedCount;
@property (nonatomic, retain) ErrorAlerter *errorAlerter;
-- (void)setCompletionBlock:(ASIBasicBlock)aCompletionBlock;
-- (void)setFailedBlock:(ASIBasicBlock)aFailedBlock;
-
-- (BOOL)isSuccess;
-- (void)notify;
-- (void)notify:(NSString *)name;
-
+ (id)request:(OpenStackAccount *)account method:(NSString *)method url:(NSURL *)url;
-+ (id)getSharingAccountsRequest:(OpenStackAccount *)account;
+ (id)filesRequest:(OpenStackAccount *)account method:(NSString *)method path:(NSString *)path;
-#pragma mark - Authentication
++ (id)userCatalogRequest:(OpenStackAccount *)account displaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs;
+- (NSDictionary *)catalogs;
+- (NSDictionary *)displaynameCatalog;
+- (NSDictionary *)UUIDCatalog;
-+ (OpenStackRequest *)authenticationRequest:(OpenStackAccount *)account;
-
-#pragma mark - Object Storage Requests
++ (id)authenticationRequest:(OpenStackAccount *)account;
++ (id)getSharingAccountsRequest:(OpenStackAccount *)account;
+- (NSArray *)sharingAccounts;
-+ (OpenStackRequest *)getStorageAccountInfoRequest:(OpenStackAccount *)account;
-+ (OpenStackRequest *)getContainerInfoRequest:(OpenStackAccount *)account container:(Container *)container;
-+ (OpenStackRequest *)getContainersRequest:(OpenStackAccount *)account;
++ (id)getStorageAccountInfoRequest:(OpenStackAccount *)account;
++ (id)getContainersRequest:(OpenStackAccount *)account;
- (NSMutableDictionary *)containers;
++ (id)writeAccountMetadataRequest:(OpenStackAccount *)account withAccountInfo:(NSDictionary *)accountInfo;
-+ (OpenStackRequest *)createContainerRequest:(OpenStackAccount *)account container:(Container *)container;
-+ (OpenStackRequest *)deleteContainerRequest:(OpenStackAccount *)account container:(Container *)container;
-
-+ (OpenStackRequest *)getObjectsRequest:(OpenStackAccount *)account container:(Container *)container;
++ (id)getContainerInfoRequest:(OpenStackAccount *)account container:(Container *)container;
++ (id)createContainerRequest:(OpenStackAccount *)account container:(Container *)container;
++ (id)deleteContainerRequest:(OpenStackAccount *)account container:(Container *)container;
++ (id)getObjectsRequest:(OpenStackAccount *)account container:(Container *)container;
- (NSMutableDictionary *)objects;
++ (id)writeContainerPolicyRequest:(OpenStackAccount *)account container:(Container *)container;
+
++ (id)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
++ (id)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object version:(NSString *)version;
++ (id)getObjectVersionsRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
+- (NSMutableArray *)versions;
++ (id)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
++ (id)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object version:(NSString *)version;
++ (id)writeObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
++ (id)writeObjectMetadataRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
++ (id)deleteObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
-+ (OpenStackRequest *)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
-+ (OpenStackRequest *)getObjectInfoRequest:(OpenStackAccount *)account
- container:(Container *)container
- object:(StorageObject *)object
- version:(NSString *)version;
-+ (OpenStackRequest *)getObjectVersionsRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
-+ (OpenStackRequest *)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
-+ (OpenStackRequest *)getObjectRequest:(OpenStackAccount *)account
- container:(Container *)container
- object:(StorageObject *)object
- version:(NSString *)version;
-+ (OpenStackRequest *)writeObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
-+ (OpenStackRequest *)writeObjectMetadataRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
-+ (OpenStackRequest *)deleteObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
-
-#pragma mark - Container Write Requests
-
-+ (OpenStackRequest *)writeContainerPolicyRequest:(OpenStackAccount *)account container:(Container *)container;
-
-#pragma mark - Account Write Requests
-
-+ (OpenStackRequest *)writeAccountMetadataRequest:(OpenStackAccount *)account withAccountInfo:(NSDictionary *)accountInfo;
+- (BOOL)isSuccess;
+- (void)notify;
+- (void)notify:(NSString *)name;
@end
#import "APILogEntry.h"
#import "NSString+Conveniences.h"
-static NSRecursiveLock *accessDetailsLock = nil;
-
@implementation OpenStackRequest
-@synthesize account, callback, retriedCount, errorAlerter;
-
-- (BOOL)isSuccess {
- return (200 <= [self responseStatusCode]) && ([self responseStatusCode] <= 299);
-}
-
-- (void)notify:(NSString *)name {
- NSDictionary *callbackUserInfo = [NSDictionary dictionaryWithObject:self forKey:@"response"];
- NSNotification *notification = [NSNotification notificationWithName:name object:nil userInfo:callbackUserInfo];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
-}
-
-- (void)notify {
- NSString *observeName = [NSString stringWithFormat:@"%@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, [self.url description]];
- NSString *callbackName = [NSString stringWithFormat:@"%@ %@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, [self.url description], self.callback.uuid];
-
- NSDictionary *callbackUserInfo = [NSDictionary dictionaryWithObject:self forKey:@"response"];
-
- NSNotification *observeNotification = [NSNotification notificationWithName:observeName object:nil userInfo:callbackUserInfo];
- [[NSNotificationCenter defaultCenter] postNotification:observeNotification];
-
- NSNotification *callbackNotification = [NSNotification notificationWithName:callbackName object:nil userInfo:callbackUserInfo];
- [[NSNotificationCenter defaultCenter] postNotification:callbackNotification];
-
-}
-
-- (NSString *)responseString {
- if (retried) {
- return [retriedRequest responseString];
- } else {
- return [super responseString];
- }
-}
-
-- (NSData *)responseData {
- if (retried) {
- return [retriedRequest responseData];
- } else {
- return [super responseData];
- }
-}
-
-- (NSDictionary *)responseHeaders {
- if (retried) {
- return [retriedRequest responseHeaders];
- } else {
- return [super responseHeaders];
- }
-}
-
-- (int)responseStatusCode {
- if (retried) {
- return [retriedRequest responseStatusCode];
- } else {
- return [super responseStatusCode];
- }
-}
-
-- (NSString *)responseStatusMessage {
- if (retried) {
- return [retriedRequest responseStatusMessage];
- } else {
- return [super responseStatusMessage];
- }
-}
-
-- (void)setCompletionBlock:(ASIBasicBlock)aCompletionBlock {
- [super setCompletionBlock:aCompletionBlock];
- [backupCompletionBlock release];
- backupCompletionBlock = [aCompletionBlock copy];
-}
-
-- (void)setFailedBlock:(ASIBasicBlock)aFailedBlock {
- [super setFailedBlock:aFailedBlock];
- [backupFailureBlock release];
- backupFailureBlock = [aFailedBlock copy];
-}
-
-#pragma mark -
-#pragma mark Generic Constructors
+@synthesize account, callback, errorAlerter;
-+ (void)initialize {
- if (self == [OpenStackRequest class]) {
- accessDetailsLock = [[NSRecursiveLock alloc] init];
- }
-}
+#pragma mark - Constructors
+#pragma mark Generic
+ (id)request:(OpenStackAccount *)account method:(NSString *)method url:(NSURL *)url {
- OpenStackRequest *request = [[[OpenStackRequest alloc] initWithURL:url] autorelease];
+ OpenStackRequest *request = [[[self alloc] initWithURL:url] autorelease];
request.account = account;
- [request setRequestMethod:method];
- [request addRequestHeader:@"X-Auth-Token" value:[account authToken]];
+ request.requestMethod = method;
+ [request addRequestHeader:@"X-Auth-Token" value:account.authToken];
[request addRequestHeader:@"Content-Type" value:@"application/json"];
- [request setTimeOutSeconds:60];
- [request setNumberOfTimesToRetryOnTimeout:5];
- request.retriedCount = 0;
+ request.timeOutSeconds = 60;
+ request.numberOfTimesToRetryOnTimeout = 5;
return request;
}
-+ (id)getSharingAccountsRequest:(OpenStackAccount *)account {
- NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?format=json", account.provider.authEndpointURL]];
- return [OpenStackRequest request:account method:@"GET" url:url];
-}
-
+ (id)filesRequest:(OpenStackAccount *)account method:(NSString *)method path:(NSString *)path {
NSString *urlString = [account.filesURL description];
if (account.sharingAccount) {
- NSRange authUserRange = [urlString rangeOfString:account.username];
- urlString = [NSString stringWithFormat:@"%@%@", [urlString substringToIndex:authUserRange.location], account.sharingAccount];
+ urlString = [NSString stringWithFormat:@"%@%@",
+ [urlString substringToIndex:[urlString rangeOfString:account.username].location],
+ account.sharingAccount];
}
-
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@?format=json%@",
urlString,
path,
- account.shared ? @"&shared=" : @""]];
-
- return [OpenStackRequest request:account method:method url:url];
-}
-
-#pragma mark -
-#pragma mark Auth Retry
-
-- (void)authRetrySucceded:(OpenStackRequest *)retryRequest {
- self.account.authToken = [[retryRequest responseHeaders] objectForKey:@"X-Auth-Token"];
- [self.account persist];
-
- // try the original request again!
- retried = YES;
- retriedRequest = [self copy];
-
- [retriedRequest addRequestHeader:@"X-Auth-Token" value:self.account.authToken];
-
- if (backupCompletionBlock) {
- [retriedRequest setCompletionBlock:^{
- backupCompletionBlock();
- }];
+ (account.shared ? @"&shared=" : @"")]];
+ return [self request:account method:method url:url];
+}
+
+#pragma mark User Catalog
++ (id)userCatalogRequest:(OpenStackAccount *)account displaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs {
+ OpenStackRequest *request = [self request:account method:@"POST" url:account.provider.userCatalogURL];
+ NSMutableString *dataString = [NSMutableString stringWithString:@"{\"displaynames\":["];
+ if (displaynames) {
+ for (NSUInteger index = 0 ; index < displaynames.count ; index++) {
+ [dataString appendFormat:@"\"%@\"%@",
+ [[[displaynames objectAtIndex:index] stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]
+ stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""],
+ ((index == displaynames.count - 1) ? @"" : @",")];
+ }
}
- if (backupFailureBlock) {
- [retriedRequest setFailedBlock:^{
- backupFailureBlock();
- }];
+ [dataString appendFormat:@"],\"uuids\":["];
+ if (UUIDs) {
+ for (NSUInteger index = 0 ; index < UUIDs.count ; index++) {
+ [dataString appendFormat:@"\"%@\"%@", [UUIDs objectAtIndex:index], ((index == UUIDs.count - 1) ? @"" : @",")];
+ }
}
-
- [retriedRequest startSynchronous];
+ [dataString appendFormat:@"]}"];
+ [request appendPostData:[dataString dataUsingEncoding:NSUTF8StringEncoding]];
+ return request;
}
-- (void)authRetryFailed:(OpenStackRequest *)retryRequest {
- // if it fails due to bad connection, try again?
- NSNotification *notification = [NSNotification notificationWithName:[self.account.manager notificationName:@"authRetryFailed" identifier:0] object:nil userInfo:[NSDictionary dictionaryWithObject:retryRequest forKey:@"request"]];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
+- (NSDictionary *)catalogs {
+ SBJSON *parser = [[[SBJSON alloc] init] autorelease];
+ NSDictionary *catalogs = [parser objectWithString:[self responseString]];
+ return catalogs;
}
-#pragma mark -
-#pragma mark ASIHTTPRequest Overrides
-
-- (void)failWithError:(NSError *)theError {
- if (responseStatusCode == 401 && ![url isEqual:account.provider.authEndpointURL]) {
- // auth is expired, so get a fresh token
- if (account && ![account.provider isGRNet]) {
- OpenStackRequest *retryRequest = [OpenStackRequest authenticationRequest:account];
- retryRequest.delegate = self;
- retryRequest.didFinishSelector = @selector(authRetrySucceded:);
- retryRequest.didFailSelector = @selector(authRetryFailed:);
- [retryRequest startSynchronous];
- }
- } else if (responseStatusCode == 503) {
- NSNotification *notification = [NSNotification notificationWithName:@"serviceUnavailable" object:nil userInfo:nil];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
-// [super failWithError:theError];
- } else if (responseStatusCode == 0) {
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- if (![defaults boolForKey:@"already_failed_on_connection"]) {
- UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Connection Error" message:@"Please check your connection or API URL and try again." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
- [alert show];
- [alert release];
- }
- [defaults setBool:YES forKey:@"already_failed_on_connection"];
- [defaults synchronize];
- }
-
- [super failWithError:theError];
+- (NSDictionary *)displaynameCatalog {
+ return [[self catalogs] objectForKey:@"displayname_catalog"];
}
-#pragma mark - Authentication
+- (NSDictionary *)UUIDCatalog {
+ return [[self catalogs] objectForKey:@"uuid_catalog"];
+}
-+ (OpenStackRequest *)authenticationRequest:(OpenStackAccount *)account {
+#pragma mark Top
- OpenStackRequest *request = [[[OpenStackRequest alloc] initWithURL:account.provider.authEndpointURL] autorelease];
++ (id)authenticationRequest:(OpenStackAccount *)account {
+ OpenStackRequest *request = [[[self alloc] initWithURL:account.provider.authEndpointURL] autorelease];
request.account = account;
+ request.requestMethod = @"GET";
[request addRequestHeader:@"X-Auth-User" value:account.username];
- if (account.authToken) {
- [request addRequestHeader:@"X-Auth-Token" value:account.authToken];
- } else {
- [request addRequestHeader:@"X-Auth-Token" value:@""];
- }
-
+ [request addRequestHeader:@"X-Auth-Token" value:(account.authToken ? account.authToken : @"")];
+ request.timeOutSeconds = 60;
+ request.numberOfTimesToRetryOnTimeout = 5;
return request;
}
-#pragma mark -
-#pragma mark Object Storage Requests
++ (id)getSharingAccountsRequest:(OpenStackAccount *)account {
+ NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?format=json", account.provider.authEndpointURL]];
+ return [self request:account method:@"GET" url:url];
+}
+
+- (NSArray *)sharingAccounts {
+ SBJSON *parser = [[[SBJSON alloc] init] autorelease];
+ NSArray *sharingAccounts = [parser objectWithString:[self responseString]];
+ return sharingAccounts;
+}
+
+#pragma mark Account
-+ (OpenStackRequest *)getStorageAccountInfoRequest:(OpenStackAccount *)account {
- return [OpenStackRequest filesRequest:account method:@"HEAD" path:@""];
++ (id)getStorageAccountInfoRequest:(OpenStackAccount *)account {
+ return [self filesRequest:account method:@"HEAD" path:@""];
}
-+ (OpenStackRequest *)getContainersRequest:(OpenStackAccount *)account {
- return [OpenStackRequest filesRequest:account method:@"GET" path:@""];
++ (id)getContainersRequest:(OpenStackAccount *)account {
+ return [self filesRequest:account method:@"GET" path:@""];
}
- (NSMutableDictionary *)containers {
- SBJSON *parser = [[SBJSON alloc] init];
+ SBJSON *parser = [[[SBJSON alloc] init] autorelease];
NSArray *jsonObjects = [parser objectWithString:[self responseString]];
- NSMutableDictionary *objects = [NSMutableDictionary dictionaryWithCapacity:[jsonObjects count]];
-
- for (int i = 0; i < [jsonObjects count]; i++) {
- NSDictionary *dict = [jsonObjects objectAtIndex:i];
+ NSMutableDictionary *containers = [NSMutableDictionary dictionaryWithCapacity:[jsonObjects count]];
+ for (NSDictionary *dict in jsonObjects) {
Container *container = [Container fromJSON:dict];
- [objects setObject:container forKey:container.name];
+ [containers setObject:container forKey:container.name];
+ }
+ return containers;
+}
+
++ (id)writeAccountMetadataRequest:(OpenStackAccount *)account withAccountInfo:(NSDictionary *)accountInfo {
+ OpenStackRequest *request = [self filesRequest:account method:@"POST" path:@""];
+
+ NSMutableDictionary *groups = [accountInfo objectForKey:@"groups"];
+ if (groups.count) {
+ for (NSString *groupName in groups) {
+ [request.requestHeaders setObject:[NSString encodeToPercentEscape:[groups objectForKey:groupName]]
+ forKey:[NSString stringWithFormat:@"X-Account-Group-%@", [NSString encodeToPercentEscape:groupName]]];
+ }
+ } else {
+ [request.requestHeaders setObject:@"" forKey:@"X-Account-Group-group"];
}
- [parser release];
- return objects;
+ NSMutableDictionary *metadata = [accountInfo objectForKey:@"metadata"];
+ for (NSString *key in metadata) {
+ [request.requestHeaders setObject:[NSString encodeToPercentEscape:[metadata objectForKey:key]]
+ forKey:[NSString stringWithFormat:@"X-Account-Meta-%@", [NSString encodeToPercentEscape:key]]];
+ }
+
+ return request;
+}
+
+#pragma mark Container
+
++ (id)getContainerInfoRequest:(OpenStackAccount *)account container:(Container *)container {
+ return [self filesRequest:account method:@"HEAD" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
}
-+ (OpenStackRequest *)createContainerRequest:(OpenStackAccount *)account container:(Container *)container {
- return [OpenStackRequest filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
++ (id)createContainerRequest:(OpenStackAccount *)account container:(Container *)container {
+ return [self filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
}
-+ (OpenStackRequest *)deleteContainerRequest:(OpenStackAccount *)account container:(Container *)container {
- return [OpenStackRequest filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
++ (id)deleteContainerRequest:(OpenStackAccount *)account container:(Container *)container {
+ return [self filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
}
-+ (OpenStackRequest *)getObjectsRequest:(OpenStackAccount *)account container:(Container *)container {
- return [OpenStackRequest filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
++ (id)getObjectsRequest:(OpenStackAccount *)account container:(Container *)container {
+ return [self filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
}
- (NSMutableDictionary *)objects {
- SBJSON *parser = [[SBJSON alloc] init];
+ SBJSON *parser = [[[SBJSON alloc] init] autorelease];
NSArray *jsonObjects = [parser objectWithString:[self responseString]];
-
- NSMutableDictionary *objects = [[[NSMutableDictionary alloc] initWithCapacity:[jsonObjects count]] autorelease];
-
- for (int i = 0; i < [jsonObjects count]; i++) {
- NSDictionary *dict = [jsonObjects objectAtIndex:i];
+ NSMutableDictionary *objects = [NSMutableDictionary dictionaryWithCapacity:[jsonObjects count]];
+ for (NSDictionary *dict in jsonObjects) {
StorageObject *object = [StorageObject fromJSON:dict];
[objects setObject:object forKey:object.name];
}
-
- [parser release];
return objects;
}
-+ (OpenStackRequest *)getContainerInfoRequest:(OpenStackAccount *)account container:(Container *)container {
- return [OpenStackRequest filesRequest:account method:@"HEAD" path:[NSString stringWithFormat:@"/%@",[NSString encodeToPercentEscape:container.name]]];
++ (id)writeContainerPolicyRequest:(OpenStackAccount *)account container:(Container *)container {
+ OpenStackRequest *request = [self filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
+ [request.requestHeaders setObject:container.versioning forKey:@"X-Container-Policy-Versioning"];
+ [request.requestHeaders setObject:[NSString stringWithFormat:@"%u", container.quota] forKey:@"X-Container-Policy-Quota"];
+ return request;
}
-+ (OpenStackRequest *)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
+#pragma mark Storage Object
+
++ (id)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
NSString *objectFullPath = object.fullPath;
if ([objectFullPath hasPrefix:@"/"])
objectFullPath = [objectFullPath substringFromIndex:1];
- return [OpenStackRequest filesRequest:account method:@"HEAD" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
+ return [self filesRequest:account method:@"HEAD" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
}
-+ (OpenStackRequest *)getObjectInfoRequest:(OpenStackAccount *)account
- container:(Container *)container
- object:(StorageObject *)object
- version:(NSString *)version {
- OpenStackRequest *request = [OpenStackRequest getObjectInfoRequest:account container:container object:object];
- request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=%@",request.url.description, version]];
++ (id)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object version:(NSString *)version {
+ OpenStackRequest *request = [self getObjectInfoRequest:account container:container object:object];
+ request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=%@", request.url.description, version]];
return request;
}
-+ (OpenStackRequest *)getObjectVersionsRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
- OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
-
- request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=list",request.url.description]];
++ (id)getObjectVersionsRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
+ OpenStackRequest *request = [self filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
+ request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=list", request.url.description]];
return request;
}
-+ (OpenStackRequest *)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
- return [OpenStackRequest filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
+- (NSMutableArray *)versions {
+ SBJSON *parser = [[[SBJSON alloc] init] autorelease];
+ NSArray *jsonVersions = [[parser objectWithString:[self responseString]] objectForKey:@"versions"];
+ NSMutableArray *versions = [NSMutableArray arrayWithCapacity:[jsonVersions count]];
+ for (NSArray *jsonVersion in jsonVersions) {
+ [versions addObject:[NSDictionary dictionaryWithObjectsAndKeys:
+ [jsonVersion objectAtIndex:0], @"versionID",
+ [NSDate dateWithTimeIntervalSince1970:[[jsonVersion objectAtIndex:1] doubleValue]], @"versionDate",
+ nil]];
+ }
+ return versions;
}
-+ (OpenStackRequest *)getObjectRequest:(OpenStackAccount *)account
- container:(Container *)container
- object:(StorageObject *)object
- version:(NSString *)version {
- OpenStackRequest *request = [OpenStackRequest getObjectRequest:account container:container object:object];
- if (version) {
- request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=%@",request.url.description, version]];
- }
+
++ (id)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
+ return [self filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
+}
+
++ (id)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object version:(NSString *)version {
+ OpenStackRequest *request = [self getObjectRequest:account container:container object:object];
+ if (version)
+ request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=%@", request.url.description, version]];
return request;
}
-+ (OpenStackRequest *)writeObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
- NSString *fullPath = object.fullPath;
- if ([fullPath characterAtIndex:0] == '/') {
- fullPath = [fullPath substringFromIndex:1];
- }
-
- OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
-
++ (id)writeObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
+ NSString *objectFullPath = object.fullPath;
+ if ([objectFullPath hasPrefix:@"/"])
+ objectFullPath = [objectFullPath substringFromIndex:1];
+ OpenStackRequest *request = [self filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
+
if (object.sharing)
[request.requestHeaders setObject:object.sharing forKey:@"X-Object-Sharing"];
-
+
NSString *metadataKeyHeaderPrefix;
- if ([fullPath length] == 0)
+ if ([objectFullPath length] == 0)
metadataKeyHeaderPrefix = @"X-Container-Meta-";
else
metadataKeyHeaderPrefix = @"X-Object-Meta-";
-
for (NSString *metadataKey in object.metadata) {
- NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey];
+ NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey];
metadataKeyHeader = [NSString encodeToPercentEscape:metadataKeyHeader];
NSString *metadataValue = [NSString encodeToPercentEscape:[object.metadata objectForKey:metadataKey]];
[request.requestHeaders setObject:metadataValue forKey:metadataKeyHeader];
}
-
+
[request setPostBody:[NSMutableData dataWithData:object.data]];
- [request.requestHeaders setObject:object.contentType forKey:@"Content-Type"];
+ [request.requestHeaders setObject:object.contentType forKey:@"Content-Type"];
return request;
}
-+ (OpenStackRequest *)writeObjectMetadataRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
- NSString *fullPath = object.fullPath;
- if ([fullPath length] != 0 && [fullPath characterAtIndex:0] == '/') {
- fullPath = [fullPath substringFromIndex:1];
- }
-
++ (id)writeObjectMetadataRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
+ NSString *objectFullPath = object.fullPath;
+ if ([objectFullPath hasPrefix:@"/"])
+ objectFullPath = [objectFullPath substringFromIndex:1];
+ OpenStackRequest *request = [self filesRequest:account method:@"POST" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
+
NSString *metadataKeyHeaderPrefix;
- if ([fullPath length] == 0)
+ if ([objectFullPath length] == 0)
metadataKeyHeaderPrefix = @"X-Container-Meta-";
else
metadataKeyHeaderPrefix = @"X-Object-Meta-";
-
- OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"POST" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
-
for (NSString *metadataKey in object.metadata) {
- NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey];
+ NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey];
metadataKeyHeader = [NSString encodeToPercentEscape:metadataKeyHeader];
NSString *metadataValue = [NSString encodeToPercentEscape:[object.metadata objectForKey:metadataKey]];
[request.requestHeaders setObject:metadataValue forKey:metadataKeyHeader];
}
- if (!account.sharingAccount) {
- NSString *objectIsPublic = ([object.publicURI length] > 0) ? @"true" : @"false";
- [request.requestHeaders setObject:objectIsPublic forKey:@"X-Object-Public"];
+ if (!account.sharingAccount) {
+ [request.requestHeaders setObject:([object.publicURI length] ? @"true" : @"false") forKey:@"X-Object-Public"];
if (object.sharing) {
- NSString *urlEncodedSharingString = [NSString encodeToPercentEscape:object.sharing];
- [request.requestHeaders setObject:urlEncodedSharingString forKey:@"X-Object-Sharing"];
+ [request.requestHeaders setObject:[NSString encodeToPercentEscape:object.sharing] forKey:@"X-Object-Sharing"];
}
}
return request;
}
-+ (OpenStackRequest *)deleteObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
- if ([object.fullPath characterAtIndex:0] == '/') {
- return [OpenStackRequest filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
- } else {
- return [OpenStackRequest filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
- }
++ (id)deleteObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
+ NSString *objectFullPath = object.fullPath;
+ if ([objectFullPath hasPrefix:@"/"])
+ objectFullPath = [objectFullPath substringFromIndex:1];
+ return [self filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
}
-+ (OpenStackRequest *)writeContainerPolicyRequest:(OpenStackAccount *)account container:(Container *)container {
- OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
-
- [request.requestHeaders setObject:container.versioning forKey:@"X-Container-Policy-Versioning"];
- [request.requestHeaders setObject:[NSString stringWithFormat:@"%u", container.quota] forKey:@"X-Container-Policy-Quota"];
-
- return request;
+#pragma mark - ASIHTTPRequest Overrides
+
+- (void)setCompletionBlock:(ASIBasicBlock)aCompletionBlock {
+ [super setCompletionBlock:aCompletionBlock];
+ [backupCompletionBlock release];
+ backupCompletionBlock = [aCompletionBlock copy];
}
-+ (OpenStackRequest *)writeAccountMetadataRequest:(OpenStackAccount *)account withAccountInfo:(NSDictionary *)accountInfo {
- OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"POST" path:@""];
-
- NSMutableDictionary *groups = [accountInfo objectForKey:@"groups"];
- for (NSString *groupName in groups) {
- NSString *group = [NSString encodeToPercentEscape:[groups objectForKey:groupName]];
- groupName = [NSString encodeToPercentEscape:groupName];
- [request.requestHeaders setObject:group forKey:[NSString stringWithFormat:@"X-Account-Group-%@", groupName]];
+- (void)setFailedBlock:(ASIBasicBlock)aFailedBlock {
+ [super setFailedBlock:aFailedBlock];
+ [backupFailureBlock release];
+ backupFailureBlock = [aFailedBlock copy];
+}
+
+- (void)failWithError:(NSError *)theError {
+ if (responseStatusCode == 503) {
+ NSNotification *notification = [NSNotification notificationWithName:@"serviceUnavailable" object:nil userInfo:nil];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+ } else if (responseStatusCode == 0) {
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ if (![defaults boolForKey:@"already_failed_on_connection"]) {
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Connection Error" message:@"Please check your connection or API URL and try again." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
+ [alert show];
+ [alert release];
+ }
+ [defaults setBool:YES forKey:@"already_failed_on_connection"];
+ [defaults synchronize];
}
- if ([groups count] == 0)
- [request.requestHeaders setObject:@"" forKey:@"X-Account-Group-group"];
- NSMutableDictionary *accountMetadata = [accountInfo objectForKey:@"metadata"];
- for (NSString *metadataKey in accountMetadata) {
- NSString *metadataValue = [NSString encodeToPercentEscape:[accountMetadata objectForKey:metadataKey]];
- metadataKey = [NSString encodeToPercentEscape:[accountMetadata objectForKey:metadataKey]];
- [request.requestHeaders setObject:metadataValue forKey:[NSString stringWithFormat:@"X-Account-Meta-%@",metadataKey]];
- }
+ [super failWithError:theError];
+}
+
+#pragma mark - Actions
+
+- (BOOL)isSuccess {
+ return (200 <= [self responseStatusCode]) && ([self responseStatusCode] <= 299);
+}
+
+#pragma mark - Notifications
+
+- (void)notify:(NSString *)name {
+ NSDictionary *callbackUserInfo = [NSDictionary dictionaryWithObject:self forKey:@"response"];
+ NSNotification *notification = [NSNotification notificationWithName:name object:nil userInfo:callbackUserInfo];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+}
+
+- (void)notify {
+ NSString *observeName = [NSString stringWithFormat:@"%@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, [self.url description]];
+ NSString *callbackName = [NSString stringWithFormat:@"%@ %@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, [self.url description], self.callback.uuid];
+
+ NSDictionary *callbackUserInfo = [NSDictionary dictionaryWithObject:self forKey:@"response"];
+
+ NSNotification *observeNotification = [NSNotification notificationWithName:observeName object:nil userInfo:callbackUserInfo];
+ [[NSNotificationCenter defaultCenter] postNotification:observeNotification];
+
+ NSNotification *callbackNotification = [NSNotification notificationWithName:callbackName object:nil userInfo:callbackUserInfo];
+ [[NSNotificationCenter defaultCenter] postNotification:callbackNotification];
- return request;
}
-#pragma mark -
-#pragma mark Memory Management
+#pragma mark - Memory Management
- (void)releaseBackupBlocksOnMainThread {
NSMutableArray *blocks = [NSMutableArray array];
- (void)hideToolbarActivityMessage;
- (void)hideToolbarInfoMessage;
-- (void)addHomeButton;
-
@end
}
- (void)viewDidLoad {
+ [super viewDidLoad];
if (self.navigationController.navigationBar) {
if ([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad) {
self.toolbar.tintColor = self.navigationController.navigationBar.tintColor;
self.toolbar.opaque = NO;
}
}
-
- toolbarProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
- toolbarProgressView.frame = CGRectMake(0.0, 24.0, 185.0, 10.0);
}
#pragma mark - Internal
UIView *labelWithProgress = [[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 185.0, 40.0)] autorelease];
[labelWithProgress addSubview:toolbarLabel];
+ if (!toolbarProgressView) {
+ toolbarProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
+ toolbarProgressView.frame = CGRectMake(0.0, 24.0, 185.0, 10.0);
+ }
[labelWithProgress addSubview:toolbarProgressView];
toolbarLabelItem = [[UIBarButtonItem alloc] initWithCustomView:labelWithProgress];
}
}
-- (void)addHomeButton {
- UIImage *buttonImage = [UIImage imageNamed:@"HomeFolderIcon.png"];
-
- UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
- [button setImage:buttonImage forState:UIControlStateNormal];
- button.frame = CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height);
- [button addTarget:self action:@selector(homeButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
- UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:button];
- NSMutableArray *toolbarItems = [NSMutableArray arrayWithArray:self.toolbar.items];
- [toolbarItems addObject:customBarItem];
- self.toolbar.items = toolbarItems;
- [customBarItem release];
-}
-
- (void)dealloc {
- if (toolbarActivityMessageVisible)
- [self hideToolbarActivityMessage];
- if (toolbarInfoMessageVisible)
- [self hideToolbarInfoMessage];
+ [self hideToolbarActivityMessage];
+ [self hideToolbarInfoMessage];
[toolbarProgressView release];
[super dealloc];
}
// The OpenStack project is provided under the Apache 2.0 license.
//
-#import <Foundation/Foundation.h>
-
@interface Provider : NSObject <NSCoding> {
- // the name of the provider (example: okeanos)
NSString *name;
-
- // endpoint for authentication (example: https://pithos.okeanos.grnet.gr )
- NSURL *authEndpointURL;
+ NSURL *hostURL;
+ NSString *version;
}
+ (NSArray *)providers;
-- (BOOL)isGRNet;
@property (nonatomic, retain) NSString *name;
-@property (nonatomic, retain) NSURL *authEndpointURL;
+@property (nonatomic, retain) NSURL *hostURL;
+@property (nonatomic, retain) NSString *version;
+
+@property (nonatomic, readonly) NSURL *authEndpointURL;
+@property (nonatomic, readonly) NSURL *loginURL;
+@property (nonatomic, readonly) NSURL *userCatalogURL;
+@property (nonatomic, readonly) NSURL *publicLinkURLPrefix;
@end
@implementation Provider
-@synthesize name, authEndpointURL;
-
-+ (void)initialize {
- Provider *provider = [[Provider alloc] init];
- provider.name = @"okeanos";
- provider.authEndpointURL = [NSURL URLWithString:@"https://pithos.okeanos.grnet.gr/v1"];
- providers = [[NSArray alloc] initWithObjects:provider, nil];
- [provider release];
-}
+@synthesize name, hostURL, version, authEndpointURL, loginURL, userCatalogURL, publicLinkURLPrefix;
+ (NSArray *)providers {
if (providers == nil) {
Provider *provider = [[Provider alloc] init];
provider.name = @"okeanos";
- provider.authEndpointURL = [NSURL URLWithString:@"https://pithos.okeanos.grnet.gr/v1"];
+ provider.hostURL = [NSURL URLWithString:@"https://pithos.okeanos.grnet.gr"];
+ provider.version = @"v1";
providers = [[NSArray alloc] initWithObjects:provider, nil];
[provider release];
}
#pragma mark - Serialization
-- (void)encodeWithCoder: (NSCoder *)coder {
+- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:name forKey:@"name"];
- [coder encodeObject:authEndpointURL forKey:@"authEndpointURL"];
+ [coder encodeObject:hostURL forKey:@"hostURL"];
+ [coder encodeObject:version forKey:@"version"];
}
- (id)initWithCoder:(NSCoder *)coder {
if ((self = [super init])) {
- name = [[coder decodeObjectForKey:@"name"] retain];
- authEndpointURL = [[coder decodeObjectForKey:@"authEndpointURL"] retain];
+ self.name = [coder decodeObjectForKey:@"name"];
+ self.hostURL = [coder decodeObjectForKey:@"hostURL"];
+ self.version = [coder decodeObjectForKey:@"version"];
+
+ // Support for older versions.
+ if (!hostURL) {
+ NSURL *tmpURL = [coder decodeObjectForKey:@"authEndpointURL"];
+ if (tmpURL) {
+ self.hostURL = [tmpURL URLByDeletingLastPathComponent];
+ } else {
+ self.hostURL = [NSURL URLWithString:@"https://pithos.okeanos.grnet.gr"];
+ }
+ }
+ if (!version) {
+ self.version = @"v1";
+ }
}
return self;
}
-#pragma mark - HTTP Logo Requests
-
-- (BOOL)isGRNet {
- return ([self.authEndpointURL.host rangeOfString:@"grnet.gr"].location != NSNotFound);
-}
-
#pragma mark - Memory Management
- (void)dealloc {
[name release];
- [authEndpointURL release];
+ [hostURL release];
+ [version release];
[super dealloc];
}
+#pragma mark - Properties
+
+- (NSURL *)authEndpointURL {
+ return [hostURL URLByAppendingPathComponent:version];
+}
+
+- (NSURL *)loginURL {
+ return [hostURL URLByAppendingPathComponent:@"login"];
+}
+
+- (NSURL *)userCatalogURL {
+ return [hostURL URLByAppendingPathComponent:@"user_catalogs"];
+}
+
+- (NSURL *)publicLinkURLPrefix {
+ return hostURL;
+}
+
@end
// Since most people will just have one account this is preferred.
[self presentAccountHomeViewControllerForAccount:[[OpenStackAccount accounts] objectAtIndex:0] animated:NO];
AccountHomeViewController *ahvc = [[self.navigationController viewControllers] lastObject];
- ahvc.navigationItem.title = ahvc.account.username;
+ ahvc.navigationItem.title = [ahvc.account displaynameForUUID:ahvc.account.username safe:YES];
[ahvc presentContainersViewControllerShared:NO Animated:NO];
} else {
// If there are no accounts, go straight to the add account screen on launch.
// Configure the cell.
OpenStackAccount *account = [[OpenStackAccount accounts] objectAtIndex:indexPath.row];
- cell.textLabel.text = account.username;
+ cell.textLabel.text = [account displaynameForUUID:account.username safe:YES];
cell.detailTextLabel.text = account.provider.name;
cell.imageView.image = [UIImage imageNamed:@"pithos-solo-smallest.png"];
@property (nonatomic, assign) AccountHomeViewController *accountHomeViewController;
- (IBAction)refreshButtonPressed:(id)sender;
+- (IBAction)homeButtonPressed:(id)sender;
@end
#import "OpenStackAccount.h"
#import "AccountManager.h"
#import "ActivityIndicatorView.h"
-#import "SBJSON.h"
#import "AccountHomeViewController.h"
#import "APICallback.h"
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @"Sharing Accounts";
- [self addHomeButton];
sharingAccounts = [[NSMutableArray alloc] init];
}
#pragma mark - Button Handlers
-- (IBAction)homeButtonPressed:(id)sender {
- [self.navigationController popToViewController:accountHomeViewController animated:YES];
-}
-
-- (IBAction)refreshButtonPressed:(id)sender {
+- (void)refreshButtonPressed:(id)sender {
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading..."
andAddToView:self.view];
[[self.account.manager getSharingAccounts]
success:^(OpenStackRequest *request) {
- SBJSON *parser = [[SBJSON alloc] init];
- NSArray *jsonObjects = [parser objectWithString:[request responseString]];
-
[sharingAccounts removeAllObjects];
- for (int i = 0; i < [jsonObjects count]; i++) {
- NSDictionary *dict = [jsonObjects objectAtIndex:i];
+ for (NSDictionary *dict in [request sharingAccounts]) {
[sharingAccounts addObject:[dict objectForKey:@"name"]];
}
- [parser release];
- contentsLoaded = YES;
-
- [activityIndicatorView removeFromSuperview];
- [self.tableView reloadData];
+ [[self.account.manager userCatalogForDisplaynames:nil
+ UUIDs:sharingAccounts]
+ success:^(OpenStackRequest *request) {
+ contentsLoaded = YES;
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView reloadData];
+ }
+ failure:^(OpenStackRequest *request) {
+ contentsLoaded = YES;
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView reloadData];
+ }];
}
failure:^(OpenStackRequest *request) {
contentsLoaded = NO;
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
if (request.responseStatusCode != 0) {
[self alert:@"There was a problem loading sharing accounts." request:request addAccountSettingsButton:YES];
}
}];
}
+- (void)homeButtonPressed:(id)sender {
+ [self.navigationController popToViewController:accountHomeViewController animated:YES];
+}
+
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
- cell.textLabel.text = [sharingAccounts objectAtIndex:indexPath.row];
+ cell.textLabel.text = [account displaynameForUUID:[sharingAccounts objectAtIndex:indexPath.row] safe:YES];
cell.imageView.image = [UIImage imageNamed:@"myShared.png"];
return cell;
<reference key="IBUIToolbar" ref="606510169"/>
<int key="IBUISystemItemIdentifier">5</int>
</object>
+ <object class="IBUIBarButtonItem" id="1072651937">
+ <object class="NSCustomResource" key="IBUIImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">HomeFolderIcon.png</string>
+ </object>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <float key="IBUIWidth">33</float>
+ <reference key="IBUIToolbar" ref="606510169"/>
+ </object>
</array>
</object>
</array>
</object>
<int key="connectionID">12</int>
</object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchEventConnection" key="connection">
+ <string key="label">homeButtonPressed:</string>
+ <reference key="source" ref="1072651937"/>
+ <reference key="destination" ref="372490531"/>
+ </object>
+ <int key="connectionID">15</int>
+ </object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<array class="NSMutableArray" key="children">
<reference ref="856980934"/>
<reference ref="458687891"/>
+ <reference ref="1072651937"/>
</array>
<reference key="parent" ref="191373211"/>
</object>
<reference key="object" ref="458687891"/>
<reference key="parent" ref="606510169"/>
</object>
+ <object class="IBObjectRecord">
+ <int key="objectID">14</int>
+ <reference key="object" ref="1072651937"/>
+ <reference key="parent" ref="606510169"/>
+ </object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="13.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="14.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="4.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="5.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="6.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
- <int key="maxID">13</int>
+ <int key="maxID">15</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">SharingAccountsViewController</string>
<string key="superclassName">OpenStackViewController</string>
- <object class="NSMutableDictionary" key="actions">
- <string key="NS.key.0">refreshButtonPressed:</string>
- <string key="NS.object.0">id</string>
- </object>
- <object class="NSMutableDictionary" key="actionInfosByName">
- <string key="NS.key.0">refreshButtonPressed:</string>
- <object class="IBActionInfo" key="NS.object.0">
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="homeButtonPressed:">id</string>
+ <string key="refreshButtonPressed:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="homeButtonPressed:">
+ <string key="name">homeButtonPressed:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="refreshButtonPressed:">
<string key="name">refreshButtonPressed:</string>
<string key="candidateClassName">id</string>
</object>
- </object>
+ </dictionary>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">tableView</string>
<string key="NS.object.0">UITableView</string>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+ <string key="NS.key.0">HomeFolderIcon.png</string>
+ <string key="NS.object.0">{22, 22}</string>
+ </object>
<string key="IBCocoaTouchPluginVersion">1929</string>
</data>
</archive>
- (void)setPropertiesfromResponseHeaders:(NSDictionary *)headers;
- (NSString *)humanizedBytes;
- (BOOL)isPlayableMedia;
+- (NSMutableDictionary *)permissions;
+- (void)setPermissions:(NSMutableDictionary *)permissions;
@end
self.hash = [headers objectForKey:@"X-Object-Hash"];
self.bytes = [[headers objectForKey:@"Content-Length"] intValue];
self.contentType = [headers objectForKey:@"Content-Type"];
- self.lastModified = [headers objectForKey:@"Last-Modified"];
+ self.lastModified = [ComputeModel dateFromRFC1123String:[headers objectForKey:@"Last-Modified"]];
self.publicURI = [headers objectForKey:@"X-Object-Public"];
self.sharing = [headers objectForKey:@"X-Object-Sharing"];
return matches > 0;
}
+- (NSMutableDictionary *)permissions {
+ NSMutableDictionary *permissions = [NSMutableDictionary dictionary];
+ if (self.sharing.length) {
+ for (NSString *sharingList in [self.sharing componentsSeparatedByString:@";"]) {
+ NSArray *components = [sharingList componentsSeparatedByString:@"="];
+ NSString *type = [components objectAtIndex:0];
+ if ([type hasPrefix:@" "]) {
+ type = [type substringFromIndex:1];
+ }
+ for (NSString *user in [[components objectAtIndex:1] componentsSeparatedByString:@","]) {
+ [permissions setObject:type forKey:user];
+ }
+ }
+ }
+ return permissions;
+}
+
+- (void)setPermissions:(NSMutableDictionary *)permissions {
+ NSMutableString *readSharingList = nil;
+ NSMutableString *writeSharingList = nil;
+ for (NSString *user in permissions) {
+ if ([[permissions objectForKey:user] isEqual:@"read"]) {
+ if (!readSharingList) {
+ readSharingList = [NSMutableString stringWithFormat:@"read=%@", user];
+ } else {
+ [readSharingList appendFormat:@",%@", user];
+ }
+ } else if ([[permissions objectForKey:user] isEqual:@"write"]) {
+ if (!writeSharingList) {
+ writeSharingList = [NSMutableString stringWithFormat:@"write=%@", user];
+ } else {
+ [writeSharingList appendFormat:@",%@", user];
+ }
+ }
+ }
+ if (readSharingList) {
+ if (writeSharingList) {
+ self.sharing = [NSString stringWithFormat:@"%@;%@", readSharingList, writeSharingList];
+ } else {
+ self.sharing = readSharingList;
+ }
+ } else if (writeSharingList) {
+ self.sharing = writeSharingList;
+ } else {
+ self.sharing = @"";
+ }
+}
+
@end
@property (nonatomic, retain) Folder *folder;
@property (nonatomic, retain) StorageObject *object;
@property (nonatomic, retain) FolderViewController *folderViewController;
-@property (nonatomic, retain) NSString *oldPubicURI;
+@property (nonatomic, retain) NSString *oldPublicURI;
+@property (nonatomic, retain) NSMutableDictionary *permissions;
@property (nonatomic, retain) UIDocumentInteractionController *documentInteractionController;
@property (nonatomic, assign) BOOL objectIsReadOnly;
@property (nonatomic, retain) NSString *versionID;
@implementation StorageObjectViewController
@synthesize account, container, folder, object, folderViewController;
-@synthesize oldPubicURI, documentInteractionController, objectIsReadOnly, versionID;
+@synthesize oldPublicURI, permissions, documentInteractionController, objectIsReadOnly, versionID;
#pragma mark - View lifecycle
objectIsPublicSwitch = [[UISwitch alloc] init];
[objectIsPublicSwitch addTarget:self action:@selector(objectIsPublicSwitchChanged:) forControlEvents:UIControlEventValueChanged];
- permissions = [[NSMutableDictionary alloc] init];
- if (object.sharing.length > 0) {
- NSArray *sharingArray = [object.sharing componentsSeparatedByString:@";"];
- for (NSString *typeSpecificPermissions in sharingArray) {
- NSArray *array=[typeSpecificPermissions componentsSeparatedByString:@"="];
- NSString *permissionsType = [array objectAtIndex:0];
- if ([permissionsType hasPrefix:@" "])
- permissionsType = [permissionsType substringFromIndex:1];
-
- NSArray *users = [[array objectAtIndex:1] componentsSeparatedByString:@","];
- for (NSString *user in users) {
- [permissions setObject:permissionsType forKey:user];
- }
- }
- }
+ self.permissions = [object permissions];
objectIsReadOnly = NO;
if (account.sharingAccount) {
- if ([permissions count] > 0) {
- objectIsReadOnly = [[permissions objectForKey:[account username]] isEqualToString:@"read"];
+ if (permissions.count) {
+ objectIsReadOnly = ![[permissions objectForKey:account.username] isEqualToString:@"write"];
}
objectIsPublicSwitch.enabled = NO;
}
}
- (void)viewDidAppear:(BOOL)animated {
- if (object.metadata == nil) {
+ [super viewDidAppear:animated];
+ if (!object.metadata) {
[self reloadMetadataSection];
+ } else {
+ [self updatePermissionsUserCatalog];
}
if (fileDownloading) {
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:kActions]
[downloadProgressView release];
[cdnURLActionSheet release];
[folderViewController release];
- [objectIsPublicSwitch release];
+ [oldPublicURI release];
[permissions release];
+ [objectIsPublicSwitch release];
[documentInteractionController release];
[versionID release];
[super dealloc];
- (void)objectIsPublicSwitchChanged:(id)sender {
NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."];
- self.oldPubicURI = object.publicURI;
+ self.oldPublicURI = object.publicURI;
if (objectIsPublic) {
activityMessage = [NSString stringWithFormat:@"Disabling public link.."];
if (objectIsPublic) {
[[self.account.manager getObjectInfo:container object:object version:versionID]
success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"];
NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
withRowAnimation:UITableViewRowAnimationBottom];
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self alert:@"There was a problem retrieving the public link from the server." request:request];
}];
} else {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
withRowAnimation:UITableViewRowAnimationTop];
}
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
objectIsPublic = !objectIsPublic;
objectIsPublicSwitch.on = !objectIsPublicSwitch.on;
- object.publicURI = oldPubicURI;
+ object.publicURI = oldPublicURI;
[self alert:@"There was a problem enabling the public link." request:request];
}];
}
}
}
+- (void)updatePermissionsUserCatalog {
+ NSMutableArray *UUIDs = [NSMutableArray arrayWithCapacity:permissions.count];
+ for (NSString *user in permissions) {
+ NSRange rangeOfColumn = [user rangeOfString:@":"];
+ NSString *UUID = (rangeOfColumn.location == NSNotFound) ? user : [user substringToIndex:rangeOfColumn.location];
+ if (![UUID isEqualToString:@"*"]) {
+ [UUIDs addObject:UUID];
+ }
+ }
+ if (UUIDs.count) {
+ __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading permissions..."
+ andAddToView:self.view];
+ [[self.account.manager userCatalogForDisplaynames:nil UUIDs:UUIDs]
+ success:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:permissionsSection] withRowAnimation:UITableViewRowAnimationNone];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self alert:@"Failed to translate sharing UUIDs." request:request];
+ }];
+ }
+}
+
+- (void)delete {
+ if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
+ folderViewController.deletedObject = object;
+ [self.navigationController popViewControllerAnimated:YES];
+ } else {
+ [folderViewController deleteAnimatedObject:object];
+ [folderViewController setDetailViewController];
+ }
+}
+
#pragma mark - Actions
- (void)reloadMetadataSection {
andAddToView:self.view];
[[self.account.manager getObjectInfo:container object:object version:versionID]
success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
object.metadata = [NSMutableDictionary dictionary];
for (NSString *header in request.responseHeaders) {
NSString *metadataKey;
[object.metadata setObject:metadataValue forKey:metadataKey];
}
}
- NSIndexSet *metadataSections = [NSIndexSet indexSetWithIndex:kMetadata];
- [self.tableView reloadSections:metadataSections withRowAnimation:UITableViewRowAnimationFade];
+ [self.tableView reloadData];
+ [self updatePermissionsUserCatalog];
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self alert:@"There was a problem retrieving the object's metadata." request:request];
}];
}
}
}
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
- [self.folderViewController reloadData];
}
}
failure:^(OpenStackRequest *request) {
if (objectIsReadOnly) {
return [object.metadata count];
} else {
- return 1 + [object.metadata count];
+ return ([object.metadata count] + 1);
}
} else if (section == publicLinkSection) {
- return objectIsPublic ? 2 : 1;
+ return (objectIsPublic ? 2 : 1);
} else if (section == permissionsSection) {
if (account.sharingAccount || objectIsReadOnly) {
- return [permissions count];
+ return permissions.count;
} else {
- return 1 + [permissions count];
+ return (permissions.count + 1);
}
} else {
return 1;
CGSizeMake(221.0, 9000.0))
lineBreakMode:UILineBreakModeCharacterWrap].height;
} else if ((indexPath.section == publicLinkSection) && (indexPath.row == 1)) {
- NSURL *publicLinkURL = [account.pithosPublicLinkURLPrefix URLByAppendingPathComponent:
+ NSURL *publicLinkURL = [account.provider.publicLinkURLPrefix URLByAppendingPathComponent:
[NSString encodeToPercentEscape:[self.object.publicURI substringFromIndex:1]
charactersToEncode:@"!*'();:@&=+$,?%#[]"]];
result = 30.0 + [[publicLinkURL description] sizeWithFont:[UIFont systemFontOfSize:18.0]
cell.textView.backgroundColor = [UIColor clearColor];
cell.textView.font = [UIFont systemFontOfSize:15.0];
cell.textView.dataDetectorTypes = UIDataDetectorTypeLink;
- cell.textView.text = [[account.pithosPublicLinkURLPrefix URLByAppendingPathComponent:
+ cell.textView.text = [[account.provider.publicLinkURLPrefix URLByAppendingPathComponent:
[NSString encodeToPercentEscape:[self.object.publicURI substringFromIndex:1]
charactersToEncode:@"!*'();:@&=+$,?%#[]"]] description];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryNone;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.userInteractionEnabled = NO;
- }
- else {
+ } else {
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.accessoryView = nil;
- if (indexPath.row == [permissions count]) {
+ if (indexPath.row == permissions.count) {
cell.textLabel.text = @"Share";
cell.detailTextLabel.text = @"";
} else {
- NSString *user = [[permissions allKeys] objectAtIndex:indexPath.row];
- cell.textLabel.text = user;
- NSString *accessType;
- if ([[permissions objectForKey:user] isEqualToString:@"write"])
- accessType = @"Read/Write";
- else
- accessType = @"Read Only";
+ NSString *user = [[[permissions allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:indexPath.row];
+ NSRange rangeOfColumn = [user rangeOfString:@":"];
+ NSString *UUID = (rangeOfColumn.location == NSNotFound) ? user : [user substringToIndex:rangeOfColumn.location];
+ NSString *group = ((rangeOfColumn.location == NSNotFound) || (rangeOfColumn.location == user.length - 1)) ? nil : [user substringFromIndex:(rangeOfColumn.location + 1)];
+ NSMutableString *displayname = [NSMutableString stringWithString:[account displaynameForUUID:UUID safe:YES]];
+ if (group) {
+ [displayname appendFormat:@":%@", group];
+ }
+ cell.textLabel.text = displayname;
+
cell.detailTextLabel.numberOfLines = 1;
cell.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation;
- cell.detailTextLabel.text = accessType;
+ cell.detailTextLabel.text = ([[permissions objectForKey:user] isEqualToString:@"write"] ? @"Read/Write" : @"Read Only");
}
} else if (indexPath.section == kActions) {
if (indexPath.row == 0) {
EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
NSString *user;
- if (indexPath.row == [permissions count]) {
+ if (indexPath.row == permissions.count) {
user = @"";
vc.removePermissionsEnabled = NO;
- vc.navigationItem.title = @"Share";
+ vc.navigationItem.title = @"Add Permission";
} else {
- user = [[permissions allKeys] objectAtIndex:indexPath.row];
+ user = [[[permissions allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:indexPath.row];
NSString *userPermissions = [permissions objectForKey:user];
- if ([userPermissions rangeOfString:@"read"].location != NSNotFound)
+ if ([userPermissions rangeOfString:@"read"].location != NSNotFound) {
vc.readPermissionSelected = YES;
- else
+ } else {
vc.readPermissionSelected = NO;
-
- if ([userPermissions rangeOfString:@"write"].location != NSNotFound)
+ }
+ if ([userPermissions rangeOfString:@"write"].location != NSNotFound) {
vc.writePermissionSelected = YES;
- else
+ } else {
vc.writePermissionSelected = NO;
-
+ }
vc.removePermissionsEnabled = YES;
- vc.navigationItem.title = @"Edit Sharing";
+ vc.navigationItem.title = @"Edit Permission";
}
- vc.user = user;
+ vc.permissionUser = user;
vc.permissions = permissions;
vc.account = account;
vc.container = container;
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
// delete the file and pop out
- self.folderViewController.refreshButton.enabled = NO;
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Deleting file..."
andAddToView:self.view];
[[self.account.manager deleteObject:self.container object:self.object]
success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
- [self.navigationController popViewControllerAnimated:YES];
- if (account.shared)
- self.folderViewController.needsRefreshing = YES;
- } else if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
- self.folderViewController.selectedObjectViewController = nil;
- if (!account.shared)
- [self.folderViewController setDetailViewController];
- else
- [self.folderViewController refreshButtonPressed:nil];
-// self.folderViewController.refreshButton.enabled = YES;
- }
- if (self.folder.objectsAndFoldersCount == 1) {
- [self.folder removeObject:self.object];
- self.folderViewController.folder = self.folderViewController.folder;
- } else {
- [self.folderViewController deleteAnimatedObject:self.object];
- }
- self.folderViewController.refreshButton.enabled = YES;
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self delete];
}
failure:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
- [self alert:@"There was a problem deleting this file." request:request];
- self.folderViewController.refreshButton.enabled = YES;
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ // 404 Not Found means it's not there, so we can show the user that it's deleted
+ if (request.responseStatusCode == 404) {
+ [self delete];
+ } else {
+ [self alert:@"There was a problem deleting this file." request:request];
+ }
}];
}
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:deleteSection];
Container *container;
Folder *folder;
FolderViewController *folderViewController;
- id successObserver;
- id failureObserver;
NSData *data;
UITextField *nameTextField;
UILabel *formatLabel;
andAddToView:self.view];
[[self.account.manager writeObject:self.container object:object downloadProgressDelegate:activityIndicatorView.progressView]
success:^(OpenStackRequest *request) {
- [activityIndicatorView removeFromSuperview];
object.data = nil;
object.sharing = folder.sharing;
object.lastModified = [ComputeModel dateFromRFC1123String:[request.responseHeaders objectForKey:@"Date"]];
-
- BOOL currentFolderIsRoot = NO;
- if ([folderViewController.folder isEqual:container.rootFolder]) {
- currentFolderIsRoot = YES;
- }
+ BOOL currentFolderIsRoot = (folderViewController.folder == container.rootFolder);
[folder addObject:object];
folderViewController.folder = folderViewController.folder;
if (currentFolderIsRoot) {
container.count += 1;
container.rootFolder = folder;
- [self.account.containers setObject:container forKey:container.name];
}
- if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
- [folderViewController setDetailViewController];
allowOverwrite = NO;
- [self dismissModalViewControllerAnimated:YES];
+ [[self.account.manager getObjectInfo:self.container object:object]
+ success:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ object.hash = [request.responseHeaders objectForKey:@"X-Object-Hash"];
+ [self dismissModalViewControllerAnimated:YES];
+ }
+ failure:^(OpenStackRequest *request) {
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+ [self dismissModalViewControllerAnimated:YES];
+ }];
}
failure:^(OpenStackRequest *request) {
allowOverwrite = NO;
- [activityIndicatorView removeFromSuperview];
+ [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
[self alert:@"There was a problem uploading the file." request:request];
}];
}
<string>English</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
- <key>CFBundleDocumentTypes</key>
- <array/>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>1.5.1</string>
+ <string>1.6.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
</dict>
</array>
<key>CFBundleVersion</key>
- <string>20130122.0</string>
+ <string>20130209.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSMainNibFile</key>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
- <key>UTExportedTypeDeclarations</key>
- <array/>
- <key>UTImportedTypeDeclarations</key>
- <array/>
</dict>
</plist>
objects = {
/* Begin PBXBuildFile section */
- 1D18292C16AFE84A00C9FCCD /* OpenStack-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8D1107310486CEB800E47090 /* OpenStack-Info.plist */; };
1D3623260D0F684500981E51 /* OpenStackAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* OpenStackAppDelegate.m */; };
1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; };
24332D531472839300063C8B /* myShared.png in Resources */ = {isa = PBXBuildFile; fileRef = 24332D521472839300063C8B /* myShared.png */; };
2787C13E12F0D26A009EAD7C /* text-file.txt in Resources */ = {isa = PBXBuildFile; fileRef = 2787C13D12F0D26A009EAD7C /* text-file.txt */; };
2788941B12C28C66006448E2 /* ContainerDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2788941812C28C66006448E2 /* ContainerDetailViewController.m */; };
2788941C12C28C66006448E2 /* ContainerDetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2788941912C28C66006448E2 /* ContainerDetailViewController.xib */; };
- 2788965612C52E5F006448E2 /* GetContainersRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2788965412C52E5F006448E2 /* GetContainersRequest.m */; };
278896B312C53956006448E2 /* GetObjectsRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 278896B112C53956006448E2 /* GetObjectsRequest.m */; };
278906E012BEDAB5007112B6 /* StorageObjectViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 278906DD12BEDAB5007112B6 /* StorageObjectViewController.xib */; };
278907A212BEF72C007112B6 /* StorageObjectViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 278907A112BEF72C007112B6 /* StorageObjectViewController.m */; };
2788941712C28C66006448E2 /* ContainerDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContainerDetailViewController.h; sourceTree = "<group>"; };
2788941812C28C66006448E2 /* ContainerDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContainerDetailViewController.m; sourceTree = "<group>"; };
2788941912C28C66006448E2 /* ContainerDetailViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ContainerDetailViewController.xib; sourceTree = "<group>"; };
- 2788965312C52E5F006448E2 /* GetContainersRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetContainersRequest.h; sourceTree = "<group>"; };
- 2788965412C52E5F006448E2 /* GetContainersRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GetContainersRequest.m; sourceTree = "<group>"; };
278896B012C53956006448E2 /* GetObjectsRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetObjectsRequest.h; sourceTree = "<group>"; };
278896B112C53956006448E2 /* GetObjectsRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GetObjectsRequest.m; sourceTree = "<group>"; };
278906DB12BEDAB5007112B6 /* StorageObjectViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StorageObjectViewController.h; sourceTree = "<group>"; };
2788958612C476A3006448E2 /* Object Storage */ = {
isa = PBXGroup;
children = (
- 2788965312C52E5F006448E2 /* GetContainersRequest.h */,
- 2788965412C52E5F006448E2 /* GetContainersRequest.m */,
278896B012C53956006448E2 /* GetObjectsRequest.h */,
278896B112C53956006448E2 /* GetObjectsRequest.m */,
);
6142B27D1523602300A1BFAD /* pithos_icon_iPad@2x.png in Resources */,
6152AEEF166519F200A3EA6B /* Default-568h@2x.png in Resources */,
61BC2A2D166BDAE5002DF74D /* FolderViewController-iPad.xib in Resources */,
- 1D18292C16AFE84A00C9FCCD /* OpenStack-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
277B89B912B97D6D006483B0 /* FolderViewController.m in Sources */,
278907A212BEF72C007112B6 /* StorageObjectViewController.m in Sources */,
2788941B12C28C66006448E2 /* ContainerDetailViewController.m in Sources */,
- 2788965612C52E5F006448E2 /* GetContainersRequest.m in Sources */,
278896B312C53956006448E2 /* GetObjectsRequest.m in Sources */,
2734687112D3AB9500341268 /* AddObjectViewController.m in Sources */,
273468C512D3B0E800341268 /* AddFolderViewController.m in Sources */,