]> err.no Git - scalable-opengroupware.org/blob - UI/MailerUI/UIxMailEditor.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1275 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 *)
284         [part headerForKey: @"content-disposition"];
285       mimeType = [(NGMimeType *)
286                    [part headerForKey: @"content-type"] stringValue];
287       attachment = [NSDictionary dictionaryWithObjectsAndKeys:
288                                    [header filename], @"filename",
289                                  mimeType, @"mimetype", nil];
290       [filenames setObject: attachment forKey: [header name]];
291     }
292
293   return filenames;
294 }
295
296 - (BOOL) _saveAttachments
297 {
298   WORequest *request;
299   NSEnumerator *allKeys;
300   NSString *key;
301   BOOL success;
302   NSDictionary *filenames;
303   id httpBody;
304   SOGoDraftObject *co;
305
306   success = YES;
307   request = [context request];
308
309   httpBody = [[request httpRequest] body];
310   filenames = [self _scanAttachmentFilenamesInRequest: httpBody];
311
312   co = [self clientObject];
313   allKeys = [[request formValueKeys] objectEnumerator];
314   key = [allKeys nextObject];
315   while (key && success)
316     {
317       if ([key hasPrefix: @"attachment"])
318         success
319           = (![co saveAttachment: (NSData *) [request formValueForKey: key]
320                   withMetadata: [filenames objectForKey: key]]);
321       key = [allKeys nextObject];
322     }
323
324   return success;
325 }
326
327 - (BOOL) _saveFormInfo
328 {
329   NSDictionary *info;
330   NSException *error;
331   BOOL success;
332   SOGoDraftObject *co;
333
334   co = [self clientObject];
335   [co fetchInfo];
336
337   success = YES;
338
339   if ([self _saveAttachments])
340     {
341       info = [self storeInfo];
342       [co setHeaders: info];
343       [co setText: text];
344       error = [co storeInfo];
345       if (error)
346         {
347           [self errorWithFormat: @"failed to store draft: %@", error];
348           // TODO: improve error handling
349           success = NO;
350         }
351     }
352   else
353     success = NO;
354
355   // TODO: wrap content
356   
357   return success;
358 }
359
360 - (id) failedToSaveFormResponse
361 {
362   // TODO: improve error handling
363   return [NSException exceptionWithHTTPStatus:500 /* server error */
364                       reason:@"failed to store draft object on server!"];
365 }
366
367 /* attachment helper */
368
369 - (NSArray *) attachmentNames
370 {
371   NSArray *a;
372
373   if (attachmentNames != nil)
374     return attachmentNames;
375
376   a = [[self clientObject] fetchAttachmentNames];
377   a = [a sortedArrayUsingSelector: @selector (compare:)];
378   attachmentNames = [a copy];
379
380   return attachmentNames;
381 }
382
383 - (BOOL) hasAttachments
384 {
385   return [[self attachmentNames] count] > 0 ? YES : NO;
386 }
387
388 - (id) defaultAction
389 {
390   SOGoDraftObject *co;
391
392   co = [self clientObject];
393   [co fetchInfo];
394   [self loadInfo: [co headers]];
395   [self setText: [co text]];
396
397   return self;
398 }
399
400 - (id <WOActionResults>) saveAction
401 {
402   id result;
403
404   if ([self _saveFormInfo])
405     {
406       result = [[self clientObject] save];
407       if (!result)
408         result = [self responseWith204];
409     }
410   else
411     result = [self failedToSaveFormResponse];
412
413   return result;
414 }
415
416 - (NSException *) validateForSend
417 {
418   NSException *error;
419
420   if (![self hasOneOrMoreRecipients])
421     error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
422                          reason: @"Please select a recipient!"];
423   else if ([[self subject] length] == 0)
424     error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
425                          reason: @"Please set a subject!"];
426   else
427     error = nil;
428   
429   return error;
430 }
431
432 - (id <WOActionResults>) sendAction
433 {
434   id <WOActionResults> result;
435
436   // TODO: need to validate whether we have a To etc
437   
438   /* first, save form data */
439   result = [self validateForSend];
440   if (!result)
441     {
442       if ([self _saveFormInfo])
443         {
444           result = [[self clientObject] sendMail];
445           if (!result)
446             result = [self jsCloseWithRefreshMethod: @"refreshFolderByType(\"sent\")"];
447         }
448       else
449         result = [self failedToSaveFormResponse];
450     }
451
452   return result;
453 }
454
455 @end /* UIxMailEditor */