Added account groups functionality.
[pithos-ios] / Classes / StorageObjectViewController.m
1 //
2 //  StorageObjectViewController.m
3 //  OpenStack
4 //
5 //  Created by Mike Mayo on 12/19/10.
6 //  The OpenStack project is provided under the Apache 2.0 license.
7 //
8
9 #import "StorageObjectViewController.h"
10 #import "OpenStackAccount.h"
11 #import "OpenStackRequest.h"
12 #import "Container.h"
13 #import "Folder.h"
14 #import "StorageObject.h"
15 #import "AccountManager.h"
16 #import "AnimatedProgressView.h"
17 #import "UIViewController+Conveniences.h"
18 #import "MediaViewController.h"
19 #import "FolderViewController.h"
20 #import "UIColor+MoreColors.h"
21 #import "OpenStackAppDelegate.h"
22 #import "EditMetadataViewController.h"
23 #import "Provider.h"
24 #import "EditPermissionsViewController.h"
25
26 #define kDetails 0
27 #define kMetadata 1
28 #define kPublic 2
29 #define kPermissions 3
30
31 #define maxMetadataViewableLength 12
32
33
34 // TODO: use etag to reset download
35 // TODO: try downloading directly to the file to save memory.  don't use object.data
36
37 /*
38  Name                           whatever.txt
39  Full Path
40  Size                           123 KB
41  Content Type                   text/plain
42  
43  Metadata
44  Key                            Value -> tap goes to a metadata item VC to edit or delete
45  Key                            Value
46  Add Metadata... (if max not already reached)
47  
48  CDN URL sections (action sheet to copy, open in safari, and email link)
49  
50  Download File (if downloaded, Open File and Mail File as Attachment)
51  "After you download the file, you'll be able to attempt to open it and mail is as an attachment."
52  
53  Delete Object
54  */
55
56 @implementation StorageObjectViewController
57
58 @synthesize account, container, folder, object, tableView, folderViewController;
59 @synthesize oldPubicURI;
60
61 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
62     return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
63 }
64
65 - (void)setBackgroundView {
66     
67     UIImageView *logo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cloudfiles-large.png"]];
68     
69     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
70         UIView *backgroundContainer = [[UIView alloc] init];
71         backgroundContainer.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
72         backgroundContainer.backgroundColor = [UIColor iPadTableBackgroundColor];
73         logo.contentMode = UIViewContentModeScaleAspectFit;
74         logo.frame = CGRectMake(100.0, 100.0, 1000.0, 1000.0);
75         logo.alpha = 0.5;        
76         [backgroundContainer addSubview:logo];
77         tableView.backgroundView = backgroundContainer;
78         [backgroundContainer release];
79     } else {        
80         self.tableView.backgroundView = nil;
81     }
82     [logo release];    
83 }
84
85
86 #pragma mark -
87 #pragma mark View lifecycle
88
89 - (void)viewDidLoad {
90     [super viewDidLoad];
91     objectIsPublicSwitch = [[UISwitch alloc] init];
92     [objectIsPublicSwitch addTarget:self action:@selector(objectIsPublicSwitchChanged:) forControlEvents:UIControlEventValueChanged];
93     
94     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];
95     
96     permissions = [[NSMutableDictionary alloc] init];
97     NSArray *sharingArray = [object.sharing componentsSeparatedByString:@";"];
98     for (NSString *typeSpecificPermissions in sharingArray) { 
99         NSArray *array=[typeSpecificPermissions componentsSeparatedByString:@"="];
100         NSString *permissionsType = [array objectAtIndex:0];
101         if ([permissionsType hasPrefix:@" "])
102             permissionsType = [permissionsType substringFromIndex:1];
103                             
104         NSArray *users = [[array objectAtIndex:1] componentsSeparatedByString:@","];
105         for (NSString *user in users) {
106             [permissions setObject:permissionsType forKey:user];
107         }
108     }    
109 }
110
111 - (void)viewWillAppear:(BOOL)animated {
112     [super viewWillAppear:animated];
113     self.navigationItem.title = object.name;
114     
115     NSIndexPath*        selection = [self.tableView indexPathForSelectedRow];
116         if (selection)
117                 [self.tableView deselectRowAtIndexPath:selection animated:YES];
118     
119     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
120     NSString *documentsDirectory = [paths objectAtIndex:0];        
121     NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
122     NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
123     
124     NSFileManager *fileManager = [NSFileManager defaultManager];
125     fileDownloaded = [fileManager fileExistsAtPath:filePath];
126     
127     downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
128     
129     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
130         CGRect rect = downloadProgressView.frame;
131         rect.size.width = 440.0;
132         downloadProgressView.frame = rect;
133     }
134     
135     [self setBackgroundView];
136     if (self.container.cdnEnabled) {
137         cdnURLSection = 4;
138         actionsSection = 5;
139         deleteSection = 6;
140     } else {
141         cdnURLSection = -1;
142         actionsSection = 4;
143         deleteSection = 5;
144     }
145     
146     // let's see if we can tweet
147     UIApplication *app = [UIApplication sharedApplication];
148     NSURL *twitterURL = [NSURL URLWithString:@"twitter://post?message=test"];
149
150     if ([app canOpenURL:twitterURL]) {
151         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];
152     } else {
153         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];
154     }
155     
156     objectIsPublic = (object.publicURI != nil);
157     objectIsPublicSwitch.on = objectIsPublic;
158     [self.tableView reloadData];
159 }
160
161 #pragma mark -
162 #pragma mark Table view data source
163
164 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
165     return self.container.cdnEnabled ? 7 : 6;
166 }
167
168 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
169     if (section == kDetails) {
170         return 4;
171     } else if (section == kMetadata) {
172         return 1 + [object.metadata count];
173     } else if (section == cdnURLSection) {
174         return 1;
175     } else if (section == actionsSection) {
176         return fileDownloaded ? 2 : 1;
177     } else if (section == kPublic) {
178         return objectIsPublic ? 2 : 1;
179     } else if (section == kPermissions) {
180         return 1 + [permissions count];
181     } else {
182         return 1;
183     }
184 }
185
186 - (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font {
187     CGSize textLabelSize;    
188     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
189         // 616, 678
190         textLabelSize = CGSizeMake(596.0, 9000.0f);
191     } else {
192         textLabelSize = CGSizeMake(280.0, 9000.0f);
193     }
194     CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeCharacterWrap];
195     return stringSize.height;
196 }
197
198 - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
199     CGFloat result = aTableView.rowHeight;
200     
201     if (indexPath.section == cdnURLSection) {
202         result = 22.0 + [self findLabelHeight:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] font:[UIFont systemFontOfSize:18.0]];
203     } else if (indexPath.section == kDetails && indexPath.row == 1) {
204         CGSize textLabelSize;
205         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
206             textLabelSize = CGSizeMake(537.0, 9000.0f);
207         } else {
208             textLabelSize = CGSizeMake(221.0, 9000.0f);
209         }
210         CGSize stringSize = [object.fullPath sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
211         return 22.0 + stringSize.height;
212     } else if (indexPath.section == kDetails && indexPath.row == 0) {
213         CGSize textLabelSize;
214         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
215             textLabelSize = CGSizeMake(537.0, 9000.0f);
216         } else {
217             textLabelSize = CGSizeMake(221.0, 9000.0f);
218         }
219         CGSize stringSize = [object.name sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
220         return 22.0 + stringSize.height;
221     } else if (indexPath.section == kPublic && indexPath.row == 1) {
222         result = 22.0 + [self findLabelHeight:[NSString stringWithFormat:@"%@%@", self.account.provider.authEndpointURL, self.object.fullPath] font:[UIFont systemFontOfSize:15.0]];
223         return result;
224     }
225     
226     return MAX(aTableView.rowHeight, result);
227 }
228
229 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
230     
231     static NSString *CellIdentifier = @"Cell";
232     
233     UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
234     if (cell == nil) {
235         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
236         //cell.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.93];
237         cell.textLabel.backgroundColor = [UIColor clearColor];
238         cell.detailTextLabel.backgroundColor = [UIColor clearColor];
239         cell.detailTextLabel.numberOfLines = 0;
240         cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
241     }
242     
243     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
244         cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8];
245     }
246     
247     cell.detailTextLabel.textAlignment = UITextAlignmentRight;
248     cell.accessoryView = nil;
249     cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
250     
251     // Configure the cell...
252     if (indexPath.section == kDetails) {
253         cell.accessoryType = UITableViewCellAccessoryNone;
254         cell.selectionStyle = UITableViewCellSelectionStyleNone;
255         cell.accessoryView = nil;
256         if (indexPath.row == 0) {
257             cell.textLabel.text = @"Name";
258             cell.detailTextLabel.text = object.name;
259         } else if (indexPath.row == 1) {
260             cell.textLabel.text = @"Full Path";
261             cell.detailTextLabel.text = object.fullPath;
262         } else if (indexPath.row == 2) {
263             cell.textLabel.text = @"Size";
264             cell.detailTextLabel.text = [object humanizedBytes];
265         } else if (indexPath.row == 3) {
266             cell.textLabel.text = @"Type";
267             cell.detailTextLabel.text = object.contentType;
268         }
269     } else if (indexPath.section == kMetadata) {
270         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
271         cell.selectionStyle = UITableViewCellSelectionStyleBlue;
272         cell.accessoryView = nil;
273         if (indexPath.row == [object.metadata count]) {
274             cell.textLabel.text = @"Add Metadata";
275             cell.detailTextLabel.text = @"";
276         } else {
277             NSString *key = [[object.metadata allKeys] objectAtIndex:indexPath.row];
278             NSString *value = [object.metadata objectForKey:key];
279             NSString *metadataKeyCellText = key;
280             NSString *metadataValueCellText = value;
281             if ([metadataKeyCellText length] > maxMetadataViewableLength) {
282                 metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
283                 metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
284             }
285             if ([metadataValueCellText length] > maxMetadataViewableLength) {
286                 metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
287                 metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
288             }
289             
290             cell.textLabel.text = metadataKeyCellText;
291             cell.detailTextLabel.text = metadataValueCellText;
292         }
293     } else if (indexPath.section == kPublic) {
294         if (indexPath.row == 0) {
295             cell.textLabel.text = @"Public URL";        
296             cell.detailTextLabel.text = @"";
297             cell.accessoryView = objectIsPublicSwitch;
298             cell.accessoryType = UITableViewCellAccessoryNone;
299             cell.selectionStyle = UITableViewCellSelectionStyleNone;
300         }
301         else if (indexPath.row == 1) {
302             cell.textLabel.numberOfLines = 0;
303             cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
304             cell.textLabel.text = [NSString stringWithFormat:@"%@%@", self.account.provider.authEndpointURL, object.publicURI];
305             cell.textLabel.font = [UIFont systemFontOfSize:15.0];
306             cell.selectionStyle = UITableViewCellSelectionStyleNone;
307             cell.accessoryView = UITableViewCellAccessoryNone;
308             cell.detailTextLabel.text = @"";
309         }
310     } else if (indexPath.section == kPermissions) {
311         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
312         cell.selectionStyle = UITableViewCellSelectionStyleBlue;
313         cell.accessoryView = nil;
314         if (indexPath.row == [permissions count]) {
315             cell.textLabel.text = @"Add Permissions";
316             cell.detailTextLabel.text = @""; 
317         }
318         else {
319             NSString *user = [[permissions allKeys] objectAtIndex:indexPath.row];
320             cell.textLabel.text = user;
321             cell.detailTextLabel.text = [permissions objectForKey:user];
322         }
323     } else if (indexPath.section == cdnURLSection) {
324         cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
325         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
326         cell.textLabel.text = @"";
327         cell.detailTextLabel.text = [[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
328     } else if (indexPath.section == actionsSection) {
329         cell.accessoryView = nil;
330         if (performingAction) {
331             cell.textLabel.textColor = [UIColor grayColor];
332             cell.selectionStyle = UITableViewCellSelectionStyleNone;
333             cell.accessoryType = UITableViewCellAccessoryNone;
334         } else {
335             cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
336             cell.selectionStyle = UITableViewCellSelectionStyleBlue;
337         }
338
339         if (indexPath.row == 0) {
340             if (fileDownloaded) {
341                 cell.textLabel.text = @"Open File";
342             } else {
343                 if (fileDownloading) {
344                     cell.accessoryView = downloadProgressView;
345                     // TODO: if you leave this view while downloading, there's EXC_BAD_ACCESS
346                     cell.textLabel.text = @"Downloading";
347                 } else {
348                     cell.textLabel.text = @"Download File";
349                 }
350             }
351             cell.detailTextLabel.text = @"";
352             
353         } else if (indexPath.row == 1) {
354             cell.textLabel.text = @"Email File as Attachment";
355             cell.detailTextLabel.text = @"";
356         }
357     } else if (indexPath.section == deleteSection) {
358         cell.textLabel.text = @"Delete Object";
359         cell.detailTextLabel.text = @"";
360         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
361         cell.selectionStyle = UITableViewCellSelectionStyleBlue;
362     }
363     
364     return cell;
365 }
366
367
368 #pragma mark -
369 #pragma mark Table view delegate
370
371 - (void)reloadActionsTitleRow:(NSTimer *)timer {
372     [[timer.userInfo objectForKey:@"tableView"] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
373 }
374
375 - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
376     if (indexPath.section == kMetadata) {
377         EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
378         NSString *metadataKey;
379         NSString *metadataValue;
380         
381         if (indexPath.row == [self.object.metadata count]) {
382             metadataKey = @"";
383             metadataValue = @"";
384             vc.deleteEnabled = FALSE;
385             vc.navigationItem.title = @"Add Metadata";
386         }
387         else {
388             metadataKey = [[self.object.metadata allKeys] objectAtIndex:indexPath.row];
389             metadataValue = [self.object.metadata objectForKey:metadataKey];
390             vc.deleteEnabled = YES;
391             vc.navigationItem.title = @"Edit Metadata";
392         }
393
394         vc.metadataKey = metadataKey;
395         vc.metadataValue = metadataValue;
396         vc.account = account;
397         vc.container = container;
398         vc.object = object;
399         [self.navigationController pushViewController:vc animated:YES];
400         [vc release];
401     } else if (indexPath.section == kPermissions) {
402         EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
403         NSString *user;
404         
405         if (indexPath.row == [permissions count]) {
406             user = @"";
407             vc.newPermissionsEntry = TRUE;
408             vc.navigationItem.title = @"Add Permissions";
409         }
410         else {
411             user = [[permissions allKeys] objectAtIndex:indexPath.row];
412             NSString *userPermissions = [permissions objectForKey:user];
413             if ([userPermissions rangeOfString:@"read"].location != NSNotFound)
414                 vc.readPermissionSelected = YES;
415             else
416                 vc.readPermissionSelected = NO;
417             
418             if ([userPermissions rangeOfString:@"write"].location != NSNotFound)
419                 vc.writePermissionSelected = YES;
420             else
421                 vc.writePermissionSelected = NO;
422             
423             vc.newPermissionsEntry = FALSE;
424             vc.navigationItem.title = @"Edit Permissions";
425         }
426         
427         vc.user = user;
428         vc.permissions = permissions;
429         vc.account = account;
430         vc.container = container;
431         vc.object = object;
432         [self.navigationController pushViewController:vc animated:YES];
433         [vc release];
434     } else if (indexPath.section == cdnURLSection) {
435         [cdnURLActionSheet showInView:self.view];
436     } else if (indexPath.section == actionsSection) {
437         if (indexPath.row == 0) {
438             if (fileDownloaded) {
439                 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
440                 NSString *documentsDirectory = [paths objectAtIndex:0];        
441                 NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
442                 NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
443                                 
444                 UIDocumentInteractionController *vc = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
445                 vc.delegate = self;
446                 
447                 if (![vc presentPreviewAnimated:YES]) {
448                     
449                     if ([self.object isPlayableMedia]) {
450                         MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
451                         vc.container = self.container;
452                         vc.object = self.object;
453                         [self.navigationController pushViewController:vc animated:YES];
454                         [vc release];
455                     } else {
456                         [self alert:@"Error" message:@"This file could not be opened."];
457                         [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
458                     }
459                 }
460                 
461                 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];                
462                 
463             } else {                
464                 if (!fileDownloading) {
465                     // download the file
466                     fileDownloading = YES;
467                     [self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self];
468                     [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
469                 }
470                 
471             }
472         } else if (indexPath.row == 1) {
473             
474             NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
475             NSString *documentsDirectory = [paths objectAtIndex:0];        
476             NSString *shortPath = @"";
477             
478             if (self.container && [self.container respondsToSelector:@selector(name)] && self.object && [self.object respondsToSelector:@selector(fullPath)]) {
479                 shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
480             }
481             
482             NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
483             NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
484             
485             MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
486             vc.mailComposeDelegate = self;              
487             [vc setSubject:self.object.name];
488             [vc addAttachmentData:data mimeType:self.object.contentType fileName:self.object.name];
489             [vc setMessageBody:@"" isHTML:NO];    
490             if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
491                 vc.modalPresentationStyle = UIModalPresentationPageSheet;
492             }                
493             [self presentModalViewController:vc animated:YES];
494             [vc release];        
495         }
496     } else if (indexPath.section == deleteSection) {
497         [deleteActionSheet showInView:self.view];
498     }
499 }
500
501 - (void)setProgress:(float)newProgress {
502     [downloadProgressView setProgress:newProgress animated:YES];    
503     if (newProgress >= 1.0) {
504         fileDownloading = NO;
505         fileDownloaded = YES;
506         
507         [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:1 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationBottom];
508         [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
509     }
510 }
511
512 #pragma mark -
513 #pragma mark Document Interation Controller Delegate
514
515 - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *) controller {
516     return self.navigationController;
517 }
518
519 - (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controllers {
520     [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
521 }
522
523 #pragma mark -
524 #pragma mark Action Sheet Delegate
525
526 - (void)deleteObjectRow {
527     [self.folderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
528 }
529
530 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
531     if ([actionSheet isEqual:deleteActionSheet]) {
532         if (buttonIndex == 0) {
533             // delete the file and pop out
534             [self showToolbarActivityMessage:@"Deleting file..."];
535             
536             [self.account.manager deleteObject:self.container object:self.object];
537             
538             deleteSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectSucceeded" object:self.object
539                                                                                                queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
540             {
541                 [self hideToolbarActivityMessage];
542                 performingAction = NO;
543                 [self.folder.objects removeObjectForKey:self.object.name];
544                 [self.navigationController popViewControllerAnimated:YES];
545                 if ([self.folder.objects count] + [self.folder.folders count] == 0) {
546                     [self.folderViewController.tableView reloadData];
547                 } else {
548                     [self.folderViewController.tableView selectRowAtIndexPath:selectedIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
549                     [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteObjectRow) userInfo:nil repeats:NO];
550                 }
551                 [[NSNotificationCenter defaultCenter] removeObserver:deleteSuccessObserver];
552             }];
553
554             deleteFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectFailed" object:self.object
555                                                                                        queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
556             {
557                 [self hideToolbarActivityMessage];
558                 performingAction = NO;
559                 [self alert:@"There was a problem deleting this file." request:[notification.userInfo objectForKey:@"request"]];
560
561                 [[NSNotificationCenter defaultCenter] removeObserver:deleteFailureObserver];
562
563             }];
564             
565         }
566         NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:deleteSection];
567         [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
568     } else if ([actionSheet isEqual:cdnURLActionSheet]) {
569         NSURL *url = [NSURL URLWithString:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
570
571         if (buttonIndex == 0) {
572             // copy to pasteboard
573             UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
574             [pasteboard setString:[url description]];
575         } else if (buttonIndex == 1) {
576             // open in safari
577             UIApplication *application = [UIApplication sharedApplication];
578             if ([application canOpenURL:url]) {
579                 [application openURL:url];
580             } else {
581                 [self alert:@"Error" message:[NSString stringWithFormat:@"This URL cannot be opened.\n%@", url]];
582             }
583         } else if (buttonIndex == 2) {
584             // email link to file
585             MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
586             vc.mailComposeDelegate = self;              
587             [vc setSubject:self.object.name];
588             [vc setMessageBody:[url description] isHTML:NO];
589             if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
590                 vc.modalPresentationStyle = UIModalPresentationPageSheet;
591             }                
592             [self presentModalViewController:vc animated:YES];
593             [vc release];        
594         } else if (buttonIndex == 3) {
595             // tweet link to file
596             UIApplication *app = [UIApplication sharedApplication];            
597             NSURL *twitterURL = [NSURL URLWithString:[NSString stringWithFormat:@"twitter://post?message=%@", [[url description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
598             if ([app canOpenURL:twitterURL]) {
599                 [app openURL:twitterURL];
600             }
601         }
602         [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:cdnURLSection] animated:YES];
603     }
604 }
605
606 #pragma mark -
607 #pragma mark Mail Composer Delegate
608
609 // Dismisses the email composition interface when users tap Cancel or Send.
610 - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {  
611         [self dismissModalViewControllerAnimated:YES];
612     [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:actionsSection] animated:YES];
613 }
614
615 #pragma mark -
616 #pragma mark Switches
617
618 - (void)objectIsPublicSwitchChanged:(id)sender
619 {
620     NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."];
621     self.oldPubicURI = object.publicURI;
622     
623     if (objectIsPublic) {
624         activityMessage = [NSString stringWithFormat:@"Disabling public link.."];
625         object.publicURI = @"";
626     }
627     else {
628         object.publicURI = @"TRUE";
629     }
630     
631     activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
632     [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];    
633     
634     objectIsPublic = !objectIsPublic;
635     [self.account.manager writeObjectMetadata:container object:object];
636     if (objectIsPublic) {
637         getObjectInfoSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectInfoSucceeded"
638                                                                                          object:object
639                                                                                           queue:[NSOperationQueue mainQueue]
640                                                                                      usingBlock:^(NSNotification *notification)
641                                     {
642                                         [activityIndicatorView removeFromSuperviewAndRelease];
643                                         OpenStackRequest *request = [notification.userInfo objectForKey:@"request"];
644                                         object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"];
645                                         NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
646                                         [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
647                                                               withRowAnimation:UITableViewRowAnimationBottom];
648                                         
649                                         [[NSNotificationCenter defaultCenter] removeObserver:getObjectInfoSuccessObserver];
650                                     }];
651
652         getObjectInfoFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectInfoFailed"
653                                                                                             object:object
654                                                                                              queue:[NSOperationQueue mainQueue]
655                                                                                         usingBlock:^(NSNotification *notification)
656                                         {
657                                             [activityIndicatorView removeFromSuperviewAndRelease];
658                                             [self alert:@"There was a problem retrieving the public link from the server." request:[notification.userInfo objectForKey:@"request"]]; 
659                                             [[NSNotificationCenter defaultCenter] removeObserver:getObjectInfoFailureObserver];
660                                         }];
661     }
662     enablePublicSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataSucceeded"
663                                                                         object:object
664                                                                          queue:[NSOperationQueue mainQueue]
665                                                                     usingBlock:^(NSNotification* notification) 
666                        {
667                            NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
668                            if (objectIsPublic) {
669                                [self.account.manager getObjectInfo:container object:object];
670                            }
671                            else {
672                                [activityIndicatorView removeFromSuperviewAndRelease];
673                                [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
674                                                      withRowAnimation:UITableViewRowAnimationTop];
675                            }
676                         
677                            [[NSNotificationCenter defaultCenter] removeObserver:enablePublicSuccessObserver];
678                        }];
679     
680     enablePublicFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataFailed"
681                                                                         object:object
682                                                                          queue:[NSOperationQueue mainQueue]
683                                                                     usingBlock:^(NSNotification* notification) 
684                        {
685                            [activityIndicatorView removeFromSuperviewAndRelease];
686                            objectIsPublic = !objectIsPublic;
687                            objectIsPublicSwitch.on = !objectIsPublicSwitch.on;
688                            object.publicURI = oldPubicURI;
689                            [self alert:@"There was a problem enabling the public link." request:[notification.userInfo objectForKey:@"request"]];           
690                            [[NSNotificationCenter defaultCenter] removeObserver:enablePublicFailureObserver];
691                        }];
692
693     
694 }
695
696
697
698 #pragma mark -
699 #pragma mark Memory management
700
701 - (void)dealloc {
702     [account release];
703     [downloadProgressView release];
704     [deleteActionSheet release];
705     [cdnURLActionSheet release];
706     [tableView release];
707     [folderViewController release];
708     [objectIsPublicSwitch release];
709     [permissions release];
710     [super dealloc];
711 }
712
713
714 @end
715