2 // ContainerDetailViewController.m
5 // Created by Mike Mayo on 12/22/10.
6 // The OpenStack project is provided under the Apache 2.0 license.
9 #import "ContainerDetailViewController.h"
10 #import "OpenStackAccount.h"
12 #import "ActivityIndicatorView.h"
13 #import "AccountManager.h"
14 #import "UIViewController+Conveniences.h"
15 #import "UIColor+MoreColors.h"
16 #import "ContainersViewController.h"
17 #import "EditMetadataViewController.h"
18 #import "EditPolicyViewController.h"
20 #import "FolderViewController.h"
21 #import "OpenStackRequest.h"
22 #import "APICallback.h"
23 #import "NSString+Conveniences.h"
28 #define maxMetadataViewableLength 12
30 @implementation ContainerDetailViewController
32 @synthesize account, container, containersViewController, selectedContainerIndexPath, rootFolderViewController;
34 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
35 return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
38 - (void)setBackgroundView {
39 if (!self.container) {
40 UIView *viewContainer = [[UIView alloc] init];
41 viewContainer.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
42 viewContainer.backgroundColor = [UIColor iPadTableBackgroundColor];
44 UILabel *label = [[UILabel alloc] init];
45 label.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
46 label.backgroundColor = [UIColor clearColor];
47 label.textColor = [UIColor emptyCollectionGrayColor];
48 label.font = [UIFont boldSystemFontOfSize:18.0];
49 label.text = @"No container selected";
50 label.textAlignment = UITextAlignmentCenter;
51 [viewContainer addSubview:label];
54 self.tableView.backgroundView = viewContainer;
55 [viewContainer release];
60 #pragma mark View lifecycle
64 if (!account.sharingAccount) {
73 - (void)viewWillAppear:(BOOL)animated {
74 [super viewWillAppear:animated];
76 self.navigationItem.title = container.name;
78 [self setBackgroundView];
79 [self.tableView reloadData];
82 - (void)viewDidAppear:(BOOL)animated {
83 [super viewDidAppear:animated];
84 if (self.container && !container.metadata && (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone))
85 [self reloadMetadataSection];
88 - (void)viewWillDisappear:(BOOL)animated {
89 [super viewWillDisappear:animated];
93 #pragma mark Table view data source
95 - (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font {
98 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
99 textLabelSize = CGSizeMake(577.0, 9000.0f);
101 textLabelSize = CGSizeMake(260.0, 9000.0f);
103 // pad \n\n to fix layout bug
104 CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
105 return stringSize.height;
108 - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
109 if (section == deleteSection) {
110 return @"Only empty containers can be deleted.";
116 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
117 if (self.container) {
118 if (account.sharingAccount)
127 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
128 // Return the number of rows in the section.
129 if (section == kOverview) {
131 } else if (section == kMetadata) {
132 if (account.sharingAccount)
133 return [container.metadata count];
135 return 1 + [container.metadata count];
136 } else if (section == policySection) {
137 if (account.sharingAccount)
146 // Customize the appearance of table view cells.
147 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
148 if (indexPath.section == deleteSection) {
149 static NSString *CellIdentifier = @"DeleteCell";
150 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
152 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
153 cell.textLabel.textAlignment = UITextAlignmentCenter;
154 cell.textLabel.text = @"Delete Container";
156 if ((self.container.count == 0) ||
157 (self.container.rootFolder && !self.container.rootFolder.objectsAndFoldersCount)) {
158 cell.textLabel.textColor = [UIColor blackColor];
159 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
161 cell.textLabel.textColor = [UIColor grayColor];
162 cell.selectionStyle = UITableViewCellSelectionStyleNone;
163 cell.userInteractionEnabled = NO;
168 static NSString *CellIdentifier = @"Cell";
169 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
171 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
172 cell.selectionStyle = UITableViewCellSelectionStyleNone;
173 cell.textLabel.backgroundColor = [UIColor clearColor];
174 cell.detailTextLabel.backgroundColor = [UIColor clearColor];
175 cell.detailTextLabel.numberOfLines = 0;
176 cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
177 cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
179 cell.accessoryType = UITableViewCellAccessoryNone;
180 cell.selectionStyle = UITableViewCellSelectionStyleNone;
182 if (indexPath.section == kOverview) {
183 cell.accessoryView = nil;
184 if (indexPath.row == 0) {
185 cell.textLabel.text = @"Name";
186 cell.detailTextLabel.text = container.name;
187 } else if (indexPath.row == 1) {
188 cell.textLabel.text = @"Size";
189 cell.detailTextLabel.text = [container humanizedSize];
191 } else if (indexPath.section == kMetadata) {
192 if (account.sharingAccount) {
193 cell.accessoryType = UITableViewCellAccessoryNone;
194 cell.selectionStyle = UITableViewCellSelectionStyleNone;
195 cell.userInteractionEnabled = NO;
198 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
199 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
201 if (indexPath.row == [container.metadata count]) {
202 cell.textLabel.text = @"Add Metadata";
203 cell.detailTextLabel.text = @"";
205 NSString *key = [[container.metadata allKeys] objectAtIndex:indexPath.row];
206 NSString *value = [container.metadata objectForKey:key];
207 NSString *metadataKeyCellText = key;
208 NSString *metadataValueCellText = value;
209 if ([metadataKeyCellText length] > maxMetadataViewableLength) {
210 metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
211 metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
213 if ([metadataValueCellText length] > maxMetadataViewableLength) {
214 metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
215 metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
217 cell.textLabel.text = metadataKeyCellText;
218 cell.detailTextLabel.text = metadataValueCellText;
220 } else if (indexPath.section == policySection) {
221 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
222 if (indexPath.row == 0) {
223 cell.textLabel.text = @"Versioning";
224 cell.detailTextLabel.text = container.versioning;
225 cell.accessoryType = UITableViewCellAccessoryNone;
226 cell.selectionStyle = UITableViewCellSelectionStyleNone;
228 else if (indexPath.row == 1) {
229 cell.textLabel.text = @"Quota";
230 cell.detailTextLabel.text = [NSString stringWithFormat:@"%u", container.quota];
231 cell.accessoryType = UITableViewCellAccessoryNone;
232 cell.selectionStyle = UITableViewCellSelectionStyleNone;
235 cell.textLabel.text = @"Edit policy";
236 cell.detailTextLabel.text = @"";
237 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
245 #pragma mark Table view delegate
247 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
249 if (indexPath.section == kMetadata) {
250 EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
251 NSString *metadataKey;
252 NSString *metadataValue;
254 if (indexPath.row == [self.container.metadata count]) {
257 vc.removeMetadataEnabled = FALSE;
258 vc.navigationItem.title = @"Add Metadata";
261 metadataKey = [[self.container.metadata allKeys] objectAtIndex:indexPath.row];
262 metadataValue = [self.container.metadata objectForKey:metadataKey];
263 vc.removeMetadataEnabled = TRUE;
264 vc.navigationItem.title = @"Edit Metadata";
267 StorageObject *object = [[[StorageObject alloc] init] autorelease];
268 object.name = container.name;
269 object.metadata = container.metadata;
270 object.fullPath = @"";
272 vc.metadataKey = metadataKey;
273 vc.metadataValue = metadataValue;
274 vc.account = account;
275 vc.container = container;
277 vc.objectIsContainer = YES;
279 [self.navigationController pushViewController:vc animated:YES];
281 } else if (indexPath.section == policySection) {
282 if (indexPath.row == 2) {
283 EditPolicyViewController *vc = [[EditPolicyViewController alloc] initWithNibName:@"EditPolicyViewController" bundle:nil];
285 vc.account = account;
286 vc.container = container;
287 vc.containerDetailViewController = self;
289 [self.navigationController pushViewController:vc animated:YES];
292 } else if (indexPath.section == deleteSection) {
293 if (self.container.count == 0 || !self.container.rootFolder.objectsAndFoldersCount) {
294 UIActionSheet *deleteActionSheet = [[[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this container? This operation cannot be undone."
296 cancelButtonTitle:@"Cancel"
297 destructiveButtonTitle:@"Delete Container"
298 otherButtonTitles:nil] autorelease];
299 [deleteActionSheet showInView:self.view];
305 #pragma mark Action Sheet
307 - (void)deleteContainerRow {
308 if ([self.account.containers count] == 0) {
309 [self.containersViewController.tableView reloadData];
311 [self.containersViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedContainerIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
313 [self.rootFolderViewController.navigationController popViewControllerAnimated:YES];
316 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
317 if (buttonIndex == 0) {
319 NSString *activityMessage = @"Deleting container...";
320 [activityIndicatorView release];
321 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
322 [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];
324 [[self.account.manager deleteContainer:self.container] success:^(OpenStackRequest *request) {
326 [activityIndicatorView removeFromSuperview];
327 [self.account.containers removeObjectForKey:self.container.name];
328 [self.account persist];
329 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
330 [self.containersViewController.navigationController popViewControllerAnimated:YES];
332 [self.navigationController popViewControllerAnimated:YES];
334 [self deleteContainerRow];
337 } failure:^(OpenStackRequest *request) {
339 // 404 Not Found means it's not there, so we can show the user that it's deleted
340 if ([request responseStatusCode] == 404) {
341 [self.account.containers removeObjectForKey:container.name];
342 [self.account persist];
345 [activityIndicatorView removeFromSuperview];
346 [self alert:@"There was a problem deleting this container." request:request];
351 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:deleteSection] animated:YES];
356 #pragma mark Memory management
361 [containersViewController release];
362 [selectedContainerIndexPath release];
363 [rootFolderViewController release];
364 [activityIndicatorView release];
369 #pragma mark Helper functions
371 - (void)reloadMetadataSection {
372 NSString *activityMessage = @"Loading metadata...";
373 [activityIndicatorView release];
374 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
375 [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];
376 [[self.account.manager getContainerInfo:container]
377 success:^(OpenStackRequest *request) {
378 [activityIndicatorView removeFromSuperview];
379 container.metadata = [NSMutableDictionary dictionary];
380 for (NSString *header in request.responseHeaders) {
381 NSString *metadataKey;
382 NSString *metadataValue;
383 if ([header rangeOfString:@"X-Container-Meta-"].location != NSNotFound) {
384 metadataKey = [NSString decodeFromPercentEscape:[header substringFromIndex:17]];
385 metadataValue = [NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:header]];
386 [container.metadata setObject:metadataValue forKey:metadataKey];
389 NSIndexSet *metadataSections = [NSIndexSet indexSetWithIndex:kMetadata];
390 [self.tableView reloadSections:metadataSections withRowAnimation:UITableViewRowAnimationFade];
392 failure:^(OpenStackRequest *request) {
393 [activityIndicatorView removeFromSuperview];
394 [self alert:@"There was a problem retrieving the container's metadata." request:request];