Applied relevant changes from latest rackspace-ios commits. Using llvm instead of...
[pithos-ios] / Classes / ContainerDetailViewController.m
1 //
2 //  ContainerDetailViewController.m
3 //  OpenStack
4 //
5 //  Created by Mike Mayo on 12/22/10.
6 //  The OpenStack project is provided under the Apache 2.0 license.
7 //
8
9 #import "ContainerDetailViewController.h"
10 #import "OpenStackAccount.h"
11 #import "Container.h"
12 #import "ActivityIndicatorView.h"
13 #import "AccountManager.h"
14 #import "UIViewController+Conveniences.h"
15 #import "UIColor+MoreColors.h"
16 #import "ReferrerACLViewController.h"
17 #import "UserAgentACLViewController.h"
18 #import "ContainersViewController.h"
19 #import "EditMetadataViewController.h"
20 #import "EditPolicyViewController.h"
21 #import "Folder.h"
22 #import "FolderViewController.h"
23 #import "OpenStackRequest.h"
24 #import "APICallback.h"
25
26 #define kOverview 0
27 #define kMetadata 1
28 #define kCDNAccess 3
29 #define kCDNAttributes 4
30 #define kLogRetention 5
31 #define kReferrerACL 6
32 #define kUserAgentACL 7
33
34 #define maxMetadataViewableLength 12
35
36
37 /* 
38  Name
39  Size
40  
41  Publish to CDN [On/Off]
42  @"Containers published to the CDN will be viewable to everyone.  If you disable CDN access, container contents will not be purged from the CDN until the TTL expires."
43  
44  CDN URL
45  TTL
46  
47  Log Retention [On/Off]
48  @"When Log Retention is enabled, CDN access logs will be stored in the .CDN_ACCESS_LOGS container in your account."
49  
50  Referrer ACL
51  @"The Referrer ACL is a Perl Compatible Regular Expression (PCRE) that must match the referrer in order for container contents to be served.  This is a useful mechanism to prevent other content providers from hot-linking your CDN content."
52  
53  User Agent ACL
54  @"The User Agent ACL is a PCRE that must match the user agent that your users are using to access the CDN content." 
55  
56  */
57
58 @implementation ContainerDetailViewController
59
60 @synthesize account, container, containersViewController, selectedContainerIndexPath, rootFolderViewController;
61
62 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
63     return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
64 }
65
66 - (void)setBackgroundView {
67     if (!self.container) {
68         UIView *viewContainer = [[UIView alloc] init];
69         viewContainer.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
70         viewContainer.backgroundColor = [UIColor iPadTableBackgroundColor];
71         
72         UILabel *label = [[UILabel alloc] init];
73         label.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
74         label.backgroundColor = [UIColor clearColor];
75         label.textColor = [UIColor emptyCollectionGrayColor];
76         label.font = [UIFont boldSystemFontOfSize:18.0];
77         label.text = @"No container selected";
78         label.textAlignment = UITextAlignmentCenter;
79         [viewContainer addSubview:label];
80         [label release];
81         
82         self.tableView.backgroundView = viewContainer;
83         [viewContainer release];
84     }
85 }
86
87 #pragma mark -
88 #pragma mark View lifecycle
89
90 - (void)viewDidLoad {
91     [super viewDidLoad];
92     cdnEnabledSwitch = [[UISwitch alloc] init];
93     cdnEnabledSwitch.on = container.cdnEnabled;
94     [cdnEnabledSwitch addTarget:self action:@selector(cdnEnabledSwitchChanged:) forControlEvents:UIControlEventValueChanged];
95     
96     logRetentionSwitch = [[UISwitch alloc] init];
97     logRetentionSwitch.on = container.logRetention;
98     [logRetentionSwitch addTarget:self action:@selector(logRetentionSwitchChanged:) forControlEvents:UIControlEventValueChanged];
99
100     ttlSlider = [[UISlider alloc] init];
101     cdnURLActionSheet = [[UIActionSheet alloc] initWithTitle:container.cdnURL delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Copy to Pasteboard", nil];
102     deleteActionSheet = [[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this container?  This operation cannot be undone." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete Container" otherButtonTitles:nil];
103     
104     if (!account.sharingAccount) {
105         deleteSection = container.cdnEnabled ? 8 : 3;
106         policySection = container.cdnEnabled ? 7 : 2;
107     } else {
108         deleteSection = -1;
109         policySection = -1;
110     }
111 }
112
113 - (void)viewWillAppear:(BOOL)animated {
114     [super viewWillAppear:animated];
115     if (container) {
116         self.navigationItem.title = container.name;
117     } 
118     originalTTL = container.ttl;
119     [self setBackgroundView];
120     [self.tableView reloadData];
121 }
122
123 - (void)viewWillDisappear:(BOOL)animated {
124     [super viewWillDisappear:animated];
125 }
126
127 #pragma mark -
128 #pragma mark TTL
129
130 - (float)ttlToPercentage {
131     return self.container.ttl / 259200.0;
132 }
133
134 - (NSInteger)ttlFromPercentage:(float)percentage {
135     return percentage * 259200;
136 }
137
138 - (NSString *)ttlToHours {
139     NSString *result = [NSString stringWithFormat:@"%.0f hours", self.container.ttl / 3600.0];
140     if ([result isEqualToString:@"1 hours"]) {
141         result = @"1 hour";
142     }
143     return result;
144 }
145
146 - (void)ttlSliderFinished:(id)sender {
147     NSString *activityMessage = @"Updating TTL...";
148     activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
149     [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];
150     
151     [self.account.manager updateCDNContainer:self.container];
152     
153     successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"updateCDNContainerSucceeded" object:self.container
154                                                                          queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
155        {
156            [activityIndicatorView removeFromSuperviewAndRelease];
157            [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
158        }];
159     
160     failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"updateCDNContainerFailed" object:self.container
161                                                                          queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
162        {
163            [activityIndicatorView removeFromSuperviewAndRelease];
164            container.ttl = originalTTL;
165            [self.tableView reloadData];
166            [self alert:@"There was a problem updating this container." request:[notification.userInfo objectForKey:@"request"]];
167            [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
168        }];
169 }
170
171 - (void)ttlSliderMoved:(id)sender {
172         CGFloat newTTL = ttlSlider.value;
173     self.container.ttl = [self ttlFromPercentage:newTTL];
174     ttlLabel.text = [self ttlToHours];
175 }
176
177
178 #pragma mark -
179 #pragma mark Table view data source
180
181 - (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font {
182     CGSize textLabelSize;
183     
184     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
185         textLabelSize = CGSizeMake(577.0, 9000.0f);
186     } else {
187         textLabelSize = CGSizeMake(260.0, 9000.0f);
188     }
189     // pad \n\n to fix layout bug
190     CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
191     return stringSize.height;
192 }
193
194 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
195     if (indexPath.section == deleteSection) {
196         return tableView.rowHeight;
197     } else if (indexPath.section == kCDNAttributes) {
198         if (indexPath.row == 0) { // URL
199             return tableView.rowHeight;
200             //return 22.0 + [self findLabelHeight:container.cdnURL font:[UIFont systemFontOfSize:18.0]];
201         } else { // if (indexPath.row == 1) {
202             return tableView.rowHeight + ttlSlider.frame.size.height + 3.0;
203         }
204     } else {
205         return tableView.rowHeight;
206     }
207 }
208
209 - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
210     if (section == deleteSection) {
211         return @"Only empty containers can be deleted.";
212     } else if (section == kCDNAccess) {
213         //return @"CDN allows you to access files in this container via the public internet. Transfer charges apply.";
214         return @"CDN allows you to access files via the public internet. Transfer charges apply.";
215     } else if (section == kLogRetention) {
216         return @"When enabled, access logs will be stored in the .CDN_ACCESS_LOGS container.";
217     } else if (section == kReferrerACL) {
218         return @"The Referrer ACL is a Perl Compatible Regular Expression that must match the referrer for all content requests.";
219     } else if (section == kUserAgentACL) {
220         return @"The User Agent ACL is a Perl Compatible Regular Expression that must match the user agent for all content requests.";
221     } else {
222         return @"";
223     }
224 }
225
226 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
227     if (self.container) {
228         int numberOfSections;
229         if (transitioning) {
230             numberOfSections = container.cdnEnabled ? 4 : 9;
231         } else {
232             numberOfSections = container.cdnEnabled ? 9 : 4;
233         }
234         if (account.sharingAccount)
235             return numberOfSections - 2;
236         else
237             return numberOfSections;
238     } else {
239         return 0;
240     }
241 }
242
243 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
244     // Return the number of rows in the section.
245     if (section == kOverview) {
246         return 2;
247     } else if (section == kMetadata) {
248         if (account.sharingAccount)
249             return [container.metadata count];
250         else
251             return 1 + [container.metadata count];     
252     } else if (section == policySection) {
253         if (account.sharingAccount)
254             return 2;
255         else
256             return 3;
257     } else {
258         return 1;
259     }
260 }
261
262 - (UITableViewCell *)tableView:(UITableView *)tableView ttlCellForRowAtIndexPath:(NSIndexPath *)indexPath {
263     static NSString *CellIdentifier = @"TTLCell";
264     
265     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
266     if (cell == nil) {
267         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
268         cell.selectionStyle = UITableViewCellSelectionStyleNone;
269         
270         UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 13.0, 280.0, 20.0)];
271         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
272             textLabel.frame = CGRectMake(54.0, 13.0, 458.0, 20.0);
273         }
274         textLabel.font = [UIFont boldSystemFontOfSize:17.0];
275         textLabel.text = @"TTL";
276         textLabel.textColor = [UIColor blackColor];
277         textLabel.backgroundColor = [UIColor clearColor];
278         [cell addSubview:textLabel];
279         [textLabel release];
280         
281         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
282             ttlSlider.frame = CGRectMake(54.0, 38.0, 596.0, ttlSlider.frame.size.height);
283         } else {
284             ttlSlider.frame = CGRectMake(20.0, 38.0, 280.0, ttlSlider.frame.size.height);
285         }
286         [ttlSlider addTarget:self action:@selector(ttlSliderMoved:) forControlEvents:UIControlEventValueChanged];
287         [ttlSlider addTarget:self action:@selector(ttlSliderFinished:) forControlEvents:UIControlEventTouchUpInside];
288         
289         [cell addSubview:ttlSlider];
290         ttlLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 14.0, 280.0, 18.0)];
291         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
292             ttlLabel.frame = CGRectMake(54.0, 14.0, 596.0, 18.0);
293         }
294         ttlLabel.font = [UIFont systemFontOfSize:17.0];
295         ttlLabel.textColor = [UIColor value1DetailTextLabelColor];
296         ttlLabel.backgroundColor = [UIColor clearColor];
297         ttlLabel.textAlignment = UITextAlignmentRight;
298         [cell addSubview:ttlLabel];
299     }
300     
301     ttlLabel.text = [self ttlToHours];
302     ttlSlider.value = [self ttlToPercentage];
303     
304     return cell;
305 }
306
307 - (UITableViewCell *)tableView:(UITableView *)tableView deleteCellForRowAtIndexPath:(NSIndexPath *)indexPath {
308     static NSString *CellIdentifier = @"DeleteCell";
309     
310     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
311     if (cell == nil) {
312         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
313         cell.textLabel.textAlignment = UITextAlignmentCenter;
314         cell.textLabel.text = @"Delete Container";
315     }
316     
317     if (self.container.rootFolder) {
318         if (self.container.count == 0 || ([self.container.rootFolder.folders count] + [self.container.rootFolder.objects count] == 0)) {
319             cell.textLabel.textColor = [UIColor blackColor];
320             cell.selectionStyle = UITableViewCellSelectionStyleBlue;
321         } else {
322             cell.textLabel.textColor = [UIColor grayColor];
323             cell.selectionStyle = UITableViewCellSelectionStyleNone;
324             cell.userInteractionEnabled = NO;
325         }
326     } else {
327         if (self.container.count == 0) {
328             cell.textLabel.textColor = [UIColor blackColor];
329             cell.selectionStyle = UITableViewCellSelectionStyleBlue;
330         } else {
331             cell.textLabel.textColor = [UIColor grayColor];
332             cell.selectionStyle = UITableViewCellSelectionStyleNone;
333             cell.userInteractionEnabled = NO;
334         }
335     }
336     
337     return cell;
338 }
339
340 // Customize the appearance of table view cells.
341 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
342     
343     static NSString *CellIdentifier = @"Cell";
344     
345     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
346     if (cell == nil) {
347         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
348         cell.selectionStyle = UITableViewCellSelectionStyleNone;
349         cell.textLabel.backgroundColor = [UIColor clearColor];
350         cell.detailTextLabel.backgroundColor = [UIColor clearColor];
351         cell.detailTextLabel.numberOfLines = 0;
352         cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
353         cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
354     }
355     
356     cell.accessoryType = UITableViewCellAccessoryNone;
357     cell.selectionStyle = UITableViewCellSelectionStyleNone;
358     
359     if (indexPath.section == kOverview) {
360         cell.accessoryView = nil;
361         if (indexPath.row == 0) {
362             cell.textLabel.text = @"Name";
363             cell.detailTextLabel.text = container.name;
364         } else if (indexPath.row == 1) {
365             cell.textLabel.text = @"Size";
366             cell.detailTextLabel.text = [container humanizedSize];
367         }
368     } else if (indexPath.section == kMetadata) {
369         if (account.sharingAccount) {
370             cell.accessoryType = UITableViewCellAccessoryNone;
371             cell.selectionStyle = UITableViewCellSelectionStyleNone;
372             cell.userInteractionEnabled = NO;
373         }
374         else {
375             cell.selectionStyle = UITableViewCellSelectionStyleBlue;
376             cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
377         }
378         if (indexPath.row == [container.metadata count]) {
379             cell.textLabel.text = @"Add Metadata";
380             cell.detailTextLabel.text = @"";
381         } else {
382             NSString *key = [[container.metadata allKeys] objectAtIndex:indexPath.row];
383             NSString *value = [container.metadata objectForKey:key];
384             NSString *metadataKeyCellText = key;
385             NSString *metadataValueCellText = value;
386             if ([metadataKeyCellText length] > maxMetadataViewableLength) {
387                 metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
388                 metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
389             }
390             if ([metadataValueCellText length] > maxMetadataViewableLength) {
391                 metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
392                 metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
393             }
394             cell.textLabel.text = metadataKeyCellText;
395             cell.detailTextLabel.text = metadataValueCellText;
396         }
397     } else if (indexPath.section == policySection) {
398         cell.selectionStyle = UITableViewCellSelectionStyleBlue;
399         if (indexPath.row == 0) {
400             cell.textLabel.text = @"Versioning";
401             cell.detailTextLabel.text = container.versioning;
402             cell.accessoryType = UITableViewCellAccessoryNone;
403             cell.selectionStyle = UITableViewCellSelectionStyleNone;
404         }
405         else if (indexPath.row == 1) {
406             cell.textLabel.text = @"Quota";
407             cell.detailTextLabel.text = [NSString stringWithFormat:@"%u", container.quota];
408             cell.accessoryType = UITableViewCellAccessoryNone;
409             cell.selectionStyle = UITableViewCellSelectionStyleNone;
410         }
411         else {
412             cell.textLabel.text = @"Edit policy";
413             cell.detailTextLabel.text = @"";
414             cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
415         }
416     } else if (indexPath.section == deleteSection) {
417         return [self tableView:tableView deleteCellForRowAtIndexPath:indexPath];
418     } else if (indexPath.section == kLogRetention) {
419         cell.textLabel.text = @"Log Retention";
420         cell.detailTextLabel.text = @"";
421         cell.accessoryView = logRetentionSwitch;
422         cell.accessoryType = UITableViewCellAccessoryNone;
423         cell.selectionStyle = UITableViewCellSelectionStyleNone;
424     } else if (indexPath.section == kReferrerACL) {
425         cell.textLabel.text = @"Referrer ACL";
426         cell.detailTextLabel.text = container.referrerACL;
427         cell.accessoryView = nil;
428         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
429         cell.selectionStyle = UITableViewCellSelectionStyleBlue;
430         cell.detailTextLabel.numberOfLines = 1;
431     } else if (indexPath.section == kUserAgentACL) {
432         cell.textLabel.text = @"User Agent ACL";
433         cell.detailTextLabel.text = container.useragentACL;
434         cell.accessoryView = nil;
435         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
436         cell.selectionStyle = UITableViewCellSelectionStyleBlue;
437         cell.detailTextLabel.numberOfLines = 1;
438     }
439     
440     return cell;
441 }
442
443 #pragma mark -
444 #pragma mark Table view delegate
445
446 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
447     
448     if (indexPath.section == kMetadata) {
449         EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
450         NSString *metadataKey;
451         NSString *metadataValue;
452         
453         if (indexPath.row == [self.container.metadata count]) {
454             metadataKey = @"";
455             metadataValue = @"";
456             vc.removeMetadataEnabled = FALSE;
457             vc.navigationItem.title = @"Add Metadata";
458         }
459         else {
460             metadataKey = [[self.container.metadata allKeys] objectAtIndex:indexPath.row];
461             metadataValue = [self.container.metadata objectForKey:metadataKey];
462             vc.removeMetadataEnabled = TRUE;
463             vc.navigationItem.title = @"Edit Metadata";
464         }
465         
466         StorageObject *object = [[[StorageObject alloc] init] autorelease];
467         object.name = container.name;
468         object.metadata = container.metadata;
469         object.fullPath = @"";
470         
471         vc.metadataKey = metadataKey;
472         vc.metadataValue = metadataValue;
473         vc.account = account;
474         vc.container = container;
475         vc.object = object;
476         vc.objectIsContainer = YES;
477         
478         [self.navigationController pushViewController:vc animated:YES];
479         [vc release];
480     } else if (indexPath.section == policySection) {
481         if (indexPath.row == 2) {
482             EditPolicyViewController *vc = [[EditPolicyViewController alloc] initWithNibName:@"EditPolicyViewController" bundle:nil];
483     
484             vc.account = account;
485             vc.container = container;
486             vc.containerDetailViewController = self;
487         
488             [self.navigationController pushViewController:vc animated:YES];
489             [vc release];
490         }
491     } else if (indexPath.section == deleteSection) {
492         if (self.container.count == 0 || ([self.container.rootFolder.folders count] + [self.container.rootFolder.objects count] == 0)) {
493             [deleteActionSheet showInView:self.view];
494         }
495     } else if (indexPath.section == kCDNAttributes && indexPath.row == 0) {
496         [cdnURLActionSheet showInView:self.view];
497     } else if (indexPath.section == kReferrerACL) {
498         ReferrerACLViewController *vc = [[ReferrerACLViewController alloc] initWithNibName:@"ReferrerACLViewController" bundle:nil];
499         vc.account = self.account;
500         vc.container = self.container;
501         vc.containerDetailViewController = self;
502         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
503             vc.modalPresentationStyle = UIModalPresentationFormSheet;
504             [self presentModalViewControllerWithNavigation:vc animated:YES];
505         } else {
506             [self.navigationController pushViewController:vc animated:YES];
507         }        
508         [vc release];
509     } else if (indexPath.section == kUserAgentACL) {
510         UserAgentACLViewController *vc = [[UserAgentACLViewController alloc] initWithNibName:@"UserAgentACLViewController" bundle:nil];
511         vc.account = self.account;
512         vc.container = self.container;
513         vc.containerDetailViewController = self;
514         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
515             vc.modalPresentationStyle = UIModalPresentationFormSheet;
516             [self presentModalViewControllerWithNavigation:vc animated:YES];
517         } else {
518             [self.navigationController pushViewController:vc animated:YES];
519         }        
520         [vc release];
521     }
522 }
523
524 #pragma mark -
525 #pragma mark Switches
526
527 - (void)cdnEnabledSwitchChanged:(id)sender {
528     
529     NSString *activityMessage = @"Disabling CDN Access...";
530     if (!container.cdnEnabled) {
531         activityMessage = @"Enabling CDN Access...";
532     }
533     activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
534     [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];    
535     container.cdnEnabled = !container.cdnEnabled;
536     [self.account.manager updateCDNContainer:container];
537     
538     successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"updateCDNContainerSucceeded" object:self.container
539                                                                          queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
540     {
541         [activityIndicatorView removeFromSuperviewAndRelease];
542         
543         NSIndexSet *sections = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(3, 4)];
544         NSInteger oldDeleteSection = deleteSection;
545         if (container.cdnEnabled) {
546             deleteSection = 6;
547             transitioning = YES;
548             [self.tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(oldDeleteSection, 1)] withRowAnimation:UITableViewRowAnimationBottom];
549             transitioning = NO;
550             [self.tableView insertSections:sections withRowAnimation:UITableViewRowAnimationBottom];
551         } else {
552             deleteSection = 2;
553             transitioning = YES;
554             [self.tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(deleteSection, 1)] withRowAnimation:UITableViewRowAnimationTop];
555             transitioning = NO;
556             [self.tableView deleteSections:sections withRowAnimation:UITableViewRowAnimationTop];
557         }
558         
559         [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
560     }];
561     
562     failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"updateCDNContainerFailed" object:self.container
563                                                                          queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
564     {
565         [activityIndicatorView removeFromSuperviewAndRelease];
566         container.cdnEnabled = !container.cdnEnabled;
567         cdnEnabledSwitch.on = !cdnEnabledSwitch.on;
568         [self alert:@"There was a problem updating this container." request:[notification.userInfo objectForKey:@"request"]];           
569         [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
570     }];
571 }
572
573 - (void)logRetentionSwitchChanged:(id)sender {
574
575     NSString *activityMessage = @"Disabling Log Retention...";
576     if (!container.logRetention) {
577         activityMessage = @"Enabling Log Retention...";
578     }
579     activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
580     [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];    
581
582     container.logRetention = !container.logRetention;
583     [self.account.manager updateCDNContainer:container];
584     
585     successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"updateCDNContainerSucceeded" object:self.container
586                                                                          queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
587        {
588            [activityIndicatorView removeFromSuperviewAndRelease];
589            [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
590        }];
591     
592     failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"updateCDNContainerFailed" object:self.container
593                                                                          queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
594        {
595            [activityIndicatorView removeFromSuperviewAndRelease];
596            container.logRetention = !container.logRetention;
597            logRetentionSwitch.on = !logRetentionSwitch.on;
598            [self alert:@"There was a problem updating this container." request:[notification.userInfo objectForKey:@"request"]];
599            [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
600        }];
601 }
602
603 #pragma mark -
604 #pragma mark Action Sheet
605
606 - (void)deleteContainerRow {
607     if ([self.account.containers count] == 0) {
608         [self.containersViewController.tableView reloadData];
609     } else {
610         [self.containersViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedContainerIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
611     }
612     [self.rootFolderViewController.navigationController popViewControllerAnimated:YES];
613 }
614
615 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
616     if ([actionSheet isEqual:cdnURLActionSheet]) {
617         if (buttonIndex == 0) {
618             UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
619             [pasteboard setString:container.cdnURL];
620         }
621         [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:kCDNAttributes] animated:YES];
622     } else if ([actionSheet isEqual:deleteActionSheet]) {
623         if (buttonIndex == 0) {
624          
625             NSString *activityMessage = @"Deleting container...";
626             activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
627             [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];  
628             
629             [[self.account.manager deleteContainer:self.container] success:^(OpenStackRequest *request) {
630                 
631                 [activityIndicatorView removeFromSuperviewAndRelease];                
632                 [self.account.containers removeObjectForKey:self.container.name];
633                 [self.account persist];
634                 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
635                     [self.containersViewController.navigationController popViewControllerAnimated:YES];
636                 else
637                     [self.navigationController popViewControllerAnimated:YES];
638                 
639                 [self deleteContainerRow];
640
641                 
642             } failure:^(OpenStackRequest *request) {
643                 
644                 // 404 Not Found means it's not there, so we can show the user that it's deleted
645                 if ([request responseStatusCode] == 404) {
646                     [self.account.containers removeObjectForKey:container.name];
647                     [self.account persist];
648                     
649                 } else {
650                     [activityIndicatorView removeFromSuperviewAndRelease];
651                     [self alert:@"There was a problem deleting this container." request:request];
652                 }
653             }];
654                         
655         }
656         [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:deleteSection] animated:YES];
657     }
658 }
659
660
661 #pragma mark -
662 #pragma mark Memory management
663
664 - (void)dealloc {
665     [account release];
666     [container release];
667     [cdnEnabledSwitch release];
668     [ttlSlider release];
669     [cdnURLActionSheet release];
670     [deleteActionSheet release];
671     [containersViewController release];
672     [selectedContainerIndexPath release];
673     [rootFolderViewController release];
674     [super dealloc];
675 }
676
677 @end
678