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 /* expunge / delete setup and permissions */
102 - (BOOL) isTrashingAllowed
106 trash = [[[self clientObject] mailAccountFolder]
107 trashFolderInContext:context];
108 if ([trash isKindOfClass:[NSException class]])
111 return [trash isWriteAllowed];
114 - (BOOL) showMarkDeletedButton
116 // TODO: we might also want to add a default to always show delete
117 if (![[self clientObject] isDeletionAllowed])
120 return [self isTrashingAllowed] ? NO : YES;
123 - (BOOL) showTrashButton
125 if (![[self clientObject] isDeletionAllowed])
128 return [self isTrashingAllowed];
131 /* links (DUP to UIxMailPartViewer!) */
133 - (NSString *)linkToEnvelopeAddress:(NGImap4EnvelopeAddress *)_address {
134 // TODO: make some web-link, eg open a new compose panel?
135 return [@"mailto:" stringByAppendingString:[_address baseEMail]];
138 - (NSString *)currentAddressLink {
139 return [self linkToEnvelopeAddress:[self currentAddress]];
145 return [[self clientObject] fetchCoreInfos];
149 return [[[self clientObject] ccEnvelopeAddresses] count] > 0 ? YES : NO;
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.
159 info = [[self clientObject] bodyStructure];
160 return [[context mailRenderingContext] viewerForBodyInfo:info];
165 - (id)defaultAction {
166 /* check etag to see whether we really must rerender */
167 if (mailETag != nil ) {
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.
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];
181 if (![[self clientObject] doesMailExist]) {
182 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
183 reason:@"message got deleted"];
186 [[context response] setStatus:304 /* Not Modified */];
187 return [context response];
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!"];
201 - (BOOL)isDeletableClientObject {
202 return [[self clientObject] respondsToSelector:@selector(delete)];
204 - (BOOL)isInlineViewer {
208 - (id)redirectToParentFolder {
211 url = [[[self clientObject] container] baseURLInContext:context];
212 return [self redirectToLocation:url];
218 if (![self isDeletableClientObject]) {
219 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
220 reason:@"method cannot be invoked on "
221 @"the specified object"];
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!"];
229 if ((ex = [[self clientObject] delete]) != nil) {
232 url = [[ex reason] stringByEscapingURL];
233 url = [@"view?error=" stringByAppendingString:url];
234 return [self redirectToLocation:url];
238 if (![self isInlineViewer]) {
239 // if everything is ok, close the window (send a JS closing the Window)
242 page = [self pageWithName:@"UIxMailWindowCloser"];
243 [page takeValue:@"YES" forKey:@"refreshOpener"];
247 return [self redirectToParentFolder];
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!"];
258 if ((ex = [[self clientObject] trashInContext:context]) != nil) {
261 if ([[[context request] formValueForKey:@"jsonly"] boolValue])
262 /* called using XMLHttpRequest */
265 url = [[ex reason] stringByEscapingURL];
266 url = [@"view?error=" stringByAppendingString:url];
267 return [self redirectToLocation:url];
270 if ([[[context request] formValueForKey:@"jsonly"] boolValue]) {
271 /* called using XMLHttpRequest */
272 [[context response] setStatus:200 /* OK */];
273 return [context response];
276 if (![self isInlineViewer]) {
277 // if everything is ok, close the window (send a JS closing the Window)
280 page = [self pageWithName:@"UIxMailWindowCloser"];
281 [page takeValue:@"YES" forKey:@"refreshOpener"];
285 return [self redirectToParentFolder];
288 - (id <WOActionResults>) moveAction
290 id <WOActionResults> result;
291 NSString *destinationFolder;
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!"];
299 destinationFolder = [self queryParameterForKey: @"tofolder"];
300 if ([destinationFolder length] > 0)
302 result = [[self clientObject] moveToFolderNamed: destinationFolder
306 if (![[[context request] formValueForKey:@"jsonly"] boolValue])
308 url = [NSString stringWithFormat: @"view?error=%@",
309 [[result reason] stringByEscapingURL]];
310 result = [self redirectToLocation: url];
315 result = [context response];
316 [result setStatus: 200];
320 result = [NSException exceptionWithHTTPStatus:500 /* Server Error */
321 reason: @"No destination folder given"];
326 /* generating response */
328 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
329 UIxMailRenderingContext *mctx;
332 [[_ctx response] setHeader:mailETag forKey:@"etag"];
334 mctx = [[NSClassFromString(@"UIxMailRenderingContext")
335 alloc] initWithViewer:self context:_ctx];
336 [_ctx pushMailRenderingContext:mctx];
339 [super appendToResponse:_response inContext:_ctx];
341 [[_ctx popMailRenderingContext] reset];
344 @end /* UIxMailView */