@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;
#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];
}
#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) {
--- /dev/null
+//
+// 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
--- /dev/null
+//
+// 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
--- /dev/null
+<?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>
@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
@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];
}
- (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;
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] == '/') {
@interface PithosUtilities : NSObject
+ (BOOL)isContentTypeDirectory:(NSString *)contentType;
++ (unsigned long long)sizeOfDirectory:(NSString *)directoryPath;
++ (NSString *)humanReadableSize:(unsigned long long)bytes;
@end
[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
#import <UIKit/UIKit.h>
@interface SettingsViewController : UITableViewController {
+ NSInteger cacheSection;
NSInteger aboutSection;
}
#import "SettingsPluginHandler.h"
#import "SettingsPlugin.h"
#import "AboutViewController.h"
+#import "OpenStackAppDelegate.h"
+#import "PithosUtilities.h"
#define kPasscodeLock 0
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];
#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 {
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
//return 2 + [[SettingsPluginHandler plugins] count];
- return 2;
+ return 3;
}
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];
}
[self.navigationController pushViewController:vc animated:YES];
[vc refreshButtonPressed:nil];
[vc release];
- [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
+ [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (NSString *)humanizedBytes;
+ (StorageObject *)fromJSON:(NSDictionary *)dict;
+- (void)setPropertiesfromResponseHeaders:(NSDictionary *)headers;
- (BOOL)isPlayableMedia;
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
#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;
@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;
#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
@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];
}
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;
};