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