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"
31 #define kPermissions 3
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 CDN URL sections (action sheet to copy, open in safari, and email link)
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;
63 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
64 return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
67 - (void)setBackgroundView {
69 UIImageView *logo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cloudfiles-large.png"]];
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);
78 [backgroundContainer addSubview:logo];
79 tableView.backgroundView = backgroundContainer;
80 [backgroundContainer release];
82 self.tableView.backgroundView = nil;
87 - (IBAction)homeButtonPressed:(id)sender {
88 [self.navigationController popToRootViewControllerAnimated:YES];
92 #pragma mark View lifecycle
96 objectIsPublicSwitch = [[UISwitch alloc] init];
97 [objectIsPublicSwitch addTarget:self action:@selector(objectIsPublicSwitchChanged:) forControlEvents:UIControlEventValueChanged];
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];
109 NSArray *users = [[array objectAtIndex:1] componentsSeparatedByString:@","];
110 for (NSString *user in users) {
111 [permissions setObject:permissionsType forKey:user];
115 objectIsReadOnly = NO;
116 if (account.sharingAccount) {
117 if ([permissions count] > 0) {
118 objectIsReadOnly = [[permissions objectForKey:[account username]] isEqualToString:@"read"];
120 objectIsPublicSwitch.enabled = NO;
124 - (void)viewWillAppear:(BOOL)animated {
125 [super viewWillAppear:animated];
126 self.navigationItem.title = object.name;
128 NSIndexPath *selection = [self.tableView indexPathForSelectedRow];
130 [self.tableView deselectRowAtIndexPath:selection animated:YES];
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];
137 NSFileManager *fileManager = [NSFileManager defaultManager];
138 fileDownloaded = [fileManager fileExistsAtPath:filePath];
140 downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
142 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
143 CGRect rect = downloadProgressView.frame;
144 rect.size.width = 440.0;
145 downloadProgressView.frame = rect;
148 //[self setBackgroundView];
149 if (self.container.cdnEnabled) {
159 if (account.sharingAccount || account.shared) {
163 // let's see if we can tweet
164 UIApplication *app = [UIApplication sharedApplication];
165 NSURL *twitterURL = [NSURL URLWithString:@"twitter://post?message=test"];
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];
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];
172 objectIsPublic = ([object.publicURI length]);
173 objectIsPublicSwitch.on = objectIsPublic;
174 [self.tableView reloadData];
178 #pragma mark Table view data source
180 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
181 if (account.sharingAccount || account.shared)
182 return self.container.cdnEnabled ? 6 : 5;
184 return self.container.cdnEnabled ? 7 : 6;
187 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
188 if (section == kDetails) {
190 } else if (section == kMetadata) {
191 if (objectIsReadOnly)
192 return [object.metadata count];
194 return 1 + [object.metadata count];
195 } else if (section == cdnURLSection) {
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];
205 return 1 + [permissions count];
211 - (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font {
212 CGSize textLabelSize;
213 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
215 textLabelSize = CGSizeMake(596.0, 9000.0f);
217 textLabelSize = CGSizeMake(280.0, 9000.0f);
219 CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeCharacterWrap];
220 return stringSize.height;
223 - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
224 CGFloat result = aTableView.rowHeight;
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);
233 textLabelSize = CGSizeMake(221.0, 9000.0f);
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);
242 textLabelSize = CGSizeMake(221.0, 9000.0f);
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];
251 result = 30.0 + [self findLabelHeight:publicLinkUrl font:[UIFont systemFontOfSize:15.0]];
253 return MAX(aTableView.rowHeight, result);
256 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
258 static NSString *CellIdentifier = @"Cell";
259 static NSString *TextViewCellIdentifier = @"TextViewCell";
261 if (indexPath.section == kPublic) {
262 if (indexPath.row == 1) {
263 TextViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:TextViewCellIdentifier];
265 cell = [[[TextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TextViewCellIdentifier] autorelease];
266 cell.textView.frame = cell.contentView.frame;
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:@"!*'();:@&=+$,?%#[]"]];
277 cell.selectionStyle = UITableViewCellSelectionStyleNone;
278 cell.accessoryView = UITableViewCellAccessoryNone;
283 UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
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;
292 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
293 cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8];
295 cell.userInteractionEnabled = YES;
296 cell.detailTextLabel.textAlignment = UITextAlignmentRight;
297 cell.accessoryView = nil;
298 cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
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;
318 } else if (indexPath.section == kMetadata) {
319 if (objectIsReadOnly) {
320 cell.accessoryType = UITableViewCellAccessoryNone;
321 cell.selectionStyle = UITableViewCellSelectionStyleNone;
322 cell.userInteractionEnabled = NO;
325 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
326 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
329 cell.accessoryView = nil;
330 if (indexPath.row == [object.metadata count]) {
331 cell.textLabel.text = @"Add Metadata";
332 cell.detailTextLabel.text = @"";
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:@"..."];
342 if ([metadataValueCellText length] > maxMetadataViewableLength) {
343 metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
344 metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
347 cell.textLabel.text = metadataKeyCellText;
348 cell.detailTextLabel.text = metadataValueCellText;
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;
358 } else if (indexPath.section == kPermissions) {
359 if (account.sharingAccount) {
360 cell.accessoryType = UITableViewCellAccessoryNone;
361 cell.selectionStyle = UITableViewCellSelectionStyleNone;
362 cell.userInteractionEnabled = NO;
365 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
366 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
368 cell.accessoryView = nil;
370 if (indexPath.row == [permissions count]) {
371 cell.textLabel.text = @"Add Permissions";
372 cell.detailTextLabel.text = @"";
375 NSString *user = [[permissions allKeys] objectAtIndex:indexPath.row];
376 cell.textLabel.text = user;
377 cell.detailTextLabel.text = [permissions objectForKey:user];
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;
391 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
392 cell.selectionStyle = UITableViewCellAccessoryNone;
395 if (indexPath.row == 0) {
396 if (fileDownloaded) {
397 cell.textLabel.text = @"Open File";
398 cell.accessoryType = UITableViewCellAccessoryNone;
399 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
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";
406 cell.textLabel.text = @"Download File";
409 cell.detailTextLabel.text = @"";
411 } else if (indexPath.row == 1) {
412 cell.textLabel.text = @"Email File as Attachment";
413 cell.detailTextLabel.text = @"";
414 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
416 } else if (indexPath.section == deleteSection) {
417 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
418 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
419 cell.textLabel.text = @"Delete Object";
420 cell.detailTextLabel.text = @"";
430 #pragma mark Table view delegate
432 - (void)reloadActionsTitleRow:(NSTimer *)timer {
433 [[timer.userInfo objectForKey:@"tableView"] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
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;
442 if (indexPath.row == [self.object.metadata count]) {
445 vc.removeMetadataEnabled = FALSE;
446 vc.navigationItem.title = @"Add Metadata";
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";
455 vc.metadataKey = metadataKey;
456 vc.metadataValue = metadataValue;
457 vc.account = account;
458 vc.container = container;
460 [self.navigationController pushViewController:vc animated:YES];
462 } else if (indexPath.section == kPermissions) {
463 EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
466 if (indexPath.row == [permissions count]) {
468 vc.removePermissionsEnabled = NO;
469 vc.navigationItem.title = @"Add Permissions";
472 user = [[permissions allKeys] objectAtIndex:indexPath.row];
473 NSString *userPermissions = [permissions objectForKey:user];
474 if ([userPermissions rangeOfString:@"read"].location != NSNotFound)
475 vc.readPermissionSelected = YES;
477 vc.readPermissionSelected = NO;
479 if ([userPermissions rangeOfString:@"write"].location != NSNotFound)
480 vc.writePermissionSelected = YES;
482 vc.writePermissionSelected = NO;
484 vc.removePermissionsEnabled = YES;
485 vc.navigationItem.title = @"Edit Permissions";
489 vc.permissions = permissions;
490 vc.account = account;
491 vc.container = container;
493 vc.folderViewController = folderViewController;
494 [self.navigationController pushViewController:vc animated:YES];
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];
506 self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
507 self.documentInteractionController.delegate = self;
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];
517 [self alert:@"Error" message:@"This file could not be opened."];
518 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
522 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
525 if (!fileDownloading) {
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];
533 } else if (indexPath.row == 1) {
535 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
536 NSString *documentsDirectory = [paths objectAtIndex:0];
537 NSString *shortPath = @"";
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];
543 NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
544 NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
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];
552 MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
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;
561 [self presentModalViewController:vc animated:YES];
565 } else if (indexPath.section == deleteSection) {
566 [deleteActionSheet showInView:self.view];
570 - (void)setProgress:(float)newProgress {
571 [downloadProgressView setProgress:newProgress animated:YES];
572 if (newProgress >= 1.0) {
573 fileDownloading = NO;
574 fileDownloaded = YES;
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];
583 #pragma mark Document Interation Controller Delegate
585 - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *) controller {
586 return self.navigationController;
589 - (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controllers {
590 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
594 #pragma mark Action Sheet Delegate
596 - (void)deleteObjectRow {
597 [self.folderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
600 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
601 if ([actionSheet isEqual:deleteActionSheet]) {
602 if (buttonIndex == 0) {
603 // delete the file and pop out
605 NSString *activityMessage = @"Deleting file";
607 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
608 [activityIndicatorView addToView:self.view];
611 [self.account.manager deleteObject:self.container object:self.object];
613 deleteSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectSucceeded" object:self.object
614 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
616 [activityIndicatorView removeFromSuperviewAndRelease];
617 performingAction = NO;
618 [self.folder.objects removeObjectForKey:self.object.name];
620 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
621 [self.navigationController popViewControllerAnimated:YES];
623 self.folderViewController.needsRefreshing = YES;
624 } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
626 [self.folderViewController setDetailViewController];
628 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
629 [self.folderViewController refreshButtonPressed:nil];
632 if ([self.folder.objects count] + [self.folder.folders count] == 0) {
633 [self.folderViewController.tableView reloadData];
635 [self.folderViewController.tableView selectRowAtIndexPath:selectedIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
636 [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteObjectRow) userInfo:nil repeats:NO];
638 [[NSNotificationCenter defaultCenter] removeObserver:deleteSuccessObserver];
641 deleteFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectFailed" object:self.object
642 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
644 [activityIndicatorView removeFromSuperviewAndRelease];
645 [self hideToolbarActivityMessage];
646 performingAction = NO;
647 [self alert:@"There was a problem deleting this file." request:[notification.userInfo objectForKey:@"request"]];
649 [[NSNotificationCenter defaultCenter] removeObserver:deleteFailureObserver];
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]];
659 if (buttonIndex == 0) {
660 // copy to pasteboard
661 UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
662 [pasteboard setString:[url description]];
663 } else if (buttonIndex == 1) {
665 UIApplication *application = [UIApplication sharedApplication];
666 if ([application canOpenURL:url]) {
667 [application openURL:url];
669 [self alert:@"Error" message:[NSString stringWithFormat:@"This URL cannot be opened.\n%@", url]];
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;
680 [self presentModalViewController:vc animated:YES];
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];
690 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:cdnURLSection] animated:YES];
695 #pragma mark Mail Composer Delegate
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];
704 #pragma mark Switches
706 - (void)objectIsPublicSwitchChanged:(id)sender
708 NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."];
709 self.oldPubicURI = object.publicURI;
711 if (objectIsPublic) {
712 activityMessage = [NSString stringWithFormat:@"Disabling public link.."];
713 object.publicURI = @"";
716 object.publicURI = @"TRUE";
719 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
720 [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];
722 objectIsPublic = !objectIsPublic;
723 [self.account.manager writeObjectMetadata:container object:object];
724 if (objectIsPublic) {
725 getObjectInfoSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectInfoSucceeded"
727 queue:[NSOperationQueue mainQueue]
728 usingBlock:^(NSNotification *notification)
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];
737 [[NSNotificationCenter defaultCenter] removeObserver:getObjectInfoSuccessObserver];
740 getObjectInfoFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectInfoFailed"
742 queue:[NSOperationQueue mainQueue]
743 usingBlock:^(NSNotification *notification)
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];
750 enablePublicSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataSucceeded"
752 queue:[NSOperationQueue mainQueue]
753 usingBlock:^(NSNotification* notification)
755 NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
756 if (objectIsPublic) {
757 [self.account.manager getObjectInfo:container object:object];
760 [activityIndicatorView removeFromSuperviewAndRelease];
761 [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
762 withRowAnimation:UITableViewRowAnimationTop];
765 [[NSNotificationCenter defaultCenter] removeObserver:enablePublicSuccessObserver];
768 enablePublicFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataFailed"
770 queue:[NSOperationQueue mainQueue]
771 usingBlock:^(NSNotification* notification)
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];
787 #pragma mark Memory management
791 [downloadProgressView release];
792 [deleteActionSheet release];
793 [cdnURLActionSheet release];
795 [folderViewController release];
796 [objectIsPublicSwitch release];
797 [permissions release];
798 [documentInteractionController release];