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"
29 #define kPermissions 3
31 #define maxMetadataViewableLength 12
34 // TODO: use etag to reset download
35 // TODO: try downloading directly to the file to save memory. don't use object.data
41 Content Type text/plain
44 Key Value -> tap goes to a metadata item VC to edit or delete
46 Add Metadata... (if max not already reached)
48 CDN URL sections (action sheet to copy, open in safari, and email link)
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;
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;
87 #pragma mark View lifecycle
91 objectIsPublicSwitch = [[UISwitch alloc] init];
92 [objectIsPublicSwitch addTarget:self action:@selector(objectIsPublicSwitchChanged:) forControlEvents:UIControlEventValueChanged];
94 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];
96 permissions = [[NSMutableDictionary alloc] init];
97 NSArray *sharingArray = [object.sharing componentsSeparatedByString:@";"];
98 for (NSString *typeSpecificPermissions in sharingArray) {
99 NSArray *array=[typeSpecificPermissions componentsSeparatedByString:@"="];
100 NSString *permissionsType = [array objectAtIndex:0];
101 if ([permissionsType hasPrefix:@" "])
102 permissionsType = [permissionsType substringFromIndex:1];
104 NSArray *users = [[array objectAtIndex:1] componentsSeparatedByString:@","];
105 for (NSString *user in users) {
106 [permissions setObject:permissionsType forKey:user];
111 - (void)viewWillAppear:(BOOL)animated {
112 [super viewWillAppear:animated];
113 self.navigationItem.title = object.name;
115 NSIndexPath* selection = [self.tableView indexPathForSelectedRow];
117 [self.tableView deselectRowAtIndexPath:selection animated:YES];
119 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
120 NSString *documentsDirectory = [paths objectAtIndex:0];
121 NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
122 NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
124 NSFileManager *fileManager = [NSFileManager defaultManager];
125 fileDownloaded = [fileManager fileExistsAtPath:filePath];
127 downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
129 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
130 CGRect rect = downloadProgressView.frame;
131 rect.size.width = 440.0;
132 downloadProgressView.frame = rect;
135 [self setBackgroundView];
136 if (self.container.cdnEnabled) {
146 // let's see if we can tweet
147 UIApplication *app = [UIApplication sharedApplication];
148 NSURL *twitterURL = [NSURL URLWithString:@"twitter://post?message=test"];
150 if ([app canOpenURL:twitterURL]) {
151 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];
153 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];
156 objectIsPublic = (object.publicURI != nil);
157 objectIsPublicSwitch.on = objectIsPublic;
158 [self.tableView reloadData];
162 #pragma mark Table view data source
164 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
165 return self.container.cdnEnabled ? 7 : 6;
168 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
169 if (section == kDetails) {
171 } else if (section == kMetadata) {
172 return 1 + [object.metadata count];
173 } else if (section == cdnURLSection) {
175 } else if (section == actionsSection) {
176 return fileDownloaded ? 2 : 1;
177 } else if (section == kPublic) {
178 return objectIsPublic ? 2 : 1;
179 } else if (section == kPermissions) {
180 return 1 + [permissions count];
186 - (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font {
187 CGSize textLabelSize;
188 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
190 textLabelSize = CGSizeMake(596.0, 9000.0f);
192 textLabelSize = CGSizeMake(280.0, 9000.0f);
194 CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeCharacterWrap];
195 return stringSize.height;
198 - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
199 CGFloat result = aTableView.rowHeight;
201 if (indexPath.section == cdnURLSection) {
202 result = 22.0 + [self findLabelHeight:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] font:[UIFont systemFontOfSize:18.0]];
203 } else if (indexPath.section == kDetails && indexPath.row == 1) {
204 CGSize textLabelSize;
205 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
206 textLabelSize = CGSizeMake(537.0, 9000.0f);
208 textLabelSize = CGSizeMake(221.0, 9000.0f);
210 CGSize stringSize = [object.fullPath sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
211 return 22.0 + stringSize.height;
212 } else if (indexPath.section == kDetails && indexPath.row == 0) {
213 CGSize textLabelSize;
214 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
215 textLabelSize = CGSizeMake(537.0, 9000.0f);
217 textLabelSize = CGSizeMake(221.0, 9000.0f);
219 CGSize stringSize = [object.name sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
220 return 22.0 + stringSize.height;
221 } else if (indexPath.section == kPublic && indexPath.row == 1) {
222 result = 22.0 + [self findLabelHeight:[NSString stringWithFormat:@"%@%@", self.account.provider.authEndpointURL, self.object.fullPath] font:[UIFont systemFontOfSize:15.0]];
226 return MAX(aTableView.rowHeight, result);
229 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
231 static NSString *CellIdentifier = @"Cell";
233 UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
235 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
236 //cell.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.93];
237 cell.textLabel.backgroundColor = [UIColor clearColor];
238 cell.detailTextLabel.backgroundColor = [UIColor clearColor];
239 cell.detailTextLabel.numberOfLines = 0;
240 cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
243 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
244 cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8];
247 cell.detailTextLabel.textAlignment = UITextAlignmentRight;
248 cell.accessoryView = nil;
249 cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
251 // Configure the cell...
252 if (indexPath.section == kDetails) {
253 cell.accessoryType = UITableViewCellAccessoryNone;
254 cell.selectionStyle = UITableViewCellSelectionStyleNone;
255 cell.accessoryView = nil;
256 if (indexPath.row == 0) {
257 cell.textLabel.text = @"Name";
258 cell.detailTextLabel.text = object.name;
259 } else if (indexPath.row == 1) {
260 cell.textLabel.text = @"Full Path";
261 cell.detailTextLabel.text = object.fullPath;
262 } else if (indexPath.row == 2) {
263 cell.textLabel.text = @"Size";
264 cell.detailTextLabel.text = [object humanizedBytes];
265 } else if (indexPath.row == 3) {
266 cell.textLabel.text = @"Type";
267 cell.detailTextLabel.text = object.contentType;
269 } else if (indexPath.section == kMetadata) {
270 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
271 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
272 cell.accessoryView = nil;
273 if (indexPath.row == [object.metadata count]) {
274 cell.textLabel.text = @"Add Metadata";
275 cell.detailTextLabel.text = @"";
277 NSString *key = [[object.metadata allKeys] objectAtIndex:indexPath.row];
278 NSString *value = [object.metadata objectForKey:key];
279 NSString *metadataKeyCellText = key;
280 NSString *metadataValueCellText = value;
281 if ([metadataKeyCellText length] > maxMetadataViewableLength) {
282 metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
283 metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
285 if ([metadataValueCellText length] > maxMetadataViewableLength) {
286 metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
287 metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
290 cell.textLabel.text = metadataKeyCellText;
291 cell.detailTextLabel.text = metadataValueCellText;
293 } else if (indexPath.section == kPublic) {
294 if (indexPath.row == 0) {
295 cell.textLabel.text = @"Public URL";
296 cell.detailTextLabel.text = @"";
297 cell.accessoryView = objectIsPublicSwitch;
298 cell.accessoryType = UITableViewCellAccessoryNone;
299 cell.selectionStyle = UITableViewCellSelectionStyleNone;
301 else if (indexPath.row == 1) {
302 cell.textLabel.numberOfLines = 0;
303 cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
304 cell.textLabel.text = [NSString stringWithFormat:@"%@%@", self.account.provider.authEndpointURL, object.publicURI];
305 cell.textLabel.font = [UIFont systemFontOfSize:15.0];
306 cell.selectionStyle = UITableViewCellSelectionStyleNone;
307 cell.accessoryView = UITableViewCellAccessoryNone;
308 cell.detailTextLabel.text = @"";
310 } else if (indexPath.section == kPermissions) {
311 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
312 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
313 cell.accessoryView = nil;
314 if (indexPath.row == [permissions count]) {
315 cell.textLabel.text = @"Add Permissions";
316 cell.detailTextLabel.text = @"";
319 NSString *user = [[permissions allKeys] objectAtIndex:indexPath.row];
320 cell.textLabel.text = user;
321 cell.detailTextLabel.text = [permissions objectForKey:user];
323 } else if (indexPath.section == cdnURLSection) {
324 cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
325 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
326 cell.textLabel.text = @"";
327 cell.detailTextLabel.text = [[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
328 } else if (indexPath.section == actionsSection) {
329 cell.accessoryView = nil;
330 if (performingAction) {
331 cell.textLabel.textColor = [UIColor grayColor];
332 cell.selectionStyle = UITableViewCellSelectionStyleNone;
333 cell.accessoryType = UITableViewCellAccessoryNone;
335 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
336 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
339 if (indexPath.row == 0) {
340 if (fileDownloaded) {
341 cell.textLabel.text = @"Open File";
343 if (fileDownloading) {
344 cell.accessoryView = downloadProgressView;
345 // TODO: if you leave this view while downloading, there's EXC_BAD_ACCESS
346 cell.textLabel.text = @"Downloading";
348 cell.textLabel.text = @"Download File";
351 cell.detailTextLabel.text = @"";
353 } else if (indexPath.row == 1) {
354 cell.textLabel.text = @"Email File as Attachment";
355 cell.detailTextLabel.text = @"";
357 } else if (indexPath.section == deleteSection) {
358 cell.textLabel.text = @"Delete Object";
359 cell.detailTextLabel.text = @"";
360 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
361 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
369 #pragma mark Table view delegate
371 - (void)reloadActionsTitleRow:(NSTimer *)timer {
372 [[timer.userInfo objectForKey:@"tableView"] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
375 - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
376 if (indexPath.section == kMetadata) {
377 EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
378 NSString *metadataKey;
379 NSString *metadataValue;
381 if (indexPath.row == [self.object.metadata count]) {
384 vc.deleteEnabled = FALSE;
385 vc.navigationItem.title = @"Add Metadata";
388 metadataKey = [[self.object.metadata allKeys] objectAtIndex:indexPath.row];
389 metadataValue = [self.object.metadata objectForKey:metadataKey];
390 vc.deleteEnabled = YES;
391 vc.navigationItem.title = @"Edit Metadata";
394 vc.metadataKey = metadataKey;
395 vc.metadataValue = metadataValue;
396 vc.account = account;
397 vc.container = container;
399 [self.navigationController pushViewController:vc animated:YES];
401 } else if (indexPath.section == kPermissions) {
402 EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
405 if (indexPath.row == [permissions count]) {
407 vc.newPermissionsEntry = TRUE;
408 vc.navigationItem.title = @"Add Permissions";
411 user = [[permissions allKeys] objectAtIndex:indexPath.row];
412 NSString *userPermissions = [permissions objectForKey:user];
413 if ([userPermissions rangeOfString:@"read"].location != NSNotFound)
414 vc.readPermissionSelected = YES;
416 vc.readPermissionSelected = NO;
418 if ([userPermissions rangeOfString:@"write"].location != NSNotFound)
419 vc.writePermissionSelected = YES;
421 vc.writePermissionSelected = NO;
423 vc.newPermissionsEntry = FALSE;
424 vc.navigationItem.title = @"Edit Permissions";
428 vc.permissions = permissions;
429 vc.account = account;
430 vc.container = container;
432 [self.navigationController pushViewController:vc animated:YES];
434 } else if (indexPath.section == cdnURLSection) {
435 [cdnURLActionSheet showInView:self.view];
436 } else if (indexPath.section == actionsSection) {
437 if (indexPath.row == 0) {
438 if (fileDownloaded) {
439 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
440 NSString *documentsDirectory = [paths objectAtIndex:0];
441 NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
442 NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
444 UIDocumentInteractionController *vc = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
447 if (![vc presentPreviewAnimated:YES]) {
449 if ([self.object isPlayableMedia]) {
450 MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
451 vc.container = self.container;
452 vc.object = self.object;
453 [self.navigationController pushViewController:vc animated:YES];
456 [self alert:@"Error" message:@"This file could not be opened."];
457 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
461 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
464 if (!fileDownloading) {
466 fileDownloading = YES;
467 [self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self];
468 [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
472 } else if (indexPath.row == 1) {
474 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
475 NSString *documentsDirectory = [paths objectAtIndex:0];
476 NSString *shortPath = @"";
478 if (self.container && [self.container respondsToSelector:@selector(name)] && self.object && [self.object respondsToSelector:@selector(fullPath)]) {
479 shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
482 NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
483 NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
485 MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
486 vc.mailComposeDelegate = self;
487 [vc setSubject:self.object.name];
488 [vc addAttachmentData:data mimeType:self.object.contentType fileName:self.object.name];
489 [vc setMessageBody:@"" isHTML:NO];
490 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
491 vc.modalPresentationStyle = UIModalPresentationPageSheet;
493 [self presentModalViewController:vc animated:YES];
496 } else if (indexPath.section == deleteSection) {
497 [deleteActionSheet showInView:self.view];
501 - (void)setProgress:(float)newProgress {
502 [downloadProgressView setProgress:newProgress animated:YES];
503 if (newProgress >= 1.0) {
504 fileDownloading = NO;
505 fileDownloaded = YES;
507 [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:1 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationBottom];
508 [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
513 #pragma mark Document Interation Controller Delegate
515 - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *) controller {
516 return self.navigationController;
519 - (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controllers {
520 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
524 #pragma mark Action Sheet Delegate
526 - (void)deleteObjectRow {
527 [self.folderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
530 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
531 if ([actionSheet isEqual:deleteActionSheet]) {
532 if (buttonIndex == 0) {
533 // delete the file and pop out
534 [self showToolbarActivityMessage:@"Deleting file..."];
536 [self.account.manager deleteObject:self.container object:self.object];
538 deleteSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectSucceeded" object:self.object
539 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
541 [self hideToolbarActivityMessage];
542 performingAction = NO;
543 [self.folder.objects removeObjectForKey:self.object.name];
544 [self.navigationController popViewControllerAnimated:YES];
545 if ([self.folder.objects count] + [self.folder.folders count] == 0) {
546 [self.folderViewController.tableView reloadData];
548 [self.folderViewController.tableView selectRowAtIndexPath:selectedIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
549 [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteObjectRow) userInfo:nil repeats:NO];
551 [[NSNotificationCenter defaultCenter] removeObserver:deleteSuccessObserver];
554 deleteFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteObjectFailed" object:self.object
555 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
557 [self hideToolbarActivityMessage];
558 performingAction = NO;
559 [self alert:@"There was a problem deleting this file." request:[notification.userInfo objectForKey:@"request"]];
561 [[NSNotificationCenter defaultCenter] removeObserver:deleteFailureObserver];
566 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:deleteSection];
567 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
568 } else if ([actionSheet isEqual:cdnURLActionSheet]) {
569 NSURL *url = [NSURL URLWithString:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
571 if (buttonIndex == 0) {
572 // copy to pasteboard
573 UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
574 [pasteboard setString:[url description]];
575 } else if (buttonIndex == 1) {
577 UIApplication *application = [UIApplication sharedApplication];
578 if ([application canOpenURL:url]) {
579 [application openURL:url];
581 [self alert:@"Error" message:[NSString stringWithFormat:@"This URL cannot be opened.\n%@", url]];
583 } else if (buttonIndex == 2) {
584 // email link to file
585 MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
586 vc.mailComposeDelegate = self;
587 [vc setSubject:self.object.name];
588 [vc setMessageBody:[url description] isHTML:NO];
589 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
590 vc.modalPresentationStyle = UIModalPresentationPageSheet;
592 [self presentModalViewController:vc animated:YES];
594 } else if (buttonIndex == 3) {
595 // tweet link to file
596 UIApplication *app = [UIApplication sharedApplication];
597 NSURL *twitterURL = [NSURL URLWithString:[NSString stringWithFormat:@"twitter://post?message=%@", [[url description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
598 if ([app canOpenURL:twitterURL]) {
599 [app openURL:twitterURL];
602 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:cdnURLSection] animated:YES];
607 #pragma mark Mail Composer Delegate
609 // Dismisses the email composition interface when users tap Cancel or Send.
610 - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
611 [self dismissModalViewControllerAnimated:YES];
612 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:actionsSection] animated:YES];
616 #pragma mark Switches
618 - (void)objectIsPublicSwitchChanged:(id)sender
620 NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."];
621 self.oldPubicURI = object.publicURI;
623 if (objectIsPublic) {
624 activityMessage = [NSString stringWithFormat:@"Disabling public link.."];
625 object.publicURI = @"";
628 object.publicURI = @"TRUE";
631 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
632 [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];
634 objectIsPublic = !objectIsPublic;
635 [self.account.manager writeObjectMetadata:container object:object];
636 if (objectIsPublic) {
637 getObjectInfoSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectInfoSucceeded"
639 queue:[NSOperationQueue mainQueue]
640 usingBlock:^(NSNotification *notification)
642 [activityIndicatorView removeFromSuperviewAndRelease];
643 OpenStackRequest *request = [notification.userInfo objectForKey:@"request"];
644 object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"];
645 NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
646 [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
647 withRowAnimation:UITableViewRowAnimationBottom];
649 [[NSNotificationCenter defaultCenter] removeObserver:getObjectInfoSuccessObserver];
652 getObjectInfoFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectInfoFailed"
654 queue:[NSOperationQueue mainQueue]
655 usingBlock:^(NSNotification *notification)
657 [activityIndicatorView removeFromSuperviewAndRelease];
658 [self alert:@"There was a problem retrieving the public link from the server." request:[notification.userInfo objectForKey:@"request"]];
659 [[NSNotificationCenter defaultCenter] removeObserver:getObjectInfoFailureObserver];
662 enablePublicSuccessObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataSucceeded"
664 queue:[NSOperationQueue mainQueue]
665 usingBlock:^(NSNotification* notification)
667 NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
668 if (objectIsPublic) {
669 [self.account.manager getObjectInfo:container object:object];
672 [activityIndicatorView removeFromSuperviewAndRelease];
673 [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
674 withRowAnimation:UITableViewRowAnimationTop];
677 [[NSNotificationCenter defaultCenter] removeObserver:enablePublicSuccessObserver];
680 enablePublicFailureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataFailed"
682 queue:[NSOperationQueue mainQueue]
683 usingBlock:^(NSNotification* notification)
685 [activityIndicatorView removeFromSuperviewAndRelease];
686 objectIsPublic = !objectIsPublic;
687 objectIsPublicSwitch.on = !objectIsPublicSwitch.on;
688 object.publicURI = oldPubicURI;
689 [self alert:@"There was a problem enabling the public link." request:[notification.userInfo objectForKey:@"request"]];
690 [[NSNotificationCenter defaultCenter] removeObserver:enablePublicFailureObserver];
699 #pragma mark Memory management
703 [downloadProgressView release];
704 [deleteActionSheet release];
705 [cdnURLActionSheet release];
707 [folderViewController release];
708 [objectIsPublicSwitch release];
709 [permissions release];