Use service catalog and update version
[pithos-ios] / Classes / StorageObjectViewController.m
index 8fa0d76..98c353d 100755 (executable)
 #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)
- CDN URL sections (action sheet to copy, open in safari, and email link)
+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
  
- 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."
+Versions
  
- Delete Object
- */
+Delete Object
+*/
 
 @implementation StorageObjectViewController
 
-@synthesize account, container, folder, object, tableView, folderViewController;
-@synthesize oldPubicURI, documentInteractionController;
+@synthesize account, container, folder, object, folderViewController;
+@synthesize oldPublicURI, permissions, documentInteractionController, objectIsReadOnly, versionID;
 
-- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
-    return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
-}
-
-- (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];    
-}
+#pragma mark - View lifecycle
 
-- (IBAction)homeButtonPressed:(id)sender {
-    [self.navigationController popToRootViewControllerAnimated:YES];
+- (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];
-    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;
     }
+    
+    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 {
        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"];
-
-    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];
-    } 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];
-    }
     objectIsPublic = ([object.publicURI length]);
     objectIsPublicSwitch.on = objectIsPublic;
     [self.tableView reloadData];
 }
 
-#pragma mark -
-#pragma mark Table view data source
+- (void)viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    if (!object.metadata) {
+        [self reloadMetadataSection];
+    } else {
+        [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;
+    
+    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."];
+        }
+    }
+}
+
+- (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 {
-    if (account.sharingAccount || account.shared)
-        return self.container.cdnEnabled ? 6 : 5;
-    else
-        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) {
-        if (objectIsReadOnly)
+        if (objectIsReadOnly) {
             return [object.metadata count];
-        else
-            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) {
-        if (account.sharingAccount)
-            return [permissions count];
-        else
-            return 1 + [permissions 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) {
-        NSString *publicLinkUrl = [NSString stringWithFormat:@"%@%@",
-                                   account.pithosPublicLinkURLPrefix,
-                                   self.object.publicURI];
-                                   
-        result = 30.0 + [self findLabelHeight:publicLinkUrl font:[UIFont systemFontOfSize:15.0]];
+        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 {
-    
-    static NSString *CellIdentifier = @"Cell";
-    static NSString *TextViewCellIdentifier = @"TextViewCell";
-    
-    if (indexPath.section == kPublic) {
-        if (indexPath.row == 1) {
-            TextViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:TextViewCellIdentifier];
-            if (cell == nil) {
-                cell = [[[TextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TextViewCellIdentifier] autorelease];
-                cell.textView.frame = cell.contentView.frame;
+    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 = [NSString stringWithFormat:@"%@%@",
-                                  account.pithosPublicLinkURLPrefix,
-                                  [NSString encodeToPercentEscape:self.object.publicURI charactersToEncode:@"!*'();:@&=+$,?%#[]"]];
-            
-            cell.selectionStyle = UITableViewCellSelectionStyleNone;
-            cell.accessoryView = UITableViewCellAccessoryNone;
-            return cell;
         }
+        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;
     }
     
+    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.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;
         } 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) {
         if (objectIsReadOnly) {
             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.section == kPermissions) {
+    } else if (indexPath.section == permissionsSection) {
         if (account.sharingAccount) {
             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]) {
-            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 = UITableViewCellSelectionStyleNone;
+            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.accessoryType = UITableViewCellAccessoryNone;
-                cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+            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.selectionStyle = UITableViewCellSelectionStyleBlue;
+            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) {
+    } else if (indexPath.section == versionsSection) {
         cell.selectionStyle = UITableViewCellSelectionStyleBlue;
         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
-        cell.textLabel.text = @"Delete Object";
+        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) {
             metadataValue = @"";
             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.removeMetadataEnabled = YES;
         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.removePermissionsEnabled = NO;
-            vc.navigationItem.title = @"Add Permissions";
-        }
-        else {
-            user = [[permissions allKeys] objectAtIndex:indexPath.row];
+            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.removePermissionsEnabled = YES;
-            vc.navigationItem.title = @"Edit Permissions";
+            vc.navigationItem.title = @"Edit Permission";
         }
         
-        vc.user = user;
+        vc.permissionUser = user;
         vc.permissions = permissions;
         vc.account = account;
         vc.container = container;
         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];
-                                
-                self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
-                self.documentInteractionController.delegate = self;
-
-                UITableViewCell *openFileCell = [self.tableView cellForRowAtIndexPath:indexPath];
-                if (![self.documentInteractionController presentOptionsMenuFromRect:openFileCell.frame 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."];
-                        [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];
             }
-            
-            NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
-            NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
-            
-            
-            if (![MFMailComposeViewController canSendMail]) {
-                [self alert:@"Cannot send mail" message:@"Your device has not been configured for sending email"];
-                [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
+        } 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 {
-                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.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];
             }
         }
     } 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
-            
-            NSString *activityMessage = @"Deleting file";
-            
-            activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
-            [activityIndicatorView addToView:self.view];
-
-            
-            [[self.account.manager deleteObject:self.container object:self.object] 
-             success:^(OpenStackRequest *request) {
-                 [activityIndicatorView removeFromSuperviewAndRelease];
-                 performingAction = NO;
-                 [self.folder.objects removeObjectForKey:self.object.name];
-                 
-                 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
-                     [self.navigationController popViewControllerAnimated:YES];
-                     if (account.shared)
-                         self.folderViewController.needsRefreshing = YES;
-                 } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
-                     if (!account.shared)
-                         [self.folderViewController setDetailViewController];
-                     else 
-                         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
-                             [self.folderViewController refreshButtonPressed:nil];
-                 }
-                 
-                 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];
-                 }
+    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];
              }
-             failure:^(OpenStackRequest *request) {
-                 [activityIndicatorView removeFromSuperviewAndRelease];
-                 [self hideToolbarActivityMessage];
-                 performingAction = NO;
-                 [self alert:@"There was a problem deleting this file." request:request]; 
-             }];
-        }
-        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];
+         }];
     }
+    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] 
-     success:^(OpenStackRequest *request) {
-         NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
-         if (objectIsPublic) {
-             [[self.account.manager getObjectInfo:container object:object] 
-              success:^(OpenStackRequest *request) {
-                  [activityIndicatorView removeFromSuperviewAndRelease];
-                  object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"];
-                  NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
-                  [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
-                                        withRowAnimation:UITableViewRowAnimationBottom];
-              }
-              failure:^(OpenStackRequest *request) {
-                  [activityIndicatorView removeFromSuperviewAndRelease];
-                  [self alert:@"There was a problem retrieving the public link from the server." request:request]; 
-              }];
-         }
-         else {
-             [activityIndicatorView removeFromSuperviewAndRelease];
-             [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
-                                   withRowAnimation:UITableViewRowAnimationTop];
-         }
-     }
-     failure:^(OpenStackRequest *request) {
-         [activityIndicatorView removeFromSuperviewAndRelease];
-         objectIsPublic = !objectIsPublic;
-         objectIsPublicSwitch.on = !objectIsPublicSwitch.on;
-         object.publicURI = oldPubicURI;
-         [self alert:@"There was a problem enabling the public link." request:request];           
-
-     }];    
-}
-
-
-
-#pragma mark -
-#pragma mark Memory management
-
-- (void)dealloc {
-    [account release];
-    [downloadProgressView release];
-    [deleteActionSheet release];
-    [cdnURLActionSheet release];
-    [tableView release];
-    [folderViewController release];
-    [objectIsPublicSwitch release];
-    [permissions release];
-    [documentInteractionController release];
-
-    [super dealloc];
-}
-
-
 @end