Added metadata functionality.
authorMiltiadis Vasilakis <mvasilak@gmail.com>
Wed, 9 Nov 2011 18:33:56 +0000 (20:33 +0200)
committerMiltiadis Vasilakis <mvasilak@gmail.com>
Wed, 9 Nov 2011 18:33:56 +0000 (20:33 +0200)
17 files changed:
Classes/AccountManager.m
Classes/Container.h
Classes/Container.m
Classes/ContainerDetailViewController.m
Classes/EditMetadataViewController.h [new file with mode: 0644]
Classes/EditMetadataViewController.m [new file with mode: 0644]
Classes/EditMetadataViewController.xib [new file with mode: 0644]
Classes/Folder.h
Classes/Folder.m
Classes/FolderDetailViewController.h [new file with mode: 0644]
Classes/FolderDetailViewController.m [new file with mode: 0644]
Classes/FolderDetailViewController.xib [new file with mode: 0644]
Classes/FolderViewController.m
Classes/OpenStackRequest.m
Classes/StorageObject.m
Classes/StorageObjectViewController.m
OpenStack.xcodeproj/project.pbxproj

index 16626a7..5c3e2ea 100755 (executable)
 
 - (void)writeObjectMetadataSucceeded:(OpenStackRequest *)request {
     if ([request isSuccess]) {
+        NSNotification *notification = [NSNotification notificationWithName:@"writeObjectMetadataSucceeded" object:[request.userInfo objectForKey:@"object"] userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
+        [[NSNotificationCenter defaultCenter] postNotification:notification];
     } else {
-        NSNotification *notification = [NSNotification notificationWithName:@"getObjectsFailed" object:self.account userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
+        NSNotification *notification = [NSNotification notificationWithName:@"writeObjectMetadataFailed" object:[request.userInfo objectForKey:@"object"] userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
         [[NSNotificationCenter defaultCenter] postNotification:notification];
     }
 }
 
 - (void)writeObjectMetadataFailed:(OpenStackRequest *)request {
-    NSNotification *notification = [NSNotification notificationWithName:@"writeObjectMetadataFailed" object:self.account userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
+    NSNotification *notification = [NSNotification notificationWithName:@"writeObjectMetadataFailed" object:[request.userInfo objectForKey:@"object"] userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
     [[NSNotificationCenter defaultCenter] postNotification:notification];
 }
 
index 8cc3d3e..3b7a2c8 100755 (executable)
@@ -16,6 +16,9 @@
        NSString *name;
        NSUInteger count;
        unsigned long long bytes;
+    
+    // metadata
+    NSMutableDictionary *metadata;
 
     // TODO: wade having trouble with directories without markers: user_files/12345/wade.jpg
        
@@ -40,6 +43,7 @@
 @property (nonatomic, retain) NSString *name;
 @property (nonatomic, assign) NSUInteger count;
 @property (nonatomic, assign) unsigned long long bytes;
+@property (nonatomic, retain) NSMutableDictionary *metadata;
 
 -(NSString *)humanizedSize;
 
index f8a4121..46f37d3 100755 (executable)
@@ -17,6 +17,7 @@
 
 // regular container attributes
 @synthesize name, count, bytes;
+@synthesize metadata;
 
 // CDN container attributes
 @synthesize cdnEnabled, ttl, cdnURL, logRetention, referrerACL, useragentACL, rootFolder;
     // regular attributes
     container.name = [dict objectForKey:@"name"];
     
+    // metadata
+    container.metadata = [NSMutableDictionary dictionary];
+    for (NSString *key in dict) {
+        if ([key hasPrefix:@"x_container_meta_"]) {
+            NSString *metadataKey = [key substringFromIndex:17];
+            [container.metadata setObject:[dict objectForKey:key] forKey:metadataKey];
+        }
+    }
+
     // if count is in here, we're parsing from the object storage API
     if ([dict objectForKey:@"count"]) {
         container.count = [[dict objectForKey:@"count"] intValue];
index ef89f29..7877821 100755 (executable)
 #import "ReferrerACLViewController.h"
 #import "UserAgentACLViewController.h"
 #import "ContainersViewController.h"
+#import "EditMetadataViewController.h"
 #import "Folder.h"
 
 #define kOverview 0
-#define kCDNAccess 1
-#define kCDNAttributes 2
-#define kLogRetention 3
-#define kReferrerACL 4
-#define kUserAgentACL 5
-
-/*
- #define kOverview 0
- #define kCDNAccess 1
- #define kCDNAttributes 2
- #define kLogRetention 3
- #define kReferrerACL 4
- #define kUserAgentACL 5
+#define kMetadata 1
+#define kCDNAccess 2
+#define kCDNAttributes 3
+#define kLogRetention 4
+#define kReferrerACL 5
+#define kUserAgentACL 6
+
+#define maxMetadataViewableLength 12
+
+
+/* 
  Name
  Size
  
@@ -98,7 +97,7 @@
     cdnURLActionSheet = [[UIActionSheet alloc] initWithTitle:container.cdnURL delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Copy to Pasteboard", nil];
     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];
     
-    deleteSection = container.cdnEnabled ? 6 : 2;
+    deleteSection = container.cdnEnabled ? 7 : 2;
 }
 
 - (void)viewWillAppear:(BOOL)animated {
     }
     originalTTL = container.ttl;
     [self setBackgroundView];
+    [self.tableView reloadData];
 }
 
 #pragma mark -
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
     if (self.container) {
         if (transitioning) {
-            return container.cdnEnabled ? 3 : 7;
+            return container.cdnEnabled ? 3 : 8;
         } else {
-            return container.cdnEnabled ? 7 : 3;
+            return container.cdnEnabled ? 8 : 3;
         }
     } else {
         return 0;
 
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
     // Return the number of rows in the section.
-    if (section == deleteSection) {
-        return 1;
-    } else if (section == kOverview) {
-        return 2;
-    } else if (section == kCDNAccess) {
-        return 1;
-    } else if (section == kCDNAttributes) {
+    if (section == kOverview) {
         return 2;
+    } else if (section == kMetadata) {
+        return 1 + [container.metadata count];
     } else {
         return 1;
     }
             cell.textLabel.text = @"Size";
             cell.detailTextLabel.text = [container humanizedSize];
         }
-    } else if (indexPath.section == kCDNAccess) {
-        cell.textLabel.text = @"Publish to CDN";
-        cell.detailTextLabel.text = @"";
-        cell.accessoryView = cdnEnabledSwitch;
-        cell.selectionStyle = UITableViewCellSelectionStyleNone;
-    } else if (indexPath.section == deleteSection) {
-        return [self tableView:tableView deleteCellForRowAtIndexPath:indexPath];
-    } else if (indexPath.section == kCDNAttributes) {
-        if (indexPath.row == 0) {
-            cell.textLabel.text = @"Public URL";
-            //cell.detailTextLabel.text = container.cdnURL;
+    } else if (indexPath.section == kMetadata) {
+        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+        cell.accessoryView = nil;
+        if (indexPath.row == [container.metadata count]) {
+            cell.textLabel.text = @"Add Metadata";
             cell.detailTextLabel.text = @"";
-            cell.accessoryView = nil;
-            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
-            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
-            cell.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation;
-            cell.detailTextLabel.numberOfLines = 0;
-            //cell.detailTextLabel.adjustsFontSizeToFitWidth = YES;
-        } else if (indexPath.row == 1) {
-            return [self tableView:tableView ttlCellForRowAtIndexPath:indexPath];
+        } else {
+            NSString *key = [[container.metadata allKeys] objectAtIndex:indexPath.row];
+            NSString *value = [container.metadata objectForKey:key];
+            NSString *metadataKeyCellText = key;
+            NSString *metadataValueCellText = value;
+            if ([metadataKeyCellText length] > maxMetadataViewableLength) {
+                metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
+                metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
+            }
+            if ([metadataValueCellText length] > maxMetadataViewableLength) {
+                metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
+                metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
+            }
+            cell.textLabel.text = metadataKeyCellText;
+            cell.detailTextLabel.text = metadataValueCellText;
         }
+    } else if (indexPath.section == deleteSection) {
+        return [self tableView:tableView deleteCellForRowAtIndexPath:indexPath];
     } else if (indexPath.section == kLogRetention) {
         cell.textLabel.text = @"Log Retention";
         cell.detailTextLabel.text = @"";
 #pragma mark Table view delegate
 
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
-    if (indexPath.section == deleteSection) {
+    
+    if (indexPath.section == kMetadata) {
+        EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
+        NSString *metadataKey;
+        NSString *metadataValue;
+        
+        if (indexPath.row == [self.container.metadata count]) {
+            metadataKey = @"";
+            metadataValue = @"";
+            vc.deleteEnabled = FALSE;
+            vc.navigationItem.title = @"Add Metadata";
+        }
+        else {
+            metadataKey = [[self.container.metadata allKeys] objectAtIndex:indexPath.row];
+            metadataValue = [self.container.metadata objectForKey:metadataKey];
+            vc.deleteEnabled = TRUE;
+            vc.navigationItem.title = @"Edit Metadata";
+        }
+        
+        StorageObject *object = [[[StorageObject alloc] init] autorelease];
+        object.name = container.name;
+        object.metadata = container.metadata;
+        object.fullPath = @"";
+        
+        vc.metadataKey = metadataKey;
+        vc.metadataValue = metadataValue;
+        vc.account = account;
+        vc.container = container;
+        vc.object = object;
+        
+        [self.navigationController pushViewController:vc animated:YES];
+        [vc release];
+    } else if (indexPath.section == deleteSection) {
         if (self.container.count == 0 || ([self.container.rootFolder.folders count] + [self.container.rootFolder.objects count] == 0)) {
             [deleteActionSheet showInView:self.view];
         }
diff --git a/Classes/EditMetadataViewController.h b/Classes/EditMetadataViewController.h
new file mode 100644 (file)
index 0000000..b9960c3
--- /dev/null
@@ -0,0 +1,73 @@
+//
+//  EditMetadataViewController.h
+//  pithos-ios
+//
+// Copyright 2011 GRNET S.A. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or
+// without modification, are permitted provided that the following
+// conditions are met:
+// 
+//   1. Redistributions of source code must retain the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer in the documentation and/or other materials
+//      provided with the distribution.
+// 
+// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+// 
+// The views and conclusions contained in the software and
+// documentation are those of the authors and should not be
+// interpreted as representing official policies, either expressed
+// or implied, of GRNET S.A.
+
+#import <UIKit/UIKit.h>
+#import "OpenStackViewController.h"
+#import "OpenStackAccount.h"
+#import "Container.h"
+#import "Folder.h"
+#import "StorageObject.h"
+#import "ActivityIndicatorView.h"
+
+
+@interface EditMetadataViewController : UITableViewController <UITextFieldDelegate> {
+    
+    OpenStackAccount *account;
+    Container *container;
+    StorageObject *object;
+    
+    NSString *metadataKey;
+    NSString *metadataValue;
+    
+    ActivityIndicatorView *activityIndicatorView;
+    id successObserver;
+    id failureObserver;
+    
+    BOOL deleteEnabled;
+}
+
+@property (nonatomic, retain) OpenStackAccount *account;
+@property (nonatomic, retain) Container *container;
+@property (nonatomic, retain) StorageObject *object;
+
+@property (nonatomic, retain) NSString *metadataKey;
+@property (nonatomic, retain) NSString *metadataValue;
+
+@property (nonatomic) BOOL deleteEnabled;
+
+
+@end
diff --git a/Classes/EditMetadataViewController.m b/Classes/EditMetadataViewController.m
new file mode 100644 (file)
index 0000000..42af962
--- /dev/null
@@ -0,0 +1,302 @@
+//
+//  EditMetadataViewController.m
+//  pithos-ios
+//
+// Copyright 2011 GRNET S.A. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or
+// without modification, are permitted provided that the following
+// conditions are met:
+// 
+//   1. Redistributions of source code must retain the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer in the documentation and/or other materials
+//      provided with the distribution.
+// 
+// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+// 
+// The views and conclusions contained in the software and
+// documentation are those of the authors and should not be
+// interpreted as representing official policies, either expressed
+// or implied, of GRNET S.A.
+
+#import "EditMetadataViewController.h"
+#import "AccountManager.h"
+#import "UIViewController+Conveniences.h"
+
+
+
+@implementation EditMetadataViewController
+
+
+@synthesize container, account, object;
+
+@synthesize metadataKey;
+@synthesize metadataValue;
+@synthesize deleteEnabled;
+
+#define kMetadata 0 
+#define kSaveMedata 1
+#define kDeleteMetadata 2
+
+
+- (void)dealloc
+{
+    [account release];
+    [container release];
+    [object release];
+    [metadataKey release];
+    [metadataValue release];
+    [super dealloc];
+}
+
+- (void)didReceiveMemoryWarning
+{
+    // Releases the view if it doesn't have a superview.
+    [super didReceiveMemoryWarning];
+    
+    // Release any cached data, images, etc that aren't in use.
+}
+
+#pragma mark - View lifecycle
+
+- (void)viewDidLoad
+{
+    [super viewDidLoad];
+}
+
+- (void)viewDidUnload
+{
+    [super viewDidUnload];
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{    
+    [super viewWillAppear:animated];
+}
+
+- (void)viewDidAppear:(BOOL)animated
+{
+    [super viewDidAppear:animated];
+}
+
+- (void)viewWillDisappear:(BOOL)animated
+{
+    [super viewWillDisappear:animated];
+}
+
+- (void)viewDidDisappear:(BOOL)animated
+{
+    [super viewDidDisappear:animated];
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
+    return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
+}
+
+#pragma mark - Table view data source
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+    return (deleteEnabled)? 3:2;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+    if (section == kMetadata)
+        return 2;
+    else
+        return 1;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    static NSString *CellIdentifier = @"Cell";
+    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+    if (cell == nil) {
+        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+    }
+    CGRect bounds = [cell.contentView bounds];
+    CGRect rect = CGRectInset(bounds, 20.0, 10.0);                        
+    UITextField *textField = [[UITextField alloc] initWithFrame:rect];
+    [textField setFrame:rect];
+    [textField setClearButtonMode:UITextFieldViewModeWhileEditing];
+    [textField setBackgroundColor:[UIColor whiteColor]];
+    [textField setOpaque:YES];
+    [textField setAutocorrectionType:UITextAutocorrectionTypeNo];
+    [textField setAutocapitalizationType:UITextAutocapitalizationTypeNone];
+    [textField setDelegate:self];
+            
+    if (indexPath.section == kMetadata) {
+        if (indexPath.row == 0) {
+            [textField setReturnKeyType:UIReturnKeyNext];
+            textField.placeholder = @"Key";
+            textField.text = metadataKey;
+            textField.tag = 0;
+        }
+        else if (indexPath.row == 1) {
+            textField.placeholder = @"Value";
+            textField.text = metadataValue;
+            textField.tag = 1;
+        }
+            
+        cell.selectionStyle = UITableViewCellSelectionStyleNone;
+        [cell.contentView addSubview:textField];
+    }
+    else if (indexPath.section == kSaveMedata) 
+        cell.textLabel.text = @"Save";
+    else if (indexPath.section == kDeleteMetadata)
+        cell.textLabel.text = @"Delete";
+    
+    return cell;
+}
+
+
+#pragma mark - Table view delegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    NSString *activityMessage;
+    NSIndexPath *keyCellIndexPath;
+    NSIndexPath *valueCellIndexPath;
+    UITableViewCell *cell;
+    switch (indexPath.section) {
+        case kSaveMedata:
+            keyCellIndexPath = [NSIndexPath indexPathForRow:0 inSection:kMetadata];
+            cell = [self.tableView cellForRowAtIndexPath:keyCellIndexPath];
+            UITextField *textField = [[cell.contentView subviews] objectAtIndex:0];
+            NSString *newMetadataKey = textField.text;
+        
+            if ([newMetadataKey length] == 0
+                || [[newMetadataKey stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) {
+                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+                [self alert:@"Invalid input" message:@"Metadata key cannot be empty"];
+                return;
+            }
+            
+            valueCellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kMetadata];
+            cell = [self.tableView cellForRowAtIndexPath:valueCellIndexPath];
+            textField = [[cell.contentView subviews] objectAtIndex:0];
+            NSString *newMetadataValue = textField.text;
+            
+            activityMessage = @"Saving metadata";
+            
+            activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
+            [activityIndicatorView addToView:self.view];
+
+            [object.metadata removeObjectForKey:metadataKey];
+            [object.metadata setObject:newMetadataValue forKey:newMetadataKey];
+            [self.account.manager writeObjectMetadata:container object:object];
+            successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataSucceeded"
+                                                                                object:object
+                                                                                 queue:[NSOperationQueue mainQueue]
+                                                                            usingBlock:^(NSNotification *notification)
+                               {
+                                   self.metadataKey = newMetadataKey;
+                                   self.metadataValue = newMetadataValue;
+                                   
+                                   [activityIndicatorView removeFromSuperviewAndRelease];
+                                   [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+                                   [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
+                               }];
+            
+            failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataFailed" 
+                                                                                object:object 
+                                                                                 queue:[NSOperationQueue mainQueue] 
+                                                                            usingBlock:^(NSNotification *notification)
+                               {
+                                   [object.metadata removeObjectForKey:newMetadataKey];
+                                   [object.metadata setObject:metadataValue forKey:metadataKey];
+                                   [activityIndicatorView removeFromSuperviewAndRelease];
+                                   [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+                                   [self alert:@"There was a problem saving the metadata." request:[notification.userInfo objectForKey:@"request"]];
+                                   
+                                   [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
+                               }];
+            
+            break;
+        case kDeleteMetadata:
+            activityMessage = @"Deleting metadata";
+            activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
+            [activityIndicatorView addToView:self.view];
+
+            [object.metadata removeObjectForKey:metadataKey];
+            [self.account.manager writeObjectMetadata:container object:object];
+            successObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataSucceeded"
+                                                                                object:object
+                                                                                 queue:[NSOperationQueue mainQueue]
+                                                                            usingBlock:^(NSNotification *notification)
+                               {
+                                   [activityIndicatorView removeFromSuperviewAndRelease];
+                                   [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+                                   self.metadataKey = @"";
+                                   self.metadataValue = @"";
+
+                                   [self.tableView reloadData];
+                                   [[NSNotificationCenter defaultCenter] removeObserver:successObserver];
+                               }];
+            
+            failureObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"writeObjectMetadataFailed" 
+                                                                                object:object 
+                                                                                 queue:[NSOperationQueue mainQueue] 
+                                                                            usingBlock:^(NSNotification *notification)
+                               {
+                                   [object.metadata setObject:metadataValue forKey:metadataKey];
+                                   [activityIndicatorView removeFromSuperviewAndRelease];
+                                   [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+                                   [self alert:@"There was a problem saving the metadata." request:[notification.userInfo objectForKey:@"request"]];
+                                   
+                                   [[NSNotificationCenter defaultCenter] removeObserver:failureObserver];
+                               }];
+
+            break;
+    }
+}
+
+#pragma mark - Textfield delegate
+
+- (void)textFieldDidEndEditing:(UITextField *)textField
+{
+    if ([textField tag] == 0) {
+        if ([textField.text length] == 0
+            || [[textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) {
+            [self alert:@"Invalid input" message:@"Metadata key cannot be empty"];
+        }
+    }
+}
+
+
+
+- (BOOL)textFieldShouldReturn:(UITextField *)textField
+{
+    
+    if ([textField returnKeyType] == UIReturnKeyNext)
+    {
+        NSInteger nextTag = [textField tag] + 1;
+        UIView *nextTextField = [self.tableView viewWithTag:nextTag];
+        [nextTextField becomeFirstResponder];
+    }
+    else
+    {
+        [textField resignFirstResponder];
+    }
+    
+    return YES;
+}
+
+@end
diff --git a/Classes/EditMetadataViewController.xib b/Classes/EditMetadataViewController.xib
new file mode 100644 (file)
index 0000000..a89bd94
--- /dev/null
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
+       <data>
+               <int key="IBDocument.SystemTarget">1056</int>
+               <string key="IBDocument.SystemVersion">10K549</string>
+               <string key="IBDocument.InterfaceBuilderVersion">1306</string>
+               <string key="IBDocument.AppKitVersion">1038.36</string>
+               <string key="IBDocument.HIToolboxVersion">461.00</string>
+               <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+                       <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+                       <string key="NS.object.0">301</string>
+               </object>
+               <object class="NSArray" key="IBDocument.IntegratedClassDependencies">
+                       <bool key="EncodedWithXMLCoder">YES</bool>
+                       <string>IBProxyObject</string>
+                       <string>IBUITableView</string>
+               </object>
+               <object class="NSArray" key="IBDocument.PluginDependencies">
+                       <bool key="EncodedWithXMLCoder">YES</bool>
+                       <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+               </object>
+               <object class="NSMutableDictionary" key="IBDocument.Metadata">
+                       <bool key="EncodedWithXMLCoder">YES</bool>
+                       <object class="NSArray" key="dict.sortedKeys" id="0">
+                               <bool key="EncodedWithXMLCoder">YES</bool>
+                       </object>
+                       <reference key="dict.values" ref="0"/>
+               </object>
+               <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+                       <bool key="EncodedWithXMLCoder">YES</bool>
+                       <object class="IBProxyObject" id="372490531">
+                               <string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
+                               <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+                       </object>
+                       <object class="IBProxyObject" id="975951072">
+                               <string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
+                               <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+                       </object>
+                       <object class="IBUITableView" id="403916641">
+                               <reference key="NSNextResponder"/>
+                               <int key="NSvFlags">274</int>
+                               <string key="NSFrameSize">{292, 440}</string>
+                               <reference key="NSSuperview"/>
+                               <reference key="NSWindow"/>
+                               <object class="NSColor" key="IBUIBackgroundColor">
+                                       <int key="NSColorSpace">10</int>
+                                       <object class="NSImage" key="NSImage">
+                                               <int key="NSImageFlags">549453824</int>
+                                               <string key="NSSize">{84, 1}</string>
+                                               <object class="NSMutableArray" key="NSReps">
+                                                       <bool key="EncodedWithXMLCoder">YES</bool>
+                                                       <object class="NSArray">
+                                                               <bool key="EncodedWithXMLCoder">YES</bool>
+                                                               <integer value="0"/>
+                                                               <object class="NSBitmapImageRep">
+                                                                       <object class="NSData" key="NSTIFFRepresentation">
+                                                                               <bytes key="NS.bytes">TU0AKgAAAVjFzNT/xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/xczU/8XM1P/FzNT/xczU/8XM0v/L0tj/
+y9LY/8XM1P/FzNT/xczU/8XM1P/FzNL/y9LY/8vS2P/FzNT/xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/
+xczU/8XM1P/FzNT/xczU/8XM0v/L0tj/y9LY/8XM1P/FzNT/xczU/8XM1P/FzNL/y9LY/8vS2P/FzNT/
+xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/xczU/8XM1P/FzNT/xczU/8XM0v/L0tj/y9LY/8XM1P/FzNT/
+xczU/8XM1P/FzNL/y9LY/8vS2P/FzNT/xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/xczU/8XM1P/FzNT/
+xczU/8XM0v/L0tj/y9LY/8XM1P/FzNT/xczU/8XM1P/FzNL/y9LY/8vS2P8ADQEAAAMAAAABAFQAAAEB
+AAMAAAABAAEAAAECAAMAAAAEAAAB+gEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES
+AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABAAEAAAEXAAQAAAABAAABUAEcAAMAAAABAAEAAAFS
+AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA</bytes>
+                                                                       </object>
+                                                               </object>
+                                                       </object>
+                                               </object>
+                                               <object class="NSColor" key="NSColor">
+                                                       <int key="NSColorSpace">3</int>
+                                                       <bytes key="NSWhite">MCAwAA</bytes>
+                                               </object>
+                                       </object>
+                                       <string key="IBUIColorCocoaTouchKeyPath">groupTableViewBackgroundColor</string>
+                               </object>
+                               <bool key="IBUIClipsSubviews">YES</bool>
+                               <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+                               <bool key="IBUIBouncesZoom">NO</bool>
+                               <int key="IBUIStyle">1</int>
+                               <int key="IBUISeparatorStyle">1</int>
+                               <int key="IBUISectionIndexMinimumDisplayRowCount">0</int>
+                               <bool key="IBUIShowsSelectionImmediatelyOnTouchBegin">YES</bool>
+                               <float key="IBUIRowHeight">44</float>
+                               <float key="IBUISectionHeaderHeight">10</float>
+                               <float key="IBUISectionFooterHeight">10</float>
+                       </object>
+               </object>
+               <object class="IBObjectContainer" key="IBDocument.Objects">
+                       <object class="NSMutableArray" key="connectionRecords">
+                               <bool key="EncodedWithXMLCoder">YES</bool>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBCocoaTouchOutletConnection" key="connection">
+                                               <string key="label">dataSource</string>
+                                               <reference key="source" ref="403916641"/>
+                                               <reference key="destination" ref="372490531"/>
+                                       </object>
+                                       <int key="connectionID">9</int>
+                               </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBCocoaTouchOutletConnection" key="connection">
+                                               <string key="label">delegate</string>
+                                               <reference key="source" ref="403916641"/>
+                                               <reference key="destination" ref="372490531"/>
+                                       </object>
+                                       <int key="connectionID">10</int>
+                               </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBCocoaTouchOutletConnection" key="connection">
+                                               <string key="label">view</string>
+                                               <reference key="source" ref="372490531"/>
+                                               <reference key="destination" ref="403916641"/>
+                                       </object>
+                                       <int key="connectionID">11</int>
+                               </object>
+                       </object>
+                       <object class="IBMutableOrderedSet" key="objectRecords">
+                               <object class="NSArray" key="orderedObjects">
+                                       <bool key="EncodedWithXMLCoder">YES</bool>
+                                       <object class="IBObjectRecord">
+                                               <int key="objectID">0</int>
+                                               <reference key="object" ref="0"/>
+                                               <reference key="children" ref="1000"/>
+                                               <nil key="parent"/>
+                                       </object>
+                                       <object class="IBObjectRecord">
+                                               <int key="objectID">-1</int>
+                                               <reference key="object" ref="372490531"/>
+                                               <reference key="parent" ref="0"/>
+                                               <string key="objectName">File's Owner</string>
+                                       </object>
+                                       <object class="IBObjectRecord">
+                                               <int key="objectID">-2</int>
+                                               <reference key="object" ref="975951072"/>
+                                               <reference key="parent" ref="0"/>
+                                       </object>
+                                       <object class="IBObjectRecord">
+                                               <int key="objectID">8</int>
+                                               <reference key="object" ref="403916641"/>
+                                               <reference key="parent" ref="0"/>
+                                       </object>
+                               </object>
+                       </object>
+                       <object class="NSMutableDictionary" key="flattenedProperties">
+                               <bool key="EncodedWithXMLCoder">YES</bool>
+                               <object class="NSArray" key="dict.sortedKeys">
+                                       <bool key="EncodedWithXMLCoder">YES</bool>
+                                       <string>-1.CustomClassName</string>
+                                       <string>-2.CustomClassName</string>
+                                       <string>8.IBPluginDependency</string>
+                               </object>
+                               <object class="NSMutableArray" key="dict.values">
+                                       <bool key="EncodedWithXMLCoder">YES</bool>
+                                       <string>EditMetadataViewController</string>
+                                       <string>UIResponder</string>
+                                       <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+                               </object>
+                       </object>
+                       <object class="NSMutableDictionary" key="unlocalizedProperties">
+                               <bool key="EncodedWithXMLCoder">YES</bool>
+                               <reference key="dict.sortedKeys" ref="0"/>
+                               <reference key="dict.values" ref="0"/>
+                       </object>
+                       <nil key="activeLocalization"/>
+                       <object class="NSMutableDictionary" key="localizations">
+                               <bool key="EncodedWithXMLCoder">YES</bool>
+                               <reference key="dict.sortedKeys" ref="0"/>
+                               <reference key="dict.values" ref="0"/>
+                       </object>
+                       <nil key="sourceID"/>
+                       <int key="maxID">13</int>
+               </object>
+               <object class="IBClassDescriber" key="IBDocument.Classes">
+                       <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+                               <bool key="EncodedWithXMLCoder">YES</bool>
+                               <object class="IBPartialClassDescription">
+                                       <string key="className">EditMetadataViewController</string>
+                                       <string key="superclassName">UITableViewController</string>
+                                       <object class="NSMutableDictionary" key="outlets">
+                                               <string key="NS.key.0">aTableView</string>
+                                               <string key="NS.object.0">UITableView</string>
+                                       </object>
+                                       <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+                                               <string key="NS.key.0">aTableView</string>
+                                               <object class="IBToOneOutletInfo" key="NS.object.0">
+                                                       <string key="name">aTableView</string>
+                                                       <string key="candidateClassName">UITableView</string>
+                                               </object>
+                                       </object>
+                                       <object class="IBClassDescriptionSource" key="sourceIdentifier">
+                                               <string key="majorKey">IBProjectSource</string>
+                                               <string key="minorKey">./Classes/EditMetadataViewController.h</string>
+                                       </object>
+                               </object>
+                       </object>
+               </object>
+               <int key="IBDocument.localizationMode">0</int>
+               <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
+               <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+                       <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
+                       <integer value="3000" key="NS.object.0"/>
+               </object>
+               <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+               <int key="IBDocument.defaultPropertyAccessControl">3</int>
+               <string key="IBCocoaTouchPluginVersion">301</string>
+       </data>
+</archive>
index 71c8b55..99210bd 100755 (executable)
@@ -23,6 +23,8 @@
        NSMutableDictionary *folders;
        NSMutableDictionary *objects;
     NSArray *sortedContents;
+    
+    NSMutableDictionary *metadata;
 }
 
 @property (nonatomic, retain) NSString *name;
@@ -30,6 +32,7 @@
 @property (nonatomic, retain) NSMutableDictionary *folders;
 @property (nonatomic, retain) NSMutableDictionary *objects;
 @property (readonly, retain) NSArray *sortedContents;
+@property (nonatomic, retain) NSMutableDictionary *metadata;
 
 + (id)folder;
 - (NSArray *)sortedContents;
index b4f0602..eea7c8f 100755 (executable)
@@ -13,7 +13,7 @@
 
 @implementation Folder
 
-@synthesize name, parent, folders, objects;
+@synthesize name, parent, folders, objects, metadata;
 
 + (id)folder {
        Folder *folder = [[[self alloc] init] autorelease];
@@ -92,6 +92,7 @@
                 Folder *folder = [[Folder alloc] init];
                 folder.name = object.name;
                 folder.parent = self;
+                folder.metadata = object.metadata;    
                 [self.folders setObject:folder forKey:folder.name];
                 [folder release];
             } else {
             folder.name = folderName;
             folder.parent = self;
             folder.objects = files;
+            StorageObject *object = [objs objectForKey:folderName];
+            folder.metadata = object.metadata;
             [self.folders setObject:folder forKey:folder.name];
             [folder release];
         }
diff --git a/Classes/FolderDetailViewController.h b/Classes/FolderDetailViewController.h
new file mode 100644 (file)
index 0000000..9993e1b
--- /dev/null
@@ -0,0 +1,55 @@
+//
+//  FolderDetailViewController.h
+//  pithos-ios
+//
+// Copyright 2011 GRNET S.A. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or
+// without modification, are permitted provided that the following
+// conditions are met:
+// 
+//   1. Redistributions of source code must retain the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer in the documentation and/or other materials
+//      provided with the distribution.
+// 
+// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+// 
+// The views and conclusions contained in the software and
+// documentation are those of the authors and should not be
+// interpreted as representing official policies, either expressed
+// or implied, of GRNET S.A.
+
+#import <UIKit/UIKit.h>
+#import "OpenStackAccount.h"
+#import "Container.h"
+#import "Folder.h"
+
+
+
+@interface FolderDetailViewController : UITableViewController {
+    OpenStackAccount *account;
+    Container *container;
+    Folder *folder;
+}
+
+@property (nonatomic, retain) OpenStackAccount *account;
+@property (nonatomic, retain) Container *container;
+@property (nonatomic, retain) Folder *folder;
+
+@end
diff --git a/Classes/FolderDetailViewController.m b/Classes/FolderDetailViewController.m
new file mode 100644 (file)
index 0000000..a406115
--- /dev/null
@@ -0,0 +1,225 @@
+//
+//  FolderDetailViewController.m
+//  pithos-ios
+//
+// Copyright 2011 GRNET S.A. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or
+// without modification, are permitted provided that the following
+// conditions are met:
+// 
+//   1. Redistributions of source code must retain the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer in the documentation and/or other materials
+//      provided with the distribution.
+// 
+// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+// 
+// The views and conclusions contained in the software and
+// documentation are those of the authors and should not be
+// interpreted as representing official policies, either expressed
+// or implied, of GRNET S.A.
+
+#import "FolderDetailViewController.h"
+#import "EditMetadataViewController.h"
+
+#define kOverview 0
+#define kMetadata 1
+
+#define maxMetadataViewableLength 12
+
+
+@implementation FolderDetailViewController
+
+@synthesize account, container, folder;
+
+- (id)initWithStyle:(UITableViewStyle)style
+{
+    self = [super initWithStyle:style];
+    if (self) {
+        // Custom initialization
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    [folder release];
+    [super dealloc];
+}
+
+- (void)didReceiveMemoryWarning
+{
+    // Releases the view if it doesn't have a superview.
+    [super didReceiveMemoryWarning];
+    
+    // Release any cached data, images, etc that aren't in use.
+}
+
+#pragma mark - View lifecycle
+
+- (void)viewDidLoad
+{
+    [super viewDidLoad];
+}
+
+- (void)viewDidUnload
+{
+    [super viewDidUnload];
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+    [super viewWillAppear:animated];
+    
+    [self.tableView reloadData];
+}
+
+- (void)viewDidAppear:(BOOL)animated
+{
+    [super viewDidAppear:animated];
+}
+
+- (void)viewWillDisappear:(BOOL)animated
+{
+    [super viewWillDisappear:animated];
+}
+
+- (void)viewDidDisappear:(BOOL)animated
+{
+    [super viewDidDisappear:animated];
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
+    return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
+}
+
+#pragma mark - Table view data source
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+    return 2;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+    if (section == kOverview)
+        return 2;
+    else 
+        return 1 + [folder.metadata count];
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    static NSString *CellIdentifier = @"Cell";
+    
+    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+    if (cell == nil) {
+        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
+
+        cell.textLabel.backgroundColor = [UIColor clearColor];
+        cell.detailTextLabel.backgroundColor = [UIColor clearColor];
+        cell.detailTextLabel.numberOfLines = 0;
+        cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
+        cell.detailTextLabel.textAlignment = UITextAlignmentRight;
+    }
+    
+    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+        cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8];
+    }
+    
+    if (indexPath.section == kOverview) {
+        cell.accessoryType = UITableViewCellAccessoryNone;
+        cell.selectionStyle = UITableViewCellSelectionStyleNone;
+        cell.accessoryView = nil;
+        if (indexPath.row == 0) {
+            cell.textLabel.text = @"Name";
+            cell.detailTextLabel.text = folder.name;
+        } else if (indexPath.row == 1) {
+            cell.textLabel.text = @"Full Path";
+            cell.detailTextLabel.text = [folder fullPath];
+        }
+    } else if (indexPath.section == kMetadata) {
+        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+        cell.accessoryView = nil;
+        if (indexPath.row == [folder.metadata count]) {
+            cell.textLabel.text = @"Add Metadata";
+            cell.detailTextLabel.text = @"";
+        } else {
+            NSString *key = [[folder.metadata allKeys] objectAtIndex:indexPath.row];
+            NSString *value = [folder.metadata objectForKey:key];
+            NSString *metadataKeyCellText = key;
+            NSString *metadataValueCellText = value;
+            if ([metadataKeyCellText length] > maxMetadataViewableLength) {
+                metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
+                metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
+            }
+            if ([metadataValueCellText length] > maxMetadataViewableLength) {
+                metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
+                metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
+            }
+
+            cell.textLabel.text = metadataKeyCellText;
+            cell.detailTextLabel.text = metadataValueCellText;
+        }
+    }
+
+    return cell;
+}
+
+
+#pragma mark - Table view delegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    if (indexPath.section == kMetadata) {
+        EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
+        NSString *metadataKey;
+        NSString *metadataValue;
+        
+        if (indexPath.row == [self.folder.metadata count]) {
+            metadataKey = @"";
+            metadataValue = @"";
+            vc.deleteEnabled = FALSE;
+            vc.navigationItem.title = @"Add Metadata";
+        }
+        else {
+            metadataKey = [[self.folder.metadata allKeys] objectAtIndex:indexPath.row];
+            metadataValue = [self.folder.metadata objectForKey:metadataKey];
+            vc.deleteEnabled = TRUE;
+            vc.navigationItem.title = @"Edit Metadata";
+        }
+        
+        StorageObject *object = [[[StorageObject alloc] init] autorelease];
+        object.name = folder.name;
+        object.metadata = folder.metadata;
+        object.fullPath = [folder fullPath];
+        
+        vc.metadataKey = metadataKey;
+        vc.metadataValue = metadataValue;
+        vc.account = account;
+        vc.container = container;
+        vc.object = object;
+
+        [self.navigationController pushViewController:vc animated:YES];
+        [vc release];
+    }
+}
+
+@end
diff --git a/Classes/FolderDetailViewController.xib b/Classes/FolderDetailViewController.xib
new file mode 100644 (file)
index 0000000..39e366b
--- /dev/null
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
+       <data>
+               <int key="IBDocument.SystemTarget">1056</int>
+               <string key="IBDocument.SystemVersion">10K549</string>
+               <string key="IBDocument.InterfaceBuilderVersion">1306</string>
+               <string key="IBDocument.AppKitVersion">1038.36</string>
+               <string key="IBDocument.HIToolboxVersion">461.00</string>
+               <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+                       <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+                       <string key="NS.object.0">301</string>
+               </object>
+               <object class="NSArray" key="IBDocument.IntegratedClassDependencies">
+                       <bool key="EncodedWithXMLCoder">YES</bool>
+                       <string>IBProxyObject</string>
+                       <string>IBUITableView</string>
+               </object>
+               <object class="NSArray" key="IBDocument.PluginDependencies">
+                       <bool key="EncodedWithXMLCoder">YES</bool>
+                       <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+               </object>
+               <object class="NSMutableDictionary" key="IBDocument.Metadata">
+                       <bool key="EncodedWithXMLCoder">YES</bool>
+                       <object class="NSArray" key="dict.sortedKeys" id="0">
+                               <bool key="EncodedWithXMLCoder">YES</bool>
+                       </object>
+                       <reference key="dict.values" ref="0"/>
+               </object>
+               <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+                       <bool key="EncodedWithXMLCoder">YES</bool>
+                       <object class="IBProxyObject" id="372490531">
+                               <string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
+                               <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+                       </object>
+                       <object class="IBProxyObject" id="975951072">
+                               <string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
+                               <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+                       </object>
+                       <object class="IBUITableView" id="873029372">
+                               <reference key="NSNextResponder"/>
+                               <int key="NSvFlags">274</int>
+                               <string key="NSFrame">{{0, 20}, {320, 460}}</string>
+                               <reference key="NSSuperview"/>
+                               <reference key="NSNextKeyView"/>
+                               <object class="NSColor" key="IBUIBackgroundColor">
+                                       <int key="NSColorSpace">10</int>
+                                       <object class="NSImage" key="NSImage">
+                                               <int key="NSImageFlags">549453824</int>
+                                               <string key="NSSize">{84, 1}</string>
+                                               <object class="NSMutableArray" key="NSReps">
+                                                       <bool key="EncodedWithXMLCoder">YES</bool>
+                                                       <object class="NSArray">
+                                                               <bool key="EncodedWithXMLCoder">YES</bool>
+                                                               <integer value="0"/>
+                                                               <object class="NSBitmapImageRep">
+                                                                       <object class="NSData" key="NSTIFFRepresentation">
+                                                                               <bytes key="NS.bytes">TU0AKgAAAVjFzNT/xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/xczU/8XM1P/FzNT/xczU/8XM0v/L0tj/
+y9LY/8XM1P/FzNT/xczU/8XM1P/FzNL/y9LY/8vS2P/FzNT/xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/
+xczU/8XM1P/FzNT/xczU/8XM0v/L0tj/y9LY/8XM1P/FzNT/xczU/8XM1P/FzNL/y9LY/8vS2P/FzNT/
+xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/xczU/8XM1P/FzNT/xczU/8XM0v/L0tj/y9LY/8XM1P/FzNT/
+xczU/8XM1P/FzNL/y9LY/8vS2P/FzNT/xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/xczU/8XM1P/FzNT/
+xczU/8XM0v/L0tj/y9LY/8XM1P/FzNT/xczU/8XM1P/FzNL/y9LY/8vS2P8ADQEAAAMAAAABAFQAAAEB
+AAMAAAABAAEAAAECAAMAAAAEAAAB+gEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES
+AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABAAEAAAEXAAQAAAABAAABUAEcAAMAAAABAAEAAAFS
+AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA</bytes>
+                                                                       </object>
+                                                               </object>
+                                                       </object>
+                                               </object>
+                                               <object class="NSColor" key="NSColor">
+                                                       <int key="NSColorSpace">3</int>
+                                                       <bytes key="NSWhite">MCAwAA</bytes>
+                                               </object>
+                                       </object>
+                                       <string key="IBUIColorCocoaTouchKeyPath">groupTableViewBackgroundColor</string>
+                               </object>
+                               <bool key="IBUIOpaque">NO</bool>
+                               <bool key="IBUIClipsSubviews">YES</bool>
+                               <bool key="IBUIClearsContextBeforeDrawing">NO</bool>
+                               <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+                               <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+                               <bool key="IBUIBouncesZoom">NO</bool>
+                               <int key="IBUIStyle">1</int>
+                               <int key="IBUISeparatorStyle">1</int>
+                               <int key="IBUISectionIndexMinimumDisplayRowCount">0</int>
+                               <bool key="IBUIShowsSelectionImmediatelyOnTouchBegin">YES</bool>
+                               <float key="IBUIRowHeight">44</float>
+                               <float key="IBUISectionHeaderHeight">10</float>
+                               <float key="IBUISectionFooterHeight">10</float>
+                       </object>
+               </object>
+               <object class="IBObjectContainer" key="IBDocument.Objects">
+                       <object class="NSMutableArray" key="connectionRecords">
+                               <bool key="EncodedWithXMLCoder">YES</bool>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBCocoaTouchOutletConnection" key="connection">
+                                               <string key="label">view</string>
+                                               <reference key="source" ref="372490531"/>
+                                               <reference key="destination" ref="873029372"/>
+                                       </object>
+                                       <int key="connectionID">5</int>
+                               </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBCocoaTouchOutletConnection" key="connection">
+                                               <string key="label">dataSource</string>
+                                               <reference key="source" ref="873029372"/>
+                                               <reference key="destination" ref="372490531"/>
+                                       </object>
+                                       <int key="connectionID">6</int>
+                               </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBCocoaTouchOutletConnection" key="connection">
+                                               <string key="label">delegate</string>
+                                               <reference key="source" ref="873029372"/>
+                                               <reference key="destination" ref="372490531"/>
+                                       </object>
+                                       <int key="connectionID">7</int>
+                               </object>
+                       </object>
+                       <object class="IBMutableOrderedSet" key="objectRecords">
+                               <object class="NSArray" key="orderedObjects">
+                                       <bool key="EncodedWithXMLCoder">YES</bool>
+                                       <object class="IBObjectRecord">
+                                               <int key="objectID">0</int>
+                                               <reference key="object" ref="0"/>
+                                               <reference key="children" ref="1000"/>
+                                               <nil key="parent"/>
+                                       </object>
+                                       <object class="IBObjectRecord">
+                                               <int key="objectID">-1</int>
+                                               <reference key="object" ref="372490531"/>
+                                               <reference key="parent" ref="0"/>
+                                               <string key="objectName">File's Owner</string>
+                                       </object>
+                                       <object class="IBObjectRecord">
+                                               <int key="objectID">-2</int>
+                                               <reference key="object" ref="975951072"/>
+                                               <reference key="parent" ref="0"/>
+                                       </object>
+                                       <object class="IBObjectRecord">
+                                               <int key="objectID">4</int>
+                                               <reference key="object" ref="873029372"/>
+                                               <reference key="parent" ref="0"/>
+                                       </object>
+                               </object>
+                       </object>
+                       <object class="NSMutableDictionary" key="flattenedProperties">
+                               <bool key="EncodedWithXMLCoder">YES</bool>
+                               <object class="NSArray" key="dict.sortedKeys">
+                                       <bool key="EncodedWithXMLCoder">YES</bool>
+                                       <string>-1.CustomClassName</string>
+                                       <string>-2.CustomClassName</string>
+                                       <string>4.IBEditorWindowLastContentRect</string>
+                                       <string>4.IBPluginDependency</string>
+                               </object>
+                               <object class="NSMutableArray" key="dict.values">
+                                       <bool key="EncodedWithXMLCoder">YES</bool>
+                                       <string>FolderDetailViewController</string>
+                                       <string>UIResponder</string>
+                                       <string>{{329, 504}, {320, 480}}</string>
+                                       <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+                               </object>
+                       </object>
+                       <object class="NSMutableDictionary" key="unlocalizedProperties">
+                               <bool key="EncodedWithXMLCoder">YES</bool>
+                               <reference key="dict.sortedKeys" ref="0"/>
+                               <reference key="dict.values" ref="0"/>
+                       </object>
+                       <nil key="activeLocalization"/>
+                       <object class="NSMutableDictionary" key="localizations">
+                               <bool key="EncodedWithXMLCoder">YES</bool>
+                               <reference key="dict.sortedKeys" ref="0"/>
+                               <reference key="dict.values" ref="0"/>
+                       </object>
+                       <nil key="sourceID"/>
+                       <int key="maxID">7</int>
+               </object>
+               <object class="IBClassDescriber" key="IBDocument.Classes"/>
+               <int key="IBDocument.localizationMode">0</int>
+               <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
+               <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+                       <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
+                       <integer value="3000" key="NS.object.0"/>
+               </object>
+               <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+               <int key="IBDocument.defaultPropertyAccessControl">3</int>
+               <string key="IBCocoaTouchPluginVersion">301</string>
+       </data>
+</archive>
index c40cc85..30fbcba 100755 (executable)
@@ -20,6 +20,7 @@
 #import "AddObjectViewController.h"
 #import "OpenStackAppDelegate.h"
 #import "RootViewController.h"
+#import "FolderDetailViewController.h"
 
 
 @implementation FolderViewController
         
         id item = [self.folder.sortedContents objectAtIndex:indexPath.row];    
         cell.textLabel.text = [item name];
-        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
         
         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 = @"";
             }
             cell.detailTextLabel.text = [NSString stringWithFormat:@"%@%@", folderString, objectString];
         } else if ([[item class] isEqual:[StorageObject class]]) {
+            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+            
             StorageObject *object = item;
             NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
             NSString *documentsDirectory = [paths objectAtIndex:0];
             vc.parentFolderViewController = self;
             [self.navigationController pushViewController:vc animated:YES];
             [vc release];
+            if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+                Folder *folderForDetailView;
+                if ([folder.sortedContents count] > 0) {
+                    folderForDetailView = [self.folder.sortedContents objectAtIndex:indexPath.row];
+                }
+                FolderDetailViewController *folderDetailViewController = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
+                folderDetailViewController.account = account;
+                folderDetailViewController.container = container;
+                folderDetailViewController.folder = folderForDetailView;
+                [self presentPrimaryViewController:folderDetailViewController];
+                [folderDetailViewController release];
+            }
         } else if ([[item class] isEqual:[StorageObject class]]) {
             StorageObjectViewController *vc = [[StorageObjectViewController alloc] initWithNibName:@"StorageObjectViewController" bundle:nil];
             vc.account = self.account;
     }
 }
 
+- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
+    Folder *folderForDetailView;
+    if ([folder.sortedContents count] > 0) {
+        folderForDetailView = [self.folder.sortedContents objectAtIndex:indexPath.row];
+    }
+    FolderDetailViewController *vc = [[FolderDetailViewController alloc] initWithNibName:@"FolderDetailViewController" bundle:nil];
+    vc.account = account;
+    vc.container = container;
+    vc.folder = folderForDetailView;
+
+    [self.navigationController pushViewController:vc animated:YES];
+    [vc release];
+}
+
 #pragma mark -
 #pragma mark Buttons
 
index 922ae68..4e98118 100755 (executable)
@@ -653,7 +653,23 @@ static NSRecursiveLock *accessDetailsLock = nil;
 }
 
 + (OpenStackRequest *)writeObjectMetadataRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
-    return nil;
+    NSString *fullPath = [NSString stringWithString:object.fullPath];
+    if ([fullPath length] && ([fullPath characterAtIndex:0] == '/'))
+        fullPath = [fullPath substringFromIndex:1];
+
+    NSString *metadataKeyHeaderPrefix;
+    if (![fullPath length])
+        metadataKeyHeaderPrefix = @"X-Container-Meta-";
+    else
+        metadataKeyHeaderPrefix = @"X-Object-Meta-";
+        
+    OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"POST" path:[[NSString stringWithFormat:@"/%@/%@", container.name, fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; 
+    
+    for (NSString *metadataKey in object.metadata) {
+        NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey]; 
+        [request.requestHeaders setObject:[object.metadata objectForKey:metadataKey] forKey:metadataKeyHeader];
+    }
+    return request;
 }
 
 + (OpenStackRequest *)deleteObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
index 3a9efae..fd730f1 100755 (executable)
     object.contentType = [dict objectForKey:@"content_type"];
     object.lastModified = [ComputeModel dateFromString:[dict objectForKey:@"last_modified"]];
     
+    object.metadata = [NSMutableDictionary dictionary];
+    for (NSString *key in dict) {
+        if ([key hasPrefix:@"x_object_meta_"]) {
+            NSString *metadataKey = [key substringFromIndex:14];
+            [object.metadata setObject:[dict objectForKey:key] forKey:metadataKey];
+        }
+    }
+        
     return object;
 }
 
index 29a4e56..f1cf992 100755 (executable)
 #import "FolderViewController.h"
 #import "UIColor+MoreColors.h"
 #import "OpenStackAppDelegate.h"
+#import "EditMetadataViewController.h"
 
 #define kDetails 0
-#define kMetadata -1
+#define kMetadata 1
+
+#define maxMetadataViewableLength 12
+
 
 // TODO: use etag to reset download
 // TODO: try downloading directly to the file to save memory.  don't use object.data
     [super viewWillAppear:animated];
     self.navigationItem.title = object.name;
     
+    NSIndexPath*       selection = [self.tableView indexPathForSelectedRow];
+       if (selection)
+               [self.tableView deselectRowAtIndexPath:selection animated:YES];
+    
     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
     NSString *documentsDirectory = [paths objectAtIndex:0];        
     NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
     
     [self setBackgroundView];
     if (self.container.cdnEnabled) {
-        cdnURLSection = 1;
-        actionsSection = 2;
-        deleteSection = 3;
+        cdnURLSection = 2;
+        actionsSection = 3;
+        deleteSection = 4;
     } else {
         cdnURLSection = -1;
-        actionsSection = 1;
-        deleteSection = 2;
+        actionsSection = 2;
+        deleteSection = 3;
     }
     
     // let's see if we can tweet
         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];
     }
     
+    [self.tableView reloadData];
 }
 
 #pragma mark -
 #pragma mark Table view data source
 
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
-    return self.container.cdnEnabled ? 4 : 3;
+    return self.container.cdnEnabled ? 5 : 4;
 }
 
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
             cell.detailTextLabel.text = @"";
         } else {
             NSString *key = [[object.metadata allKeys] objectAtIndex:indexPath.row];
-            cell.textLabel.text = key;
-            cell.detailTextLabel.text = [object.metadata objectForKey:key];
+            NSString *value = [object.metadata objectForKey:key];
+            NSString *metadataKeyCellText = key;
+            NSString *metadataValueCellText = value;
+            if ([metadataKeyCellText length] > maxMetadataViewableLength) {
+                metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
+                metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
+            }
+            if ([metadataValueCellText length] > maxMetadataViewableLength) {
+                metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
+                metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
+            }
+            
+            cell.textLabel.text = metadataKeyCellText;
+            cell.detailTextLabel.text = metadataValueCellText;
         }
     } else if (indexPath.section == cdnURLSection) {
         cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
 }
 
 - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
-    if (indexPath.section == cdnURLSection) {
+    if (indexPath.section == kMetadata) {
+        EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
+        NSString *metadataKey;
+        NSString *metadataValue;
+        
+        if (indexPath.row == [self.object.metadata count]) {
+            metadataKey = @"";
+            metadataValue = @"";
+            vc.deleteEnabled = FALSE;
+            vc.navigationItem.title = @"Add Metadata";
+        }
+        else {
+            metadataKey = [[self.object.metadata allKeys] objectAtIndex:indexPath.row];
+            metadataValue = [self.object.metadata objectForKey:metadataKey];
+            vc.deleteEnabled = YES;
+            vc.navigationItem.title = @"Edit Metadata";
+        }
+
+        vc.metadataKey = metadataKey;
+        vc.metadataValue = metadataValue;
+        vc.account = account;
+        vc.container = container;
+        vc.object = object;
+        [self.navigationController pushViewController:vc animated:YES];
+        [vc release];
+    } else if (indexPath.section == cdnURLSection) {
         [cdnURLActionSheet showInView:self.view];
     } else if (indexPath.section == actionsSection) {
         if (indexPath.row == 0) {
index 2a4c5b6..43d6486 100755 (executable)
@@ -9,6 +9,11 @@
 /* Begin PBXBuildFile section */
                1D3623260D0F684500981E51 /* OpenStackAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* OpenStackAppDelegate.m */; };
                1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; };
+               24A18E22143DB870003232F1 /* EditMetadataViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24A18E20143DB870003232F1 /* EditMetadataViewController.m */; };
+               24A18E23143DB870003232F1 /* EditMetadataViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 24A18E21143DB870003232F1 /* EditMetadataViewController.xib */; };
+               24A18E56143F0429003232F1 /* FolderDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24A18E54143F0428003232F1 /* FolderDetailViewController.m */; };
+               24A18E57143F0429003232F1 /* FolderDetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 24A18E55143F0428003232F1 /* FolderDetailViewController.xib */; };
+               24E040051442FB3C0065E8DB /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E040041442FB3C0065E8DB /* libz.dylib */; };
                2705F3EB12E7FA5900949131 /* RSTextFieldCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2705F3E912E7FA5900949131 /* RSTextFieldCell.m */; };
                2705F3ED12E7FA5900949131 /* RSTextFieldCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2705F3E912E7FA5900949131 /* RSTextFieldCell.m */; };
                270878F213282104006D4EE3 /* NSObject+NSCoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 270878F013282104006D4EE3 /* NSObject+NSCoding.m */; };
                277D9E7B126E78FD002C69D9 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F740E6125FB44F002C6CB0 /* CFNetwork.framework */; };
                277D9E7C126E78FD002C69D9 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F740F9125FB46A002C6CB0 /* MobileCoreServices.framework */; };
                277D9E7D126E78FD002C69D9 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F740FD125FB476002C6CB0 /* SystemConfiguration.framework */; };
-               277D9E7E126E78FD002C69D9 /* libz.1.2.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F74158125FB66F002C6CB0 /* libz.1.2.3.dylib */; };
                277D9E7F126E78FD002C69D9 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27869AF31263F22300CE3435 /* MessageUI.framework */; };
                277D9E99126E7963002C69D9 /* MainWindow-Rackspace.xib in Resources */ = {isa = PBXBuildFile; fileRef = 277D9E98126E7963002C69D9 /* MainWindow-Rackspace.xib */; };
                277D9EA2126E79A1002C69D9 /* AccountHomeViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2798AA45125E74F800E02475 /* AccountHomeViewController.xib */; };
                27C15CAF13A7C4D500A1BB9F /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F740E6125FB44F002C6CB0 /* CFNetwork.framework */; };
                27C15CB013A7C4D800A1BB9F /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F740F9125FB46A002C6CB0 /* MobileCoreServices.framework */; };
                27C15CB113A7C4DB00A1BB9F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F740FD125FB476002C6CB0 /* SystemConfiguration.framework */; };
-               27C15CB213A7C4EA00A1BB9F /* libz.1.2.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F74158125FB66F002C6CB0 /* libz.1.2.3.dylib */; };
                27C15CB313A7C4EC00A1BB9F /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27869AF31263F22300CE3435 /* MessageUI.framework */; };
                27C15CB413A7C4EF00A1BB9F /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27B0A1D81278047900A31CED /* QuartzCore.framework */; };
                27C15CB613A7C50600A1BB9F /* libobjc.A.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 277F29211321A9DA001F8685 /* libobjc.A.dylib */; };
                27F740E7125FB44F002C6CB0 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F740E6125FB44F002C6CB0 /* CFNetwork.framework */; };
                27F740FA125FB46A002C6CB0 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F740F9125FB46A002C6CB0 /* MobileCoreServices.framework */; };
                27F740FE125FB476002C6CB0 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F740FD125FB476002C6CB0 /* SystemConfiguration.framework */; };
-               27F74159125FB66F002C6CB0 /* libz.1.2.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 27F74158125FB66F002C6CB0 /* libz.1.2.3.dylib */; };
                27F7445C1261130F002C6CB0 /* UIViewController+Conveniences.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F7445A1261130F002C6CB0 /* UIViewController+Conveniences.m */; };
                27F7446312611341002C6CB0 /* NSObject+Conveniences.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F7446112611341002C6CB0 /* NSObject+Conveniences.m */; };
                27F744DA126122AF002C6CB0 /* NSString+Conveniences.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F744D8126122AF002C6CB0 /* NSString+Conveniences.m */; };
                1D3623240D0F684500981E51 /* OpenStackAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenStackAppDelegate.h; sourceTree = "<group>"; };
                1D3623250D0F684500981E51 /* OpenStackAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OpenStackAppDelegate.m; sourceTree = "<group>"; };
                1D6058910D05DD3D006BFB54 /* OpenStack.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OpenStack.app; sourceTree = BUILT_PRODUCTS_DIR; };
+               24A18E1F143DB86F003232F1 /* EditMetadataViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditMetadataViewController.h; sourceTree = "<group>"; };
+               24A18E20143DB870003232F1 /* EditMetadataViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EditMetadataViewController.m; sourceTree = "<group>"; };
+               24A18E21143DB870003232F1 /* EditMetadataViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EditMetadataViewController.xib; sourceTree = "<group>"; };
+               24A18E53143F0427003232F1 /* FolderDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FolderDetailViewController.h; sourceTree = "<group>"; };
+               24A18E54143F0428003232F1 /* FolderDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FolderDetailViewController.m; sourceTree = "<group>"; };
+               24A18E55143F0428003232F1 /* FolderDetailViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FolderDetailViewController.xib; sourceTree = "<group>"; };
+               24E040041442FB3C0065E8DB /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
                2705F3E812E7FA5900949131 /* RSTextFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSTextFieldCell.h; sourceTree = "<group>"; };
                2705F3E912E7FA5900949131 /* RSTextFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSTextFieldCell.m; sourceTree = "<group>"; };
                270878EF13282104006D4EE3 /* NSObject+NSCoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+NSCoding.h"; sourceTree = "<group>"; };
                271EFE59127E840900431D19 /* ChefAddServerPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChefAddServerPlugin.m; sourceTree = "<group>"; };
                271EFE92127E884000431D19 /* AddServerPluginHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddServerPluginHandler.h; sourceTree = "<group>"; };
                271EFE93127E884000431D19 /* AddServerPluginHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddServerPluginHandler.m; sourceTree = "<group>"; };
-               27265A191397EAE5000617C0 /* LBAlgorithmAnimationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = LBAlgorithmAnimationViewController.h; sourceTree = "<group>"; };
+               27265A191397EAE5000617C0 /* LBAlgorithmAnimationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LBAlgorithmAnimationViewController.h; sourceTree = "<group>"; };
                27265A1A1397EAE5000617C0 /* LBAlgorithmAnimationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LBAlgorithmAnimationViewController.m; sourceTree = "<group>"; };
                27265A1B1397EAE5000617C0 /* LBAlgorithmAnimationViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LBAlgorithmAnimationViewController.xib; sourceTree = "<group>"; };
                27265ADF139802B6000617C0 /* lc-01_s01.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "lc-01_s01.png"; sourceTree = "<group>"; };
                2726C63613B26D9F00BC7330 /* dot-orange@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dot-orange@2x.png"; sourceTree = "<group>"; };
                2726C63713B26D9F00BC7330 /* dot-blue@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dot-blue@2x.png"; sourceTree = "<group>"; };
                2726C63813B26D9F00BC7330 /* dot-green@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dot-green@2x.png"; sourceTree = "<group>"; };
-               2726C64A13B28D1000BC7330 /* ConfigureLoadBalancerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ConfigureLoadBalancerViewController.h; sourceTree = "<group>"; };
+               2726C64A13B28D1000BC7330 /* ConfigureLoadBalancerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigureLoadBalancerViewController.h; sourceTree = "<group>"; };
                2726C64B13B28D1000BC7330 /* ConfigureLoadBalancerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConfigureLoadBalancerViewController.m; sourceTree = "<group>"; };
                272833A31270C87C00F8CCDD /* OpenStackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenStackViewController.h; sourceTree = "<group>"; };
                272833A41270C87C00F8CCDD /* OpenStackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OpenStackViewController.m; sourceTree = "<group>"; };
                273FF39913313A5300437033 /* ServersOnHostViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServersOnHostViewController.h; sourceTree = "<group>"; };
                273FF39A13313A5300437033 /* ServersOnHostViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ServersOnHostViewController.m; sourceTree = "<group>"; };
                273FF39B13313A5400437033 /* ServersOnHostViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ServersOnHostViewController.xib; sourceTree = "<group>"; };
-               2742A3EA13AFB24D00C10640 /* LBTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = LBTitleView.h; sourceTree = "<group>"; };
+               2742A3EA13AFB24D00C10640 /* LBTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LBTitleView.h; sourceTree = "<group>"; };
                2742A3EB13AFB24D00C10640 /* LBTitleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LBTitleView.m; sourceTree = "<group>"; };
                2748C57C126E90FD00A24941 /* PingIPAddressViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PingIPAddressViewController.h; sourceTree = "<group>"; };
                2748C57D126E90FD00A24941 /* PingIPAddressViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PingIPAddressViewController.m; sourceTree = "<group>"; };
                2756082D136F3078009EB5BA /* LBVirtualIPTypeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LBVirtualIPTypeViewController.h; sourceTree = "<group>"; };
                2756082E136F3078009EB5BA /* LBVirtualIPTypeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LBVirtualIPTypeViewController.m; sourceTree = "<group>"; };
                2756082F136F3078009EB5BA /* LBVirtualIPTypeViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LBVirtualIPTypeViewController.xib; sourceTree = "<group>"; };
-               2758565F1399585000311115 /* LBLinkSharedVIPViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = LBLinkSharedVIPViewController.h; sourceTree = "<group>"; };
+               2758565F1399585000311115 /* LBLinkSharedVIPViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LBLinkSharedVIPViewController.h; sourceTree = "<group>"; };
                275856601399585000311115 /* LBLinkSharedVIPViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LBLinkSharedVIPViewController.m; sourceTree = "<group>"; };
                275856611399585000311115 /* LBLinkSharedVIPViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LBLinkSharedVIPViewController.xib; sourceTree = "<group>"; };
                275B421112DBAE86003DF100 /* MainWindow-iPad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "MainWindow-iPad.xib"; path = "Resources-iPad/MainWindow-iPad.xib"; sourceTree = "<group>"; };
                27D5E222130F032400404EC2 /* LoadBalancersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoadBalancersViewController.h; sourceTree = "<group>"; };
                27D5E223130F032400404EC2 /* LoadBalancersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoadBalancersViewController.m; sourceTree = "<group>"; };
                27D5E224130F032400404EC2 /* LoadBalancersViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LoadBalancersViewController.xib; sourceTree = "<group>"; };
-               27DADC1213B94D6B00F9E906 /* LBNodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = LBNodeViewController.h; sourceTree = "<group>"; };
+               27DADC1213B94D6B00F9E906 /* LBNodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LBNodeViewController.h; sourceTree = "<group>"; };
                27DADC1313B94D6B00F9E906 /* LBNodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LBNodeViewController.m; sourceTree = "<group>"; };
-               27DC5DD513CB8F0F00091902 /* LoadBalancerConnectionThrottle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = LoadBalancerConnectionThrottle.h; sourceTree = "<group>"; };
+               27DC5DD513CB8F0F00091902 /* LoadBalancerConnectionThrottle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoadBalancerConnectionThrottle.h; sourceTree = "<group>"; };
                27DC5DD613CB8F0F00091902 /* LoadBalancerConnectionThrottle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoadBalancerConnectionThrottle.m; sourceTree = "<group>"; };
                27E6B5D412D9211C00EA623F /* MediaViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaViewController.h; sourceTree = "<group>"; };
                27E6B5D512D9211C00EA623F /* MediaViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MediaViewController.m; sourceTree = "<group>"; };
                27F740E6125FB44F002C6CB0 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
                27F740F9125FB46A002C6CB0 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
                27F740FD125FB476002C6CB0 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
-               27F74158125FB66F002C6CB0 /* libz.1.2.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.1.2.3.dylib; path = usr/lib/libz.1.2.3.dylib; sourceTree = SDKROOT; };
                27F744591261130F002C6CB0 /* UIViewController+Conveniences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Conveniences.h"; sourceTree = "<group>"; };
                27F7445A1261130F002C6CB0 /* UIViewController+Conveniences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Conveniences.m"; sourceTree = "<group>"; };
                27F7446012611341002C6CB0 /* NSObject+Conveniences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+Conveniences.h"; sourceTree = "<group>"; };
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               24E040051442FB3C0065E8DB /* libz.dylib in Frameworks */,
                                7F8F374913859F190034710B /* libxml2.dylib in Frameworks */,
                                7F9351B913845AC80097720F /* libsqlite3.0.dylib in Frameworks */,
                                27F740B6125FB358002C6CB0 /* Foundation.framework in Frameworks */,
                                27F740E7125FB44F002C6CB0 /* CFNetwork.framework in Frameworks */,
                                27F740FA125FB46A002C6CB0 /* MobileCoreServices.framework in Frameworks */,
                                27F740FE125FB476002C6CB0 /* SystemConfiguration.framework in Frameworks */,
-                               27F74159125FB66F002C6CB0 /* libz.1.2.3.dylib in Frameworks */,
                                27869AF41263F22300CE3435 /* MessageUI.framework in Frameworks */,
                                27B0A1D91278047900A31CED /* QuartzCore.framework in Frameworks */,
                                27C0510F12B00D3F00CB21C7 /* UIKit.framework in Frameworks */,
                                277D9E7B126E78FD002C69D9 /* CFNetwork.framework in Frameworks */,
                                277D9E7C126E78FD002C69D9 /* MobileCoreServices.framework in Frameworks */,
                                277D9E7D126E78FD002C69D9 /* SystemConfiguration.framework in Frameworks */,
-                               277D9E7E126E78FD002C69D9 /* libz.1.2.3.dylib in Frameworks */,
                                277D9E7F126E78FD002C69D9 /* MessageUI.framework in Frameworks */,
                                271B09331279184D00525011 /* QuartzCore.framework in Frameworks */,
                                27F853EA12AEFAE4003327C0 /* UIKit.framework in Frameworks */,
                                27C15CAF13A7C4D500A1BB9F /* CFNetwork.framework in Frameworks */,
                                27C15CB013A7C4D800A1BB9F /* MobileCoreServices.framework in Frameworks */,
                                27C15CB113A7C4DB00A1BB9F /* SystemConfiguration.framework in Frameworks */,
-                               27C15CB213A7C4EA00A1BB9F /* libz.1.2.3.dylib in Frameworks */,
                                27C15CB313A7C4EC00A1BB9F /* MessageUI.framework in Frameworks */,
                                27C15CB413A7C4EF00A1BB9F /* QuartzCore.framework in Frameworks */,
                                27C15CB613A7C50600A1BB9F /* libobjc.A.dylib in Frameworks */,
                                27F852F112AEF6AB003327C0 /* ContainersViewController.xib */,
                                27C0517512B0134F00CB21C7 /* AddContainerViewController.h */,
                                27C0517612B0134F00CB21C7 /* AddContainerViewController.m */,
+                               27C0517712B0134F00CB21C7 /* AddContainerViewController.xib */,
                                277B89B212B97D6D006483B0 /* FolderViewController.h */,
                                277B89B312B97D6D006483B0 /* FolderViewController.m */,
                                277B89B412B97D6D006483B0 /* FolderViewController.xib */,
                                278906DB12BEDAB5007112B6 /* StorageObjectViewController.h */,
                                278907A112BEF72C007112B6 /* StorageObjectViewController.m */,
                                278906DD12BEDAB5007112B6 /* StorageObjectViewController.xib */,
+                               24A18E1F143DB86F003232F1 /* EditMetadataViewController.h */,
+                               24A18E20143DB870003232F1 /* EditMetadataViewController.m */,
+                               24A18E21143DB870003232F1 /* EditMetadataViewController.xib */,
                                27E6B5D412D9211C00EA623F /* MediaViewController.h */,
                                27E6B5D512D9211C00EA623F /* MediaViewController.m */,
                                27E6B5D612D9211C00EA623F /* MediaViewController.xib */,
+                               24A18E53143F0427003232F1 /* FolderDetailViewController.h */,
+                               24A18E54143F0428003232F1 /* FolderDetailViewController.m */,
+                               24A18E55143F0428003232F1 /* FolderDetailViewController.xib */,
                                2788941712C28C66006448E2 /* ContainerDetailViewController.h */,
                                2788941812C28C66006448E2 /* ContainerDetailViewController.m */,
                                2788941912C28C66006448E2 /* ContainerDetailViewController.xib */,
-                               27C0517712B0134F00CB21C7 /* AddContainerViewController.xib */,
                        );
                        name = "Object Storage";
                        sourceTree = "<group>";
                                27F740E6125FB44F002C6CB0 /* CFNetwork.framework */,
                                27F740F9125FB46A002C6CB0 /* MobileCoreServices.framework */,
                                27F740FD125FB476002C6CB0 /* SystemConfiguration.framework */,
-                               27F74158125FB66F002C6CB0 /* libz.1.2.3.dylib */,
+                               24E040041442FB3C0065E8DB /* libz.dylib */,
                                27869AF31263F22300CE3435 /* MessageUI.framework */,
                                27B0A1D81278047900A31CED /* QuartzCore.framework */,
                                27F853E912AEFAE4003327C0 /* UIKit.framework */,
                                27295C1313C6563200A32F20 /* dark-grey-highlight.png in Resources */,
                                27C38BC013D0D48500CC7222 /* red-delete-button@2x.png in Resources */,
                                27C38BC213D0D48500CC7222 /* red-delete-button.png in Resources */,
+                               24A18E23143DB870003232F1 /* EditMetadataViewController.xib in Resources */,
+                               24A18E57143F0429003232F1 /* FolderDetailViewController.xib in Resources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                2726C64E13B28D1000BC7330 /* ConfigureLoadBalancerViewController.m in Sources */,
                                27DADC1613B94D6C00F9E906 /* LBNodeViewController.m in Sources */,
                                27DC5DD813CB8F0F00091902 /* LoadBalancerConnectionThrottle.m in Sources */,
+                               24A18E22143DB870003232F1 /* EditMetadataViewController.m in Sources */,
+                               24A18E56143F0429003232F1 /* FolderDetailViewController.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };