X-Git-Url: https://code.grnet.gr/git/pithos-macos/blobdiff_plain/9f3a92de6d51dba59ba6079d612274bca2fa951a..6d9d5dce6b6df63e7e44314f25ee2a39b953397d:/pithos-macos/PithosObjectNode.m diff --git a/pithos-macos/PithosObjectNode.m b/pithos-macos/PithosObjectNode.m index 490881e..ffe8fd1 100644 --- a/pithos-macos/PithosObjectNode.m +++ b/pithos-macos/PithosObjectNode.m @@ -1,8 +1,8 @@ // -// 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 @@ -37,35 +37,94 @@ #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 { @@ -77,38 +136,358 @@ 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