// // FolderViewController.m // OpenStack // // Created by Mike Mayo on 12/15/10. // The OpenStack project is provided under the Apache 2.0 license. // #import "FolderViewController.h" #import "OpenStackAccount.h" #import "Container.h" #import "Folder.h" #import "StorageObject.h" #import "StorageObjectViewController.h" #import "ContainerDetailViewController.h" #import "AccountManager.h" #import "ActivityIndicatorView.h" #import "ContainersViewController.h" #import "UIViewController+Conveniences.h" #import "AddObjectViewController.h" #import "OpenStackAppDelegate.h" #import "RootViewController.h" #import "FolderDetailViewController.h" #import "AccountHomeViewController.h" #import "PithosImageViewController.h" #import "OpenStackRequest.h" #import "JSON.h" #import "APICallback.h" #import "OpenStackAppDelegate.h" #import #import "ComputeModel.h" @implementation FolderViewController @synthesize account, container, folder, containersViewController, selectedContainerIndexPath, contentsLoaded, parentFolderViewController, selectedFolderIndexPath, tableView, needsRefreshing, folderHasBeenRemoved, refreshWhenAppeared, folderDetailVC, selectedObjectViewController, refreshButton; - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait); } #pragma mark - #pragma mark View lifecycle - (void)viewDidLoad { [super viewDidLoad]; [self addAddButton]; [self addHomeButton]; deleteActionSheet = [[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this folder? This operation cannot be undone." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete Folder" otherButtonTitles:nil]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (folder.name && ![@"" isEqualToString:folder.name]) { self.navigationItem.title = self.folder.name; } else { self.navigationItem.title = self.container.name; } self.selectedObjectViewController = nil; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (!self.folder) { if (folderHasBeenRemoved) { if (needsRefreshing) self.parentFolderViewController.needsRefreshing = YES; [self.navigationController popViewControllerAnimated:YES]; } else [self refreshButtonPressed:nil]; } else { if (self.needsRefreshing) { self.parentFolderViewController.needsRefreshing = YES; if (refreshWhenAppeared) { [self refreshButtonPressed:nil]; refreshWhenAppeared = NO; } if (account.shared && ([folder.folders count] + [folder.objects count] == 0)) { [self.navigationController popViewControllerAnimated:YES]; } } } } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (![self.navigationController.viewControllers containsObject:self]) { if (self.parentViewController && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) [parentFolderViewController setDetailViewController]; } if (!(folder.name && ![@"" isEqualToString:folder.name])) { // [self.containersViewController.tableView deselectRowAtIndexPath:self.selectedContainerIndexPath animated:YES]; } } - (void)setDetailViewController { if ([self.folder isEqual:self.container.rootFolder]) { ContainerDetailViewController *vc = [[ContainerDetailViewController alloc] initWithNibName:@"ContainerDetailViewController" bundle:nil]; vc.account = self.account; vc.container = self.container; vc.containersViewController = self.containersViewController; vc.rootFolderViewController = self; vc.selectedContainerIndexPath = self.selectedContainerIndexPath; [self presentPrimaryViewController:vc]; self.containersViewController.containerDetailViewController = vc; [vc release]; } else { FolderDetailViewController *folderDetailViewController = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil]; folderDetailViewController.account = account; folderDetailViewController.container = container; folderDetailViewController.folder = self.folder; folderDetailViewController.folderViewController = self.parentFolderViewController; [self presentPrimaryViewController:folderDetailViewController]; self.parentFolderViewController.folderDetailVC = folderDetailViewController; [folderDetailViewController release]; } } - (void)reloadFolderViewControllers { NSArray *viewControllers = [self.navigationController viewControllers]; int rootFolderControllerIndex = [viewControllers indexOfObject:self.containersViewController] + 1; FolderViewController *rootFolderViewController = [viewControllers objectAtIndex:rootFolderControllerIndex]; rootFolderViewController.folder = self.container.rootFolder; [rootFolderViewController.tableView reloadData]; for (int i = rootFolderControllerIndex + 1; i < [viewControllers count]; i++) { FolderViewController *folderViewController = [viewControllers objectAtIndex:i]; Folder *parentFolder = folderViewController.parentFolderViewController.folder; folderViewController.folder = [parentFolder.folders objectForKey:folderViewController.folder.name]; if (folderViewController.folder) { [folderViewController.tableView reloadData]; } else { folderViewController.folderHasBeenRemoved = YES; } } if (!self.folder || (account.shared && [folder.folders count] + [folder.objects count] == 0)) { if (needsRefreshing && self.parentFolderViewController) { self.parentFolderViewController.needsRefreshing = YES; } [self.navigationController popViewControllerAnimated:YES]; } else { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad && account.shared) [self setDetailViewController]; } } #pragma mark - #pragma mark Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView { return 1; } - (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section { return MAX(contentsLoaded ? 1 : 0, [folder.objects count] + [folder.folders count]); } - (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if ([folder.objects count] + [folder.folders count] == 0) { aTableView.scrollEnabled = NO; aTableView.allowsSelection = NO; return aTableView.frame.size.height; } else { aTableView.scrollEnabled = YES; aTableView.allowsSelection = YES; return aTableView.rowHeight; } } // Customize the appearance of table view cells. - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if ([folder.objects count] + [folder.folders count] == 0) { NSString *noFoldersTitle = (self.account.shared || self.account.sharingAccount) ? @"No shared Files or Folders" : @"No Files or Folders"; NSString *noFoldersSubtitle = (self.account.shared || self.account.sharingAccount || [self.folder.name hasSuffix:@"/"]) ? @"" : @"Tap the + button to create a new Folder"; NSString *deleteFolderButtonTitle = (self.account.shared || self.account.sharingAccount) ? nil : @"Delete Folder"; SEL deleteButtonSelector = (self.account.shared || self.account.sharingAccount) ? nil : @selector(deleteButtonPressed:); self.navigationItem.rightBarButtonItem.enabled = (self.account.shared || [self.folder.name hasSuffix:@"/"]) ? NO : YES; if ([self.container.rootFolder isEqual:self.folder]) { return [self tableView:aTableView emptyCellWithImage:[UIImage imageNamed:@"empty-files.png"] title:noFoldersTitle subtitle:noFoldersSubtitle]; } else { return [self tableView:aTableView emptyCellWithImage:[UIImage imageNamed:@"empty-files.png"] title:noFoldersTitle subtitle:noFoldersSubtitle deleteButtonTitle:deleteFolderButtonTitle deleteButtonSelector:deleteButtonSelector]; } } else { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease]; } id item = [[self.folder sortedContents] objectAtIndex:indexPath.row]; [cell.textLabel setLineBreakMode:UILineBreakModeTailTruncation]; cell.textLabel.text = [item name]; if ([[item class] isEqual:[Folder class]]) { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; } else { cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; } cell.imageView.image = [UIImage imageNamed:@"folder-icon.png"]; NSString *folderString = @""; NSString *objectString = @""; if ([[item folders] count] > 1) { folderString = [NSString stringWithFormat:@"%i folders, ", [[item folders] count]]; } else if ([[item folders] count] > 0) { folderString = @"1 folder, "; } if ([[item objects] count] != 1) { objectString = [NSString stringWithFormat:@"%i objects", [[item objects] count]]; } else if ([[item objects] count]) { objectString = @"1 object"; } cell.detailTextLabel.text = [NSString stringWithFormat:@"%@%@", folderString, objectString]; } else if ([[item class] isEqual:[StorageObject class]]) { [cell.textLabel setLineBreakMode:UILineBreakModeMiddleTruncation]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; StorageObject *object = item; CFStringRef mimeType = (CFStringRef)object.contentType; CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL); NSString *emptyPath = [[NSBundle mainBundle] pathForResource:@"empty-file" ofType:@""]; UIDocumentInteractionController *udic = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:emptyPath]]; if (((NSString *)uti).length > 0) udic.UTI = (NSString *)uti; if ([udic.icons count] > 0) cell.imageView.image = [udic.icons objectAtIndex:0]; if ([item lastModifiedString]) cell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@", [item humanizedBytes], [item lastModifiedString]]; else cell.detailTextLabel.text = [item humanizedBytes]; } return cell; } } #pragma mark - #pragma mark Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if ([folder.objects count] + [folder.folders count] > 0) { id item = [self.folder.sortedContents objectAtIndex:indexPath.row]; if ([[item class] isEqual:[Folder class]]) { FolderViewController *vc = [[FolderViewController alloc] initWithNibName:@"FolderViewController" bundle:nil]; vc.account = self.account; vc.container = self.container; vc.folder = item; vc.contentsLoaded = YES; vc.selectedFolderIndexPath = indexPath; vc.parentFolderViewController = self; vc.containersViewController = self.containersViewController; [self.navigationController pushViewController:vc animated:YES]; [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { Folder *folderForDetailView; if ([folder.sortedContents count] > 0) { folderForDetailView = [self.folder.sortedContents objectAtIndex:indexPath.row]; } else { folderForDetailView = nil; } FolderDetailViewController *folderDetailViewController = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil]; folderDetailViewController.account = account; folderDetailViewController.container = container; folderDetailViewController.folder = folderForDetailView; folderDetailViewController.folderViewController = vc; [self presentPrimaryViewController:folderDetailViewController]; self.folderDetailVC = folderDetailViewController; [folderDetailViewController release]; } [vc release]; } else if ([[item class] isEqual:[StorageObject class]]) { StorageObjectViewController *vc = [[StorageObjectViewController alloc] initWithNibName:@"StorageObjectViewController" bundle:nil]; vc.account = self.account; vc.container = self.container; vc.folder = self.folder; vc.object = item; vc.folderViewController = self; vc.selectedIndexPath = indexPath; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { [self.navigationController presentPrimaryViewController:vc]; self.selectedObjectViewController = vc; OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate]; if (app.rootViewController.popoverController != nil) { [app.rootViewController.popoverController dismissPopoverAnimated:YES]; } } else { [self.navigationController pushViewController:vc animated:YES]; [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; } [vc release]; } } } - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath { Folder *folderForDetailView; if ([folder.sortedContents count] > 0) { folderForDetailView = [self.folder.sortedContents objectAtIndex:indexPath.row]; } else { folderForDetailView = nil; } FolderDetailViewController *vc = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil]; vc.account = account; vc.container = container; vc.folder = folderForDetailView; vc.folderViewController = self; [self.navigationController pushViewController:vc animated:YES]; [vc release]; } #pragma mark - #pragma mark Buttons - (IBAction)homeButtonPressed:(id)sender { [self.navigationController popToViewController:containersViewController.accountHomeViewController animated:YES]; } - (IBAction)refreshButtonPressed:(id)sender { activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Loading..."] text:@"Loading..."]; [activityIndicatorView addToView:self.view]; [self.account.manager getObjects:self.container]; successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectsSucceeded" object:self.container queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) { [activityIndicatorView removeFromSuperviewAndRelease]; contentsLoaded = YES; [self.containersViewController refreshButtonPressed:nil]; [self reloadFolderViewControllers]; needsRefreshing = NO; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { if (self.selectedObjectViewController) { [self.selectedObjectViewController reloadMetadataSection]; } else if ([self.folder isEqual:container.rootFolder]) { [containersViewController.containerDetailViewController reloadMetadataSection]; } else if ([parentFolderViewController.folderDetailVC.navigationController.topViewController isEqual:parentFolderViewController.folderDetailVC]) { [parentFolderViewController.folderDetailVC reloadMetadataSection]; } } [[NSNotificationCenter defaultCenter] removeObserver:successObserver]; }]; failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getObjectsFailed" object:self.container queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) { needsRefreshing = NO; [self.tableView reloadData]; [activityIndicatorView removeFromSuperviewAndRelease]; [self alert:@"Error" message:@"Failed to retrieve files from server"]; [[NSNotificationCenter defaultCenter] removeObserver:failureObserver]; }]; } - (void)addButtonPressed:(id)sender { AddObjectViewController *vc = [[AddObjectViewController alloc] initWithNibName:@"AddObjectViewController" bundle:nil]; vc.account = self.account; vc.container = self.container; vc.folder = self.folder; vc.folderViewController = self; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { vc.modalPresentationStyle = UIModalPresentationFormSheet; OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate]; if (app.rootViewController.popoverController != nil) { [app.rootViewController.popoverController dismissPopoverAnimated:YES]; } } [self presentModalViewControllerWithNavigation:vc animated:YES]; [vc release]; } - (void)deleteButtonPressed:(id)sender { OpenStackAppDelegate *appDelegate = (OpenStackAppDelegate *)[[UIApplication sharedApplication] delegate]; [deleteActionSheet showInView:appDelegate.masterNavigationController.view]; } #pragma mark - #pragma mark Action Sheet Delegate - (void)deleteFolderRow { [self.parentFolderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedFolderIndexPath] withRowAnimation:UITableViewRowAnimationLeft]; } - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex == 0) { StorageObject *object = [[StorageObject alloc] init]; object.name = object.fullPath = [self.folder fullPath]; NSString *activityMessage = @"Deleting folder..."; activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage]; [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y]; [[self.account.manager deleteObject:self.container object:object] success:^(OpenStackRequest *request) { if (self.folder.parent) { [self.folder.parent.folders removeObjectForKey:self.folder.name]; } else { [self.container.rootFolder.folders removeObjectForKey:self.folder.name]; } [self.account persist]; [activityIndicatorView removeFromSuperviewAndRelease]; [self.navigationController popViewControllerAnimated:YES]; if (self.folder.parent) { if ([self.folder.parent.folders count] + [self.folder.parent.objects count] == 0) { [self.parentFolderViewController.tableView reloadData]; } else { [self.parentFolderViewController.tableView selectRowAtIndexPath:selectedFolderIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteFolderRow) userInfo:nil repeats:NO]; } if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) [self.parentFolderViewController setDetailViewController]; } else { if ([self.container.rootFolder.folders count] + [self.container.rootFolder.objects count] == 0) { [self.parentFolderViewController.tableView reloadData]; } else { [self.parentFolderViewController.tableView selectRowAtIndexPath:selectedFolderIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteFolderRow) userInfo:nil repeats:NO]; } } [object release]; } failure:^(OpenStackRequest *request) { [activityIndicatorView removeFromSuperviewAndRelease]; [self alert:@"There was a problem deleting this folder." request:request]; [object release]; }]; } } #pragma mark - #pragma mark Memory management - (void)dealloc { [account release]; [container release]; [folder release]; [containersViewController release]; [selectedContainerIndexPath release]; [deleteActionSheet release]; [parentFolderViewController release]; [selectedFolderIndexPath release]; [refreshButton release]; [super dealloc]; } @end