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