2 // PithosBrowserController.m
5 // Copyright 2011 GRNET S.A. All rights reserved.
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
11 // 1. Redistributions of source code must retain the above
12 // copyright notice, this list of conditions and the following
15 // 2. Redistributions in binary form must reproduce the above
16 // copyright notice, this list of conditions and the following
17 // disclaimer in the documentation and/or other materials
18 // provided with the distribution.
20 // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
33 // The views and conclusions contained in the software and
34 // documentation are those of the authors and should not be
35 // interpreted as representing official policies, either expressed
36 // or implied, of GRNET S.A.
38 #import "PithosBrowserController.h"
39 #import "PithosNode.h"
40 #import "PithosAccountNode.h"
41 #import "PithosContainerNode.h"
42 #import "PithosSubdirNode.h"
43 #import "PithosObjectNode.h"
44 #import "PithosSharingAccountsNode.h"
45 #import "PithosEmptyNode.h"
46 #import "ImageAndTextCell.h"
47 #import "FileSystemBrowserCell.h"
48 #import "ASIPithosRequest.h"
49 #import "ASIPithosContainerRequest.h"
50 #import "ASIPithosObjectRequest.h"
51 #import "ASIPithosAccount.h"
52 #import "ASIPithosContainer.h"
53 #import "ASIPithosObject.h"
54 #import "PithosUtilities.h"
55 #import "BytesSizeTransformer.h"
57 @interface PithosBrowserCell : FileSystemBrowserCell {}
60 @implementation PithosBrowserCell
63 if ((self = [super init])) {
64 [self setLineBreakMode:NSLineBreakByTruncatingMiddle];
65 [self setEditable:YES];
70 - (void)setObjectValue:(id)object {
71 if ([object isKindOfClass:[PithosNode class]]) {
72 PithosNode *node = (PithosNode *)object;
73 [self setStringValue:node.displayName];
74 [self setImage:node.icon];
76 [super setObjectValue:object];
82 @interface PithosOutlineViewCell : ImageAndTextCell {}
85 @implementation PithosOutlineViewCell
87 - (void)setObjectValue:(id)object {
88 if ([object isKindOfClass:[PithosNode class]]) {
89 PithosNode *node = (PithosNode *)object;
90 [self setStringValue:node.displayName];
91 [self setImage:node.icon];
92 [self setEditable:NO];
94 [super setObjectValue:object];
100 @interface PithosBrowserController (Private) {}
101 - (void)resetContainers:(NSNotification *)notification;
102 - (BOOL)uploadFiles:(NSArray *)filenames toNode:(PithosNode *)destinationNode;
103 - (BOOL)moveNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode;
104 - (BOOL)copyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode;
107 @implementation PithosBrowserController
108 @synthesize accountNode;
109 @synthesize verticalSplitView, horizontalSplitView, leftTopView, leftBottomView, outlineView, browser, outlineViewMenu, browserMenu;
110 @synthesize draggedNodes, draggedParentNode;
111 @synthesize clipboardNodes, clipboardParentNode, clipboardCopy;
112 @synthesize activityTextField, activityProgressIndicator;
115 #pragma Object Lifecycle
118 return [super initWithWindowNibName:@"PithosBrowserController"];
122 [[NSNotificationCenter defaultCenter] removeObserver:self];
123 [clipboardParentNode release];
124 [clipboardNodes release];
125 [draggedParentNode release];
126 [draggedNodes release];
127 [sharedPreviewController release];
128 [othersSharedNode release];
129 [mySharedNode release];
130 [sharedNode release];
131 [containersNodeChildren release];
132 [containersNode release];
133 [accountNode release];
138 - (void)awakeFromNib {
139 [super awakeFromNib];
141 [browser registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]];
142 [browser setDraggingSourceOperationMask:(NSDragOperationCopy|NSDragOperationMove) forLocal:YES];
143 [browser setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
145 [outlineView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]];
146 [outlineView setDraggingSourceOperationMask:(NSDragOperationCopy|NSDragOperationMove) forLocal:YES];
147 [outlineView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
149 [browser setCellClass:[PithosBrowserCell class]];
152 - (void)resetContainers:(NSNotification *)notification {
154 [browser loadColumnZero];
155 [containersNodeChildren removeAllObjects];
156 [outlineView reloadData];
157 // Expand the folder outline view
158 [outlineView expandItem:nil expandChildren:YES];
159 [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
162 [accountNode refresh];
163 [mySharedNode refresh];
164 [othersSharedNode refresh];
166 [activityFacility reset];
169 - (void)windowDidLoad {
170 [super windowDidLoad];
172 [activityProgressIndicator setUsesThreadedAnimation:YES];
173 [activityProgressIndicator setMinValue:0.0];
174 [activityProgressIndicator setMaxValue:1.0];
175 activityFacility = [PithosActivityFacility defaultPithosActivityFacility];
176 activityFacility.delegate = self;
178 accountNode = [[PithosAccountNode alloc] init];
179 containersNode = [[PithosEmptyNode alloc] initWithDisplayName:@"CONTAINERS" icon:nil];
180 containersNodeChildren = [[NSMutableArray alloc] init];
181 sharedNode = [[PithosEmptyNode alloc] initWithDisplayName:@"SHARED" icon:nil];
182 mySharedNode = [[PithosAccountNode alloc] init];
183 mySharedNode.displayName = @"my shared";
184 mySharedNode.shared = YES;
185 mySharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)];
186 othersSharedNode = [[PithosSharingAccountsNode alloc] init];
187 othersSharedNode.displayName = @"others shared";
188 othersSharedNode.icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGroupIcon)];
190 [[[outlineView tableColumns] objectAtIndex:0] setDataCell:[[[PithosOutlineViewCell alloc] init] autorelease]];
192 // Register for updates
193 // PithosContainerNode updates browser nodes
194 [[NSNotificationCenter defaultCenter] addObserver:self
195 selector:@selector(pithosNodeChildrenUpdated:)
196 name:@"PithosContainerNodeChildrenUpdated"
198 // PithosSubdirNode updates browser nodes
199 [[NSNotificationCenter defaultCenter] addObserver:self
200 selector:@selector(pithosNodeChildrenUpdated:)
201 name:@"PithosSubdirNodeChildrenUpdated"
203 // PithosAccountNode accountNode updates outlineView container nodes
204 [[NSNotificationCenter defaultCenter] addObserver:self
205 selector:@selector(pithosAccountNodeChildrenUpdated:)
206 name:@"PithosAccountNodeChildrenUpdated"
208 // PithosAccountNode other than accountNode updates nodes
209 [[NSNotificationCenter defaultCenter] addObserver:self
210 selector:@selector(pithosNodeChildrenUpdated:)
211 name:@"PithosAccountNodeChildrenUpdated"
213 // PithosSharingAccountsNode othersSharedNode updates browser nodes
214 [[NSNotificationCenter defaultCenter] addObserver:self
215 selector:@selector(pithosNodeChildrenUpdated:)
216 name:@"PithosSharingAccountsNodeChildrenUpdated"
217 object:othersSharedNode];
218 // Updated authentication credentials reset containers in the outline view
219 [[NSNotificationCenter defaultCenter] addObserver:self
220 selector:@selector(resetContainers:)
221 name:@"PithosAuthenticationCredentialsUpdated"
223 // Request for browser refresh
224 [[NSNotificationCenter defaultCenter] addObserver:self
225 selector:@selector(pithosBrowserRefreshNeeded:)
226 name:@"PithosBrowserRefreshNeeeded"
231 #pragma mark Observers
233 - (void)pithosNodeChildrenUpdated:(NSNotification *)notification {
234 PithosNode *node = (PithosNode *)[notification object];
235 if (node == accountNode)
237 NSLog(@"pithosNodeChildrenUpdated:%@", node.url);
238 NSInteger lastColumn = [browser lastColumn];
239 for (NSInteger column = lastColumn; column >= 0; column--) {
240 if ([[browser parentForItemsInColumn:column] isEqualTo:node]) {
241 [browser reloadColumn:column];
247 - (void)pithosAccountNodeChildrenUpdated:(NSNotification *)notification {
248 BOOL containerPithosFound = NO;
249 BOOL containerTrashFound = NO;
250 NSMutableIndexSet *removedContainersNodeChildren = [NSMutableIndexSet indexSet];
251 for (NSUInteger i = 0 ; i < [containersNodeChildren count] ; i++) {
252 if (![accountNode.children containsObject:[containersNodeChildren objectAtIndex:i]])
253 [removedContainersNodeChildren addIndex:i];
255 [containersNodeChildren removeObjectsAtIndexes:removedContainersNodeChildren];
256 for (PithosContainerNode *containerNode in accountNode.children) {
257 if ([containerNode.pithosContainer.name isEqualToString:@"pithos"]) {
258 if (![containersNodeChildren containsObject:containerNode])
259 [containersNodeChildren insertObject:containerNode atIndex:0];
260 containerPithosFound = YES;
261 } else if ([containerNode.pithosContainer.name isEqualToString:@"trash"]) {
262 NSUInteger insertIndex = 1;
263 if (!containerPithosFound)
265 if (![containersNodeChildren containsObject:containerNode])
266 [containersNodeChildren insertObject:containerNode atIndex:insertIndex];
267 containerTrashFound = YES;
268 } else if (![containersNodeChildren containsObject:containerNode]) {
269 [containersNodeChildren addObject:containerNode];
272 BOOL refreshAccountNode = NO;
273 if (!containerPithosFound) {
274 // Create pithos node
275 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"pithos"];
276 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
277 while (![containerRequest isFinished]) {
280 if ([containerRequest error]) {
281 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
283 refreshAccountNode = YES;
286 if (!containerTrashFound) {
288 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithContainerName:@"trash"];
289 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
290 while (![containerRequest isFinished]) {
293 if ([containerRequest error]) {
294 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
296 refreshAccountNode = YES;
300 if (refreshAccountNode)
301 [accountNode refresh];
303 [outlineView reloadData];
305 // Expand the folder outline view
306 [outlineView expandItem:nil expandChildren:YES];
308 if ((rootNode == nil) || (rootNode == containersNode) || (rootNode == sharedNode)) {
309 rootNode = [containersNodeChildren objectAtIndex:0];
310 [browser loadColumnZero];
317 - (void)pithosBrowserRefreshNeeded:(NSNotification *)notification {
324 - (IBAction)forceRefresh:(id)sender {
326 [accountNode forceRefresh];
327 for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
328 PithosNode *node = (PithosNode *)[browser parentForItemsInColumn:column];
329 node.forcedRefresh = YES;
330 [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren];
332 [browser validateVisibleColumns];
335 - (IBAction)refresh:(id)sender {
336 if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) {
337 [self forceRefresh:sender];
340 [accountNode refresh];
341 for (NSInteger column = [browser lastColumn]; column >= 0; column--) {
342 [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren];
344 [browser validateVisibleColumns];
349 #pragma mark NSBrowserDelegate
351 - (id)rootItemForBrowser:(NSBrowser *)browser {
355 - (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item {
356 PithosNode *node = (PithosNode *)item;
357 return node.children.count;
360 - (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
361 PithosNode *node = (PithosNode *)item;
362 return [node.children objectAtIndex:index];
365 - (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item {
366 PithosNode *node = (PithosNode *)item;
367 return node.isLeafItem;
370 - (id)browser:(NSBrowser *)browser objectValueForItem:(id)item {
371 PithosNode *node = (PithosNode *)item;
375 - (NSViewController *)browser:(NSBrowser *)browser previewViewControllerForLeafItem:(id)item {
376 if (sharedPreviewController == nil)
377 sharedPreviewController = [[NSViewController alloc] initWithNibName:@"PithosBrowserPreviewController" bundle:[NSBundle bundleForClass:[self class]]];
378 return sharedPreviewController;
381 //- (CGFloat)browser:(NSBrowser *)browser shouldSizeColumn:(NSInteger)columnIndex forUserResize:(BOOL)forUserResize toWidth:(CGFloat)suggestedWidth {
382 // if (!forUserResize) {
383 // id item = [browser parentForItemsInColumn:columnIndex];
384 // if ([self browser:browser isLeafItem:item]) {
385 // suggestedWidth = 200;
388 // return suggestedWidth;
391 - (BOOL)browser:(NSBrowser *)sender isColumnValid:(NSInteger)column {
397 - (BOOL)browser:(NSBrowser *)browser shouldEditItem:(id)item {
398 PithosNode *node = (PithosNode *)item;
399 if (node.shared || node.sharingAccount ||
400 ([node class] == [PithosContainerNode class]) || ([node class] == [PithosAccountNode class]))
405 - (void)browser:(NSBrowser *)browser setObjectValue:(id)object forItem:(id)item {
406 PithosNode *node = (PithosNode *)item;
407 NSString *newName = (NSString *)object;
408 NSUInteger newNameLength = [newName length];
409 NSRange firstSlashRange = [newName rangeOfString:@"/"];
410 if ((newNameLength == 0) ||
411 ((firstSlashRange.length == 1) && (firstSlashRange.location != (newNameLength - 1))) ||
412 ([newName isEqualToString:node.displayName])) {
415 if (([node class] == [PithosObjectNode class]) ||
416 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
417 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
418 dispatch_async(queue, ^{
419 NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
420 if ([newName hasSuffix:@"/"])
421 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
422 NSError *error = nil;
424 if ([PithosUtilities objectExistsAtContainerName:node.pithosContainer.name
425 objectName:destinationObjectName
427 isDirectory:&isDirectory
428 sharingAccount:nil]) {
429 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
430 [alert setMessageText:@"Name Taken"];
431 [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
432 [alert addButtonWithTitle:@"OK"];
438 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name
439 objectName:node.pithosObject.name
440 destinationContainerName:node.pithosContainer.name
441 destinationObjectName:destinationObjectName
444 objectRequest.delegate = self;
445 objectRequest.didFinishSelector = @selector(moveFinished:);
446 objectRequest.didFailSelector = @selector(moveFailed:);
447 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
448 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
449 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
450 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
451 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
452 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
453 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
454 [NSDictionary dictionaryWithObjectsAndKeys:
455 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
456 [NSNumber numberWithBool:YES], @"refresh",
457 activity, @"activity",
458 [NSNumber numberWithUnsignedInteger:10], @"retries",
460 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
463 } else if ([node class] == [PithosSubdirNode class]) {
464 if (firstSlashRange.length == 1)
466 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
467 dispatch_async(queue, ^{
468 NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
469 NSError *error = nil;
471 if ([PithosUtilities objectExistsAtContainerName:node.pithosContainer.name
472 objectName:destinationObjectName
474 isDirectory:&isDirectory
475 sharingAccount:nil]) {
476 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
477 [alert setMessageText:@"Name Taken"];
478 [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
479 [alert addButtonWithTitle:@"OK"];
485 if (node.pithosObject.subdir)
486 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
487 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
488 objectName:node.pithosObject.name
489 destinationContainerName:node.pithosContainer.name
490 destinationObjectName:destinationObjectName
492 if (objectRequests) {
493 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
494 objectRequest.delegate = self;
495 objectRequest.didFinishSelector = @selector(moveFinished:);
496 objectRequest.didFailSelector = @selector(moveFailed:);
497 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
498 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
499 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
500 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
501 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
502 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
503 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
504 [NSDictionary dictionaryWithObjectsAndKeys:
505 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
506 [NSNumber numberWithBool:YES], @"refresh",
507 activity, @"activity",
508 [NSNumber numberWithUnsignedInteger:10], @"retries",
510 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
517 #pragma mark Drag and Drop source
519 - (BOOL)browser:(NSBrowser *)aBrowser canDragRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column
520 withEvent:(NSEvent *)event {
521 NSIndexPath *baseIndexPath = [browser indexPathForColumn:column];
522 __block BOOL result = YES;
523 [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
524 PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:idx]];
525 if (([node class] == [PithosContainerNode class]) || ([node class] == [PithosAccountNode class])) {
533 - (BOOL)browser:(NSBrowser *)aBrowser writeRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column
534 toPasteboard:(NSPasteboard *)pasteboard {
535 NSMutableArray *propertyList = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
536 NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
537 NSIndexPath *baseIndexPath = [browser indexPathForColumn:column];
538 [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
539 PithosNode *node = [browser itemAtIndexPath:[baseIndexPath indexPathByAddingIndex:idx]];
540 [propertyList addObject:[node.pithosObject.name pathExtension]];
541 [nodes addObject:node];
544 [pasteboard declareTypes:[NSArray arrayWithObject:NSFilesPromisePboardType] owner:self];
545 [pasteboard setPropertyList:propertyList forType:NSFilesPromisePboardType];
546 self.draggedNodes = nodes;
547 self.draggedParentNode = [browser parentForItemsInColumn:column];
551 - (NSArray *)browser:(NSBrowser *)aBrowser namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
552 forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
553 NSMutableArray *names = [NSMutableArray arrayWithCapacity:[draggedNodes count]];
554 for (PithosNode *node in draggedNodes) {
555 // If the node is a subdir ask if the whole tree should be downloaded
556 if ([node class] == [PithosSubdirNode class]) {
557 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
558 [alert setMessageText:@"Download directory"];
559 [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to download its contents?", node.displayName]];
560 [alert addButtonWithTitle:@"OK"];
561 [alert addButtonWithTitle:@"Cancel"];
562 NSInteger choice = [alert runModal];
563 if (choice == NSAlertFirstButtonReturn) {
564 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
565 dispatch_async(queue, ^{
566 NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithContainerName:node.pithosContainer.name
567 objectName:node.pithosObject.name
568 toDirectory:[dropDestination path]
570 sharingAccount:node.sharingAccount];
571 if (objectRequests) {
572 for (__block ASIPithosObjectRequest *objectRequest in objectRequests) {
573 [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
574 objectRequest.delegate = self;
575 objectRequest.didFinishSelector = @selector(downloadObjectFinished:);
576 objectRequest.didFailSelector = @selector(downloadObjectFailed:);
577 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload
578 message:[NSString stringWithFormat:@"Downloading '%@' (0%%)", [objectRequest.userInfo objectForKey:@"fileName"]]
579 totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue]
581 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
582 [NSDictionary dictionaryWithObjectsAndKeys:
583 activity, @"activity",
584 [NSNumber numberWithUnsignedInteger:10], @"retries",
586 [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
587 [activityFacility updateActivity:activity
588 withMessage:[NSString stringWithFormat:@"Downloading '%@' (%.0f%%)", [objectRequest.userInfo valueForKey:@"fileName"], (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
589 totalBytes:activity.totalBytes
590 currentBytes:(activity.currentBytes + size)];
592 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
598 __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithContainerName:node.pithosContainer.name
599 objectName:node.pithosObject.name
600 toDirectory:[dropDestination path]
602 sharingAccount:node.sharingAccount];
604 [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
605 objectRequest.delegate = self;
606 objectRequest.didFinishSelector = @selector(downloadObjectFinished:);
607 objectRequest.didFailSelector = @selector(downloadObjectFailed:);
608 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload
609 message:[NSString stringWithFormat:@"Downloading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
610 totalBytes:node.pithosObject.bytes
612 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
613 [NSDictionary dictionaryWithObjectsAndKeys:
614 activity, @"activity",
615 [NSNumber numberWithUnsignedInteger:10], @"retries",
617 [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
618 [activityFacility updateActivity:activity
619 withMessage:[NSString stringWithFormat:@"Downloading '%@' (%.0f%%)", [objectRequest.userInfo valueForKey:@"fileName"], (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
620 totalBytes:activity.totalBytes
621 currentBytes:(activity.currentBytes + size)];
623 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
630 #pragma mark Drag and Drop destination
632 - (NSDragOperation)browser:aBrowser
633 validateDrop:(id<NSDraggingInfo>)info
634 proposedRow:(NSInteger *)row
635 column:(NSInteger *)column
636 dropOperation:(NSBrowserDropOperation *)dropOperation {
637 NSDragOperation result = NSDragOperationNone;
638 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
639 // For a drop above, the drop is redirected to the parent item
640 if (*dropOperation == NSBrowserDropAbove)
642 // Only allow dropping in folders
644 PithosNode *dropNode;
646 // Check if the node is not a folder and if so redirect to the parent item
647 dropNode = [browser itemAtRow:*row inColumn:*column];
648 if ([dropNode class] == [PithosObjectNode class])
652 dropNode = [browser parentForItemsInColumn:*column];
654 if (!dropNode.shared &&
655 (!dropNode.sharingAccount ||
656 ([dropNode class] == [PithosSubdirNode class]) ||
657 ([dropNode class] == [PithosContainerNode class]))) {
658 *dropOperation = NSBrowserDropOn;
659 result = NSDragOperationCopy;
662 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
663 // For a drop above, the drop is redirected to the parent item
664 if (*dropOperation == NSBrowserDropAbove)
666 // Only allow dropping in folders
668 PithosNode *dropNode;
670 // Check if the node is not a folder and if so redirect to the parent item
671 dropNode = [browser itemAtRow:*row inColumn:*column];
672 if ([dropNode class] == [PithosObjectNode class])
676 dropNode = [browser parentForItemsInColumn:*column];
678 if (!dropNode.shared && !dropNode.sharingAccount) {
679 if ([info draggingSourceOperationMask] & NSDragOperationMove) {
680 // NSDragOperationCopy | NSDragOperationMove -> NSDragOperationMove
681 if ((([dropNode class] == [PithosContainerNode class]) ||
682 dropNode.pithosObject.subdir ||
683 ![dropNode.pithosObject.name hasSuffix:@"/"]) &&
684 ![dropNode isEqualTo:draggedParentNode]) {
685 // ![dropNode isEqualTo:draggedParentNode] &&
686 // ![draggedNodes containsObject:dropNode]) {
687 result = NSDragOperationMove;
689 } else if ([info draggingSourceOperationMask] & NSDragOperationCopy) {
690 // NSDragOperationCopy (Option modifier) -> NSDragOperationCopy
691 if (([dropNode class] == [PithosContainerNode class]) ||
692 dropNode.pithosObject.subdir ||
693 ![dropNode.pithosObject.name hasSuffix:@"/"]) {
694 result = NSDragOperationCopy;
703 - (BOOL)browser:(NSBrowser *)aBrowser
704 acceptDrop:(id<NSDraggingInfo>)info
706 column:(NSInteger)column
707 dropOperation:(NSBrowserDropOperation)dropOperation {
708 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
709 NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
710 NSLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames);
711 if ((column != -1) && (filenames != nil)) {
714 node = [browser itemAtRow:row inColumn:column];
716 node = [browser parentForItemsInColumn:column];
717 NSLog(@"drag in node: %@", node.url);
718 return [self uploadFiles:filenames toNode:node];
720 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
721 NSLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes);
722 if ((column != -1) && (draggedNodes != nil)) {
725 node = [browser itemAtRow:row inColumn:column];
727 node = [browser parentForItemsInColumn:column];
728 NSLog(@"drag local node: %@", node.url);
729 if ([info draggingSourceOperationMask] & NSDragOperationMove)
730 return [self moveNodes:draggedNodes toNode:node];
731 else if ([info draggingSourceOperationMask] & NSDragOperationCopy)
732 return [self copyNodes:draggedNodes toNode:node];
739 #pragma mark Drag and Drop methods
741 - (BOOL)uploadFiles:(NSArray *)filenames toNode:(PithosNode *)destinationNode {
742 if (([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class]))
744 NSFileManager *defaultManager = [NSFileManager defaultManager];
745 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
746 NSString *objectNamePrefix;
747 if ([destinationNode class] == [PithosSubdirNode class])
748 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
750 objectNamePrefix = [NSString string];
751 if ((destinationNode.pithosContainer.blockHash == nil) || (destinationNode.pithosContainer.blockSize == 0)) {
752 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest containerMetadataRequestWithContainerName:containerName];
753 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
754 while (![containerRequest isFinished]) {
757 if ([containerRequest error]) {
758 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
760 } else if (containerRequest.responseStatusCode != 200) {
761 [PithosUtilities unexpectedResponseStatusAlertWithRequest:containerRequest];
764 destinationNode.pithosContainer.blockHash = [containerRequest blockHash];
765 destinationNode.pithosContainer.blockSize = [containerRequest blockSize];
767 NSUInteger blockSize = destinationNode.pithosContainer.blockSize;
768 NSString *blockHash = destinationNode.pithosContainer.blockHash;
770 for (NSString *filePath in filenames) {
772 if ([defaultManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
775 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
776 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
777 dispatch_async(queue, ^{
778 NSError *error = nil;
779 NSString *contentType = [PithosUtilities contentTypeOfFile:filePath error:&error];
780 if (contentType == nil)
781 contentType = @"application/octet-stream";
783 NSLog(@"contentType detection error: %@", error);
784 NSArray *hashes = nil;
785 ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithContainerName:containerName
786 objectName:objectName
787 contentType:contentType
793 sharingAccount:destinationNode.sharingAccount];
795 objectRequest.delegate = self;
796 objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
797 objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
798 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload
799 message:[NSString stringWithFormat:@"Uploading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
800 totalBytes:[[objectRequest.userInfo valueForKey:@"bytes"] unsignedIntegerValue]
802 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
803 [NSDictionary dictionaryWithObjectsAndKeys:
804 containerName, @"containerName",
805 objectName, @"objectName",
806 contentType, @"contentType",
807 [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
808 blockHash, @"blockHash",
809 filePath, @"filePath",
811 [NSArray arrayWithObject:destinationNode], @"refreshNodes",
812 [NSNumber numberWithBool:YES], @"refresh",
813 [NSNumber numberWithUnsignedInteger:10], @"iteration",
814 activity, @"activity",
815 [NSNumber numberWithUnsignedInteger:10], @"retries",
817 if (destinationNode.sharingAccount)
818 [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
819 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
823 // Upload directory, confirm first
824 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
825 [alert setMessageText:@"Upload directory"];
826 [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to upload it and its contents?", filePath]];
827 [alert addButtonWithTitle:@"OK"];
828 [alert addButtonWithTitle:@"Cancel"];
829 NSInteger choice = [alert runModal];
830 if (choice == NSAlertFirstButtonReturn) {
831 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
832 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
833 dispatch_async(queue, ^{
834 NSMutableArray *objectNames = nil;
835 NSMutableArray *contentTypes = nil;
836 NSMutableArray *filePaths = nil;
837 NSMutableArray *hashesArrays = nil;
838 NSMutableArray *directoryObjectRequests = nil;
839 NSArray *objectRequests = [PithosUtilities writeObjectDataRequestsWithContainerName:containerName
840 objectName:objectName
843 forDirectory:filePath
845 objectNames:&objectNames
846 contentTypes:&contentTypes
848 hashesArrays:&hashesArrays
849 directoryObjectRequests:&directoryObjectRequests
850 sharingAccount:destinationNode.sharingAccount];
851 for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) {
852 objectRequest.delegate = self;
853 objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
854 objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
855 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory
856 message:[NSString stringWithFormat:@"Creating directory '%@'", [objectRequest.userInfo valueForKey:@"fileName"]]];
857 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
858 [NSDictionary dictionaryWithObjectsAndKeys:
859 [NSNumber numberWithBool:YES], @"refresh",
860 activity, @"activity",
861 [NSNumber numberWithUnsignedInteger:10], @"retries",
863 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
865 if (objectRequests) {
866 for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) {
867 ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i];
868 objectRequest.delegate = self;
869 objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
870 objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
871 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload
872 message:[NSString stringWithFormat:@"Uploading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
873 totalBytes:[[objectRequest.userInfo valueForKey:@"bytes"] unsignedIntegerValue]
875 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
876 [NSDictionary dictionaryWithObjectsAndKeys:
877 containerName, @"containerName",
878 [objectNames objectAtIndex:i], @"objectName",
879 [contentTypes objectAtIndex:i], @"contentType",
880 [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize",
881 blockHash, @"blockHash",
882 [filePaths objectAtIndex:i], @"filePath",
883 [hashesArrays objectAtIndex:i], @"hashes",
884 [NSNumber numberWithBool:YES], @"refresh",
885 [NSNumber numberWithUnsignedInteger:10], @"iteration",
886 activity, @"activity",
887 [NSNumber numberWithUnsignedInteger:10], @"retries",
889 if (destinationNode.sharingAccount)
890 [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
891 [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
902 - (BOOL)moveNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode {
903 if ((([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class])) ||
904 (([destinationNode class] == [PithosSubdirNode class]) && !destinationNode.pithosObject.subdir && [destinationNode.pithosObject.name hasSuffix:@"/"]))
906 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
907 NSString *objectNamePrefix;
908 if ([destinationNode class] == [PithosSubdirNode class])
909 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
911 objectNamePrefix = [NSString string];
913 for (PithosNode *node in nodes) {
914 if (([node class] == [PithosObjectNode class]) ||
915 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
916 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
917 dispatch_async(queue, ^{
918 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
919 if ([node.pithosObject.name hasSuffix:@"/"])
920 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
921 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name
922 objectName:node.pithosObject.name
923 destinationContainerName:containerName
924 destinationObjectName:destinationObjectName
927 objectRequest.delegate = self;
928 objectRequest.didFinishSelector = @selector(moveFinished:);
929 objectRequest.didFailSelector = @selector(moveFailed:);
930 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
931 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
932 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
933 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
934 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
935 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
936 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
937 [NSDictionary dictionaryWithObjectsAndKeys:
938 [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes",
939 activity, @"activity",
940 [NSNumber numberWithUnsignedInteger:10], @"retries",
942 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
945 } else if ([node class] == [PithosSubdirNode class]) {
946 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
947 dispatch_async(queue, ^{
948 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
949 if (node.pithosObject.subdir)
950 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
951 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
952 objectName:node.pithosObject.name
953 destinationContainerName:containerName
954 destinationObjectName:destinationObjectName
956 if (objectRequests) {
957 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
958 objectRequest.delegate = self;
959 objectRequest.didFinishSelector = @selector(moveFinished:);
960 objectRequest.didFailSelector = @selector(moveFailed:);
961 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
962 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
963 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
964 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
965 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
966 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
967 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
968 [NSDictionary dictionaryWithObjectsAndKeys:
969 [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes",
970 [NSNumber numberWithBool:YES], @"refresh",
971 activity, @"activity",
972 [NSNumber numberWithUnsignedInteger:10], @"retries",
974 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
983 - (BOOL)copyNodes:(NSArray *)nodes toNode:(PithosNode *)destinationNode {
984 if ((([destinationNode class] != [PithosSubdirNode class]) && ([destinationNode class] != [PithosContainerNode class])) ||
985 (([destinationNode class] == [PithosSubdirNode class]) && !destinationNode.pithosObject.subdir && [destinationNode.pithosObject.name hasSuffix:@"/"]))
987 NSString *containerName = [NSString stringWithString:destinationNode.pithosContainer.name];
988 NSString *objectNamePrefix;
989 if ([destinationNode class] == [PithosSubdirNode class])
990 objectNamePrefix = [NSString stringWithString:destinationNode.pithosObject.name];
992 objectNamePrefix = [NSString string];
994 for (PithosNode *node in nodes) {
995 if (([node class] == [PithosObjectNode class]) ||
996 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
997 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
998 dispatch_async(queue, ^{
999 NSString *destinationObjectName;
1000 if (![destinationNode isEqualTo:node.parent]) {
1001 destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
1002 if ([node.pithosObject.name hasSuffix:@"/"])
1003 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
1005 destinationObjectName = [PithosUtilities safeObjectNameForContainerName:containerName
1006 objectName:node.pithosObject.name];
1008 ASIPithosObjectRequest *objectRequest = [PithosUtilities copyObjectRequestWithContainerName:node.pithosContainer.name
1009 objectName:node.pithosObject.name
1010 destinationContainerName:containerName
1011 destinationObjectName:destinationObjectName
1013 sharingAccount:node.sharingAccount];
1014 if (objectRequest) {
1015 objectRequest.delegate = self;
1016 objectRequest.didFinishSelector = @selector(copyFinished:);
1017 objectRequest.didFailSelector = @selector(copyFailed:);
1018 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy
1019 message:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'",
1020 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1021 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1022 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1023 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1024 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1025 [NSDictionary dictionaryWithObjectsAndKeys:
1026 [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes",
1027 activity, @"activity",
1028 [NSNumber numberWithUnsignedInteger:10], @"retries",
1030 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1033 } else if ([node class] == [PithosSubdirNode class]) {
1034 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1035 dispatch_async(queue, ^{
1036 NSString *destinationObjectName;
1037 if (![destinationNode isEqualTo:node.parent]) {
1038 destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
1039 if (node.pithosObject.subdir)
1040 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
1042 destinationObjectName = [PithosUtilities safeSubdirNameForContainerName:containerName
1043 subdirName:node.pithosObject.name];
1045 NSArray *objectRequests = [PithosUtilities copyObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
1046 objectName:node.pithosObject.name
1047 destinationContainerName:containerName
1048 destinationObjectName:destinationObjectName
1050 sharingAccount:node.sharingAccount];
1051 if (objectRequests) {
1052 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1053 objectRequest.delegate = self;
1054 objectRequest.didFinishSelector = @selector(copyFinished:);
1055 objectRequest.didFailSelector = @selector(copyFailed:);
1056 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy
1057 message:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'",
1058 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1059 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1060 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1061 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1062 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1063 [NSDictionary dictionaryWithObjectsAndKeys:
1064 [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes",
1065 activity, @"activity",
1066 [NSNumber numberWithUnsignedInteger:10], @"retries",
1068 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1078 #pragma mark ASIHTTPRequestDelegate
1080 - (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1081 NSLog(@"Download finished: %@", objectRequest.url);
1082 if (objectRequest.responseStatusCode == 200) {
1083 NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"];
1084 PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
1085 NSUInteger totalBytes = activity.totalBytes;
1086 NSUInteger currentBytes = activity.currentBytes;
1088 // XXX change contentLength to objectContentLength if it is fixed in the server
1089 if (([objectRequest contentLength] == 0) && (![[objectRequest contentType] isEqualToString:@"application/directory"])) {
1090 NSLog(@"Downloaded 0 bytes");
1091 NSFileManager *defaultManager = [NSFileManager defaultManager];
1092 if (![defaultManager fileExistsAtPath:filePath]) {
1093 if (![defaultManager createFileAtPath:filePath contents:nil attributes:nil]) {
1094 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1095 [alert setMessageText:@"Create File Error"];
1096 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create zero length file at %@", filePath]];
1097 [alert addButtonWithTitle:@"OK"];
1103 currentBytes = [objectRequest objectContentLength];
1104 if (currentBytes == 0)
1105 currentBytes = totalBytes;
1106 [activityFacility endActivity:activity
1107 withMessage:[NSString stringWithFormat:@"Downloading '%@' (100%%)",
1108 [objectRequest.userInfo objectForKey:@"fileName"]]
1109 totalBytes:totalBytes
1110 currentBytes:currentBytes];
1112 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1114 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1115 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1116 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1118 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1119 withMessage:[NSString stringWithFormat:@"Downloading '%@' (failed)",
1120 [objectRequest.userInfo objectForKey:@"fileName"]]];
1121 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1126 - (void)downloadObjectFailed:(ASIPithosObjectRequest *)objectRequest {
1127 NSLog(@"Download failed: %@", objectRequest.url);
1128 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1130 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1131 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1132 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1134 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1135 withMessage:[NSString stringWithFormat:@"Downloading '%@' (failed)",
1136 [objectRequest.userInfo objectForKey:@"fileName"]]];
1137 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1141 - (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1142 NSLog(@"Upload directory object finished: %@", objectRequest.url);
1143 if (objectRequest.responseStatusCode == 201) {
1144 NSLog(@"Directory object created: %@", objectRequest.url);
1145 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1146 withMessage:[NSString stringWithFormat:@"Creating directory '%@' (finished)",
1147 [objectRequest.userInfo objectForKey:@"fileName"]]];
1148 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1149 [node forceRefresh];
1151 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1154 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1155 [self forceRefresh:self];
1156 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1157 [self refresh:self];
1159 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1161 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1162 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1163 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1165 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1166 withMessage:[NSString stringWithFormat:@"Creating directory '%@' (failed)",
1167 [objectRequest.userInfo objectForKey:@"fileName"]]];
1168 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1173 - (void)uploadDirectoryObjectFailed:(ASIPithosObjectRequest *)objectRequest {
1174 NSLog(@"Upload directory object failed: %@", objectRequest.url);
1175 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1177 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1178 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1179 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1181 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1182 withMessage:[NSString stringWithFormat:@"Creating directory '%@' (failed)",
1183 [objectRequest.userInfo objectForKey:@"fileName"]]];
1184 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1188 - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
1189 NSLog(@"Upload using hashmap finished: %@", objectRequest.url);
1190 NSString *fileName = [objectRequest.userInfo objectForKey:@"fileName"];
1191 PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
1192 NSUInteger totalBytes = activity.totalBytes;
1193 NSUInteger currentBytes = activity.currentBytes;
1194 if (objectRequest.responseStatusCode == 201) {
1195 NSLog(@"Object created: %@", objectRequest.url);
1196 [activityFacility endActivity:activity
1197 withMessage:[NSString stringWithFormat:@"Uploading '%@' (100%%)", fileName]
1198 totalBytes:totalBytes
1199 currentBytes:totalBytes];
1200 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1201 [node forceRefresh];
1203 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1206 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1207 [self forceRefresh:self];
1208 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1209 [self refresh:self];
1210 } else if (objectRequest.responseStatusCode == 409) {
1211 NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue];
1212 if (iteration == 0) {
1213 NSLog(@"Upload iteration limit reached: %@", objectRequest.url);
1214 [activityFacility endActivity:activity
1215 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]];
1216 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1217 [alert setMessageText:@"Upload Timeout"];
1218 [alert setInformativeText:[NSString stringWithFormat:@"Upload iteration limit reached for object with path '%@'",
1219 [objectRequest.userInfo objectForKey:@"objectName"]]];
1220 [alert addButtonWithTitle:@"OK"];
1224 NSLog(@"object is missing hashes: %@", objectRequest.url);
1225 NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForHashes:[objectRequest.userInfo objectForKey:@"hashes"]
1226 withMissingHashesResponse:[objectRequest responseString]];
1227 NSUInteger blockSize = [[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
1228 if (totalBytes >= [missingBlocks count]*blockSize)
1229 currentBytes = totalBytes - [missingBlocks count]*blockSize;
1230 [activityFacility updateActivity:activity
1231 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(currentBytes + 0.0)/(totalBytes + 0.0))]
1232 totalBytes:totalBytes
1233 currentBytes:currentBytes];
1234 NSUInteger missingBlockIndex = [missingBlocks firstIndex];
1235 __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithContainerName:[objectRequest.userInfo objectForKey:@"containerName"]
1237 forFile:[objectRequest.userInfo objectForKey:@"filePath"]
1238 missingBlockIndex:missingBlockIndex
1239 sharingAccount:[objectRequest.userInfo objectForKey:@"sharingAccount"]];
1240 newContainerRequest.delegate = self;
1241 newContainerRequest.didFinishSelector = @selector(uploadMissingBlockFinished:);
1242 newContainerRequest.didFailSelector = @selector(uploadMissingBlockFailed:);
1243 newContainerRequest.userInfo = objectRequest.userInfo;
1244 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:(--iteration)] forKey:@"iteration"];
1245 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1246 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"];
1247 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
1248 [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){
1249 [activityFacility updateActivity:activity
1250 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
1251 totalBytes:activity.totalBytes
1252 currentBytes:(activity.currentBytes + size)];
1254 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1256 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1258 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1259 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1260 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1262 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1263 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]];
1264 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1269 - (void)uploadObjectUsingHashMapFailed:(ASIPithosObjectRequest *)objectRequest {
1270 NSLog(@"Upload using hashmap failed: %@", objectRequest.url);
1271 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1273 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1274 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1275 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1277 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1278 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)",
1279 [objectRequest.userInfo objectForKey:@"fileName"]]];
1280 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1284 - (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest {
1285 NSLog(@"Upload of missing block finished: %@", containerRequest.url);
1286 NSUInteger blockSize = [[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
1287 NSString *fileName = [containerRequest.userInfo objectForKey:@"fileName"];
1288 PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"];
1289 NSUInteger totalBytes = activity.totalBytes;
1290 NSUInteger currentBytes = activity.currentBytes + blockSize;
1291 if (currentBytes > totalBytes)
1292 currentBytes = totalBytes;
1293 if (containerRequest.responseStatusCode == 202) {
1294 [activityFacility updateActivity:activity
1295 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(currentBytes + 0.0)/(totalBytes + 0.0))]
1296 totalBytes:totalBytes
1297 currentBytes:currentBytes];
1298 NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"];
1299 NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue];
1300 missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex];
1301 if (missingBlockIndex == NSNotFound) {
1302 NSArray *hashes = [containerRequest.userInfo objectForKey:@"hashes"];
1303 ASIPithosObjectRequest *newObjectRequest = [PithosUtilities writeObjectDataRequestWithContainerName:[containerRequest.userInfo objectForKey:@"containerName"]
1304 objectName:[containerRequest.userInfo objectForKey:@"objectName"]
1305 contentType:[containerRequest.userInfo objectForKey:@"contentType"]
1307 blockHash:[containerRequest.userInfo objectForKey:@"blockHash"]
1308 forFile:[containerRequest.userInfo objectForKey:@"filePath"]
1311 sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
1312 newObjectRequest.delegate = self;
1313 newObjectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
1314 newObjectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
1315 newObjectRequest.userInfo = containerRequest.userInfo;
1316 [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1317 [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"];
1318 [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"];
1319 [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
1321 __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithContainerName:[containerRequest.userInfo objectForKey:@"containerName"]
1322 blockSize:[[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]
1323 forFile:[containerRequest.userInfo objectForKey:@"filePath"]
1324 missingBlockIndex:missingBlockIndex
1325 sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
1326 newContainerRequest.delegate = self;
1327 newContainerRequest.didFinishSelector = @selector(uploadMissingBlockFinished:);
1328 newContainerRequest.didFailSelector = @selector(uploadMissingBlockFailed:);
1329 newContainerRequest.userInfo = containerRequest.userInfo;
1330 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1331 [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
1332 [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){
1333 [activityFacility updateActivity:activity
1334 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))]
1335 totalBytes:activity.totalBytes
1336 currentBytes:(activity.currentBytes + size)];
1338 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1341 NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1343 ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest];
1344 [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1345 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1347 [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"]
1348 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]];
1349 [PithosUtilities unexpectedResponseStatusAlertWithRequest:containerRequest];
1354 - (void)uploadMissingBlockFailed:(ASIPithosContainerRequest *)containerRequest {
1355 NSLog(@"Upload of missing block failed: %@", containerRequest.url);
1356 NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1358 ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest];
1359 [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1360 [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
1362 [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"]
1363 withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)",
1364 [containerRequest.userInfo objectForKey:@"fileName"]]];
1365 [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
1369 - (void)moveFinished:(ASIPithosObjectRequest *)objectRequest {
1370 NSLog(@"Move object finished: %@", objectRequest.url);
1371 if (objectRequest.responseStatusCode == 201) {
1372 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1373 withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' (finished)",
1374 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1375 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1376 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1377 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1378 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1379 [node forceRefresh];
1381 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1384 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1385 [self forceRefresh:self];
1386 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1387 [self refresh:self];
1389 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1391 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1392 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1393 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1395 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1396 withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' (failed)",
1397 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1398 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1399 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1400 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1401 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1406 - (void)moveFailed:(ASIPithosObjectRequest *)objectRequest {
1407 NSLog(@"Move object failed: %@", objectRequest.url);
1408 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1410 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1411 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1412 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1414 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1415 withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' (failed)",
1416 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1417 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1418 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1419 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1420 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1424 - (void)copyFinished:(ASIPithosObjectRequest *)objectRequest {
1425 NSLog(@"Copy object finished: %@", objectRequest.url);
1426 if (objectRequest.responseStatusCode == 201) {
1427 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1428 withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' (finished)",
1429 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1430 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1431 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1432 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1433 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1434 [node forceRefresh];
1436 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1439 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1440 [self forceRefresh:self];
1441 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1442 [self refresh:self];
1444 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1446 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1447 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1448 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1450 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1451 withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' (failed)",
1452 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1453 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1454 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1455 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1456 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1461 - (void)copyFailed:(ASIPithosObjectRequest *)objectRequest {
1462 NSLog(@"Copy object failed: %@", objectRequest.url);
1463 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1465 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1466 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1467 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1469 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1470 withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' (failed)",
1471 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1472 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1473 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1474 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1475 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1479 - (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1480 NSLog(@"Delete object finished: %@", objectRequest.url);
1481 if (objectRequest.responseStatusCode == 204) {
1482 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1483 withMessage:[NSString stringWithFormat:@"Deleting '%@' (finished)",
1484 [objectRequest.userInfo objectForKey:@"fileName"]]];
1485 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
1486 [node forceRefresh];
1488 for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) {
1491 if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue])
1492 [self forceRefresh:self];
1493 else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
1494 [self refresh:self];
1496 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1498 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1499 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1500 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1502 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1503 withMessage:[NSString stringWithFormat:@"Deleting '%@' (failed)",
1504 [objectRequest.userInfo objectForKey:@"fileName"]]];
1505 [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
1510 - (void)deleteObjectFailed:(ASIPithosObjectRequest *)objectRequest {
1511 NSLog(@"Delete object failed: %@", objectRequest.url);
1512 NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
1514 ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:objectRequest];
1515 [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
1516 [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1518 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
1519 withMessage:[NSString stringWithFormat:@"Deleting '%@' (failed)",
1520 [objectRequest.userInfo objectForKey:@"fileName"]]];
1521 [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
1526 #pragma mark NSSplitViewDelegate
1528 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
1529 if (splitView == verticalSplitView)
1532 return ([horizontalSplitView bounds].size.height - 108);
1535 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
1536 if (splitView == verticalSplitView)
1539 return ([horizontalSplitView bounds].size.height - 108);
1542 - (CGFloat)splitView:(NSSplitView *)splitView constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)dividerIndex {
1543 if (splitView == verticalSplitView) {
1544 if (proposedPosition < 120)
1546 else if (proposedPosition > 220)
1549 return proposedPosition;
1551 return ([horizontalSplitView bounds].size.height - 108);
1556 #pragma mark NSOutlineViewDataSource
1558 - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
1561 if (item == containersNode)
1562 return containersNodeChildren.count;
1563 if (item == sharedNode)
1568 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
1570 return (!index ? containersNode : sharedNode);
1571 if (item == sharedNode)
1572 return (!index ? mySharedNode : othersSharedNode);
1573 return [containersNodeChildren objectAtIndex:index];
1576 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
1577 if ((item == containersNode) || (item == sharedNode))
1582 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
1583 PithosNode *node = (PithosNode *)item;
1587 #pragma mark Drag and Drop destination
1589 - (NSDragOperation)outlineView:(NSOutlineView *)anOutlineView
1590 validateDrop:(id<NSDraggingInfo>)info
1591 proposedItem:(id)item
1592 proposedChildIndex:(NSInteger)index {
1593 NSDragOperation result = NSDragOperationNone;
1594 if ((item == nil) || (index != NSOutlineViewDropOnItemIndex))
1596 PithosNode *dropNode = (PithosNode *)item;
1597 if ([dropNode class] != [PithosContainerNode class])
1599 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
1600 result = NSDragOperationCopy;
1601 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
1602 if ([info draggingSourceOperationMask] & NSDragOperationMove) {
1603 // NSDragOperationCopy | NSDragOperationMove -> NSDragOperationMove
1604 if (![dropNode isEqualTo:draggedParentNode])
1605 result = NSDragOperationMove;
1606 } else if ([info draggingSourceOperationMask] & NSDragOperationCopy) {
1607 // NSDragOperationCopy (Option modifier) -> NSDragOperationCopy
1608 result = NSDragOperationCopy;
1614 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id<NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index {
1615 if ([[[info draggingPasteboard] types] containsObject:NSFilenamesPboardType]) {
1616 NSArray *filenames = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
1617 NSLog(@"drag in operation: %lu filenames: %@", [info draggingSourceOperationMask], filenames);
1618 if (item && (index == NSOutlineViewDropOnItemIndex) && (filenames != nil)) {
1619 PithosNode *node = (PithosNode *)item;
1620 NSLog(@"drag in node: %@", node.url);
1621 return [self uploadFiles:filenames toNode:node];
1623 } else if ([[[info draggingPasteboard] types] containsObject:NSFilesPromisePboardType]) {
1624 NSLog(@"drag local operation: %lu nodes: %@", [info draggingSourceOperationMask], draggedNodes);
1625 if (item && (index == NSOutlineViewDropOnItemIndex) && (draggedNodes != nil)) {
1626 PithosNode *node = (PithosNode *)item;
1627 NSLog(@"drag local node: %@", node.url);
1628 if ([info draggingSourceOperationMask] & NSDragOperationMove)
1629 return [self moveNodes:draggedNodes toNode:node];
1630 else if ([info draggingSourceOperationMask] & NSDragOperationCopy)
1631 return [self copyNodes:draggedNodes toNode:node];
1638 #pragma mark NSOutlineViewDelegate
1640 - (BOOL)outlineView:outlineView shouldSelectItem:(id)item {
1641 if ((item == containersNode) || (item == sharedNode))
1646 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
1647 if ((item == containersNode) || (item == sharedNode))
1652 - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
1653 PithosNode *node = (PithosNode *)[outlineView itemAtRow:[outlineView selectedRow]];
1656 [browser loadColumnZero];
1662 #pragma mark NSMenuDelegate
1664 - (void)menuNeedsUpdate:(NSMenu *)menu {
1665 [menu removeAllItems];
1666 NSMenuItem *menuItem;
1667 NSString *menuItemTitle;
1668 BOOL nodeContextMenu = NO;
1669 PithosNode *menuNode;
1670 NSMutableArray *menuNodes;
1671 if (menu == browserMenu) {
1672 NSInteger column = [browser clickedColumn];
1673 NSInteger row = [browser clickedRow];
1674 if ((column == -1) || (row == -1)) {
1675 // General context menu
1676 NSArray *menuNodesIndexPaths = [browser selectionIndexPaths];
1677 if ([menuNodesIndexPaths count] == 0) {
1678 menuNode = [browser parentForItemsInColumn:0];
1679 } else if (([menuNodesIndexPaths count] != 1) ||
1680 ([[browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]] class] == [PithosObjectNode class])) {
1681 menuNode = [browser parentForItemsInColumn:([[menuNodesIndexPaths objectAtIndex:0] length] - 1)];
1683 menuNode = [browser itemAtIndexPath:[menuNodesIndexPaths objectAtIndex:0]];
1686 // Node context menu
1687 NSIndexPath *clickedNodeIndexPath = [[browser indexPathForColumn:column] indexPathByAddingIndex:row];
1688 NSArray *menuNodesIndexPaths = [browser selectionIndexPaths];
1689 menuNodes = [NSMutableArray arrayWithCapacity:[menuNodesIndexPaths count]];
1690 if ([menuNodesIndexPaths containsObject:clickedNodeIndexPath]) {
1691 for (NSIndexPath *nodeIndexPath in menuNodesIndexPaths) {
1692 [menuNodes addObject:[browser itemAtIndexPath:nodeIndexPath]];
1695 [menuNodes addObject:[browser itemAtIndexPath:clickedNodeIndexPath]];
1697 nodeContextMenu = YES;
1699 } else if (menu == outlineViewMenu) {
1700 NSInteger row = [outlineView clickedRow];
1702 row = [outlineView selectedRow];
1705 menuNode = [outlineView itemAtRow:row];
1708 if (!nodeContextMenu) {
1709 // General context menu
1710 if (([menuNode class] == [PithosAccountNode class]) ||
1711 ([menuNode class] == [PithosSharingAccountsNode class]) ||
1712 ([menuNode class] == [PithosEmptyNode class]))
1714 BOOL shared = menuNode.shared;
1715 BOOL sharingAccount = (menuNode.sharingAccount != nil);
1717 if (!shared && !sharingAccount) {
1718 menuItem = [[[NSMenuItem alloc] initWithTitle:@"New Folder" action:@selector(menuNewFolder:) keyEquivalent:@""] autorelease];
1719 [menuItem setRepresentedObject:menuNode];
1720 [menu addItem:menuItem];
1721 [menu addItem:[NSMenuItem separatorItem]];
1724 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(menuGetInfo:) keyEquivalent:@""] autorelease];
1725 [menuItem setRepresentedObject:[NSArray arrayWithObject:menuNode]];
1726 [menu addItem:menuItem];
1728 if (!shared && !sharingAccount) {
1729 if (clipboardNodes) {
1730 NSUInteger clipboardNodesCount = [clipboardNodes count];
1731 if (clipboardNodesCount == 0) {
1732 self.clipboardNodes = nil;
1733 } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) {
1734 if (clipboardNodesCount == 1)
1735 menuItemTitle = [NSString stringWithString:@"Paste Item"];
1737 menuItemTitle = [NSString stringWithString:@"Paste Items"];
1738 [menu addItem:[NSMenuItem separatorItem]];
1739 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
1740 [menuItem setRepresentedObject:menuNode];
1741 [menu addItem:menuItem];
1746 // Node context menu
1747 NSUInteger menuNodesCount = [menuNodes count];
1748 PithosNode *firstMenuNode = (PithosNode *)[menuNodes objectAtIndex:0];
1749 BOOL shared = firstMenuNode.shared;
1750 BOOL sharingAccount = (firstMenuNode.sharingAccount != nil);
1751 // Move to Trash (pithos container only)
1753 if (!shared && !sharingAccount) {
1754 if ([rootNode class] == [PithosContainerNode class]) {
1755 if ([rootNode.pithosContainer.name isEqualToString:@"pithos"]) {
1756 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Move to Trash" action:@selector(menuMoveToTrash:) keyEquivalent:@""] autorelease];
1757 [menuItem setRepresentedObject:menuNodes];
1758 [menu addItem:menuItem];
1760 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(menuDelete:) keyEquivalent:@""] autorelease];
1761 [menuItem setRepresentedObject:menuNodes];
1762 [menu addItem:menuItem];
1763 [menu addItem:[NSMenuItem separatorItem]];
1767 if (!sharingAccount || ([firstMenuNode class] != [PithosAccountNode class])) {
1768 menuItem = [[[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(menuGetInfo:) keyEquivalent:@""] autorelease];
1769 [menuItem setRepresentedObject:menuNodes];
1770 [menu addItem:menuItem];
1772 if ((!shared && !sharingAccount) || ([firstMenuNode class] != [PithosContainerNode class]))
1773 [menu addItem:[NSMenuItem separatorItem]];
1776 if (!shared && !sharingAccount) {
1777 if (menuNodesCount == 1)
1778 menuItemTitle = [NSString stringWithFormat:@"Cut \"%@\"", ((PithosNode *)[menuNodes objectAtIndex:0]).displayName];
1780 menuItemTitle = [NSString stringWithFormat:@"Cut %lu Items", menuNodesCount];
1781 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCut:) keyEquivalent:@""] autorelease];
1782 [menuItem setRepresentedObject:menuNodes];
1783 [menu addItem:menuItem];
1786 if ((!shared && !sharingAccount) ||
1787 (([firstMenuNode class] != [PithosContainerNode class]) && ([firstMenuNode class] != [PithosAccountNode class]))) {
1788 if (menuNodesCount == 1)
1789 menuItemTitle = [NSString stringWithFormat:@"Copy \"%@\"", ((PithosNode *)[menuNodes objectAtIndex:0]).displayName];
1791 menuItemTitle = [NSString stringWithFormat:@"Copy %lu Items", menuNodesCount];
1792 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuCopy:) keyEquivalent:@""] autorelease];
1793 [menuItem setRepresentedObject:menuNodes];
1794 [menu addItem:menuItem];
1797 if (!shared && !sharingAccount) {
1798 if (menuNodesCount == 1) {
1799 PithosNode *menuNode = [menuNodes objectAtIndex:0];
1800 if (([menuNode class] == [PithosSubdirNode class]) &&
1801 (menuNode.pithosObject.subdir || ![menuNode.pithosObject.name hasSuffix:@"/"])) {
1802 if (clipboardNodes) {
1803 NSUInteger clipboardNodesCount = [clipboardNodes count];
1804 if (clipboardNodesCount == 0) {
1805 self.clipboardNodes = nil;
1806 } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) {
1807 if (clipboardNodesCount == 1)
1808 menuItemTitle = [NSString stringWithString:@"Paste Item"];
1810 menuItemTitle = [NSString stringWithString:@"Paste Items"];
1811 menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease];
1812 [menuItem setRepresentedObject:menuNode];
1813 [menu addItem:menuItem];
1823 #pragma mark Menu Actions
1825 - (void)menuNewFolder:(NSMenuItem *)sender {
1826 PithosNode *node = (PithosNode *)[sender representedObject];
1827 if ([node class] == [PithosContainerNode class]) {
1828 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1829 dispatch_async(queue, ^{
1830 NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:node.pithosContainer.name
1831 subdirName:@"untitled folder"];
1832 NSString *fileName = [safeObjectName lastPathComponent];
1833 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:node.pithosContainer.name
1834 objectName:safeObjectName
1836 contentType:@"application/directory"
1838 contentDisposition:nil
1841 isPublic:ASIPithosObjectRequestPublicIgnore
1843 data:[NSData data]];
1844 objectRequest.delegate = self;
1845 objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
1846 objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
1847 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory
1848 message:[NSString stringWithFormat:@"Creating directory '%@'", fileName]];
1849 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1850 fileName, @"fileName",
1851 [NSArray arrayWithObject:node], @"refreshNodes",
1852 [NSNumber numberWithBool:YES], @"refresh",
1853 activity, @"activity",
1854 [NSNumber numberWithUnsignedInteger:10], @"retries",
1856 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1858 } else if (([node class] == [PithosSubdirNode class]) &&
1859 (node.pithosObject.subdir || ![node.pithosObject.name hasSuffix:@"/"])) {
1860 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1861 dispatch_async(queue, ^{
1862 NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:node.pithosContainer.name
1863 subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]];
1864 NSString *fileName = [safeObjectName lastPathComponent];
1865 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:node.pithosContainer.name
1866 objectName:safeObjectName
1868 contentType:@"application/directory"
1870 contentDisposition:nil
1873 isPublic:ASIPithosObjectRequestPublicIgnore
1875 data:[NSData data]];
1876 objectRequest.delegate = self;
1877 objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
1878 objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
1879 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory
1880 message:[NSString stringWithFormat:@"Creating directory '%@'", fileName]];
1881 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1882 fileName, @"fileName",
1883 [NSArray arrayWithObject:node], @"refreshNodes",
1884 [NSNumber numberWithBool:YES], @"refresh",
1885 activity, @"activity",
1886 [NSNumber numberWithUnsignedInteger:10], @"retries",
1888 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1893 - (void)menuGetInfo:(NSMenuItem *)sender {
1894 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
1895 [node showPithosNodeInfo:sender];
1899 - (void)menuDelete:(NSMenuItem *)sender {
1900 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
1901 if (([node class] == [PithosObjectNode class]) ||
1902 (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
1903 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:node.pithosContainer.name
1904 objectName:node.pithosObject.name];
1905 objectRequest.delegate = self;
1906 objectRequest.didFinishSelector = @selector(deleteObjectFinished:);
1907 objectRequest.didFailSelector = @selector(deleteObjectFailed:);
1908 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete
1909 message:[NSString stringWithFormat:@"Deleting '%@'",
1910 [objectRequest.userInfo objectForKey:@"fileName"]]];
1911 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1912 [NSDictionary dictionaryWithObjectsAndKeys:
1913 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
1914 activity, @"activity",
1915 [NSNumber numberWithUnsignedInteger:10], @"retries",
1917 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1918 } else if ([node class] == [PithosSubdirNode class]) {
1919 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1920 dispatch_async(queue, ^{
1921 NSArray *objectRequests = [PithosUtilities deleteObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
1922 objectName:node.pithosObject.name];
1923 if (objectRequests) {
1924 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1925 objectRequest.delegate = self;
1926 objectRequest.didFinishSelector = @selector(deleteObjectFinished:);
1927 objectRequest.didFailSelector = @selector(deleteObjectFailed:);
1928 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete
1929 message:[NSString stringWithFormat:@"Deleting '%@'",
1930 [objectRequest.userInfo objectForKey:@"fileName"]]];
1931 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1932 [NSDictionary dictionaryWithObjectsAndKeys:
1933 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
1934 activity, @"activity",
1935 [NSNumber numberWithUnsignedInteger:10], @"retries",
1937 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1945 - (void)menuMoveToTrash:(NSMenuItem *)sender {
1946 for (PithosNode *node in ((NSArray *)[sender representedObject])) {
1947 if (([node class] == [PithosObjectNode class]) ||
1948 (([node class] == [PithosSubdirNode class]) &&
1949 !node.pithosObject.subdir &&
1950 [node.pithosObject.name hasSuffix:@"/"])) {
1951 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1952 dispatch_async(queue, ^{
1953 NSString *safeObjectName = [PithosUtilities safeObjectNameForContainerName:@"trash"
1954 objectName:node.pithosObject.name];
1955 if (safeObjectName) {
1956 ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name
1957 objectName:node.pithosObject.name
1958 destinationContainerName:@"trash"
1959 destinationObjectName:safeObjectName
1961 if (objectRequest) {
1962 objectRequest.delegate = self;
1963 objectRequest.didFinishSelector = @selector(moveFinished:);
1964 objectRequest.didFailSelector = @selector(moveFailed:);
1965 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
1966 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
1967 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
1968 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
1969 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
1970 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
1971 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
1972 [NSDictionary dictionaryWithObjectsAndKeys:
1973 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
1974 activity, @"activity",
1975 [NSNumber numberWithUnsignedInteger:10], @"retries",
1977 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
1981 } else if ([node class] == [PithosSubdirNode class]) {
1982 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1983 dispatch_async(queue, ^{
1984 NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:@"trash"
1985 subdirName:node.pithosObject.name];
1986 if (safeObjectName) {
1987 NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithContainerName:node.pithosContainer.name
1988 objectName:node.pithosObject.name
1989 destinationContainerName:@"trash"
1990 destinationObjectName:safeObjectName
1992 if (objectRequests) {
1993 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1994 objectRequest.delegate = self;
1995 objectRequest.didFinishSelector = @selector(moveFinished:);
1996 objectRequest.didFailSelector = @selector(moveFailed:);
1997 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove
1998 message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'",
1999 [objectRequest.userInfo objectForKey:@"sourceContainerName"],
2000 [objectRequest.userInfo objectForKey:@"sourceObjectName"],
2001 [objectRequest.userInfo objectForKey:@"destinationContainerName"],
2002 [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
2003 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
2004 [NSDictionary dictionaryWithObjectsAndKeys:
2005 [NSArray arrayWithObject:node.parent], @"forceRefreshNodes",
2006 activity, @"activity",
2007 [NSNumber numberWithUnsignedInteger:10], @"retries",
2009 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
2018 - (void)menuCut:(NSMenuItem *)sender {
2019 self.clipboardNodes = (NSArray *)[sender representedObject];
2020 self.clipboardParentNode = ((PithosNode *)[clipboardNodes objectAtIndex:0]).parent;
2021 self.clipboardCopy = NO;
2024 - (void)menuCopy:(NSMenuItem *)sender {
2025 self.clipboardNodes = (NSArray *)[sender representedObject];
2026 self.clipboardParentNode = ((PithosNode *)[clipboardNodes objectAtIndex:0]).parent;
2027 self.clipboardCopy = YES;
2030 - (void)menuPaste:(NSMenuItem *)sender {
2031 if (!clipboardNodes || ![clipboardNodes count])
2033 PithosNode *dropNode = (PithosNode *)[sender representedObject];
2034 NSArray *localClipboardNodes = [NSArray arrayWithArray:clipboardNodes];
2035 if (!clipboardCopy && ![dropNode isEqualTo:clipboardParentNode]) {
2036 self.clipboardNodes = nil;
2037 self.clipboardParentNode = nil;
2038 [self moveNodes:localClipboardNodes toNode:dropNode];
2040 [self copyNodes:localClipboardNodes toNode:dropNode];
2045 #pragma mark PithosActivityFacilityDelegate
2047 - (void)activityUpdate:(NSDictionary *)info {
2048 NSString *message = [info objectForKey:@"message"];
2049 NSUInteger runningActivitiesCount = [[info objectForKey:@"runningActivitiesCount"] unsignedIntegerValue];
2050 // NSUInteger endingActivitiesCount = [[info objectForKey:@"endingActivitiesCount"] unsignedIntegerValue];
2051 NSUInteger totalUploadBytes = [[info objectForKey:@"totalUploadBytes"] unsignedIntegerValue];
2052 NSUInteger currentUploadBytes = [[info objectForKey:@"currentUploadBytes"] unsignedIntegerValue];
2053 NSUInteger totalDownloadBytes = [[info objectForKey:@"totalDownloadBytes"] unsignedIntegerValue];
2054 NSUInteger currentDownloadBytes = [[info objectForKey:@"currentDownloadBytes"] unsignedIntegerValue];
2055 NSUInteger totalBytes = totalUploadBytes + totalDownloadBytes;
2056 if (runningActivitiesCount && totalBytes) {
2057 [activityProgressIndicator setDoubleValue:((currentUploadBytes + currentDownloadBytes + 0.0)/(totalBytes + 0.0))];
2058 [activityProgressIndicator startAnimation:self];
2060 [activityProgressIndicator setDoubleValue:1.0];
2061 [activityProgressIndicator stopAnimation:self];
2065 message = [NSString stringWithFormat:@"%@ used",
2066 [[[[BytesSizeTransformer alloc] init] autorelease] transformedValue:
2067 [NSNumber numberWithUnsignedInteger:accountNode.pithosAccount.bytesUsed]]];
2068 [activityTextField setStringValue:message];