]> err.no Git - scalable-opengroupware.org/blob - UI/MailerUI/UIxMailView.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1147 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / MailerUI / UIxMailView.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #import <Foundation/NSException.h>
23 #import <Foundation/NSUserDefaults.h>
24 #import <NGObjWeb/NSException+HTTP.h>
25 #import <NGObjWeb/WORequest.h>
26 #import <NGObjWeb/WOResponse.h>
27 #import <NGExtensions/NSException+misc.h>
28 #import <NGExtensions/NSString+misc.h>
29 #import <NGImap4/NGImap4Envelope.h>
30 #import <NGImap4/NGImap4EnvelopeAddress.h>
31 #import <SoObjects/Mailer/SOGoMailObject.h>
32 #import <SoObjects/Mailer/SOGoMailAccount.h>
33 #import <SoObjects/Mailer/SOGoMailFolder.h>
34 #import <SOGoUI/UIxComponent.h>
35 #import <MailPartViewers/UIxMailRenderingContext.h> // cyclic
36
37 #import "WOContext+UIxMailer.h"
38
39 @interface UIxMailView : UIxComponent
40 {
41   id currentAddress;
42 }
43
44 - (BOOL)isDeletableClientObject;
45
46 @end
47
48 @implementation UIxMailView
49
50 static NSString *mailETag = nil;
51
52 + (int)version {
53   return [super version] + 0 /* v2 */;
54 }
55
56 + (void)initialize {
57   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
58   
59   NSAssert2([super version] == 2,
60             @"invalid superclass (%@) version %i !",
61             NSStringFromClass([self superclass]), [super version]);
62   
63   if ([ud boolForKey:@"SOGoDontUseETagsForMailViewer"]) {
64     NSLog(@"Note: usage of constant etag for mailer viewer is disabled.");
65   }
66   else {
67     mailETag = [[NSString alloc] initWithFormat:@"\"imap4url_%d_%d_%03d\"",
68                                  UIX_MAILER_MAJOR_VERSION,
69                                  UIX_MAILER_MINOR_VERSION,
70                                  UIX_MAILER_SUBMINOR_VERSION];
71     NSLog(@"Note: using constant etag for mail viewer: '%@'", mailETag);
72   }
73 }
74
75 - (void)dealloc {
76   [super dealloc];
77 }
78
79 /* accessors */
80
81 - (void) setCurrentAddress: (id) _addr
82 {
83   currentAddress = _addr;
84 }
85
86 - (id) currentAddress
87 {
88   return currentAddress;
89 }
90
91 - (NSString *) objectTitle
92 {
93   return [[self clientObject] subject];
94 }
95
96 - (NSString *) panelTitle
97 {
98   return [NSString stringWithFormat: @"%@: %@",
99                    [self labelForKey: @"View Mail"],
100                    [self objectTitle]];
101 }
102
103 /* links (DUP to UIxMailPartViewer!) */
104
105 - (NSString *) linkToEnvelopeAddress: (NGImap4EnvelopeAddress *) _address
106 {
107   // TODO: make some web-link, eg open a new compose panel?
108   return [NSString stringWithFormat: @"mailto: %@", [_address baseEMail]];
109 }
110
111 - (NSString *) currentAddressLink
112 {
113   return [self linkToEnvelopeAddress:[self currentAddress]];
114 }
115
116 /* fetching */
117
118 - (id) message
119 {
120   return [[self clientObject] fetchCoreInfos];
121 }
122
123 - (BOOL) hasCC
124 {
125   return [[[self clientObject] ccEnvelopeAddresses] count] > 0 ? YES : NO;
126 }
127
128 /* viewers */
129
130 - (id) contentViewerComponent
131 {
132   // TODO: I would prefer to flatten the body structure prior rendering,
133   //       using some delegate to decide which parts to select for alternative.
134   id info;
135   
136   info = [[self clientObject] bodyStructure];
137
138   return [[context mailRenderingContext] viewerForBodyInfo:info];
139 }
140
141 /* actions */
142
143 - (id) defaultAction
144 {
145   /* check etag to see whether we really must rerender */
146   if (mailETag != nil ) {
147     /*
148       Note: There is one thing which *can* change for an existing message,
149             those are the IMAP4 flags (and annotations, which we do not use).
150             Since we don't render the flags, it should be OK, if this changes
151             we must embed the flagging into the etag.
152     */
153     NSString *s;
154     
155     if ((s = [[context request] headerForKey:@"if-none-match"])) {
156       if ([s rangeOfString:mailETag].length > 0) { /* not perfectly correct */
157         /* client already has the proper entity */
158         // [self logWithFormat:@"MATCH: %@ (tag %@)", s, mailETag];
159         
160         if (![[self clientObject] doesMailExist]) {
161           return [NSException exceptionWithHTTPStatus:404 /* Not Found */
162                               reason:@"message got deleted"];
163         }
164         
165         [[context response] setStatus:304 /* Not Modified */];
166         return [context response];
167       }
168     }
169   }
170   
171   if ([self message] == nil) {
172     // TODO: redirect to proper error
173     return [NSException exceptionWithHTTPStatus:404 /* Not Found */
174                         reason:@"did not find specified message!"];
175   }
176   
177   return self;
178 }
179
180 - (BOOL) isDeletableClientObject
181 {
182   return [[self clientObject] respondsToSelector: @selector (delete)];
183 }
184
185 - (BOOL) isInlineViewer
186 {
187   return NO;
188 }
189
190 - (BOOL) mailIsDraft
191 {
192   return [[self clientObject] isInDraftsFolder];
193 }
194
195 - (id) redirectToParentFolder
196 {
197   id url;
198   
199   url = [[[self clientObject] container] baseURLInContext: context];
200
201   return [self redirectToLocation: url];
202 }
203
204 - (id) deleteAction
205 {
206   NSException *ex;
207   
208   if (![self isDeletableClientObject]) {
209     return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
210                         reason:@"method cannot be invoked on "
211                                @"the specified object"];
212   }
213   
214   if ([self isInvokedBySafeMethod]) {
215     // TODO: fix UI to use POST for unsafe actions
216     [self logWithFormat:@"WARNING: method is invoked using safe HTTP method!"];
217   }
218   
219   if ((ex = [[self clientObject] delete]) != nil) {
220     id url;
221     
222     url = [[ex reason] stringByEscapingURL];
223     url = [@"view?error=" stringByAppendingString:url];
224     return [self redirectToLocation:url];
225     //return ex;
226   }
227   
228   if (![self isInlineViewer]) {
229     // if everything is ok, close the window (send a JS closing the Window)
230     id page;
231     
232     page = [self pageWithName:@"UIxMailWindowCloser"];
233     [page takeValue:@"YES" forKey:@"refreshOpener"];
234     return page;
235   }
236   
237   return [self redirectToParentFolder];
238 }
239
240 - (id) trashAction
241 {
242   NSException *ex;
243   
244   if ([self isInvokedBySafeMethod]) {
245     // TODO: fix UI to use POST for unsafe actions
246     [self logWithFormat:@"WARNING: method is invoked using safe HTTP method!"];
247   }
248   
249   if ((ex = [[self clientObject] trashInContext:context]) != nil) {
250     id url;
251     
252     if ([[[context request] formValueForKey:@"jsonly"] boolValue])
253       /* called using XMLHttpRequest */
254       return ex;
255     
256     url = [[ex reason] stringByEscapingURL];
257     url = [@"view?error=" stringByAppendingString:url];
258     return [self redirectToLocation:url];
259   }
260
261   if ([[[context request] formValueForKey:@"jsonly"] boolValue]) {
262     /* called using XMLHttpRequest */
263     [[context response] setStatus:200 /* OK */];
264     return [context response];
265   }
266   
267   if (![self isInlineViewer]) {
268     // if everything is ok, close the window (send a JS closing the Window)
269     id page;
270     
271     page = [self pageWithName:@"UIxMailWindowCloser"];
272     [page takeValue:@"YES" forKey:@"refreshOpener"];
273     return page;
274   }
275   
276   return [self redirectToParentFolder];
277 }
278
279 - (id <WOActionResults>) moveAction
280 {
281   id <WOActionResults> result;
282   NSString *destinationFolder;
283   id url;
284
285   if ([self isInvokedBySafeMethod]) {
286     // TODO: fix UI to use POST for unsafe actions
287     [self logWithFormat:@"WARNING: method is invoked using safe HTTP method!"];
288   }
289
290   destinationFolder = [self queryParameterForKey: @"tofolder"];
291   if ([destinationFolder length] > 0)
292     {
293       result = [[self clientObject] moveToFolderNamed: destinationFolder
294                                     inContext: context];
295       if (result)
296         {
297           if (![[[context request] formValueForKey:@"jsonly"] boolValue])
298             {
299               url = [NSString stringWithFormat: @"view?error=%@",
300                               [[result reason] stringByEscapingURL]];
301               result = [self redirectToLocation: url];
302             }
303         }
304       else
305         {
306           result = [context response];
307           [result setStatus: 200];
308         }
309     }
310   else
311     result = [NSException exceptionWithHTTPStatus:500 /* Server Error */
312                           reason: @"No destination folder given"];
313
314   return result;
315 }
316
317 /* generating response */
318
319 - (void) appendToResponse: (WOResponse *) _response
320                 inContext: (WOContext *) _ctx
321 {
322   UIxMailRenderingContext *mctx;
323
324   if (mailETag != nil)
325     [[_ctx response] setHeader:mailETag forKey:@"etag"];
326
327   mctx = [[UIxMailRenderingContext alloc] initWithViewer: self
328                                           context: _ctx];
329
330   [_ctx pushMailRenderingContext: mctx];
331   [mctx release];
332
333   [super appendToResponse: _response inContext: _ctx];
334   
335   [[_ctx popMailRenderingContext] reset];
336 }
337
338 @end /* UIxMailView */