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"
32 #define kPermissions 3
34 #define maxMetadataViewableLength 12
37 // TODO: use etag to reset download
38 // TODO: try downloading directly to the file to save memory. don't use object.data
44 Content Type text/plain
47 Key Value -> tap goes to a metadata item VC to edit or delete
49 Add Metadata... (if max not already reached)
51 CDN URL sections (action sheet to copy, open in safari, and email link)
53 Download File (if downloaded, Open File and Mail File as Attachment)
54 "After you download the file, you'll be able to attempt to open it and mail is as an attachment."
59 @implementation StorageObjectViewController
61 @synthesize account, container, folder, object, tableView, folderViewController;
62 @synthesize oldPubicURI, documentInteractionController;
64 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
65 return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
68 - (void)setBackgroundView {
70 UIImageView *logo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cloudfiles-large.png"]];
72 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
73 UIView *backgroundContainer = [[UIView alloc] init];
74 backgroundContainer.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
75 backgroundContainer.backgroundColor = [UIColor iPadTableBackgroundColor];
76 logo.contentMode = UIViewContentModeScaleAspectFit;
77 logo.frame = CGRectMake(100.0, 100.0, 1000.0, 1000.0);
79 [backgroundContainer addSubview:logo];
80 tableView.backgroundView = backgroundContainer;
81 [backgroundContainer release];
83 self.tableView.backgroundView = nil;
88 - (IBAction)homeButtonPressed:(id)sender {
89 [self.navigationController popToRootViewControllerAnimated:YES];
93 #pragma mark View lifecycle
97 objectIsPublicSwitch = [[UISwitch alloc] init];
98 [objectIsPublicSwitch addTarget:self action:@selector(objectIsPublicSwitchChanged:) forControlEvents:UIControlEventValueChanged];
100 deleteActionSheet = [[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this file? This operation cannot be undone." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete File" otherButtonTitles:nil];
101 permissions = [[NSMutableDictionary alloc] init];
102 if (object.sharing.length > 0) {
103 NSArray *sharingArray = [object.sharing componentsSeparatedByString:@";"];
104 for (NSString *typeSpecificPermissions in sharingArray) {
105 NSArray *array=[typeSpecificPermissions componentsSeparatedByString:@"="];
106 NSString *permissionsType = [array objectAtIndex:0];
107 if ([permissionsType hasPrefix:@" "])
108 permissionsType = [permissionsType substringFromIndex:1];
110 NSArray *users = [[array objectAtIndex:1] componentsSeparatedByString:@","];
111 for (NSString *user in users) {
112 [permissions setObject:permissionsType forKey:user];
116 objectIsReadOnly = NO;
117 if (account.sharingAccount) {
118 if ([permissions count] > 0) {
119 objectIsReadOnly = [[permissions objectForKey:[account username]] isEqualToString:@"read"];
121 objectIsPublicSwitch.enabled = NO;
125 - (void)viewWillAppear:(BOOL)animated {
126 [super viewWillAppear:animated];
127 self.navigationItem.title = object.name;
129 NSIndexPath *selection = [self.tableView indexPathForSelectedRow];
131 [self.tableView deselectRowAtIndexPath:selection animated:YES];
133 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
134 NSString *documentsDirectory = [paths objectAtIndex:0];
135 NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
136 NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
138 NSFileManager *fileManager = [NSFileManager defaultManager];
139 fileDownloaded = [fileManager fileExistsAtPath:filePath];
141 downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
143 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
144 CGRect rect = downloadProgressView.frame;
145 rect.size.width = 440.0;
146 downloadProgressView.frame = rect;
149 //[self setBackgroundView];
150 if (self.container.cdnEnabled) {
160 if (account.sharingAccount || account.shared) {
164 // let's see if we can tweet
165 UIApplication *app = [UIApplication sharedApplication];
166 NSURL *twitterURL = [NSURL URLWithString:@"twitter://post?message=test"];
168 if ([app canOpenURL:twitterURL]) {
169 cdnURLActionSheet = [[UIActionSheet alloc] initWithTitle:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Copy to Pasteboard", @"Open in Safari", @"Email Link to File", @"Tweet Link to File", nil];
171 cdnURLActionSheet = [[UIActionSheet alloc] initWithTitle:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Copy to Pasteboard", @"Open in Safari", @"Email Link to File", nil];
173 objectIsPublic = ([object.publicURI length]);
174 objectIsPublicSwitch.on = objectIsPublic;
175 [self.tableView reloadData];
179 #pragma mark Table view data source
181 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
182 if (account.sharingAccount || account.shared)
183 return self.container.cdnEnabled ? 6 : 5;
185 return self.container.cdnEnabled ? 7 : 6;
188 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
189 if (section == kDetails) {
191 } else if (section == kMetadata) {
192 if (objectIsReadOnly)
193 return [object.metadata count];
195 return 1 + [object.metadata count];
196 } else if (section == cdnURLSection) {
198 } else if (section == actionsSection) {
199 return fileDownloaded ? 2 : 1;
200 } else if (section == kPublic) {
201 return objectIsPublic ? 2 : 1;
202 } else if (section == kPermissions) {
203 if (account.sharingAccount)
204 return [permissions count];
206 return 1 + [permissions count];
212 - (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font {
213 CGSize textLabelSize;
214 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
216 textLabelSize = CGSizeMake(596.0, 9000.0f);
218 textLabelSize = CGSizeMake(280.0, 9000.0f);
220 CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeCharacterWrap];
221 return stringSize.height;
224 - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
225 CGFloat result = aTableView.rowHeight;
227 if (indexPath.section == cdnURLSection) {
228 result = 22.0 + [self findLabelHeight:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] font:[UIFont systemFontOfSize:18.0]];
229 } else if (indexPath.section == kDetails && indexPath.row == 1) {
230 CGSize textLabelSize;
231 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
232 textLabelSize = CGSizeMake(537.0, 9000.0f);
234 textLabelSize = CGSizeMake(221.0, 9000.0f);
236 CGSize stringSize = [object.fullPath sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
237 return 22.0 + stringSize.height;
238 } else if (indexPath.section == kDetails && indexPath.row == 0) {
239 CGSize textLabelSize;
240 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
241 textLabelSize = CGSizeMake(537.0, 9000.0f);
243 textLabelSize = CGSizeMake(221.0, 9000.0f);
245 CGSize stringSize = [object.name sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
246 return 22.0 + stringSize.height;
247 } else if (indexPath.section == kPublic && indexPath.row == 1) {
248 NSString *publicLinkUrl = [NSString stringWithFormat:@"%@%@",
249 account.pithosPublicLinkURLPrefix,
250 self.object.publicURI];
252 result = 30.0 + [self findLabelHeight:publicLinkUrl font:[UIFont systemFontOfSize:15.0]];
254 return MAX(aTableView.rowHeight, result);
257 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
259 static NSString *CellIdentifier = @"Cell";
260 static NSString *TextViewCellIdentifier = @"TextViewCell";
262 if (indexPath.section == kPublic) {
263 if (indexPath.row == 1) {
264 TextViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:TextViewCellIdentifier];
266 cell = [[[TextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TextViewCellIdentifier] autorelease];
267 cell.textView.frame = cell.contentView.frame;
270 cell.selectionStyle = UITableViewCellSelectionStyleNone;
271 cell.textView.backgroundColor = [UIColor clearColor];
272 cell.textView.font = [UIFont systemFontOfSize:15.0];
273 cell.textView.dataDetectorTypes = UIDataDetectorTypeLink;
274 cell.textView.text = [NSString stringWithFormat:@"%@%@",
275 account.pithosPublicLinkURLPrefix,
276 [NSString encodeToPercentEscape:self.object.publicURI charactersToEncode:@"!*'();:@&=+$,?%#[]"]];
278 cell.selectionStyle = UITableViewCellSelectionStyleNone;
279 cell.accessoryView = UITableViewCellAccessoryNone;
284 UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
286 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
287 cell.textLabel.backgroundColor = [UIColor clearColor];
288 cell.detailTextLabel.backgroundColor = [UIColor clearColor];
289 cell.detailTextLabel.numberOfLines = 0;
290 cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
293 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
294 cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8];
296 cell.userInteractionEnabled = YES;
297 cell.detailTextLabel.textAlignment = UITextAlignmentRight;
298 cell.accessoryView = nil;
299 cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
301 // Configure the cell...
302 if (indexPath.section == kDetails) {
303 cell.accessoryType = UITableViewCellAccessoryNone;
304 cell.selectionStyle = UITableViewCellSelectionStyleNone;
305 cell.accessoryView = nil;
306 if (indexPath.row == 0) {
307 cell.textLabel.text = @"Name";
308 cell.detailTextLabel.text = object.name;
309 } else if (indexPath.row == 1) {
310 cell.textLabel.text = @"Full Path";
311 cell.detailTextLabel.text = object.fullPath;
312 } else if (indexPath.row == 2) {
313 cell.textLabel.text = @"Size";
314 cell.detailTextLabel.text = [object humanizedBytes];
315 } else if (indexPath.row == 3) {
316 cell.textLabel.text = @"Type";
317 cell.detailTextLabel.text = object.contentType;
319 } else if (indexPath.section == kMetadata) {
320 if (objectIsReadOnly) {
321 cell.accessoryType = UITableViewCellAccessoryNone;
322 cell.selectionStyle = UITableViewCellSelectionStyleNone;
323 cell.userInteractionEnabled = NO;
326 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
327 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
330 cell.accessoryView = nil;
331 if (indexPath.row == [object.metadata count]) {
332 cell.textLabel.text = @"Add Metadata";
333 cell.detailTextLabel.text = @"";
335 NSString *key = [[object.metadata allKeys] objectAtIndex:indexPath.row];
336 NSString *value = [object.metadata objectForKey:key];
337 NSString *metadataKeyCellText = key;
338 NSString *metadataValueCellText = value;
339 if ([metadataKeyCellText length] > maxMetadataViewableLength) {
340 metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
341 metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
343 if ([metadataValueCellText length] > maxMetadataViewableLength) {
344 metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
345 metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
348 cell.textLabel.text = metadataKeyCellText;
349 cell.detailTextLabel.text = metadataValueCellText;
351 } else if (indexPath.section == kPublic) {
352 if (indexPath.row == 0) {
353 cell.textLabel.text = @"Public URL";
354 cell.detailTextLabel.text = @"";
355 cell.accessoryView = objectIsPublicSwitch;
356 cell.accessoryType = UITableViewCellAccessoryNone;
357 cell.selectionStyle = UITableViewCellSelectionStyleNone;
359 } else if (indexPath.section == kPermissions) {
360 if (account.sharingAccount) {
361 cell.accessoryType = UITableViewCellAccessoryNone;
362 cell.selectionStyle = UITableViewCellSelectionStyleNone;
363 cell.userInteractionEnabled = NO;
366 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
367 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
369 cell.accessoryView = nil;
371 if (indexPath.row == [permissions count]) {
372 cell.textLabel.text = @"Add Permissions";
373 cell.detailTextLabel.text = @"";
376 NSString *user = [[permissions allKeys] objectAtIndex:indexPath.row];
377 cell.textLabel.text = user;
378 cell.detailTextLabel.text = [permissions objectForKey:user];
380 } else if (indexPath.section == cdnURLSection) {
381 cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
382 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
383 cell.textLabel.text = @"";
384 cell.detailTextLabel.text = [[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
385 } else if (indexPath.section == actionsSection) {
386 cell.accessoryView = nil;
387 if (performingAction) {
388 cell.textLabel.textColor = [UIColor grayColor];
389 cell.selectionStyle = UITableViewCellSelectionStyleNone;
390 cell.accessoryType = UITableViewCellAccessoryNone;
392 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
393 cell.selectionStyle = UITableViewCellSelectionStyleNone;
396 if (indexPath.row == 0) {
397 if (fileDownloaded) {
398 cell.textLabel.text = @"Open File";
399 cell.accessoryType = UITableViewCellAccessoryNone;
400 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
402 if (fileDownloading) {
403 cell.accessoryView = downloadProgressView;
404 // TODO: if you leave this view while downloading, there's EXC_BAD_ACCESS
405 cell.textLabel.text = @"Downloading";
407 cell.textLabel.text = @"Download File";
410 cell.detailTextLabel.text = @"";
412 } else if (indexPath.row == 1) {
413 cell.textLabel.text = @"Email File as Attachment";
414 cell.detailTextLabel.text = @"";
415 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
417 } else if (indexPath.section == deleteSection) {
418 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
419 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
420 cell.textLabel.text = @"Delete Object";
421 cell.detailTextLabel.text = @"";
431 #pragma mark Table view delegate
433 - (void)reloadActionsTitleRow:(NSTimer *)timer {
434 [[timer.userInfo objectForKey:@"tableView"] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
437 - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
438 if (indexPath.section == kMetadata) {
439 EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
440 NSString *metadataKey;
441 NSString *metadataValue;
443 if (indexPath.row == [self.object.metadata count]) {
446 vc.removeMetadataEnabled = FALSE;
447 vc.navigationItem.title = @"Add Metadata";
450 metadataKey = [[self.object.metadata allKeys] objectAtIndex:indexPath.row];
451 metadataValue = [self.object.metadata objectForKey:metadataKey];
452 vc.removeMetadataEnabled = YES;
453 vc.navigationItem.title = @"Edit Metadata";
456 vc.metadataKey = metadataKey;
457 vc.metadataValue = metadataValue;
458 vc.account = account;
459 vc.container = container;
461 [self.navigationController pushViewController:vc animated:YES];
463 } else if (indexPath.section == kPermissions) {
464 EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
467 if (indexPath.row == [permissions count]) {
469 vc.removePermissionsEnabled = NO;
470 vc.navigationItem.title = @"Add Permissions";
473 user = [[permissions allKeys] objectAtIndex:indexPath.row];
474 NSString *userPermissions = [permissions objectForKey:user];
475 if ([userPermissions rangeOfString:@"read"].location != NSNotFound)
476 vc.readPermissionSelected = YES;
478 vc.readPermissionSelected = NO;
480 if ([userPermissions rangeOfString:@"write"].location != NSNotFound)
481 vc.writePermissionSelected = YES;
483 vc.writePermissionSelected = NO;
485 vc.removePermissionsEnabled = YES;
486 vc.navigationItem.title = @"Edit Permissions";
490 vc.permissions = permissions;
491 vc.account = account;
492 vc.container = container;
494 vc.folderViewController = folderViewController;
495 [self.navigationController pushViewController:vc animated:YES];
497 } else if (indexPath.section == cdnURLSection) {
498 [cdnURLActionSheet showInView:self.view];
499 } else if (indexPath.section == actionsSection) {
500 if (indexPath.row == 0) {
501 if (fileDownloaded) {
502 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
503 NSString *documentsDirectory = [paths objectAtIndex:0];
504 NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
505 NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
507 self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
508 self.documentInteractionController.delegate = self;
510 if (![self.documentInteractionController presentOptionsMenuFromRect:self.view.frame inView:self.view animated:YES]) {
511 if ([self.object isPlayableMedia]) {
512 MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
513 vc.container = self.container;
514 vc.object = self.object;
515 [self.navigationController pushViewController:vc animated:YES];
518 [self alert:@"Error" message:@"This file could not be opened."];
519 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
523 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
526 if (!fileDownloading) {
528 fileDownloading = YES;
529 [self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self];
530 [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
534 } else if (indexPath.row == 1) {
536 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
537 NSString *documentsDirectory = [paths objectAtIndex:0];
538 NSString *shortPath = @"";
540 if (self.container && [self.container respondsToSelector:@selector(name)] && self.object && [self.object respondsToSelector:@selector(fullPath)]) {
541 shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
544 NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
545 NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
548 if (![MFMailComposeViewController canSendMail]) {
549 [self alert:@"Cannot send mail" message:@"Your device has not been configured for sending email"];
550 [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
553 MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
555 vc.mailComposeDelegate = self;
556 [vc setSubject:self.object.name];
557 [vc addAttachmentData:data mimeType:self.object.contentType fileName:self.object.name];
558 [vc setMessageBody:@"" isHTML:NO];
559 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
560 vc.modalPresentationStyle = UIModalPresentationPageSheet;
562 [self presentModalViewController:vc animated:YES];
566 } else if (indexPath.section == deleteSection) {
567 [deleteActionSheet showInView:self.view];
571 - (void)setProgress:(float)newProgress {
572 [downloadProgressView setProgress:newProgress animated:YES];
573 if (newProgress >= 1.0) {
574 fileDownloading = NO;
575 fileDownloaded = YES;
577 [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:1 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationBottom];
578 [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
584 #pragma mark Document Interation Controller Delegate
586 - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *) controller {
587 return self.navigationController;
590 - (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controllers {
591 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
595 #pragma mark Action Sheet Delegate
597 - (void)deleteObjectRow {
598 [self.folderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
601 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
602 if ([actionSheet isEqual:deleteActionSheet]) {
603 if (buttonIndex == 0) {
604 // delete the file and pop out
606 NSString *activityMessage = @"Deleting file";
608 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
609 [activityIndicatorView addToView:self.view];
612 [[self.account.manager deleteObject:self.container object:self.object]
613 success:^(OpenStackRequest *request) {
614 [activityIndicatorView removeFromSuperviewAndRelease];
615 performingAction = NO;
616 [self.folder.objects removeObjectForKey:self.object.name];
618 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
619 [self.navigationController popViewControllerAnimated:YES];
621 self.folderViewController.needsRefreshing = YES;
622 } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
624 [self.folderViewController setDetailViewController];
626 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
627 [self.folderViewController refreshButtonPressed:nil];
630 if ([self.folder.objects count] + [self.folder.folders count] == 0) {
631 [self.folderViewController.tableView reloadData];
633 [self.folderViewController.tableView selectRowAtIndexPath:selectedIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
634 [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteObjectRow) userInfo:nil repeats:NO];
637 failure:^(OpenStackRequest *request) {
638 [activityIndicatorView removeFromSuperviewAndRelease];
639 [self hideToolbarActivityMessage];
640 performingAction = NO;
641 [self alert:@"There was a problem deleting this file." request:request];
644 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:deleteSection];
645 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
646 } else if ([actionSheet isEqual:cdnURLActionSheet]) {
647 NSURL *url = [NSURL URLWithString:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
649 if (buttonIndex == 0) {
650 // copy to pasteboard
651 UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
652 [pasteboard setString:[url description]];
653 } else if (buttonIndex == 1) {
655 UIApplication *application = [UIApplication sharedApplication];
656 if ([application canOpenURL:url]) {
657 [application openURL:url];
659 [self alert:@"Error" message:[NSString stringWithFormat:@"This URL cannot be opened.\n%@", url]];
661 } else if (buttonIndex == 2) {
662 // email link to file
663 MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
664 vc.mailComposeDelegate = self;
665 [vc setSubject:self.object.name];
666 [vc setMessageBody:[url description] isHTML:NO];
667 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
668 vc.modalPresentationStyle = UIModalPresentationPageSheet;
670 [self presentModalViewController:vc animated:YES];
672 } else if (buttonIndex == 3) {
673 // tweet link to file
674 UIApplication *app = [UIApplication sharedApplication];
675 NSURL *twitterURL = [NSURL URLWithString:[NSString stringWithFormat:@"twitter://post?message=%@", [[url description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
676 if ([app canOpenURL:twitterURL]) {
677 [app openURL:twitterURL];
680 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:cdnURLSection] animated:YES];
685 #pragma mark Mail Composer Delegate
687 // Dismisses the email composition interface when users tap Cancel or Send.
688 - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
689 [self dismissModalViewControllerAnimated:YES];
690 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:actionsSection] animated:YES];
694 #pragma mark Switches
696 - (void)objectIsPublicSwitchChanged:(id)sender
698 NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."];
699 self.oldPubicURI = object.publicURI;
701 if (objectIsPublic) {
702 activityMessage = [NSString stringWithFormat:@"Disabling public link.."];
703 object.publicURI = @"";
706 object.publicURI = @"TRUE";
709 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
710 [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];
712 objectIsPublic = !objectIsPublic;
713 [[self.account.manager writeObjectMetadata:container object:object]
714 success:^(OpenStackRequest *request) {
715 NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
716 if (objectIsPublic) {
717 [[self.account.manager getObjectInfo:container object:object]
718 success:^(OpenStackRequest *request) {
719 [activityIndicatorView removeFromSuperviewAndRelease];
720 object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"];
721 NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
722 [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
723 withRowAnimation:UITableViewRowAnimationBottom];
725 failure:^(OpenStackRequest *request) {
726 [activityIndicatorView removeFromSuperviewAndRelease];
727 [self alert:@"There was a problem retrieving the public link from the server." request:request];
731 [activityIndicatorView removeFromSuperviewAndRelease];
732 [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
733 withRowAnimation:UITableViewRowAnimationTop];
736 failure:^(OpenStackRequest *request) {
737 [activityIndicatorView removeFromSuperviewAndRelease];
738 objectIsPublic = !objectIsPublic;
739 objectIsPublicSwitch.on = !objectIsPublicSwitch.on;
740 object.publicURI = oldPubicURI;
741 [self alert:@"There was a problem enabling the public link." request:request];
749 #pragma mark Memory management
753 [downloadProgressView release];
754 [deleteActionSheet release];
755 [cdnURLActionSheet release];
757 [folderViewController release];
758 [objectIsPublicSwitch release];
759 [permissions release];
760 [documentInteractionController release];