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/NSFileManager.h>
23 #import <Foundation/NSKeyValueCoding.h>
24 #import <Foundation/NSString.h>
25 #import <Foundation/NSUserDefaults.h>
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>
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>
53 An mail editor component which works on SOGoDraftObject's.
56 @interface UIxMailEditor : UIxComponent
65 SOGoMailFolder *sentFolder;
67 /* these are for the inline attachment list */
68 NSString *attachmentName;
69 NSArray *attachmentNames;
70 NSMutableArray *attachedFiles;
75 @implementation UIxMailEditor
77 static BOOL showInternetMarker = NO;
78 static BOOL useLocationBasedSentFolder = NO;
79 static NSDictionary *internetMailHeaders = nil;
80 static NSArray *infoKeys = nil;
84 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
86 infoKeys = [[NSArray alloc] initWithObjects:
87 @"subject", @"to", @"cc", @"bcc",
88 @"from", @"replyTo", @"inReplyTo",
91 useLocationBasedSentFolder =
92 [ud boolForKey:@"SOGoUseLocationBasedSentFolder"];
94 /* Internet mail settings */
96 showInternetMarker = [ud boolForKey:@"SOGoShowInternetMarker"];
97 if (!showInternetMarker)
98 NSLog(@"Note: visual Internet marker on mail editor disabled "
99 @"(SOGoShowInternetMarker)");
101 internetMailHeaders =
102 [[ud dictionaryForKey:@"SOGoInternetMailHeaders"] copy];
103 NSLog (@"Note: specified %d headers for mails send via the Internet.",
104 [internetMailHeaders count]);
109 [sentFolder release];
110 [fromEMails release];
117 [attachmentName release];
118 [attachmentNames release];
119 [attachedFiles release];
125 - (void) setFrom: (NSString *) newFrom
127 ASSIGN (from, newFrom);
132 NSDictionary *identity;
136 identity = [[context activeUser] primaryIdentity];
137 from = [identity keysWithFormat: @"%{fullName} <%{email}>"];
143 // - (void) setReplyTo: (NSString *) ignore
147 // - (NSString *) replyTo
149 // /* we are here for future extensibility */
153 - (void) setSubject: (NSString *) newSubject
155 ASSIGN (subject, newSubject);
158 - (NSString *) subject
163 - (void) setText: (NSString *) newText
165 ASSIGN (text, newText);
173 - (void) setTo: (NSArray *) newTo
175 if ([newTo isKindOfClass: [NSNull class]])
186 - (void) setCc: (NSArray *) newCc
188 if ([newCc isKindOfClass: [NSNull class]])
199 - (void) setBcc: (NSArray *) newBcc
201 if ([newBcc isKindOfClass: [NSNull class]])
204 ASSIGN (bcc, newBcc);
212 - (BOOL) hasOneOrMoreRecipients
214 return (([to count] + [cc count] + [bcc count]) > 0);
217 - (void) setAttachmentName: (NSString *) newAttachmentName
219 ASSIGN (attachmentName, newAttachmentName);
222 - (NSString *) attachmentName
224 return attachmentName;
229 - (NSArray *) fromEMails
231 NSArray *allIdentities;
235 allIdentities = [[context activeUser] allIdentities];
236 fromEMails = [allIdentities keysWithFormat: @"%{fullName} <%{email}>"];
245 - (void) loadInfo: (NSDictionary *) _info
247 if (![_info isNotNull]) return;
248 [self debugWithFormat:@"loading info ..."];
249 [self takeValuesFromDictionary:_info];
252 - (NSDictionary *) storeInfo
254 [self debugWithFormat:@"storing info ..."];
255 return [self valuesForKeys:infoKeys];
260 - (BOOL) shouldTakeValuesFromRequest: (WORequest *) request
261 inContext: (WOContext*) localContext
267 - (NSString *) _fixedFilename: (NSString *) filename
269 NSString *newFilename, *baseFilename, *extension;
270 unsigned int variation;
273 attachedFiles = [NSMutableArray new];
275 newFilename = filename;
277 baseFilename = [filename stringByDeletingPathExtension];
278 extension = [filename pathExtension];
280 while ([attachedFiles containsObject: newFilename])
283 newFilename = [NSString stringWithFormat: @"%@-%d.%@", baseFilename,
284 variation, extension];
286 [attachedFiles addObject: newFilename];
291 - (NSDictionary *) _scanAttachmentFilenamesInRequest: (id) httpBody
293 NSMutableDictionary *filenames;
294 NSDictionary *attachment;
296 unsigned int count, max;
297 NGMimeBodyPart *part;
298 NGMimeContentDispositionHeaderField *header;
299 NSString *mimeType, *filename;
301 parts = [httpBody parts];
303 filenames = [NSMutableDictionary dictionaryWithCapacity: max];
305 for (count = 0; count < max; count++)
307 part = [parts objectAtIndex: count];
308 header = (NGMimeContentDispositionHeaderField *)
309 [part headerForKey: @"content-disposition"];
310 mimeType = [(NGMimeType *)
311 [part headerForKey: @"content-type"] stringValue];
312 filename = [self _fixedFilename: [header filename]];
313 attachment = [NSDictionary dictionaryWithObjectsAndKeys:
314 filename, @"filename",
315 mimeType, @"mimetype", nil];
316 [filenames setObject: attachment forKey: [header name]];
322 - (BOOL) _saveAttachments
325 NSEnumerator *allKeys;
328 NSDictionary *filenames;
333 request = [context request];
335 httpBody = [[request httpRequest] body];
336 filenames = [self _scanAttachmentFilenamesInRequest: httpBody];
338 co = [self clientObject];
339 allKeys = [[request formValueKeys] objectEnumerator];
340 key = [allKeys nextObject];
341 while (key && success)
343 if ([key hasPrefix: @"attachment"])
345 = (![co saveAttachment: (NSData *) [request formValueForKey: key]
346 withMetadata: [filenames objectForKey: key]]);
347 key = [allKeys nextObject];
353 - (BOOL) _saveFormInfo
360 co = [self clientObject];
365 if ([self _saveAttachments])
367 info = [self storeInfo];
368 [co setHeaders: info];
370 error = [co storeInfo];
373 [self errorWithFormat: @"failed to store draft: %@", error];
374 // TODO: improve error handling
381 // TODO: wrap content
386 - (id) failedToSaveFormResponse
388 // TODO: improve error handling
389 return [NSException exceptionWithHTTPStatus:500 /* server error */
390 reason:@"failed to store draft object on server!"];
393 /* attachment helper */
395 - (NSArray *) attachmentNames
399 if (!attachmentNames)
401 a = [[self clientObject] fetchAttachmentNames];
402 ASSIGN (attachmentNames,
403 [a sortedArrayUsingSelector: @selector (compare:)]);
406 return attachmentNames;
409 - (BOOL) hasAttachments
411 return [[self attachmentNames] count] > 0 ? YES : NO;
418 co = [self clientObject];
420 [self loadInfo: [co headers]];
421 [self setText: [co text]];
426 - (id <WOActionResults>) saveAction
430 if ([self _saveFormInfo])
432 result = [[self clientObject] save];
434 result = [self responseWith204];
437 result = [self failedToSaveFormResponse];
442 - (NSException *) validateForSend
446 if (![self hasOneOrMoreRecipients])
447 error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
448 reason: @"Please select a recipient!"];
449 else if ([[self subject] length] == 0)
450 error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
451 reason: @"Please set a subject!"];
458 - (id <WOActionResults>) sendAction
460 id <WOActionResults> result;
462 // TODO: need to validate whether we have a To etc
464 /* first, save form data */
465 result = [self validateForSend];
468 if ([self _saveFormInfo])
470 result = [[self clientObject] sendMail];
472 result = [self jsCloseWithRefreshMethod: @"refreshCurrentFolder()"];
475 result = [self failedToSaveFormResponse];
481 @end /* UIxMailEditor */