//
-// PithosNode.m
+// PithosObjectNode.m
// pithos-macos
//
-// Copyright 2011 GRNET S.A. All rights reserved.
+// Copyright 2011-2013 GRNET S.A. All rights reserved.
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
#import "PithosObjectNode.h"
#import "ASIPithosRequest.h"
+#import "ASIPithos.h"
+#import "ASIPithosObjectRequest.h"
+#import "ASIPithosContainer.h"
+#import "ASIPithosObject.h"
+#import "ASIPithosSharingUser.h"
+#import "ASIDownloadCache.h"
+#import "PithosAccount.h"
+#import "PithosUtilities.h"
+#import "PithosObjectNodeInfoController.h"
@implementation PithosObjectNode
+@synthesize pithosContainer, pithosObject, versions, applyMetadataObjectRequest, refreshMetadataObjectRequest, refreshVersionsObjectRequest;
+@synthesize isPublic, translatedModifiedBy, translatedPermissions;
#pragma mark -
#pragma mark Object Lifecycle
-- (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer pithosObject:(ASIPithosObject *)aPithosObject {
- if ((self = [super init])) {
- pithosContainer = [aPithosContainer retain];
- pithosObject = [aPithosObject retain];
+- (id)initWithPithosAccountManager:(PithosAccount *)aPithosAccountManager
+ pithosContainer:(ASIPithosContainer *)aPithosContainer
+ pithosObject:(ASIPithosObject *)aPithosObject {
+ if ((self = [super initWithPithosAccountManager:aPithosAccountManager])) {
isLeafItem = YES;
+ self.pithosContainer = aPithosContainer;
+ self.pithosObject = aPithosObject;
}
return self;
}
- (void)dealloc {
- [icon release];
- [pithosObject release];
- [pithosContainer release];
- [super dealloc];
+ [refreshVersionsObjectRequest clearDelegatesAndCancel];
+ [refreshMetadataObjectRequest clearDelegatesAndCancel];
+ [applyMetadataObjectRequest clearDelegatesAndCancel];
+}
+
+#pragma mark -
+#pragma mark Internal
+
+- (void)updateModifiedBy {
+ if (!pithosObject.modifiedBy) {
+ self.translatedModifiedBy = nil;
+ } else if (pithosAccountManager) {
+ NSString *displayname = [pithosAccountManager displaynameForUUID:pithosObject.modifiedBy safe:NO];
+ if (displayname) {
+ self.translatedModifiedBy = displayname;
+ } else {
+ [pithosAccountManager updateUserCatalogForDisplaynames:nil UUIDs:[NSArray arrayWithObject:pithosObject.modifiedBy]];
+ self.translatedModifiedBy = [pithosAccountManager displaynameForUUID:pithosObject.modifiedBy safe:YES];
+ }
+ } else {
+ self.translatedModifiedBy = [pithosObject.modifiedBy copy];
+ }
+}
+
+- (void)updatePermissions {
+ if (!pithosObject) {
+ self.translatedPermissions = [NSMutableArray array];
+ } else if (pithosAccountManager) {
+ NSMutableSet *UUIDs = [NSMutableSet set];
+ for (ASIPithosSharingUser *sharingUser in pithosObject.permissions) {
+ [UUIDs addObject:sharingUser.name];
+ }
+ [UUIDs removeObject:@""];
+ [UUIDs removeObject:@"*"];
+ if (UUIDs.count) {
+ [pithosAccountManager updateUserCatalogForDisplaynames:nil UUIDs:[UUIDs allObjects]];
+ }
+
+ NSMutableArray *newTranslatedPermissions = [NSMutableArray arrayWithCapacity:pithosObject.permissions.count];
+ for (ASIPithosSharingUser *sharingUser in pithosObject.permissions) {
+ ASIPithosSharingUser *translatedSharingUser = [sharingUser copy];
+ translatedSharingUser.name = [pithosAccountManager displaynameForUUID:translatedSharingUser.name safe:YES];
+ [newTranslatedPermissions addObject:translatedSharingUser];
+ }
+ self.translatedPermissions = newTranslatedPermissions;
+ } else {
+ self.translatedPermissions = [NSMutableArray arrayWithArray:[pithosObject.permissions copy]];
+ }
}
#pragma mark -
#pragma mark Properties
- (NSString *)url {
- if (url == nil)
- url = [[NSString alloc] initWithFormat:@"object %@/%@/%@", [ASIPithosRequest storageURL], pithosContainer.name, pithosObject.name];
- return url;
+ return [NSString stringWithFormat:@"@object@%@/%@/%@%@",
+ (sharingAccount ? sharingAccount : pithosAccountManager.pithos.authUser),
+ pithosContainer.name,
+ pithosObject.name,
+ (shared ? @"?shared" : @"")];
}
- (NSArray *)children {
displayName = [pithosObject.name lastPathComponent];
if([pithosObject.name hasSuffix:@"/"])
displayName = [displayName stringByAppendingString:@"/"];
- [displayName retain];
}
- return [[displayName copy] autorelease];
+ return [displayName copy];
+}
+
+- (void)setDisplayName:(NSString *)aDisplayName {
}
- (NSImage *)icon {
- if (icon == nil) {
- icon = [[[NSWorkspace sharedWorkspace] iconForFileType:[pithosObject.name pathExtension]] retain];
- }
+ if (icon == nil)
+ icon = [[NSWorkspace sharedWorkspace] iconForFileType:[pithosObject.name pathExtension]];
return icon;
}
-- (NSString *)kind {
- return pithosObject.contentType;
+- (void)setPithosObject:(ASIPithosObject *)aPithosObject {
+ if (![pithosObject isEqualTo:aPithosObject]) {
+ pithosObject = aPithosObject;
+ [self updateModifiedBy];
+ [self updatePermissions];
+ }
+ self.isPublic = (pithosObject.publicURI != nil);
+ // Refresh browser if the object is in my shared and is no longer shared
+ if (shared && !pithosObject.sharing)
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosBrowserRefreshNeeded" object:self];
}
-- (NSString *)size {
- return [NSString stringWithFormat:@"%lu B", pithosObject.bytes];
+- (void)setLimitedPithosObject:(ASIPithosObject *)aPithosObject {
+ if (![pithosObject isEqualTo:aPithosObject]) {
+ self.pithosObject.subdir = aPithosObject.subdir;
+ self.pithosObject.name = aPithosObject.name;
+ self.pithosObject.hash = aPithosObject.hash;
+ self.pithosObject.objectHash = aPithosObject.objectHash;
+ self.pithosObject.UUID = aPithosObject.UUID;
+ self.pithosObject.bytes = aPithosObject.bytes;
+ self.pithosObject.contentType = aPithosObject.contentType;
+ self.pithosObject.lastModified = aPithosObject.lastModified;
+ self.pithosObject.version = aPithosObject.version;
+ self.pithosObject.versionTimestamp = aPithosObject.versionTimestamp;
+ self.pithosObject.modifiedBy = aPithosObject.modifiedBy;
+ self.pithosObject.sharedBy = aPithosObject.sharedBy;
+ self.pithosObject.allowedTo = aPithosObject.allowedTo;
+ if (!pithosNodeInfoController) {
+ self.pithosObject.sharing = aPithosObject.sharing;
+ self.pithosObject.publicURI = aPithosObject.publicURI;
+ self.pithosObject = pithosObject;
+ [self updatePermissions];
+ } else {
+ [self updateModifiedBy];
+ }
+ }
}
-- (NSString *)modified {
- return [NSDateFormatter localizedStringFromDate:pithosObject.lastModified
- dateStyle:NSDateFormatterShortStyle
- timeStyle:NSDateFormatterShortStyle];
+#pragma mark -
+#pragma mark ASIHTTPRequestDelegate
+
+- (void)objectRequestFinished:(ASIPithosObjectRequest *)request {
+ @autoreleasepool {
+ DLog(@"URL: %@", [request url]);
+ DLog(@"cached: %d", [request didUseCachedResponse]);
+
+ if ([request isEqualTo:applyMetadataObjectRequest]) {
+ int responseStatusCode = applyMetadataObjectRequest.responseStatusCode;
+ if (responseStatusCode != 202)
+ [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest];
+ @synchronized(self) {
+ self.applyMetadataObjectRequest = nil;
+ }
+ if (responseStatusCode == 202)
+ [self refreshInfo];
+ } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
+ [[pithosNodeInfoController window] makeFirstResponder:nil];
+ self.pithosObject = [refreshMetadataObjectRequest object];
+ @synchronized(self) {
+ self.refreshMetadataObjectRequest = nil;
+ }
+ } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
+ [[pithosNodeInfoController window] makeFirstResponder:nil];
+ self.versions = [refreshVersionsObjectRequest versions];
+ @synchronized(self) {
+ self.refreshVersionsObjectRequest = nil;
+ }
+ }
+ }
}
-- (NSString *)modifiedBy {
- return pithosObject.modifiedBy;
+- (void)objectRequestFailed:(ASIPithosObjectRequest *)request {
+ @autoreleasepool {
+ NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
+ if (retries > 0) {
+ ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[PithosUtilities retryWithUpdatedURLRequest:request
+ andPithosAccountManager:pithosAccountManager];
+ [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
+ if ([request isEqualTo:applyMetadataObjectRequest]) {
+ @synchronized(self) {
+ self.applyMetadataObjectRequest = newRequest;
+ }
+ } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
+ @synchronized(self) {
+ self.refreshMetadataObjectRequest = newRequest;
+ }
+ } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
+ @synchronized(self) {
+ self.refreshVersionsObjectRequest = newRequest;
+ }
+ }
+ [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
+ } else {
+ if ([request isEqualTo:applyMetadataObjectRequest]) {
+ [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest];
+ @synchronized(self) {
+ self.applyMetadataObjectRequest = nil;
+ }
+ } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
+ [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest];
+ @synchronized(self) {
+ self.refreshMetadataObjectRequest = nil;
+ }
+ } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
+ [PithosUtilities httpRequestErrorAlertWithRequest:refreshVersionsObjectRequest];
+ @synchronized(self) {
+ self.refreshVersionsObjectRequest = nil;
+ }
+ }
+ }
+ }
}
-- (NSString *)version {
- return pithosObject.version;
+#pragma mark -
+#pragma mark Info
+
+- (void)applyInfo {
+ @synchronized(self) {
+ if (applyMetadataObjectRequest == nil) {
+ [[pithosNodeInfoController window] makeFirstResponder:nil];
+ if (sharingAccount) {
+ self.applyMetadataObjectRequest = [ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithosAccountManager.pithos
+ containerName:pithosContainer.name
+ objectName:pithosObject.name
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse)
+ metadata:pithosObject.metadata
+ update:NO];
+ [applyMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithosAccountManager.pithos];
+ } else {
+ NSMutableArray *permissions = [NSMutableArray array];
+ if (translatedPermissions.count) {
+ for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) {
+ if (translatedsSharingUser.group.length &&
+ [translatedsSharingUser.group rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" -_~,;"]].location != NSNotFound) {
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"Invalid Input"];
+ [alert setInformativeText:@"Group names cannot contain ' ', '-', '_', '~', ',' or ';'."];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ return;
+ }
+ }
+ if (pithosAccountManager) {
+ NSMutableSet *allUsers = [NSMutableSet set];
+ for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) {
+ if (translatedsSharingUser.name.length) {
+ [allUsers addObject:translatedsSharingUser.name];
+ }
+ }
+ [allUsers removeObject:@""];
+ [allUsers removeObject:@"*"];
+ if (allUsers.count) {
+ ASIPithosRequest *userCatalogRequest = [pithosAccountManager updateUserCatalogForDisplaynames:[allUsers allObjects]
+ UUIDs:nil];
+ if (userCatalogRequest.error || ((userCatalogRequest.responseStatusCode != 200) && (userCatalogRequest.responseStatusCode != 404))) {
+ return;
+ } else if (userCatalogRequest.responseStatusCode == 200) {
+ // Check if all users exist.
+ NSDictionary *displaynameCatalog = [userCatalogRequest displaynameCatalog];
+ NSMutableArray *inexistentUsers = [NSMutableArray array];
+ for (NSString *user in allUsers) {
+ if (![displaynameCatalog objectForKey:user]) {
+ [inexistentUsers addObject:user];
+ }
+ }
+ if (!inexistentUsers.count) {
+ // Create permissions.
+ for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) {
+ if (translatedsSharingUser.name.length) {
+ ASIPithosSharingUser *sharingUser = [translatedsSharingUser copy];
+ if (![sharingUser.name isEqualToString:@"*"]) {
+ sharingUser.name = [displaynameCatalog objectForKey:sharingUser.name];
+ }
+ if (!sharingUser.permission) {
+ sharingUser.permission = @"read";
+ }
+ [permissions addObject:sharingUser];
+ }
+ }
+ } else {
+ NSAlert *alert = [[NSAlert alloc] init];
+ if (inexistentUsers.count == 1) {
+ [alert setMessageText:@"Invalid User"];
+ [alert setInformativeText:[NSString stringWithFormat:@"User '%@' doesn't exist.", [inexistentUsers objectAtIndex:0]]];
+ } else {
+ [alert setMessageText:@"Invalid Users"];
+ [alert setInformativeText:[NSString stringWithFormat:@"Users '%@' don't exist.", [inexistentUsers componentsJoinedByString:@"', '"]]];
+ }
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ return;
+ }
+ } else {
+ // 404. Since we don't translate to UUIDs, check for invalid chars.
+ BOOL valid = YES;
+ // Create permissions.
+ for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) {
+ if (translatedsSharingUser.name.length &&
+ ([translatedsSharingUser.name rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" ~,;:"]].location != NSNotFound)) {
+ valid = NO;
+ break;
+ }
+ }
+ if (valid) {
+ for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) {
+ if (translatedsSharingUser.name.length) {
+ ASIPithosSharingUser *sharingUser = [translatedsSharingUser copy];
+ if (!sharingUser.permission) {
+ sharingUser.permission = @"read";
+ }
+ [permissions addObject:sharingUser];
+ }
+ }
+ } else {
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"Invalid Input"];
+ [alert setInformativeText:@"Users cannot contain ' ', '~', ',', ';' or ':'."];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ return;
+ }
+ }
+ } else {
+ for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) {
+ if ([translatedsSharingUser.name isEqualToString:@"*"]) {
+ ASIPithosSharingUser *sharingUser = [translatedsSharingUser copy];
+ if (!sharingUser.permission) {
+ sharingUser.permission = @"read";
+ }
+ [permissions addObject:sharingUser];
+ }
+ }
+ }
+ } else {
+ for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) {
+ if (translatedsSharingUser.name.length) {
+ ASIPithosSharingUser *sharingUser = [translatedsSharingUser copy];
+ if (!sharingUser.permission) {
+ sharingUser.permission = @"read";
+ }
+ [permissions addObject:sharingUser];
+ }
+ }
+ }
+ }
+ pithosObject.permissions = permissions;
+
+ self.applyMetadataObjectRequest = [ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithosAccountManager.pithos
+ containerName:pithosContainer.name
+ objectName:pithosObject.name
+ contentEncoding:pithosObject.contentEncoding
+ contentDisposition:pithosObject.contentDisposition
+ manifest:pithosObject.manifest
+ sharing:(pithosObject.sharing ? pithosObject.sharing : @"")
+ isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse)
+ metadata:pithosObject.metadata
+ update:NO];
+ }
+ applyMetadataObjectRequest.delegate = self;
+ applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+ applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+ applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
+ [NSNumber numberWithUnsignedInteger:10], @"retries",
+ NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector",
+ NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector",
+ nil];
+ [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+ }
+ }
+}
+
+- (void)refreshInfo {
+ @synchronized(self) {
+ if (refreshMetadataObjectRequest == nil) {
+ self.refreshMetadataObjectRequest = [ASIPithosObjectRequest objectMetadataRequestWithPithos:pithosAccountManager.pithos
+ containerName:pithosContainer.name
+ objectName:pithosObject.name];
+ if (sharingAccount)
+ [refreshMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithosAccountManager.pithos];
+ refreshMetadataObjectRequest.delegate = self;
+ refreshMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+ refreshMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+ refreshMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
+ [NSNumber numberWithUnsignedInteger:10], @"retries",
+ NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector",
+ NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector",
+ nil];
+ if (!sharingAccount)
+ refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
+ [[PithosUtilities prepareRequest:refreshMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+ }
+ }
+ [self refreshVersions];
+}
+
+#pragma mark -
+#pragma mark Versions
+
+- (void)refreshVersions {
+ @synchronized(self) {
+ if (refreshVersionsObjectRequest == nil) {
+ self.refreshVersionsObjectRequest = [ASIPithosObjectRequest objectVersionsRequestWithPithos:pithosAccountManager.pithos
+ containerName:pithosContainer.name
+ objectName:pithosObject.name];
+ if (sharingAccount)
+ [refreshVersionsObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithosAccountManager.pithos];
+ refreshVersionsObjectRequest.delegate = self;
+ refreshVersionsObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+ refreshVersionsObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+ refreshVersionsObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
+ [NSNumber numberWithUnsignedInteger:10], @"retries",
+ NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector",
+ NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector",
+ nil];
+ if (!sharingAccount)
+ refreshVersionsObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
+ [[PithosUtilities prepareRequest:refreshVersionsObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Actions
+
+- (void)showPithosNodeInfo:(id)sender {
+ if (!pithosNodeInfoController) {
+ pithosNodeInfoController = [[PithosObjectNodeInfoController alloc] initWithPithosNode:self];
+ [self refreshInfo];
+ }
+ [pithosNodeInfoController showWindow:sender];
+ [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
+ [NSApp activateIgnoringOtherApps:YES];
}
@end