Initial implementation of 'my shared'.
[pithos-macos] / pithos-macos / PithosSubdirNode.m
1 //
2 //  PithosSubdirNode.m
3 //  pithos-macos
4 //
5 // Copyright 2011 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 "PithosSubdirNode.h"
39 #import "ASIPithosRequest.h"
40 #import "ASIPithosObjectRequest.h"
41 #import "ASIPithosContainer.h"
42 #import "ASIPithosObject.h"
43 #import "ASIDownloadCache.h"
44 #import "PithosFileUtilities.h"
45 #import "PithosObjectNodeInfoController.h"
46
47 static NSImage *sharedIcon = nil;
48
49 @implementation PithosSubdirNode
50 @synthesize pithosObject;
51 @synthesize isPublic;
52
53 + (void)initialize {
54         if (self == [PithosSubdirNode class])
55         sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)] retain];
56 }
57
58 #pragma mark -
59 #pragma mark Object Lifecycle
60
61 - (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer pithosObject:(ASIPithosObject *)aPithosObject {
62     if ((self = [super init])) {
63         self.pithosContainer = aPithosContainer;
64         self.pithosObject = aPithosObject;
65         childrenUpdatedNotificationName = @"PithosSubdirNodeChildrenUpdated";
66         refreshParent = NO;
67     }
68     return self;
69 }
70
71 - (void)dealloc {
72     [refreshMetadataObjectRequest clearDelegatesAndCancel];
73     [refreshMetadataObjectRequest release];
74     [applyMetadataObjectRequest clearDelegatesAndCancel];
75     [applyMetadataObjectRequest release];
76     [pithosObject release];
77     [super dealloc];
78 }
79
80 #pragma mark -
81 #pragma mark Properties
82
83 - (NSString *)url {
84     if (url == nil) 
85         url = [[NSString alloc] initWithFormat:@"subdir %@/%@/%@", [ASIPithosRequest storageURL], pithosContainer.name, prefix];
86     return url;
87 }
88
89 - (NSString *)displayName {
90     if (displayName == nil) {
91         displayName = [pithosObject.name lastPathComponent];
92         if (!pithosObject.subdir && [pithosObject.name hasSuffix:@"/"])
93             displayName = [displayName stringByAppendingString:@"/"];
94         [displayName retain];
95     } 
96     return [[displayName copy] autorelease];
97 }
98
99 - (void)setDisplayName:(NSString *)aDisplayName {    
100 }
101
102 - (NSImage *)icon {
103     if (icon == nil)
104         icon = [sharedIcon retain];
105     return icon;
106 }
107
108 - (void)setPithosObject:(ASIPithosObject *)aPithosObject {
109     if (![pithosObject isEqualTo:aPithosObject]) {
110         [pithosObject release];
111         pithosObject = [aPithosObject retain];
112     }
113     if (pithosObject.subdir) {
114         self.prefix = [pithosObject.name substringToIndex:([pithosObject.name length] - 1)];
115     } else {
116         self.prefix = [NSString stringWithString:pithosObject.name];
117     }
118     self.isPublic = (pithosObject.publicURI != nil);
119     // Refresh parent if the object is in my shared and is no longer shared
120     if (shared && !pithosObject.subdir && !pithosObject.sharing)
121         [self.parent refreshToRoot];
122 }
123
124 #pragma mark -
125 #pragma mark ASIHTTPRequestDelegate
126
127 - (void)objectRequestFinished:(ASIPithosObjectRequest *)request {
128     NSLog(@"URL: %@", [request url]);
129     NSLog(@"cached: %d", [request didUseCachedResponse]);
130     
131     if ([request isEqualTo:applyMetadataObjectRequest]) {
132         @synchronized(self) {
133             [applyMetadataObjectRequest release];
134             applyMetadataObjectRequest = nil;
135         }
136         [self refreshInfo];
137     } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
138         [[pithosNodeInfoController window] makeFirstResponder:nil];
139         self.pithosObject = [refreshMetadataObjectRequest object];
140         if (refreshParent) {
141             // Ask the parent for refresh for the case where an object was removed
142             // It is done here so that it doesn't affect the info window refresh
143             [parent invalidateChildren];
144             [parent children];
145             refreshParent = NO;
146         }
147         @synchronized(self) {
148             [refreshMetadataObjectRequest release];
149             refreshMetadataObjectRequest = nil;
150         }
151     }
152 }
153
154 - (void)objectRequestFailed:(ASIPithosObjectRequest *)request {
155     if ([request isEqualTo:applyMetadataObjectRequest]) {
156         [PithosFileUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest];
157         @synchronized(self) {
158             [applyMetadataObjectRequest release];
159             applyMetadataObjectRequest = nil;
160         }
161     } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
162         [PithosFileUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest];
163         @synchronized(self) {
164             [refreshMetadataObjectRequest release];
165             refreshMetadataObjectRequest = nil;
166         }
167     }
168 }
169
170 #pragma mark -
171 #pragma mark Info
172
173 - (void)applyInfo {
174     @synchronized(self) {
175         if (applyMetadataObjectRequest == nil) {
176             if (pithosObject.subdir) {
177                 BOOL createObject = NO;
178                 NSAlert *alert;
179                 ASIPithosObjectRequest *request = [ASIPithosObjectRequest objectMetadataRequestWithContainerName:pithosContainer.name 
180                                                                                                       objectName:prefix];
181                 [request startSynchronous];
182                 if ([request error]) {
183                     alert = [[[NSAlert alloc] init] autorelease];
184                     [alert setMessageText:@"HTTP Request Error"];
185                     [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [request error]]];
186                     [alert addButtonWithTitle:@"OK"];
187                     [alert runModal];
188                     return;
189                 } else if (request.responseStatusCode == 200) {
190                     alert = [[[NSAlert alloc] init] autorelease];
191                     [alert setMessageText:@"Apply changes"];
192                     [alert setInformativeText:[NSString stringWithFormat:@"In order to apply the changes in '%@', the object at the same path must be replaced. Continue?", self.displayName]];
193                     [alert addButtonWithTitle:@"OK"];
194                     [alert addButtonWithTitle:@"Cancel"];
195                     NSInteger choice = [alert runModal];
196                     if (choice == NSAlertFirstButtonReturn) {
197                         request = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:pithosContainer.name 
198                                                                                        objectName:prefix];
199                         [request startSynchronous];
200                         if ([request error]) {
201                             alert = [[[NSAlert alloc] init] autorelease];
202                             [alert setMessageText:@"HTTP Request Error"];
203                             [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [request error]]];
204                             [alert addButtonWithTitle:@"OK"];
205                             [alert runModal];
206                             return;
207                         } else if (request.responseStatusCode != 204) {
208                             [PithosFileUtilities unexpectedResponseStatusAlertWithRequest:request];
209                             return;
210                         }
211                         refreshParent = YES;
212                         createObject = YES;
213                     } else {
214                         return;
215                     }
216                 } else if (request.responseStatusCode == 404) {
217                     createObject = YES;
218                 } else {
219                     [PithosFileUtilities unexpectedResponseStatusAlertWithRequest:request];
220                     return;
221                 }
222                 if (createObject) {
223                     [[pithosNodeInfoController window] makeFirstResponder:nil];
224                     applyMetadataObjectRequest = [[ASIPithosObjectRequest writeObjectDataRequestWithContainerName:pithosContainer.name 
225                                                                                                        objectName:prefix 
226                                                                                                              eTag:nil
227                                                                                                       contentType:@"application/directory"
228                                                                                                   contentEncoding:pithosObject.contentEncoding 
229                                                                                                contentDisposition:pithosObject.contentDisposition 
230                                                                                                          manifest:pithosObject.manifest 
231                                                                                                           sharing:pithosObject.sharing 
232                                                                                                          isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
233                                                                                                          metadata:pithosObject.metadata 
234                                                                                                              data:[NSData data]] retain];
235                     pithosObject.subdir = NO;
236                     applyMetadataObjectRequest.delegate = self;
237                     applyMetadataObjectRequest.didFinishSelector = @selector(objectRequestFinished:);
238                     applyMetadataObjectRequest.didFailSelector = @selector(objectRequestFailed:);
239                     [applyMetadataObjectRequest startAsynchronous];
240                 }
241             } else {
242                 [[pithosNodeInfoController window] makeFirstResponder:nil];
243                 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithContainerName:pithosContainer.name 
244                                                                                                         objectName:pithosObject.name 
245                                                                                                    contentEncoding:pithosObject.contentEncoding
246                                                                                                 contentDisposition:pithosObject.contentDisposition 
247                                                                                                           manifest:pithosObject.manifest 
248                                                                                                            sharing:pithosObject.sharing 
249                                                                                                           isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
250                                                                                                           metadata:pithosObject.metadata
251                                                                                                             update:NO] retain];
252                 applyMetadataObjectRequest.delegate = self;
253                 applyMetadataObjectRequest.didFinishSelector = @selector(objectRequestFinished:);
254                 applyMetadataObjectRequest.didFailSelector = @selector(objectRequestFailed:);
255                 [applyMetadataObjectRequest startAsynchronous];
256             }
257         }
258     }
259 }
260
261 - (void)refreshInfo {
262     @synchronized(self) {
263         if (pithosObject.subdir) {
264             self.pithosObject = [ASIPithosObject subdirWithName:pithosObject.name];
265         } else if (refreshMetadataObjectRequest == nil) {
266             refreshMetadataObjectRequest = [[ASIPithosObjectRequest objectMetadataRequestWithContainerName:pithosContainer.name 
267                                                                                                 objectName:prefix] retain];
268             refreshMetadataObjectRequest.delegate = self;
269             refreshMetadataObjectRequest.didFinishSelector = @selector(objectRequestFinished:);
270             refreshMetadataObjectRequest.didFailSelector = @selector(objectRequestFailed:);
271             refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
272             [refreshMetadataObjectRequest startAsynchronous];
273         }
274     }
275 }
276
277 #pragma mark -
278 #pragma mark Actions
279
280 - (void)showPithosNodeInfo:(id)sender {
281     if (!pithosNodeInfoController)
282         pithosNodeInfoController = [[PithosObjectNodeInfoController alloc] initWithPithosNode:self];
283     [pithosNodeInfoController showWindow:sender];
284     [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
285     [NSApp activateIgnoringOtherApps:YES];
286 }
287
288 @end