From e7201afa4a952ba21d5e73f85a32a9b661537a55 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 17 Aug 2007 02:42:17 +0000 Subject: [PATCH] Monotone-Parent: d0cc38cc291cb895f6e1f64d453e66215671d6a0 Monotone-Revision: 83572f5aa6453bbf0ec254ae1887286fb7e0c0a1 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2007-08-17T02:42:17 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 15 ++ UI/MailerUI/UIxMailEditor.m | 497 ++++++++++-------------------------- 2 files changed, 152 insertions(+), 360 deletions(-) diff --git a/ChangeLog b/ChangeLog index c0b7db351..cf536a10d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,20 @@ 2007-08-16 Wolfgang Sourdeau + * UI/MailerUI/UIxMailEditor.m ([-patchFlagsInStore]): removed + useless stub method, of which the intention was implemented in + SOGoDraftObject. + ([-lookupSentFolderUsingAccount]): removed obsolete method. + ([-selectedMailIdentity]): removed obsolete method. + ([-lookupSentFolderUsingFrom]): removed obsolete method. + ([-storeMailInSentFolder:_path]): removed obsolete method, of + which the mechanism has been put in -[SOGoDraftObject sendMail] + method. + ([UIxMailEditor -_saveFormInfo], [UIxMailEditor -defaultAction]) + ([UIxMailEditor -saveAction], [UIxMailEditor -sendAction]): + adapted algorithms to the new SOGoDraftObject methods. + ([-deleteAction]): removed method since local draft objects cannot + be removed by the user. + * UI/MailerUI/UIxMailFolderActions.m ([UIxMailFolderActions -expungeAction]): new method replacing the one previously found in UIxMailListView. diff --git a/UI/MailerUI/UIxMailEditor.m b/UI/MailerUI/UIxMailEditor.m index 89d8cd2ab..a221609e1 100644 --- a/UI/MailerUI/UIxMailEditor.m +++ b/UI/MailerUI/UIxMailEditor.m @@ -24,9 +24,10 @@ #import #import -#import -#import #import +#import +#import +#import #import #import #import @@ -41,8 +42,9 @@ #import #import #import -#import #import +#import +#import #import /* @@ -51,9 +53,6 @@ An mail editor component which works on SOGoDraftObject's. */ -@class NSArray, NSString; -@class SOGoMailFolder; - @interface UIxMailEditor : UIxComponent { NSArray *to; @@ -61,7 +60,7 @@ NSArray *bcc; NSString *subject; NSString *text; - NSMutableArray *fromEMails; + NSArray *fromEMails; NSString *from; SOGoMailFolder *sentFolder; @@ -74,18 +73,18 @@ @implementation UIxMailEditor -static BOOL keepMailTmpFile = NO; -static BOOL showInternetMarker = NO; -static BOOL useLocationBasedSentFolder = NO; +static BOOL keepMailTmpFile = NO; +static BOOL showInternetMarker = NO; +static BOOL useLocationBasedSentFolder = NO; static NSDictionary *internetMailHeaders = nil; -static NSArray *infoKeys = nil; +static NSArray *infoKeys = nil; + (void) initialize { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; infoKeys = [[NSArray alloc] initWithObjects: - @"subject", @"text", @"to", @"cc", @"bcc", + @"subject", @"to", @"cc", @"bcc", @"from", @"replyTo", nil]; @@ -113,104 +112,114 @@ static NSArray *infoKeys = nil; { [sentFolder release]; [fromEMails release]; - [from release]; - [text release]; + [from release]; + [text release]; [subject release]; - [to release]; - [cc release]; - [bcc release]; - - [attachmentName release]; + [to release]; + [cc release]; + [bcc release]; + [attachmentName release]; [attachmentNames release]; [super dealloc]; } /* accessors */ -- (void) setFrom: (NSString *) _value +- (void) setFrom: (NSString *) newFrom { - ASSIGNCOPY(from, _value); + ASSIGN (from, newFrom); } - (NSString *) from { - if (![from isNotEmpty]) - return [[[self context] activeUser] primaryEmail]; + NSDictionary *identity; + + if (!from) + { + identity = [[context activeUser] primaryIdentity]; + from = [identity keysWithFormat: @"%{fullName} <%{email}>"]; + } return from; } -- (void) setReplyTo: (NSString *) _ignore -{ -} +// - (void) setReplyTo: (NSString *) ignore +// { +// } -- (NSString *) replyTo -{ - /* we are here for future extensibility */ - return @""; -} +// - (NSString *) replyTo +// { +// /* we are here for future extensibility */ +// return @""; +// } -- (void) setSubject: (NSString *) _value +- (void) setSubject: (NSString *) newSubject { - ASSIGNCOPY(subject, _value); + ASSIGN (subject, newSubject); } - (NSString *) subject { - return subject ? subject : @""; + return subject; } -- (void) setText: (NSString *) _value +- (void) setText: (NSString *) newText { - ASSIGNCOPY(text, _value); + ASSIGN (text, newText); } - (NSString *) text { - return [text isNotNull] ? text : @""; + return text; } -- (void) setTo: (NSArray *)_value +- (void) setTo: (NSArray *) newTo { - ASSIGNCOPY(to, _value); + if ([newTo isKindOfClass: [NSNull class]]) + newTo = nil; + + ASSIGN (to, newTo); } - (NSArray *) to { - return [to isNotNull] ? to : [NSArray array]; + return to; } -- (void) setCc: (NSArray *) _value +- (void) setCc: (NSArray *) newCc { - ASSIGNCOPY(cc, _value); + if ([newCc isKindOfClass: [NSNull class]]) + newCc = nil; + + ASSIGN (cc, newCc); } - (NSArray *) cc { - return [cc isNotNull] ? cc : [NSArray array]; + return cc; } -- (void) setBcc: (NSArray *) _value +- (void) setBcc: (NSArray *) newBcc { - ASSIGNCOPY(bcc, _value); + if ([newBcc isKindOfClass: [NSNull class]]) + newBcc = nil; + + ASSIGN (bcc, newBcc); } - (NSArray *) bcc { - return [bcc isNotNull] ? bcc : [NSArray array]; + return bcc; } - (BOOL) hasOneOrMoreRecipients { - if ([[self to] count] > 0) return YES; - if ([[self cc] count] > 0) return YES; - if ([[self bcc] count] > 0) return YES; - return NO; + return (([to count] + [cc count] + [bcc count]) > 0); } -- (void) setAttachmentName: (NSString *) _attachmentName +- (void) setAttachmentName: (NSString *) newAttachmentName { - ASSIGN(attachmentName, _attachmentName); + ASSIGN (attachmentName, newAttachmentName); } - (NSString *) attachmentName @@ -222,223 +231,41 @@ static NSArray *infoKeys = nil; - (NSArray *) fromEMails { - NSEnumerator *emails; - SOGoUser *activeUser; - NSString *cn, *fullMail, *email; - + NSArray *allIdentities; + if (!fromEMails) { - fromEMails = [NSMutableArray new]; - activeUser = [context activeUser]; - cn = [activeUser cn]; - if ([cn length] == 0) - cn = nil; - emails = [[activeUser allEmails] objectEnumerator]; - email = [emails nextObject]; - while (email) - { - if (cn) - fullMail = [NSString stringWithFormat: @"%@ <%@>", cn, email]; - else - fullMail = email; - [fromEMails addObject: fullMail]; - email = [emails nextObject]; - } + allIdentities = [[context activeUser] allIdentities]; + fromEMails = [allIdentities keysWithFormat: @"%{fullName} <%{email}>"]; + [fromEMails retain]; } return fromEMails; } -/* title */ - -- (NSString *)panelTitle { - return [self labelForKey:@"Compose Mail"]; -} - /* info loading */ -- (void)loadInfo:(NSDictionary *)_info { +- (void) loadInfo: (NSDictionary *) _info +{ if (![_info isNotNull]) return; [self debugWithFormat:@"loading info ..."]; [self takeValuesFromDictionary:_info]; } -- (NSDictionary *)storeInfo { + +- (NSDictionary *) storeInfo +{ [self debugWithFormat:@"storing info ..."]; return [self valuesForKeys:infoKeys]; } /* requests */ -- (BOOL) shouldTakeValuesFromRequest: (WORequest *) _rq - inContext: (WOContext*) _c +- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request + inContext: (WOContext*) localContext { return YES; } -/* IMAP4 store */ - -- (NSException *) patchFlagsInStore -{ - /* - Flags we should set: - if the draft is a reply => [message markAnswered] - if the draft is a forward => [message addFlag:@"forwarded"] - - This is hard, we would need to find the original message in Cyrus. - */ - return nil; -} - -- (id) lookupSentFolderUsingAccount -{ - SOGoMailAccount *account; - SOGoMailFolder *folder; - - if (sentFolder != nil) - return [sentFolder isNotNull] ? sentFolder : nil;; - - account = [[self clientObject] mailAccountFolder]; - if ([account isKindOfClass:[NSException class]]) return account; - - folder = [account sentFolderInContext:[self context]]; - if ([folder isKindOfClass:[NSException class]]) return folder; - return ((sentFolder = [folder retain])); -} - -- (void) _presetFromBasedOnAccountsQueryParameter -{ - /* preset the from field to the primary identity of the given account */ - /* Note: The compose action sets the 'accounts' query parameter */ - NSString *accountID; - SOGoMailAccounts *accounts; - SOGoMailAccount *account; - SOGoMailIdentity *identity; - - if (useLocationBasedSentFolder) /* from will be based on location */ - return; - - if ([from isNotEmpty]) /* a from is already set */ - return; - - accountID = [[[self context] request] formValueForKey:@"account"]; - if (![accountID isNotEmpty]) - return; - - accounts = [[self clientObject] mailAccountsFolder]; - if ([accounts isExceptionOrNull]) - return; /* we don't treat this as an error but are tolerant */ - - account = [accounts lookupName:accountID inContext:[self context] - acquire:NO]; - if ([account isExceptionOrNull]) - return; /* we don't treat this as an error but are tolerant */ - - identity = [account valueForKey:@"preferredIdentity"]; - if (![identity isNotNull]) { - [self warnWithFormat:@"Account has no preferred identity: %@", account]; - return; - } - - [self setFrom: [identity email]]; -} - -- (SOGoMailIdentity *) selectedMailIdentity -{ - SOGoMailAccounts *accounts; - NSEnumerator *e; - SOGoMailIdentity *identity; - - accounts = [[self clientObject] mailAccountsFolder]; - if ([accounts isExceptionOrNull]) return (id)accounts; - - // TODO: This is still a hack because we detect the identity based on the - // from. In Agenor all of the identities have unique emails, but this - // is not required for SOGo. - - if ([[self from] length] == 0) - return nil; - - e = [[accounts fetchIdentitiesWithEmitterPermissions] objectEnumerator]; - while ((identity = [e nextObject]) != nil) { - if ([[identity email] isEqualToString:[self from]]) - return identity; - } - return nil; -} - -- (id) lookupSentFolderUsingFrom -{ - // TODO: if we have the identity we could also support BCC - SOGoMailAccounts *accounts; - SOGoMailIdentity *identity; - SoSubContext *ctx; - NSString *sentFolderName; - NSArray *sentFolderPath; - NSException *error = nil; - - if (sentFolder != nil) - return [sentFolder isNotNull] ? sentFolder : nil;; - - identity = [self selectedMailIdentity]; - if ([identity isKindOfClass:[NSException class]]) return identity; - - if (![(sentFolderName = [identity sentFolderName]) isNotEmpty]) { - [self warnWithFormat:@"Identity has no sent folder name: %@", identity]; - return nil; - } - - // TODO: fixme, we treat the foldername as a hardcoded path from SOGoAccounts - // TODO: escaping of foldernames with slashes - // TODO: maybe the SOGoMailIdentity should have an 'account-identifier' - // which is used to lookup the account and _then_ perform an account - // local folder lookup? => would not be possible to have identities - // saving to different accounts. - sentFolderPath = [sentFolderName componentsSeparatedByString:@"/"]; - - accounts = [[self clientObject] mailAccountsFolder]; - if ([accounts isKindOfClass:[NSException class]]) return (id)accounts; - - ctx = [[SoSubContext alloc] initWithParentContext:[self context]]; - - sentFolder = [[accounts traversePathArray:sentFolderPath - inContext:ctx error:&error - acquire:NO] retain]; - [ctx release]; ctx = nil; - if (error != nil) { - [self errorWithFormat:@"Sent-Folder lookup for identity %@ failed: %@", - identity, sentFolderPath]; - return error; - } - -#if 0 - [self logWithFormat:@"Sent-Folder: %@", sentFolderName]; - [self logWithFormat:@" object: %@", sentFolder]; -#endif - return sentFolder; -} - -- (NSException *) storeMailInSentFolder: (NSString *) _path -{ - SOGoMailFolder *folder; - NSData *data; - id result; - - folder = useLocationBasedSentFolder - ? [self lookupSentFolderUsingAccount] - : [self lookupSentFolderUsingFrom]; - if ([folder isKindOfClass:[NSException class]]) return (id)folder; - if (folder == nil) return nil; - - if ((data = [[NSData alloc] initWithContentsOfMappedFile:_path]) == nil) { - return [NSException exceptionWithHTTPStatus:500 /* server error */ - reason:@"could not find temporary draft file!"]; - } - - result = [folder postData:data flags:@"seen"]; - [data release]; data = nil; - return result; -} - /* actions */ - (NSDictionary *) _scanAttachmentFilenamesInRequest: (id) httpBody @@ -458,8 +285,8 @@ static NSArray *infoKeys = nil; for (count = 0; count < max; count++) { part = [parts objectAtIndex: count]; - header = [part headerForKey: @"content-disposition"]; - mimeType = [[part headerForKey: @"content-type"] stringValue]; + header = (NGMimeContentDispositionHeaderField *) [part headerForKey: @"content-disposition"]; + mimeType = [(NGMimeType *) [part headerForKey: @"content-type"] stringValue]; attachment = [NSDictionary dictionaryWithObjectsAndKeys: [header filename], @"filename", mimeType, @"mime-type", nil]; @@ -506,26 +333,29 @@ static NSArray *infoKeys = nil; NSDictionary *info; NSException *error; BOOL success; + SOGoDraftObject *co; + + co = [self clientObject]; + [co fetchInfo]; success = YES; if ([self _saveAttachments]) { info = [self storeInfo]; - if (info) + [co setHeaders: info]; + [co setText: text]; + error = [co storeInfo]; + if (error) { - error = [[self clientObject] storeInfo:info]; - if (error) - { - [self errorWithFormat:@"failed to store draft: %@", error]; - // TODO: improve error handling - success = NO; - } + [self errorWithFormat: @"failed to store draft: %@", error]; + // TODO: improve error handling + success = NO; } } else success = NO; - + // TODO: wrap content return success; @@ -543,13 +373,14 @@ static NSArray *infoKeys = nil; - (NSArray *) attachmentNames { NSArray *a; - + if (attachmentNames != nil) return attachmentNames; - + a = [[self clientObject] fetchAttachmentNames]; - a = [a sortedArrayUsingSelector:@selector(compare:)]; + a = [a sortedArrayUsingSelector: @selector (compare:)]; attachmentNames = [a copy]; + return attachmentNames; } @@ -560,126 +391,72 @@ static NSArray *infoKeys = nil; - (id) defaultAction { -#if 0 - [self logWithFormat:@"edit action, load content from: %@", - [self clientObject]]; -#endif - - [self loadInfo:[[self clientObject] fetchInfo]]; - [self _presetFromBasedOnAccountsQueryParameter]; + SOGoDraftObject *co; + + co = [self clientObject]; + [co fetchInfo]; + [self loadInfo: [co headers]]; + [self setText: [co text]]; + return self; } -- (id) saveAction +- (id ) saveAction { - return [self _saveFormInfo] ? self : [self failedToSaveFormResponse]; + id result; + + if ([self _saveFormInfo]) + { + result = [[self clientObject] save]; + if (!result) + { + result = [context response]; + [result setStatus: 204]; + } + } + else + result = [self failedToSaveFormResponse]; + + return result; } - (NSException *) validateForSend { - // TODO: localize errors + NSException *error; + + if (![self hasOneOrMoreRecipients]) + error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */ + reason: @"Please select a recipient!"]; + else if ([[self subject] length] == 0) + error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */ + reason: @"Please set a subject!"]; + else + error = nil; - if (![self hasOneOrMoreRecipients]) { - return [NSException exceptionWithHTTPStatus:400 /* Bad Request */ - reason:@"Please select a recipient!"]; - } - if ([[self subject] length] == 0) { - return [NSException exceptionWithHTTPStatus:400 /* Bad Request */ - reason:@"Please set a subject!"]; - } - - return nil; + return error; } - (id ) sendAction { - NSException *error; - NSString *mailPath; id result; // TODO: need to validate whether we have a To etc /* first, save form data */ - - if (![self _saveFormInfo]) - return [self failedToSaveFormResponse]; - - /* validate for send */ - - if ((error = [self validateForSend]) != nil) { - id url; - - url = [[error reason] stringByEscapingURL]; - url = [@"edit?error=" stringByAppendingString:url]; - return [self redirectToLocation:url]; - } - - /* setup some extra headers if required */ - - /* save mail to file (so that we can upload the mail to Cyrus) */ - // TODO: all this could be handled by the SOGoDraftObject? - - mailPath = [[self clientObject] saveMimeMessageToTemporaryFileWithHeaders: internetMailHeaders]; - - /* then, send mail */ - - if ((error = [[self clientObject] sendMimeMessageAtPath:mailPath]) != nil) { - // TODO: improve error handling - [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil]; - return error; - } - - /* patch flags in store for replies etc */ - - if ((error = [self patchFlagsInStore]) != nil) - return error; - - /* finally store in Sent */ - - if ((error = [self storeMailInSentFolder:mailPath]) != nil) - return error; - - /* delete temporary mail file */ - - if (keepMailTmpFile) - [self warnWithFormat:@"keeping mail file: '%@'", mailPath]; - else - [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil]; - mailPath = nil; - - /* delete draft */ - - if ((error = [[self clientObject] delete]) != nil) - return error; - - if ([[[[self context] request] formValueForKey: @"nojs"] intValue]) - result = [self redirectToLocation: [self applicationPath]]; - else - result = [self jsCloseWithRefreshMethod: nil]; + result = [self validateForSend]; + if (!result) + { + if ([self _saveFormInfo]) + { + result = [[self clientObject] sendMail]; + if (!result) + result = [self jsCloseWithRefreshMethod: nil]; + } + else + result = [self failedToSaveFormResponse]; + } return result; } -- (id) deleteAction -{ - NSException *error; - id page; - - if ((error = [[self clientObject] delete]) != nil) { - /* Note: we ignore 404: those are drafts which were not yet saved */ - if (![error httpStatus] == 404) - return error; - } - -#if 1 - page = [self pageWithName:@"UIxMailWindowCloser"]; - [page takeValue:@"YES" forKey:@"refreshOpener"]; - return page; -#else - // TODO: if we just return nil, we produce a 500 - return [NSException exceptionWithHTTPStatus:204 /* No Content */ - reason:@"object was deleted."]; -#endif -} - @end /* UIxMailEditor */