]> err.no Git - scalable-opengroupware.org/blob - UI/MailerUI/UIxMailEditor.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1162 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / MailerUI / UIxMailEditor.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #import <Foundation/NSFileManager.h>
23 #import <Foundation/NSKeyValueCoding.h>
24 #import <Foundation/NSString.h>
25 #import <Foundation/NSUserDefaults.h>
26
27 #import <NGObjWeb/NSException+HTTP.h>
28 #import <NGObjWeb/SoSubContext.h>
29 #import <NGObjWeb/WORequest.h>
30 #import <NGObjWeb/WOResponse.h>
31 #import <NGExtensions/NSNull+misc.h>
32 #import <NGExtensions/NSObject+Logs.h>
33 #import <NGExtensions/NSString+misc.h>
34 #import <NGExtensions/NSException+misc.h>
35 #import <NGMail/NGMimeMessage.h>
36 #import <NGMail/NGMimeMessageGenerator.h>
37 #import <NGMime/NGMimeBodyPart.h>
38 #import <NGMime/NGMimeHeaderFields.h>
39 #import <NGMime/NGMimeMultipartBody.h>
40
41 #import <SoObjects/Mailer/SOGoDraftObject.h>
42 #import <SoObjects/Mailer/SOGoMailFolder.h>
43 #import <SoObjects/Mailer/SOGoMailAccount.h>
44 #import <SoObjects/Mailer/SOGoMailAccounts.h>
45 #import <SoObjects/SOGo/SOGoUser.h>
46 #import <SoObjects/SOGo/NSArray+Utilities.h>
47 #import <SoObjects/SOGo/NSDictionary+Utilities.h>
48 #import <SOGoUI/UIxComponent.h>
49
50 /*
51   UIxMailEditor
52   
53   An mail editor component which works on SOGoDraftObject's.
54 */
55
56 @interface UIxMailEditor : UIxComponent
57 {
58   NSArray  *to;
59   NSArray  *cc;
60   NSArray  *bcc;
61   NSString *subject;
62   NSString *text;
63   NSArray *fromEMails;
64   NSString *from;
65   SOGoMailFolder *sentFolder;
66
67   /* these are for the inline attachment list */
68   NSString *attachmentName;
69   NSArray  *attachmentNames;
70 }
71
72 @end
73
74 @implementation UIxMailEditor
75
76 static BOOL showInternetMarker = NO;
77 static BOOL useLocationBasedSentFolder = NO;
78 static NSDictionary *internetMailHeaders = nil;
79 static NSArray *infoKeys = nil;
80
81 + (void) initialize
82 {
83   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
84   
85   infoKeys = [[NSArray alloc] initWithObjects:
86                                 @"subject", @"to", @"cc", @"bcc", 
87                               @"from", @"replyTo", @"inReplyTo",
88                               nil];
89   
90   useLocationBasedSentFolder =
91     [ud boolForKey:@"SOGoUseLocationBasedSentFolder"];
92   
93   /* Internet mail settings */
94   
95   showInternetMarker = [ud boolForKey:@"SOGoShowInternetMarker"];
96   if (!showInternetMarker)
97     NSLog(@"Note: visual Internet marker on mail editor disabled "
98           @"(SOGoShowInternetMarker)");
99   
100   internetMailHeaders = 
101     [[ud dictionaryForKey:@"SOGoInternetMailHeaders"] copy];
102   NSLog (@"Note: specified %d headers for mails send via the Internet.", 
103         [internetMailHeaders count]);
104 }
105
106 - (void) dealloc
107 {
108   [sentFolder release];
109   [fromEMails release];
110   [from release];
111   [text release];
112   [subject release];
113   [to release];
114   [cc release];
115   [bcc release];
116   [attachmentName release];
117   [attachmentNames release];
118   [super dealloc];
119 }
120
121 /* accessors */
122
123 - (void) setFrom: (NSString *) newFrom
124 {
125   ASSIGN (from, newFrom);
126 }
127
128 - (NSString *) from
129 {
130   NSDictionary *identity;
131
132   if (!from)
133     {
134       identity = [[context activeUser] primaryIdentity];
135       from = [identity keysWithFormat: @"%{fullName} <%{email}>"];
136     }
137
138   return from;
139 }
140
141 // - (void) setReplyTo: (NSString *) ignore
142 // {
143 // }
144
145 // - (NSString *) replyTo
146 // {
147 //   /* we are here for future extensibility */
148 //   return @"";
149 // }
150
151 - (void) setSubject: (NSString *) newSubject
152 {
153   ASSIGN (subject, newSubject);
154 }
155
156 - (NSString *) subject
157 {
158   return subject;
159 }
160
161 - (void) setText: (NSString *) newText
162 {
163   ASSIGN (text, newText);
164 }
165
166 - (NSString *) text
167 {
168   return text;
169 }
170
171 - (void) setTo: (NSArray *) newTo
172 {
173   if ([newTo isKindOfClass: [NSNull class]])
174     newTo = nil;
175
176   ASSIGN (to, newTo);
177 }
178
179 - (NSArray *) to
180 {
181   return to;
182 }
183
184 - (void) setCc: (NSArray *) newCc
185 {
186   if ([newCc isKindOfClass: [NSNull class]])
187     newCc = nil;
188
189   ASSIGN (cc, newCc);
190 }
191
192 - (NSArray *) cc
193 {
194   return cc;
195 }
196
197 - (void) setBcc: (NSArray *) newBcc
198 {
199   if ([newBcc isKindOfClass: [NSNull class]])
200     newBcc = nil;
201
202   ASSIGN (bcc, newBcc);
203 }
204
205 - (NSArray *) bcc
206 {
207   return bcc;
208 }
209
210 - (BOOL) hasOneOrMoreRecipients
211 {
212   return (([to count] + [cc count] + [bcc count]) > 0);
213 }
214
215 - (void) setAttachmentName: (NSString *) newAttachmentName
216 {
217   ASSIGN (attachmentName, newAttachmentName);
218 }
219
220 - (NSString *) attachmentName
221 {
222   return attachmentName;
223 }
224
225 /* from addresses */
226
227 - (NSArray *) fromEMails
228 {
229   NSArray *allIdentities;
230
231   if (!fromEMails)
232     { 
233       allIdentities = [[context activeUser] allIdentities];
234       fromEMails = [allIdentities keysWithFormat: @"%{fullName} <%{email}>"];
235       [fromEMails retain];
236     }
237
238   return fromEMails;
239 }
240
241 /* info loading */
242
243 - (void) loadInfo: (NSDictionary *) _info
244 {
245   if (![_info isNotNull]) return;
246   [self debugWithFormat:@"loading info ..."];
247   [self takeValuesFromDictionary:_info];
248 }
249
250 - (NSDictionary *) storeInfo
251 {
252   [self debugWithFormat:@"storing info ..."];
253   return [self valuesForKeys:infoKeys];
254 }
255
256 /* requests */
257
258 - (BOOL) shouldTakeValuesFromRequest: (WORequest *) request
259                            inContext: (WOContext*) localContext
260 {
261   return YES;
262 }
263
264 /* actions */
265
266 - (NSDictionary *) _scanAttachmentFilenamesInRequest: (id) httpBody
267 {
268   NSMutableDictionary *filenames;
269   NSDictionary *attachment;
270   NSArray *parts;
271   unsigned int count, max;
272   NGMimeBodyPart *part;
273   NGMimeContentDispositionHeaderField *header;
274   NSString *mimeType;
275
276   parts = [httpBody parts];
277   max = [parts count];
278   filenames = [NSMutableDictionary dictionaryWithCapacity: max];
279
280   for (count = 0; count < max; count++)
281     {
282       part = [parts objectAtIndex: count];
283       header = (NGMimeContentDispositionHeaderField *) [part headerForKey: @"content-disposition"];
284       mimeType = [(NGMimeType *) [part headerForKey: @"content-type"] stringValue];
285       attachment = [NSDictionary dictionaryWithObjectsAndKeys:
286                                    [header filename], @"filename",
287                                  mimeType, @"mime-type", nil];
288       [filenames setObject: attachment
289                  forKey: [header name]];
290     }
291
292   return filenames;
293 }
294
295 - (BOOL) _saveAttachments
296 {
297   WORequest *request;
298   NSEnumerator *allKeys;
299   NSString *key;
300   BOOL success;
301   NSDictionary *filenames;
302   id httpBody;
303   SOGoDraftObject *co;
304
305   success = YES;
306   request = [context request];
307
308   httpBody = [[request httpRequest] body];
309   filenames = [self _scanAttachmentFilenamesInRequest: httpBody];
310
311   co = [self clientObject];
312   allKeys = [[request formValueKeys] objectEnumerator];
313   key = [allKeys nextObject];
314   while (key && success)
315     {
316       if ([key hasPrefix: @"attachment"])
317         success
318           = (![co saveAttachment: (NSData *) [request formValueForKey: key]
319                   withMetadata: [filenames objectForKey: key]]);
320       key = [allKeys nextObject];
321     }
322
323   return success;
324 }
325
326 - (BOOL) _saveFormInfo
327 {
328   NSDictionary *info;
329   NSException *error;
330   BOOL success;
331   SOGoDraftObject *co;
332
333   co = [self clientObject];
334   [co fetchInfo];
335
336   success = YES;
337
338   if ([self _saveAttachments])
339     {
340       info = [self storeInfo];
341       [co setHeaders: info];
342       [co setText: text];
343       error = [co storeInfo];
344       if (error)
345         {
346           [self errorWithFormat: @"failed to store draft: %@", error];
347           // TODO: improve error handling
348           success = NO;
349         }
350     }
351   else
352     success = NO;
353
354   // TODO: wrap content
355   
356   return success;
357 }
358
359 - (id) failedToSaveFormResponse
360 {
361   // TODO: improve error handling
362   return [NSException exceptionWithHTTPStatus:500 /* server error */
363                       reason:@"failed to store draft object on server!"];
364 }
365
366 /* attachment helper */
367
368 - (NSArray *) attachmentNames
369 {
370   NSArray *a;
371
372   if (attachmentNames != nil)
373     return attachmentNames;
374
375   a = [[self clientObject] fetchAttachmentNames];
376   a = [a sortedArrayUsingSelector: @selector (compare:)];
377   attachmentNames = [a copy];
378
379   return attachmentNames;
380 }
381
382 - (BOOL) hasAttachments
383 {
384   return [[self attachmentNames] count] > 0 ? YES : NO;
385 }
386
387 - (id) defaultAction
388 {
389   SOGoDraftObject *co;
390
391   co = [self clientObject];
392   [co fetchInfo];
393   [self loadInfo: [co headers]];
394   [self setText: [co text]];
395
396   return self;
397 }
398
399 - (id <WOActionResults>) saveAction
400 {
401   id result;
402
403   if ([self _saveFormInfo])
404     {
405       result = [[self clientObject] save];
406       if (!result)
407         {
408           result = [context response];
409           [result setStatus: 204];
410         }
411     }
412   else
413     result = [self failedToSaveFormResponse];
414
415   return result;
416 }
417
418 - (NSException *) validateForSend
419 {
420   NSException *error;
421
422   if (![self hasOneOrMoreRecipients])
423     error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
424                          reason: @"Please select a recipient!"];
425   else if ([[self subject] length] == 0)
426     error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
427                          reason: @"Please set a subject!"];
428   else
429     error = nil;
430   
431   return error;
432 }
433
434 - (id <WOActionResults>) sendAction
435 {
436   id <WOActionResults> result;
437
438   // TODO: need to validate whether we have a To etc
439   
440   /* first, save form data */
441   result = [self validateForSend];
442   if (!result)
443     {
444       if ([self _saveFormInfo])
445         {
446           result = [[self clientObject] sendMail];
447           if (!result)
448             result = [self jsCloseWithRefreshMethod: nil];
449         }
450       else
451         result = [self failedToSaveFormResponse];
452     }
453
454   return result;
455 }
456
457 @end /* UIxMailEditor */