Change download file to directly show open/email as attachment. Fix bugs and improve...
authorMiltiadis Vasilakis <mvasilak@gmail.com>
Mon, 16 Jan 2012 17:54:56 +0000 (19:54 +0200)
committerMiltiadis Vasilakis <mvasilak@gmail.com>
Mon, 16 Jan 2012 17:54:56 +0000 (19:54 +0200)
20 files changed:
Classes/AccountManager.h
Classes/AccountManager.m
Classes/FolderViewController.m
Classes/ObjectVersionsViewController.h [new file with mode: 0644]
Classes/ObjectVersionsViewController.m [new file with mode: 0644]
Classes/ObjectVersionsViewController.xib [new file with mode: 0644]
Classes/OpenStackAppDelegate.h
Classes/OpenStackAppDelegate.m
Classes/OpenStackRequest.h
Classes/OpenStackRequest.m
Classes/PithosUtilities.h
Classes/PithosUtilities.m
Classes/SettingsViewController.h
Classes/SettingsViewController.m
Classes/SharingAccountsViewController.m
Classes/StorageObject.h
Classes/StorageObject.m
Classes/StorageObjectViewController.h
Classes/StorageObjectViewController.m
OpenStack.xcodeproj/project.pbxproj

index a60e1b7..42b170b 100755 (executable)
 @interface AccountManager : NSObject {
     OpenStackAccount *account;
     ASINetworkQueue *queue;
+    NSMutableDictionary *objectDownloadRequests;
 }
 
 @property (nonatomic, retain) ASINetworkQueue *queue;
 
 @property (nonatomic, assign) OpenStackAccount *account;
+@property (nonatomic, retain) NSMutableDictionary *objectDownloadRequests;
 
 - (NSString *)notificationName:(NSString *)key identifier:(NSString *)identifier;
 - (void)notify:(NSString *)name request:(OpenStackRequest *)request;
 - (void)getObjects:(Container *)container;
 - (void)getObjects:(Container *)container afterMarker:(NSString *)marker objectsBuffer:(NSMutableDictionary *)objectsBuffer;
 - (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object;
+- (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object version:(NSString *)version;
+- (APICallback *)getObjectVersionsList:(Container *)container object:(StorageObject *)object;
 - (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate;
+
+- (APICallback *)getObject:(Container *)container
+                    object:(StorageObject *)object
+  downloadProgressDelegate:(id)downloadProgressDelegate
+           requestUserInfo:(NSDictionary *)requestUserInfo;
+
+- (APICallback *)getObject:(Container *)container
+                    object:(StorageObject *)object
+  downloadProgressDelegate:(id)downloadProgressDelegate 
+           requestUserInfo:(NSDictionary *)requestUserInfo
+                   version:(NSString *)version;
+
+
 - (APICallback *)writeObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate;
 - (APICallback *)writeObjectMetadata:(Container *)container object:(StorageObject *)object;
 - (APICallback *)deleteObject:(Container *)container object:(StorageObject *)object;
index 6f3f2c7..2e92b3e 100755 (executable)
 #import "APICallback.h"
 #import "Analytics.h"
 #import "JSON.h"
+#import "OpenStackAppDelegate.h"
 
 
 
 
 @implementation AccountManager
 
-@synthesize account, queue;
+@synthesize account, queue, objectDownloadRequests;
 
 #pragma mark - Callbacks
 
     return [self callbackWithRequest:request];
 }
 
+- (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object version:(NSString *)version {
+    if (!version)
+        return [self getObjectInfo:container object:object];
+    
+    __block OpenStackRequest *request = [OpenStackRequest getObjectInfoRequest:self.account container:container object:object version:version];
+    return [self callbackWithRequest:request];
+}
+
+- (APICallback *)getObjectVersionsList:(Container *)container object:(StorageObject *)object {
+    __block OpenStackRequest *request = [OpenStackRequest getObjectVersionsRequest:account container:container object:object];
+    return [self callbackWithRequest:request];
+}
+
 - (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate {
-    __block OpenStackRequest *request = [OpenStackRequest getObjectRequest:self.account container:container object:object];
+    return [self getObject:container object:object downloadProgressDelegate:downloadProgressDelegate requestUserInfo:nil];
+}
+
+- (APICallback *)getObject:(Container *)container
+                    object:(StorageObject *)object
+  downloadProgressDelegate:(id)downloadProgressDelegate
+           requestUserInfo:(NSDictionary *)requestUserInfo {
+    
+    return [self getObject:container object:object downloadProgressDelegate:downloadProgressDelegate requestUserInfo:requestUserInfo version:nil];
+}
+
+
+- (APICallback *)getObject:(Container *)container
+                    object:(StorageObject *)object
+  downloadProgressDelegate:(id)downloadProgressDelegate 
+           requestUserInfo:(NSDictionary *)requestUserInfo
+                   version:(NSString *)version{
+    __block OpenStackRequest *request = [OpenStackRequest getObjectRequest:self.account container:container object:object version:version];
     request.delegate = self;
     request.downloadProgressDelegate = downloadProgressDelegate;
-    request.showAccurateProgress = YES;    
+    request.showAccurateProgress = YES;  
+    if (requestUserInfo) {
+        request.userInfo = requestUserInfo;
+    }
+    if (!objectDownloadRequests)
+        self.objectDownloadRequests = [NSMutableDictionary dictionary];
+    [objectDownloadRequests setObject:request forKey:object.fullPath];
     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
         
-        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
-        NSString *documentsDirectory = [paths objectAtIndex:0];        
-        NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", container.name, object.fullPath];
-        NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
-        NSString *directoryPath = [filePath stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"/%@", object.name] withString:@""];
-        
-        NSFileManager *fileManager = [NSFileManager defaultManager];
-        if ([fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:nil]) {
-            
-            [[request responseData] writeToFile:filePath atomically:YES];
-            
+        OpenStackAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
+        NSString *filePath = [appDelegate.cacheDirectoryPath stringByAppendingFormat:@"/%@.%@",object.hash, object.name.pathExtension];
+        [[request responseData] writeToFile:filePath atomically:YES];
+        @synchronized(appDelegate.cachedObjectsDictionary) {
+            [appDelegate.cachedObjectsDictionary setObject:filePath forKey:object.hash];
+            [appDelegate saveCacheDictionary];
         }
-
+        [objectDownloadRequests removeObjectForKey:object.fullPath];
+    }
+    failure:^(OpenStackRequest *request) { 
+        [objectDownloadRequests removeObjectForKey:object.fullPath];
     }];
 }
 
 #pragma mark Memory Management
 
 - (void)dealloc {
+    [objectDownloadRequests release];
     [super dealloc];
 }
 
index d6a3641..b57c9ea 100755 (executable)
@@ -26,6 +26,7 @@
 #import "OpenStackRequest.h"
 #import "JSON.h"
 #import "APICallback.h"
+#import "OpenStackAppDelegate.h"
 
 
 @implementation FolderViewController
             cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
             
             StorageObject *object = item;
-            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
-            NSString *documentsDirectory = [paths objectAtIndex:0];
-            NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, object.fullPath];
-            NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
-
+            OpenStackAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
+            NSString *filePath = [appDelegate.cachedObjectsDictionary objectForKey:object.hash];
             if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
                 UIDocumentInteractionController *udic = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
                 if ([udic.icons count] > 0) {
diff --git a/Classes/ObjectVersionsViewController.h b/Classes/ObjectVersionsViewController.h
new file mode 100644 (file)
index 0000000..009fa0d
--- /dev/null
@@ -0,0 +1,60 @@
+//
+//  ObjectVersionsViewController.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"
+
+@class OpenStackAccount, Container, StorageObject;
+
+@interface ObjectVersionsViewController : OpenStackViewController <UITableViewDelegate, UITableViewDataSource> {
+    
+    IBOutlet UITableView *tableView;
+    NSMutableArray *versions;
+    OpenStackAccount *account;
+    Container *container;
+    StorageObject *object;
+    BOOL versionsLoaded;
+    
+}
+
+@property (nonatomic, retain) IBOutlet UITableView *tableView;
+@property (nonatomic, retain) NSMutableArray *versions;
+@property (nonatomic, retain) OpenStackAccount *account;
+@property (nonatomic, retain) Container *container;
+@property (nonatomic, retain) StorageObject *object;
+
+@end
diff --git a/Classes/ObjectVersionsViewController.m b/Classes/ObjectVersionsViewController.m
new file mode 100644 (file)
index 0000000..4623d65
--- /dev/null
@@ -0,0 +1,172 @@
+//
+//  ObjectVersionsViewController.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 "ObjectVersionsViewController.h"
+#import "StorageObjectViewController.h"
+#import "OpenStackAccount.h"
+#import "OpenStackRequest.h"
+#import "AccountManager.h"
+#import "APICallback.h"
+#import "JSON.h"
+#import "ActivityIndicatorView.h"
+#import "UIViewController+Conveniences.h"
+#import "StorageObject.h"
+
+
+@implementation ObjectVersionsViewController
+
+@synthesize tableView, versions, account, container, object;
+
+#pragma mark - View lifecycle
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    self.navigationItem.title = @"Versions";
+    self.versions = [NSMutableArray array];
+    versionsLoaded = NO;
+    NSString *activityMessage = @"Loading..";
+    ActivityIndicatorView *activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
+    [activityIndicatorView addToView:self.view];
+
+    [[self.account.manager  getObjectVersionsList:container object:object]
+     success:^(OpenStackRequest *request) {
+         SBJSON *parser = [[SBJSON alloc] init];
+         NSArray *versionsInJson = [[parser objectWithString:[request responseString]] objectForKey:@"versions"];
+         for (NSArray *versionInfo in versionsInJson) {
+             [versions addObject:[NSDictionary dictionaryWithObjectsAndKeys:[versionInfo objectAtIndex:0], @"versionID",
+                                 [NSDate dateWithTimeIntervalSince1970:[[versionInfo objectAtIndex:1] doubleValue]], @"versionDate",
+                                 nil]]; 
+         }
+         [activityIndicatorView removeFromSuperviewAndRelease];
+         versionsLoaded = YES;
+         [self.tableView reloadData];
+     }
+     failure:^(OpenStackRequest *request) {
+         [activityIndicatorView removeFromSuperviewAndRelease];
+         [self alert:@"Couldn't get versions from server" request:request];
+     }];    
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
+    return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
+}
+
+#pragma mark - memory management
+
+- (void)dealloc {
+    [account release];
+    [container release];
+    [object release];
+    [versions release];
+    [super dealloc];
+}
+
+
+#pragma mark - Table view data source
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+    return 1;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+    if (!versionsLoaded)
+        return 0;
+    else
+        return MAX(1, versions.count);
+}
+
+- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    static NSString *CellIdentifier = @"Cell";
+    
+    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
+    if (cell == nil) {
+        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
+    }
+    cell.userInteractionEnabled = YES;
+    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+    cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+    if (versions.count == 0 || !versions) {
+        cell.textLabel.text = @"No previous versions available";
+        cell.userInteractionEnabled = NO;
+        cell.accessoryType = UITableViewCellAccessoryNone;
+        cell.selectionStyle = UITableViewCellSelectionStyleNone;
+    } else {
+        NSDictionary *versionDetails = [versions objectAtIndex:indexPath.row];
+        cell.textLabel.text = [[versionDetails objectForKey:@"versionID"] description];
+        NSString *dateString = [NSDateFormatter localizedStringFromDate:[versionDetails objectForKey:@"versionDate"]
+                                                              dateStyle:NSDateFormatterMediumStyle
+                                                              timeStyle:NSDateFormatterMediumStyle];
+        cell.detailTextLabel.text = dateString;
+    }
+    
+    return cell;
+}
+
+#pragma mark - Table view delegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+    NSString *activityMessage = @"Loading..";
+    ActivityIndicatorView *activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
+    [activityIndicatorView addToView:self.view];
+    
+    NSDictionary *versionDetails = [versions objectAtIndex:indexPath.row];
+    NSString *versionID = [[versionDetails objectForKey:@"versionID"] description];
+    
+    [[self.account.manager getObjectInfo:container object:object version:versionID]
+     success:^(OpenStackRequest *request) {
+         [activityIndicatorView removeFromSuperviewAndRelease];
+         StorageObject *versionedObject = [[[StorageObject alloc] init] autorelease];
+         versionedObject.name = object.name;
+         versionedObject.fullPath = object.fullPath;
+         [versionedObject setPropertiesfromResponseHeaders:request.responseHeaders];
+         StorageObjectViewController *vc = [[StorageObjectViewController alloc] initWithNibName:@"StorageObjectViewController" bundle:nil];
+         vc.objectIsReadOnly = YES;
+         vc.account = account;
+         vc.container = container;
+         vc.object = versionedObject;
+         vc.versionID = versionID;
+         [self.navigationController pushViewController:vc animated:YES];
+         [vc release];
+         [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+     } 
+     failure:^(OpenStackRequest *request) {
+         [activityIndicatorView removeFromSuperviewAndRelease];
+         [self alert:[NSString stringWithFormat:@"Failed to get file info for version: %@", versionID] request:request];
+     }];
+}
+
+@end
diff --git a/Classes/ObjectVersionsViewController.xib b/Classes/ObjectVersionsViewController.xib
new file mode 100644 (file)
index 0000000..e25579f
--- /dev/null
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
+       <data>
+               <int key="IBDocument.SystemTarget">1280</int>
+               <string key="IBDocument.SystemVersion">11C74</string>
+               <string key="IBDocument.InterfaceBuilderVersion">1938</string>
+               <string key="IBDocument.AppKitVersion">1138.23</string>
+               <string key="IBDocument.HIToolboxVersion">567.00</string>
+               <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+                       <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+                       <string key="NS.object.0">933</string>
+               </object>
+               <array key="IBDocument.IntegratedClassDependencies">
+                       <string>IBProxyObject</string>
+                       <string>IBUIView</string>
+                       <string>IBUITableView</string>
+               </array>
+               <array key="IBDocument.PluginDependencies">
+                       <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+               </array>
+               <object class="NSMutableDictionary" key="IBDocument.Metadata">
+                       <string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+                       <integer value="1" key="NS.object.0"/>
+               </object>
+               <array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+                       <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="IBUIView" id="191373211">
+                               <reference key="NSNextResponder"/>
+                               <int key="NSvFlags">274</int>
+                               <array class="NSMutableArray" key="NSSubviews">
+                                       <object class="IBUITableView" id="360754307">
+                                               <reference key="NSNextResponder" ref="191373211"/>
+                                               <int key="NSvFlags">274</int>
+                                               <string key="NSFrameSize">{320, 460}</string>
+                                               <reference key="NSSuperview" ref="191373211"/>
+                                               <string key="NSReuseIdentifierKey">_NS:418</string>
+                                               <object class="NSColor" key="IBUIBackgroundColor">
+                                                       <int key="NSColorSpace">3</int>
+                                                       <bytes key="NSWhite">MQA</bytes>
+                                               </object>
+                                               <bool key="IBUIClipsSubviews">YES</bool>
+                                               <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+                                               <bool key="IBUIAlwaysBounceVertical">YES</bool>
+                                               <int key="IBUISeparatorStyle">1</int>
+                                               <int key="IBUISectionIndexMinimumDisplayRowCount">0</int>
+                                               <bool key="IBUIShowsSelectionImmediatelyOnTouchBegin">YES</bool>
+                                               <float key="IBUIRowHeight">44</float>
+                                               <float key="IBUISectionHeaderHeight">22</float>
+                                               <float key="IBUISectionFooterHeight">22</float>
+                                       </object>
+                               </array>
+                               <string key="NSFrame">{{0, 20}, {320, 460}}</string>
+                               <reference key="NSSuperview"/>
+                               <reference key="NSNextKeyView"/>
+                               <object class="NSColor" key="IBUIBackgroundColor">
+                                       <int key="NSColorSpace">3</int>
+                                       <bytes key="NSWhite">MQA</bytes>
+                                       <object class="NSColorSpace" key="NSCustomColorSpace">
+                                               <int key="NSID">2</int>
+                                       </object>
+                               </object>
+                               <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+                               <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+                       </object>
+               </array>
+               <object class="IBObjectContainer" key="IBDocument.Objects">
+                       <array class="NSMutableArray" key="connectionRecords">
+                               <object class="IBConnectionRecord">
+                                       <object class="IBCocoaTouchOutletConnection" key="connection">
+                                               <string key="label">view</string>
+                                               <reference key="source" ref="372490531"/>
+                                               <reference key="destination" ref="191373211"/>
+                                       </object>
+                                       <int key="connectionID">3</int>
+                               </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBCocoaTouchOutletConnection" key="connection">
+                                               <string key="label">tableView</string>
+                                               <reference key="source" ref="372490531"/>
+                                               <reference key="destination" ref="360754307"/>
+                                       </object>
+                                       <int key="connectionID">5</int>
+                               </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBCocoaTouchOutletConnection" key="connection">
+                                               <string key="label">dataSource</string>
+                                               <reference key="source" ref="360754307"/>
+                                               <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="360754307"/>
+                                               <reference key="destination" ref="372490531"/>
+                                       </object>
+                                       <int key="connectionID">7</int>
+                               </object>
+                       </array>
+                       <object class="IBMutableOrderedSet" key="objectRecords">
+                               <array key="orderedObjects">
+                                       <object class="IBObjectRecord">
+                                               <int key="objectID">0</int>
+                                               <array key="object" id="0"/>
+                                               <reference key="children" ref="1000"/>
+                                               <nil key="parent"/>
+                                       </object>
+                                       <object class="IBObjectRecord">
+                                               <int key="objectID">1</int>
+                                               <reference key="object" ref="191373211"/>
+                                               <array class="NSMutableArray" key="children">
+                                                       <reference ref="360754307"/>
+                                               </array>
+                                               <reference key="parent" ref="0"/>
+                                       </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="360754307"/>
+                                               <reference key="parent" ref="191373211"/>
+                                       </object>
+                               </array>
+                       </object>
+                       <dictionary class="NSMutableDictionary" key="flattenedProperties">
+                               <string key="-1.CustomClassName">ObjectVersionsViewController</string>
+                               <string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+                               <string key="-2.CustomClassName">UIResponder</string>
+                               <string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+                               <string key="1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+                               <string key="4.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+                       </dictionary>
+                       <dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
+                       <nil key="activeLocalization"/>
+                       <dictionary class="NSMutableDictionary" key="localizations"/>
+                       <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>
+               <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+               <int key="IBDocument.defaultPropertyAccessControl">3</int>
+               <string key="IBCocoaTouchPluginVersion">933</string>
+       </data>
+</archive>
index 4387a07..4222834 100755 (executable)
@@ -10,6 +10,9 @@
 
 @class RootViewController;
 
+#define CACHED_OBJECTS_ARCHIVE_KEY @"cachedObjectsDictionary"
+#define CACHE_DICT_ARCHIVE_NAME @"cache.dict"
+
 @interface OpenStackAppDelegate : NSObject <UIApplicationDelegate> {
     UIWindow *window;
     UINavigationController *navigationController;
     UIBarButtonItem *barButtonItem;
     RootViewController *rootViewController;
     id serviceUnavailableObserver;
+    NSMutableDictionary *cachedObjectsDictionary;
+    NSString *cacheDictionaryFilePath;
+    NSString *cacheDirectoryPath;
     
+    NSUserDefaults *userDefaults;
 }
 
 @property (nonatomic, retain) IBOutlet UIWindow *window;
 @property (nonatomic, retain) UINavigationController *masterNavigationController;
 @property (nonatomic, retain) UIBarButtonItem *barButtonItem;
 @property (nonatomic, retain) RootViewController *rootViewController;
+@property (nonatomic, retain) NSMutableDictionary *cachedObjectsDictionary;
+@property (nonatomic, copy) NSString *cacheDictionaryFilePath;
+@property (nonatomic, copy) NSString *cacheDirectoryPath;
 
 //- (void) setupDependencies;
+- (void)saveCacheDictionary;
 
 @end
 
index dac75d6..34e57c2 100755 (executable)
@@ -39,6 +39,9 @@
 @synthesize masterNavigationController;
 @synthesize barButtonItem;
 @synthesize rootViewController;
+@synthesize cachedObjectsDictionary;
+@synthesize cacheDictionaryFilePath;
+@synthesize cacheDirectoryPath;
 
 - (void)loadSettingsDefaults {
     
         [Keychain setString:@"NO" forKey:@"passcode_lock_erase_data_on"];
     }
     
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    userDefaults = [[NSUserDefaults standardUserDefaults] retain];
     
-    if (![defaults stringForKey:@"api_logging_level"]) {
-        [defaults setValue:@"all" forKey:@"api_logging_level"];
+    if (![userDefaults stringForKey:@"api_logging_level"]) {
+        [userDefaults setValue:@"all" forKey:@"api_logging_level"];
     }
     
-    if (![defaults stringForKey:@"aboutURL"]) {
-        [defaults setValue:@"https://plus.pithos.grnet.gr" forKey:@"aboutURL"];
-    } 
+    if (![userDefaults stringForKey:@"aboutURL"]) {
+        [userDefaults setValue:@"https://plus.pithos.grnet.gr" forKey:@"aboutURL"];
+    }
+    
+    self.cachedObjectsDictionary = [userDefaults objectForKey:@"cachedObjectsDictionary"];
+    if (!cachedObjectsDictionary) {
+        self.cachedObjectsDictionary = [NSMutableDictionary dictionary];
+        [userDefaults setValue:cachedObjectsDictionary forKey:@"cachedObjectsDictionary"];
+    }
     
-    [defaults synchronize];
+    [userDefaults synchronize];
     
 }
 
         [alert release];
         [[NSNotificationCenter defaultCenter] removeObserver:serviceUnavailableObserver];
     }];
-        
+    
+    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+    self.cacheDirectoryPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"DownloadedFiles"];
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    NSError *error = nil;
+    [fileManager createDirectoryAtPath:cacheDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error];
+    if (error) {
+        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
+                                                        message:[NSString stringWithFormat:@"Error in creating cache directory\n%@",error.localizedDescription]
+                                                       delegate:nil 
+                                              cancelButtonTitle:@"OK" otherButtonTitles:nil];
+        [alert show];
+        [alert release];
+    }
+
     return YES;
 }
 
 
 
 - (void)applicationDidBecomeActive:(UIApplication *)application {
-    /*
-     Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
-     */
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-    [defaults setBool:NO forKey:@"already_failed_on_connection"];
-    [defaults synchronize];
+    
+    /* 
+        Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+       */
+    [userDefaults setBool:NO forKey:@"already_failed_on_connection"];
+    [userDefaults synchronize];
     
     [self showPasscodeLock];
     //DispatchAnalytics();
     return YES;
 }
 
+#pragma mark - persistence
+
+- (void)saveCacheDictionary {
+    [userDefaults setObject:cachedObjectsDictionary forKey:@"cachedObjectsDictionary"];
+    [userDefaults synchronize];
+}
+
 
 #pragma mark -
 #pragma mark Memory management
     [barButtonItem release];
     [rootViewController release];
        [window release];
+    [cachedObjectsDictionary release];
+    [userDefaults release];
        [super dealloc];
 }
 
index 578807f..9800b66 100755 (executable)
 - (NSMutableDictionary *)objects;
 
 + (OpenStackRequest *)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
++ (OpenStackRequest *)getObjectInfoRequest:(OpenStackAccount *)account
+                                 container:(Container *)container
+                                    object:(StorageObject *)object
+                                   version:(NSString *)version;
++ (OpenStackRequest *)getObjectVersionsRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
 + (OpenStackRequest *)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
++ (OpenStackRequest *)getObjectRequest:(OpenStackAccount *)account
+                             container:(Container *)container
+                                object:(StorageObject *)object
+                               version:(NSString *)version;
 + (OpenStackRequest *)writeObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
 + (OpenStackRequest *)writeObjectMetadataRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
 + (OpenStackRequest *)deleteObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object;
index 945f651..1335d22 100755 (executable)
@@ -661,10 +661,37 @@ static NSRecursiveLock *accessDetailsLock = nil;
     return [OpenStackRequest filesRequest:account method:@"HEAD" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];    
 }
 
++ (OpenStackRequest *)getObjectInfoRequest:(OpenStackAccount *)account
+                                 container:(Container *)container
+                                    object:(StorageObject *)object
+                                   version:(NSString *)version {
+    OpenStackRequest *request = [OpenStackRequest getObjectInfoRequest:account container:container object:object];
+    request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=%@",request.url.description, version]];
+    return request;
+}
+
++ (OpenStackRequest *)getObjectVersionsRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
+    OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];    
+    
+    request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=list",request.url.description]];
+    return request;
+}
+
 + (OpenStackRequest *)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
     return [OpenStackRequest filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];    
 }
 
++ (OpenStackRequest *)getObjectRequest:(OpenStackAccount *)account
+                             container:(Container *)container
+                                object:(StorageObject *)object
+                               version:(NSString *)version {
+    OpenStackRequest *request = [OpenStackRequest getObjectRequest:account container:container object:object];
+    if (version) {
+        request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=%@",request.url.description, version]];
+    }
+    return request;
+}
+
 + (OpenStackRequest *)writeObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
     NSString *fullPath = object.fullPath;
     if ([fullPath characterAtIndex:0] == '/') {
index 91ec400..3d2ee95 100644 (file)
@@ -40,5 +40,7 @@
 @interface PithosUtilities : NSObject
 
 + (BOOL)isContentTypeDirectory:(NSString *)contentType;
++ (unsigned long long)sizeOfDirectory:(NSString *)directoryPath;
++ (NSString *)humanReadableSize:(unsigned long long)bytes;
 
 @end
index d3187e3..31b6df4 100644 (file)
             [contentType hasPrefix:@"application/folder;"]);
 }
 
++ (unsigned long long)sizeOfDirectory:(NSString *)directoryPath {
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    NSDirectoryEnumerator *directoryEnumerator = [fileManager enumeratorAtPath:directoryPath];
+    unsigned long long directorySize = 0;
+    NSString *file;
+    while (file = [directoryEnumerator nextObject]) {
+        NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:[NSString stringWithFormat:@"%@/%@",directoryPath,file] error:nil];
+        directorySize += [fileAttributes fileSize];
+    }
+    return directorySize;
+}
+
++ (NSString *)humanReadableSize:(unsigned long long)bytes {
+    if (bytes <= 999) {
+        return [NSString stringWithFormat:@"%d B", (int)bytes];
+    } else if (bytes <= 999000) {
+        return [NSString stringWithFormat:@"%d KB", (int)ceil(bytes / 1000)];
+    } else if (bytes <= 999900000) {
+        double megabytes = floor(bytes / 1000000);
+        double hundredkilobytes = ceil((bytes - megabytes * 1000000) / 100000);
+        if (hundredkilobytes == 10) {
+            megabytes++;
+            hundredkilobytes = 0;
+        }
+        if (hundredkilobytes == 0) {
+            return [NSString stringWithFormat:@"%d MB", (int)megabytes];
+        } else {
+            return [NSString stringWithFormat:@"%d.%d MB", (int)megabytes, (int)hundredkilobytes];
+        }
+    } else {
+        double gigabytes = floor(bytes / 1000000000);
+        double hundredmegabytes = ceil((bytes - gigabytes * 1000000000) / 100000000);
+        if (hundredmegabytes == 10) {
+            gigabytes++;
+            hundredmegabytes = 0;
+        }
+        if (hundredmegabytes == 0) {
+            return [NSString stringWithFormat:@"%d GB", (int)gigabytes];
+        } else {
+            return [NSString stringWithFormat:@"%d.%d GB", (int)gigabytes, (int)hundredmegabytes];
+        }
+    }
+}
+
 @end
index 787dcfb..f4eef57 100755 (executable)
@@ -9,6 +9,7 @@
 #import <UIKit/UIKit.h>
 
 @interface SettingsViewController : UITableViewController {
+    NSInteger cacheSection;
     NSInteger aboutSection;
 }
 
index 71201e1..f54dc88 100755 (executable)
@@ -14,6 +14,8 @@
 #import "SettingsPluginHandler.h"
 #import "SettingsPlugin.h"
 #import "AboutViewController.h"
+#import "OpenStackAppDelegate.h"
+#import "PithosUtilities.h"
 
 #define kPasscodeLock 0
 
@@ -37,7 +39,8 @@
     self.navigationItem.title = @"Settings";
     
     //aboutSection = 1 + [[SettingsPluginHandler plugins] count];
-    aboutSection = 1;
+    cacheSection = 1;
+    aboutSection = 2;
     UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneButtonPressed:)];
     self.navigationItem.rightBarButtonItem = doneButton;
     [doneButton release];
@@ -47,7 +50,7 @@
 #pragma mark Table view data source
 
 - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
-    if (section > kPasscodeLock && section < aboutSection) {
+    if (section > kPasscodeLock && section < cacheSection) {
         id <SettingsPlugin> plugin = [[SettingsPluginHandler plugins] objectAtIndex:section - 1];
         return [plugin tableView:tableView titleForFooterInSection:section];
     } else {
@@ -57,7 +60,7 @@
 
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
     //return 2 + [[SettingsPluginHandler plugins] count];
-    return 2;
+    return 3;
 }
 
 
@@ -86,6 +89,8 @@
     if (cell == nil) {
         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
     }
+    cell.userInteractionEnabled = YES;
+    cell.textLabel.textColor = [UIColor blackColor];
     
     // Configure the cell...
     if (indexPath.section == kPasscodeLock) {
         }
         
         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+    } else if (indexPath.section == cacheSection) {
+        OpenStackAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
+        NSString *cacheSize = [PithosUtilities humanReadableSize:[PithosUtilities sizeOfDirectory:appDelegate.cacheDirectoryPath]];
+        if ([cacheSize hasPrefix:@"0"]) {
+            cell.textLabel.textColor = [UIColor grayColor];
+            cell.userInteractionEnabled = NO;
+        }
+        cell.textLabel.text = [NSString stringWithFormat:@"Clear Cache  (%@)", cacheSize];
+        cell.detailTextLabel.text = @"";
+        cell.accessoryType = UITableViewCellAccessoryNone;
     } else if (indexPath.section == aboutSection) {
         cell.textLabel.text = @"About This App";
         cell.detailTextLabel.text = @"";
         vc.settingsViewController = self;
         [self.navigationController pushViewController:vc animated:YES];
         [vc release];
+    } else if (indexPath.section == cacheSection) {
+        OpenStackAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
+        NSFileManager *fileManager = [NSFileManager defaultManager];
+        NSError *error = nil;
+        NSString *file;
+        NSDirectoryEnumerator *directoryEnumerator = [fileManager enumeratorAtPath:appDelegate.cacheDirectoryPath];
+        @synchronized(appDelegate.cachedObjectsDictionary) {
+            while (file = [directoryEnumerator nextObject]) {
+                [fileManager removeItemAtPath:[NSString stringWithFormat:@"%@/%@",appDelegate.cacheDirectoryPath,file] error:&error];
+                if (error) {
+                    [self alert:@"Error" message:[NSString stringWithFormat:@"Error in removing cached file, %@", error.localizedDescription]];
+                } else {
+                    for (NSString *key in [appDelegate.cachedObjectsDictionary allKeys])
+                        if ([[appDelegate.cachedObjectsDictionary objectForKey:key] isEqualToString:file])
+                            [appDelegate.cachedObjectsDictionary removeObjectForKey:key];
+                }        
+            }
+        }
+        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+        [self.tableView reloadData];
     } else if (indexPath.section == aboutSection) {
         //This currently is a link to the pithos docs page. It might change in the future to use the AboutViewController;
         NSString *aboutURLString = [[NSUserDefaults standardUserDefaults] objectForKey:@"aboutURL"];
         /*AboutViewController *vc = [[AboutViewController alloc] initWithNibName:@"AboutViewController" bundle:nil];
         [self.navigationController pushViewController:vc animated:YES];
         [vc release];*/
-    } else {
+    }  else {
         id <SettingsPlugin> plugin = [[SettingsPluginHandler plugins] objectAtIndex:indexPath.section - 1];
         return [plugin tableView:tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath];
     }
index 298c1bf..bd7548b 100644 (file)
     [self.navigationController pushViewController:vc animated:YES];
     [vc refreshButtonPressed:nil];
     [vc release];
-    [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
+    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
 }
 
 
index 0855847..dd7caa0 100755 (executable)
@@ -35,6 +35,7 @@
 
 - (NSString *)humanizedBytes;
 + (StorageObject *)fromJSON:(NSDictionary *)dict;
+- (void)setPropertiesfromResponseHeaders:(NSDictionary *)headers;
 
 - (BOOL)isPlayableMedia;
 
index 5fa278a..11a6456 100755 (executable)
@@ -58,7 +58,7 @@
     
     object.name = [dict objectForKey:@"name"]; // this may be shortened by folder parsing later
     object.fullPath = [dict objectForKey:@"name"];
-    object.hash = [dict objectForKey:@"hash"];
+    object.hash = [dict objectForKey:@"x_object_hash"];
     object.bytes = [[dict objectForKey:@"bytes"] intValue];
     object.contentType = [dict objectForKey:@"content_type"];
     object.lastModified = [ComputeModel dateFromString:[dict objectForKey:@"last_modified"]];
     return object;
 }
 
+- (void)setPropertiesfromResponseHeaders:(NSDictionary *)headers {
+    self.hash = [headers objectForKey:@"X-Object-Hash"];
+    self.bytes = [[headers objectForKey:@"Content-Length"] intValue];
+    self.contentType = [headers objectForKey:@"Content-Type"];
+    self.lastModified = [headers objectForKey:@"Last-Modified"];
+    self.publicURI = [headers objectForKey:@"X-Object-Public"];
+    self.sharing = [headers objectForKey:@"X-Object-Sharing"];
+    
+    self.metadata = [NSMutableDictionary dictionary];
+    for (NSString *key in headers) {
+        if ([key hasPrefix:@"X-Object-Meta-"]) {
+            NSString *metadataValue = [headers objectForKey:key];
+            NSString *metadataKey = [key substringFromIndex:14];
+            [self.metadata setObject:metadataValue forKey:metadataKey];
+        }
+    }
+}
+
 #pragma mark -
 #pragma mark Presentation
 
index bae5162..3399040 100755 (executable)
@@ -13,6 +13,7 @@
 #import <MessageUI/MessageUI.h>
 #import <MessageUI/MFMailComposeViewController.h>
 #import "ActivityIndicatorView.h"
+#import "OpenStackAppDelegate.h"
 
 @class OpenStackAccount, Container, Folder, StorageObject, AnimatedProgressView, FolderViewController;
 
     NSInteger cdnURLSection;
     NSInteger actionsSection;
     NSInteger deleteSection;
+    NSInteger versionsSection;
+    NSInteger publicLinkSection;
+    NSInteger permissionsSection;
     
     NSString *oldObjectSharingString;
     NSString *oldPublicURI;
     NSMutableDictionary *permissions;
+    NSIndexPath *actionSelectedIndexPath;
+    NSString *versionID;
+    OpenStackAppDelegate *appDelegate;
 }
 
 @property (nonatomic, retain) OpenStackAccount *account;
@@ -55,6 +62,9 @@
 @property (nonatomic, retain) FolderViewController *folderViewController;
     @property (nonatomic, retain) NSString *oldPubicURI;
 @property (nonatomic, retain) UIDocumentInteractionController *documentInteractionController;
+@property (nonatomic, retain) NSIndexPath *actionSelectedIndexPath;
+@property (nonatomic, assign) BOOL objectIsReadOnly;
+@property (nonatomic, retain) NSString *versionID;
 
 - (void)setProgress:(float)newProgress;
 - (IBAction)homeButtonPressed:(id)sender;
index 8fa0d76..41e6485 100755 (executable)
 #import "TextViewCell.h"
 #import "NSString+Conveniences.h"
 #import "APICallback.h"
+#import "ObjectVersionsViewController.h"
 
 #define kDetails 0
 #define kMetadata 1
-#define kPublic 2
-#define kPermissions 3
 
 #define maxMetadataViewableLength 12
 
@@ -59,7 +58,7 @@
 @implementation StorageObjectViewController
 
 @synthesize account, container, folder, object, tableView, folderViewController;
-@synthesize oldPubicURI, documentInteractionController;
+@synthesize oldPubicURI, documentInteractionController, actionSelectedIndexPath, objectIsReadOnly, versionID;
 
 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
     return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
         }
         objectIsPublicSwitch.enabled = NO;
     }
+    
+
+    downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
+    fileDownloading = ([self.account.manager.objectDownloadRequests objectForKey:object.fullPath] != nil) ? YES : NO;
+    if (fileDownloading) {
+        OpenStackRequest *request = [self.account.manager.objectDownloadRequests objectForKey:object.fullPath];
+        self.actionSelectedIndexPath = [request.userInfo objectForKey:@"actionSelectedIndexPath"];
+        [request setDownloadProgressDelegate:self];
+    }
+    appDelegate = [[UIApplication sharedApplication] delegate];
 }
 
 - (void)viewWillAppear:(BOOL)animated {
     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];
-    NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
-    
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    fileDownloaded = [fileManager fileExistsAtPath:filePath];
-    
-    downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
+    if ([appDelegate.cachedObjectsDictionary objectForKey:object.hash] != nil) {
+        if ([[NSFileManager defaultManager] fileExistsAtPath:[appDelegate.cachedObjectsDictionary objectForKey:object.hash]])
+            fileDownloaded = YES;
+        else {
+            fileDownloaded = NO;
+            [appDelegate.cachedObjectsDictionary removeObjectForKey:object.hash];
+            [appDelegate saveCacheDictionary];
+        }
+    } else {
+        fileDownloaded = NO;
+    }
     
     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
         CGRect rect = downloadProgressView.frame;
     }
     
     //[self setBackgroundView];
+    publicLinkSection = 2;
+    permissionsSection = 3;
     if (self.container.cdnEnabled) {
         cdnURLSection = 4;
         actionsSection = 5;
         deleteSection = 6;
+        versionsSection = 7;
     } else {
         cdnURLSection = -1;
         actionsSection = 4;
         deleteSection = 5;
+        versionsSection = 6;
     }
     
-    if (account.sharingAccount || account.shared) {
+    if (versionID) {
+        publicLinkSection = -1;
+        permissionsSection = -1;
+        versionsSection = -1;
         deleteSection = -1;
+        actionsSection = 2;
+        objectIsReadOnly = YES;
     }
+    if (account.sharingAccount || account.shared)
+            deleteSection = -1;
+    
     
     // let's see if we can tweet
     UIApplication *app = [UIApplication sharedApplication];
 #pragma mark Table view data source
 
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
-    if (account.sharingAccount || account.shared)
-        return self.container.cdnEnabled ? 6 : 5;
-    else
-        return self.container.cdnEnabled ? 7 : 6;
+    int numberOfSections = 8;
+    if (!self.container.cdnEnabled)
+        numberOfSections--;
+    if (deleteSection < 0)
+        numberOfSections--;
+    if (versionsSection < 0)
+        numberOfSections--;
+    if (publicLinkSection < 0)
+        numberOfSections--;
+    if (permissionsSection < 0)
+        numberOfSections--;
+
+    return numberOfSections;
 }
 
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
     if (section == kDetails) {
         return 4;
     } else if (section == kMetadata) {
-        if (objectIsReadOnly)
+        if (objectIsReadOnly) {
             return [object.metadata count];
-        else
+        } else {
             return 1 + [object.metadata count];
+        }
     } else if (section == cdnURLSection) {
         return 1;
     } else if (section == actionsSection) {
-        return fileDownloaded ? 2 : 1;
-    } else if (section == kPublic) {
+        return 2;
+    } else if (section == publicLinkSection) {
         return objectIsPublic ? 2 : 1;
-    } else if (section == kPermissions) {
-        if (account.sharingAccount)
+    } else if (section == permissionsSection) {
+        if (account.sharingAccount || objectIsReadOnly)
             return [permissions count];
         else
             return 1 + [permissions count];
         }
         CGSize stringSize = [object.name sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
         return 22.0 + stringSize.height;
-    } else if (indexPath.section == kPublic && indexPath.row == 1) {
+    } else if (indexPath.section == publicLinkSection && indexPath.row == 1) {
         NSString *publicLinkUrl = [NSString stringWithFormat:@"%@%@",
                                    account.pithosPublicLinkURLPrefix,
                                    self.object.publicURI];
     static NSString *CellIdentifier = @"Cell";
     static NSString *TextViewCellIdentifier = @"TextViewCell";
     
-    if (indexPath.section == kPublic) {
+    if (indexPath.section == publicLinkSection) {
         if (indexPath.row == 1) {
             TextViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:TextViewCellIdentifier];
             if (cell == nil) {
         cell.detailTextLabel.numberOfLines = 0;
         cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
     }
-    
+    cell.textLabel.textColor = [UIColor blackColor];
     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
         cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8];
     }
             cell.textLabel.text = metadataKeyCellText;
             cell.detailTextLabel.text = metadataValueCellText;
         }
-    } else if (indexPath.section == kPublic) {
+    } else if (indexPath.section == publicLinkSection) {
         if (indexPath.row == 0) {
             cell.textLabel.text = @"Public URL";        
             cell.detailTextLabel.text = @"";
             cell.accessoryType = UITableViewCellAccessoryNone;
             cell.selectionStyle = UITableViewCellSelectionStyleNone;
         }
-    } else if (indexPath.section == kPermissions) {
+    } else if (indexPath.section == permissionsSection) {
         if (account.sharingAccount) {
             cell.accessoryType = UITableViewCellAccessoryNone;
             cell.selectionStyle = UITableViewCellSelectionStyleNone;
         cell.textLabel.text = @"";
         cell.detailTextLabel.text = [[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
     } else if (indexPath.section == actionsSection) {
-        cell.accessoryView = nil;
-        if (performingAction) {
-            cell.textLabel.textColor = [UIColor grayColor];
-            cell.selectionStyle = UITableViewCellSelectionStyleNone;
-            cell.accessoryType = UITableViewCellAccessoryNone;
+        if (fileDownloading) {
+            if (actionSelectedIndexPath.row == indexPath.row) {
+                cell.accessoryView = downloadProgressView;
+            } else {
+                cell.textLabel.textColor = [UIColor grayColor];
+                cell.selectionStyle = UITableViewCellSelectionStyleNone;
+            }
         } else {
-            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
-            cell.selectionStyle = UITableViewCellSelectionStyleNone;
+            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+            cell.accessoryView = nil;
         }
-
         if (indexPath.row == 0) {
-            if (fileDownloaded) {
-                cell.textLabel.text = @"Open File"; 
-                cell.accessoryType = UITableViewCellAccessoryNone;
-                cell.selectionStyle = UITableViewCellSelectionStyleBlue;
-            } else {
-                if (fileDownloading) {
-                    cell.accessoryView = downloadProgressView;
-                    // TODO: if you leave this view while downloading, there's EXC_BAD_ACCESS
-                    cell.textLabel.text = @"Downloading";
-                } else {
-                    cell.textLabel.text = @"Download File";
-                }
-            }
+            cell.textLabel.text = @"Open File"; 
+            cell.accessoryType = UITableViewCellAccessoryNone;
             cell.detailTextLabel.text = @"";
             
         } else if (indexPath.row == 1) {
             cell.textLabel.text = @"Email File as Attachment";
             cell.detailTextLabel.text = @"";
-            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
-        }
+            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+        }        
     } else if (indexPath.section == deleteSection) {
         cell.selectionStyle = UITableViewCellSelectionStyleBlue;
         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
         cell.textLabel.text = @"Delete Object";
         cell.detailTextLabel.text = @"";
         
+    } else if (indexPath.section == versionsSection) {
+        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+        cell.textLabel.text = @"Versions";
+        cell.detailTextLabel.text = @"";
     }
     
     return cell;
         vc.object = object;
         [self.navigationController pushViewController:vc animated:YES];
         [vc release];
-    } else if (indexPath.section == kPermissions) {
+    } else if (indexPath.section == permissionsSection) {
         EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
         NSString *user;
         
     } else if (indexPath.section == cdnURLSection) {
         [cdnURLActionSheet showInView:self.view];
     } else if (indexPath.section == actionsSection) {
-        if (indexPath.row == 0) {
+        if (!fileDownloaded) {
+            if (!fileDownloading) {
+                // download the file
+                fileDownloading = YES;
+                self.actionSelectedIndexPath = indexPath;
+                [self.tableView reloadData];
+                NSMutableDictionary *requestUserInfo = [NSDictionary dictionaryWithObject:indexPath forKey:@"actionSelectedIndexPath"];
+                [[self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self requestUserInfo:requestUserInfo version:versionID]
+                 success:^(OpenStackRequest *request) {
+                     fileDownloaded = YES;
+                     fileDownloading = NO;
+                     if ([self.navigationController.visibleViewController isEqual:self])
+                         [self.tableView.delegate tableView:self.tableView didSelectRowAtIndexPath:indexPath]; 
+                     [self.tableView reloadData];
+                     [self.folderViewController.tableView reloadData];
+                 }
+                 failure:^(OpenStackRequest *request) {
+                     fileDownloaded = NO;
+                     fileDownloading = NO;
+                     [self alert:@"File failed to download" request:request];
+                     [self.tableView reloadData];
+                 }];
+                [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
+            }
+        } else if (indexPath.row == 0) {        
             if (fileDownloaded) {
-                NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
-                NSString *documentsDirectory = [paths objectAtIndex:0];        
-                NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
-                NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
-                                
+                NSString *filePath = [appDelegate.cachedObjectsDictionary objectForKey:object.hash];                                 
                 self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
                 self.documentInteractionController.delegate = self;
+                self.documentInteractionController.name = object.name;
 
                 UITableViewCell *openFileCell = [self.tableView cellForRowAtIndexPath:indexPath];
                 if (![self.documentInteractionController presentOptionsMenuFromRect:openFileCell.frame inView:self.view animated:YES]) {
                 
                 [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];                
                 
-            } else {                
-                if (!fileDownloading) {
-                    // download the file
-                    fileDownloading = YES;
-                    [self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self];
-                    [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
-                }
-                
-            }
+            } 
         } else if (indexPath.row == 1) {
             
             NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
         }
     } else if (indexPath.section == deleteSection) {
         [deleteActionSheet showInView:self.view];
+    } else if (indexPath.section == versionsSection) {
+        ObjectVersionsViewController *vc = [[ObjectVersionsViewController alloc] initWithNibName:@"ObjectVersionsViewController" bundle:nil];
+        vc.account = account;
+        vc.container = container;
+        vc.object = object;
+        [self.navigationController pushViewController:vc animated:YES];
+        [vc release];
     }
 }
 
     if (newProgress >= 1.0) {
         fileDownloading = NO;
         fileDownloaded = YES;
-        
-        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:1 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationBottom];
-        [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
+        [self.tableView reloadData];
     }
 }
 
     objectIsPublic = !objectIsPublic;
     [[self.account.manager writeObjectMetadata:container object:object] 
      success:^(OpenStackRequest *request) {
-         NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
+         NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
          if (objectIsPublic) {
-             [[self.account.manager getObjectInfo:container object:object] 
+             [[self.account.manager getObjectInfo:container object:object version:versionID] 
               success:^(OpenStackRequest *request) {
                   [activityIndicatorView removeFromSuperviewAndRelease];
                   object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"];
-                  NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kPublic];
+                  NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
                   [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
                                         withRowAnimation:UITableViewRowAnimationBottom];
               }
 #pragma mark Memory management
 
 - (void)dealloc {
+    if (fileDownloading) {
+        OpenStackRequest *request = [self.account.manager.objectDownloadRequests objectForKey:object.fullPath];
+        [request setDownloadProgressDelegate:nil];
+    }
     [account release];
     [downloadProgressView release];
     [deleteActionSheet release];
     [objectIsPublicSwitch release];
     [permissions release];
     [documentInteractionController release];
+    [actionSelectedIndexPath release];
+    [versionID release];
 
     [super dealloc];
 }
index fed3e21..2c7f1fa 100755 (executable)
@@ -28,6 +28,8 @@
                248547AC144C4B8200E48921 /* AccountGroupsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 248547AA144C4B8200E48921 /* AccountGroupsViewController.xib */; };
                248547BB144C614D00E48921 /* EditAccountGroupsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 248547B9144C613700E48921 /* EditAccountGroupsViewController.m */; };
                248547BC144C614D00E48921 /* EditAccountGroupsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 248547BA144C613A00E48921 /* EditAccountGroupsViewController.xib */; };
+               249F004C14BC836A000AB729 /* ObjectVersionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 249F004A14BC8369000AB729 /* ObjectVersionsViewController.m */; };
+               249F004D14BC836A000AB729 /* ObjectVersionsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 249F004B14BC8369000AB729 /* ObjectVersionsViewController.xib */; };
                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 */; };
                248547B8144C613400E48921 /* EditAccountGroupsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditAccountGroupsViewController.h; sourceTree = "<group>"; };
                248547B9144C613700E48921 /* EditAccountGroupsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EditAccountGroupsViewController.m; sourceTree = "<group>"; };
                248547BA144C613A00E48921 /* EditAccountGroupsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EditAccountGroupsViewController.xib; sourceTree = "<group>"; };
+               249F004914BC8369000AB729 /* ObjectVersionsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectVersionsViewController.h; sourceTree = "<group>"; };
+               249F004A14BC8369000AB729 /* ObjectVersionsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjectVersionsViewController.m; sourceTree = "<group>"; };
+               249F004B14BC8369000AB729 /* ObjectVersionsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ObjectVersionsViewController.xib; sourceTree = "<group>"; };
                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>"; };
                                278906DB12BEDAB5007112B6 /* StorageObjectViewController.h */,
                                278907A112BEF72C007112B6 /* StorageObjectViewController.m */,
                                278906DD12BEDAB5007112B6 /* StorageObjectViewController.xib */,
+                               249F004914BC8369000AB729 /* ObjectVersionsViewController.h */,
+                               249F004A14BC8369000AB729 /* ObjectVersionsViewController.m */,
+                               249F004B14BC8369000AB729 /* ObjectVersionsViewController.xib */,
                                248547781446D36700E48921 /* EditPermissionsViewController.h */,
                                248547791446D36800E48921 /* EditPermissionsViewController.m */,
                                2485477A1446D36800E48921 /* EditPermissionsViewController.xib */,
                                24A8518D1488F0100088CD4B /* pithos-logo-large.png in Resources */,
                                61DEA8F714904BC000E0D3AD /* pithos_icon.png in Resources */,
                                61DEA8F814904BC000E0D3AD /* pithos_icon@2x.png in Resources */,
+                               249F004D14BC836A000AB729 /* ObjectVersionsViewController.xib in Resources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                24332D5A1472A35800063C8B /* SharingAccountsViewController.m in Sources */,
                                24C9DE5B1484E3E9001F4DB9 /* PithosUtilities.m in Sources */,
                                376B22B414975DAD00007386 /* LBAlgorithmAnimationViewController.m in Sources */,
+                               249F004C14BC836A000AB729 /* ObjectVersionsViewController.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };