Make info windows modular. Add support for versions pane in info window. Allow downlo...
[pithos-macos] / pithos-macos / PithosObjectNode.m
1 //
2 //  PithosObjectNode.m
3 //  pithos-macos
4 //
5 // Copyright 2011-2012 GRNET S.A. All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
9 // conditions are met:
10 // 
11 //   1. Redistributions of source code must retain the above
12 //      copyright notice, this list of conditions and the following
13 //      disclaimer.
14 // 
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.
19 // 
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.
32 // 
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.
37
38 #import "PithosObjectNode.h"
39 #import "ASIPithosRequest.h"
40 #import "ASIPithos.h"
41 #import "ASIPithosObjectRequest.h"
42 #import "ASIPithosContainer.h"
43 #import "ASIPithosObject.h"
44 #import "ASIDownloadCache.h"
45 #import "PithosUtilities.h"
46 #import "PithosObjectNodeInfoController.h"
47
48 @implementation PithosObjectNode
49 @synthesize pithos, pithosContainer, pithosObject, versions;
50 @synthesize isPublic;
51
52 #pragma mark -
53 #pragma mark Object Lifecycle
54
55 - (id)initWithPithos:(ASIPithos *)aPithos 
56      pithosContainer:(ASIPithosContainer *)aPithosContainer 
57         pithosObject:(ASIPithosObject *)aPithosObject {
58     if ((self = [super init])) {
59         self.pithos = aPithos;
60         self.pithosContainer = aPithosContainer;
61         self.pithosObject = aPithosObject;
62         isLeafItem = YES;
63     }
64     return self;
65 }
66
67 - (void)dealloc {
68     [refreshVersionsObjectRequest clearDelegatesAndCancel];
69     [refreshVersionsObjectRequest release];
70     [refreshMetadataObjectRequest clearDelegatesAndCancel];
71     [refreshMetadataObjectRequest release];
72     [applyMetadataObjectRequest clearDelegatesAndCancel];
73     [applyMetadataObjectRequest release];
74     [versions release];
75     [pithosObject release];
76     [pithosContainer release];
77     [pithos release];
78     [super dealloc];
79 }
80
81 #pragma mark -
82 #pragma mark Properties
83
84 - (void)setPithos:(ASIPithos *)aPithos {
85     if (aPithos && ![aPithos isEqualTo:pithos]) {
86         [pithos release];
87         pithos = [aPithos retain];
88         [url release];
89         url = nil;
90     }
91 }
92
93 - (NSString *)url {
94     if (url == nil)
95         url = [[NSString alloc] initWithFormat:@"object %@/%@/%@%@", 
96                (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL), 
97                pithosContainer.name, 
98                pithosObject.name, 
99                (shared ? @"?shared" : @"")];
100     return url;
101 }
102
103 - (NSArray *)children {
104     return nil;
105 }
106
107 - (NSString *)displayName {
108     if (displayName == nil) {
109         displayName = [pithosObject.name lastPathComponent];
110         if([pithosObject.name hasSuffix:@"/"])
111             displayName = [displayName stringByAppendingString:@"/"];
112         [displayName retain];
113     }
114     return [[displayName copy] autorelease];
115 }
116
117 - (void)setDisplayName:(NSString *)aDisplayName {    
118 }
119
120 - (NSImage *)icon {
121     if (icon == nil)
122         icon = [[[NSWorkspace sharedWorkspace] iconForFileType:[pithosObject.name pathExtension]] retain];
123     return icon;
124 }
125
126 - (void)setPithosObject:(ASIPithosObject *)aPithosObject {
127     if (![pithosObject isEqualTo:aPithosObject]) {
128         [pithosObject release];
129         pithosObject = [aPithosObject retain];
130     }
131     self.isPublic = (pithosObject.publicURI != nil);
132     // Refresh browser if the object is in my shared and is no longer shared
133     if (shared && !pithosObject.sharing)
134         [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosBrowserRefreshNeeeded" object:self];
135 }
136
137 - (void)setLimitedPithosObject:(ASIPithosObject *)aPithosObject {
138     if (![pithosObject isEqualTo:aPithosObject]) {
139         self.pithosObject.subdir = aPithosObject.subdir;
140         self.pithosObject.name = aPithosObject.name;
141         self.pithosObject.hash = aPithosObject.hash;
142         self.pithosObject.objectHash = aPithosObject.objectHash;
143         self.pithosObject.UUID = aPithosObject.UUID;
144         self.pithosObject.bytes = aPithosObject.bytes;
145         self.pithosObject.contentType = aPithosObject.contentType;
146         self.pithosObject.lastModified = aPithosObject.lastModified;
147         self.pithosObject.version = aPithosObject.version;
148         self.pithosObject.versionTimestamp = aPithosObject.versionTimestamp;
149         self.pithosObject.modifiedBy = aPithosObject.modifiedBy;
150         self.pithosObject.sharedBy = aPithosObject.sharedBy;
151         self.pithosObject.allowedTo = aPithosObject.allowedTo;
152         if (!pithosNodeInfoController) {
153             self.pithosObject.sharing = aPithosObject.sharing;
154             self.pithosObject.publicURI = aPithosObject.publicURI;
155             self.pithosObject = pithosObject;
156         }
157     }
158 }
159
160 #pragma mark -
161 #pragma mark ASIHTTPRequestDelegate
162
163 - (void)objectRequestFinished:(ASIPithosObjectRequest *)request {
164     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
165     NSLog(@"URL: %@", [request url]);
166     NSLog(@"cached: %d", [request didUseCachedResponse]);
167     
168     if ([request isEqualTo:applyMetadataObjectRequest]) {
169         int responseStatusCode = applyMetadataObjectRequest.responseStatusCode;
170         if (responseStatusCode != 202)
171             [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest];
172         @synchronized(self) {
173             [applyMetadataObjectRequest release];
174             applyMetadataObjectRequest = nil;
175         }
176         if (responseStatusCode == 202)
177             [self refreshInfo];
178     } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
179         [[pithosNodeInfoController window] makeFirstResponder:nil];
180         self.pithosObject = [refreshMetadataObjectRequest object];
181         @synchronized(self) {
182             [refreshMetadataObjectRequest release];
183             refreshMetadataObjectRequest = nil;
184         }
185     } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
186         [[pithosNodeInfoController window] makeFirstResponder:nil];
187         self.versions = [refreshVersionsObjectRequest versions];
188         @synchronized(self) {
189             [refreshVersionsObjectRequest release];
190             refreshVersionsObjectRequest = nil;
191         }
192     }
193     [pool drain];
194 }
195
196 - (void)objectRequestFailed:(ASIPithosObjectRequest *)request {
197     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
198     NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
199     if (retries > 0) {
200         ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:request] autorelease];
201         [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
202         if ([request isEqualTo:applyMetadataObjectRequest]) {
203             @synchronized(self) {
204                 [applyMetadataObjectRequest release];
205                 applyMetadataObjectRequest = newRequest;
206             }
207         } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
208             @synchronized(self) {
209                 [refreshMetadataObjectRequest release];
210                 refreshMetadataObjectRequest = newRequest;
211             }
212         } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
213             @synchronized(self) {
214                 [refreshVersionsObjectRequest release];
215                 refreshVersionsObjectRequest = newRequest;
216             }
217         }
218         [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
219     } else {
220         if ([request isEqualTo:applyMetadataObjectRequest]) {
221             dispatch_async(dispatch_get_main_queue(), ^{
222                 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest];
223             });
224             @synchronized(self) {
225                 [applyMetadataObjectRequest release];
226                 applyMetadataObjectRequest = nil;
227             }
228         } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
229             dispatch_async(dispatch_get_main_queue(), ^{
230                 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest];
231             });
232             @synchronized(self) {
233                 [refreshMetadataObjectRequest release];
234                 refreshMetadataObjectRequest = nil;
235             }
236         } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
237             dispatch_async(dispatch_get_main_queue(), ^{
238                 [PithosUtilities httpRequestErrorAlertWithRequest:refreshVersionsObjectRequest];
239             });
240             @synchronized(self) {
241                 [refreshVersionsObjectRequest release];
242                 refreshVersionsObjectRequest = nil;
243             }
244         }
245     }
246     [pool drain];
247 }
248
249 #pragma mark -
250 #pragma mark Info
251
252 - (void)applyInfo {
253     @synchronized(self) {
254         if (applyMetadataObjectRequest == nil) {
255             [[pithosNodeInfoController window] makeFirstResponder:nil];
256             if (sharingAccount) {
257                 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos 
258                                                                                               containerName:pithosContainer.name 
259                                                                                                  objectName:pithosObject.name 
260                                                                                             contentEncoding:nil 
261                                                                                          contentDisposition:nil 
262                                                                                                    manifest:nil 
263                                                                                                     sharing:nil
264                                                                                                    isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
265                                                                                                    metadata:pithosObject.metadata
266                                                                                                      update:NO] retain];
267                 [applyMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
268             } else {
269                 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos 
270                                                                                               containerName:pithosContainer.name 
271                                                                                                  objectName:pithosObject.name 
272                                                                                             contentEncoding:pithosObject.contentEncoding 
273                                                                                          contentDisposition:pithosObject.contentDisposition 
274                                                                                                    manifest:pithosObject.manifest 
275                                                                                                     sharing:(pithosObject.sharing ? pithosObject.sharing : @"")
276                                                                                                    isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
277                                                                                                    metadata:pithosObject.metadata
278                                                                                                      update:NO] retain];
279             }
280             applyMetadataObjectRequest.delegate = self;
281             applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
282             applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
283             applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
284                                                    [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
285                                                    [NSNumber numberWithUnsignedInteger:10], @"retries", 
286                                                    NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
287                                                    NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
288                                                    nil];
289             [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
290         }
291     }
292 }
293
294 - (void)refreshInfo {
295     @synchronized(self) {
296         if (refreshMetadataObjectRequest == nil) {
297             refreshMetadataObjectRequest = [[ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos 
298                                                                                       containerName:pithosContainer.name 
299                                                                                          objectName:pithosObject.name] retain];
300             if (sharingAccount)
301                 [refreshMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
302             refreshMetadataObjectRequest.delegate = self;
303             refreshMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
304             refreshMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
305             refreshMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
306                                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
307                                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
308                                                      NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
309                                                      NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
310                                                      nil];
311             refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
312             [[PithosUtilities prepareRequest:refreshMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
313         }
314     }
315     [self refreshVersions];
316 }
317
318 #pragma mark -
319 #pragma mark Versions
320
321 - (void)refreshVersions {
322     @synchronized(self) {
323         if (refreshVersionsObjectRequest == nil) {
324             refreshVersionsObjectRequest = [[ASIPithosObjectRequest objectVersionsRequestWithPithos:pithos 
325                                                                                       containerName:pithosContainer.name 
326                                                                                          objectName:pithosObject.name] retain];
327             if (sharingAccount)
328                 [refreshVersionsObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
329             refreshVersionsObjectRequest.delegate = self;
330             refreshVersionsObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
331             refreshVersionsObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
332             refreshVersionsObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
333                                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
334                                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
335                                                      NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
336                                                      NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
337                                                      nil];
338             refreshVersionsObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
339             [[PithosUtilities prepareRequest:refreshVersionsObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
340         }
341     }
342 }
343
344 #pragma mark -
345 #pragma mark Actions
346
347 - (void)showPithosNodeInfo:(id)sender {
348     if (!pithosNodeInfoController) {
349         pithosNodeInfoController = [[PithosObjectNodeInfoController alloc] initWithPithosNode:self];
350         [self refreshInfo];
351     }
352     [pithosNodeInfoController showWindow:sender];
353     [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
354     [NSApp activateIgnoringOtherApps:YES];
355 }
356
357 @end