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 #include <SOGoUI/UIxComponent.h>
24 @interface UIxMailView : UIxComponent
29 - (BOOL)isDeletableClientObject;
33 #include <UI/MailPartViewers/UIxMailRenderingContext.h> // cyclic
34 #include "WOContext+UIxMailer.h"
35 #include <SoObjects/Mailer/SOGoMailObject.h>
36 #include <SoObjects/Mailer/SOGoMailAccount.h>
37 #include <SoObjects/Mailer/SOGoMailFolder.h>
38 #include <NGImap4/NGImap4Envelope.h>
39 #include <NGImap4/NGImap4EnvelopeAddress.h>
42 @implementation UIxMailView
44 static NSString *mailETag = nil;
47 return [super version] + 0 /* v2 */;
51 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
53 NSAssert2([super version] == 2,
54 @"invalid superclass (%@) version %i !",
55 NSStringFromClass([self superclass]), [super version]);
57 if ([ud boolForKey:@"SOGoDontUseETagsForMailViewer"]) {
58 NSLog(@"Note: usage of constant etag for mailer viewer is disabled.");
61 mailETag = [[NSString alloc] initWithFormat:@"\"imap4url_%d_%d_%03d\"",
62 UIX_MAILER_MAJOR_VERSION,
63 UIX_MAILER_MINOR_VERSION,
64 UIX_MAILER_SUBMINOR_VERSION];
65 NSLog(@"Note: using constant etag for mail viewer: '%@'", mailETag);
70 [self->currentAddress release];
77 [self->currentAddress release]; self->currentAddress = nil;
83 - (void)setCurrentAddress:(id)_addr {
84 ASSIGN(self->currentAddress, _addr);
86 - (id)currentAddress {
87 return self->currentAddress;
90 - (NSString *)objectTitle {
91 return [[self clientObject] subject];
93 - (NSString *)panelTitle {
96 s = [self labelForKey:@"View Mail"];
97 s = [s stringByAppendingString:@": "];
98 s = [s stringByAppendingString:[self objectTitle]];
102 /* expunge / delete setup and permissions */
104 - (BOOL)isTrashingAllowed {
107 trash = [[[self clientObject] mailAccountFolder]
108 trashFolderInContext:[self context]];
109 if ([trash isKindOfClass:[NSException class]])
112 return [trash isWriteAllowed];
115 - (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 {
124 if (![[self clientObject] isDeletionAllowed])
127 return [self isTrashingAllowed];
130 /* links (DUP to UIxMailPartViewer!) */
132 - (NSString *)linkToEnvelopeAddress:(NGImap4EnvelopeAddress *)_address {
133 // TODO: make some web-link, eg open a new compose panel?
134 return [@"mailto:" stringByAppendingString:[_address baseEMail]];
137 - (NSString *)currentAddressLink {
138 return [self linkToEnvelopeAddress:[self currentAddress]];
144 return [[self clientObject] fetchCoreInfos];
148 return [[[self clientObject] ccEnvelopeAddresses] count] > 0 ? YES : NO;
153 - (id)contentViewerComponent {
154 // TODO: I would prefer to flatten the body structure prior rendering,
155 // using some delegate to decide which parts to select for alternative.
158 info = [[self clientObject] bodyStructure];
159 return [[[self context] mailRenderingContext] viewerForBodyInfo:info];
164 - (id)defaultAction {
165 /* check etag to see whether we really must rerender */
166 if (mailETag != nil ) {
168 Note: There is one thing which *can* change for an existing message,
169 those are the IMAP4 flags (and annotations, which we do not use).
170 Since we don't render the flags, it should be OK, if this changes
171 we must embed the flagging into the etag.
175 if ((s = [[[self context] request] headerForKey:@"if-none-match"])) {
176 if ([s rangeOfString:mailETag].length > 0) { /* not perfectly correct */
177 /* client already has the proper entity */
178 // [self logWithFormat:@"MATCH: %@ (tag %@)", s, mailETag];
180 if (![[self clientObject] doesMailExist]) {
181 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
182 reason:@"message got deleted"];
185 [[[self context] response] setStatus:304 /* Not Modified */];
186 return [[self context] response];
191 if ([self message] == nil) {
192 // TODO: redirect to proper error
193 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
194 reason:@"did not find specified message!"];
200 - (BOOL)isDeletableClientObject {
201 return [[self clientObject] respondsToSelector:@selector(delete)];
203 - (BOOL)isInlineViewer {
207 - (id)redirectToParentFolder {
210 url = [[[self clientObject] container] baseURLInContext:[self context]];
211 return [self redirectToLocation:url];
217 if (![self isDeletableClientObject]) {
218 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
219 reason:@"method cannot be invoked on "
220 @"the specified object"];
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] delete]) != nil) {
231 url = [[ex reason] stringByEscapingURL];
232 url = [@"view?error=" stringByAppendingString:url];
233 return [self redirectToLocation:url];
237 if (![self isInlineViewer]) {
238 // if everything is ok, close the window (send a JS closing the Window)
241 page = [self pageWithName:@"UIxMailWindowCloser"];
242 [page takeValue:@"YES" forKey:@"refreshOpener"];
246 return [self redirectToParentFolder];
252 if ([self isInvokedBySafeMethod]) {
253 // TODO: fix UI to use POST for unsafe actions
254 [self logWithFormat:@"WARNING: method is invoked using safe HTTP method!"];
257 if ((ex = [[self clientObject] trashInContext:[self context]]) != nil) {
260 if ([[[[self context] request] formValueForKey:@"jsonly"] boolValue])
261 /* called using XMLHttpRequest */
264 url = [[ex reason] stringByEscapingURL];
265 url = [@"view?error=" stringByAppendingString:url];
266 return [self redirectToLocation:url];
269 if ([[[[self context] request] formValueForKey:@"jsonly"] boolValue]) {
270 /* called using XMLHttpRequest */
271 [[[self context] response] setStatus:200 /* OK */];
272 return [[self context] response];
275 if (![self isInlineViewer]) {
276 // if everything is ok, close the window (send a JS closing the Window)
279 page = [self pageWithName:@"UIxMailWindowCloser"];
280 [page takeValue:@"YES" forKey:@"refreshOpener"];
284 return [self redirectToParentFolder];
287 - (id)getMailAction {
288 // TODO: we might want to flush the caches?
289 return [self redirectToLocation:@"view"];
292 /* generating response */
294 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
295 UIxMailRenderingContext *mctx;
298 [[_ctx response] setHeader:mailETag forKey:@"etag"];
300 mctx = [[NSClassFromString(@"UIxMailRenderingContext")
301 alloc] initWithViewer:self context:_ctx];
302 [_ctx pushMailRenderingContext:mctx];
305 [super appendToResponse:_response inContext:_ctx];
307 [[_ctx popMailRenderingContext] reset];
310 @end /* UIxMailView */