2 // StorageObjectViewController.m
5 // Created by Mike Mayo on 12/19/10.
6 // The OpenStack project is provided under the Apache 2.0 license.
9 #import "StorageObjectViewController.h"
10 #import "OpenStackAccount.h"
11 #import "OpenStackRequest.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"
24 #import "EditPermissionsViewController.h"
25 #import "TextViewCell.h"
26 #import "NSString+Conveniences.h"
27 #import "APICallback.h"
28 #import "ObjectVersionsViewController.h"
29 #import <MediaPlayer/MPMoviePlayerController.h>
30 #import <MessageUI/MessageUI.h>
31 #import "ActivityIndicatorView.h"
36 #define maxMetadataViewableLength 12
38 // TODO: use etag to reset download
39 // TODO: try downloading directly to the file to save memory. don't use object.data
45 Content Type text/plain
48 Key Value -> tap goes to a metadata item VC to edit or delete
50 Add Metadata... (if max not already reached)
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."
58 @implementation StorageObjectViewController
60 @synthesize account, container, folder, object, tableView, folderViewController;
61 @synthesize oldPubicURI, documentInteractionController, actionSelectedIndexPath, objectIsReadOnly, versionID;
63 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
64 return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
67 - (void)setBackgroundView {
68 UIImageView *logo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cloudfiles-large.png"]];
70 if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
71 UIView *backgroundContainer = [[UIView alloc] init];
72 backgroundContainer.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
73 backgroundContainer.backgroundColor = [UIColor iPadTableBackgroundColor];
74 logo.contentMode = UIViewContentModeScaleAspectFit;
75 logo.frame = CGRectMake(100.0, 100.0, 1000.0, 1000.0);
77 [backgroundContainer addSubview:logo];
78 tableView.backgroundView = backgroundContainer;
79 [backgroundContainer release];
81 self.tableView.backgroundView = nil;
86 - (IBAction)homeButtonPressed:(id)sender {
87 [self.navigationController popToRootViewControllerAnimated:YES];
90 #pragma mark - View lifecycle
95 objectIsPublicSwitch = [[UISwitch alloc] init];
96 [objectIsPublicSwitch addTarget:self action:@selector(objectIsPublicSwitchChanged:) forControlEvents:UIControlEventValueChanged];
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];
107 NSArray *users = [[array objectAtIndex:1] componentsSeparatedByString:@","];
108 for (NSString *user in users) {
109 [permissions setObject:permissionsType forKey:user];
113 objectIsReadOnly = NO;
114 if (account.sharingAccount) {
115 if ([permissions count] > 0) {
116 objectIsReadOnly = [[permissions objectForKey:[account username]] isEqualToString:@"read"];
118 objectIsPublicSwitch.enabled = NO;
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];
130 - (void)viewWillAppear:(BOOL)animated {
131 [super viewWillAppear:animated];
132 self.navigationItem.title = object.name;
134 NSIndexPath *selection = [self.tableView indexPathForSelectedRow];
136 [self.tableView deselectRowAtIndexPath:selection animated:YES];
137 OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
138 if ([app.cachedObjectsDictionary objectForKey:object.hash] != nil) {
139 if ([[NSFileManager defaultManager] fileExistsAtPath:[app.cachedObjectsDictionary objectForKey:object.hash]]) {
140 fileDownloaded = YES;
143 [app.cachedObjectsDictionary removeObjectForKey:object.hash];
144 [app saveCacheDictionary];
150 if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
151 CGRect rect = downloadProgressView.frame;
152 rect.size.width = 440.0;
153 downloadProgressView.frame = rect;
156 //[self setBackgroundView];
158 publicLinkSection = 3;
159 permissionsSection = 4;
164 publicLinkSection = -1;
165 permissionsSection = -1;
166 versionsSection = -1;
168 objectIsReadOnly = YES;
170 if (account.sharingAccount || account.shared) {
171 versionsSection = -1;
175 objectIsPublic = ([object.publicURI length]);
176 objectIsPublicSwitch.on = objectIsPublic;
177 [self.tableView reloadData];
180 - (void)viewDidAppear:(BOOL)animated {
181 if (object.metadata == nil) {
182 [self reloadMetadataSection];
186 #pragma mark - Table view data source
188 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
189 int numberOfSections = 7;
190 if (deleteSection < 0)
192 if (versionsSection < 0)
194 if (publicLinkSection < 0)
196 if (permissionsSection < 0)
199 return numberOfSections;
202 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
203 if (section == kDetails) {
204 return (object.lastModifiedString ? 5: 4);
205 } else if (section == kMetadata) {
206 if (objectIsReadOnly) {
207 return [object.metadata count];
209 return 1 + [object.metadata count];
211 } else if (section == actionsSection) {
213 } else if (section == publicLinkSection) {
214 return objectIsPublic ? 2 : 1;
215 } else if (section == permissionsSection) {
216 if (account.sharingAccount || objectIsReadOnly) {
217 return [permissions count];
219 return 1 + [permissions count];
226 - (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font {
227 CGSize textLabelSize;
228 if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
230 textLabelSize = CGSizeMake(596.0, 9000.0f);
232 textLabelSize = CGSizeMake(280.0, 9000.0f);
234 CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeCharacterWrap];
235 return stringSize.height;
238 - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
239 CGFloat result = aTableView.rowHeight;
241 if (indexPath.section == kDetails && indexPath.row == 1) {
242 CGSize textLabelSize;
243 if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
244 textLabelSize = CGSizeMake(537.0, 9000.0f);
246 textLabelSize = CGSizeMake(221.0, 9000.0f);
248 CGSize stringSize = [object.fullPath sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
249 return 22.0 + stringSize.height;
250 } else if (indexPath.section == kDetails && indexPath.row == 0) {
251 CGSize textLabelSize;
252 if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
253 textLabelSize = CGSizeMake(537.0, 9000.0f);
255 textLabelSize = CGSizeMake(221.0, 9000.0f);
257 CGSize stringSize = [object.name sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
258 return 22.0 + stringSize.height;
259 } else if (indexPath.section == publicLinkSection && indexPath.row == 1) {
260 NSString *publicLinkUrl = [NSString stringWithFormat:@"%@%@",
261 account.pithosPublicLinkURLPrefix,
262 self.object.publicURI];
264 result = 30.0 + [self findLabelHeight:publicLinkUrl font:[UIFont systemFontOfSize:15.0]];
266 return MAX(aTableView.rowHeight, result);
269 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
270 if ((indexPath.section == publicLinkSection) && (indexPath.row == 1)) {
271 static NSString *CellIdentifier = @"TextViewCell";
272 TextViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
274 cell = [[[TextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
275 cell.textView.frame = cell.contentView.frame;
278 cell.selectionStyle = UITableViewCellSelectionStyleNone;
279 cell.textView.backgroundColor = [UIColor clearColor];
280 cell.textView.font = [UIFont systemFontOfSize:15.0];
281 cell.textView.dataDetectorTypes = UIDataDetectorTypeLink;
282 cell.textView.text = [NSString stringWithFormat:@"%@%@",
283 account.pithosPublicLinkURLPrefix,
284 [NSString encodeToPercentEscape:self.object.publicURI charactersToEncode:@"!*'();:@&=+$,?%#[]"]];
285 cell.selectionStyle = UITableViewCellSelectionStyleNone;
286 cell.accessoryView = UITableViewCellAccessoryNone;
290 if (indexPath.section == deleteSection) {
291 static NSString *CellIdentifier = @"DeleteCell";
292 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
294 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
295 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
296 cell.textLabel.textAlignment = UITextAlignmentCenter;
297 cell.textLabel.text = @"Delete Object";
302 static NSString *CellIdentifier = @"Cell";
303 UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
305 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
306 cell.textLabel.backgroundColor = [UIColor clearColor];
307 cell.detailTextLabel.backgroundColor = [UIColor clearColor];
308 cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
310 cell.textLabel.textColor = [UIColor blackColor];
311 cell.detailTextLabel.numberOfLines = 0;
312 if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
313 cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8];
315 cell.userInteractionEnabled = YES;
316 cell.detailTextLabel.textAlignment = UITextAlignmentRight;
317 cell.accessoryView = nil;
318 cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
320 // Configure the cell...
321 if (indexPath.section == kDetails) {
322 cell.accessoryType = UITableViewCellAccessoryNone;
323 cell.selectionStyle = UITableViewCellSelectionStyleNone;
324 cell.accessoryView = nil;
325 if (indexPath.row == 0) {
326 cell.textLabel.text = @"Name";
327 cell.detailTextLabel.text = object.name;
328 } else if (indexPath.row == 1) {
329 cell.textLabel.text = @"Full Path";
330 cell.detailTextLabel.text = object.fullPath;
331 } else if (indexPath.row == 2) {
332 cell.textLabel.text = @"Size";
333 cell.detailTextLabel.text = [object humanizedBytes];
334 } else if (indexPath.row == 3) {
335 cell.textLabel.text = @"Type";
336 cell.detailTextLabel.text = object.contentType;
337 } else if (indexPath.row == 4) {
338 cell.textLabel.text = @"Last Modified";
339 cell.detailTextLabel.text = object.lastModifiedString;
341 } else if (indexPath.section == kMetadata) {
342 if (objectIsReadOnly) {
343 cell.accessoryType = UITableViewCellAccessoryNone;
344 cell.selectionStyle = UITableViewCellSelectionStyleNone;
345 cell.userInteractionEnabled = NO;
348 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
349 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
352 cell.accessoryView = nil;
353 if (indexPath.row == [object.metadata count]) {
354 cell.textLabel.text = @"Add Metadata";
355 cell.detailTextLabel.text = @"";
357 NSString *key = [[object.metadata allKeys] objectAtIndex:indexPath.row];
358 NSString *value = [object.metadata objectForKey:key];
359 NSString *metadataKeyCellText = key;
360 NSString *metadataValueCellText = value;
361 if ([metadataKeyCellText length] > maxMetadataViewableLength) {
362 metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
363 metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
365 if ([metadataValueCellText length] > maxMetadataViewableLength) {
366 metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
367 metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
370 cell.textLabel.text = metadataKeyCellText;
371 cell.detailTextLabel.text = metadataValueCellText;
373 } else if (indexPath.section == publicLinkSection) {
374 if (indexPath.row == 0) {
375 cell.textLabel.text = @"Public URL";
376 cell.detailTextLabel.text = @"";
377 cell.accessoryView = objectIsPublicSwitch;
378 cell.accessoryType = UITableViewCellAccessoryNone;
379 cell.selectionStyle = UITableViewCellSelectionStyleNone;
381 } else if (indexPath.section == permissionsSection) {
382 if (account.sharingAccount) {
383 cell.accessoryType = UITableViewCellAccessoryNone;
384 cell.selectionStyle = UITableViewCellSelectionStyleNone;
385 cell.userInteractionEnabled = NO;
388 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
389 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
391 cell.accessoryView = nil;
393 if (indexPath.row == [permissions count]) {
394 cell.textLabel.text = @"Share";
395 cell.detailTextLabel.text = @"";
397 NSString *user = [[permissions allKeys] objectAtIndex:indexPath.row];
398 cell.textLabel.text = user;
399 NSString *accessType;
400 if ([[permissions objectForKey:user] isEqualToString:@"write"])
401 accessType = @"Read/Write";
403 accessType = @"Read Only";
404 cell.detailTextLabel.numberOfLines = 1;
405 cell.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation;
406 cell.detailTextLabel.text = accessType;
408 } else if (indexPath.section == actionsSection) {
409 if (fileDownloading) {
410 if (actionSelectedIndexPath.row == indexPath.row) {
411 cell.accessoryView = downloadProgressView;
413 cell.textLabel.textColor = [UIColor grayColor];
414 cell.selectionStyle = UITableViewCellSelectionStyleNone;
417 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
418 cell.accessoryView = nil;
420 if (indexPath.row == 0) {
421 cell.textLabel.text = @"Open File";
422 cell.accessoryType = UITableViewCellAccessoryNone;
423 cell.detailTextLabel.text = @"";
425 } else if (indexPath.row == 1) {
426 cell.textLabel.text = @"Email File as Attachment";
427 cell.detailTextLabel.text = @"";
428 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
430 } else if (indexPath.section == versionsSection) {
431 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
432 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
433 cell.textLabel.text = @"Versions";
434 cell.detailTextLabel.text = @"";
441 #pragma mark Table view delegate
443 - (void)reloadActionsTitleRow:(NSTimer *)timer {
444 [[timer.userInfo objectForKey:@"tableView"] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
447 - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
448 if (indexPath.section == kMetadata) {
449 EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
450 NSString *metadataKey;
451 NSString *metadataValue;
453 if (indexPath.row == [self.object.metadata count]) {
456 vc.removeMetadataEnabled = FALSE;
457 vc.navigationItem.title = @"Add Metadata";
459 metadataKey = [[self.object.metadata allKeys] objectAtIndex:indexPath.row];
460 metadataValue = [self.object.metadata objectForKey:metadataKey];
461 vc.removeMetadataEnabled = YES;
462 vc.navigationItem.title = @"Edit Metadata";
465 vc.metadataKey = metadataKey;
466 vc.metadataValue = metadataValue;
467 vc.account = account;
468 vc.container = container;
470 [self.navigationController pushViewController:vc animated:YES];
472 } else if (indexPath.section == permissionsSection) {
473 EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
476 if (indexPath.row == [permissions count]) {
478 vc.removePermissionsEnabled = NO;
479 vc.navigationItem.title = @"Share";
481 user = [[permissions allKeys] objectAtIndex:indexPath.row];
482 NSString *userPermissions = [permissions objectForKey:user];
483 if ([userPermissions rangeOfString:@"read"].location != NSNotFound)
484 vc.readPermissionSelected = YES;
486 vc.readPermissionSelected = NO;
488 if ([userPermissions rangeOfString:@"write"].location != NSNotFound)
489 vc.writePermissionSelected = YES;
491 vc.writePermissionSelected = NO;
493 vc.removePermissionsEnabled = YES;
494 vc.navigationItem.title = @"Edit Sharing";
498 vc.permissions = permissions;
499 vc.account = account;
500 vc.container = container;
502 vc.folderViewController = folderViewController;
503 [self.navigationController pushViewController:vc animated:YES];
505 } else if (indexPath.section == actionsSection) {
506 if (!fileDownloaded) {
507 if (!fileDownloading) {
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];
522 failure:^(OpenStackRequest *request) {
524 fileDownloading = NO;
525 [self alert:@"File failed to download." request:request];
526 [self.tableView reloadData];
528 [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
530 } else if (indexPath.row == 0) {
531 if (fileDownloaded) {
532 OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
533 NSString *filePath = [app.cachedObjectsDictionary objectForKey:object.hash];
534 self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
535 self.documentInteractionController.delegate = self;
536 self.documentInteractionController.name = object.name;
538 UITableViewCell *openFileCell = [self.tableView cellForRowAtIndexPath:indexPath];
539 CGRect frameToPresentMenuFrom = CGRectMake(openFileCell.frame.origin.x,
540 openFileCell.frame.origin.y - self.tableView.contentOffset.y,
541 openFileCell.frame.size.width,
542 openFileCell.frame.size.height);
543 if (![self.documentInteractionController presentOptionsMenuFromRect:frameToPresentMenuFrom inView:self.view animated:YES]) {
544 if ([self.object isPlayableMedia]) {
545 MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
546 vc.container = self.container;
547 vc.object = self.object;
548 [self.navigationController pushViewController:vc animated:YES];
551 [self alert:@"Error" message:@"This file could not be opened."];
552 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
556 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
559 } else if (indexPath.row == 1) {
561 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
562 NSString *documentsDirectory = [paths objectAtIndex:0];
563 NSString *shortPath = @"";
565 if (self.container && [self.container respondsToSelector:@selector(name)] && self.object && [self.object respondsToSelector:@selector(fullPath)]) {
566 shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
569 NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
570 NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
573 if (![MFMailComposeViewController canSendMail]) {
574 [self alert:@"Cannot send mail" message:@"Your device has not been configured for sending email"];
575 [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
577 MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
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 ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
584 vc.modalPresentationStyle = UIModalPresentationPageSheet;
586 [self presentModalViewController:vc animated:YES];
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."
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;
602 [self.navigationController pushViewController:vc animated:YES];
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];
618 #pragma mark Document Interaction Controller Delegate
620 - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *) controller {
621 return self.navigationController;
624 - (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controllers {
625 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
629 #pragma mark Action Sheet Delegate
631 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
632 if (buttonIndex == 0) {
633 // delete the file and pop out
634 self.folderViewController.refreshButton.enabled = NO;
635 __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Deleting file..."
636 andAddToView:self.view];
637 [[self.account.manager deleteObject:self.container object:self.object]
638 success:^(OpenStackRequest *request) {
639 [activityIndicatorView removeFromSuperview];
640 if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
641 [self.navigationController popViewControllerAnimated:YES];
643 self.folderViewController.needsRefreshing = YES;
644 } else if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
645 self.folderViewController.selectedObjectViewController = nil;
647 [self.folderViewController setDetailViewController];
649 [self.folderViewController refreshButtonPressed:nil];
650 // self.folderViewController.refreshButton.enabled = YES;
652 if (self.folder.objectsAndFoldersCount == 1) {
653 [self.folder removeObject:self.object];
654 self.folderViewController.folder = self.folderViewController.folder;
656 [self.folderViewController deleteAnimatedObject:self.object];
658 self.folderViewController.refreshButton.enabled = YES;
660 failure:^(OpenStackRequest *request) {
661 [activityIndicatorView removeFromSuperview];
662 [self alert:@"There was a problem deleting this file." request:request];
663 self.folderViewController.refreshButton.enabled = YES;
666 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:deleteSection];
667 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
670 #pragma mark - Mail Composer Delegate
672 // Dismisses the email composition interface when users tap Cancel or Send.
673 - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
674 [self dismissModalViewControllerAnimated:YES];
675 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:actionsSection] animated:YES];
678 #pragma mark - Switches
680 - (void)objectIsPublicSwitchChanged:(id)sender {
681 NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."];
682 self.oldPubicURI = object.publicURI;
684 if (objectIsPublic) {
685 activityMessage = [NSString stringWithFormat:@"Disabling public link.."];
686 object.publicURI = @"";
688 object.publicURI = @"TRUE";
690 __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:activityMessage
691 andAddToView:self.view
692 scrollOffset:self.tableView.contentOffset.y];
693 objectIsPublic = !objectIsPublic;
694 [[self.account.manager writeObjectMetadata:container object:object]
695 success:^(OpenStackRequest *request) {
696 NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
697 if (objectIsPublic) {
698 [[self.account.manager getObjectInfo:container object:object version:versionID]
699 success:^(OpenStackRequest *request) {
700 [activityIndicatorView removeFromSuperview];
701 object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"];
702 NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
703 [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
704 withRowAnimation:UITableViewRowAnimationBottom];
706 failure:^(OpenStackRequest *request) {
707 [activityIndicatorView removeFromSuperview];
708 [self alert:@"There was a problem retrieving the public link from the server." request:request];
711 [activityIndicatorView removeFromSuperview];
712 [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
713 withRowAnimation:UITableViewRowAnimationTop];
716 failure:^(OpenStackRequest *request) {
717 [activityIndicatorView removeFromSuperview];
718 objectIsPublic = !objectIsPublic;
719 objectIsPublicSwitch.on = !objectIsPublicSwitch.on;
720 object.publicURI = oldPubicURI;
721 [self alert:@"There was a problem enabling the public link." request:request];
725 #pragma mark - Memory management
728 if (fileDownloading) {
729 OpenStackRequest *request = [self.account.manager.objectDownloadRequests objectForKey:object.fullPath];
730 [request setDownloadProgressDelegate:nil];
733 [downloadProgressView release];
734 [cdnURLActionSheet release];
736 [folderViewController release];
737 [objectIsPublicSwitch release];
738 [permissions release];
739 [documentInteractionController release];
740 [actionSelectedIndexPath release];
745 #pragma mark - Helper functions
747 - (void)reloadMetadataSection {
748 __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading metadata..."
749 andAddToView:self.view];
750 [[self.account.manager getObjectInfo:container object:object version:versionID]
751 success:^(OpenStackRequest *request) {
752 [activityIndicatorView removeFromSuperview];
753 object.metadata = [NSMutableDictionary dictionary];
754 for (NSString *header in request.responseHeaders) {
755 NSString *metadataKey;
756 NSString *metadataValue;
757 if ([header rangeOfString:@"X-Object-Meta-"].location != NSNotFound) {
758 metadataKey = [NSString decodeFromPercentEscape:[header substringFromIndex:14]];
759 metadataValue = [NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:header]];
760 [object.metadata setObject:metadataValue forKey:metadataKey];
763 NSIndexSet *metadataSections = [NSIndexSet indexSetWithIndex:kMetadata];
764 [self.tableView reloadSections:metadataSections withRowAnimation:UITableViewRowAnimationFade];
766 failure:^(OpenStackRequest *request) {
767 [activityIndicatorView removeFromSuperview];
768 [self alert:@"There was a problem retrieving the object's metadata." request:request];