2 // PithosBrowserController.m
5 // Copyright 2011-2012 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 "ASINetworkQueue.h"
49 #import "ASIPithosRequest.h"
51 #import "ASIPithosContainerRequest.h"
52 #import "ASIPithosObjectRequest.h"
53 #import "ASIPithosAccount.h"
54 #import "ASIPithosContainer.h"
55 #import "ASIPithosObject.h"
56 #import "PithosUtilities.h"
57 #import "UsingSizeTransformer.h"
59 @interface PithosBrowserCell : FileSystemBrowserCell {}
62 @implementation PithosBrowserCell
65 if ((self = [super init])) {
66 [self setLineBreakMode:NSLineBreakByTruncatingMiddle];
67 [self setEditable:YES];
72 - (void)setObjectValue:(id)object {
73 if ([object isKindOfClass:[PithosNode class]]) {
74 PithosNode *node = (PithosNode *)object;
75 [self setStringValue:node.displayName];
76 [self setImage:node.icon];
78 [super setObjectValue:object];
84 @interface PithosOutlineViewCell : ImageAndTextCell {}
87 @implementation PithosOutlineViewCell
89 - (void)setObjectValue:(id)object {
90 if ([object isKindOfClass:[PithosNode class]]) {
91 PithosNode *node = (PithosNode *)object;
92 [self setStringValue:node.displayName];
93 [self setImage:node.icon];
94 [self setEditable:NO];
96 [super setObjectValue:object];
102 @interface PithosBrowserController (Private)
103 - (BOOL)uploadFiles:(NSArray *)filenames toNode:(PithosNode *)destinationNode;
104 - (BOOL)moveNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode;
105 - (BOOL)copyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode;
108 @implementation PithosBrowserController
110 @synthesize accountNode;
111 @synthesize verticalSplitView, horizontalSplitView, leftTopView, leftBottomView, outlineView, browser, outlineViewMenu, browserMenu;
112 @synthesize draggedNodes, draggedParentNode;
113 @synthesize clipboardNodes, clipboardParentNode, clipboardCopy;
114 @synthesize activityTextField, activityProgressIndicator;
117 #pragma Object Lifecycle
120 return [super initWithWindowNibName:@"PithosBrowserController"];
124 [[NSNotificationCenter defaultCenter] removeObserver:self];
125 dispatch_release(moveQueue);
126 dispatch_release(copyQueue);
127 dispatch_release(deleteQueue);
128 dispatch_release(uploadQueue);
129 dispatch_release(downloadQueue);
130 [moveNetworkQueue cancelAllOperations];
131 [moveNetworkQueue release];
132 [copyNetworkQueue cancelAllOperations];
133 [copyNetworkQueue release];
134 [deleteNetworkQueue cancelAllOperations];
135 [deleteNetworkQueue release];
136 [uploadNetworkQueue cancelAllOperations];
137 [uploadNetworkQueue release];
138 [downloadNetworkQueue cancelAllOperations];
139 [downloadNetworkQueue release];
140 [refreshTimer invalidate];
141 [refreshTimer release];
142 [clipboardParentNode release];
143 [clipboardNodes release];
144 [draggedParentNode release];
145 [draggedNodes release];
146 [sharedPreviewController release];
147 [othersSharedNode release];
148 [mySharedNode release];
149 [sharedNode release];
150 [containersNodeChildren release];
151 [containersNode release];
152 [accountNode release];
158 - (void)initBrowser {
159 [browser registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]];
160 [browser setDraggingSourceOperationMask:(NSDragOperationCopy|NSDragOperationMove) forLocal:YES];
161 [browser setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
163 [outlineView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]];
164 [outlineView setDraggingSourceOperationMask:(NSDragOperationCopy|NSDragOperationMove) forLocal:YES];
165 [outlineView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
167 [browser setCellClass:[PithosBrowserCell class]];
168 [browser setAllowsBranchSelection:YES];
169 [browser setAllowsMultipleSelection:YES];
170 [browser setAllowsEmptySelection:YES];
171 [browser setAllowsTypeSelect:YES];
173 moveNetworkQueue = [[ASINetworkQueue alloc] init];
174 moveNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
175 [moveNetworkQueue go];
176 copyNetworkQueue = [[ASINetworkQueue alloc] init];
177 copyNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
178 [copyNetworkQueue go];
179 deleteNetworkQueue = [[ASINetworkQueue alloc] init];
180 deleteNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
181 [deleteNetworkQueue go];
182 uploadNetworkQueue = [[ASINetworkQueue alloc] init];
183 uploadNetworkQueue.showAccurateProgress = YES;
184 uploadNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
185 [uploadNetworkQueue go];
186 downloadNetworkQueue = [[ASINetworkQueue alloc] init];
187 downloadNetworkQueue.showAccurateProgress = YES;
188 downloadNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
189 [downloadNetworkQueue go];
191 moveQueue = dispatch_queue_create("gr.grnet.pithos.MoveQueue", NULL);
192 copyQueue = dispatch_queue_create("gr.grnet.pithos.CopyQueue", NULL);
193 deleteQueue = dispatch_queue_create("gr.grnet.pithos.DeleteQueue", NULL);
194 uploadQueue = dispatch_queue_create("gr.grnet.pithos.UploadQueue", NULL);
195 downloadQueue = dispatch_queue_create("gr.grnet.pithos.DownloadQueue", NULL);
197 [activityProgressIndicator setUsesThreadedAnimation:YES];
198 [activityProgressIndicator setMinValue:0.0];
199 [activityProgressIndicator setMaxValue:1.0];
200 activityFacility = [PithosActivityFacility defaultPithosActivityFacility];
202 self.accountNode = [[[PithosAccountNode alloc] initWithPithos:pithos] autorelease];
203 containersNode = [[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil];
204 containersNodeChildren = [[NSMutableArray alloc] init];
205 sharedNode = [[PithosEmptyNode alloc] initWithDisplayName:@"SHARED" icon:nil];
206 mySharedNode = [[PithosAccountNode alloc] initWithPithos:pithos];
207 mySharedNode.displayName = @"my shared";
208 mySharedNode.shared = YES;
209 mySharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)];
210 othersSharedNode = [[PithosSharingAccountsNode alloc] initWithPithos:pithos];
211 othersSharedNode.displayName = @"others shared";
212 othersSharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGroupIcon)];
214 [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
216 // Register for updates
217 // PithosContainerNode updates browser nodes
218 [[NSNotificationCenter defaultCenter] addObserver:self
219 selector:@selector(pithosNodeChildrenUpdated:)
220 name:@"PithosContainerNodeChildrenUpdated"
222 // PithosSubdirNode updates browser nodes
223 [[NSNotificationCenter defaultCenter] addObserver:self
224 selector:@selector(pithosNodeChildrenUpdated:)
225 name:@"PithosSubdirNodeChildrenUpdated"
227 // PithosAccountNode accountNode updates outlineView container nodes
228 [[NSNotificationCenter defaultCenter] addObserver:self
229 selector:@selector(pithosAccountNodeChildrenUpdated:)
230 name:@"PithosAccountNodeChildrenUpdated"
232 // PithosAccountNode other than accountNode updates nodes
233 [[NSNotificationCenter defaultCenter] addObserver:self
234 selector:@selector(pithosNodeChildrenUpdated:)
235 name:@"PithosAccountNodeChildrenUpdated"
237 // PithosSharingAccountsNode othersSharedNode updates browser nodes
238 [[NSNotificationCenter defaultCenter] addObserver:self
239 selector:@selector(pithosNodeChildrenUpdated:)
240 name:@"PithosSharingAccountsNodeChildrenUpdated"
241 object:othersSharedNode];
242 // Request for browser refresh
243 [[NSNotificationCenter defaultCenter] addObserver:self
244 selector:@selector(pithosBrowserRefreshNeeded:)
245 name:@"PithosBrowserRefreshNeeeded"
249 - (void)resetBrowser {
250 [refreshTimer invalidate];
251 [refreshTimer release];
253 [moveNetworkQueue cancelAllOperations];
254 [copyNetworkQueue cancelAllOperations];
255 [deleteNetworkQueue cancelAllOperations];
256 [uploadNetworkQueue cancelAllOperations];
257 [downloadNetworkQueue cancelAllOperations];
260 [browser loadColumnZero];
261 [containersNodeChildren removeAllObjects];
262 [outlineView reloadData];
263 // Expand the folder outline view
264 [outlineView expandItem:nil expandChildren:YES];
265 [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
267 activityFacility.delegate = nil;
268 [activityProgressIndicator setDoubleValue:1.0];
269 [activityProgressIndicator stopAnimation:self];
272 - (void)startBrowser {
273 accountNode.pithos = pithos;
274 [accountNode refresh];
275 mySharedNode.pithos = pithos;
276 [mySharedNode refresh];
277 othersSharedNode.pithos = pithos;
278 [othersSharedNode refresh];
280 [activityFacility reset];
281 activityFacility.delegate = self;
283 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(forceRefresh:) userInfo:self repeats:YES] retain];
286 - (void)setPithos:(ASIPithos *)aPithos {
287 if (aPithos && [aPithos isNotEqualTo:pithos]) {
290 pithos = [aPithos retain];
295 - (void)windowDidLoad {
296 [super windowDidLoad];
297 if (browser && !browserInitialized) {
298 browserInitialized = YES;
304 #pragma mark Observers
306 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
307 PithosNode *node = (PithosNode *)[notification object];
308 if (node == accountNode)
310 NSLog(@"pithosNodeChildrenUpdated:%@", node.url);
311 NSInteger lastColumn = [browser lastColumn];
312 for (NSInteger column = lastColumn; column >= 0; column--) {
313 if ([[browser parentForItemsInColumn:column] isEqualTo:node]) {
314 [browser reloadColumn:column];
320 - (void)pithosAccountNodeChildrenUpdated:(NSNotification *)notification {
321 BOOL containerPithosFound = NO;
322 BOOL containerTrashFound = NO;
323 NSMutableIndexSet *removedContainersNodeChildren = [NSMutableIndexSet indexSet];
324 for (NSUInteger i = 0 ; i < [containersNodeChildren count] ; i++) {
325 if (![accountNode.children containsObject:[containersNodeChildren objectAtIndex:i]])
326 [removedContainersNodeChildren addIndex:i];
328 [containersNodeChildren removeObjectsAtIndexes:removedContainersNodeChildren];
329 for (PithosContainerNode *containerNode in accountNode.children) {
330 if ([containerNode.pithosContainer.name isEqualToString:@"pithos"]) {
331 if (![containersNodeChildren containsObject:containerNode])
332 [containersNodeChildren insertObject:containerNode atIndex:0];
333 containerPithosFound = YES;
334 } else if ([containerNode.pithosContainer.name isEqualToString:@"trash"]) {
335 NSUInteger insertIndex = 1;
336 if (!containerPithosFound)
338 if (![containersNodeChildren containsObject:containerNode])
339 [containersNodeChildren insertObject:containerNode atIndex:insertIndex];
340 containerTrashFound = YES;
341 } else if (![containersNodeChildren containsObject:containerNode]) {
342 [containersNodeChildren addObject:containerNode];
345 BOOL refreshAccountNode = NO;
346 if (!containerPithosFound) {
347 // Create pithos node
348 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithPithos:pithos
349 containerName:@"pithos"];
350 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
351 while (![containerRequest isFinished]) {
354 if ([containerRequest error]) {
355 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
357 refreshAccountNode = YES;
360 if (!containerTrashFound) {
362 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithPithos:pithos
363 containerName:@"trash"];
364 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
365 while (![containerRequest isFinished]) {
368 if ([containerRequest error]) {
369 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
371 refreshAccountNode = YES;
375 if (refreshAccountNode)
376 [accountNode refresh];
378 [outlineView reloadData];
380 // Expand the folder outline view
381 [outlineView expandItem:nil expandChildren:YES];
383 if ((rootNode == nil) || (rootNode == containersNode) || (rootNode == sharedNode)) {
384 rootNode = [containersNodeChildren objectAtIndex:0];
385 [browser loadColumnZero];
392 - (void)pithosBrowserRefreshNeeded:(NSNotification *)notification {
399 - (IBAction)forceRefresh:(id)sender {
401 [accountNode forceRefresh];
402 for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
403 PithosNode *node = (PithosNode *)[browser parentForItemsInColumn:column];
404 node.forcedRefresh = YES;
405 [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren];
407 [browser validateVisibleColumns];
410 - (IBAction)refresh:(id)sender {
411 if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) {
412 [self forceRefresh:sender];
415 [accountNode refresh];
416 for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
417 [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren];
419 [browser validateVisibleColumns];
424 #pragma mark NSBrowserDelegate
426 - (id)rootItemForBrowser:(NSBrowser *)browser {
430 - (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item {
431 PithosNode *node = (PithosNode *)item;
432 return node.children.count;
435 - (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
436 PithosNode *node = (PithosNode *)item;
437 return [node.children objectAtIndex:index];
440 - (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item {
441 PithosNode *node = (PithosNode *)item;
442 return node.isLeafItem;
445 - (id)browser:(NSBrowser *)browser objectValueForItem:(id)item {
446 PithosNode *node = (PithosNode *)item;
450 - (NSViewController *)browser:(NSBrowser *)browser previewViewControllerForLeafItem:(id)item {
451 if (sharedPreviewController == nil)
452 sharedPreviewController = [[NSViewController alloc] initWithNibName:@"PithosBrowserPreviewController" bundle:[NSBundle bundleForClass:[self class]]];
453 return sharedPreviewController;
456 //- (CGFloat)browser:(NSBrowser *)browser shouldSizeColumn:(NSInteger)columnIndex forUserResize:(BOOL)forUserResize toWidth:(CGFloat)suggestedWidth {
457 // if (!forUserResize) {
458 // id item = [browser parentForItemsInColumn:columnIndex];
459 // if ([self browser:browser isLeafItem:item]) {
460 // suggestedWidth = 200;
463 // return suggestedWidth;
466 - (BOOL)browser:(NSBrowser *)sender isColumnValid:(NSInteger)column {
472 - (BOOL)browser:(NSBrowser *)browser shouldEditItem:(id)item {
473 PithosNode *node = (PithosNode *)item;
474 if (node.shared || node.sharingAccount ||
475 ([node class] == [PithosContainerNode class]) || ([node class] == [PithosAccountNode class]))
480 - (void)browser:(NSBrowser *)browser setObjectValue:(id)object forItem:(id)item {
481 PithosNode *node = (PithosNode *)item;
482 NSString *newName = (NSString *)object;
483 NSUInteger newNameLength = [newName length];
484 NSRange firstSlashRange = [newName rangeOfString:@"/"];
485 if ((newNameLength == 0) ||
486 ((firstSlashRange.length == 1) && (firstSlashRange.location != (newNameLength - 1))) ||
487 ([newName isEqualToString:node.displayName])) {
490 if (([node class] == [PithosObjectNode class]) ||
491 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
492 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
493 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
494 NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
495 if ([newName hasSuffix:@"/"])
496 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
497 NSError *error = nil;
499 if ([PithosUtilities objectExistsAtPithos:pithos
500 containerName:node.pithosContainer.name
501 objectName:destinationObjectName
503 isDirectory:&isDirectory
504 sharingAccount:nil]) {
505 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
506 [alert setMessageText:@"Name Taken"];
507 [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
508 [alert addButtonWithTitle:@"OK"];
516 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos
517 containerName:node.pithosContainer.name
518 objectName:node.pithosObject.name
519 destinationContainerName:node.pithosContainer.name
520 destinationObjectName:destinationObjectName
523 objectRequest.delegate = self;
524 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
525 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
526 NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
527 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
528 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
529 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
530 [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
531 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
532 message:messagePrefix];
533 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
534 [NSDictionary dictionaryWithObjectsAndKeys:
535 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
536 [NSNumber numberWithBool:YES], @"refresh",
537 activity, @"activity",
538 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
539 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
540 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
541 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
542 [NSNumber numberWithUnsignedInteger:10], @"retries",
543 NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector",
544 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
545 moveNetworkQueue, @"networkQueue",
546 @"moveQueue", @"queueType",
548 [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
552 } else if ([node class] == [PithosSubdirNode class]) {
553 if (firstSlashRange.length == 1)
555 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
556 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
557 NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
558 NSError *error = nil;
560 if ([PithosUtilities objectExistsAtPithos:pithos
561 containerName:node.pithosContainer.name
562 objectName:destinationObjectName
564 isDirectory:&isDirectory
565 sharingAccount:nil]) {
566 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
567 [alert setMessageText:@"Name Taken"];
568 [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
569 [alert addButtonWithTitle:@"OK"];
577 if (node.pithosObject.subdir)
578 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
579 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos
580 containerName:node.pithosContainer.name
581 objectName:node.pithosObject.name
582 destinationContainerName:node.pithosContainer.name
583 destinationObjectName:destinationObjectName
585 if (objectRequests) {
586 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
587 objectRequest.delegate = self;
588 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
589 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
590 NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
591 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
592 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
593 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
594 [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
595 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
596 message:messagePrefix];
597 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
598 [NSDictionary dictionaryWithObjectsAndKeys:
599 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
600 [NSNumber numberWithBool:YES], @"refresh",
601 activity, @"activity",
602 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
603 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
604 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
605 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
606 [NSNumber numberWithUnsignedInteger:10], @"retries",
607 NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector",
608 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
609 moveNetworkQueue, @"networkQueue",
610 @"moveQueue", @"queueType",
612 [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
620 #pragma mark Drag and Drop source
622 - (BOOL)browser:(NSBrowser *)aBrowser canDragRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column
623 withEvent:(NSEvent *)event {
624 NSIndexPath *baseIndexPath = [browser indexPathForColumn:column];
625 __block BOOL result = YES;
626 [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
627 PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:idx]];
628 if (([node class] == [PithosContainerNode class]) || ([node class] == [PithosAccountNode class])) {
636 - (BOOL)browser:(NSBrowser *)aBrowser writeRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column
637 toPasteboard:(NSPasteboard *)pasteboard {
638 NSMutableArray *propertyList = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
639 NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
640 NSIndexPath *baseIndexPath = [browser indexPathForColumn:column];
641 [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
642 PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:idx]];
643 [propertyList addObject:[node.pithosObject.name pathExtension]];
644 [nodes addObject:node];
647 [pasteboard declareTypes:[NSArray arrayWithObject:NSFilesPromisePboardType] owner:self];
648 [pasteboard setPropertyList:propertyList forType:NSFilesPromisePboardType];
649 self.draggedNodes = nodes;
650 self.draggedParentNode = [browser parentForItemsInColumn:column];
654 - (NSArray *)browser:(NSBrowser *)aBrowser namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
655 forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
656 NSMutableArray *names = [NSMutableArray arrayWithCapacity:[draggedNodes count]];
657 for (PithosNode *node in draggedNodes) {
658 // If the node is a subdir ask if the whole tree should be downloaded
659 if ([node class] == [PithosSubdirNode class]) {
660 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
661 [alert setMessageText:@"Download directory"];
662 [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to download its contents?", node.displayName]];
663 [alert addButtonWithTitle:@"OK"];
664 [alert addButtonWithTitle:@"Cancel"];
665 NSInteger choice = [alert runModal];
666 if (choice == NSAlertFirstButtonReturn) {
667 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
668 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
669 NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithPithos:pithos
670 containerName:node.pithosContainer.name
671 objectName:node.pithosObject.name
672 toDirectory:[dropDestination path]
674 sharingAccount:node.sharingAccount];
675 if (objectRequests) {
676 for (__block ASIPithosObjectRequest *objectRequest in objectRequests) {
677 [names addObject:[objectRequest.userInfo objectForKey:@"fileName"]];
678 objectRequest.delegate = self;
679 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
680 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
681 NSString *messagePrefix = [NSString stringWithFormat:@"Downloading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
682 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload
683 message:[messagePrefix stringByAppendingString:@" (0%)"]
684 totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue]
686 dispatch_async(dispatch_get_main_queue(), ^{
687 [activityFacility updateActivity:activity withMessage:activity.message];
689 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
690 [NSDictionary dictionaryWithObjectsAndKeys:
691 activity, @"activity",
692 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
693 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
694 [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage",
695 [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority",
696 [NSNumber numberWithUnsignedInteger:10], @"retries",
697 NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector",
698 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
699 downloadNetworkQueue, @"networkQueue",
700 @"downloadQueue", @"queueType",
702 [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
703 [activityFacility updateActivity:activity
704 withMessage:[messagePrefix stringByAppendingFormat:@" (%.0f%%)", (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)]
705 totalBytes:activity.totalBytes
706 currentBytes:(activity.currentBytes + size)];
708 [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
715 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
716 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
717 __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithPithos:pithos
718 containerName:node.pithosContainer.name
719 objectName:node.pithosObject.name
720 toDirectory:[dropDestination path]
722 sharingAccount:node.sharingAccount];
724 [names addObject:[objectRequest.userInfo objectForKey:@"fileName"]];
725 objectRequest.delegate = self;
726 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
727 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
728 NSString *messagePrefix = [NSString stringWithFormat:@"Downloading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
729 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload
730 message:[messagePrefix stringByAppendingString:@" (0%)"]
731 totalBytes:node.pithosObject.bytes
733 dispatch_async(dispatch_get_main_queue(), ^{
734 [activityFacility updateActivity:activity withMessage:activity.message];
736 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
737 [NSDictionary dictionaryWithObjectsAndKeys:
738 activity, @"activity",
739 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
740 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
741 [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage",
742 [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority",
743 [NSNumber numberWithUnsignedInteger:10], @"retries",
744 NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector",
745 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
746 downloadNetworkQueue, @"networkQueue",
747 @"downloadQueue", @"queueType",
749 [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
750 [activityFacility updateActivity:activity
751 withMessage:[messagePrefix stringByAppendingFormat:@" (%.0f%%)", (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)]
752 totalBytes:activity.totalBytes
753 currentBytes:(activity.currentBytes + size)];
755 [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
764 #pragma mark Drag and Drop destination
766 - (NSDragOperation)browser:aBrowser
767 validateDrop:(id<NSDraggingInfo>)info
768 proposedRow:(NSInteger *)row
769 column:(NSInteger *)column
770 dropOperation:(NSBrowserDropOperation *)dropOperation {
771 NSDragOperation result = NSDragOperationNone;
772 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
773 // For a drop above, the drop is redirected to the parent item
774 if (*dropOperation == NSBrowserDropAbove)
776 // Only allow dropping in folders
778 PithosNode *dropNode;
780 // Check if the node is not a folder and if so redirect to the parent item
781 dropNode = [browser itemAtRow:*row inColumn:*column];
782 if ([dropNode class] == [PithosObjectNode class])
786 dropNode = [browser parentForItemsInColumn:*column];
788 if (!dropNode.shared &&
789 (!dropNode.sharingAccount ||
790 ([dropNode class] == [PithosSubdirNode class]) ||
791 ([dropNode class] == [PithosContainerNode class]))) {
792 *dropOperation = NSBrowserDropOn;
793 result = NSDragOperationCopy;
796 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
797 // For a drop above, the drop is redirected to the parent item
798 if (*dropOperation == NSBrowserDropAbove)
800 // Only allow dropping in folders
802 PithosNode *dropNode;
804 // Check if the node is not a folder and if so redirect to the parent item
805 dropNode = [browser itemAtRow:*row inColumn:*column];
806 if ([dropNode class] == [PithosObjectNode class])
810 dropNode = [browser parentForItemsInColumn:*column];
812 if (!dropNode.shared && !dropNode.sharingAccount) {
813 if ([info draggingSourceOperationMask] & NSDragOperationMove) {
814 // NSDragOperationCopy | NSDragOperationMove -> NSDragOperationMove
815 if ((([dropNode class] == [PithosContainerNode class]) ||
816 dropNode.pithosObject.subdir ||
817 ![dropNode.pithosObject.name hasSuffix:@"/"]) &&
818 ![dropNode isEqualTo:draggedParentNode]) {
819 // ![dropNode isEqualTo:draggedParentNode] &&
820 // ![draggedNodes containsObject:dropNode]) {
821 result = NSDragOperationMove;
823 } else if ([info draggingSourceOperationMask] & NSDragOperationCopy) {
824 // NSDragOperationCopy (Option modifier) -> NSDragOperationCopy
825 if (([dropNode class] == [PithosContainerNode class]) ||
826 dropNode.pithosObject.subdir ||
827 ![dropNode.pithosObject.name hasSuffix:@"/"]) {
828 result = NSDragOperationCopy;
837 - (BOOL)browser:(NSBrowser *)aBrowser
838 acceptDrop:(id<NSDraggingInfo>)info
840 column:(NSInteger)column
841 dropOperation:(NSBrowserDropOperation)dropOperation {
842 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
843 NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
844 NSLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames);
845 if ((column != -1) && (filenames != nil)) {
848 node = [browser itemAtRow:row inColumn:column];
850 node = [browser parentForItemsInColumn:column];
851 NSLog(@"drag in node: %@", node.url);
852 return [self uploadFiles:filenames toNode:node];
854 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
855 NSLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes);
856 if ((column != -1) && (draggedNodes != nil)) {
859 node = [browser itemAtRow:row inColumn:column];
861 node = [browser parentForItemsInColumn:column];
862 NSLog(@"drag local node: %@", node.url);
863 if ([info draggingSourceOperationMask] & NSDragOperationMove)
864 return [self moveNodes:draggedNodes toNode:node];
865 else if ([info draggingSourceOperationMask] & NSDragOperationCopy)
866 return [self copyNodes:draggedNodes toNode:node];
873 #pragma mark Drag and Drop methods
875 - (BOOL)uploadFiles:(NSArray *)filenames toNode:(PithosNode *)destinationNode {
876 if (([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class]))
878 NSFileManager *fileManager = [NSFileManager defaultManager];
879 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
880 NSString *objectNamePrefix;
881 if ([destinationNode class] == [PithosSubdirNode class])
882 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
884 objectNamePrefix = [NSString string];
885 if ((destinationNode.pithosContainer.blockHash == nil) || (destinationNode.pithosContainer.blockSize == 0)) {
886 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest containerMetadataRequestWithPithos:pithos
887 containerName:containerName];
888 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
889 while (![containerRequest isFinished]) {
892 if ([containerRequest error]) {
893 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
895 } else if (containerRequest.responseStatusCode != 204) {
896 [PithosUtilities unexpectedResponseStatusAlertWithRequest:containerRequest];
899 destinationNode.pithosContainer.blockHash = [containerRequest blockHash];
900 destinationNode.pithosContainer.blockSize = [containerRequest blockSize];
902 NSUInteger blockSize = destinationNode.pithosContainer.blockSize;
903 NSString *blockHash = destinationNode.pithosContainer.blockHash;
905 for (NSString *filePath in filenames) {
907 if ([fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
910 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
911 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
912 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
913 NSError *error = nil;
914 NSString *contentType = [PithosUtilities contentTypeOfFile:filePath error:&error];
915 if (contentType == nil)
916 contentType = @"application/octet-stream";
918 NSLog(@"contentType detection error: %@", error);
919 NSArray *hashes = nil;
920 ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos
921 containerName:containerName
922 objectName:objectName
923 contentType:contentType
929 sharingAccount:destinationNode.sharingAccount];
931 objectRequest.delegate = self;
932 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
933 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
934 NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
935 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload
936 message:[messagePrefix stringByAppendingString:@" (0%)"]
937 totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue]
939 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
940 [NSDictionary dictionaryWithObjectsAndKeys:
941 containerName, @"containerName",
942 objectName, @"objectName",
943 contentType, @"contentType",
944 [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
945 blockHash, @"blockHash",
946 filePath, @"filePath",
948 [NSArray arrayWithObject:destinationNode], @"refreshNodes",
949 [NSNumber numberWithBool:YES], @"refresh",
950 [NSNumber numberWithUnsignedInteger:10], @"iteration",
951 activity, @"activity",
952 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
953 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
954 [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage",
955 [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority",
956 [NSNumber numberWithUnsignedInteger:10], @"retries",
957 NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector",
958 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
959 uploadNetworkQueue, @"networkQueue",
960 @"uploadQueue", @"queueType",
962 if (destinationNode.sharingAccount)
963 [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
964 [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
969 // Upload directory, confirm first
970 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
971 [alert setMessageText:@"Upload directory"];
972 [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to upload it and its contents?", filePath]];
973 [alert addButtonWithTitle:@"OK"];
974 [alert addButtonWithTitle:@"Cancel"];
975 NSInteger choice = [alert runModal];
976 if (choice == NSAlertFirstButtonReturn) {
977 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
978 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
979 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
980 NSMutableArray *objectNames = nil;
981 NSMutableArray *contentTypes = nil;
982 NSMutableArray *filePaths = nil;
983 NSMutableArray *hashesArrays = nil;
984 NSMutableArray *directoryObjectRequests = nil;
985 NSArray *objectRequests = [PithosUtilities writeObjectDataRequestsWithPithos:pithos
986 containerName:containerName
987 objectName:objectName
990 forDirectory:filePath
992 objectNames:&objectNames
993 contentTypes:&contentTypes
995 hashesArrays:&hashesArrays
996 directoryObjectRequests:&directoryObjectRequests
997 sharingAccount:destinationNode.sharingAccount];
998 for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) {
999 objectRequest.delegate = self;
1000 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1001 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1002 NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
1003 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory
1004 message:messagePrefix];
1005 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
1006 [NSDictionary dictionaryWithObjectsAndKeys:
1007 [NSNumber numberWithBool:YES], @"refresh",
1008 activity, @"activity",
1009 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
1010 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
1011 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
1012 [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority",
1013 [NSNumber numberWithUnsignedInteger:10], @"retries",
1014 NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector",
1015 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
1016 uploadNetworkQueue, @"networkQueue",
1017 @"uploadQueue", @"queueType",
1019 [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
1021 if (objectRequests) {
1022 for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) {
1023 ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i];
1024 objectRequest.delegate = self;
1025 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1026 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1027 NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
1028 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload
1029 message:[messagePrefix stringByAppendingString:@" (0%)"]
1030 totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue]
1032 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
1033 [NSDictionary dictionaryWithObjectsAndKeys:
1034 containerName, @"containerName",
1035 [objectNames objectAtIndex:i], @"objectName",
1036 [contentTypes objectAtIndex:i], @"contentType",
1037 [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
1038 blockHash, @"blockHash",
1039 [filePaths objectAtIndex:i], @"filePath",
1040 [hashesArrays objectAtIndex:i], @"hashes",
1041 [NSNumber numberWithBool:YES], @"refresh",
1042 [NSNumber numberWithUnsignedInteger:10], @"iteration",
1043 activity, @"activity",
1044 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
1045 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
1046 [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage",
1047 [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority",
1048 [NSNumber numberWithUnsignedInteger:10], @"retries",
1049 NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector",
1050 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
1051 uploadNetworkQueue, @"networkQueue",
1052 @"uploadQueue", @"queueType",
1054 if (destinationNode.sharingAccount)
1055 [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
1056 [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
1068 - (BOOL)moveNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode {
1069 if ((([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class])) ||
1070 (([destinationNode class] == [PithosSubdirNode class]) && !destinationNode.pithosObject.subdir && [destinationNode.pithosObject.name hasSuffix:@"/"]))
1072 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
1073 NSString *objectNamePrefix;
1074 if ([destinationNode class] == [PithosSubdirNode class])
1075 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
1077 objectNamePrefix = [NSString string];
1079 for (PithosNode *node in nodes) {
1080 if (([node class] == [PithosObjectNode class]) ||
1081 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
1082 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1083 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1084 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
1085 if ([node.pithosObject.name hasSuffix:@"/"])
1086 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
1087 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos
1088 containerName:node.pithosContainer.name
1089 objectName:node.pithosObject.name
1090 destinationContainerName:containerName
1091 destinationObjectName:destinationObjectName
1093 if (objectRequest) {
1094 objectRequest.delegate = self;
1095 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1096 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1097 NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
1098 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1099 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1100 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1101 [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
1102 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
1103 message:messagePrefix];
1104 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1105 [NSDictionary dictionaryWithObjectsAndKeys:
1106 [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes",
1107 activity, @"activity",
1108 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
1109 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
1110 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
1111 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
1112 [NSNumber numberWithUnsignedInteger:10], @"retries",
1113 NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector",
1114 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
1115 moveNetworkQueue, @"networkQueue",
1116 @"moveQueue", @"queueType",
1118 [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1122 } else if ([node class] == [PithosSubdirNode class]) {
1123 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1124 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1125 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
1126 if (node.pithosObject.subdir)
1127 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
1128 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos
1129 containerName:node.pithosContainer.name
1130 objectName:node.pithosObject.name
1131 destinationContainerName:containerName
1132 destinationObjectName:destinationObjectName
1134 if (objectRequests) {
1135 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1136 objectRequest.delegate = self;
1137 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1138 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1139 NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
1140 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1141 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1142 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1143 [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
1144 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
1145 message:messagePrefix];
1146 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1147 [NSDictionary dictionaryWithObjectsAndKeys:
1148 [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes",
1149 [NSNumber numberWithBool:YES], @"refresh",
1150 activity, @"activity",
1151 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
1152 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
1153 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
1154 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
1155 [NSNumber numberWithUnsignedInteger:10], @"retries",
1156 NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector",
1157 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
1158 moveNetworkQueue, @"networkQueue",
1159 @"moveQueue", @"queueType",
1161 [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1171 - (BOOL)copyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode {
1172 if ((([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class])) ||
1173 (([destinationNode class] == [PithosSubdirNode class]) && !destinationNode.pithosObject.subdir && [destinationNode.pithosObject.name hasSuffix:@"/"]))
1175 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
1176 NSString *objectNamePrefix;
1177 if ([destinationNode class] == [PithosSubdirNode class])
1178 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
1180 objectNamePrefix = [NSString string];
1182 for (PithosNode *node in nodes) {
1183 if (([node class] == [PithosObjectNode class]) ||
1184 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
1185 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1186 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1187 NSString *destinationObjectName;
1188 if (![destinationNode isEqualTo:node.parent]) {
1189 destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
1190 if ([node.pithosObject.name hasSuffix:@"/"])
1191 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
1193 destinationObjectName = [PithosUtilities safeObjectNameForPithos:pithos
1194 containerName:containerName
1195 objectName:node.pithosObject.name];
1197 ASIPithosObjectRequest *objectRequest = [PithosUtilities copyObjectRequestWithPithos:pithos
1198 containerName:node.pithosContainer.name
1199 objectName:node.pithosObject.name
1200 destinationContainerName:containerName
1201 destinationObjectName:destinationObjectName
1203 sharingAccount:node.sharingAccount];
1204 if (objectRequest) {
1205 objectRequest.delegate = self;
1206 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1207 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1208 NSString *messagePrefix = [NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'",
1209 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1210 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1211 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1212 [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
1213 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy
1214 message:messagePrefix];
1215 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1216 [NSDictionary dictionaryWithObjectsAndKeys:
1217 [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes",
1218 activity, @"activity",
1219 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
1220 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
1221 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
1222 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
1223 [NSNumber numberWithUnsignedInteger:10], @"retries",
1224 NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector",
1225 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
1226 copyNetworkQueue, @"networkQueue",
1227 @"copyQueue", @"queueType",
1229 [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1233 } else if ([node class] == [PithosSubdirNode class]) {
1234 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1235 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1236 NSString *destinationObjectName;
1237 if (![destinationNode isEqualTo:node.parent]) {
1238 destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
1239 if (node.pithosObject.subdir)
1240 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
1242 destinationObjectName = [PithosUtilities safeSubdirNameForPithos:pithos
1243 containerName:containerName
1244 subdirName:node.pithosObject.name];
1246 NSArray *objectRequests = [PithosUtilities copyObjectRequestsForSubdirWithPithos:pithos
1247 containerName:node.pithosContainer.name
1248 objectName:node.pithosObject.name
1249 destinationContainerName:containerName
1250 destinationObjectName:destinationObjectName
1252 sharingAccount:node.sharingAccount];
1253 if (objectRequests) {
1254 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1255 objectRequest.delegate = self;
1256 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1257 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1258 NSString *messagePrefix = [NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'",
1259 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1260 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1261 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1262 [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
1263 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy
1264 message:messagePrefix];
1265 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1266 [NSDictionary dictionaryWithObjectsAndKeys:
1267 [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes",
1268 activity, @"activity",
1269 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
1270 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
1271 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
1272 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
1273 [NSNumber numberWithUnsignedInteger:10], @"retries",
1274 NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector",
1275 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
1276 copyNetworkQueue, @"networkQueue",
1277 @"copyQueue", @"queueType",
1279 [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1290 #pragma mark ASIHTTPRequestDelegate
1292 - (void)performRequestFinishedDelegateInBackground:(ASIPithosRequest *)request {
1293 dispatch_queue_t queue;
1294 if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"moveQueue"])
1296 else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"copyQueue"])
1298 else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"deleteQueue"])
1299 queue = deleteQueue;
1300 else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"uploadQueue"])
1301 queue = uploadQueue;
1302 else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"downloadQueue"])
1303 queue = downloadQueue;
1306 dispatch_async(queue, ^{
1307 [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) withObject:request];
1311 - (void)performRequestFailedDelegateInBackground:(ASIPithosRequest *)request {
1312 dispatch_queue_t queue;
1313 if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"moveQueue"])
1315 else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"copyQueue"])
1317 else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"deleteQueue"])
1318 queue = deleteQueue;
1319 else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"uploadQueue"])
1320 queue = uploadQueue;
1321 else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"downloadQueue"])
1322 queue = downloadQueue;
1325 dispatch_async(queue, ^{
1326 [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) withObject:request];
1330 - (void)requestFailed:(ASIPithosRequest *)request {
1331 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1332 NSLog(@"Request failed: %@", request.url);
1333 if ([request isCancelled]) {
1334 dispatch_async(dispatch_get_main_queue(), ^{
1335 [activityFacility endActivity:[request.userInfo objectForKey:@"activity"]
1336 withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
1341 NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1343 ASIPithosRequest *newRequest = (ASIPithosRequest *)[[PithosUtilities copyRequest:request] autorelease];
1344 [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1345 [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithBool:NO] forKey:@"unexpectedResponseStatus"];
1346 [[newRequest.userInfo objectForKey:@"networkQueue"] addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]];
1348 dispatch_async(dispatch_get_main_queue(), ^{
1349 [activityFacility endActivity:[request.userInfo objectForKey:@"activity"]
1350 withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]];
1351 if ([[request.userInfo objectForKey:@"unexpectedResponseStatus"] boolValue])
1352 [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
1354 [PithosUtilities httpRequestErrorAlertWithRequest:request];
1360 - (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1361 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1362 NSLog(@"Download finished: %@", objectRequest.url);
1363 if (objectRequest.responseStatusCode == 200) {
1364 NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"];
1365 PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
1366 NSUInteger totalBytes = activity.totalBytes;
1368 // XXX change contentLength to objectContentLength if it is fixed in the server
1369 if (([objectRequest contentLength] == 0) && ![PithosUtilities isContentTypeDirectory:[objectRequest contentType]]) {
1370 NSLog(@"Downloaded 0 bytes");
1371 NSFileManager *fileManager = [NSFileManager defaultManager];
1372 if (![fileManager fileExistsAtPath:filePath]) {
1373 if (![fileManager createFileAtPath:filePath contents:nil attributes:nil]) {
1374 dispatch_async(dispatch_get_main_queue(), ^{
1375 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1376 [alert setMessageText:@"Create File Error"];
1377 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create zero length file at %@", filePath]];
1378 [alert addButtonWithTitle:@"OK"];
1385 NSUInteger currentBytes = [objectRequest objectContentLength];
1386 if (currentBytes == 0)
1387 currentBytes = totalBytes;
1388 dispatch_async(dispatch_get_main_queue(), ^{
1389 [activityFacility endActivity:activity
1390 withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]
1391 totalBytes:totalBytes
1392 currentBytes:currentBytes];
1395 [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
1396 [self requestFailed:objectRequest];
1401 - (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1402 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1403 NSLog(@"Upload directory object finished: %@", objectRequest.url);
1404 if (objectRequest.responseStatusCode == 201) {
1405 NSLog(@"Directory object created: %@", objectRequest.url);
1406 dispatch_async(dispatch_get_main_queue(), ^{
1407 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1408 withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
1410 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1411 [node forceRefresh];
1413 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1416 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1417 [self forceRefresh:self];
1418 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1419 [self refresh:self];
1421 [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
1422 [self requestFailed:objectRequest];
1427 - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
1428 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1429 NSLog(@"Upload using hashmap finished: %@", objectRequest.url);
1430 NSString *fileName = [objectRequest.userInfo objectForKey:@"fileName"];
1431 PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
1432 NSUInteger totalBytes = activity.totalBytes;
1433 NSUInteger currentBytes = activity.currentBytes;
1434 if (objectRequest.responseStatusCode == 201) {
1435 NSLog(@"Object created: %@", objectRequest.url);
1436 dispatch_async(dispatch_get_main_queue(), ^{
1437 [activityFacility endActivity:activity
1438 withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]
1439 totalBytes:totalBytes
1440 currentBytes:totalBytes];
1442 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1443 [node forceRefresh];
1445 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1448 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1449 [self forceRefresh:self];
1450 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1451 [self refresh:self];
1452 } else if (objectRequest.responseStatusCode == 409) {
1453 NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue];
1454 if (iteration == 0) {
1455 NSLog(@"Upload iteration limit reached: %@", objectRequest.url);
1456 dispatch_async(dispatch_get_main_queue(), ^{
1457 [activityFacility endActivity:activity
1458 withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
1459 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1460 [alert setMessageText:@"Upload Timeout"];
1461 [alert setInformativeText:[NSString stringWithFormat:@"Upload iteration limit reached for object with path '%@'",
1462 [objectRequest.userInfo objectForKey:@"objectName"]]];
1463 [alert addButtonWithTitle:@"OK"];
1469 NSLog(@"object is missing hashes: %@", objectRequest.url);
1470 NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForHashes:[objectRequest.userInfo objectForKey:@"hashes"]
1471 withMissingHashes:[objectRequest hashes]];
1472 NSUInteger blockSize = [[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
1473 if (totalBytes >= [missingBlocks count]*blockSize)
1474 currentBytes = totalBytes - [missingBlocks count]*blockSize;
1475 dispatch_async(dispatch_get_main_queue(), ^{
1476 [activityFacility updateActivity:activity
1477 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (totalBytes ? (100*(currentBytes + 0.0)/(totalBytes + 0.0)) : 100)]
1478 totalBytes:totalBytes
1479 currentBytes:currentBytes];
1481 NSUInteger missingBlockIndex = [missingBlocks firstIndex];
1482 __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos
1483 containerName:[objectRequest.userInfo objectForKey:@"containerName"]
1485 forFile:[objectRequest.userInfo objectForKey:@"filePath"]
1486 missingBlockIndex:missingBlockIndex
1487 sharingAccount:[objectRequest.userInfo objectForKey:@"sharingAccount"]];
1488 newContainerRequest.delegate = self;
1489 newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1490 newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1491 newContainerRequest.userInfo = objectRequest.userInfo;
1492 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:(--iteration)] forKey:@"iteration"];
1493 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1494 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"];
1495 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
1496 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadMissingBlockFinished:)) forKey:@"didFinishSelector"];
1497 [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){
1498 [activityFacility updateActivity:activity
1499 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)]
1500 totalBytes:activity.totalBytes
1501 currentBytes:(activity.currentBytes + size)];
1503 [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
1505 [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
1506 [self requestFailed:objectRequest];
1511 - (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest {
1512 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1513 NSLog(@"Upload of missing block finished: %@", containerRequest.url);
1514 NSUInteger blockSize = [[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
1515 NSString *fileName = [containerRequest.userInfo objectForKey:@"fileName"];
1516 PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"];
1517 if (containerRequest.responseStatusCode == 202) {
1518 NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"];
1519 NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue];
1520 missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex];
1521 if (missingBlockIndex == NSNotFound) {
1522 NSArray *hashes = [containerRequest.userInfo objectForKey:@"hashes"];
1523 ASIPithosObjectRequest *newObjectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos
1524 containerName:[containerRequest.userInfo objectForKey:@"containerName"]
1525 objectName:[containerRequest.userInfo objectForKey:@"objectName"]
1526 contentType:[containerRequest.userInfo objectForKey:@"contentType"]
1528 blockHash:[containerRequest.userInfo objectForKey:@"blockHash"]
1529 forFile:[containerRequest.userInfo objectForKey:@"filePath"]
1532 sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
1533 newObjectRequest.delegate = self;
1534 newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1535 newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1536 newObjectRequest.userInfo = containerRequest.userInfo;
1537 [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1538 [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"];
1539 [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"];
1540 [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)) forKey:@"didFinishSelector"];
1541 [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
1543 __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos
1544 containerName:[containerRequest.userInfo objectForKey:@"containerName"]
1545 blockSize:[[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]
1546 forFile:[containerRequest.userInfo objectForKey:@"filePath"]
1547 missingBlockIndex:missingBlockIndex
1548 sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
1549 newContainerRequest.delegate = self;
1550 newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1551 newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1552 newContainerRequest.userInfo = containerRequest.userInfo;
1553 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1554 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
1555 [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){
1556 [activityFacility updateActivity:activity
1557 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)]
1558 totalBytes:activity.totalBytes
1559 currentBytes:(activity.currentBytes + size)];
1561 [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
1564 [(NSMutableDictionary *)(containerRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
1565 [self requestFailed:containerRequest];
1570 - (void)moveFinished:(ASIPithosObjectRequest *)objectRequest {
1571 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1572 NSLog(@"Move object finished: %@", objectRequest.url);
1573 if (objectRequest.responseStatusCode == 201) {
1574 dispatch_async(dispatch_get_main_queue(), ^{
1575 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1576 withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
1578 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1579 [node forceRefresh];
1581 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1584 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1585 [self forceRefresh:self];
1586 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1587 [self refresh:self];
1589 [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
1590 [self requestFailed:objectRequest];
1595 - (void)copyFinished:(ASIPithosObjectRequest *)objectRequest {
1596 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1597 NSLog(@"Copy object finished: %@", objectRequest.url);
1598 if (objectRequest.responseStatusCode == 201) {
1599 dispatch_async(dispatch_get_main_queue(), ^{
1600 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1601 withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
1603 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1604 [node forceRefresh];
1606 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1609 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1610 [self forceRefresh:self];
1611 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1612 [self refresh:self];
1614 [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
1615 [self requestFailed:objectRequest];
1620 - (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1621 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1622 NSLog(@"Delete object finished: %@", objectRequest.url);
1623 if (objectRequest.responseStatusCode == 204) {
1624 dispatch_async(dispatch_get_main_queue(), ^{
1625 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1626 withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
1628 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1629 [node forceRefresh];
1631 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1634 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1635 [self forceRefresh:self];
1636 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1637 [self refresh:self];
1639 [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
1640 [self requestFailed:objectRequest];
1646 #pragma mark NSSplitViewDelegate
1648 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
1649 if (splitView == verticalSplitView)
1652 return ([horizontalSplitView bounds].size.height - 108);
1655 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
1656 if (splitView == verticalSplitView)
1659 return ([horizontalSplitView bounds].size.height - 108);
1662 - (CGFloat)splitView:(NSSplitView *)splitView constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)dividerIndex {
1663 if (splitView == verticalSplitView) {
1664 if (proposedPosition < 120)
1666 else if (proposedPosition > 220)
1669 return proposedPosition;
1671 return ([horizontalSplitView bounds].size.height - 108);
1676 #pragma mark NSOutlineViewDataSource
1678 - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
1681 if (item == containersNode)
1682 return containersNodeChildren.count;
1683 if (item == sharedNode)
1688 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
1690 return (!index ? containersNode : sharedNode);
1691 if (item == sharedNode)
1692 return (!index ? mySharedNode : othersSharedNode);
1693 return [containersNodeChildren objectAtIndex:index];
1696 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
1697 if ((item == containersNode) || (item == sharedNode))
1702 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
1703 PithosNode *node = (PithosNode *)item;
1707 #pragma mark Drag and Drop destination
1709 - (NSDragOperation)outlineView:(NSOutlineView *)anOutlineView
1710 validateDrop:(id<NSDraggingInfo>)info
1711 proposedItem:(id)item
1712 proposedChildIndex:(NSInteger)index {
1713 NSDragOperation result = NSDragOperationNone;
1714 if ((item == nil) || (index != NSOutlineViewDropOnItemIndex))
1716 PithosNode *dropNode = (PithosNode *)item;
1717 if ([dropNode class] != [PithosContainerNode class])
1719 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
1720 result = NSDragOperationCopy;
1721 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
1722 if ([info draggingSourceOperationMask] & NSDragOperationMove) {
1723 // NSDragOperationCopy | NSDragOperationMove -> NSDragOperationMove
1724 if (![dropNode isEqualTo:draggedParentNode])
1725 result = NSDragOperationMove;
1726 } else if ([info draggingSourceOperationMask] & NSDragOperationCopy) {
1727 // NSDragOperationCopy (Option modifier) -> NSDragOperationCopy
1728 result = NSDragOperationCopy;
1734 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id<NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index {
1735 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
1736 NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
1737 NSLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames);
1738 if (item && (index == NSOutlineViewDropOnItemIndex) && (filenames != nil)) {
1739 PithosNode *node = (PithosNode *)item;
1740 NSLog(@"drag in node: %@", node.url);
1741 return [self uploadFiles:filenames toNode:node];
1743 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
1744 NSLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes);
1745 if (item && (index == NSOutlineViewDropOnItemIndex) && (draggedNodes != nil)) {
1746 PithosNode *node = (PithosNode *)item;
1747 NSLog(@"drag local node: %@", node.url);
1748 if ([info draggingSourceOperationMask] & NSDragOperationMove)
1749 return [self moveNodes:draggedNodes toNode:node];
1750 else if ([info draggingSourceOperationMask] & NSDragOperationCopy)
1751 return [self copyNodes:draggedNodes toNode:node];
1758 #pragma mark NSOutlineViewDelegate
1760 - (BOOL)outlineView:outlineView shouldSelectItem:(id)item {
1761 if ((item == containersNode) || (item == sharedNode))
1766 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
1767 if ((item == containersNode) || (item == sharedNode))
1772 - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
1773 PithosNode *node = (PithosNode *)[outlineView itemAtRow:[outlineView selectedRow]];
1776 [browser loadColumnZero];
1782 #pragma mark NSMenuDelegate
1784 - (void)menuNeedsUpdate:(NSMenu *)menu {
1785 [menu removeAllItems];
1786 NSMenuItem *menuItem;
1787 NSString *menuItemTitle;
1788 BOOL nodeContextMenu = NO;
1789 PithosNode *menuNode = nil;
1790 NSMutableArray *menuNodes;
1791 if (menu == browserMenu) {
1792 NSInteger column = [browser clickedColumn];
1793 NSInteger row = [browser clickedRow];
1794 if ((column == -1) || (row == -1)) {
1795 // General context menu
1796 NSArray *menuNodesIndexPaths = [browser selectionIndexPaths];
1797 if ([menuNodesIndexPaths count] == 0) {
1798 menuNode = [browser parentForItemsInColumn:0];
1799 } else if (([menuNodesIndexPaths count] != 1) ||
1800 ([[browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]] class] == [PithosObjectNode class])) {
1801 menuNode = [browser parentForItemsInColumn:([[menuNodesIndexPaths objectAtIndex:0] length] - 1)];
1803 menuNode = [browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]];
1806 // Node context menu
1807 NSIndexPath *clickedNodeIndexPath = [[browser indexPathForColumn:column] indexPathByAddingIndex:row];
1808 NSArray *menuNodesIndexPaths = [browser selectionIndexPaths];
1809 menuNodes = [NSMutableArray arrayWithCapacity:[menuNodesIndexPaths count]];
1810 if ([menuNodesIndexPaths containsObject:clickedNodeIndexPath]) {
1811 for (NSIndexPath *nodeIndexPath in menuNodesIndexPaths) {
1812 [menuNodes addObject:[browser itemAtIndexPath:nodeIndexPath]];
1815 [menuNodes addObject:[browser itemAtIndexPath:clickedNodeIndexPath]];
1817 nodeContextMenu = YES;
1819 } else if (menu == outlineViewMenu) {
1820 NSInteger row = [outlineView clickedRow];
1822 row = [outlineView selectedRow];
1825 menuNode = [outlineView itemAtRow:row];
1828 if (!nodeContextMenu) {
1829 // General context menu
1830 if (([menuNode class] == [PithosAccountNode class]) ||
1831 ([menuNode class] == [PithosSharingAccountsNode class]) ||
1832 ([menuNode class] == [PithosEmptyNode class]))
1834 BOOL shared = menuNode.shared;
1835 BOOL sharingAccount = (menuNode.sharingAccount != nil);
1837 if (!shared && !sharingAccount) {
1838 menuItem = [[[NSMenuItem alloc] initWithTitle:@"New Folder" action:@selector(menuNewFolder:) keyEquivalent:@""] autorelease];
1839 [menuItem setRepresentedObject:menuNode];
1840 [menu addItem:menuItem];
1841 [menu addItem:[NSMenuItem separatorItem]];
1844 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(menuGetInfo:) keyEquivalent:@""] autorelease];
1845 [menuItem setRepresentedObject:[NSArray arrayWithObject:menuNode]];
1846 [menu addItem:menuItem];
1848 if (!shared && !sharingAccount) {
1849 if (clipboardNodes) {
1850 NSUInteger clipboardNodesCount = [clipboardNodes count];
1851 if (clipboardNodesCount == 0) {
1852 self.clipboardNodes = nil;
1853 } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) {
1854 if (clipboardNodesCount == 1)
1855 menuItemTitle = [NSString stringWithString:@"Paste Item"];
1857 menuItemTitle = [NSString stringWithString:@"Paste Items"];
1858 [menu addItem:[NSMenuItem separatorItem]];
1859 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
1860 [menuItem setRepresentedObject:menuNode];
1861 [menu addItem:menuItem];
1866 // Node context menu
1867 NSUInteger menuNodesCount = [menuNodes count];
1868 PithosNode *firstMenuNode = (PithosNode *)[menuNodes objectAtIndex:0];
1869 BOOL shared = firstMenuNode.shared;
1870 BOOL sharingAccount = (firstMenuNode.sharingAccount != nil);
1871 // Move to Trash (pithos container only)
1873 if (!shared && !sharingAccount) {
1874 if ([rootNode class] == [PithosContainerNode class]) {
1875 if ([rootNode.pithosContainer.name isEqualToString:@"pithos"]) {
1876 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Move to Trash" action:@selector(menuMoveToTrash:) keyEquivalent:@""] autorelease];
1877 [menuItem setRepresentedObject:menuNodes];
1878 [menu addItem:menuItem];
1880 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(menuDelete:) keyEquivalent:@""] autorelease];
1881 [menuItem setRepresentedObject:menuNodes];
1882 [menu addItem:menuItem];
1883 [menu addItem:[NSMenuItem separatorItem]];
1887 if (!sharingAccount || ([firstMenuNode class] != [PithosAccountNode class])) {
1888 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(menuGetInfo:) keyEquivalent:@""] autorelease];
1889 [menuItem setRepresentedObject:menuNodes];
1890 [menu addItem:menuItem];
1892 if ((!shared && !sharingAccount) || ([firstMenuNode class] != [PithosContainerNode class]))
1893 [menu addItem:[NSMenuItem separatorItem]];
1896 if (!shared && !sharingAccount) {
1897 if (menuNodesCount == 1)
1898 menuItemTitle = [NSString stringWithFormat:@"Cut \"%@\"", ((PithosNode *)[menuNodes objectAtIndex:0]).displayName];
1900 menuItemTitle = [NSString stringWithFormat:@"Cut %lu Items", menuNodesCount];
1901 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCut:) keyEquivalent:@""] autorelease];
1902 [menuItem setRepresentedObject:menuNodes];
1903 [menu addItem:menuItem];
1906 if ((!shared && !sharingAccount) ||
1907 (([firstMenuNode class] != [PithosContainerNode class]) && ([firstMenuNode class] != [PithosAccountNode class]))) {
1908 if (menuNodesCount == 1)
1909 menuItemTitle = [NSString stringWithFormat:@"Copy \"%@\"", ((PithosNode *)[menuNodes objectAtIndex:0]).displayName];
1911 menuItemTitle = [NSString stringWithFormat:@"Copy %lu Items", menuNodesCount];
1912 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCopy:) keyEquivalent:@""] autorelease];
1913 [menuItem setRepresentedObject:menuNodes];
1914 [menu addItem:menuItem];
1917 if (!shared && !sharingAccount) {
1918 if (menuNodesCount == 1) {
1919 PithosNode *menuNode = [menuNodes objectAtIndex:0];
1920 if (([menuNode class] == [PithosSubdirNode class]) &&
1921 (menuNode.pithosObject.subdir || ![menuNode.pithosObject.name hasSuffix:@"/"])) {
1922 if (clipboardNodes) {
1923 NSUInteger clipboardNodesCount = [clipboardNodes count];
1924 if (clipboardNodesCount == 0) {
1925 self.clipboardNodes = nil;
1926 } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) {
1927 if (clipboardNodesCount == 1)
1928 menuItemTitle = [NSString stringWithString:@"Paste Item"];
1930 menuItemTitle = [NSString stringWithString:@"Paste Items"];
1931 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
1932 [menuItem setRepresentedObject:menuNode];
1933 [menu addItem:menuItem];
1943 #pragma mark Menu Actions
1945 - (void)menuNewFolder:(NSMenuItem *)sender {
1946 PithosNode *node = (PithosNode *)[sender representedObject];
1947 if ([node class] == [PithosContainerNode class]) {
1948 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1949 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1950 NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos
1951 containerName:node.pithosContainer.name
1952 subdirName:@"untitled folder"];
1953 NSString *fileName = [safeObjectName lastPathComponent];
1954 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos
1955 containerName:node.pithosContainer.name
1956 objectName:safeObjectName
1958 contentType:@"application/directory"
1960 contentDisposition:nil
1963 isPublic:ASIPithosObjectRequestPublicIgnore
1965 data:[NSData data]];
1966 objectRequest.delegate = self;
1967 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1968 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1969 NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName];
1970 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory
1971 message:messagePrefix];
1972 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1973 fileName, @"fileName",
1974 [NSArray arrayWithObject:node], @"refreshNodes",
1975 [NSNumber numberWithBool:YES], @"refresh",
1976 activity, @"activity",
1977 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
1978 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
1979 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
1980 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
1981 [NSNumber numberWithUnsignedInteger:10], @"retries",
1982 NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector",
1983 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
1984 uploadNetworkQueue, @"networkQueue",
1985 @"uploadQueue", @"queueType",
1987 [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1990 } else if (([node class] == [PithosSubdirNode class]) &&
1991 (node.pithosObject.subdir || ![node.pithosObject.name hasSuffix:@"/"])) {
1992 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1993 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1994 NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos
1995 containerName:node.pithosContainer.name
1996 subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]];
1997 NSString *fileName = [safeObjectName lastPathComponent];
1998 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos
1999 containerName:node.pithosContainer.name
2000 objectName:safeObjectName
2002 contentType:@"application/directory"
2004 contentDisposition:nil
2007 isPublic:ASIPithosObjectRequestPublicIgnore
2009 data:[NSData data]];
2010 objectRequest.delegate = self;
2011 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
2012 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
2013 NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName];
2014 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory
2015 message:messagePrefix];
2016 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
2017 fileName, @"fileName",
2018 [NSArray arrayWithObject:node], @"refreshNodes",
2019 [NSNumber numberWithBool:YES], @"refresh",
2020 activity, @"activity",
2021 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
2022 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
2023 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
2024 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
2025 [NSNumber numberWithUnsignedInteger:10], @"retries",
2026 NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector",
2027 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
2028 uploadNetworkQueue, @"networkQueue",
2029 @"uploadQueue", @"queueType",
2031 [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
2037 - (void)menuGetInfo:(NSMenuItem *)sender {
2038 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
2039 [node showPithosNodeInfo:sender];
2043 - (void)menuDelete:(NSMenuItem *)sender {
2044 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
2045 if (([node class] == [PithosObjectNode class]) ||
2046 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
2047 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2048 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2049 NSString *fileName = [node.pithosObject.name lastPathComponent];
2050 if ([node.pithosObject.name hasSuffix:@"/"])
2051 fileName = [fileName stringByAppendingString:@"/"];
2052 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos
2053 containerName:node.pithosContainer.name
2054 objectName:node.pithosObject.name];
2055 objectRequest.delegate = self;
2056 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
2057 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
2058 NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", fileName];
2059 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete
2060 message:messagePrefix];
2061 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
2062 fileName, @"fileName",
2063 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
2064 activity, @"activity",
2065 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
2066 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
2067 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
2068 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
2069 [NSNumber numberWithUnsignedInteger:10], @"retries",
2070 NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector",
2071 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
2072 deleteNetworkQueue, @"networkQueue",
2073 @"deleteQueue", @"queueType",
2075 [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
2078 } else if ([node class] == [PithosSubdirNode class]) {
2079 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2080 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2081 NSArray *objectRequests = [PithosUtilities deleteObjectRequestsForSubdirWithPithos:pithos
2082 containerName:node.pithosContainer.name
2083 objectName:node.pithosObject.name];
2084 if (objectRequests) {
2085 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
2086 objectRequest.delegate = self;
2087 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
2088 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
2089 NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
2090 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete
2091 message:messagePrefix];
2092 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
2093 [NSDictionary dictionaryWithObjectsAndKeys:
2094 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
2095 activity, @"activity",
2096 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
2097 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
2098 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
2099 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
2100 [NSNumber numberWithUnsignedInteger:10], @"retries",
2101 NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector",
2102 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
2103 deleteNetworkQueue, @"networkQueue",
2104 @"deleteQueue", @"queueType",
2106 [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
2115 - (void)menuMoveToTrash:(NSMenuItem *)sender {
2116 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
2117 if (([node class] == [PithosObjectNode class]) ||
2118 (([node class] == [PithosSubdirNode class]) &&
2119 !node.pithosObject.subdir &&
2120 [node.pithosObject.name hasSuffix:@"/"])) {
2121 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2122 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2123 NSString *safeObjectName = [PithosUtilities safeObjectNameForPithos:pithos
2124 containerName:@"trash"
2125 objectName:node.pithosObject.name];
2126 if (safeObjectName) {
2127 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos
2128 containerName:node.pithosContainer.name
2129 objectName:node.pithosObject.name
2130 destinationContainerName:@"trash"
2131 destinationObjectName:safeObjectName
2133 if (objectRequest) {
2134 objectRequest.delegate = self;
2135 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
2136 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
2137 NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
2138 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
2139 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
2140 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
2141 [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
2142 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
2143 message:messagePrefix];
2144 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
2145 [NSDictionary dictionaryWithObjectsAndKeys:
2146 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
2147 activity, @"activity",
2148 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
2149 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
2150 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
2151 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
2152 [NSNumber numberWithUnsignedInteger:10], @"retries",
2153 NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector",
2154 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
2155 moveNetworkQueue, @"networkQueue",
2156 @"moveQueue", @"queueType",
2158 [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
2163 } else if ([node class] == [PithosSubdirNode class]) {
2164 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2165 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2166 NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos
2167 containerName:@"trash"
2168 subdirName:node.pithosObject.name];
2169 if (safeObjectName) {
2170 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos
2171 containerName:node.pithosContainer.name
2172 objectName:node.pithosObject.name
2173 destinationContainerName:@"trash"
2174 destinationObjectName:safeObjectName
2176 if (objectRequests) {
2177 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
2178 objectRequest.delegate = self;
2179 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
2180 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
2181 NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
2182 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
2183 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
2184 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
2185 [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
2186 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
2187 message:messagePrefix];
2188 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
2189 [NSDictionary dictionaryWithObjectsAndKeys:
2190 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
2191 activity, @"activity",
2192 [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage",
2193 [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage",
2194 [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage",
2195 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
2196 [NSNumber numberWithUnsignedInteger:10], @"retries",
2197 NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector",
2198 NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector",
2199 moveNetworkQueue, @"networkQueue",
2200 @"moveQueue", @"queueType",
2202 [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
2212 - (void)menuCut:(NSMenuItem *)sender {
2213 self.clipboardNodes = (NSArray *)[sender representedObject];
2214 self.clipboardParentNode = ((PithosNode *)[clipboardNodes objectAtIndex:0]).parent;
2215 self.clipboardCopy = NO;
2218 - (void)menuCopy:(NSMenuItem *)sender {
2219 self.clipboardNodes = (NSArray *)[sender representedObject];
2220 self.clipboardParentNode = ((PithosNode *)[clipboardNodes objectAtIndex:0]).parent;
2221 self.clipboardCopy = YES;
2224 - (void)menuPaste:(NSMenuItem *)sender {
2225 if (!clipboardNodes || ![clipboardNodes count])
2227 PithosNode *dropNode = (PithosNode *)[sender representedObject];
2228 NSArray *localClipboardNodes = [NSArray arrayWithArray:clipboardNodes];
2229 if (!clipboardCopy && ![dropNode isEqualTo:clipboardParentNode]) {
2230 self.clipboardNodes = nil;
2231 self.clipboardParentNode = nil;
2232 [self moveNodes:localClipboardNodes toNode:dropNode];
2234 [self copyNodes:localClipboardNodes toNode:dropNode];
2239 #pragma mark PithosActivityFacilityDelegate
2241 - (void)activityUpdate:(NSDictionary *)info {
2242 NSString *message = [info objectForKey:@"message"];
2243 NSUInteger runningActivitiesCount = [[info objectForKey:@"runningActivitiesCount"] unsignedIntegerValue];
2244 // NSUInteger endingActivitiesCount = [[info objectForKey:@"endingActivitiesCount"] unsignedIntegerValue];
2245 NSUInteger totalUploadBytes = [[info objectForKey:@"totalUploadBytes"] unsignedIntegerValue];
2246 NSUInteger currentUploadBytes = [[info objectForKey:@"currentUploadBytes"] unsignedIntegerValue];
2247 NSUInteger totalDownloadBytes = [[info objectForKey:@"totalDownloadBytes"] unsignedIntegerValue];
2248 NSUInteger currentDownloadBytes = [[info objectForKey:@"currentDownloadBytes"] unsignedIntegerValue];
2249 NSUInteger totalBytes = totalUploadBytes + totalDownloadBytes;
2250 if (runningActivitiesCount && totalBytes) {
2251 [activityProgressIndicator setDoubleValue:((currentUploadBytes + currentDownloadBytes + 0.0)/(totalBytes + 0.0))];
2252 [activityProgressIndicator startAnimation:self];
2254 [activityProgressIndicator setDoubleValue:1.0];
2255 [activityProgressIndicator stopAnimation:self];
2259 message = [[[[UsingSizeTransformer alloc] init] autorelease] transformedValue:accountNode.pithosAccount];
2260 [activityTextField setStringValue:message];