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