Fix bug in iOS 4.3 where a folder view controller didn't reload it's tableview when...
[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 #import "TextViewCell.h"
26 #import "NSString+Conveniences.h"
27 #import "APICallback.h"
28 #import "ObjectVersionsViewController.h"
29
30 #define kDetails 0
31 #define kMetadata 2
32
33 #define maxMetadataViewableLength 12
34
35
36 // TODO: use etag to reset download
37 // TODO: try downloading directly to the file to save memory.  don't use object.data
38
39 /*
40  Name                           whatever.txt
41  Full Path
42  Size                           123 KB
43  Content Type                   text/plain
44  
45  Metadata
46  Key                            Value -> tap goes to a metadata item VC to edit or delete
47  Key                            Value
48  Add Metadata... (if max not already reached)
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, actionSelectedIndexPath, objectIsReadOnly, versionID;
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         
95     objectIsPublicSwitch = [[UISwitch alloc] init];
96     [objectIsPublicSwitch addTarget:self action:@selector(objectIsPublicSwitchChanged:) forControlEvents:UIControlEventValueChanged];
97     
98     permissions = [[NSMutableDictionary alloc] init];
99     if (object.sharing.length > 0) {
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     objectIsReadOnly = NO;
114     if (account.sharingAccount) { 
115         if ([permissions count] > 0) {
116             objectIsReadOnly = [[permissions objectForKey:[account username]] isEqualToString:@"read"];
117         }
118         objectIsPublicSwitch.enabled = NO;
119     }
120     
121     downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
122     fileDownloading = ([self.account.manager.objectDownloadRequests objectForKey:object.fullPath] != nil) ? YES : NO;
123     if (fileDownloading) {
124         OpenStackRequest *request = [self.account.manager.objectDownloadRequests objectForKey:object.fullPath];
125         self.actionSelectedIndexPath = [request.userInfo objectForKey:@"actionSelectedIndexPath"];
126         [request setDownloadProgressDelegate:self];
127     }
128     appDelegate = [[UIApplication sharedApplication] delegate];
129 }
130
131 - (void)viewWillAppear:(BOOL)animated {
132     [super viewWillAppear:animated];
133     self.navigationItem.title = object.name;
134     
135     NSIndexPath *selection = [self.tableView indexPathForSelectedRow];
136         if (selection)
137                 [self.tableView deselectRowAtIndexPath:selection animated:YES];
138     if ([appDelegate.cachedObjectsDictionary objectForKey:object.hash] != nil) {
139         if ([[NSFileManager defaultManager] fileExistsAtPath:[appDelegate.cachedObjectsDictionary objectForKey:object.hash]])
140             fileDownloaded = YES;
141         else {
142             fileDownloaded = NO;
143             [appDelegate.cachedObjectsDictionary removeObjectForKey:object.hash];
144             [appDelegate saveCacheDictionary];
145         }
146     } else {
147         fileDownloaded = NO;
148     }
149     
150     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
151         CGRect rect = downloadProgressView.frame;
152         rect.size.width = 440.0;
153         downloadProgressView.frame = rect;
154     }
155     
156     //[self setBackgroundView];
157     actionsSection = 1;
158     publicLinkSection = 3;
159     permissionsSection = 4;
160     versionsSection = 5;
161     deleteSection = 6;
162     
163     if (versionID) {
164         publicLinkSection = -1;
165         permissionsSection = -1;
166         versionsSection = -1;
167         deleteSection = -1;
168         objectIsReadOnly = YES;
169     }
170     if (account.sharingAccount || account.shared) {
171         versionsSection = -1;
172         deleteSection = -1;
173     }
174     
175     objectIsPublic = ([object.publicURI length]);
176     objectIsPublicSwitch.on = objectIsPublic;
177     [self.tableView reloadData];
178 }
179
180 - (void)viewDidAppear:(BOOL)animated {
181     if (object.metadata == nil) {
182         [self reloadMetadataSection];
183     }
184 }
185
186 #pragma mark -
187 #pragma mark Table view data source
188
189 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
190
191     int numberOfSections = 7;
192     if (deleteSection < 0)
193         numberOfSections--;
194     if (versionsSection < 0)
195         numberOfSections--;
196     if (publicLinkSection < 0)
197         numberOfSections--;
198     if (permissionsSection < 0)
199         numberOfSections--;
200
201     return numberOfSections;
202 }
203
204 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
205     if (section == kDetails) {
206         return 4;
207     } else if (section == kMetadata) {
208         if (objectIsReadOnly) {
209             return [object.metadata count];
210         } else {
211             return 1 + [object.metadata count];
212         }
213     } else if (section == actionsSection) {
214         return 2;
215     } else if (section == publicLinkSection) {
216         return objectIsPublic ? 2 : 1;
217     } else if (section == permissionsSection) {
218         if (account.sharingAccount || objectIsReadOnly)
219             return [permissions count];
220         else
221             return 1 + [permissions count];
222     } else {
223         return 1;
224     }
225 }
226
227 - (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font {
228     CGSize textLabelSize;    
229     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
230         // 616, 678
231         textLabelSize = CGSizeMake(596.0, 9000.0f);
232     } else {
233         textLabelSize = CGSizeMake(280.0, 9000.0f);
234     }
235     CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeCharacterWrap];
236     return stringSize.height;
237 }
238
239 - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
240     CGFloat result = aTableView.rowHeight;
241
242     if (indexPath.section == kDetails && indexPath.row == 1) {
243         CGSize textLabelSize;
244         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
245             textLabelSize = CGSizeMake(537.0, 9000.0f);
246         } else {
247             textLabelSize = CGSizeMake(221.0, 9000.0f);
248         }
249         CGSize stringSize = [object.fullPath sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
250         return 22.0 + stringSize.height;
251     } else if (indexPath.section == kDetails && indexPath.row == 0) {
252         CGSize textLabelSize;
253         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
254             textLabelSize = CGSizeMake(537.0, 9000.0f);
255         } else {
256             textLabelSize = CGSizeMake(221.0, 9000.0f);
257         }
258         CGSize stringSize = [object.name sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
259         return 22.0 + stringSize.height;
260     } else if (indexPath.section == publicLinkSection && indexPath.row == 1) {
261         NSString *publicLinkUrl = [NSString stringWithFormat:@"%@%@",
262                                    account.pithosPublicLinkURLPrefix,
263                                    self.object.publicURI];
264                                    
265         result = 30.0 + [self findLabelHeight:publicLinkUrl font:[UIFont systemFontOfSize:15.0]];
266     }
267     return MAX(aTableView.rowHeight, result);
268 }
269
270 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
271     if ((indexPath.section == publicLinkSection) && (indexPath.row == 1)) {
272         static NSString *CellIdentifier = @"TextViewCell";
273         TextViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
274         if (cell == nil) {
275             cell = [[[TextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
276             cell.textView.frame = cell.contentView.frame;
277
278         }
279         cell.selectionStyle = UITableViewCellSelectionStyleNone;
280         cell.textView.backgroundColor = [UIColor clearColor];
281         cell.textView.font = [UIFont systemFontOfSize:15.0];
282         cell.textView.dataDetectorTypes = UIDataDetectorTypeLink;
283         cell.textView.text = [NSString stringWithFormat:@"%@%@",
284                               account.pithosPublicLinkURLPrefix,
285                               [NSString encodeToPercentEscape:self.object.publicURI charactersToEncode:@"!*'();:@&=+$,?%#[]"]];
286         cell.selectionStyle = UITableViewCellSelectionStyleNone;
287         cell.accessoryView = UITableViewCellAccessoryNone;
288         return cell;
289     }
290     
291     if (indexPath.section == deleteSection) {
292         static NSString *CellIdentifier = @"DeleteCell";
293         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
294         if (cell == nil) {
295             cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
296             cell.selectionStyle = UITableViewCellSelectionStyleBlue;
297             cell.textLabel.textAlignment = UITextAlignmentCenter;
298             cell.textLabel.text = @"Delete Object";
299         }
300         return cell;
301     }
302     
303     static NSString *CellIdentifier = @"Cell";
304     UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
305     if (cell == nil) {
306         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
307         cell.textLabel.backgroundColor = [UIColor clearColor];
308         cell.detailTextLabel.backgroundColor = [UIColor clearColor];
309         cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
310     }
311     cell.textLabel.textColor = [UIColor blackColor];
312     cell.detailTextLabel.numberOfLines = 0;
313     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
314         cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8];
315     }
316     cell.userInteractionEnabled = YES;
317     cell.detailTextLabel.textAlignment = UITextAlignmentRight;
318     cell.accessoryView = nil;
319     cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
320     
321     // Configure the cell...
322     if (indexPath.section == kDetails) {
323         cell.accessoryType = UITableViewCellAccessoryNone;
324         cell.selectionStyle = UITableViewCellSelectionStyleNone;
325         cell.accessoryView = nil;
326         if (indexPath.row == 0) {
327             cell.textLabel.text = @"Name";
328             cell.detailTextLabel.text = object.name;
329         } else if (indexPath.row == 1) {
330             cell.textLabel.text = @"Full Path";
331             cell.detailTextLabel.text = object.fullPath;
332         } else if (indexPath.row == 2) {
333             cell.textLabel.text = @"Size";
334             cell.detailTextLabel.text = [object humanizedBytes];
335         } else if (indexPath.row == 3) {
336             cell.textLabel.text = @"Type";
337             cell.detailTextLabel.text = object.contentType;
338         }
339     } else if (indexPath.section == kMetadata) {
340         if (objectIsReadOnly) {
341             cell.accessoryType = UITableViewCellAccessoryNone;
342             cell.selectionStyle = UITableViewCellSelectionStyleNone;
343             cell.userInteractionEnabled = NO;
344         }
345         else {
346             cell.selectionStyle = UITableViewCellSelectionStyleBlue;
347             cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
348         }
349         
350         cell.accessoryView = nil;
351         if (indexPath.row == [object.metadata count]) {
352             cell.textLabel.text = @"Add Metadata";
353             cell.detailTextLabel.text = @"";
354         } else {
355             NSString *key = [[object.metadata allKeys] objectAtIndex:indexPath.row];
356             NSString *value = [object.metadata objectForKey:key];
357             NSString *metadataKeyCellText = key;
358             NSString *metadataValueCellText = value;
359             if ([metadataKeyCellText length] > maxMetadataViewableLength) {
360                 metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
361                 metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
362             }
363             if ([metadataValueCellText length] > maxMetadataViewableLength) {
364                 metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
365                 metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
366             }
367             
368             cell.textLabel.text = metadataKeyCellText;
369             cell.detailTextLabel.text = metadataValueCellText;
370         }
371     } else if (indexPath.section == publicLinkSection) {
372         if (indexPath.row == 0) {
373             cell.textLabel.text = @"Public URL";        
374             cell.detailTextLabel.text = @"";
375             cell.accessoryView = objectIsPublicSwitch;
376             cell.accessoryType = UITableViewCellAccessoryNone;
377             cell.selectionStyle = UITableViewCellSelectionStyleNone;
378         }
379     } else if (indexPath.section == permissionsSection) {
380         if (account.sharingAccount) {
381             cell.accessoryType = UITableViewCellAccessoryNone;
382             cell.selectionStyle = UITableViewCellSelectionStyleNone;
383             cell.userInteractionEnabled = NO;
384         }
385         else {
386             cell.selectionStyle = UITableViewCellSelectionStyleBlue;
387             cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
388         }
389         cell.accessoryView = nil;
390         
391         if (indexPath.row == [permissions count]) {
392             cell.textLabel.text = @"Share";
393             cell.detailTextLabel.text = @""; 
394         } else {
395             NSString *user = [[permissions allKeys] objectAtIndex:indexPath.row];
396             cell.textLabel.text = user;
397             NSString *accessType;
398             if ([[permissions objectForKey:user] isEqualToString:@"write"])
399                 accessType = @"Read/Write";
400             else
401                 accessType = @"Read Only";
402             cell.detailTextLabel.numberOfLines = 1;
403             cell.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation;
404             cell.detailTextLabel.text = accessType;
405         }
406     } else if (indexPath.section == actionsSection) {
407         if (fileDownloading) {
408             if (actionSelectedIndexPath.row == indexPath.row) {
409                 cell.accessoryView = downloadProgressView;
410             } else {
411                 cell.textLabel.textColor = [UIColor grayColor];
412                 cell.selectionStyle = UITableViewCellSelectionStyleNone;
413             }
414         } else {
415             cell.selectionStyle = UITableViewCellSelectionStyleBlue;
416             cell.accessoryView = nil;
417         }
418         if (indexPath.row == 0) {
419             cell.textLabel.text = @"Open File"; 
420             cell.accessoryType = UITableViewCellAccessoryNone;
421             cell.detailTextLabel.text = @"";
422             
423         } else if (indexPath.row == 1) {
424             cell.textLabel.text = @"Email File as Attachment";
425             cell.detailTextLabel.text = @"";
426             cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
427         }
428     } else if (indexPath.section == versionsSection) {
429         cell.selectionStyle = UITableViewCellSelectionStyleBlue;
430         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
431         cell.textLabel.text = @"Versions";
432         cell.detailTextLabel.text = @"";
433     }
434     
435     return cell;
436 }
437
438 #pragma mark -
439 #pragma mark Table view delegate
440
441 - (void)reloadActionsTitleRow:(NSTimer *)timer {
442     [[timer.userInfo objectForKey:@"tableView"] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
443 }
444
445 - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
446     if (indexPath.section == kMetadata) {
447         EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
448         NSString *metadataKey;
449         NSString *metadataValue;
450         
451         if (indexPath.row == [self.object.metadata count]) {
452             metadataKey = @"";
453             metadataValue = @"";
454             vc.removeMetadataEnabled = FALSE;
455             vc.navigationItem.title = @"Add Metadata";
456         }
457         else {
458             metadataKey = [[self.object.metadata allKeys] objectAtIndex:indexPath.row];
459             metadataValue = [self.object.metadata objectForKey:metadataKey];
460             vc.removeMetadataEnabled = YES;
461             vc.navigationItem.title = @"Edit Metadata";
462         }
463
464         vc.metadataKey = metadataKey;
465         vc.metadataValue = metadataValue;
466         vc.account = account;
467         vc.container = container;
468         vc.object = object;
469         [self.navigationController pushViewController:vc animated:YES];
470         [vc release];
471     } else if (indexPath.section == permissionsSection) {
472         EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
473         NSString *user;
474         
475         if (indexPath.row == [permissions count]) {
476             user = @"";
477             vc.removePermissionsEnabled = NO;
478             vc.navigationItem.title = @"Share";
479         }
480         else {
481             user = [[permissions allKeys] objectAtIndex:indexPath.row];
482             NSString *userPermissions = [permissions objectForKey:user];
483             if ([userPermissions rangeOfString:@"read"].location != NSNotFound)
484                 vc.readPermissionSelected = YES;
485             else
486                 vc.readPermissionSelected = NO;
487             
488             if ([userPermissions rangeOfString:@"write"].location != NSNotFound)
489                 vc.writePermissionSelected = YES;
490             else
491                 vc.writePermissionSelected = NO;
492             
493             vc.removePermissionsEnabled = YES;
494             vc.navigationItem.title = @"Edit Sharing";
495         }
496         
497         vc.user = user;
498         vc.permissions = permissions;
499         vc.account = account;
500         vc.container = container;
501         vc.object = object;
502         vc.folderViewController = folderViewController;
503         [self.navigationController pushViewController:vc animated:YES];
504         [vc release];
505     } else if (indexPath.section == actionsSection) {
506         if (!fileDownloaded) {
507             if (!fileDownloading) {
508                 // download the file
509                 fileDownloading = YES;
510                 self.actionSelectedIndexPath = indexPath;
511                 [self.tableView reloadData];
512                 NSMutableDictionary *requestUserInfo = [NSDictionary dictionaryWithObject:indexPath forKey:@"actionSelectedIndexPath"];
513                 [[self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self requestUserInfo:requestUserInfo version:versionID]
514                  success:^(OpenStackRequest *request) {
515                      fileDownloaded = YES;
516                      fileDownloading = NO;
517                      if ([self.navigationController.visibleViewController isEqual:self])
518                          [self.tableView.delegate tableView:self.tableView didSelectRowAtIndexPath:indexPath]; 
519                      [self.tableView reloadData];
520                      [self.folderViewController reloadData];
521                  }
522                  failure:^(OpenStackRequest *request) {
523                      fileDownloaded = NO;
524                      fileDownloading = NO;
525                      [self alert:@"File failed to download" request:request];
526                      [self.tableView reloadData];
527                  }];
528                 [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
529             }
530         } else if (indexPath.row == 0) {        
531             if (fileDownloaded) {
532                 NSString *filePath = [appDelegate.cachedObjectsDictionary objectForKey:object.hash];                                 
533                 self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
534                 self.documentInteractionController.delegate = self;
535                 self.documentInteractionController.name = object.name;
536
537                 UITableViewCell *openFileCell = [self.tableView cellForRowAtIndexPath:indexPath];
538                 CGRect frameToPresentMenuFrom = CGRectMake(openFileCell.frame.origin.x,
539                                                            openFileCell.frame.origin.y - self.tableView.contentOffset.y,
540                                                            openFileCell.frame.size.width, 
541                                                            openFileCell.frame.size.height);
542                 if (![self.documentInteractionController presentOptionsMenuFromRect:frameToPresentMenuFrom inView:self.view animated:YES]) {
543                     if ([self.object isPlayableMedia]) {
544                         MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
545                         vc.container = self.container;
546                         vc.object = self.object;
547                         [self.navigationController pushViewController:vc animated:YES];
548                         [vc release];
549                     } else {
550                         [self alert:@"Error" message:@"This file could not be opened."];
551                         [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
552                     }
553                 } 
554                 
555                 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];                
556                 
557             } 
558         } else if (indexPath.row == 1) {
559             
560             NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
561             NSString *documentsDirectory = [paths objectAtIndex:0];        
562             NSString *shortPath = @"";
563             
564             if (self.container && [self.container respondsToSelector:@selector(name)] && self.object && [self.object respondsToSelector:@selector(fullPath)]) {
565                 shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
566             }
567             
568             NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
569             NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
570             
571             
572             if (![MFMailComposeViewController canSendMail]) {
573                 [self alert:@"Cannot send mail" message:@"Your device has not been configured for sending email"];
574                 [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
575             }
576             else {
577                 MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
578             
579                 vc.mailComposeDelegate = self;          
580                 [vc setSubject:self.object.name];
581                 [vc addAttachmentData:data mimeType:self.object.contentType fileName:self.object.name];
582                 [vc setMessageBody:@"" isHTML:NO];    
583                 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
584                     vc.modalPresentationStyle = UIModalPresentationPageSheet;
585                 }                
586                 [self presentModalViewController:vc animated:YES];
587                 [vc release];        
588             }
589         }
590     } else if (indexPath.section == deleteSection) {
591         UIActionSheet *deleteActionSheet = [[[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this file? This operation cannot be undone."
592                                                                         delegate:self
593                                                                cancelButtonTitle:@"Cancel"
594                                                           destructiveButtonTitle:@"Delete File"
595                                                                otherButtonTitles:nil] autorelease];
596         [deleteActionSheet showInView:self.view];
597     } else if (indexPath.section == versionsSection) {
598         ObjectVersionsViewController *vc = [[ObjectVersionsViewController alloc] initWithNibName:@"ObjectVersionsViewController" bundle:nil];
599         vc.account = account;
600         vc.container = container;
601         vc.object = object;
602         [self.navigationController pushViewController:vc animated:YES];
603         [vc release];
604     }
605 }
606
607 - (void)setProgress:(float)newProgress {
608     [downloadProgressView setProgress:newProgress animated:YES];    
609     if (newProgress >= 1.0) {
610         fileDownloading = NO;
611         fileDownloaded = YES;
612         [self.tableView reloadData];
613     }
614 }
615
616
617 #pragma mark -
618 #pragma mark Document Interation Controller Delegate
619
620 - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *) controller {
621     return self.navigationController;
622 }
623
624 - (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controllers {
625     [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
626 }
627
628 #pragma mark -
629 #pragma mark Action Sheet Delegate
630
631 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
632     if (buttonIndex == 0) {
633         // delete the file and pop out
634         
635         NSString *activityMessage = @"Deleting file";
636         [activityIndicatorView release];
637         activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
638         [activityIndicatorView addToView:self.view];
639
640         self.folderViewController.refreshButton.enabled = NO;
641         [[self.account.manager deleteObject:self.container object:self.object] 
642          success:^(OpenStackRequest *request) {
643              [activityIndicatorView removeFromSuperview];
644              if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
645                  [self.navigationController popViewControllerAnimated:YES];
646                  if (account.shared)
647                      self.folderViewController.needsRefreshing = YES;
648              } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
649                  self.folderViewController.selectedObjectViewController = nil;
650                  if (!account.shared)
651                      [self.folderViewController setDetailViewController];
652                  else 
653                     [self.folderViewController refreshButtonPressed:nil];
654                  self.folderViewController.refreshButton.enabled = YES;
655              }
656              if (self.folder.objectsAndFoldersCount == 1) {
657                  [self.folder removeObject:self.object];
658                  self.folderViewController.folder = self.folderViewController.folder;
659              } else {
660                  [self.folderViewController deleteAnimatedObject:self.object];
661              }
662          }
663          failure:^(OpenStackRequest *request) {
664              [activityIndicatorView removeFromSuperview];
665              [self hideToolbarActivityMessage];
666              [self alert:@"There was a problem deleting this file." request:request];
667          }];
668     }
669     NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:deleteSection];
670     [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
671 }
672
673 #pragma mark -
674 #pragma mark Mail Composer Delegate
675
676 // Dismisses the email composition interface when users tap Cancel or Send.
677 - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {  
678         [self dismissModalViewControllerAnimated:YES];
679     [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:actionsSection] animated:YES];
680 }
681
682 #pragma mark -
683 #pragma mark Switches
684
685 - (void)objectIsPublicSwitchChanged:(id)sender
686 {
687     NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."];
688     self.oldPubicURI = object.publicURI;
689     
690     if (objectIsPublic) {
691         activityMessage = [NSString stringWithFormat:@"Disabling public link.."];
692         object.publicURI = @"";
693     }
694     else {
695         object.publicURI = @"TRUE";
696     }
697     [activityIndicatorView release];
698     activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
699     [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];    
700     
701     objectIsPublic = !objectIsPublic;
702     [[self.account.manager writeObjectMetadata:container object:object] 
703      success:^(OpenStackRequest *request) {
704          NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
705          if (objectIsPublic) {
706              [[self.account.manager getObjectInfo:container object:object version:versionID] 
707               success:^(OpenStackRequest *request) {
708                   [activityIndicatorView removeFromSuperview];
709                   object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"];
710                   NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
711                   [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
712                                         withRowAnimation:UITableViewRowAnimationBottom];
713               }
714               failure:^(OpenStackRequest *request) {
715                   [activityIndicatorView removeFromSuperview];
716                   [self alert:@"There was a problem retrieving the public link from the server." request:request]; 
717               }];
718          }
719          else {
720              [activityIndicatorView removeFromSuperview];
721              [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
722                                    withRowAnimation:UITableViewRowAnimationTop];
723          }
724      }
725      failure:^(OpenStackRequest *request) {
726          [activityIndicatorView removeFromSuperview];
727          objectIsPublic = !objectIsPublic;
728          objectIsPublicSwitch.on = !objectIsPublicSwitch.on;
729          object.publicURI = oldPubicURI;
730          [self alert:@"There was a problem enabling the public link." request:request];           
731
732      }];    
733 }
734
735
736
737 #pragma mark -
738 #pragma mark Memory management
739
740 - (void)dealloc {
741     if (fileDownloading) {
742         OpenStackRequest *request = [self.account.manager.objectDownloadRequests objectForKey:object.fullPath];
743         [request setDownloadProgressDelegate:nil];
744     }
745     [account release];
746     [downloadProgressView release];
747     [cdnURLActionSheet release];
748     [tableView release];
749     [folderViewController release];
750     [objectIsPublicSwitch release];
751     [permissions release];
752     [documentInteractionController release];
753     [actionSelectedIndexPath release];
754     [versionID release];
755     [activityIndicatorView release];
756     [super dealloc];
757 }
758
759 #pragma mark -
760 #pragma mark Helper functions
761
762 - (void)reloadMetadataSection {
763     NSString *activityMessage = @"Loading metadata...";
764     [activityIndicatorView release];
765     activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
766     [activityIndicatorView addToView:self.view];         
767     [[self.account.manager getObjectInfo:container object:object version:versionID] 
768      success:^(OpenStackRequest *request) {
769          [activityIndicatorView removeFromSuperview];         
770          object.metadata = [NSMutableDictionary dictionary];
771          for (NSString *header in request.responseHeaders) {
772              NSString *metadataKey;
773              NSString *metadataValue;
774              if ([header rangeOfString:@"X-Object-Meta-"].location != NSNotFound) {
775                  metadataKey = [NSString decodeFromPercentEscape:[header substringFromIndex:14]];
776                  metadataValue = [NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:header]];
777                  [object.metadata setObject:metadataValue forKey:metadataKey];
778              }
779          }   
780          NSIndexSet *metadataSections = [NSIndexSet indexSetWithIndex:kMetadata];
781          [self.tableView reloadSections:metadataSections withRowAnimation:UITableViewRowAnimationFade];
782      }
783      failure:^(OpenStackRequest *request) {
784          [activityIndicatorView removeFromSuperview];
785          [self alert:@"There was a problem retrieving the object's metadata." request:request]; 
786      }];
787 }
788
789 @end
790