Use service catalog and update version
[pithos-ios] / Classes / StorageObjectViewController.m
index c410583..98c353d 100755 (executable)
 #import "EditMetadataViewController.h"
 #import "Provider.h"
 #import "EditPermissionsViewController.h"
+#import "TextViewCell.h"
+#import "NSString+Conveniences.h"
+#import "APICallback.h"
+#import "ObjectVersionsViewController.h"
+#import <MediaPlayer/MPMoviePlayerController.h>
+#import <MessageUI/MessageUI.h>
+#import "ActivityIndicatorView.h"
 
 #define kDetails 0
-#define kMetadata 1
-#define kPublic 2
-#define kPermissions 3
+#define kActions 1
+#define kMetadata 2
 
 #define maxMetadataViewableLength 12
 
-
 // TODO: use etag to reset download
 // TODO: try downloading directly to the file to save memory.  don't use object.data
 
 /*
- Name                           whatever.txt
- Full Path
- Size                           123 KB
- Content Type                   text/plain
- Metadata
- Key                            Value -> tap goes to a metadata item VC to edit or delete
- Key                            Value
- Add Metadata... (if max not already reached)
+Name                            whatever.txt
+Full Path                       folder/whatever.txt
+Size                            123 KB
+Type                            text/plain
+Last Modified                   2012-12-21 11:11:00
+
+if fileDownloaded:
+ Open File
+ Mail File
+ Delete from Cache
+else if fileDownloading:
+ Open File (disabled)
+ Mail File (disabled)
+ Downloading (progress view)
+ Cancel
+else:
+ Open File
+ Mail File
+
+Metadata
+Key                             Value -> tap goes to a metadata item VC to edit or delete
+Key                             Value
+Add Metadata... (if max not already reached)
+
+Public URL                      On/Off
+
+Share
  
- CDN URL sections (action sheet to copy, open in safari, and email link)
+Versions
  
- Download File (if downloaded, Open File and Mail File as Attachment)
- "After you download the file, you'll be able to attempt to open it and mail is as an attachment."
- Delete Object
- */
+Delete Object
+*/
 
 @implementation StorageObjectViewController
 
-@synthesize account, container, folder, object, tableView, folderViewController;
-@synthesize oldPubicURI;
+@synthesize account, container, folder, object, folderViewController;
+@synthesize oldPublicURI, permissions, documentInteractionController, objectIsReadOnly, versionID;
 
-- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
-    return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
-}
+#pragma mark - View lifecycle
 
-- (void)setBackgroundView {
-    
-    UIImageView *logo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cloudfiles-large.png"]];
-    
-    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
-        UIView *backgroundContainer = [[UIView alloc] init];
-        backgroundContainer.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
-        backgroundContainer.backgroundColor = [UIColor iPadTableBackgroundColor];
-        logo.contentMode = UIViewContentModeScaleAspectFit;
-        logo.frame = CGRectMake(100.0, 100.0, 1000.0, 1000.0);
-        logo.alpha = 0.5;        
-        [backgroundContainer addSubview:logo];
-        tableView.backgroundView = backgroundContainer;
-        [backgroundContainer release];
-    } else {        
-        self.tableView.backgroundView = nil;
-    }
-    [logo release];    
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
+    return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
 }
 
-
-#pragma mark -
-#pragma mark View lifecycle
-
 - (void)viewDidLoad {
     [super viewDidLoad];
+    
     objectIsPublicSwitch = [[UISwitch alloc] init];
     [objectIsPublicSwitch addTarget:self action:@selector(objectIsPublicSwitchChanged:) forControlEvents:UIControlEventValueChanged];
     
-    deleteActionSheet = [[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this file?  This operation cannot be undone." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete File" otherButtonTitles:nil];
-    
-    permissions = [[NSMutableDictionary alloc] init];
-    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) {
+            objectIsReadOnly = ![[permissions objectForKey:account.username] isEqualToString:@"write"];
         }
-    }    
+        objectIsPublicSwitch.enabled = NO;
+    }
+    
+    downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
+    fileDownloading = NO;
+    OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+    OpenStackRequest *request = [app objectDownloadRequestForAccount:account container:container object:object];
+    if (request) {
+        fileDownloading = YES;
+        request.downloadProgressDelegate = self;
+        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
+    }
+    
 }
 
 - (void)viewWillAppear:(BOOL)animated {
     [super viewWillAppear:animated];
     self.navigationItem.title = object.name;
     
-    NSIndexPath*       selection = [self.tableView indexPathForSelectedRow];
+    NSIndexPath *selection = [self.tableView indexPathForSelectedRow];
        if (selection)
                [self.tableView deselectRowAtIndexPath:selection animated:YES];
     
-    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
-    NSString *documentsDirectory = [paths objectAtIndex:0];        
-    NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
-    NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
-    
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    fileDownloaded = [fileManager fileExistsAtPath:filePath];
-    
-    downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
+    OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+    if ([app cacheFilePathForHash:object.hash]) {
+        fileDownloaded = YES;
+    } else {
+        fileDownloaded = NO;
+    }
     
-    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+    if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
         CGRect rect = downloadProgressView.frame;
         rect.size.width = 440.0;
         downloadProgressView.frame = rect;
     }
     
-    [self setBackgroundView];
-    if (self.container.cdnEnabled) {
-        cdnURLSection = 4;
-        actionsSection = 5;
-        deleteSection = 6;
-    } else {
-        cdnURLSection = -1;
-        actionsSection = 4;
-        deleteSection = 5;
+    publicLinkSection = 3;
+    permissionsSection = 4;
+    versionsSection = 5;
+    deleteSection = 6;
+    
+    if (versionID) {
+        publicLinkSection = -1;
+        permissionsSection = -1;
+        versionsSection = -1;
+        deleteSection = -1;
+        objectIsReadOnly = YES;
+    }
+    if (account.sharingAccount || account.shared) {
+        versionsSection = -1;
+        deleteSection = -1;
     }
     
-    // let's see if we can tweet
-    UIApplication *app = [UIApplication sharedApplication];
-    NSURL *twitterURL = [NSURL URLWithString:@"twitter://post?message=test"];
+    objectIsPublic = ([object.publicURI length]);
+    objectIsPublicSwitch.on = objectIsPublic;
+    [self.tableView reloadData];
+}
 
-    if ([app canOpenURL:twitterURL]) {
-        cdnURLActionSheet = [[UIActionSheet alloc] initWithTitle:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Copy to Pasteboard", @"Open in Safari", @"Email Link to File", @"Tweet Link to File", nil];
+- (void)viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    if (!object.metadata) {
+        [self reloadMetadataSection];
     } else {
-        cdnURLActionSheet = [[UIActionSheet alloc] initWithTitle:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Copy to Pasteboard", @"Open in Safari", @"Email Link to File", nil];
+        [self updatePermissionsUserCatalog];
+    }
+    if (fileDownloading) {
+        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:kActions]
+                              atScrollPosition:UITableViewScrollPositionMiddle
+                                      animated:YES];
+    }
+}
+
+#pragma mark - Memory management
+
+- (void)dealloc {
+    if (fileDownloading) {
+        OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+        OpenStackRequest *request = [app objectDownloadRequestForAccount:account container:container object:object];
+        request.downloadProgressDelegate = nil;
     }
+    [account release];
+    [downloadProgressView release];
+    [cdnURLActionSheet release];
+    [folderViewController release];
+    [oldPublicURI release];
+    [permissions release];
+    [objectIsPublicSwitch release];
+    [documentInteractionController release];
+    [versionID release];
+    [super dealloc];
+}
+
+#pragma mark - Internal
+
+- (void)objectIsPublicSwitchChanged:(id)sender {
+    NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."];
+    self.oldPublicURI = object.publicURI;
     
-    objectIsPublic = (object.publicURI != nil);
-    objectIsPublicSwitch.on = objectIsPublic;
-    [self.tableView reloadData];
+    if (objectIsPublic) {
+        activityMessage = [NSString stringWithFormat:@"Disabling public link.."];
+        object.publicURI = @"";
+    } else {
+        object.publicURI = @"TRUE";
+    }
+    __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:activityMessage
+                                                                                                   andAddToView:self.view
+                                                                                                   scrollOffset:self.tableView.contentOffset.y];
+    objectIsPublic = !objectIsPublic;
+    [[self.account.manager writeObjectMetadata:container object:object]
+     success:^(OpenStackRequest *request) {
+         NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
+         if (objectIsPublic) {
+             [[self.account.manager getObjectInfo:container object:object version:versionID]
+              success:^(OpenStackRequest *request) {
+                  [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 stopAnimatingAndRemoveFromSuperview];
+                  [self alert:@"There was a problem retrieving the public link from the server." request:request];
+              }];
+         } else {
+             [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+             [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
+                                   withRowAnimation:UITableViewRowAnimationTop];
+         }
+     }
+     failure:^(OpenStackRequest *request) {
+         [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+         objectIsPublic = !objectIsPublic;
+         objectIsPublicSwitch.on = !objectIsPublicSwitch.on;
+         object.publicURI = oldPublicURI;
+         [self alert:@"There was a problem enabling the public link." request:request];
+     }];
+}
+
+- (void)openFile {
+    OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+    NSString *filePath = [app cacheFilePathForHash:object.hash];
+    self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
+    self.documentInteractionController.delegate = self;
+    self.documentInteractionController.name = object.name;
+    
+    CGRect frameToPresentMenuFrom;
+    if ([[UIDevice currentDevice].systemVersion hasPrefix:@"6"]) {
+        frameToPresentMenuFrom = CGRectMake(0.0,
+                                            self.tableView.contentOffset.y,
+                                            self.view.frame.size.width,
+                                            1.0);
+    } else {
+        UITableViewCell *openFileCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:kActions]];
+        frameToPresentMenuFrom = CGRectMake(openFileCell.frame.origin.x,
+                                            openFileCell.frame.origin.y - self.tableView.contentOffset.y,
+                                            openFileCell.frame.size.width,
+                                            openFileCell.frame.size.height);
+    }
+    if (![self.documentInteractionController presentOptionsMenuFromRect:frameToPresentMenuFrom inView:self.view animated:YES]) {
+        if ([self.object isPlayableMedia]) {
+            MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
+            vc.container = self.container;
+            vc.object = self.object;
+            [self.navigationController pushViewController:vc animated:YES];
+            [vc release];
+        } else {
+            [self alert:@"Error" message:@"This file could not be opened."];
+        }
+    }
 }
 
-#pragma mark -
-#pragma mark Table view data source
+- (void)mailFile {
+    if (![MFMailComposeViewController canSendMail]) {
+        [self alert:@"Cannot send mail" message:@"Your device has not been configured for sending mail"];
+    } else {
+        OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+        NSString *filePath = [app cacheFilePathForHash:object.hash];
+        NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
+        
+        MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
+        vc.mailComposeDelegate = self;
+        [vc setSubject:object.name];
+        [vc addAttachmentData:data mimeType:object.contentType fileName:object.name];
+        [vc setMessageBody:@"" isHTML:NO];
+        if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
+            vc.modalPresentationStyle = UIModalPresentationPageSheet;
+        [self presentModalViewController:vc animated:YES];
+        [vc release];
+    }
+}
+
+- (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];
+             if (request.responseStatusCode != 404) {
+                 // Don't show alert on 404, since it can be a pre-UUID server.
+                 [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 {
+    __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading metadata..."
+                                                                                                   andAddToView:self.view];
+    [[self.account.manager getObjectInfo:container object:object version:versionID]
+     success:^(OpenStackRequest *request) {
+         [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+         object.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]];
+                 [object.metadata setObject:metadataValue forKey:metadataKey];
+             }
+         }
+         [self.tableView reloadData];
+         [self updatePermissionsUserCatalog];
+     }
+     failure:^(OpenStackRequest *request) {
+         [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+         [self alert:@"There was a problem retrieving the object's metadata." request:request];
+     }];
+}
+
+- (void)downloadFileForAction:(StorageObjectAction)action {
+    if (fileDownloading)
+        return;
+    fileDownloading = YES;
+    [downloadProgressView setProgress:0.0 animated:NO];
+    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
+    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:kActions]
+                          atScrollPosition:UITableViewScrollPositionMiddle
+                                  animated:YES];
+    NSMutableDictionary *requestUserInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:action] forKey:@"action"];
+    [[self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self requestUserInfo:requestUserInfo version:versionID]
+     success:^(OpenStackRequest *request) {
+         if (request.isCancelled) {
+             fileDownloaded = NO;
+             fileDownloading = NO;
+             [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
+         } else {
+             fileDownloaded = YES;
+             fileDownloading = NO;
+             if ([self.navigationController.visibleViewController isEqual:self]) {
+                 if ([[request.userInfo objectForKey:@"action"] integerValue] == StorageObjectActionMailFile) {
+                     [self mailFile];
+                 } else {
+                     [self openFile];
+                 }
+             }
+             [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
+         }
+     }
+     failure:^(OpenStackRequest *request) {
+         fileDownloaded = NO;
+         fileDownloading = NO;
+         [self alert:@"File failed to download." request:request];
+         [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
+     }];
+}
+
+#pragma mark - UITableViewDataSource
 
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
-    return self.container.cdnEnabled ? 7 : 6;
+    int numberOfSections = 7;
+    if (publicLinkSection < 0)
+        numberOfSections--;
+    if (permissionsSection < 0)
+        numberOfSections--;
+    if (versionsSection < 0)
+        numberOfSections--;
+    if (deleteSection < 0)
+        numberOfSections--;
+
+    return numberOfSections;
 }
 
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
     if (section == kDetails) {
-        return 4;
+        return (object.lastModifiedString ? 5: 4);
+    } else if (section == kActions) {
+        if (fileDownloaded) {
+            return 3;
+        } else if (fileDownloading) {
+            return 4;
+        } else {
+            return 2;
+        }
     } else if (section == kMetadata) {
-        return 1 + [object.metadata count];
-    } else if (section == cdnURLSection) {
-        return 1;
-    } else if (section == actionsSection) {
-        return fileDownloaded ? 2 : 1;
-    } else if (section == kPublic) {
-        return objectIsPublic ? 2 : 1;
-    } else if (section == kPermissions) {
-        return 1 + [permissions count];
+        if (objectIsReadOnly) {
+            return [object.metadata count];
+        } else {
+            return ([object.metadata count] + 1);
+        }
+    } else if (section == publicLinkSection) {
+        return (objectIsPublic ? 2 : 1);
+    } else if (section == permissionsSection) {
+        if (account.sharingAccount || objectIsReadOnly) {
+            return permissions.count;
+        } else {
+            return (permissions.count + 1);
+        }
     } else {
         return 1;
     }
 }
 
-- (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font {
-    CGSize textLabelSize;    
-    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
-        // 616, 678
-        textLabelSize = CGSizeMake(596.0, 9000.0f);
-    } else {
-        textLabelSize = CGSizeMake(280.0, 9000.0f);
-    }
-    CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeCharacterWrap];
-    return stringSize.height;
-}
-
 - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
     CGFloat result = aTableView.rowHeight;
-    
-    if (indexPath.section == cdnURLSection) {
-        result = 22.0 + [self findLabelHeight:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] font:[UIFont systemFontOfSize:18.0]];
-    } else if (indexPath.section == kDetails && indexPath.row == 1) {
-        CGSize textLabelSize;
-        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
-            textLabelSize = CGSizeMake(537.0, 9000.0f);
-        } else {
-            textLabelSize = CGSizeMake(221.0, 9000.0f);
-        }
-        CGSize stringSize = [object.fullPath sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
-        return 22.0 + stringSize.height;
-    } else if (indexPath.section == kDetails && indexPath.row == 0) {
-        CGSize textLabelSize;
-        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
-            textLabelSize = CGSizeMake(537.0, 9000.0f);
+    if ((indexPath.section == kDetails) && ((indexPath.row == 0) || (indexPath.row == 1))) {
+        NSString *text;
+        if (indexPath.row == 0) {
+            text = object.name;
         } else {
-            textLabelSize = CGSizeMake(221.0, 9000.0f);
+            text = object.fullPath;
         }
-        CGSize stringSize = [object.name sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
-        return 22.0 + stringSize.height;
-    } else if (indexPath.section == kPublic && indexPath.row == 1) {
-        result = 22.0 + [self findLabelHeight:[NSString stringWithFormat:@"%@%@", self.account.provider.authEndpointURL, self.object.fullPath] font:[UIFont systemFontOfSize:15.0]];
-        return result;
+        result = 22.0 + [text sizeWithFont:[UIFont systemFontOfSize:18.0]
+                         constrainedToSize:(([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) ?
+                                            CGSizeMake(537.0, 9000.0) :
+                                            CGSizeMake(221.0, 9000.0))
+                             lineBreakMode:UILineBreakModeCharacterWrap].height;
+    } else if ((indexPath.section == publicLinkSection) && (indexPath.row == 1)) {
+        NSURL *publicLinkURL = [account.provider.publicLinkURLPrefix URLByAppendingPathComponent:
+                                [NSString encodeToPercentEscape:[self.object.publicURI substringFromIndex:1]
+                                             charactersToEncode:@"!*'();:@&=+$,?%#[]"]];
+        result = 30.0 + [[publicLinkURL description] sizeWithFont:[UIFont systemFontOfSize:18.0]
+                                                constrainedToSize:(([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) ?
+                                                                   CGSizeMake(596.0, 9000.0) :
+                                                                   CGSizeMake(280.0, 9000.0))
+                                                    lineBreakMode:UILineBreakModeCharacterWrap].height;
     }
-    
     return MAX(aTableView.rowHeight, result);
 }
 
 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    if ((indexPath.section == publicLinkSection) && (indexPath.row == 1)) {
+        static NSString *CellIdentifier = @"TextViewCell";
+        TextViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
+        if (cell == nil) {
+            cell = [[[TextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+            cell.textView.frame = cell.contentView.frame;
+
+        }
+        cell.selectionStyle = UITableViewCellSelectionStyleNone;
+        cell.textView.backgroundColor = [UIColor clearColor];
+        cell.textView.font = [UIFont systemFontOfSize:15.0];
+        cell.textView.dataDetectorTypes = UIDataDetectorTypeLink;
+        cell.textView.text = [[account.provider.publicLinkURLPrefix URLByAppendingPathComponent:
+                               [NSString encodeToPercentEscape:[self.object.publicURI substringFromIndex:1]
+                                            charactersToEncode:@"!*'();:@&=+$,?%#[]"]] description];
+        cell.selectionStyle = UITableViewCellSelectionStyleNone;
+        cell.accessoryView = UITableViewCellAccessoryNone;
+        return cell;
+    }
     
-    static NSString *CellIdentifier = @"Cell";
+    if (indexPath.section == deleteSection) {
+        static NSString *CellIdentifier = @"DeleteCell";
+        UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+        if (cell == nil) {
+            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+            cell.textLabel.textAlignment = UITextAlignmentCenter;
+            cell.textLabel.text = @"Delete Object";
+        }
+        return cell;
+    }
     
+    static NSString *CellIdentifier = @"Cell";
     UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
     if (cell == nil) {
         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
-        //cell.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.93];
         cell.textLabel.backgroundColor = [UIColor clearColor];
         cell.detailTextLabel.backgroundColor = [UIColor clearColor];
-        cell.detailTextLabel.numberOfLines = 0;
         cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
     }
-    
-    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
-        cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8];
-    }
-    
+    cell.textLabel.textColor = [UIColor blackColor];
+    cell.detailTextLabel.numberOfLines = 0;
+    cell.userInteractionEnabled = YES;
     cell.detailTextLabel.textAlignment = UITextAlignmentRight;
     cell.accessoryView = nil;
     cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
         } else if (indexPath.row == 3) {
             cell.textLabel.text = @"Type";
             cell.detailTextLabel.text = object.contentType;
+        } else if (indexPath.row == 4) {
+            cell.textLabel.text = @"Last Modified";
+            cell.detailTextLabel.text = object.lastModifiedString;
         }
     } else if (indexPath.section == kMetadata) {
-        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
-        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+        if (objectIsReadOnly) {
+            cell.accessoryType = UITableViewCellAccessoryNone;
+            cell.selectionStyle = UITableViewCellSelectionStyleNone;
+            cell.userInteractionEnabled = NO;
+        }
+        else {
+            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+        }
+        
         cell.accessoryView = nil;
         if (indexPath.row == [object.metadata count]) {
             cell.textLabel.text = @"Add Metadata";
             cell.textLabel.text = metadataKeyCellText;
             cell.detailTextLabel.text = metadataValueCellText;
         }
-    } else if (indexPath.section == kPublic) {
+    } else if (indexPath.section == publicLinkSection) {
         if (indexPath.row == 0) {
             cell.textLabel.text = @"Public URL";        
             cell.detailTextLabel.text = @"";
             cell.accessoryType = UITableViewCellAccessoryNone;
             cell.selectionStyle = UITableViewCellSelectionStyleNone;
         }
-        else if (indexPath.row == 1) {
-            cell.textLabel.numberOfLines = 0;
-            cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
-            cell.textLabel.text = [NSString stringWithFormat:@"%@%@", self.account.provider.authEndpointURL, object.publicURI];
-            cell.textLabel.font = [UIFont systemFontOfSize:15.0];
+    } else if (indexPath.section == permissionsSection) {
+        if (account.sharingAccount) {
+            cell.accessoryType = UITableViewCellAccessoryNone;
             cell.selectionStyle = UITableViewCellSelectionStyleNone;
+            cell.userInteractionEnabled = NO;
+        } else {
+            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
         }
-    } else if (indexPath.section == kPermissions) {
-        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
-        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
         cell.accessoryView = nil;
-        if (indexPath.row == [permissions count]) {
-            cell.textLabel.text = @"Add Permissions";
+        
+        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 if (indexPath.section == cdnURLSection) {
-        cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
-        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
-        cell.textLabel.text = @"";
-        cell.detailTextLabel.text = [[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
-    } else if (indexPath.section == actionsSection) {
-        cell.accessoryView = nil;
-        if (performingAction) {
-            cell.textLabel.textColor = [UIColor grayColor];
-            cell.selectionStyle = UITableViewCellSelectionStyleNone;
-            cell.accessoryType = UITableViewCellAccessoryNone;
         } else {
-            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
-            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+            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");
         }
-
+    } else if (indexPath.section == kActions) {
         if (indexPath.row == 0) {
-            if (fileDownloaded) {
-                cell.textLabel.text = @"Open File";
+            cell.textLabel.text = @"Open File";
+            cell.accessoryType = UITableViewCellAccessoryNone;
+            cell.detailTextLabel.text = @"";
+            if (fileDownloading) {
+                cell.textLabel.textColor = [UIColor grayColor];
+                cell.selectionStyle = UITableViewCellSelectionStyleNone;
             } else {
-                if (fileDownloading) {
-                    cell.accessoryView = downloadProgressView;
-                    // TODO: if you leave this view while downloading, there's EXC_BAD_ACCESS
-                    cell.textLabel.text = @"Downloading";
-                } else {
-                    cell.textLabel.text = @"Download File";
-                }
+                cell.selectionStyle = UITableViewCellSelectionStyleBlue;
             }
-            cell.detailTextLabel.text = @"";
-            
         } else if (indexPath.row == 1) {
             cell.textLabel.text = @"Email File as Attachment";
             cell.detailTextLabel.text = @"";
+            cell.accessoryType = UITableViewCellAccessoryNone;
+            if (fileDownloading) {
+                cell.textLabel.textColor = [UIColor grayColor];
+                cell.selectionStyle = UITableViewCellSelectionStyleNone;
+            } else {
+                cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+            }
+        } else if (indexPath.row == 2) {
+            if (fileDownloaded) {
+                cell.textLabel.text = @"Remove from Cache";
+                cell.accessoryType = UITableViewCellAccessoryNone;
+                cell.detailTextLabel.text = @"";
+            } else if (fileDownloading) {
+                cell.textLabel.text = @"Downloading";
+                cell.accessoryType = UITableViewCellAccessoryNone;
+                cell.detailTextLabel.text = @"";
+                cell.selectionStyle = UITableViewCellSelectionStyleNone;
+                cell.accessoryView = downloadProgressView;
+            }
+        } else if (indexPath.row == 3) {
+            if (fileDownloading) {
+                cell.textLabel.text = @"Cancel";
+                cell.accessoryType = UITableViewCellAccessoryNone;
+                cell.detailTextLabel.text = @"";
+            }
         }
-    } else if (indexPath.section == deleteSection) {
-        cell.textLabel.text = @"Delete Object";
-        cell.detailTextLabel.text = @"";
-        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+    } else if (indexPath.section == versionsSection) {
         cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+        cell.textLabel.text = @"Versions";
+        cell.detailTextLabel.text = @"";
     }
     
     return cell;
 }
 
-
-#pragma mark -
-#pragma mark Table view delegate
-
-- (void)reloadActionsTitleRow:(NSTimer *)timer {
-    [[timer.userInfo objectForKey:@"tableView"] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
-}
+#pragma mark - UITableViewDelegate
 
 - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
     if (indexPath.section == kMetadata) {
         if (indexPath.row == [self.object.metadata count]) {
             metadataKey = @"";
             metadataValue = @"";
-            vc.deleteEnabled = FALSE;
+            vc.removeMetadataEnabled = FALSE;
             vc.navigationItem.title = @"Add Metadata";
-        }
-        else {
+        } else {
             metadataKey = [[self.object.metadata allKeys] objectAtIndex:indexPath.row];
             metadataValue = [self.object.metadata objectForKey:metadataKey];
-            vc.deleteEnabled = YES;
+            vc.removeMetadataEnabled = YES;
             vc.navigationItem.title = @"Edit Metadata";
         }
 
         vc.object = object;
         [self.navigationController pushViewController:vc animated:YES];
         [vc release];
-    } else if (indexPath.section == kPermissions) {
+    } else if (indexPath.section == permissionsSection) {
         EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
         NSString *user;
         
-        if (indexPath.row == [permissions count]) {
+        if (indexPath.row == permissions.count) {
             user = @"";
-            vc.newPermissionsEntry = TRUE;
-            vc.navigationItem.title = @"Add Permissions";
-        }
-        else {
-            user = [[permissions allKeys] objectAtIndex:indexPath.row];
+            vc.removePermissionsEnabled = NO;
+            vc.navigationItem.title = @"Add Permission";
+        } else {
+            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.newPermissionsEntry = FALSE;
-            vc.navigationItem.title = @"Edit Permissions";
+            }
+            vc.removePermissionsEnabled = YES;
+            vc.navigationItem.title = @"Edit Permission";
         }
         
-        vc.user = user;
+        vc.permissionUser = user;
         vc.permissions = permissions;
         vc.account = account;
         vc.container = container;
         vc.object = object;
+        vc.folderViewController = folderViewController;
         [self.navigationController pushViewController:vc animated:YES];
         [vc release];
-    } else if (indexPath.section == cdnURLSection) {
-        [cdnURLActionSheet showInView:self.view];
-    } else if (indexPath.section == actionsSection) {
+    } else if (indexPath.section == kActions) {
         if (indexPath.row == 0) {
             if (fileDownloaded) {
-                NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
-                NSString *documentsDirectory = [paths objectAtIndex:0];        
-                NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
-                NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
-                                
-                UIDocumentInteractionController *vc = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
-                vc.delegate = self;
-                
-                if (![vc presentPreviewAnimated:YES]) {
-                    
-                    if ([self.object isPlayableMedia]) {
-                        MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
-                        vc.container = self.container;
-                        vc.object = self.object;
-                        [self.navigationController pushViewController:vc animated:YES];
-                        [vc release];
-                    } else {
-                        [self alert:@"Error" message:@"This file could not be opened."];
-                        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
-                    }
-                }
-                
-                [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];                
-                
-            } else {                
-                if (!fileDownloading) {
-                    // download the file
-                    fileDownloading = YES;
-                    [self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self];
-                    [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
-                }
-                
+                [self openFile];
+                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+            } else if (!fileDownloading) {
+                [self downloadFileForAction:StorageObjectActionOpenFile];
+                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
             }
         } else if (indexPath.row == 1) {
-            
-            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
-            NSString *documentsDirectory = [paths objectAtIndex:0];        
-            NSString *shortPath = @"";
-            
-            if (self.container && [self.container respondsToSelector:@selector(name)] && self.object && [self.object respondsToSelector:@selector(fullPath)]) {
-                shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
+            if (fileDownloaded) {
+                [self mailFile];
+                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+            } else if (!fileDownloading) {
+                [self downloadFileForAction:StorageObjectActionMailFile];
+                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+            }
+        } else if ((indexPath.row == 2) && fileDownloaded) {
+            OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+            BOOL removed = [app removeCacheObjectForHash:object.hash];
+            [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+            if (removed) {
+                fileDownloaded = NO;
+                fileDownloading = NO;
+                [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
+            }
+        } else if ((indexPath.row == 3) && fileDownloading) {
+            OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+            OpenStackRequest *request = [app objectDownloadRequestForAccount:account container:container object:object];
+            [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+            if (request) {
+                request.downloadProgressDelegate = nil;
+                [request cancel];
+                fileDownloaded = NO;
+                fileDownloading = NO;
+                [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
             }
-            
-            NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
-            NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
-            
-            MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
-            vc.mailComposeDelegate = self;             
-            [vc setSubject:self.object.name];
-            [vc addAttachmentData:data mimeType:self.object.contentType fileName:self.object.name];
-            [vc setMessageBody:@"" isHTML:NO];    
-            if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
-                vc.modalPresentationStyle = UIModalPresentationPageSheet;
-            }                
-            [self presentModalViewController:vc animated:YES];
-            [vc release];        
         }
     } else if (indexPath.section == deleteSection) {
+        UIActionSheet *deleteActionSheet = [[[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this file? This operation cannot be undone."
+                                                                        delegate:self
+                                                               cancelButtonTitle:@"Cancel"
+                                                          destructiveButtonTitle:@"Delete File"
+                                                               otherButtonTitles:nil] autorelease];
         [deleteActionSheet showInView:self.view];
+    } else if (indexPath.section == versionsSection) {
+        ObjectVersionsViewController *vc = [[ObjectVersionsViewController alloc] initWithNibName:@"ObjectVersionsViewController" bundle:nil];
+        vc.account = account;
+        vc.container = container;
+        vc.object = object;
+        [self.navigationController pushViewController:vc animated:YES];
+        [vc release];
     }
 }
 
     if (newProgress >= 1.0) {
         fileDownloading = NO;
         fileDownloaded = YES;
-        
-        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:1 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationBottom];
-        [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
+        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
     }
 }
 
-#pragma mark -
-#pragma mark Document Interation Controller Delegate
+#pragma mark - UIDocumentInteractionControllerDelegate
 
 - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *) controller {
     return self.navigationController;
 }
 
-- (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controllers {
-    [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
-}
-
-#pragma mark -
-#pragma mark Action Sheet Delegate
-
-- (void)deleteObjectRow {
-    [self.folderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
-}
+#pragma mark - UIActionSheetDelegate
 
 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
-    if ([actionSheet isEqual:deleteActionSheet]) {
-        if (buttonIndex == 0) {
-            // delete the file and pop out
-            [self showToolbarActivityMessage:@"Deleting file..."];
-            
-            [self.account.manager deleteObject:self.container object:self.object];
-            
-            deleteSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectSucceeded" object:self.object
-                                                                                               queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
-            {
-                [self hideToolbarActivityMessage];
-                performingAction = NO;
-                [self.folder.objects removeObjectForKey:self.object.name];
-                [self.navigationController popViewControllerAnimated:YES];
-                if ([self.folder.objects count] + [self.folder.folders count] == 0) {
-                    [self.folderViewController.tableView reloadData];
-                } else {
-                    [self.folderViewController.tableView selectRowAtIndexPath:selectedIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
-                    [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteObjectRow) userInfo:nil repeats:NO];
-                }
-                [[NSNotificationCenter defaultCenter] removeObserver:deleteSuccessObserver];
-            }];
-
-            deleteFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectFailed" object:self.object
-                                                                                       queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
-            {
-                [self hideToolbarActivityMessage];
-                performingAction = NO;
-                [self alert:@"There was a problem deleting this file." request:[notification.userInfo objectForKey:@"request"]];
-
-                [[NSNotificationCenter defaultCenter] removeObserver:deleteFailureObserver];
-
-            }];
-            
-        }
-        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:deleteSection];
-        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
-    } else if ([actionSheet isEqual:cdnURLActionSheet]) {
-        NSURL *url = [NSURL URLWithString:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
-
-        if (buttonIndex == 0) {
-            // copy to pasteboard
-            UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
-            [pasteboard setString:[url description]];
-        } else if (buttonIndex == 1) {
-            // open in safari
-            UIApplication *application = [UIApplication sharedApplication];
-            if ([application canOpenURL:url]) {
-                [application openURL:url];
-            } else {
-                [self alert:@"Error" message:[NSString stringWithFormat:@"This URL cannot be opened.\n%@", url]];
-            }
-        } else if (buttonIndex == 2) {
-            // email link to file
-            MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
-            vc.mailComposeDelegate = self;             
-            [vc setSubject:self.object.name];
-            [vc setMessageBody:[url description] isHTML:NO];
-            if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
-                vc.modalPresentationStyle = UIModalPresentationPageSheet;
-            }                
-            [self presentModalViewController:vc animated:YES];
-            [vc release];        
-        } else if (buttonIndex == 3) {
-            // tweet link to file
-            UIApplication *app = [UIApplication sharedApplication];            
-            NSURL *twitterURL = [NSURL URLWithString:[NSString stringWithFormat:@"twitter://post?message=%@", [[url description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
-            if ([app canOpenURL:twitterURL]) {
-                [app openURL:twitterURL];
-            }
-        }
-        [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:cdnURLSection] animated:YES];
+    if (buttonIndex == 0) {
+        // delete the file and pop out
+        __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Deleting file..."
+                                                                                                       andAddToView:self.view];
+        [[self.account.manager deleteObject:self.container object:self.object] 
+         success:^(OpenStackRequest *request) {
+             [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
+             [self delete];
+         }
+         failure:^(OpenStackRequest *request) {
+             [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];
+    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
 }
 
-#pragma mark -
-#pragma mark Mail Composer Delegate
+#pragma mark - MFMailComposeViewControllerDelegate
 
 // Dismisses the email composition interface when users tap Cancel or Send.
-- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error { 
+- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
        [self dismissModalViewControllerAnimated:YES];
-    [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:actionsSection] animated:YES];
-}
-
-#pragma mark -
-#pragma mark Switches
-
-- (void)objectIsPublicSwitchChanged:(id)sender
-{
-    NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."];
-    self.oldPubicURI = object.publicURI;
-    
-    if (objectIsPublic) {
-        activityMessage = [NSString stringWithFormat:@"Disabling public link.."];
-        object.publicURI = @"";
-    }
-    else {
-        object.publicURI = @"TRUE";
-    }
-    
-    activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
-    [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];    
-    
-    objectIsPublic = !objectIsPublic;
-    [self.account.manager writeObjectMetadata:container object:object];
-    if (objectIsPublic) {
-        getObjectInfoSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectInfoSucceeded"
-                                                                                         object:object
-                                                                                          queue:[NSOperationQueue mainQueue]
-                                                                                     usingBlock:^(NSNotification *notification)
-                                    {
-                                        [activityIndicatorView removeFromSuperviewAndRelease];
-                                        OpenStackRequest *request = [notification.userInfo objectForKey:@"request"];
-                                        object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"];
-                                        NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
-                                        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
-                                                              withRowAnimation:UITableViewRowAnimationBottom];
-                                        
-                                        [[NSNotificationCenter defaultCenter] removeObserver:getObjectInfoSuccessObserver];
-                                    }];
-
-        getObjectInfoFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectInfoFailed"
-                                                                                            object:object
-                                                                                             queue:[NSOperationQueue mainQueue]
-                                                                                        usingBlock:^(NSNotification *notification)
-                                        {
-                                            [activityIndicatorView removeFromSuperviewAndRelease];
-                                            [self alert:@"There was a problem retrieving the public link from the server." request:[notification.userInfo objectForKey:@"request"]]; 
-                                            [[NSNotificationCenter defaultCenter] removeObserver:getObjectInfoFailureObserver];
-                                        }];
-    }
-    enablePublicSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataSucceeded"
-                                                                        object:object
-                                                                         queue:[NSOperationQueue mainQueue]
-                                                                    usingBlock:^(NSNotification* notification) 
-                       {
-                           NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
-                           if (objectIsPublic) {
-                               [self.account.manager getObjectInfo:container object:object];
-                           }
-                           else {
-                               [activityIndicatorView removeFromSuperviewAndRelease];
-                               [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
-                                                     withRowAnimation:UITableViewRowAnimationTop];
-                           }
-                        
-                           [[NSNotificationCenter defaultCenter] removeObserver:enablePublicSuccessObserver];
-                       }];
-    
-    enablePublicFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataFailed"
-                                                                        object:object
-                                                                         queue:[NSOperationQueue mainQueue]
-                                                                    usingBlock:^(NSNotification* notification) 
-                       {
-                           [activityIndicatorView removeFromSuperviewAndRelease];
-                           objectIsPublic = !objectIsPublic;
-                           objectIsPublicSwitch.on = !objectIsPublicSwitch.on;
-                           object.publicURI = oldPubicURI;
-                           [self alert:@"There was a problem enabling the public link." request:[notification.userInfo objectForKey:@"request"]];           
-                           [[NSNotificationCenter defaultCenter] removeObserver:enablePublicFailureObserver];
-                       }];
-
-    
 }
 
-
-
-#pragma mark -
-#pragma mark Memory management
-
-- (void)dealloc {
-    [account release];
-    [downloadProgressView release];
-    [deleteActionSheet release];
-    [cdnURLActionSheet release];
-    [tableView release];
-    [folderViewController release];
-    [objectIsPublicSwitch release];
-    [permissions release];
-    [super dealloc];
-}
-
-
 @end