Other fixes and improvements.
-Subproject commit 9e1f10387cee938c9844d337577d9884ee42381d
+Subproject commit de233d93db69b1631f8fdb0ae24be920ee3989d2
accountRequest = nil;
[containers release];
containers = nil;
- // XXX sort children based on preferences
@synchronized(self) {
freshness = PithosNodeStateRefreshFinished;
}
NSViewController *sharedPreviewController;
NSMenu *browserMenu;
+
+ NSSplitView *splitView;
+ NSOutlineView *outlineView;
+ NSBrowser *browser;
}
@property (nonatomic, retain) NSMutableArray *outlineViewDataSourceArray;
#import "ASIPithosObject.h"
#import "PithosFileUtilities.h"
-//@interface PithosBrowserCell : NSBrowserCell {}
@interface PithosBrowserCell : FileSystemBrowserCell {}
@end
PithosNode *node = (PithosNode *)object;
[self setStringValue:node.displayName];
[self setImage:node.icon];
-// // All cells are set as leafs because a branchingImage is already set!
-// // Maybe this cell is already inside an NSBrowserCell
-// [self setLeaf:YES];
} else {
[super setObjectValue:object];
}
@end
@interface PithosBrowserController (Private) {}
-- (void)resetContainers;
+- (void)resetContainers:(NSNotification *)notification;
- (void)getInfo:(NSMenuItem *)sender;
- (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest;
- (void)downloadObjectFailed:(ASIPithosObjectRequest *)objectRequest;
[browser setMenu:browserMenu];
}
-- (void)resetContainers {
+- (void)resetContainers:(NSNotification *)notification {
rootNode = nil;
[browser loadColumnZero];
self.outlineViewDataSourceArray = nil;
// CONTAINERS
NSTreeNode *containersTreeNode = [NSTreeNode treeNodeWithRepresentedObject:
[[[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil] autorelease]];
-// // CONTAINERS/pithos
-// [[containersTreeNode mutableChildNodes] addObject:
-// [NSTreeNode treeNodeWithRepresentedObject:
-// [[[PithosContainerNode alloc] initWithContainerName:@"pithos"
-// icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)]
-// ] autorelease]]];
-// // CONTAINERS/trash
-// [[containersTreeNode mutableChildNodes] addObject:
-// [NSTreeNode treeNodeWithRepresentedObject:
-// [[[PithosContainerNode alloc] initWithContainerName:@"trash"
-// icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)]
-// ] autorelease]]];
+
// SHARED
NSTreeNode *sharedTreeNode = [NSTreeNode treeNodeWithRepresentedObject:
[[[PithosEmptyNode alloc] initWithDisplayName:@"SHARED" icon:nil] autorelease]];
name:@"PithosAccountNodeChildrenUpdated"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(resetContainers)
+ selector:@selector(resetContainers:)
name:@"PithosAuthenticationCredentialsUpdated"
object:nil];
}
- (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
PithosNode *node = (PithosNode *)[notification object];
+ NSLog(@"pithosNodeChildrenUpdated:%@", node.url);
NSInteger lastColumn = [browser lastColumn];
for (NSInteger column = lastColumn; column >= 0; column--) {
if ([[browser parentForItemsInColumn:column] isEqualTo:node]) {
[browser reloadColumn:column];
- if ((column == lastColumn - 1) && ([[browser parentForItemsInColumn:lastColumn] isLeafItem])) {
- // This reloads the preview column
- [browser setLastColumn:column];
- [browser addColumn];
- }
+ // The following code is unnecessary since the pithosObject is set in the PithosNode in each refresh
+ // Furthermore it caused problems on delete, because a non-existing parent was asked for
+ //if ((column == lastColumn - 1) && ([[browser parentForItemsInColumn:lastColumn] isLeafItem])) {
+ // // This reloads the preview column
+ // [browser setLastColumn:column];
+ // [browser addColumn];
+ //}
return;
}
}
- (void)pithosAccountNodeChildrenUpdated:(NSNotification *)notification {
BOOL containerPithosFound = NO;
BOOL containerTrashFound = NO;
- //NSMutableArray *containersTreeNodeChildren = [[outlineViewDataSourceArray objectAtIndex:0] mutableChildNodes];
NSMutableArray *containersTreeNodeChildren = [NSMutableArray array];
for (PithosContainerNode *containerNode in accountNode.children) {
if ([containerNode.pithosContainer.name isEqualToString:@"pithos"]) {
ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"pithos"];
[containerRequest startSynchronous];
if ([containerRequest error]) {
- NSLog(@"error:%@", [containerRequest error]);
- // XXX do something on error
+ [PithosFileUtilities httpRequestErrorAlertWithRequest:containerRequest];
} else {
refreshAccountNode = YES;
}
ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"trash"];
[containerRequest startSynchronous];
if ([containerRequest error]) {
- NSLog(@"error:%@", [containerRequest error]);
- // XXX do something on error
+ [PithosFileUtilities httpRequestErrorAlertWithRequest:containerRequest];
} else {
refreshAccountNode = YES;
}
dropOperation:(NSBrowserDropOperation *)dropOperation {
NSDragOperation result = NSDragOperationNone;
// Files from the finder are accepted
- if ([[[info draggingPasteboard] types] indexOfObject:NSFilenamesPboardType] != -1) {
+ if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
// For a between drop, we let the user drop "on" the parent item
if (*dropOperation == NSBrowserDropAbove)
*row = -1;
if (node) {
rootNode = node;
[browser loadColumnZero];
+ [self refresh:nil];
}
}
NSInteger column = [browser clickedColumn];
NSInteger row = [browser clickedRow];
[menu removeAllItems];
+ NSMenuItem *menuItem;
if ((column == -1) || (row == -1)) {
- // General context menu has 0
+ // General context menu
} else {
- // PithosNode menu has 1 items
+ // Move to Trash (pithos container only)
+ // Delete
+ if ([rootNode class] == [PithosContainerNode class]) {
+ if ([rootNode.pithosContainer.name isEqualToString:@"pithos"]) {
+ menuItem = [[[NSMenuItem alloc] initWithTitle:@"Move to Trash" action:@selector(moveToTrash:) keyEquivalent:@""] autorelease];
+ [menuItem setRepresentedObject:[browser itemAtRow:row inColumn:column]];
+ [menu addItem:menuItem];
+ }
+ menuItem = [[[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(deleteObject:) keyEquivalent:@""] autorelease];
+ [menuItem setRepresentedObject:[browser itemAtRow:row inColumn:column]];
+ [menu addItem:menuItem];
+ [menu addItem:[NSMenuItem separatorItem]];
+ }
// Get Info
- NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(getInfo:) keyEquivalent:@""];
+ menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(getInfo:) keyEquivalent:@""] autorelease];
[menuItem setRepresentedObject:[browser itemAtRow:row inColumn:column]];
[menu addItem:menuItem];
}
[(PithosNode *)[sender representedObject] showPithosNodeInfo:sender];
}
+- (void)deleteObject:(NSMenuItem *)sender {
+ PithosNode *node = (PithosNode *)[sender representedObject];
+ if (([node class] == [PithosObjectNode class]) ||
+ (([node class] == [PithosSubdirNode class]) &&
+ !node.pithosObject.subdir &&
+ [node.pithosObject.name hasSuffix:@"/"])) {
+ ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:node.pithosContainer.name
+ objectName:node.pithosObject.name];
+ objectRequest.delegate = self;
+ objectRequest.didFinishSelector = @selector(deleteObjectFinished:);
+ objectRequest.didFailSelector = @selector(deleteObjectFailed:);
+ [objectRequest startAsynchronous];
+ } else if ([node class] == [PithosSubdirNode class]) {
+ NSArray *objectRequests = [PithosFileUtilities deleteObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
+ objectName:node.pithosObject.name];
+ if (objectRequests) {
+ for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+ objectRequest.delegate = self;
+ objectRequest.didFinishSelector = @selector(deleteObjectFinished:);
+ objectRequest.didFailSelector = @selector(deleteObjectFailed:);
+ [objectRequest startAsynchronous];
+ }
+ }
+
+ }
+}
+
+- (void)moveToTrash:(NSMenuItem *)sender {
+ PithosNode *node = (PithosNode *)[sender representedObject];
+ if (([node class] == [PithosObjectNode class]) ||
+ (([node class] == [PithosSubdirNode class]) &&
+ !node.pithosObject.subdir &&
+ [node.pithosObject.name hasSuffix:@"/"])) {
+ NSString *safeObjectName = [PithosFileUtilities safeObjectNameForContainerName:@"trash"
+ objectName:node.pithosObject.name];
+ if (safeObjectName) {
+ ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:node.pithosContainer.name
+ objectName:node.pithosObject.name
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:@"trash"
+ destinationObjectName:safeObjectName];
+ objectRequest.delegate = self;
+ objectRequest.didFinishSelector = @selector(moveToTrashFinished:);
+ objectRequest.didFailSelector = @selector(moveToTrashFailed:);
+ [objectRequest startAsynchronous];
+ }
+ } else if ([node class] == [PithosSubdirNode class]) {
+ NSString *safeObjectName = [PithosFileUtilities safeSubdirNameForContainerName:@"trash"
+ subdirName:node.pithosObject.name];
+ if (safeObjectName) {
+ NSArray *objectRequests = [PithosFileUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
+ objectName:node.pithosObject.name
+ destinationContainerName:@"trash"
+ destinationObjectName:safeObjectName];
+ if (objectRequests) {
+ for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+ objectRequest.delegate = self;
+ objectRequest.didFinishSelector = @selector(moveToTrashFinished:);
+ objectRequest.didFailSelector = @selector(moveToTrashFailed:);
+ [objectRequest startAsynchronous];
+ }
+ }
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Menu Actions ASIHTTPRequestDelegate
+
+- (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest {
+ if (objectRequest.responseStatusCode == 204) {
+ NSLog(@"Object deleted: %@", [objectRequest url]);
+ [self refresh:nil];
+ } else {
+ [PithosFileUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
+ }
+}
+
+- (void)deleteObjectFailed:(ASIPithosObjectRequest *)objectRequest {
+ NSLog(@"Delete of object failed");
+ [PithosFileUtilities httpRequestErrorAlertWithRequest:objectRequest];
+}
+
+- (void)moveToTrashFinished:(ASIPithosObjectRequest *)objectRequest {
+ if (objectRequest.responseStatusCode == 201) {
+ NSLog(@"Object moved: %@", [objectRequest url]);
+ [self refresh:nil];
+ } else {
+ [PithosFileUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
+ }
+}
+
+- (void)moveToTrashFailed:(ASIPithosObjectRequest *)objectRequest {
+ NSLog(@"Move of object failed");
+ [PithosFileUtilities httpRequestErrorAlertWithRequest:objectRequest];
+}
+
+
@end
\ No newline at end of file
// Save new children
NSLog(@"using newChildren");
newChildren = [[NSMutableArray alloc] init];
+ NSArray *objectNames = [objects valueForKey:@"name"];
+ NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet];
for (ASIPithosObject *object in objects) {
if (object.subdir) {
- PithosNode *previousNode = (PithosNode *)[newChildren lastObject];
- if (!previousNode ||
- ([previousNode class] != [PithosSubdirNode class]) ||
- (![((PithosSubdirNode *)previousNode).pithosObject.contentType isEqualToString:@"application/directory"]) ||
- (![[((PithosSubdirNode *)previousNode).pithosObject.name stringByAppendingString:@"/"] isEqualToString:object.name])) {
+ NSUInteger sameNameObjectIndex = [objectNames indexOfObject:[object.name substringToIndex:([object.name length] - 1)]];
+ if ((sameNameObjectIndex == NSNotFound) ||
+ ![[[objects objectAtIndex:sameNameObjectIndex] contentType] isEqualToString:@"application/directory"]) {
PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
node.parent = self;
if (children) {
node = [children objectAtIndex:oldIndex];
node.pithosContainer = pithosContainer;
node.pithosObject = object;
+ [keptNodes addIndex:oldIndex];
}
}
[newChildren addObject:node];
}
- } else if ([object.contentType isEqualToString:@"application/directory"]) {
- PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
- node.parent = self;
- if (children) {
- NSUInteger oldIndex = [children indexOfObject:node];
- if (oldIndex != NSNotFound) {
- // Use the same pointer value, if possible
- node = [children objectAtIndex:oldIndex];
- node.pithosContainer = pithosContainer;
- node.pithosObject = object;
- }
- }
- [newChildren addObject:node];
} else if (([self class] != [PithosSubdirNode class]) ||
![[((PithosSubdirNode *)self).prefix stringByAppendingString:@"/"] isEqualToString:object.name]) {
- // XXX the if above removes false objects due to trailing slash
- // XXX this will change in the server, but it is fixed in the client for now
- PithosObjectNode *node = [[[PithosObjectNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
- node.parent = self;
- if (children) {
- NSUInteger oldIndex = [children indexOfObject:node];
- if (oldIndex != NSNotFound) {
- // Use the same pointer value, if possible
- node = [children objectAtIndex:oldIndex];
- node.pithosContainer = pithosContainer;
- node.pithosObject = object;
+ // The check above removes false objects due to trailing slash
+ // This may change in the server, but it is fixed in the client for now
+ if ([object.contentType isEqualToString:@"application/directory"]) {
+ PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
+ node.parent = self;
+ if (children) {
+ NSUInteger oldIndex = [children indexOfObject:node];
+ if (oldIndex != NSNotFound) {
+ // Use the same pointer value, if possible
+ node = [children objectAtIndex:oldIndex];
+ node.pithosContainer = pithosContainer;
+ node.pithosObject = object;
+ [keptNodes addIndex:oldIndex];
+ }
+ }
+ [newChildren addObject:node];
+ } else {
+ PithosObjectNode *node = [[[PithosObjectNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
+ node.parent = self;
+ if (children) {
+ NSUInteger oldIndex = [children indexOfObject:node];
+ if (oldIndex != NSNotFound) {
+ // Use the same pointer value, if possible
+ node = [children objectAtIndex:oldIndex];
+ node.pithosContainer = pithosContainer;
+ node.pithosObject = object;
+ [keptNodes addIndex:oldIndex];
+ }
}
+ [newChildren addObject:node];
}
- [newChildren addObject:node];
}
}
+ [[children objectsAtIndexes:
+ [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){
+ if ([keptNodes containsIndex:idx])
+ return NO;
+ return YES;
+ }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)];
}
// Else cache was used and all results were fetched during this request, so existing children can be reused
[containerRequest release];
containerRequest = nil;
[objects release];
objects = nil;
- // XXX sort children based on preferences
@synchronized(self) {
freshness = PithosNodeStateRefreshFinished;
}
+ (NSIndexSet *)missingBlocksForHashes:(NSArray *)hashes withMissingHashesResponse:(NSString *)missingHashesResponse;
+ (ASIPithosObjectRequest *)updateObjectDataRequestWithContainerName:(NSString *)containerName
objectName:(NSString *)objectName
- contentType:(NSString *)contentType
blockSize:(NSUInteger)blockSize
forFile:(NSString *)filePath
hashes:(NSArray *)hashes
filePaths:(NSMutableArray **)filePaths
hashesArrays:(NSMutableArray **)hashesArrays;
++ (NSArray *)deleteObjectRequestsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName;
++ (NSArray *)moveObjectRequestsForSubdirWithContainerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ destinationContainerName:(NSString *)destinationContainerName
+ destinationObjectName:(NSString *)destinationObjectName;
+ (NSUInteger)bytesOfFile:(NSString *)filePath;
+ (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error;
+ (BOOL)objectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
responseString:(NSString **)responseString error:(NSError **)error isDirectory:(BOOL *)isDirectory;
-+ (int)deleteObjectAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
- responseStatusMessage:(NSString **)responseStatusMessage error:(NSError **)error;
++ (NSArray *)objectsWithContainerName:(NSString *)containerName objectNamePrefix:(NSString *)objectNamePrefix
+ delimiter:(NSString *)delimiter;
++ (NSArray *)objectsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName
+ delimiter:(NSString *)delimiter;
++ (NSString *)safeObjectNameForContainerName:(NSString *)containerName objectName:(NSString *)objectName;
++ (NSString *)safeSubdirNameForContainerName:(NSString *)containerName subdirName:(NSString *)subdirName;
+ (NSInteger)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request;
+ (NSInteger)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request;
return nil;
}
- NSMutableArray *objects = [NSMutableArray array];
- NSString *marker = nil;
- do {
- ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithContainerName:containerName
- limit:0
- marker:marker
- prefix:objectName
- delimiter:nil
- path:nil
- meta:nil
- shared:NO
- until:nil];
- [containerRequest startSynchronous];
- if ([containerRequest error]) {
- NSLog(@"HTTP Request Error: %@\nResponse String: %@", [containerRequest error], [containerRequest responseString]);
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"HTTP Request Error"];
- [alert setInformativeText:[NSString stringWithFormat:@"HTTP Request Error: %@\nResponse String: %@",
- [containerRequest error], [containerRequest responseString]]];
- [alert addButtonWithTitle:@"OK"];
- [alert runModal];
- return nil;
- }
- NSArray *someObjects = [containerRequest objects];
- [objects addObjectsFromArray:someObjects];
- if ([someObjects count] < 10000)
- marker = nil;
- else
- marker = [[someObjects lastObject] name];
- } while (marker);
+ NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil];
+ if (objects == nil)
+ return nil;
NSFileManager *defaultManager = [NSFileManager defaultManager];
NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[objects count]];
NSUInteger subdirPrefixLength = [objectName length];
for (ASIPithosObject *object in objects) {
- if (object.subdir || [object.contentType isEqualToString:@"application/directory"]) {
+ if ([object.contentType isEqualToString:@"application/directory"]) {
NSString *subdirDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
subdirDirectoryPath = [subdirDirectoryPath stringByAppendingPathComponent:[object.name substringFromIndex:subdirPrefixLength]];
if (![line length])
break;
NSUInteger missingBlock = [hashes indexOfObject:line];
- if (missingBlock != -1)
+ if (missingBlock != NSNotFound)
[missingBlocks addIndex:missingBlock];
}
return missingBlocks;
return [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
objectName:objectName
eTag:nil
- contentType:@"application/octet-stream"
+ contentType:@"application/octet-stream"
contentEncoding:nil
contentDisposition:nil
manifest:nil
+ (ASIPithosObjectRequest *)updateObjectDataRequestWithContainerName:(NSString *)containerName
objectName:(NSString *)objectName
- contentType:(NSString *)contentType
blockSize:(NSUInteger)blockSize
forFile:(NSString *)filePath
missingBlockIndex:(NSUInteger)missingBlockIndex {
return [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
objectName:objectName
eTag:nil
- contentType:contentType
+ contentType:@"application/octet-stream"
contentEncoding:nil
contentDisposition:nil
manifest:nil
error = nil;
contentType = [self contentTypeOfFile:filePath error:&error];
if (contentType == nil)
- contentType = @"application/binary";
+ contentType = @"application/octet-stream";
if (error)
NSLog(@"contentType detection error: %@", error);
[objectRequests addObject:[ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
[*hashesArrays addObject:hashes];
}
- }//XXX else
+ }//XXX else upload directory
}
}
}
#pragma mark -
+#pragma mark Move to Trash / Delete
+
++ (NSArray *)deleteObjectRequestsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName {
+ NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil];
+ if (objects == nil)
+ return nil;
+
+ NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
+
+ if (![objectName hasSuffix:@"/"])
+ [objectRequests addObject:[ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:objectName]];
+ for (ASIPithosObject *object in objects) {
+ [objectRequests addObject:[ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:object.name]];
+ }
+
+ if ([objectRequests count] == 0)
+ return nil;
+ return objectRequests;
+}
+
++ (NSArray *)moveObjectRequestsForSubdirWithContainerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ destinationContainerName:(NSString *)destinationContainerName
+ destinationObjectName:(NSString *)destinationObjectName {
+ NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil];
+ if (objects == nil)
+ return nil;
+
+ NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
+ if ([objectName isEqualToString:destinationObjectName]) {
+ if (![objectName hasSuffix:@"/"])
+ [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:objectName]];
+ for (ASIPithosObject *object in objects) {
+ [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
+ objectName:object.name
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:object.name]];
+ }
+ } else {
+ NSRange prefixRange = NSMakeRange(0, [objectName length]);
+ NSString *newObjectName;
+ if (![objectName hasSuffix:@"/"]) {
+ newObjectName = [objectName stringByReplacingOccurrencesOfString:objectName
+ withString:destinationObjectName
+ options:NSAnchoredSearch
+ range:prefixRange];
+ [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:newObjectName]];
+ }
+ for (ASIPithosObject *object in objects) {
+ newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
+ withString:destinationObjectName
+ options:NSAnchoredSearch
+ range:prefixRange];
+ [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
+ objectName:object.name
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:newObjectName]];
+ }
+ }
+
+ if ([objectRequests count] == 0)
+ return nil;
+ return objectRequests;
+}
+
+#pragma mark -
#pragma mark Helper Methods
+ (NSUInteger)bytesOfFile:(NSString *)filePath {
return NO;
}
-+ (int)deleteObjectAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
- responseStatusMessage:(NSString **)responseStatusMessage error:(NSError **)error {
- ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteContainerRequestWithContainerName:containerName
- objectName:objectName];
- [objectRequest startSynchronous];
- *error = [objectRequest error];
- *responseStatusMessage = [NSString stringWithString:objectRequest.responseStatusMessage];
- return objectRequest.responseStatusCode;
++ (NSArray *)objectsWithContainerName:(NSString *)containerName objectNamePrefix:(NSString *)objectNamePrefix
+ delimiter:(NSString *)delimiter {
+ NSMutableArray *objects = [NSMutableArray array];
+ NSString *marker = nil;
+ do {
+ ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithContainerName:containerName
+ limit:0
+ marker:marker
+ prefix:objectNamePrefix
+ delimiter:delimiter
+ path:nil
+ meta:nil
+ shared:NO
+ until:nil];
+ [containerRequest startSynchronous];
+ if ([containerRequest error]) {
+ [self httpRequestErrorAlertWithRequest:containerRequest];
+ return nil;
+ }
+ NSArray *someObjects = [containerRequest objects];
+ [objects addObjectsFromArray:someObjects];
+ if ([someObjects count] < 10000)
+ marker = nil;
+ else
+ marker = [[someObjects lastObject] name];
+ } while (marker);
+ return objects;
+}
+
++ (NSArray *)objectsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName
+ delimiter:(NSString *)delimiter {
+ NSString *subdirNamePrefix = [NSString stringWithString:objectName];
+ if (![subdirNamePrefix hasSuffix:@"/"])
+ subdirNamePrefix = [subdirNamePrefix stringByAppendingString:@"/"];
+ return [self objectsWithContainerName:containerName objectNamePrefix:subdirNamePrefix delimiter:delimiter];
+}
+
++ (NSString *)safeObjectNameForContainerName:(NSString *)containerName objectName:(NSString *)objectName {
+ NSString *objectNamePrefix = [NSString stringWithString:objectName];
+ NSString *objectNameExtraSuffix = [NSString string];
+ if ([objectNamePrefix hasSuffix:@"/"]) {
+ objectNamePrefix = [objectNamePrefix substringToIndex:([objectNamePrefix length] - 1)];
+ objectNameExtraSuffix = [NSString stringWithString:@"/"];
+ }
+ NSArray *objects = [self objectsWithContainerName:containerName objectNamePrefix:objectNamePrefix delimiter:nil];
+ if (objects == nil)
+ return nil;
+ if ([objects count] == 0)
+ return objectName;
+ NSArray *objectNames = [objects valueForKey:@"name"];
+ if (![objectNames containsObject:objectName])
+ return objectName;
+ NSUInteger objectNameSuffix = 1;
+ NSString *safeObjectName;
+ do {
+ safeObjectName = [objectNamePrefix stringByAppendingFormat:@" %lu%@", objectNameSuffix, objectNameExtraSuffix];
+ objectNameSuffix++;
+ } while ([objectNames containsObject:safeObjectName]);
+ return safeObjectName;
+}
+
++ (NSString *)safeSubdirNameForContainerName:(NSString *)containerName subdirName:(NSString *)subdirName {
+ NSString *subdirNamePrefix = [NSString stringWithString:subdirName];
+ NSString *subdirNameExtraSuffix = [NSString string];
+ if ([subdirNamePrefix hasSuffix:@"/"]) {
+ subdirNamePrefix = [subdirNamePrefix substringToIndex:([subdirNamePrefix length] - 1)];
+ subdirNameExtraSuffix = [NSString stringWithString:@"/"];
+ }
+ NSArray *objects = [self objectsWithContainerName:containerName
+ objectNamePrefix:[subdirNamePrefix stringByDeletingLastPathComponent]
+ delimiter:@"/"];
+ if (objects == nil)
+ return nil;
+ if ([objects count] == 0)
+ return subdirName;
+ NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
+ [[objects objectsAtIndexes:
+ [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
+ if (pithosObject.subdir)
+ return NO;
+ return YES;
+ }]] valueForKey:@"name"]];
+ for (NSString *name in [[objects objectsAtIndexes:
+ [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
+ if (pithosObject.subdir)
+ return YES;
+ return NO;
+ }]] valueForKey:@"name"]) {
+ [objectNames addObject:[name substringToIndex:([name length] - 1)]];
+ }
+ if (![objectNames containsObject:subdirNamePrefix])
+ return subdirName;
+ NSUInteger subdirNameSuffix = 1;
+ NSString *safeSubdirName;
+ do {
+ safeSubdirName = [subdirNamePrefix stringByAppendingFormat:@" %lu", subdirNameSuffix];
+ subdirNameSuffix++;
+ } while ([objectNames containsObject:safeSubdirName]);
+ return [safeSubdirName stringByAppendingString:subdirNameExtraSuffix];
}
#pragma mark -
+ (NSInteger)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
NSString *message = [NSString stringWithFormat:
- @"HTTP request error: %@\n%@ URL: %@\nResponse String: %@",
+ @"HTTP request error: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
[request error],
request.requestMethod,
request.url,
+ [request requestHeaders],
+ [request responseHeaders],
[request responseString]];
NSLog(@"%@", message);
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+ (NSInteger)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
NSString *message = [NSString stringWithFormat:
- @"Unexpected response status %d: %@\n%@ URL: %@\nResponse String: %@",
+ @"Unexpected response status %d: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
request.responseStatusCode,
request.responseStatusMessage,
request.requestMethod,
request.url,
+ [request requestHeaders],
+ [request responseHeaders],
[request responseString]];
NSLog(@"%@", message);
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- (void)showPithosNodeInfo:(id)sender;
- (void)pithosNodeInfoWillClose:(id)sender;
+- (void)pithosNodeWillBeRemoved;
+
@end
\ No newline at end of file
}
- (void)dealloc {
- [[pithosNodeInfoController window] close];
[pithosNodeInfoController release];
[icon release];
[displayName release];
}
}
+- (void)pithosNodeWillBeRemoved {
+ if (pithosNodeInfoController) {
+ pithosNodeInfoController.node = nil;
+ [[pithosNodeInfoController window] close];
+ }
+ for (PithosNode *child in children) {
+ [child pithosNodeWillBeRemoved];
+ }
+}
+
@end
\ No newline at end of file
}
- (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
#import <Cocoa/Cocoa.h>
-@interface PithosPreferencesController : NSWindowController <NSWindowDelegate>
+@interface PithosPreferencesController : NSWindowController <NSWindowDelegate> {
+ NSUserDefaultsController *userDefaultsController;
+
+ NSTextField *authenticationUserTextField;
+ NSTextField *authenticationTokenTextField;
+ NSButton *authenticationRenewCheckBox;
+}
-@property (nonatomic, retain) IBOutlet NSUserDefaultsController *userDefaultsController;
+@property (nonatomic, assign) IBOutlet NSUserDefaultsController *userDefaultsController;
-@property (nonatomic, retain) IBOutlet NSTextField *authenticationUserTextField;
-@property (nonatomic, retain) IBOutlet NSTextField *authenticationTokenTextField;
-@property (nonatomic, retain) IBOutlet NSButton *authenticationRenewCheckBox;
+@property (nonatomic, assign) IBOutlet NSTextField *authenticationUserTextField;
+@property (nonatomic, assign) IBOutlet NSTextField *authenticationTokenTextField;
+@property (nonatomic, assign) IBOutlet NSButton *authenticationRenewCheckBox;
- (IBAction)toolbarItemSelected:(id)sender;
return [super initWithWindowNibName:@"PithosPreferencesController"];
}
-- (void)dealloc {
- [authenticationRenewCheckBox release];
- [authenticationTokenTextField release];
- [authenticationUserTextField release];
- [userDefaultsController release];
- [super dealloc];
-}
-
- (void)windowDidLoad {
[super windowDidLoad];
}
- (NSString *)displayName {
- // XXX check if there are problems with . or other special characters
- return [pithosObject.name lastPathComponent];
+ NSString *name = [pithosObject.name lastPathComponent];
+ if (!pithosObject.subdir && [pithosObject.name hasSuffix:@"/"]) {
+ return [name stringByAppendingString:@"/"];
+ }
+ return name;
}
- (NSImage *)icon {
[alert addButtonWithTitle:@"Cancel"];
NSInteger choice = [alert runModal];
if (choice == NSAlertFirstButtonReturn) {
- request = [ASIPithosObjectRequest deleteContainerRequestWithContainerName:pithosContainer.name
+ request = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:pithosContainer.name
objectName:prefix];
[request startSynchronous];
if ([request error]) {