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 {
106 // TODO: make some web-link, eg open a new compose panel?
107 return [@"mailto:" stringByAppendingString:[_address baseEMail]];
110 - (NSString *)currentAddressLink {
111 return [self linkToEnvelopeAddress:[self currentAddress]];
117 return [[self clientObject] fetchCoreInfos];
121 return [[[self clientObject] ccEnvelopeAddresses] count] > 0 ? YES : NO;
126 - (id)contentViewerComponent {
127 // TODO: I would prefer to flatten the body structure prior rendering,
128 // using some delegate to decide which parts to select for alternative.
131 info = [[self clientObject] bodyStructure];
132 return [[context mailRenderingContext] viewerForBodyInfo:info];
139 /* check etag to see whether we really must rerender */
140 if (mailETag != nil ) {
142 Note: There is one thing which *can* change for an existing message,
143 those are the IMAP4 flags (and annotations, which we do not use).
144 Since we don't render the flags, it should be OK, if this changes
145 we must embed the flagging into the etag.
149 if ((s = [[context request] headerForKey:@"if-none-match"])) {
150 if ([s rangeOfString:mailETag].length > 0) { /* not perfectly correct */
151 /* client already has the proper entity */
152 // [self logWithFormat:@"MATCH: %@ (tag %@)", s, mailETag];
154 if (![[self clientObject] doesMailExist]) {
155 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
156 reason:@"message got deleted"];
159 [[context response] setStatus:304 /* Not Modified */];
160 return [context response];
165 if ([self message] == nil) {
166 // TODO: redirect to proper error
167 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
168 reason:@"did not find specified message!"];
174 - (BOOL)isDeletableClientObject {
175 return [[self clientObject] respondsToSelector:@selector(delete)];
177 - (BOOL)isInlineViewer {
181 - (id)redirectToParentFolder {
184 url = [[[self clientObject] container] baseURLInContext:context];
185 return [self redirectToLocation:url];
191 if (![self isDeletableClientObject]) {
192 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
193 reason:@"method cannot be invoked on "
194 @"the specified object"];
197 if ([self isInvokedBySafeMethod]) {
198 // TODO: fix UI to use POST for unsafe actions
199 [self logWithFormat:@"WARNING: method is invoked using safe HTTP method!"];
202 if ((ex = [[self clientObject] delete]) != nil) {
205 url = [[ex reason] stringByEscapingURL];
206 url = [@"view?error=" stringByAppendingString:url];
207 return [self redirectToLocation:url];
211 if (![self isInlineViewer]) {
212 // if everything is ok, close the window (send a JS closing the Window)
215 page = [self pageWithName:@"UIxMailWindowCloser"];
216 [page takeValue:@"YES" forKey:@"refreshOpener"];
220 return [self redirectToParentFolder];
226 if ([self isInvokedBySafeMethod]) {
227 // TODO: fix UI to use POST for unsafe actions
228 [self logWithFormat:@"WARNING: method is invoked using safe HTTP method!"];
231 if ((ex = [[self clientObject] trashInContext:context]) != nil) {
234 if ([[[context request] formValueForKey:@"jsonly"] boolValue])
235 /* called using XMLHttpRequest */
238 url = [[ex reason] stringByEscapingURL];
239 url = [@"view?error=" stringByAppendingString:url];
240 return [self redirectToLocation:url];
243 if ([[[context request] formValueForKey:@"jsonly"] boolValue]) {
244 /* called using XMLHttpRequest */
245 [[context response] setStatus:200 /* OK */];
246 return [context response];
249 if (![self isInlineViewer]) {
250 // if everything is ok, close the window (send a JS closing the Window)
253 page = [self pageWithName:@"UIxMailWindowCloser"];
254 [page takeValue:@"YES" forKey:@"refreshOpener"];
258 return [self redirectToParentFolder];
261 - (id <WOActionResults>) moveAction
263 id <WOActionResults> result;
264 NSString *destinationFolder;
267 if ([self isInvokedBySafeMethod]) {
268 // TODO: fix UI to use POST for unsafe actions
269 [self logWithFormat:@"WARNING: method is invoked using safe HTTP method!"];
272 destinationFolder = [self queryParameterForKey: @"tofolder"];
273 if ([destinationFolder length] > 0)
275 result = [[self clientObject] moveToFolderNamed: destinationFolder
279 if (![[[context request] formValueForKey:@"jsonly"] boolValue])
281 url = [NSString stringWithFormat: @"view?error=%@",
282 [[result reason] stringByEscapingURL]];
283 result = [self redirectToLocation: url];
288 result = [context response];
289 [result setStatus: 200];
293 result = [NSException exceptionWithHTTPStatus:500 /* Server Error */
294 reason: @"No destination folder given"];
299 /* generating response */
301 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
302 UIxMailRenderingContext *mctx;
305 [[_ctx response] setHeader:mailETag forKey:@"etag"];
307 mctx = [[NSClassFromString(@"UIxMailRenderingContext")
308 alloc] initWithViewer:self context:_ctx];
309 [_ctx pushMailRenderingContext:mctx];
312 [super appendToResponse:_response inContext:_ctx];
314 [[_ctx popMailRenderingContext] reset];
317 @end /* UIxMailView */