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 <NGExtensions/NSException+misc.h>
25 #include <SOGoUI/UIxComponent.h>
27 @interface UIxMailView : UIxComponent
32 - (BOOL)isDeletableClientObject;
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>
45 @implementation UIxMailView
47 static NSString *mailETag = nil;
50 return [super version] + 0 /* v2 */;
54 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
56 NSAssert2([super version] == 2,
57 @"invalid superclass (%@) version %i !",
58 NSStringFromClass([self superclass]), [super version]);
60 if ([ud boolForKey:@"SOGoDontUseETagsForMailViewer"]) {
61 NSLog(@"Note: usage of constant etag for mailer viewer is disabled.");
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);
78 - (void) setCurrentAddress: (id) _addr
80 currentAddress = _addr;
85 return currentAddress;
88 - (NSString *) objectTitle
90 return [[self clientObject] subject];
93 - (NSString *) panelTitle
95 return [NSString stringWithFormat: @"%@: %@",
96 [self labelForKey: @"View Mail"],
100 /* links (DUP to UIxMailPartViewer!) */
102 - (NSString *)linkToEnvelopeAddress:(NGImap4EnvelopeAddress *)_address {
103 // TODO: make some web-link, eg open a new compose panel?
104 return [@"mailto:" stringByAppendingString:[_address baseEMail]];
107 - (NSString *)currentAddressLink {
108 return [self linkToEnvelopeAddress:[self currentAddress]];
114 return [[self clientObject] fetchCoreInfos];
118 return [[[self clientObject] ccEnvelopeAddresses] count] > 0 ? YES : NO;
123 - (id)contentViewerComponent {
124 // TODO: I would prefer to flatten the body structure prior rendering,
125 // using some delegate to decide which parts to select for alternative.
128 info = [[self clientObject] bodyStructure];
129 return [[context mailRenderingContext] viewerForBodyInfo:info];
136 /* check etag to see whether we really must rerender */
137 if (mailETag != nil ) {
139 Note: There is one thing which *can* change for an existing message,
140 those are the IMAP4 flags (and annotations, which we do not use).
141 Since we don't render the flags, it should be OK, if this changes
142 we must embed the flagging into the etag.
146 if ((s = [[context request] headerForKey:@"if-none-match"])) {
147 if ([s rangeOfString:mailETag].length > 0) { /* not perfectly correct */
148 /* client already has the proper entity */
149 // [self logWithFormat:@"MATCH: %@ (tag %@)", s, mailETag];
151 if (![[self clientObject] doesMailExist]) {
152 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
153 reason:@"message got deleted"];
156 [[context response] setStatus:304 /* Not Modified */];
157 return [context response];
162 if ([self message] == nil) {
163 // TODO: redirect to proper error
164 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
165 reason:@"did not find specified message!"];
171 - (BOOL)isDeletableClientObject {
172 return [[self clientObject] respondsToSelector:@selector(delete)];
174 - (BOOL)isInlineViewer {
178 - (id)redirectToParentFolder {
181 url = [[[self clientObject] container] baseURLInContext:context];
182 return [self redirectToLocation:url];
188 if (![self isDeletableClientObject]) {
189 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
190 reason:@"method cannot be invoked on "
191 @"the specified object"];
194 if ([self isInvokedBySafeMethod]) {
195 // TODO: fix UI to use POST for unsafe actions
196 [self logWithFormat:@"WARNING: method is invoked using safe HTTP method!"];
199 if ((ex = [[self clientObject] delete]) != nil) {
202 url = [[ex reason] stringByEscapingURL];
203 url = [@"view?error=" stringByAppendingString:url];
204 return [self redirectToLocation:url];
208 if (![self isInlineViewer]) {
209 // if everything is ok, close the window (send a JS closing the Window)
212 page = [self pageWithName:@"UIxMailWindowCloser"];
213 [page takeValue:@"YES" forKey:@"refreshOpener"];
217 return [self redirectToParentFolder];
223 if ([self isInvokedBySafeMethod]) {
224 // TODO: fix UI to use POST for unsafe actions
225 [self logWithFormat:@"WARNING: method is invoked using safe HTTP method!"];
228 if ((ex = [[self clientObject] trashInContext:context]) != nil) {
231 if ([[[context request] formValueForKey:@"jsonly"] boolValue])
232 /* called using XMLHttpRequest */
235 url = [[ex reason] stringByEscapingURL];
236 url = [@"view?error=" stringByAppendingString:url];
237 return [self redirectToLocation:url];
240 if ([[[context request] formValueForKey:@"jsonly"] boolValue]) {
241 /* called using XMLHttpRequest */
242 [[context response] setStatus:200 /* OK */];
243 return [context response];
246 if (![self isInlineViewer]) {
247 // if everything is ok, close the window (send a JS closing the Window)
250 page = [self pageWithName:@"UIxMailWindowCloser"];
251 [page takeValue:@"YES" forKey:@"refreshOpener"];
255 return [self redirectToParentFolder];
258 - (id <WOActionResults>) moveAction
260 id <WOActionResults> result;
261 NSString *destinationFolder;
264 if ([self isInvokedBySafeMethod]) {
265 // TODO: fix UI to use POST for unsafe actions
266 [self logWithFormat:@"WARNING: method is invoked using safe HTTP method!"];
269 destinationFolder = [self queryParameterForKey: @"tofolder"];
270 if ([destinationFolder length] > 0)
272 result = [[self clientObject] moveToFolderNamed: destinationFolder
276 if (![[[context request] formValueForKey:@"jsonly"] boolValue])
278 url = [NSString stringWithFormat: @"view?error=%@",
279 [[result reason] stringByEscapingURL]];
280 result = [self redirectToLocation: url];
285 result = [context response];
286 [result setStatus: 200];
290 result = [NSException exceptionWithHTTPStatus:500 /* Server Error */
291 reason: @"No destination folder given"];
296 /* generating response */
298 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
299 UIxMailRenderingContext *mctx;
302 [[_ctx response] setHeader:mailETag forKey:@"etag"];
304 mctx = [[NSClassFromString(@"UIxMailRenderingContext")
305 alloc] initWithViewer:self context:_ctx];
306 [_ctx pushMailRenderingContext:mctx];
309 [super appendToResponse:_response inContext:_ctx];
311 [[_ctx popMailRenderingContext] reset];
314 @end /* UIxMailView */