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"
33 #define maxMetadataViewableLength 12
36 // TODO: use etag to reset download
37 // TODO: try downloading directly to the file to save memory. don't use object.data
43 Content Type text/plain
46 Key Value -> tap goes to a metadata item VC to edit or delete
48 Add Metadata... (if max not already reached)
50 Download File (if downloaded, Open File and Mail File as Attachment)
51 "After you download the file, you'll be able to attempt to open it and mail is as an attachment."
56 @implementation StorageObjectViewController
58 @synthesize account, container, folder, object, tableView, folderViewController;
59 @synthesize oldPubicURI, documentInteractionController, actionSelectedIndexPath, objectIsReadOnly, versionID;
61 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
62 return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
65 - (void)setBackgroundView {
67 UIImageView *logo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cloudfiles-large.png"]];
69 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
70 UIView *backgroundContainer = [[UIView alloc] init];
71 backgroundContainer.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
72 backgroundContainer.backgroundColor = [UIColor iPadTableBackgroundColor];
73 logo.contentMode = UIViewContentModeScaleAspectFit;
74 logo.frame = CGRectMake(100.0, 100.0, 1000.0, 1000.0);
76 [backgroundContainer addSubview:logo];
77 tableView.backgroundView = backgroundContainer;
78 [backgroundContainer release];
80 self.tableView.backgroundView = nil;
85 - (IBAction)homeButtonPressed:(id)sender {
86 [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];
128 appDelegate = [[UIApplication sharedApplication] delegate];
131 - (void)viewWillAppear:(BOOL)animated {
132 [super viewWillAppear:animated];
133 self.navigationItem.title = object.name;
135 NSIndexPath *selection = [self.tableView indexPathForSelectedRow];
137 [self.tableView deselectRowAtIndexPath:selection animated:YES];
138 if ([appDelegate.cachedObjectsDictionary objectForKey:object.hash] != nil) {
139 if ([[NSFileManager defaultManager] fileExistsAtPath:[appDelegate.cachedObjectsDictionary objectForKey:object.hash]])
140 fileDownloaded = YES;
143 [appDelegate.cachedObjectsDictionary removeObjectForKey:object.hash];
144 [appDelegate saveCacheDictionary];
150 if (UI_USER_INTERFACE_IDIOM() == 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];
187 #pragma mark Table view data source
189 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
191 int numberOfSections = 7;
192 if (deleteSection < 0)
194 if (versionsSection < 0)
196 if (publicLinkSection < 0)
198 if (permissionsSection < 0)
201 return numberOfSections;
204 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
205 if (section == kDetails) {
207 } else if (section == kMetadata) {
208 if (objectIsReadOnly) {
209 return [object.metadata count];
211 return 1 + [object.metadata count];
213 } else if (section == actionsSection) {
215 } else if (section == publicLinkSection) {
216 return objectIsPublic ? 2 : 1;
217 } else if (section == permissionsSection) {
218 if (account.sharingAccount || objectIsReadOnly)
219 return [permissions count];
221 return 1 + [permissions count];
227 - (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font {
228 CGSize textLabelSize;
229 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
231 textLabelSize = CGSizeMake(596.0, 9000.0f);
233 textLabelSize = CGSizeMake(280.0, 9000.0f);
235 CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeCharacterWrap];
236 return stringSize.height;
239 - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
240 CGFloat result = aTableView.rowHeight;
242 if (indexPath.section == kDetails && indexPath.row == 1) {
243 CGSize textLabelSize;
244 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
245 textLabelSize = CGSizeMake(537.0, 9000.0f);
247 textLabelSize = CGSizeMake(221.0, 9000.0f);
249 CGSize stringSize = [object.fullPath sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
250 return 22.0 + stringSize.height;
251 } else if (indexPath.section == kDetails && indexPath.row == 0) {
252 CGSize textLabelSize;
253 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
254 textLabelSize = CGSizeMake(537.0, 9000.0f);
256 textLabelSize = CGSizeMake(221.0, 9000.0f);
258 CGSize stringSize = [object.name sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
259 return 22.0 + stringSize.height;
260 } else if (indexPath.section == publicLinkSection && indexPath.row == 1) {
261 NSString *publicLinkUrl = [NSString stringWithFormat:@"%@%@",
262 account.pithosPublicLinkURLPrefix,
263 self.object.publicURI];
265 result = 30.0 + [self findLabelHeight:publicLinkUrl font:[UIFont systemFontOfSize:15.0]];
267 return MAX(aTableView.rowHeight, result);
270 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
271 if ((indexPath.section == publicLinkSection) && (indexPath.row == 1)) {
272 static NSString *CellIdentifier = @"TextViewCell";
273 TextViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
275 cell = [[[TextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
276 cell.textView.frame = cell.contentView.frame;
279 cell.selectionStyle = UITableViewCellSelectionStyleNone;
280 cell.textView.backgroundColor = [UIColor clearColor];
281 cell.textView.font = [UIFont systemFontOfSize:15.0];
282 cell.textView.dataDetectorTypes = UIDataDetectorTypeLink;
283 cell.textView.text = [NSString stringWithFormat:@"%@%@",
284 account.pithosPublicLinkURLPrefix,
285 [NSString encodeToPercentEscape:self.object.publicURI charactersToEncode:@"!*'();:@&=+$,?%#[]"]];
286 cell.selectionStyle = UITableViewCellSelectionStyleNone;
287 cell.accessoryView = UITableViewCellAccessoryNone;
291 if (indexPath.section == deleteSection) {
292 static NSString *CellIdentifier = @"DeleteCell";
293 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
295 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
296 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
297 cell.textLabel.textAlignment = UITextAlignmentCenter;
298 cell.textLabel.text = @"Delete Object";
303 static NSString *CellIdentifier = @"Cell";
304 UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
306 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
307 cell.textLabel.backgroundColor = [UIColor clearColor];
308 cell.detailTextLabel.backgroundColor = [UIColor clearColor];
309 cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
311 cell.textLabel.textColor = [UIColor blackColor];
312 cell.detailTextLabel.numberOfLines = 0;
313 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
314 cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8];
316 cell.userInteractionEnabled = YES;
317 cell.detailTextLabel.textAlignment = UITextAlignmentRight;
318 cell.accessoryView = nil;
319 cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
321 // Configure the cell...
322 if (indexPath.section == kDetails) {
323 cell.accessoryType = UITableViewCellAccessoryNone;
324 cell.selectionStyle = UITableViewCellSelectionStyleNone;
325 cell.accessoryView = nil;
326 if (indexPath.row == 0) {
327 cell.textLabel.text = @"Name";
328 cell.detailTextLabel.text = object.name;
329 } else if (indexPath.row == 1) {
330 cell.textLabel.text = @"Full Path";
331 cell.detailTextLabel.text = object.fullPath;
332 } else if (indexPath.row == 2) {
333 cell.textLabel.text = @"Size";
334 cell.detailTextLabel.text = [object humanizedBytes];
335 } else if (indexPath.row == 3) {
336 cell.textLabel.text = @"Type";
337 cell.detailTextLabel.text = object.contentType;
339 } else if (indexPath.section == kMetadata) {
340 if (objectIsReadOnly) {
341 cell.accessoryType = UITableViewCellAccessoryNone;
342 cell.selectionStyle = UITableViewCellSelectionStyleNone;
343 cell.userInteractionEnabled = NO;
346 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
347 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
350 cell.accessoryView = nil;
351 if (indexPath.row == [object.metadata count]) {
352 cell.textLabel.text = @"Add Metadata";
353 cell.detailTextLabel.text = @"";
355 NSString *key = [[object.metadata allKeys] objectAtIndex:indexPath.row];
356 NSString *value = [object.metadata objectForKey:key];
357 NSString *metadataKeyCellText = key;
358 NSString *metadataValueCellText = value;
359 if ([metadataKeyCellText length] > maxMetadataViewableLength) {
360 metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
361 metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
363 if ([metadataValueCellText length] > maxMetadataViewableLength) {
364 metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
365 metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
368 cell.textLabel.text = metadataKeyCellText;
369 cell.detailTextLabel.text = metadataValueCellText;
371 } else if (indexPath.section == publicLinkSection) {
372 if (indexPath.row == 0) {
373 cell.textLabel.text = @"Public URL";
374 cell.detailTextLabel.text = @"";
375 cell.accessoryView = objectIsPublicSwitch;
376 cell.accessoryType = UITableViewCellAccessoryNone;
377 cell.selectionStyle = UITableViewCellSelectionStyleNone;
379 } else if (indexPath.section == permissionsSection) {
380 if (account.sharingAccount) {
381 cell.accessoryType = UITableViewCellAccessoryNone;
382 cell.selectionStyle = UITableViewCellSelectionStyleNone;
383 cell.userInteractionEnabled = NO;
386 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
387 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
389 cell.accessoryView = nil;
391 if (indexPath.row == [permissions count]) {
392 cell.textLabel.text = @"Share";
393 cell.detailTextLabel.text = @"";
395 NSString *user = [[permissions allKeys] objectAtIndex:indexPath.row];
396 cell.textLabel.text = user;
397 NSString *accessType;
398 if ([[permissions objectForKey:user] isEqualToString:@"write"])
399 accessType = @"Read/Write";
401 accessType = @"Read Only";
402 cell.detailTextLabel.numberOfLines = 1;
403 cell.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation;
404 cell.detailTextLabel.text = accessType;
406 } else if (indexPath.section == actionsSection) {
407 if (fileDownloading) {
408 if (actionSelectedIndexPath.row == indexPath.row) {
409 cell.accessoryView = downloadProgressView;
411 cell.textLabel.textColor = [UIColor grayColor];
412 cell.selectionStyle = UITableViewCellSelectionStyleNone;
415 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
416 cell.accessoryView = nil;
418 if (indexPath.row == 0) {
419 cell.textLabel.text = @"Open File";
420 cell.accessoryType = UITableViewCellAccessoryNone;
421 cell.detailTextLabel.text = @"";
423 } else if (indexPath.row == 1) {
424 cell.textLabel.text = @"Email File as Attachment";
425 cell.detailTextLabel.text = @"";
426 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
428 } else if (indexPath.section == versionsSection) {
429 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
430 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
431 cell.textLabel.text = @"Versions";
432 cell.detailTextLabel.text = @"";
439 #pragma mark Table view delegate
441 - (void)reloadActionsTitleRow:(NSTimer *)timer {
442 [[timer.userInfo objectForKey:@"tableView"] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
445 - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
446 if (indexPath.section == kMetadata) {
447 EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
448 NSString *metadataKey;
449 NSString *metadataValue;
451 if (indexPath.row == [self.object.metadata count]) {
454 vc.removeMetadataEnabled = FALSE;
455 vc.navigationItem.title = @"Add Metadata";
458 metadataKey = [[self.object.metadata allKeys] objectAtIndex:indexPath.row];
459 metadataValue = [self.object.metadata objectForKey:metadataKey];
460 vc.removeMetadataEnabled = YES;
461 vc.navigationItem.title = @"Edit Metadata";
464 vc.metadataKey = metadataKey;
465 vc.metadataValue = metadataValue;
466 vc.account = account;
467 vc.container = container;
469 [self.navigationController pushViewController:vc animated:YES];
471 } else if (indexPath.section == permissionsSection) {
472 EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
475 if (indexPath.row == [permissions count]) {
477 vc.removePermissionsEnabled = NO;
478 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 NSString *filePath = [appDelegate.cachedObjectsDictionary objectForKey:object.hash];
533 self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
534 self.documentInteractionController.delegate = self;
535 self.documentInteractionController.name = object.name;
537 UITableViewCell *openFileCell = [self.tableView cellForRowAtIndexPath:indexPath];
538 CGRect frameToPresentMenuFrom = CGRectMake(openFileCell.frame.origin.x,
539 openFileCell.frame.origin.y - self.tableView.contentOffset.y,
540 openFileCell.frame.size.width,
541 openFileCell.frame.size.height);
542 if (![self.documentInteractionController presentOptionsMenuFromRect:frameToPresentMenuFrom inView:self.view animated:YES]) {
543 if ([self.object isPlayableMedia]) {
544 MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
545 vc.container = self.container;
546 vc.object = self.object;
547 [self.navigationController pushViewController:vc animated:YES];
550 [self alert:@"Error" message:@"This file could not be opened."];
551 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
555 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
558 } else if (indexPath.row == 1) {
560 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
561 NSString *documentsDirectory = [paths objectAtIndex:0];
562 NSString *shortPath = @"";
564 if (self.container && [self.container respondsToSelector:@selector(name)] && self.object && [self.object respondsToSelector:@selector(fullPath)]) {
565 shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
568 NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
569 NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
572 if (![MFMailComposeViewController canSendMail]) {
573 [self alert:@"Cannot send mail" message:@"Your device has not been configured for sending email"];
574 [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
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 (UI_USER_INTERFACE_IDIOM() == 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 Interation 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
635 NSString *activityMessage = @"Deleting file";
636 [activityIndicatorView release];
637 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
638 [activityIndicatorView addToView:self.view];
640 self.folderViewController.refreshButton.enabled = NO;
641 [[self.account.manager deleteObject:self.container object:self.object]
642 success:^(OpenStackRequest *request) {
643 [activityIndicatorView removeFromSuperview];
644 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
645 [self.navigationController popViewControllerAnimated:YES];
647 self.folderViewController.needsRefreshing = YES;
648 } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
649 self.folderViewController.selectedObjectViewController = nil;
651 [self.folderViewController setDetailViewController];
653 [self.folderViewController refreshButtonPressed:nil];
654 self.folderViewController.refreshButton.enabled = YES;
656 if (self.folder.objectsAndFoldersCount == 1) {
657 [self.folder removeObject:self.object];
658 self.folderViewController.folder = self.folderViewController.folder;
660 [self.folderViewController deleteAnimatedObject:self.object];
663 failure:^(OpenStackRequest *request) {
664 [activityIndicatorView removeFromSuperview];
665 [self hideToolbarActivityMessage];
666 [self alert:@"There was a problem deleting this file." request:request];
669 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:deleteSection];
670 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
674 #pragma mark Mail Composer Delegate
676 // Dismisses the email composition interface when users tap Cancel or Send.
677 - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
678 [self dismissModalViewControllerAnimated:YES];
679 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:actionsSection] animated:YES];
683 #pragma mark Switches
685 - (void)objectIsPublicSwitchChanged:(id)sender
687 NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."];
688 self.oldPubicURI = object.publicURI;
690 if (objectIsPublic) {
691 activityMessage = [NSString stringWithFormat:@"Disabling public link.."];
692 object.publicURI = @"";
695 object.publicURI = @"TRUE";
697 [activityIndicatorView release];
698 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
699 [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];
701 objectIsPublic = !objectIsPublic;
702 [[self.account.manager writeObjectMetadata:container object:object]
703 success:^(OpenStackRequest *request) {
704 NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
705 if (objectIsPublic) {
706 [[self.account.manager getObjectInfo:container object:object version:versionID]
707 success:^(OpenStackRequest *request) {
708 [activityIndicatorView removeFromSuperview];
709 object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"];
710 NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
711 [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
712 withRowAnimation:UITableViewRowAnimationBottom];
714 failure:^(OpenStackRequest *request) {
715 [activityIndicatorView removeFromSuperview];
716 [self alert:@"There was a problem retrieving the public link from the server." request:request];
720 [activityIndicatorView removeFromSuperview];
721 [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
722 withRowAnimation:UITableViewRowAnimationTop];
725 failure:^(OpenStackRequest *request) {
726 [activityIndicatorView removeFromSuperview];
727 objectIsPublic = !objectIsPublic;
728 objectIsPublicSwitch.on = !objectIsPublicSwitch.on;
729 object.publicURI = oldPubicURI;
730 [self alert:@"There was a problem enabling the public link." request:request];
738 #pragma mark Memory management
741 if (fileDownloading) {
742 OpenStackRequest *request = [self.account.manager.objectDownloadRequests objectForKey:object.fullPath];
743 [request setDownloadProgressDelegate:nil];
746 [downloadProgressView release];
747 [cdnURLActionSheet release];
749 [folderViewController release];
750 [objectIsPublicSwitch release];
751 [permissions release];
752 [documentInteractionController release];
753 [actionSelectedIndexPath release];
755 [activityIndicatorView release];
760 #pragma mark Helper functions
762 - (void)reloadMetadataSection {
763 NSString *activityMessage = @"Loading metadata...";
764 [activityIndicatorView release];
765 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
766 [activityIndicatorView addToView:self.view];
767 [[self.account.manager getObjectInfo:container object:object version:versionID]
768 success:^(OpenStackRequest *request) {
769 [activityIndicatorView removeFromSuperview];
770 object.metadata = [NSMutableDictionary dictionary];
771 for (NSString *header in request.responseHeaders) {
772 NSString *metadataKey;
773 NSString *metadataValue;
774 if ([header rangeOfString:@"X-Object-Meta-"].location != NSNotFound) {
775 metadataKey = [NSString decodeFromPercentEscape:[header substringFromIndex:14]];
776 metadataValue = [NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:header]];
777 [object.metadata setObject:metadataValue forKey:metadataKey];
780 NSIndexSet *metadataSections = [NSIndexSet indexSetWithIndex:kMetadata];
781 [self.tableView reloadSections:metadataSections withRowAnimation:UITableViewRowAnimationFade];
783 failure:^(OpenStackRequest *request) {
784 [activityIndicatorView removeFromSuperview];
785 [self alert:@"There was a problem retrieving the object's metadata." request:request];