2 Copyright (C) 2004-2005 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
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
37 #import "WOContext+UIxMailer.h"
39 @interface UIxMailView : UIxComponent
44 - (BOOL)isDeletableClientObject;
48 @implementation UIxMailView
50 static NSString *mailETag = nil;
53 return [super version] + 0 /* v2 */;
57 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
59 NSAssert2([super version] == 2,
60 @"invalid superclass (%@) version %i !",
61 NSStringFromClass([self superclass]), [super version]);
63 if ([ud boolForKey:@"SOGoDontUseETagsForMailViewer"]) {
64 NSLog(@"Note: usage of constant etag for mailer viewer is disabled.");
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);
81 - (void) setCurrentAddress: (id) _addr
83 currentAddress = _addr;
88 return currentAddress;
91 - (NSString *) objectTitle
93 return [[self clientObject] subject];
96 - (NSString *) panelTitle
98 return [NSString stringWithFormat: @"%@: %@",
99 [self labelForKey: @"View Mail"],
103 /* links (DUP to UIxMailPartViewer!) */
105 - (NSString *) linkToEnvelopeAddress: (NGImap4EnvelopeAddress *) _address
107 // TODO: make some web-link, eg open a new compose panel?
108 return [NSString stringWithFormat: @"mailto: %@", [_address baseEMail]];
111 - (NSString *) currentAddressLink
113 return [self linkToEnvelopeAddress:[self currentAddress]];
120 return [[self clientObject] fetchCoreInfos];
125 return [[[self clientObject] ccEnvelopeAddresses] count] > 0 ? YES : NO;
130 - (id) contentViewerComponent
132 // TODO: I would prefer to flatten the body structure prior rendering,
133 // using some delegate to decide which parts to select for alternative.
136 info = [[self clientObject] bodyStructure];
138 return [[context mailRenderingContext] viewerForBodyInfo:info];
145 /* check etag to see whether we really must rerender */
146 if (mailETag != nil ) {
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.
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];
160 if (![[self clientObject] doesMailExist]) {
161 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
162 reason:@"message got deleted"];
165 [[context response] setStatus:304 /* Not Modified */];
166 return [context response];
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!"];
180 - (BOOL) isDeletableClientObject
182 return [[self clientObject] respondsToSelector: @selector (delete)];
185 - (BOOL) isInlineViewer
192 return [[self clientObject] isInDraftsFolder];
195 - (id) redirectToParentFolder
199 url = [[[self clientObject] container] baseURLInContext: context];
201 return [self redirectToLocation: url];
208 if (![self isDeletableClientObject]) {
209 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
210 reason:@"method cannot be invoked on "
211 @"the specified object"];
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!"];
219 if ((ex = [[self clientObject] delete]) != nil) {
222 url = [[ex reason] stringByEscapingURL];
223 url = [@"view?error=" stringByAppendingString:url];
224 return [self redirectToLocation:url];
228 if (![self isInlineViewer]) {
229 // if everything is ok, close the window (send a JS closing the Window)
232 page = [self pageWithName:@"UIxMailWindowCloser"];
233 [page takeValue:@"YES" forKey:@"refreshOpener"];
237 return [self redirectToParentFolder];
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!"];
249 if ((ex = [[self clientObject] trashInContext:context]) != nil) {
252 if ([[[context request] formValueForKey:@"jsonly"] boolValue])
253 /* called using XMLHttpRequest */
256 url = [[ex reason] stringByEscapingURL];
257 url = [@"view?error=" stringByAppendingString:url];
258 return [self redirectToLocation:url];
261 if ([[[context request] formValueForKey:@"jsonly"] boolValue]) {
262 /* called using XMLHttpRequest */
263 [[context response] setStatus:200 /* OK */];
264 return [context response];
267 if (![self isInlineViewer]) {
268 // if everything is ok, close the window (send a JS closing the Window)
271 page = [self pageWithName:@"UIxMailWindowCloser"];
272 [page takeValue:@"YES" forKey:@"refreshOpener"];
276 return [self redirectToParentFolder];
279 - (id <WOActionResults>) moveAction
281 id <WOActionResults> result;
282 NSString *destinationFolder;
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!"];
290 destinationFolder = [self queryParameterForKey: @"tofolder"];
291 if ([destinationFolder length] > 0)
293 result = [[self clientObject] moveToFolderNamed: destinationFolder
297 if (![[[context request] formValueForKey:@"jsonly"] boolValue])
299 url = [NSString stringWithFormat: @"view?error=%@",
300 [[result reason] stringByEscapingURL]];
301 result = [self redirectToLocation: url];
306 result = [context response];
307 [result setStatus: 200];
311 result = [NSException exceptionWithHTTPStatus:500 /* Server Error */
312 reason: @"No destination folder given"];
317 /* generating response */
319 - (void) appendToResponse: (WOResponse *) _response
320 inContext: (WOContext *) _ctx
322 UIxMailRenderingContext *mctx;
325 [[_ctx response] setHeader:mailETag forKey:@"etag"];
327 mctx = [[UIxMailRenderingContext alloc] initWithViewer: self
330 [_ctx pushMailRenderingContext: mctx];
333 [super appendToResponse: _response inContext: _ctx];
335 [[_ctx popMailRenderingContext] reset];
338 @end /* UIxMailView */