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