2 // PithosBrowserController.m
5 // Copyright 2011 GRNET S.A. All rights reserved.
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
11 // 1. Redistributions of source code must retain the above
12 // copyright notice, this list of conditions and the following
15 // 2. Redistributions in binary form must reproduce the above
16 // copyright notice, this list of conditions and the following
17 // disclaimer in the documentation and/or other materials
18 // provided with the distribution.
20 // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
33 // The views and conclusions contained in the software and
34 // documentation are those of the authors and should not be
35 // interpreted as representing official policies, either expressed
36 // or implied, of GRNET S.A.
38 #import "PithosBrowserController.h"
39 #import "PithosNode.h"
40 #import "PithosAccountNode.h"
41 #import "PithosContainerNode.h"
42 #import "PithosSubdirNode.h"
43 #import "PithosObjectNode.h"
44 #import "PithosSharingAccountsNode.h"
45 #import "PithosEmptyNode.h"
46 #import "ImageAndTextCell.h"
47 #import "FileSystemBrowserCell.h"
48 #import "ASIPithosRequest.h"
49 #import "ASIPithosContainerRequest.h"
50 #import "ASIPithosObjectRequest.h"
51 #import "ASIPithosAccount.h"
52 #import "ASIPithosContainer.h"
53 #import "ASIPithosObject.h"
54 #import "PithosUtilities.h"
55 #import "UsingSizeTransformer.h"
57 @interface PithosBrowserCell : FileSystemBrowserCell {}
60 @implementation PithosBrowserCell
63 if ((self = [super init])) {
64 [self setLineBreakMode:NSLineBreakByTruncatingMiddle];
65 [self setEditable:YES];
70 - (void)setObjectValue:(id)object {
71 if ([object isKindOfClass:[PithosNode class]]) {
72 PithosNode *node = (PithosNode *)object;
73 [self setStringValue:node.displayName];
74 [self setImage:node.icon];
76 [super setObjectValue:object];
82 @interface PithosOutlineViewCell : ImageAndTextCell {}
85 @implementation PithosOutlineViewCell
87 - (void)setObjectValue:(id)object {
88 if ([object isKindOfClass:[PithosNode class]]) {
89 PithosNode *node = (PithosNode *)object;
90 [self setStringValue:node.displayName];
91 [self setImage:node.icon];
92 [self setEditable:NO];
94 [super setObjectValue:object];
100 @interface PithosBrowserController (Private)
101 - (void)resetContainers:(NSNotification *)notification;
102 - (BOOL)uploadFiles:(NSArray *)filenames toNode:(PithosNode *)destinationNode;
103 - (BOOL)moveNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode;
104 - (BOOL)copyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode;
107 @implementation PithosBrowserController
108 @synthesize accountNode;
109 @synthesize verticalSplitView, horizontalSplitView, leftTopView, leftBottomView, outlineView, browser, outlineViewMenu, browserMenu;
110 @synthesize draggedNodes, draggedParentNode;
111 @synthesize clipboardNodes, clipboardParentNode, clipboardCopy;
112 @synthesize activityTextField, activityProgressIndicator;
115 #pragma Object Lifecycle
118 return [super initWithWindowNibName:@"PithosBrowserController"];
122 [[NSNotificationCenter defaultCenter] removeObserver:self];
123 [refreshTimer invalidate];
124 [refreshTimer release];
125 [clipboardParentNode release];
126 [clipboardNodes release];
127 [draggedParentNode release];
128 [draggedNodes release];
129 [sharedPreviewController release];
130 [othersSharedNode release];
131 [mySharedNode release];
132 [sharedNode release];
133 [containersNodeChildren release];
134 [containersNode release];
135 [accountNode release];
140 - (void)awakeFromNib {
141 [super awakeFromNib];
143 [browser registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]];
144 [browser setDraggingSourceOperationMask:(NSDragOperationCopy|NSDragOperationMove) forLocal:YES];
145 [browser setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
147 [outlineView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]];
148 [outlineView setDraggingSourceOperationMask:(NSDragOperationCopy|NSDragOperationMove) forLocal:YES];
149 [outlineView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
151 [browser setCellClass:[PithosBrowserCell class]];
154 - (void)resetContainers:(NSNotification *)notification {
155 [refreshTimer invalidate];
156 [refreshTimer release];
159 [browser loadColumnZero];
160 [containersNodeChildren removeAllObjects];
161 [outlineView reloadData];
162 // Expand the folder outline view
163 [outlineView expandItem:nil expandChildren:YES];
164 [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
167 [accountNode refresh];
168 [mySharedNode refresh];
169 [othersSharedNode refresh];
171 [activityFacility reset];
173 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(forceRefresh:) userInfo:self repeats:YES] retain];
176 - (void)windowDidLoad {
177 [super windowDidLoad];
179 [activityProgressIndicator setUsesThreadedAnimation:YES];
180 [activityProgressIndicator setMinValue:0.0];
181 [activityProgressIndicator setMaxValue:1.0];
182 activityFacility = [PithosActivityFacility defaultPithosActivityFacility];
183 activityFacility.delegate = self;
185 self.accountNode = [[[PithosAccountNode alloc] init] autorelease];
186 containersNode = [[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil];
187 containersNodeChildren = [[NSMutableArray alloc] init];
188 sharedNode = [[PithosEmptyNode alloc] initWithDisplayName:@"SHARED" icon:nil];
189 mySharedNode = [[PithosAccountNode alloc] init];
190 mySharedNode.displayName = @"my shared";
191 mySharedNode.shared = YES;
192 mySharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)];
193 othersSharedNode = [[PithosSharingAccountsNode alloc] init];
194 othersSharedNode.displayName = @"others shared";
195 othersSharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGroupIcon)];
197 [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
199 // Register for updates
200 // PithosContainerNode updates browser nodes
201 [[NSNotificationCenter defaultCenter] addObserver:self
202 selector:@selector(pithosNodeChildrenUpdated:)
203 name:@"PithosContainerNodeChildrenUpdated"
205 // PithosSubdirNode updates browser nodes
206 [[NSNotificationCenter defaultCenter] addObserver:self
207 selector:@selector(pithosNodeChildrenUpdated:)
208 name:@"PithosSubdirNodeChildrenUpdated"
210 // PithosAccountNode accountNode updates outlineView container nodes
211 [[NSNotificationCenter defaultCenter] addObserver:self
212 selector:@selector(pithosAccountNodeChildrenUpdated:)
213 name:@"PithosAccountNodeChildrenUpdated"
215 // PithosAccountNode other than accountNode updates nodes
216 [[NSNotificationCenter defaultCenter] addObserver:self
217 selector:@selector(pithosNodeChildrenUpdated:)
218 name:@"PithosAccountNodeChildrenUpdated"
220 // PithosSharingAccountsNode othersSharedNode updates browser nodes
221 [[NSNotificationCenter defaultCenter] addObserver:self
222 selector:@selector(pithosNodeChildrenUpdated:)
223 name:@"PithosSharingAccountsNodeChildrenUpdated"
224 object:othersSharedNode];
225 // Updated authentication credentials reset containers in the outline view
226 [[NSNotificationCenter defaultCenter] addObserver:self
227 selector:@selector(resetContainers:)
228 name:@"PithosAuthenticationCredentialsUpdated"
230 // Request for browser refresh
231 [[NSNotificationCenter defaultCenter] addObserver:self
232 selector:@selector(pithosBrowserRefreshNeeded:)
233 name:@"PithosBrowserRefreshNeeeded"
238 #pragma mark Observers
240 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
241 PithosNode *node = (PithosNode *)[notification object];
242 if (node == accountNode)
244 NSLog(@"pithosNodeChildrenUpdated:%@", node.url);
245 NSInteger lastColumn = [browser lastColumn];
246 for (NSInteger column = lastColumn; column >= 0; column--) {
247 if ([[browser parentForItemsInColumn:column] isEqualTo:node]) {
248 [browser reloadColumn:column];
254 - (void)pithosAccountNodeChildrenUpdated:(NSNotification *)notification {
255 BOOL containerPithosFound = NO;
256 BOOL containerTrashFound = NO;
257 NSMutableIndexSet *removedContainersNodeChildren = [NSMutableIndexSet indexSet];
258 for (NSUInteger i = 0 ; i < [containersNodeChildren count] ; i++) {
259 if (![accountNode.children containsObject:[containersNodeChildren objectAtIndex:i]])
260 [removedContainersNodeChildren addIndex:i];
262 [containersNodeChildren removeObjectsAtIndexes:removedContainersNodeChildren];
263 for (PithosContainerNode *containerNode in accountNode.children) {
264 if ([containerNode.pithosContainer.name isEqualToString:@"pithos"]) {
265 if (![containersNodeChildren containsObject:containerNode])
266 [containersNodeChildren insertObject:containerNode atIndex:0];
267 containerPithosFound = YES;
268 } else if ([containerNode.pithosContainer.name isEqualToString:@"trash"]) {
269 NSUInteger insertIndex = 1;
270 if (!containerPithosFound)
272 if (![containersNodeChildren containsObject:containerNode])
273 [containersNodeChildren insertObject:containerNode atIndex:insertIndex];
274 containerTrashFound = YES;
275 } else if (![containersNodeChildren containsObject:containerNode]) {
276 [containersNodeChildren addObject:containerNode];
279 BOOL refreshAccountNode = NO;
280 if (!containerPithosFound) {
281 // Create pithos node
282 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"pithos"];
283 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
284 while (![containerRequest isFinished]) {
287 if ([containerRequest error]) {
288 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
290 refreshAccountNode = YES;
293 if (!containerTrashFound) {
295 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"trash"];
296 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
297 while (![containerRequest isFinished]) {
300 if ([containerRequest error]) {
301 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
303 refreshAccountNode = YES;
307 if (refreshAccountNode)
308 [accountNode refresh];
310 [outlineView reloadData];
312 // Expand the folder outline view
313 [outlineView expandItem:nil expandChildren:YES];
315 if ((rootNode == nil) || (rootNode == containersNode) || (rootNode == sharedNode)) {
316 rootNode = [containersNodeChildren objectAtIndex:0];
317 [browser loadColumnZero];
324 - (void)pithosBrowserRefreshNeeded:(NSNotification *)notification {
331 - (IBAction)forceRefresh:(id)sender {
333 [accountNode forceRefresh];
334 for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
335 PithosNode *node = (PithosNode *)[browser parentForItemsInColumn:column];
336 node.forcedRefresh = YES;
337 [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren];
339 [browser validateVisibleColumns];
342 - (IBAction)refresh:(id)sender {
343 if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) {
344 [self forceRefresh:sender];
347 [accountNode refresh];
348 for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
349 [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren];
351 [browser validateVisibleColumns];
356 #pragma mark NSBrowserDelegate
358 - (id)rootItemForBrowser:(NSBrowser *)browser {
362 - (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item {
363 PithosNode *node = (PithosNode *)item;
364 return node.children.count;
367 - (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
368 PithosNode *node = (PithosNode *)item;
369 return [node.children objectAtIndex:index];
372 - (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item {
373 PithosNode *node = (PithosNode *)item;
374 return node.isLeafItem;
377 - (id)browser:(NSBrowser *)browser objectValueForItem:(id)item {
378 PithosNode *node = (PithosNode *)item;
382 - (NSViewController *)browser:(NSBrowser *)browser previewViewControllerForLeafItem:(id)item {
383 if (sharedPreviewController == nil)
384 sharedPreviewController = [[NSViewController alloc] initWithNibName:@"PithosBrowserPreviewController" bundle:[NSBundle bundleForClass:[self class]]];
385 return sharedPreviewController;
388 //- (CGFloat)browser:(NSBrowser *)browser shouldSizeColumn:(NSInteger)columnIndex forUserResize:(BOOL)forUserResize toWidth:(CGFloat)suggestedWidth {
389 // if (!forUserResize) {
390 // id item = [browser parentForItemsInColumn:columnIndex];
391 // if ([self browser:browser isLeafItem:item]) {
392 // suggestedWidth = 200;
395 // return suggestedWidth;
398 - (BOOL)browser:(NSBrowser *)sender isColumnValid:(NSInteger)column {
404 - (BOOL)browser:(NSBrowser *)browser shouldEditItem:(id)item {
405 PithosNode *node = (PithosNode *)item;
406 if (node.shared || node.sharingAccount ||
407 ([node class] == [PithosContainerNode class]) || ([node class] == [PithosAccountNode class]))
412 - (void)browser:(NSBrowser *)browser setObjectValue:(id)object forItem:(id)item {
413 PithosNode *node = (PithosNode *)item;
414 NSString *newName = (NSString *)object;
415 NSUInteger newNameLength = [newName length];
416 NSRange firstSlashRange = [newName rangeOfString:@"/"];
417 if ((newNameLength == 0) ||
418 ((firstSlashRange.length == 1) && (firstSlashRange.location != (newNameLength - 1))) ||
419 ([newName isEqualToString:node.displayName])) {
422 if (([node class] == [PithosObjectNode class]) ||
423 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
424 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
425 dispatch_async(queue, ^{
426 NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
427 if ([newName hasSuffix:@"/"])
428 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
429 NSError *error = nil;
431 if ([PithosUtilities objectExistsAtContainerName:node.pithosContainer.name
432 objectName:destinationObjectName
434 isDirectory:&isDirectory
435 sharingAccount:nil]) {
436 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
437 [alert setMessageText:@"Name Taken"];
438 [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
439 [alert addButtonWithTitle:@"OK"];
445 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name
446 objectName:node.pithosObject.name
447 destinationContainerName:node.pithosContainer.name
448 destinationObjectName:destinationObjectName
451 objectRequest.delegate = self;
452 objectRequest.didFinishSelector = @selector(moveFinished:);
453 objectRequest.didFailSelector = @selector(moveFailed:);
454 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
455 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
456 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
457 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
458 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
459 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
460 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
461 [NSDictionary dictionaryWithObjectsAndKeys:
462 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
463 [NSNumber numberWithBool:YES], @"refresh",
464 activity, @"activity",
465 [NSNumber numberWithUnsignedInteger:10], @"retries",
467 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
470 } else if ([node class] == [PithosSubdirNode class]) {
471 if (firstSlashRange.length == 1)
473 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
474 dispatch_async(queue, ^{
475 NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
476 NSError *error = nil;
478 if ([PithosUtilities objectExistsAtContainerName:node.pithosContainer.name
479 objectName:destinationObjectName
481 isDirectory:&isDirectory
482 sharingAccount:nil]) {
483 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
484 [alert setMessageText:@"Name Taken"];
485 [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
486 [alert addButtonWithTitle:@"OK"];
492 if (node.pithosObject.subdir)
493 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
494 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
495 objectName:node.pithosObject.name
496 destinationContainerName:node.pithosContainer.name
497 destinationObjectName:destinationObjectName
499 if (objectRequests) {
500 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
501 objectRequest.delegate = self;
502 objectRequest.didFinishSelector = @selector(moveFinished:);
503 objectRequest.didFailSelector = @selector(moveFailed:);
504 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
505 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
506 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
507 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
508 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
509 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
510 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
511 [NSDictionary dictionaryWithObjectsAndKeys:
512 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
513 [NSNumber numberWithBool:YES], @"refresh",
514 activity, @"activity",
515 [NSNumber numberWithUnsignedInteger:10], @"retries",
517 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
524 #pragma mark Drag and Drop source
526 - (BOOL)browser:(NSBrowser *)aBrowser canDragRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column
527 withEvent:(NSEvent *)event {
528 NSIndexPath *baseIndexPath = [browser indexPathForColumn:column];
529 __block BOOL result = YES;
530 [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
531 PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:idx]];
532 if (([node class] == [PithosContainerNode class]) || ([node class] == [PithosAccountNode class])) {
540 - (BOOL)browser:(NSBrowser *)aBrowser writeRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column
541 toPasteboard:(NSPasteboard *)pasteboard {
542 NSMutableArray *propertyList = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
543 NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
544 NSIndexPath *baseIndexPath = [browser indexPathForColumn:column];
545 [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
546 PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:idx]];
547 [propertyList addObject:[node.pithosObject.name pathExtension]];
548 [nodes addObject:node];
551 [pasteboard declareTypes:[NSArray arrayWithObject:NSFilesPromisePboardType] owner:self];
552 [pasteboard setPropertyList:propertyList forType:NSFilesPromisePboardType];
553 self.draggedNodes = nodes;
554 self.draggedParentNode = [browser parentForItemsInColumn:column];
558 - (NSArray *)browser:(NSBrowser *)aBrowser namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
559 forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
560 NSMutableArray *names = [NSMutableArray arrayWithCapacity:[draggedNodes count]];
561 for (PithosNode *node in draggedNodes) {
562 // If the node is a subdir ask if the whole tree should be downloaded
563 if ([node class] == [PithosSubdirNode class]) {
564 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
565 [alert setMessageText:@"Download directory"];
566 [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to download its contents?", node.displayName]];
567 [alert addButtonWithTitle:@"OK"];
568 [alert addButtonWithTitle:@"Cancel"];
569 NSInteger choice = [alert runModal];
570 if (choice == NSAlertFirstButtonReturn) {
571 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
572 dispatch_async(queue, ^{
573 NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithContainerName:node.pithosContainer.name
574 objectName:node.pithosObject.name
575 toDirectory:[dropDestination path]
577 sharingAccount:node.sharingAccount];
578 if (objectRequests) {
579 for (__block ASIPithosObjectRequest *objectRequest in objectRequests) {
580 [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
581 objectRequest.delegate = self;
582 objectRequest.didFinishSelector = @selector(downloadObjectFinished:);
583 objectRequest.didFailSelector = @selector(downloadObjectFailed:);
584 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload
585 message:[NSString stringWithFormat:@"Downloading '%@' (0%%)", [objectRequest.userInfo objectForKey:@"fileName"]]
586 totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue]
588 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
589 [NSDictionary dictionaryWithObjectsAndKeys:
590 activity, @"activity",
591 [NSNumber numberWithUnsignedInteger:10], @"retries",
593 [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
594 [activityFacility updateActivity:activity
595 withMessage:[NSString stringWithFormat:@"Downloading '%@' (%.0f%%)", [objectRequest.userInfo valueForKey:@"fileName"], (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
596 totalBytes:activity.totalBytes
597 currentBytes:(activity.currentBytes + size)];
599 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
605 __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithContainerName:node.pithosContainer.name
606 objectName:node.pithosObject.name
607 toDirectory:[dropDestination path]
609 sharingAccount:node.sharingAccount];
611 [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
612 objectRequest.delegate = self;
613 objectRequest.didFinishSelector = @selector(downloadObjectFinished:);
614 objectRequest.didFailSelector = @selector(downloadObjectFailed:);
615 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload
616 message:[NSString stringWithFormat:@"Downloading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
617 totalBytes:node.pithosObject.bytes
619 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
620 [NSDictionary dictionaryWithObjectsAndKeys:
621 activity, @"activity",
622 [NSNumber numberWithUnsignedInteger:10], @"retries",
624 [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
625 [activityFacility updateActivity:activity
626 withMessage:[NSString stringWithFormat:@"Downloading '%@' (%.0f%%)", [objectRequest.userInfo valueForKey:@"fileName"], (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
627 totalBytes:activity.totalBytes
628 currentBytes:(activity.currentBytes + size)];
630 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
637 #pragma mark Drag and Drop destination
639 - (NSDragOperation)browser:aBrowser
640 validateDrop:(id<NSDraggingInfo>)info
641 proposedRow:(NSInteger *)row
642 column:(NSInteger *)column
643 dropOperation:(NSBrowserDropOperation *)dropOperation {
644 NSDragOperation result = NSDragOperationNone;
645 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
646 // For a drop above, the drop is redirected to the parent item
647 if (*dropOperation == NSBrowserDropAbove)
649 // Only allow dropping in folders
651 PithosNode *dropNode;
653 // Check if the node is not a folder and if so redirect to the parent item
654 dropNode = [browser itemAtRow:*row inColumn:*column];
655 if ([dropNode class] == [PithosObjectNode class])
659 dropNode = [browser parentForItemsInColumn:*column];
661 if (!dropNode.shared &&
662 (!dropNode.sharingAccount ||
663 ([dropNode class] == [PithosSubdirNode class]) ||
664 ([dropNode class] == [PithosContainerNode class]))) {
665 *dropOperation = NSBrowserDropOn;
666 result = NSDragOperationCopy;
669 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
670 // For a drop above, the drop is redirected to the parent item
671 if (*dropOperation == NSBrowserDropAbove)
673 // Only allow dropping in folders
675 PithosNode *dropNode;
677 // Check if the node is not a folder and if so redirect to the parent item
678 dropNode = [browser itemAtRow:*row inColumn:*column];
679 if ([dropNode class] == [PithosObjectNode class])
683 dropNode = [browser parentForItemsInColumn:*column];
685 if (!dropNode.shared && !dropNode.sharingAccount) {
686 if ([info draggingSourceOperationMask] & NSDragOperationMove) {
687 // NSDragOperationCopy | NSDragOperationMove -> NSDragOperationMove
688 if ((([dropNode class] == [PithosContainerNode class]) ||
689 dropNode.pithosObject.subdir ||
690 ![dropNode.pithosObject.name hasSuffix:@"/"]) &&
691 ![dropNode isEqualTo:draggedParentNode]) {
692 // ![dropNode isEqualTo:draggedParentNode] &&
693 // ![draggedNodes containsObject:dropNode]) {
694 result = NSDragOperationMove;
696 } else if ([info draggingSourceOperationMask] & NSDragOperationCopy) {
697 // NSDragOperationCopy (Option modifier) -> NSDragOperationCopy
698 if (([dropNode class] == [PithosContainerNode class]) ||
699 dropNode.pithosObject.subdir ||
700 ![dropNode.pithosObject.name hasSuffix:@"/"]) {
701 result = NSDragOperationCopy;
710 - (BOOL)browser:(NSBrowser *)aBrowser
711 acceptDrop:(id<NSDraggingInfo>)info
713 column:(NSInteger)column
714 dropOperation:(NSBrowserDropOperation)dropOperation {
715 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
716 NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
717 NSLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames);
718 if ((column != -1) && (filenames != nil)) {
721 node = [browser itemAtRow:row inColumn:column];
723 node = [browser parentForItemsInColumn:column];
724 NSLog(@"drag in node: %@", node.url);
725 return [self uploadFiles:filenames toNode:node];
727 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
728 NSLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes);
729 if ((column != -1) && (draggedNodes != nil)) {
732 node = [browser itemAtRow:row inColumn:column];
734 node = [browser parentForItemsInColumn:column];
735 NSLog(@"drag local node: %@", node.url);
736 if ([info draggingSourceOperationMask] & NSDragOperationMove)
737 return [self moveNodes:draggedNodes toNode:node];
738 else if ([info draggingSourceOperationMask] & NSDragOperationCopy)
739 return [self copyNodes:draggedNodes toNode:node];
746 #pragma mark Drag and Drop methods
748 - (BOOL)uploadFiles:(NSArray *)filenames toNode:(PithosNode *)destinationNode {
749 if (([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class]))
751 NSFileManager *fileManager = [NSFileManager defaultManager];
752 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
753 NSString *objectNamePrefix;
754 if ([destinationNode class] == [PithosSubdirNode class])
755 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
757 objectNamePrefix = [NSString string];
758 if ((destinationNode.pithosContainer.blockHash == nil) || (destinationNode.pithosContainer.blockSize == 0)) {
759 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest containerMetadataRequestWithContainerName:containerName];
760 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
761 while (![containerRequest isFinished]) {
764 if ([containerRequest error]) {
765 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
767 } else if (containerRequest.responseStatusCode != 204) {
768 [PithosUtilities unexpectedResponseStatusAlertWithRequest:containerRequest];
771 destinationNode.pithosContainer.blockHash = [containerRequest blockHash];
772 destinationNode.pithosContainer.blockSize = [containerRequest blockSize];
774 NSUInteger blockSize = destinationNode.pithosContainer.blockSize;
775 NSString *blockHash = destinationNode.pithosContainer.blockHash;
777 for (NSString *filePath in filenames) {
779 if ([fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
782 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
783 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
784 dispatch_async(queue, ^{
785 NSError *error = nil;
786 NSString *contentType = [PithosUtilities contentTypeOfFile:filePath error:&error];
787 if (contentType == nil)
788 contentType = @"application/octet-stream";
790 NSLog(@"contentType detection error: %@", error);
791 NSArray *hashes = nil;
792 ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithContainerName:containerName
793 objectName:objectName
794 contentType:contentType
800 sharingAccount:destinationNode.sharingAccount];
802 objectRequest.delegate = self;
803 objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
804 objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
805 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload
806 message:[NSString stringWithFormat:@"Uploading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
807 totalBytes:[[objectRequest.userInfo valueForKey:@"bytes"] unsignedIntegerValue]
809 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
810 [NSDictionary dictionaryWithObjectsAndKeys:
811 containerName, @"containerName",
812 objectName, @"objectName",
813 contentType, @"contentType",
814 [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
815 blockHash, @"blockHash",
816 filePath, @"filePath",
818 [NSArray arrayWithObject:destinationNode], @"refreshNodes",
819 [NSNumber numberWithBool:YES], @"refresh",
820 [NSNumber numberWithUnsignedInteger:10], @"iteration",
821 activity, @"activity",
822 [NSNumber numberWithUnsignedInteger:10], @"retries",
824 if (destinationNode.sharingAccount)
825 [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
826 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
830 // Upload directory, confirm first
831 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
832 [alert setMessageText:@"Upload directory"];
833 [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to upload it and its contents?", filePath]];
834 [alert addButtonWithTitle:@"OK"];
835 [alert addButtonWithTitle:@"Cancel"];
836 NSInteger choice = [alert runModal];
837 if (choice == NSAlertFirstButtonReturn) {
838 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
839 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
840 dispatch_async(queue, ^{
841 NSMutableArray *objectNames = nil;
842 NSMutableArray *contentTypes = nil;
843 NSMutableArray *filePaths = nil;
844 NSMutableArray *hashesArrays = nil;
845 NSMutableArray *directoryObjectRequests = nil;
846 NSArray *objectRequests = [PithosUtilities writeObjectDataRequestsWithContainerName:containerName
847 objectName:objectName
850 forDirectory:filePath
852 objectNames:&objectNames
853 contentTypes:&contentTypes
855 hashesArrays:&hashesArrays
856 directoryObjectRequests:&directoryObjectRequests
857 sharingAccount:destinationNode.sharingAccount];
858 for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) {
859 objectRequest.delegate = self;
860 objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
861 objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
862 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory
863 message:[NSString stringWithFormat:@"Creating directory '%@'", [objectRequest.userInfo valueForKey:@"fileName"]]];
864 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
865 [NSDictionary dictionaryWithObjectsAndKeys:
866 [NSNumber numberWithBool:YES], @"refresh",
867 activity, @"activity",
868 [NSNumber numberWithUnsignedInteger:10], @"retries",
870 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
872 if (objectRequests) {
873 for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) {
874 ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i];
875 objectRequest.delegate = self;
876 objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
877 objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
878 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload
879 message:[NSString stringWithFormat:@"Uploading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
880 totalBytes:[[objectRequest.userInfo valueForKey:@"bytes"] unsignedIntegerValue]
882 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
883 [NSDictionary dictionaryWithObjectsAndKeys:
884 containerName, @"containerName",
885 [objectNames objectAtIndex:i], @"objectName",
886 [contentTypes objectAtIndex:i], @"contentType",
887 [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
888 blockHash, @"blockHash",
889 [filePaths objectAtIndex:i], @"filePath",
890 [hashesArrays objectAtIndex:i], @"hashes",
891 [NSNumber numberWithBool:YES], @"refresh",
892 [NSNumber numberWithUnsignedInteger:10], @"iteration",
893 activity, @"activity",
894 [NSNumber numberWithUnsignedInteger:10], @"retries",
896 if (destinationNode.sharingAccount)
897 [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
898 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
909 - (BOOL)moveNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode {
910 if ((([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class])) ||
911 (([destinationNode class] == [PithosSubdirNode class]) && !destinationNode.pithosObject.subdir && [destinationNode.pithosObject.name hasSuffix:@"/"]))
913 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
914 NSString *objectNamePrefix;
915 if ([destinationNode class] == [PithosSubdirNode class])
916 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
918 objectNamePrefix = [NSString string];
920 for (PithosNode *node in nodes) {
921 if (([node class] == [PithosObjectNode class]) ||
922 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
923 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
924 dispatch_async(queue, ^{
925 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
926 if ([node.pithosObject.name hasSuffix:@"/"])
927 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
928 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name
929 objectName:node.pithosObject.name
930 destinationContainerName:containerName
931 destinationObjectName:destinationObjectName
934 objectRequest.delegate = self;
935 objectRequest.didFinishSelector = @selector(moveFinished:);
936 objectRequest.didFailSelector = @selector(moveFailed:);
937 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
938 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
939 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
940 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
941 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
942 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
943 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
944 [NSDictionary dictionaryWithObjectsAndKeys:
945 [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes",
946 activity, @"activity",
947 [NSNumber numberWithUnsignedInteger:10], @"retries",
949 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
952 } else if ([node class] == [PithosSubdirNode class]) {
953 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
954 dispatch_async(queue, ^{
955 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
956 if (node.pithosObject.subdir)
957 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
958 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
959 objectName:node.pithosObject.name
960 destinationContainerName:containerName
961 destinationObjectName:destinationObjectName
963 if (objectRequests) {
964 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
965 objectRequest.delegate = self;
966 objectRequest.didFinishSelector = @selector(moveFinished:);
967 objectRequest.didFailSelector = @selector(moveFailed:);
968 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
969 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
970 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
971 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
972 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
973 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
974 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
975 [NSDictionary dictionaryWithObjectsAndKeys:
976 [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes",
977 [NSNumber numberWithBool:YES], @"refresh",
978 activity, @"activity",
979 [NSNumber numberWithUnsignedInteger:10], @"retries",
981 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
990 - (BOOL)copyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode {
991 if ((([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class])) ||
992 (([destinationNode class] == [PithosSubdirNode class]) && !destinationNode.pithosObject.subdir && [destinationNode.pithosObject.name hasSuffix:@"/"]))
994 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
995 NSString *objectNamePrefix;
996 if ([destinationNode class] == [PithosSubdirNode class])
997 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
999 objectNamePrefix = [NSString string];
1001 for (PithosNode *node in nodes) {
1002 if (([node class] == [PithosObjectNode class]) ||
1003 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
1004 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1005 dispatch_async(queue, ^{
1006 NSString *destinationObjectName;
1007 if (![destinationNode isEqualTo:node.parent]) {
1008 destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
1009 if ([node.pithosObject.name hasSuffix:@"/"])
1010 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
1012 destinationObjectName = [PithosUtilities safeObjectNameForContainerName:containerName
1013 objectName:node.pithosObject.name];
1015 ASIPithosObjectRequest *objectRequest = [PithosUtilities copyObjectRequestWithContainerName:node.pithosContainer.name
1016 objectName:node.pithosObject.name
1017 destinationContainerName:containerName
1018 destinationObjectName:destinationObjectName
1020 sharingAccount:node.sharingAccount];
1021 if (objectRequest) {
1022 objectRequest.delegate = self;
1023 objectRequest.didFinishSelector = @selector(copyFinished:);
1024 objectRequest.didFailSelector = @selector(copyFailed:);
1025 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy
1026 message:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'",
1027 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1028 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1029 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1030 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1031 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1032 [NSDictionary dictionaryWithObjectsAndKeys:
1033 [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes",
1034 activity, @"activity",
1035 [NSNumber numberWithUnsignedInteger:10], @"retries",
1037 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1040 } else if ([node class] == [PithosSubdirNode class]) {
1041 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1042 dispatch_async(queue, ^{
1043 NSString *destinationObjectName;
1044 if (![destinationNode isEqualTo:node.parent]) {
1045 destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
1046 if (node.pithosObject.subdir)
1047 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
1049 destinationObjectName = [PithosUtilities safeSubdirNameForContainerName:containerName
1050 subdirName:node.pithosObject.name];
1052 NSArray *objectRequests = [PithosUtilities copyObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
1053 objectName:node.pithosObject.name
1054 destinationContainerName:containerName
1055 destinationObjectName:destinationObjectName
1057 sharingAccount:node.sharingAccount];
1058 if (objectRequests) {
1059 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1060 objectRequest.delegate = self;
1061 objectRequest.didFinishSelector = @selector(copyFinished:);
1062 objectRequest.didFailSelector = @selector(copyFailed:);
1063 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy
1064 message:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'",
1065 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1066 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1067 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1068 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1069 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1070 [NSDictionary dictionaryWithObjectsAndKeys:
1071 [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes",
1072 activity, @"activity",
1073 [NSNumber numberWithUnsignedInteger:10], @"retries",
1075 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1085 #pragma mark ASIHTTPRequestDelegate
1087 - (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1088 NSLog(@"Download finished: %@", objectRequest.url);
1089 if (objectRequest.responseStatusCode == 200) {
1090 NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"];
1091 PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
1092 NSUInteger totalBytes = activity.totalBytes;
1093 NSUInteger currentBytes = activity.currentBytes;
1095 // XXX change contentLength to objectContentLength if it is fixed in the server
1096 if (([objectRequest contentLength] == 0) && (![[objectRequest contentType] isEqualToString:@"application/directory"])) {
1097 NSLog(@"Downloaded 0 bytes");
1098 NSFileManager *fileManager = [NSFileManager defaultManager];
1099 if (![fileManager fileExistsAtPath:filePath]) {
1100 if (![fileManager createFileAtPath:filePath contents:nil attributes:nil]) {
1101 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1102 [alert setMessageText:@"Create File Error"];
1103 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create zero length file at %@", filePath]];
1104 [alert addButtonWithTitle:@"OK"];
1110 currentBytes = [objectRequest objectContentLength];
1111 if (currentBytes == 0)
1112 currentBytes = totalBytes;
1113 [activityFacility endActivity:activity
1114 withMessage:[NSString stringWithFormat:@"Downloading '%@' (100%%)",
1115 [objectRequest.userInfo objectForKey:@"fileName"]]
1116 totalBytes:totalBytes
1117 currentBytes:currentBytes];
1119 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1121 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1122 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1123 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1125 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1126 withMessage:[NSString stringWithFormat:@"Downloading '%@' (failed)",
1127 [objectRequest.userInfo objectForKey:@"fileName"]]];
1128 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1133 - (void)downloadObjectFailed:(ASIPithosObjectRequest *)objectRequest {
1134 NSLog(@"Download failed: %@", objectRequest.url);
1135 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1137 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1138 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1139 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1141 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1142 withMessage:[NSString stringWithFormat:@"Downloading '%@' (failed)",
1143 [objectRequest.userInfo objectForKey:@"fileName"]]];
1144 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1148 - (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1149 NSLog(@"Upload directory object finished: %@", objectRequest.url);
1150 if (objectRequest.responseStatusCode == 201) {
1151 NSLog(@"Directory object created: %@", objectRequest.url);
1152 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1153 withMessage:[NSString stringWithFormat:@"Creating directory '%@' (finished)",
1154 [objectRequest.userInfo objectForKey:@"fileName"]]];
1155 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1156 [node forceRefresh];
1158 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1161 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1162 [self forceRefresh:self];
1163 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1164 [self refresh:self];
1166 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1168 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1169 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1170 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1172 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1173 withMessage:[NSString stringWithFormat:@"Creating directory '%@' (failed)",
1174 [objectRequest.userInfo objectForKey:@"fileName"]]];
1175 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1180 - (void)uploadDirectoryObjectFailed:(ASIPithosObjectRequest *)objectRequest {
1181 NSLog(@"Upload directory object failed: %@", objectRequest.url);
1182 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1184 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1185 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1186 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1188 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1189 withMessage:[NSString stringWithFormat:@"Creating directory '%@' (failed)",
1190 [objectRequest.userInfo objectForKey:@"fileName"]]];
1191 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1195 - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
1196 NSLog(@"Upload using hashmap finished: %@", objectRequest.url);
1197 NSString *fileName = [objectRequest.userInfo objectForKey:@"fileName"];
1198 PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
1199 NSUInteger totalBytes = activity.totalBytes;
1200 NSUInteger currentBytes = activity.currentBytes;
1201 if (objectRequest.responseStatusCode == 201) {
1202 NSLog(@"Object created: %@", objectRequest.url);
1203 [activityFacility endActivity:activity
1204 withMessage:[NSString stringWithFormat:@"Uploading '%@' (100%%)", fileName]
1205 totalBytes:totalBytes
1206 currentBytes:totalBytes];
1207 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1208 [node forceRefresh];
1210 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1213 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1214 [self forceRefresh:self];
1215 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1216 [self refresh:self];
1217 } else if (objectRequest.responseStatusCode == 409) {
1218 NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue];
1219 if (iteration == 0) {
1220 NSLog(@"Upload iteration limit reached: %@", objectRequest.url);
1221 [activityFacility endActivity:activity
1222 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]];
1223 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1224 [alert setMessageText:@"Upload Timeout"];
1225 [alert setInformativeText:[NSString stringWithFormat:@"Upload iteration limit reached for object with path '%@'",
1226 [objectRequest.userInfo objectForKey:@"objectName"]]];
1227 [alert addButtonWithTitle:@"OK"];
1231 NSLog(@"object is missing hashes: %@", objectRequest.url);
1232 NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForHashes:[objectRequest.userInfo objectForKey:@"hashes"]
1233 withMissingHashesResponse:[objectRequest responseString]];
1234 NSUInteger blockSize = [[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
1235 if (totalBytes >= [missingBlocks count]*blockSize)
1236 currentBytes = totalBytes - [missingBlocks count]*blockSize;
1237 [activityFacility updateActivity:activity
1238 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(currentBytes + 0.0)/(totalBytes + 0.0))]
1239 totalBytes:totalBytes
1240 currentBytes:currentBytes];
1241 NSUInteger missingBlockIndex = [missingBlocks firstIndex];
1242 __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithContainerName:[objectRequest.userInfo objectForKey:@"containerName"]
1244 forFile:[objectRequest.userInfo objectForKey:@"filePath"]
1245 missingBlockIndex:missingBlockIndex
1246 sharingAccount:[objectRequest.userInfo objectForKey:@"sharingAccount"]];
1247 newContainerRequest.delegate = self;
1248 newContainerRequest.didFinishSelector = @selector(uploadMissingBlockFinished:);
1249 newContainerRequest.didFailSelector = @selector(uploadMissingBlockFailed:);
1250 newContainerRequest.userInfo = objectRequest.userInfo;
1251 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:(--iteration)] forKey:@"iteration"];
1252 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1253 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"];
1254 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
1255 [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){
1256 [activityFacility updateActivity:activity
1257 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
1258 totalBytes:activity.totalBytes
1259 currentBytes:(activity.currentBytes + size)];
1261 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1263 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1265 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1266 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1267 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1269 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1270 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]];
1271 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1276 - (void)uploadObjectUsingHashMapFailed:(ASIPithosObjectRequest *)objectRequest {
1277 NSLog(@"Upload using hashmap failed: %@", objectRequest.url);
1278 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1280 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1281 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1282 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1284 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1285 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)",
1286 [objectRequest.userInfo objectForKey:@"fileName"]]];
1287 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1291 - (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest {
1292 NSLog(@"Upload of missing block finished: %@", containerRequest.url);
1293 NSUInteger blockSize = [[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
1294 NSString *fileName = [containerRequest.userInfo objectForKey:@"fileName"];
1295 PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"];
1296 if (containerRequest.responseStatusCode == 202) {
1297 NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"];
1298 NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue];
1299 missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex];
1300 if (missingBlockIndex == NSNotFound) {
1301 NSArray *hashes = [containerRequest.userInfo objectForKey:@"hashes"];
1302 ASIPithosObjectRequest *newObjectRequest = [PithosUtilities writeObjectDataRequestWithContainerName:[containerRequest.userInfo objectForKey:@"containerName"]
1303 objectName:[containerRequest.userInfo objectForKey:@"objectName"]
1304 contentType:[containerRequest.userInfo objectForKey:@"contentType"]
1306 blockHash:[containerRequest.userInfo objectForKey:@"blockHash"]
1307 forFile:[containerRequest.userInfo objectForKey:@"filePath"]
1310 sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
1311 newObjectRequest.delegate = self;
1312 newObjectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
1313 newObjectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
1314 newObjectRequest.userInfo = containerRequest.userInfo;
1315 [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1316 [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"];
1317 [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"];
1318 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1320 __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithContainerName:[containerRequest.userInfo objectForKey:@"containerName"]
1321 blockSize:[[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]
1322 forFile:[containerRequest.userInfo objectForKey:@"filePath"]
1323 missingBlockIndex:missingBlockIndex
1324 sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
1325 newContainerRequest.delegate = self;
1326 newContainerRequest.didFinishSelector = @selector(uploadMissingBlockFinished:);
1327 newContainerRequest.didFailSelector = @selector(uploadMissingBlockFailed:);
1328 newContainerRequest.userInfo = containerRequest.userInfo;
1329 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1330 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
1331 [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){
1332 [activityFacility updateActivity:activity
1333 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
1334 totalBytes:activity.totalBytes
1335 currentBytes:(activity.currentBytes + size)];
1337 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1340 NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1342 ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest];
1343 [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1344 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1346 [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"]
1347 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]];
1348 [PithosUtilities unexpectedResponseStatusAlertWithRequest:containerRequest];
1353 - (void)uploadMissingBlockFailed:(ASIPithosContainerRequest *)containerRequest {
1354 NSLog(@"Upload of missing block failed: %@", containerRequest.url);
1355 NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1357 ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest];
1358 [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1359 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1361 [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"]
1362 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)",
1363 [containerRequest.userInfo objectForKey:@"fileName"]]];
1364 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
1368 - (void)moveFinished:(ASIPithosObjectRequest *)objectRequest {
1369 NSLog(@"Move object finished: %@", objectRequest.url);
1370 if (objectRequest.responseStatusCode == 201) {
1371 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1372 withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' (finished)",
1373 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1374 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1375 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1376 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1377 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1378 [node forceRefresh];
1380 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1383 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1384 [self forceRefresh:self];
1385 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1386 [self refresh:self];
1388 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1390 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1391 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1392 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1394 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1395 withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' (failed)",
1396 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1397 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1398 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1399 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1400 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1405 - (void)moveFailed:(ASIPithosObjectRequest *)objectRequest {
1406 NSLog(@"Move object failed: %@", objectRequest.url);
1407 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1409 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1410 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1411 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1413 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1414 withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' (failed)",
1415 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1416 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1417 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1418 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1419 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1423 - (void)copyFinished:(ASIPithosObjectRequest *)objectRequest {
1424 NSLog(@"Copy object finished: %@", objectRequest.url);
1425 if (objectRequest.responseStatusCode == 201) {
1426 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1427 withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' (finished)",
1428 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1429 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1430 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1431 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1432 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1433 [node forceRefresh];
1435 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1438 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1439 [self forceRefresh:self];
1440 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1441 [self refresh:self];
1443 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1445 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1446 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1447 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1449 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1450 withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' (failed)",
1451 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1452 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1453 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1454 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1455 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1460 - (void)copyFailed:(ASIPithosObjectRequest *)objectRequest {
1461 NSLog(@"Copy object failed: %@", objectRequest.url);
1462 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1464 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1465 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1466 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1468 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1469 withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' (failed)",
1470 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1471 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1472 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1473 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1474 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1478 - (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1479 NSLog(@"Delete object finished: %@", objectRequest.url);
1480 if (objectRequest.responseStatusCode == 204) {
1481 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1482 withMessage:[NSString stringWithFormat:@"Deleting '%@' (finished)",
1483 [objectRequest.userInfo objectForKey:@"fileName"]]];
1484 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1485 [node forceRefresh];
1487 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1490 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1491 [self forceRefresh:self];
1492 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1493 [self refresh:self];
1495 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1497 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1498 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1499 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1501 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1502 withMessage:[NSString stringWithFormat:@"Deleting '%@' (failed)",
1503 [objectRequest.userInfo objectForKey:@"fileName"]]];
1504 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1509 - (void)deleteObjectFailed:(ASIPithosObjectRequest *)objectRequest {
1510 NSLog(@"Delete object failed: %@", objectRequest.url);
1511 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1513 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1514 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1515 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1517 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1518 withMessage:[NSString stringWithFormat:@"Deleting '%@' (failed)",
1519 [objectRequest.userInfo objectForKey:@"fileName"]]];
1520 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1525 #pragma mark NSSplitViewDelegate
1527 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
1528 if (splitView == verticalSplitView)
1531 return ([horizontalSplitView bounds].size.height - 108);
1534 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
1535 if (splitView == verticalSplitView)
1538 return ([horizontalSplitView bounds].size.height - 108);
1541 - (CGFloat)splitView:(NSSplitView *)splitView constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)dividerIndex {
1542 if (splitView == verticalSplitView) {
1543 if (proposedPosition < 120)
1545 else if (proposedPosition > 220)
1548 return proposedPosition;
1550 return ([horizontalSplitView bounds].size.height - 108);
1555 #pragma mark NSOutlineViewDataSource
1557 - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
1560 if (item == containersNode)
1561 return containersNodeChildren.count;
1562 if (item == sharedNode)
1567 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
1569 return (!index ? containersNode : sharedNode);
1570 if (item == sharedNode)
1571 return (!index ? mySharedNode : othersSharedNode);
1572 return [containersNodeChildren objectAtIndex:index];
1575 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
1576 if ((item == containersNode) || (item == sharedNode))
1581 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
1582 PithosNode *node = (PithosNode *)item;
1586 #pragma mark Drag and Drop destination
1588 - (NSDragOperation)outlineView:(NSOutlineView *)anOutlineView
1589 validateDrop:(id<NSDraggingInfo>)info
1590 proposedItem:(id)item
1591 proposedChildIndex:(NSInteger)index {
1592 NSDragOperation result = NSDragOperationNone;
1593 if ((item == nil) || (index != NSOutlineViewDropOnItemIndex))
1595 PithosNode *dropNode = (PithosNode *)item;
1596 if ([dropNode class] != [PithosContainerNode class])
1598 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
1599 result = NSDragOperationCopy;
1600 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
1601 if ([info draggingSourceOperationMask] & NSDragOperationMove) {
1602 // NSDragOperationCopy | NSDragOperationMove -> NSDragOperationMove
1603 if (![dropNode isEqualTo:draggedParentNode])
1604 result = NSDragOperationMove;
1605 } else if ([info draggingSourceOperationMask] & NSDragOperationCopy) {
1606 // NSDragOperationCopy (Option modifier) -> NSDragOperationCopy
1607 result = NSDragOperationCopy;
1613 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id<NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index {
1614 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
1615 NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
1616 NSLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames);
1617 if (item && (index == NSOutlineViewDropOnItemIndex) && (filenames != nil)) {
1618 PithosNode *node = (PithosNode *)item;
1619 NSLog(@"drag in node: %@", node.url);
1620 return [self uploadFiles:filenames toNode:node];
1622 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
1623 NSLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes);
1624 if (item && (index == NSOutlineViewDropOnItemIndex) && (draggedNodes != nil)) {
1625 PithosNode *node = (PithosNode *)item;
1626 NSLog(@"drag local node: %@", node.url);
1627 if ([info draggingSourceOperationMask] & NSDragOperationMove)
1628 return [self moveNodes:draggedNodes toNode:node];
1629 else if ([info draggingSourceOperationMask] & NSDragOperationCopy)
1630 return [self copyNodes:draggedNodes toNode:node];
1637 #pragma mark NSOutlineViewDelegate
1639 - (BOOL)outlineView:outlineView shouldSelectItem:(id)item {
1640 if ((item == containersNode) || (item == sharedNode))
1645 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
1646 if ((item == containersNode) || (item == sharedNode))
1651 - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
1652 PithosNode *node = (PithosNode *)[outlineView itemAtRow:[outlineView selectedRow]];
1655 [browser loadColumnZero];
1661 #pragma mark NSMenuDelegate
1663 - (void)menuNeedsUpdate:(NSMenu *)menu {
1664 [menu removeAllItems];
1665 NSMenuItem *menuItem;
1666 NSString *menuItemTitle;
1667 BOOL nodeContextMenu = NO;
1668 PithosNode *menuNode;
1669 NSMutableArray *menuNodes;
1670 if (menu == browserMenu) {
1671 NSInteger column = [browser clickedColumn];
1672 NSInteger row = [browser clickedRow];
1673 if ((column == -1) || (row == -1)) {
1674 // General context menu
1675 NSArray *menuNodesIndexPaths = [browser selectionIndexPaths];
1676 if ([menuNodesIndexPaths count] == 0) {
1677 menuNode = [browser parentForItemsInColumn:0];
1678 } else if (([menuNodesIndexPaths count] != 1) ||
1679 ([[browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]] class] == [PithosObjectNode class])) {
1680 menuNode = [browser parentForItemsInColumn:([[menuNodesIndexPaths objectAtIndex:0] length] - 1)];
1682 menuNode = [browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]];
1685 // Node context menu
1686 NSIndexPath *clickedNodeIndexPath = [[browser indexPathForColumn:column] indexPathByAddingIndex:row];
1687 NSArray *menuNodesIndexPaths = [browser selectionIndexPaths];
1688 menuNodes = [NSMutableArray arrayWithCapacity:[menuNodesIndexPaths count]];
1689 if ([menuNodesIndexPaths containsObject:clickedNodeIndexPath]) {
1690 for (NSIndexPath *nodeIndexPath in menuNodesIndexPaths) {
1691 [menuNodes addObject:[browser itemAtIndexPath:nodeIndexPath]];
1694 [menuNodes addObject:[browser itemAtIndexPath:clickedNodeIndexPath]];
1696 nodeContextMenu = YES;
1698 } else if (menu == outlineViewMenu) {
1699 NSInteger row = [outlineView clickedRow];
1701 row = [outlineView selectedRow];
1704 menuNode = [outlineView itemAtRow:row];
1707 if (!nodeContextMenu) {
1708 // General context menu
1709 if (([menuNode class] == [PithosAccountNode class]) ||
1710 ([menuNode class] == [PithosSharingAccountsNode class]) ||
1711 ([menuNode class] == [PithosEmptyNode class]))
1713 BOOL shared = menuNode.shared;
1714 BOOL sharingAccount = (menuNode.sharingAccount != nil);
1716 if (!shared && !sharingAccount) {
1717 menuItem = [[[NSMenuItem alloc] initWithTitle:@"New Folder" action:@selector(menuNewFolder:) keyEquivalent:@""] autorelease];
1718 [menuItem setRepresentedObject:menuNode];
1719 [menu addItem:menuItem];
1720 [menu addItem:[NSMenuItem separatorItem]];
1723 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(menuGetInfo:) keyEquivalent:@""] autorelease];
1724 [menuItem setRepresentedObject:[NSArray arrayWithObject:menuNode]];
1725 [menu addItem:menuItem];
1727 if (!shared && !sharingAccount) {
1728 if (clipboardNodes) {
1729 NSUInteger clipboardNodesCount = [clipboardNodes count];
1730 if (clipboardNodesCount == 0) {
1731 self.clipboardNodes = nil;
1732 } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) {
1733 if (clipboardNodesCount == 1)
1734 menuItemTitle = [NSString stringWithString:@"Paste Item"];
1736 menuItemTitle = [NSString stringWithString:@"Paste Items"];
1737 [menu addItem:[NSMenuItem separatorItem]];
1738 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
1739 [menuItem setRepresentedObject:menuNode];
1740 [menu addItem:menuItem];
1745 // Node context menu
1746 NSUInteger menuNodesCount = [menuNodes count];
1747 PithosNode *firstMenuNode = (PithosNode *)[menuNodes objectAtIndex:0];
1748 BOOL shared = firstMenuNode.shared;
1749 BOOL sharingAccount = (firstMenuNode.sharingAccount != nil);
1750 // Move to Trash (pithos container only)
1752 if (!shared && !sharingAccount) {
1753 if ([rootNode class] == [PithosContainerNode class]) {
1754 if ([rootNode.pithosContainer.name isEqualToString:@"pithos"]) {
1755 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Move to Trash" action:@selector(menuMoveToTrash:) keyEquivalent:@""] autorelease];
1756 [menuItem setRepresentedObject:menuNodes];
1757 [menu addItem:menuItem];
1759 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(menuDelete:) keyEquivalent:@""] autorelease];
1760 [menuItem setRepresentedObject:menuNodes];
1761 [menu addItem:menuItem];
1762 [menu addItem:[NSMenuItem separatorItem]];
1766 if (!sharingAccount || ([firstMenuNode class] != [PithosAccountNode class])) {
1767 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(menuGetInfo:) keyEquivalent:@""] autorelease];
1768 [menuItem setRepresentedObject:menuNodes];
1769 [menu addItem:menuItem];
1771 if ((!shared && !sharingAccount) || ([firstMenuNode class] != [PithosContainerNode class]))
1772 [menu addItem:[NSMenuItem separatorItem]];
1775 if (!shared && !sharingAccount) {
1776 if (menuNodesCount == 1)
1777 menuItemTitle = [NSString stringWithFormat:@"Cut \"%@\"", ((PithosNode *)[menuNodes objectAtIndex:0]).displayName];
1779 menuItemTitle = [NSString stringWithFormat:@"Cut %lu Items", menuNodesCount];
1780 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCut:) keyEquivalent:@""] autorelease];
1781 [menuItem setRepresentedObject:menuNodes];
1782 [menu addItem:menuItem];
1785 if ((!shared && !sharingAccount) ||
1786 (([firstMenuNode class] != [PithosContainerNode class]) && ([firstMenuNode class] != [PithosAccountNode class]))) {
1787 if (menuNodesCount == 1)
1788 menuItemTitle = [NSString stringWithFormat:@"Copy \"%@\"", ((PithosNode *)[menuNodes objectAtIndex:0]).displayName];
1790 menuItemTitle = [NSString stringWithFormat:@"Copy %lu Items", menuNodesCount];
1791 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCopy:) keyEquivalent:@""] autorelease];
1792 [menuItem setRepresentedObject:menuNodes];
1793 [menu addItem:menuItem];
1796 if (!shared && !sharingAccount) {
1797 if (menuNodesCount == 1) {
1798 PithosNode *menuNode = [menuNodes objectAtIndex:0];
1799 if (([menuNode class] == [PithosSubdirNode class]) &&
1800 (menuNode.pithosObject.subdir || ![menuNode.pithosObject.name hasSuffix:@"/"])) {
1801 if (clipboardNodes) {
1802 NSUInteger clipboardNodesCount = [clipboardNodes count];
1803 if (clipboardNodesCount == 0) {
1804 self.clipboardNodes = nil;
1805 } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) {
1806 if (clipboardNodesCount == 1)
1807 menuItemTitle = [NSString stringWithString:@"Paste Item"];
1809 menuItemTitle = [NSString stringWithString:@"Paste Items"];
1810 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
1811 [menuItem setRepresentedObject:menuNode];
1812 [menu addItem:menuItem];
1822 #pragma mark Menu Actions
1824 - (void)menuNewFolder:(NSMenuItem *)sender {
1825 PithosNode *node = (PithosNode *)[sender representedObject];
1826 if ([node class] == [PithosContainerNode class]) {
1827 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1828 dispatch_async(queue, ^{
1829 NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:node.pithosContainer.name
1830 subdirName:@"untitled folder"];
1831 NSString *fileName = [safeObjectName lastPathComponent];
1832 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:node.pithosContainer.name
1833 objectName:safeObjectName
1835 contentType:@"application/directory"
1837 contentDisposition:nil
1840 isPublic:ASIPithosObjectRequestPublicIgnore
1842 data:[NSData data]];
1843 objectRequest.delegate = self;
1844 objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
1845 objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
1846 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory
1847 message:[NSString stringWithFormat:@"Creating directory '%@'", fileName]];
1848 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1849 fileName, @"fileName",
1850 [NSArray arrayWithObject:node], @"refreshNodes",
1851 [NSNumber numberWithBool:YES], @"refresh",
1852 activity, @"activity",
1853 [NSNumber numberWithUnsignedInteger:10], @"retries",
1855 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1857 } else if (([node class] == [PithosSubdirNode class]) &&
1858 (node.pithosObject.subdir || ![node.pithosObject.name hasSuffix:@"/"])) {
1859 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1860 dispatch_async(queue, ^{
1861 NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:node.pithosContainer.name
1862 subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]];
1863 NSString *fileName = [safeObjectName lastPathComponent];
1864 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:node.pithosContainer.name
1865 objectName:safeObjectName
1867 contentType:@"application/directory"
1869 contentDisposition:nil
1872 isPublic:ASIPithosObjectRequestPublicIgnore
1874 data:[NSData data]];
1875 objectRequest.delegate = self;
1876 objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
1877 objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
1878 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory
1879 message:[NSString stringWithFormat:@"Creating directory '%@'", fileName]];
1880 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1881 fileName, @"fileName",
1882 [NSArray arrayWithObject:node], @"refreshNodes",
1883 [NSNumber numberWithBool:YES], @"refresh",
1884 activity, @"activity",
1885 [NSNumber numberWithUnsignedInteger:10], @"retries",
1887 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1892 - (void)menuGetInfo:(NSMenuItem *)sender {
1893 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
1894 [node showPithosNodeInfo:sender];
1898 - (void)menuDelete:(NSMenuItem *)sender {
1899 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
1900 if (([node class] == [PithosObjectNode class]) ||
1901 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
1902 NSString *fileName = [node.pithosObject.name lastPathComponent];
1903 if ([node.pithosObject.name hasSuffix:@"/"])
1904 fileName = [fileName stringByAppendingString:@"/"];
1905 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:node.pithosContainer.name
1906 objectName:node.pithosObject.name];
1907 objectRequest.delegate = self;
1908 objectRequest.didFinishSelector = @selector(deleteObjectFinished:);
1909 objectRequest.didFailSelector = @selector(deleteObjectFailed:);
1910 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete
1911 message:[NSString stringWithFormat:@"Deleting '%@'", fileName]];
1912 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1913 fileName, @"fileName",
1914 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
1915 activity, @"activity",
1916 [NSNumber numberWithUnsignedInteger:10], @"retries",
1918 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1919 } else if ([node class] == [PithosSubdirNode class]) {
1920 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1921 dispatch_async(queue, ^{
1922 NSArray *objectRequests = [PithosUtilities deleteObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
1923 objectName:node.pithosObject.name];
1924 if (objectRequests) {
1925 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1926 objectRequest.delegate = self;
1927 objectRequest.didFinishSelector = @selector(deleteObjectFinished:);
1928 objectRequest.didFailSelector = @selector(deleteObjectFailed:);
1929 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete
1930 message:[NSString stringWithFormat:@"Deleting '%@'",
1931 [objectRequest.userInfo objectForKey:@"fileName"]]];
1932 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1933 [NSDictionary dictionaryWithObjectsAndKeys:
1934 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
1935 activity, @"activity",
1936 [NSNumber numberWithUnsignedInteger:10], @"retries",
1938 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1946 - (void)menuMoveToTrash:(NSMenuItem *)sender {
1947 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
1948 if (([node class] == [PithosObjectNode class]) ||
1949 (([node class] == [PithosSubdirNode class]) &&
1950 !node.pithosObject.subdir &&
1951 [node.pithosObject.name hasSuffix:@"/"])) {
1952 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1953 dispatch_async(queue, ^{
1954 NSString *safeObjectName = [PithosUtilities safeObjectNameForContainerName:@"trash"
1955 objectName:node.pithosObject.name];
1956 if (safeObjectName) {
1957 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name
1958 objectName:node.pithosObject.name
1959 destinationContainerName:@"trash"
1960 destinationObjectName:safeObjectName
1962 if (objectRequest) {
1963 objectRequest.delegate = self;
1964 objectRequest.didFinishSelector = @selector(moveFinished:);
1965 objectRequest.didFailSelector = @selector(moveFailed:);
1966 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
1967 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
1968 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1969 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1970 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1971 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1972 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1973 [NSDictionary dictionaryWithObjectsAndKeys:
1974 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
1975 activity, @"activity",
1976 [NSNumber numberWithUnsignedInteger:10], @"retries",
1978 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1982 } else if ([node class] == [PithosSubdirNode class]) {
1983 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1984 dispatch_async(queue, ^{
1985 NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:@"trash"
1986 subdirName:node.pithosObject.name];
1987 if (safeObjectName) {
1988 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
1989 objectName:node.pithosObject.name
1990 destinationContainerName:@"trash"
1991 destinationObjectName:safeObjectName
1993 if (objectRequests) {
1994 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1995 objectRequest.delegate = self;
1996 objectRequest.didFinishSelector = @selector(moveFinished:);
1997 objectRequest.didFailSelector = @selector(moveFailed:);
1998 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
1999 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
2000 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
2001 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
2002 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
2003 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
2004 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
2005 [NSDictionary dictionaryWithObjectsAndKeys:
2006 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
2007 activity, @"activity",
2008 [NSNumber numberWithUnsignedInteger:10], @"retries",
2010 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
2019 - (void)menuCut:(NSMenuItem *)sender {
2020 self.clipboardNodes = (NSArray *)[sender representedObject];
2021 self.clipboardParentNode = ((PithosNode *)[clipboardNodes objectAtIndex:0]).parent;
2022 self.clipboardCopy = NO;
2025 - (void)menuCopy:(NSMenuItem *)sender {
2026 self.clipboardNodes = (NSArray *)[sender representedObject];
2027 self.clipboardParentNode = ((PithosNode *)[clipboardNodes objectAtIndex:0]).parent;
2028 self.clipboardCopy = YES;
2031 - (void)menuPaste:(NSMenuItem *)sender {
2032 if (!clipboardNodes || ![clipboardNodes count])
2034 PithosNode *dropNode = (PithosNode *)[sender representedObject];
2035 NSArray *localClipboardNodes = [NSArray arrayWithArray:clipboardNodes];
2036 if (!clipboardCopy && ![dropNode isEqualTo:clipboardParentNode]) {
2037 self.clipboardNodes = nil;
2038 self.clipboardParentNode = nil;
2039 [self moveNodes:localClipboardNodes toNode:dropNode];
2041 [self copyNodes:localClipboardNodes toNode:dropNode];
2046 #pragma mark PithosActivityFacilityDelegate
2048 - (void)activityUpdate:(NSDictionary *)info {
2049 NSString *message = [info objectForKey:@"message"];
2050 NSUInteger runningActivitiesCount = [[info objectForKey:@"runningActivitiesCount"] unsignedIntegerValue];
2051 // NSUInteger endingActivitiesCount = [[info objectForKey:@"endingActivitiesCount"] unsignedIntegerValue];
2052 NSUInteger totalUploadBytes = [[info objectForKey:@"totalUploadBytes"] unsignedIntegerValue];
2053 NSUInteger currentUploadBytes = [[info objectForKey:@"currentUploadBytes"] unsignedIntegerValue];
2054 NSUInteger totalDownloadBytes = [[info objectForKey:@"totalDownloadBytes"] unsignedIntegerValue];
2055 NSUInteger currentDownloadBytes = [[info objectForKey:@"currentDownloadBytes"] unsignedIntegerValue];
2056 NSUInteger totalBytes = totalUploadBytes + totalDownloadBytes;
2057 if (runningActivitiesCount && totalBytes) {
2058 [activityProgressIndicator setDoubleValue:((currentUploadBytes + currentDownloadBytes + 0.0)/(totalBytes + 0.0))];
2059 [activityProgressIndicator startAnimation:self];
2061 [activityProgressIndicator setDoubleValue:1.0];
2062 [activityProgressIndicator stopAnimation:self];
2066 message = [[[[UsingSizeTransformer alloc] init] autorelease] transformedValue:accountNode.pithosAccount];
2067 [activityTextField setStringValue:message];