@end
@implementation PithosBrowserController
-@synthesize outlineViewDataSourceArray, splitView, outlineView, browser;
+@synthesize outlineViewDataSourceArray, splitView, outlineView, browser, draggedNodes, draggedParentNode;
#pragma mark -
#pragma Object Lifecycle
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
+ [draggedParentNode release];
+ [draggedNodes release];
[browserMenu release];
[sharedPreviewController release];
[outlineViewDataSourceArray release];
- (void)awakeFromNib {
[super awakeFromNib];
- [browser registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
- [browser setDraggingSourceOperationMask:NSDragOperationNone forLocal:YES];
+ [browser registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]];
+ [browser setDraggingSourceOperationMask:(NSDragOperationCopy|NSDragOperationMove) forLocal:YES];
[browser setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
[browser setCellClass:[PithosBrowserCell class]];
- (BOOL)browser:(NSBrowser *)aBrowser writeRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column
toPasteboard:(NSPasteboard *)pasteboard {
NSMutableArray *propertyList = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
+ NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
NSIndexPath *baseIndexPath = [browser indexPathForColumn:column];
[rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:idx]];
[propertyList addObject:[node.pithosObject.name pathExtension]];
+ [nodes addObject:node];
}];
[pasteboard declareTypes:[NSArray arrayWithObject:NSFilesPromisePboardType] owner:self];
[pasteboard setPropertyList:propertyList forType:NSFilesPromisePboardType];
-
+ self.draggedNodes = nodes;
+ self.draggedParentNode = [browser parentForItemsInColumn:column];
return YES;
}
column:(NSInteger *)column
dropOperation:(NSBrowserDropOperation *)dropOperation {
NSDragOperation result = NSDragOperationNone;
- // Files from the finder are accepted
if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
- // For a between drop, we let the user drop "on" the parent item
+ // For a drop above, the drop is redirected to the parent item
if (*dropOperation == NSBrowserDropAbove)
*row = -1;
// Only allow dropping in folders
if (*column != -1) {
if (*row != -1) {
- PithosNode *node = [browser itemAtRow:*row inColumn:*column];
- if ([node class] != [PithosSubdirNode class])
+ // Check if the node is not a folder and if so redirect to the parent item
+ if ([[browser itemAtRow:*row inColumn:*column] class] != [PithosSubdirNode class])
*row = -1;
}
*dropOperation = NSBrowserDropOn;
result = NSDragOperationCopy;
}
+ } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
+ // For a drop above, the drop is redirected to the parent item
+ if (*dropOperation == NSBrowserDropAbove)
+ *row = -1;
+ // Only allow dropping in folders
+ if (*column != -1) {
+ PithosNode *dropNode;
+ if (*row != -1) {
+ // Check if the node is not a folder and if so redirect to the parent item
+ dropNode = [browser itemAtRow:*row inColumn:*column];
+ if ([dropNode class] != [PithosSubdirNode class])
+ *row = -1;
+ }
+ if (*row == -1)
+ dropNode = [browser parentForItemsInColumn:*column];
+
+
+ if ([info draggingSourceOperationMask] & NSDragOperationMove) {
+ // NSDragOperationCopy | NSDragOperationMove -> NSDragOperationMove
+ if ((([dropNode class] == [PithosContainerNode class]) ||
+ dropNode.pithosObject.subdir ||
+ ![dropNode.pithosObject.name hasSuffix:@"/"]) &&
+ ![dropNode isEqualTo:draggedParentNode]) {
+// ![dropNode isEqualTo:draggedParentNode] &&
+// ![draggedNodes containsObject:dropNode]) {
+ result = NSDragOperationMove;
+ }
+ } else if ([info draggingSourceOperationMask] & NSDragOperationCopy) {
+ // NSDragOperationCopy (Option modifier) -> NSDragOperationCopy
+ if (([dropNode class] == [PithosContainerNode class]) ||
+ dropNode.pithosObject.subdir ||
+ ![dropNode.pithosObject.name hasSuffix:@"/"]) {
+ result = NSDragOperationCopy;
+ }
+ }
+ }
}
- // XXX else local file promises
return result;
}
atRow:(NSInteger)row
column:(NSInteger)column
dropOperation:(NSBrowserDropOperation)dropOperation {
- NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
- NSLog(@"drag in filenames: %@", filenames);
- PithosNode *node = nil;
- if ((column != -1) && (filenames != nil)) {
- if (row != -1)
- node = [browser itemAtRow:row inColumn:column];
- else
- node = [browser parentForItemsInColumn:column];
- NSLog(@"drag in node: %@", node.url);
- if (([node class] != [PithosSubdirNode class]) && ([node class] != [PithosContainerNode class]))
- return NO;
-
- NSFileManager *defaultManager = [NSFileManager defaultManager];
- NSString *containerName = [NSString stringWithString:node.pithosContainer.name];
- NSString *objectNamePrefix;
- if ([node class] == [PithosSubdirNode class])
- objectNamePrefix = [NSString stringWithString:node.pithosObject.name];
- else
- objectNamePrefix = [NSString stringWithString:@""];
- NSUInteger blockSize = node.pithosContainer.blockSize;
- NSString *blockHash = node.pithosContainer.blockHash;
-
- for (NSString *filePath in filenames) {
- BOOL isDirectory;
- if ([defaultManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
- if (!isDirectory) {
- // Upload file
- NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
- dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_async(queue, ^{
- NSError *error = nil;
- NSString *contentType = [PithosFileUtilities contentTypeOfFile:filePath error:&error];
- if (contentType == nil)
- contentType = @"application/octet-stream";
- if (error)
- NSLog(@"contentType detection error: %@", error);
- NSArray *hashes = nil;
- ASIPithosObjectRequest *objectRequest = [PithosFileUtilities writeObjectDataRequestWithContainerName:containerName
- objectName:objectName
- contentType:contentType
- blockSize:blockSize
- blockHash:blockHash
- forFile:filePath
- checkIfExists:YES
- hashes:&hashes];
- if (objectRequest) {
- objectRequest.delegate = self;
- objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
- objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
- objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
- containerName, @"containerName",
- objectName, @"objectName",
- contentType, @"contentType",
- [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
- blockHash, @"blockHash",
- filePath, @"filePath",
- hashes, @"hashes",
- node, @"node",
- [NSNumber numberWithUnsignedInteger:10], @"iteration",
- nil];
- [objectRequest startAsynchronous];
- }
- });
- } else {
- // Upload directory, confirm first
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"Upload directory"];
- [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to upload it and its contents?", filePath]];
- [alert addButtonWithTitle:@"OK"];
- [alert addButtonWithTitle:@"Cancel"];
- NSInteger choice = [alert runModal];
- if (choice == NSAlertFirstButtonReturn) {
+ if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
+ NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
+ NSLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames);
+ if ((column != -1) && (filenames != nil)) {
+ PithosNode *node = nil;
+ if (row != -1)
+ node = [browser itemAtRow:row inColumn:column];
+ else
+ node = [browser parentForItemsInColumn:column];
+ NSLog(@"drag in node: %@", node.url);
+ if (([node class] != [PithosSubdirNode class]) && ([node class] != [PithosContainerNode class]))
+ return NO;
+
+ NSFileManager *defaultManager = [NSFileManager defaultManager];
+ NSString *containerName = [NSString stringWithString:node.pithosContainer.name];
+ NSString *objectNamePrefix;
+ if ([node class] == [PithosSubdirNode class])
+ objectNamePrefix = [NSString stringWithString:node.pithosObject.name];
+ else
+ objectNamePrefix = [NSString string];
+ NSUInteger blockSize = node.pithosContainer.blockSize;
+ NSString *blockHash = node.pithosContainer.blockHash;
+
+ for (NSString *filePath in filenames) {
+ BOOL isDirectory;
+ if ([defaultManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
+ if (!isDirectory) {
+ // Upload file
NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
- NSMutableArray *objectNames = nil;
- NSMutableArray *contentTypes = nil;
- NSMutableArray *filePaths = nil;
- NSMutableArray *hashesArrays = nil;
- NSMutableArray *directoryObjectRequests = nil;
- NSArray *objectRequests = [PithosFileUtilities writeObjectDataRequestsWithContainerName:containerName
- objectName:objectName
- blockSize:blockSize
- blockHash:blockHash
- forDirectory:filePath
- checkIfExists:YES
- objectNames:&objectNames
- contentTypes:&contentTypes
- filePaths:&filePaths
- hashesArrays:&hashesArrays
- directoryObjectRequests:&directoryObjectRequests];
- for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) {
+ NSError *error = nil;
+ NSString *contentType = [PithosFileUtilities contentTypeOfFile:filePath error:&error];
+ if (contentType == nil)
+ contentType = @"application/octet-stream";
+ if (error)
+ NSLog(@"contentType detection error: %@", error);
+ NSArray *hashes = nil;
+ ASIPithosObjectRequest *objectRequest = [PithosFileUtilities writeObjectDataRequestWithContainerName:containerName
+ objectName:objectName
+ contentType:contentType
+ blockSize:blockSize
+ blockHash:blockHash
+ forFile:filePath
+ checkIfExists:YES
+ hashes:&hashes];
+ if (objectRequest) {
+ objectRequest.delegate = self;
+ objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
+ objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
+ objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ containerName, @"containerName",
+ objectName, @"objectName",
+ contentType, @"contentType",
+ [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
+ blockHash, @"blockHash",
+ filePath, @"filePath",
+ hashes, @"hashes",
+ node, @"node",
+ [NSNumber numberWithUnsignedInteger:10], @"iteration",
+ nil];
+ [objectRequest startAsynchronous];
+ }
+ });
+ } else {
+ // Upload directory, confirm first
+ NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+ [alert setMessageText:@"Upload directory"];
+ [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to upload it and its contents?", filePath]];
+ [alert addButtonWithTitle:@"OK"];
+ [alert addButtonWithTitle:@"Cancel"];
+ NSInteger choice = [alert runModal];
+ if (choice == NSAlertFirstButtonReturn) {
+ NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_async(queue, ^{
+ NSMutableArray *objectNames = nil;
+ NSMutableArray *contentTypes = nil;
+ NSMutableArray *filePaths = nil;
+ NSMutableArray *hashesArrays = nil;
+ NSMutableArray *directoryObjectRequests = nil;
+ NSArray *objectRequests = [PithosFileUtilities writeObjectDataRequestsWithContainerName:containerName
+ objectName:objectName
+ blockSize:blockSize
+ blockHash:blockHash
+ forDirectory:filePath
+ checkIfExists:YES
+ objectNames:&objectNames
+ contentTypes:&contentTypes
+ filePaths:&filePaths
+ hashesArrays:&hashesArrays
+ directoryObjectRequests:&directoryObjectRequests];
+ for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) {
+ objectRequest.delegate = self;
+ objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
+ objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
+ [objectRequest startAsynchronous];
+ }
+ if (objectRequests) {
+ for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) {
+ ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i];
+ objectRequest.delegate = self;
+ objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
+ objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
+ objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ containerName, @"containerName",
+ [objectNames objectAtIndex:i], @"objectName",
+ [contentTypes objectAtIndex:i], @"contentType",
+ [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
+ blockHash, @"blockHash",
+ [filePaths objectAtIndex:i], @"filePath",
+ [hashesArrays objectAtIndex:i], @"hashes",
+ [NSNumber numberWithUnsignedInteger:10], @"iteration",
+ nil];
+ [objectRequest startAsynchronous];
+ }
+ }
+ });
+ }
+ }
+ }
+
+ }
+ return YES;
+ }
+ } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
+ NSLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes);
+ if ((column != -1) && (draggedNodes != nil)) {
+ PithosNode *dropNode = nil;
+ if (row != -1)
+ dropNode = [browser itemAtRow:row inColumn:column];
+ else
+ dropNode = [browser parentForItemsInColumn:column];
+ NSLog(@"drag local node: %@", dropNode.url);
+ if (([dropNode class] != [PithosSubdirNode class]) && ([dropNode class] != [PithosContainerNode class]))
+ return NO;
+
+ NSString *containerName = [NSString stringWithString:dropNode.pithosContainer.name];
+ NSString *objectNamePrefix;
+ if ([dropNode class] == [PithosSubdirNode class])
+ objectNamePrefix = [NSString stringWithString:dropNode.pithosObject.name];
+ else
+ objectNamePrefix = [NSString string];
+
+ if ([info draggingSourceOperationMask] & NSDragOperationMove) {
+ for (PithosNode *node in draggedNodes) {
+ if (([node class] == [PithosObjectNode class]) ||
+ (([node class] == [PithosSubdirNode class]) &&
+ !node.pithosObject.subdir &&
+ [node.pithosObject.name hasSuffix:@"/"])) {
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_async(queue, ^{
+ NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
+ if ([node.pithosObject.name hasSuffix:@"/"])
+ destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
+ ASIPithosObjectRequest *objectRequest = [PithosFileUtilities moveObjectRequestWithContainerName:node.pithosContainer.name
+ objectName:node.pithosObject.name
+ destinationContainerName:containerName
+ destinationObjectName:destinationObjectName
+ checkIfExists:YES];
+ if (objectRequest) {
+ objectRequest.delegate = self;
+ objectRequest.didFinishSelector = @selector(moveFinished:);
+ objectRequest.didFailSelector = @selector(moveFailed:);
+ objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ node.parent, @"node",
+ dropNode, @"dropNode",
+ nil];
+ [objectRequest startAsynchronous];
+ }
+ });
+ } else if ([node class] == [PithosSubdirNode class]) {
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_async(queue, ^{
+ NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
+ if ([node.pithosObject.name hasSuffix:@"/"])
+ destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
+ NSArray *objectRequests = [PithosFileUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
+ objectName:node.pithosObject.name
+ destinationContainerName:containerName
+ destinationObjectName:destinationObjectName
+ checkIfExists:YES];
+ if (objectRequests) {
+ for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+ objectRequest.delegate = self;
+ objectRequest.didFinishSelector = @selector(moveFinished:);
+ objectRequest.didFailSelector = @selector(moveFailed:);
+ objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ node.parent, @"node",
+ dropNode, @"dropNode",
+ nil];
+ [objectRequest startAsynchronous];
+ }
+ }
+ });
+ }
+ }
+ } else if ([info draggingSourceOperationMask] & NSDragOperationCopy) {
+ for (PithosNode *node in draggedNodes) {
+ if (([node class] == [PithosObjectNode class]) ||
+ (([node class] == [PithosSubdirNode class]) &&
+ !node.pithosObject.subdir &&
+ [node.pithosObject.name hasSuffix:@"/"])) {
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_async(queue, ^{
+ NSString *destinationObjectName;
+ if (![dropNode isEqualTo:node.parent]) {
+ destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
+ if ([node.pithosObject.name hasSuffix:@"/"])
+ destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
+ } else {
+ destinationObjectName = [PithosFileUtilities safeObjectNameForContainerName:containerName
+ objectName:node.pithosObject.name];
+ }
+ ASIPithosObjectRequest *objectRequest = [PithosFileUtilities copyObjectRequestWithContainerName:node.pithosContainer.name
+ objectName:node.pithosObject.name
+ destinationContainerName:containerName
+ destinationObjectName:destinationObjectName
+ checkIfExists:YES];
+ if (objectRequest) {
objectRequest.delegate = self;
- objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
- objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
+ objectRequest.didFinishSelector = @selector(copyFinished:);
+ objectRequest.didFailSelector = @selector(copyFailed:);
+ objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ dropNode, @"dropNode",
+ nil];
[objectRequest startAsynchronous];
}
+ });
+ } else if ([node class] == [PithosSubdirNode class]) {
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_async(queue, ^{
+ NSString *destinationObjectName;
+ if (![dropNode isEqualTo:node.parent]) {
+ destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
+ if ([node.pithosObject.name hasSuffix:@"/"])
+ destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
+ } else {
+ destinationObjectName = [PithosFileUtilities safeSubdirNameForContainerName:containerName
+ subdirName:node.pithosObject.name];
+ }
+ NSArray *objectRequests = [PithosFileUtilities copyObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
+ objectName:node.pithosObject.name
+ destinationContainerName:containerName
+ destinationObjectName:destinationObjectName
+ checkIfExists:YES];
if (objectRequests) {
- for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) {
- ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i];
+ for (ASIPithosObjectRequest *objectRequest in objectRequests) {
objectRequest.delegate = self;
- objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
- objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
+ objectRequest.didFinishSelector = @selector(copyFinished:);
+ objectRequest.didFailSelector = @selector(copyFailed:);
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
- containerName, @"containerName",
- [objectNames objectAtIndex:i], @"objectName",
- [contentTypes objectAtIndex:i], @"contentType",
- [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
- blockHash, @"blockHash",
- [filePaths objectAtIndex:i], @"filePath",
- [hashesArrays objectAtIndex:i], @"hashes",
- [NSNull null], @"node",
- [NSNumber numberWithUnsignedInteger:10], @"iteration",
+ dropNode, @"dropNode",
nil];
[objectRequest startAsynchronous];
}
}
});
+
}
}
}
-
+ return YES;
}
- return YES;
}
-
return NO;
}
if (objectRequest.responseStatusCode == 201) {
NSLog(@"Object created: %@", [objectRequest url]);
PithosNode *node = [objectRequest.userInfo objectForKey:@"node"];
- if (node != (id)[NSNull null]) {
- [node.parent invalidateChildren];
- node.parent.children;
+ if (node) {
+ [node invalidateChildren];
+ node.children;
} else {
[self refresh:nil];
}
[PithosFileUtilities httpRequestErrorAlertWithRequest:objectRequest];
}
+- (void)moveFinished:(ASIPithosObjectRequest *)objectRequest {
+ NSLog(@"Move object completed: %@", [objectRequest url]);
+ if (objectRequest.responseStatusCode == 201) {
+ PithosNode *node = [objectRequest.userInfo objectForKey:@"node"];
+ PithosNode *dropNode = [objectRequest.userInfo objectForKey:@"dropNode"];
+ if (node) {
+ [node invalidateChildren];
+ node.children;
+ }
+ if (dropNode) {
+ [dropNode invalidateChildren];
+ dropNode.children;
+ }
+ if (!node || !dropNode)
+ [self refresh:nil];
+ } else {
+ [PithosFileUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
+ }
+}
+
+- (void)moveFailed:(ASIPithosObjectRequest *)objectRequest {
+ NSLog(@"Move object failed");
+ [PithosFileUtilities httpRequestErrorAlertWithRequest:objectRequest];
+}
+
+- (void)copyFinished:(ASIPithosObjectRequest *)objectRequest {
+ NSLog(@"Copy object completed: %@", [objectRequest url]);
+ if (objectRequest.responseStatusCode == 201) {
+ PithosNode *dropNode = [objectRequest.userInfo objectForKey:@"dropNode"];
+ if (dropNode) {
+ [dropNode invalidateChildren];
+ dropNode.children;
+ } else {
+ [self refresh:nil];
+ }
+ } else {
+ [PithosFileUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
+ }
+}
+
+- (void)copyFailed:(ASIPithosObjectRequest *)objectRequest {
+ NSLog(@"Copy object failed");
+ [PithosFileUtilities httpRequestErrorAlertWithRequest:objectRequest];
+}
+
#pragma mark -
#pragma mark NSSplitViewDelegate
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];
+ ASIPithosObjectRequest *objectRequest = [PithosFileUtilities moveObjectRequestWithContainerName:node.pithosContainer.name
+ objectName:node.pithosObject.name
+ destinationContainerName:@"trash"
+ destinationObjectName:safeObjectName
+ checkIfExists:NO];
+ if (objectRequest) {
+ objectRequest.delegate = self;
+ objectRequest.didFinishSelector = @selector(moveToTrashFinished:);
+ objectRequest.didFailSelector = @selector(moveToTrashFailed:);
+ [objectRequest startAsynchronous];
+ }
}
});
} else if ([node class] == [PithosSubdirNode class]) {
NSArray *objectRequests = [PithosFileUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
objectName:node.pithosObject.name
destinationContainerName:@"trash"
- destinationObjectName:safeObjectName];
+ destinationObjectName:safeObjectName
+ checkIfExists:NO];
if (objectRequests) {
for (ASIPithosObjectRequest *objectRequest in objectRequests) {
objectRequest.delegate = self;
if (objectRequest.responseStatusCode == 201) {
NSLog(@"New application/directory object created: %@", [objectRequest url]);
PithosNode *node = [objectRequest.userInfo objectForKey:@"node"];
- [node invalidateChildren];
- node.children;
+ if (node) {
+ [node invalidateChildren];
+ node.children;
+ } else {
+ [self refresh:nil];
+ }
} else {
[PithosFileUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
}
forFile:(NSString *)filePath
checkIfExists:(BOOL)ifExists
hashes:(NSArray **)hashes {
- if (ifExists) {
- NSError *error;
- BOOL isDirectory;
- BOOL objectExists = [self objectExistsAtContainerName:containerName objectName:objectName
- error:&error isDirectory:&isDirectory];
- if (error) {
- return nil;
- } else if (objectExists) {
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- if (isDirectory) {
- [alert setMessageText:@"Directory Exists"];
- [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
- } else {
- [alert setMessageText:@"Object Exists"];
- [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
- }
- [alert addButtonWithTitle:@"OK"];
- [alert addButtonWithTitle:@"Cancel"];
- NSInteger choice = [alert runModal];
- if (choice == NSAlertSecondButtonReturn)
- return nil;
- }
- }
+ if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName])
+ return nil;
if (*hashes == nil)
*hashes = [HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize];
filePaths:(NSMutableArray **)filePaths
hashesArrays:(NSMutableArray **)hashesArrays
directoryObjectRequests:(NSMutableArray **) directoryObjectRequests {
- if (ifExists) {
- NSError *error = nil;
- BOOL isDirectory;
- BOOL objectExists = [self objectExistsAtContainerName:containerName objectName:objectName
- error:&error isDirectory:&isDirectory];
- if (error) {
- return nil;
- } else if (objectExists) {
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- if (isDirectory) {
- [alert setMessageText:@"Directory Exists"];
- [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
- } else {
- [alert setMessageText:@"Object Exists"];
- [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
- }
- [alert addButtonWithTitle:@"OK"];
- [alert addButtonWithTitle:@"Cancel"];
- NSInteger choice = [alert runModal];
- if (choice == NSAlertSecondButtonReturn)
- return nil;
- }
- }
+ if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName])
+ return nil;
NSFileManager *defaultManager = [NSFileManager defaultManager];
NSError *error = nil;
}
#pragma mark -
-#pragma mark Move to Trash / Delete
+#pragma mark Delete
+ (NSArray *)deleteObjectRequestsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName {
NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil];
return objectRequests;
}
+#pragma mark -
+#pragma mark Copy
+
++ (ASIPithosObjectRequest *)copyObjectRequestWithContainerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ destinationContainerName:(NSString *)destinationContainerName
+ destinationObjectName:(NSString *)destinationObjectName
+ checkIfExists:(BOOL)ifExists {
+ if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName])
+ return nil;
+
+ ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:destinationObjectName
+ sourceVersion:nil];
+ return objectRequest;
+}
+
++ (NSArray *)copyObjectRequestsForSubdirWithContainerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ destinationContainerName:(NSString *)destinationContainerName
+ destinationObjectName:(NSString *)destinationObjectName
+ checkIfExists:(BOOL)ifExists {
+ if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName])
+ return nil;
+
+ 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 copyObjectDataRequestWithContainerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:objectName
+ sourceVersion:nil]];
+ for (ASIPithosObject *object in objects) {
+ [objectRequests addObject:[ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
+ objectName:object.name
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:object.name
+ sourceVersion:nil]];
+ }
+ } else {
+ if (![objectName hasSuffix:@"/"]) {
+ [objectRequests addObject:[ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:destinationObjectName
+ sourceVersion:nil]];
+ }
+ NSRange prefixRange = NSMakeRange(0, [objectName length]);
+ NSString *newObjectName;
+ for (ASIPithosObject *object in objects) {
+ newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
+ withString:destinationObjectName
+ options:NSAnchoredSearch
+ range:prefixRange];
+ [objectRequests addObject:[ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
+ objectName:object.name
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:newObjectName
+ sourceVersion:nil]];
+ }
+ }
+
+ if ([objectRequests count] == 0)
+ return nil;
+ return objectRequests;
+}
+
+#pragma mark -
+#pragma mark Move
+
++ (ASIPithosObjectRequest *)moveObjectRequestWithContainerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ destinationContainerName:(NSString *)destinationContainerName
+ destinationObjectName:(NSString *)destinationObjectName
+ checkIfExists:(BOOL)ifExists {
+ if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName])
+ return nil;
+
+ ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:destinationObjectName];
+ return objectRequest;
+}
+
+ (NSArray *)moveObjectRequestsForSubdirWithContainerName:(NSString *)containerName
objectName:(NSString *)objectName
destinationContainerName:(NSString *)destinationContainerName
- destinationObjectName:(NSString *)destinationObjectName {
+ destinationObjectName:(NSString *)destinationObjectName
+ checkIfExists:(BOOL)ifExists {
+ if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName])
+ return nil;
+
NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil];
if (objects == nil)
return nil;
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
isPublic:ASIPithosObjectRequestPublicIgnore
metadata:nil
destinationContainerName:destinationContainerName
- destinationObjectName:newObjectName]];
+ destinationObjectName:destinationObjectName]];
}
+ NSRange prefixRange = NSMakeRange(0, [objectName length]);
+ NSString *newObjectName;
for (ASIPithosObject *object in objects) {
newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
withString:destinationObjectName
#pragma mark -
#pragma mark Helper Methods
+// Size of the file in bytes
+ (NSUInteger)bytesOfFile:(NSString *)filePath {
NSFileManager *defaultManager = [NSFileManager defaultManager];
NSDictionary *attributes = [defaultManager attributesOfItemAtPath:filePath error:nil];
return [[attributes objectForKey:NSFileSize] intValue];
}
+// Content type of the file or nil if it cannot be determined
+ (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error {
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]
return [response MIMEType];
}
+// Returns if an object exists at the given container/object path and if this object is an application/directory
+// If an error occured an alert is shown and it is returned so the caller won't proceed
+ (BOOL)objectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
error:(NSError **)error isDirectory:(BOOL *)isDirectory {
ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectMetadataRequestWithContainerName:containerName
return NO;
}
+// Returns if the called should proceed, after an interactive check if an object exists
+// at the given container/object path is performed
++ (BOOL)proceedIfObjectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName {
+ NSError *error = nil;
+ BOOL isDirectory;
+ BOOL objectExists = [self objectExistsAtContainerName:containerName
+ objectName:objectName
+ error:&error
+ isDirectory:&isDirectory];
+ if (error) {
+ return NO;
+ } else if (objectExists) {
+ NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+ if (isDirectory) {
+ [alert setMessageText:@"Directory Exists"];
+ [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
+ } else {
+ [alert setMessageText:@"Object Exists"];
+ [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
+ }
+ [alert addButtonWithTitle:@"OK"];
+ [alert addButtonWithTitle:@"Cancel"];
+ NSInteger choice = [alert runModal];
+ if (choice == NSAlertSecondButtonReturn)
+ return NO;
+ }
+ return YES;
+}
+
+
+// List of objects at the given container/object path, with prefix and or delimiter
+ (NSArray *)objectsWithContainerName:(NSString *)containerName objectNamePrefix:(NSString *)objectNamePrefix
delimiter:(NSString *)delimiter {
NSMutableArray *objects = [NSMutableArray array];
return objects;
}
+// List of objects at the given container/object path, that may be a subdir or an application/directory,
+// with prefix and or delimiter
+ (NSArray *)objectsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName
delimiter:(NSString *)delimiter {
NSString *subdirNamePrefix = [NSString stringWithString:objectName];
return [self objectsWithContainerName:containerName objectNamePrefix:subdirNamePrefix delimiter:delimiter];
}
+// A safe object name at the given container/object path
+// The original name has " %d" appended to it before any ".*" suffix, for the first integer that produces a name that is free to use
+// If the original name hasn't got a "." but has a "/" suffix, then it is replaced with " %d/" instead
+// Subdirs are taken into consideration
+ (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)];
+ NSString *objectNamePrefix;
+ NSString *objectNameExtraSuffix;
+ NSRange lastDotRange = [objectName rangeOfString:@"." options:NSBackwardsSearch];
+ if (lastDotRange.length == 1) {
+ objectNamePrefix = [objectName substringToIndex:lastDotRange.location];
+ objectNameExtraSuffix = [objectName substringFromIndex:lastDotRange.location];
+ } else if ([objectName hasSuffix:@"/"]) {
+ objectNamePrefix = [objectName substringToIndex:([objectName length] - 1)];
objectNameExtraSuffix = [NSString stringWithString:@"/"];
+ } else {
+ objectNamePrefix = [NSString stringWithString:objectName];
+ objectNameExtraSuffix = [NSString string];
}
NSArray *objects = [self objectsWithContainerName:containerName
objectNamePrefix:[objectNamePrefix stringByDeletingLastPathComponent]
}
if (![objectNames containsObject:objectName])
return objectName;
- NSUInteger objectNameSuffix = 1;
+ NSUInteger objectNameSuffix = 2;
NSString *safeObjectName;
do {
safeObjectName = [objectNamePrefix stringByAppendingFormat:@" %lu%@", objectNameSuffix, objectNameExtraSuffix];
return safeObjectName;
}
+// A safe object name at the given container/object path that may be a subdir or application/directory
+// The original name has " %d" appended to it, for the first integer that produces a name that is free to use
+// If the original name has a "/" suffix, then it is replaced with " %d/" instead
+// Subdirs are taken into consideration
+ (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)];
+ NSString *subdirNamePrefix;
+ NSString *subdirNameExtraSuffix;
+ if ([subdirName hasSuffix:@"/"]) {
+ subdirNamePrefix = [subdirName substringToIndex:([subdirName length] - 1)];
subdirNameExtraSuffix = [NSString stringWithString:@"/"];
+ } else {
+ subdirNamePrefix = [NSString stringWithString:subdirName];
+ subdirNameExtraSuffix = [NSString string];
}
NSArray *objects = [self objectsWithContainerName:containerName
objectNamePrefix:[subdirNamePrefix stringByDeletingLastPathComponent]
}
if (![objectNames containsObject:subdirNamePrefix])
return subdirName;
- NSUInteger subdirNameSuffix = 1;
+ NSUInteger subdirNameSuffix = 2;
NSString *safeSubdirName;
do {
safeSubdirName = [subdirNamePrefix stringByAppendingFormat:@" %lu", subdirNameSuffix];