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