From f8aa338e643653b238018a5f655c46660229b82a Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Thu, 18 Jun 2020 16:53:42 -0400 Subject: [PATCH] feat(mail): handle multiple mail identities Fixes #768, fixes #4602 --- SoObjects/Mailer/SOGoDraftObject.m | 7 +- SoObjects/Mailer/SOGoMailAccount.h | 3 +- SoObjects/Mailer/SOGoMailAccount.m | 32 +++- SoObjects/Mailer/SOGoMailForward.m | 7 +- SoObjects/SOGo/SOGoUser.m | 173 ++++++++++-------- SoObjects/SOGo/SOGoUserDefaults.h | 17 +- SoObjects/SOGo/SOGoUserDefaults.m | 127 ++++++------- SoObjects/SOGo/SOGoUserFolder.m | 24 +-- UI/MailerUI/English.lproj/Localizable.strings | 3 +- UI/MailerUI/UIxMailAccountActions.m | 10 +- UI/MailerUI/UIxMailEditor.m | 17 +- .../English.lproj/Localizable.strings | 3 + UI/PreferencesUI/UIxJSONPreferences.m | 5 +- UI/PreferencesUI/UIxPreferences.m | 82 +-------- UI/PreferencesUI/product.plist | 4 - UI/Templates/MailerUI/UIxMailEditor.wox | 37 +++- .../PreferencesUI/UIxAccountEditor.wox | 129 +++++++++---- UI/WebServerResources/angular-material | 2 +- .../js/Common/sgCkeditor.component.js | 12 +- .../js/Mailer/Account.service.js | 55 +++++- .../js/Mailer/MessageEditorController.js | 77 +++++++- .../js/Preferences/AccountDialogController.js | 138 +++++++++----- .../js/Preferences/PreferencesController.js | 50 +++-- .../components/autocomplete/autocomplete.scss | 5 + 24 files changed, 599 insertions(+), 420 deletions(-) diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index 2cbd0e3463..e9a143eebd 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -995,7 +995,7 @@ - (void) fetchMailForReplying: (SOGoMailObject *) sourceMail - (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail { NSDictionary *info, *attachment; - NSString *signature, *nl; + NSString *signature, *nl, *space; SOGoUserDefaults *ud; [sourceMail fetchCoreInfos]; @@ -1031,8 +1031,9 @@ - (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail signature = [[self mailAccountFolder] signature]; if ([signature length]) { - nl = (isHTML ? @"
" : @"\n"); - [self setText: [NSString stringWithFormat: @"%@%@-- %@%@", nl, nl, nl, signature]]; + nl = (isHTML ? @"
" : @"\n"); + space = (isHTML ? @" " : @" "); + [self setText: [NSString stringWithFormat: @"%@%@--%@%@%@", nl, nl, space, nl, signature]]; } attachment = [NSDictionary dictionaryWithObjectsAndKeys: [sourceMail filenameForForward], @"filename", diff --git a/SoObjects/Mailer/SOGoMailAccount.h b/SoObjects/Mailer/SOGoMailAccount.h index daf9a859f5..c886ec9f9e 100644 --- a/SoObjects/Mailer/SOGoMailAccount.h +++ b/SoObjects/Mailer/SOGoMailAccount.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2009-2019 Inverse inc. + Copyright (C) 2009-2020 Inverse inc. This file is part of SOGo. @@ -87,6 +87,7 @@ typedef enum { forceActivation: (BOOL) forceActivation; - (NSArray *) identities; +- (NSDictionary *) defaultIdentity; - (NSString *) signature; - (NSString *) encryption; diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index 38bb204587..baba805ee6 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2007-2019 Inverse inc. + Copyright (C) 2007-2020 Inverse inc. This file is part of SOGo. @@ -667,13 +667,37 @@ - (NSArray *) identities return identities; } +- (NSDictionary *) defaultIdentity +{ + NSDictionary *defaultIdentity, *currentIdentity; + unsigned int count, max; + + defaultIdentity = nil; + [self identities]; + + max = [identities count]; + for (count = 0; count < max; count++) + { + currentIdentity = [identities objectAtIndex: count]; + if ([[currentIdentity objectForKey: @"isDefault"] boolValue]) + { + defaultIdentity = currentIdentity; + break; + } + } + + return defaultIdentity; // can be nil +} + - (NSString *) signature { + NSDictionary *identity; NSString *signature; - [self identities]; - if ([identities count] > 0) - signature = [[identities objectAtIndex: 0] objectForKey: @"signature"]; + identity = [self defaultIdentity]; + + if (identity) + signature = [identity objectForKey: @"signature"]; else signature = nil; diff --git a/SoObjects/Mailer/SOGoMailForward.m b/SoObjects/Mailer/SOGoMailForward.m index 54ba0dc2bf..4e80aa823d 100644 --- a/SoObjects/Mailer/SOGoMailForward.m +++ b/SoObjects/Mailer/SOGoMailForward.m @@ -234,14 +234,15 @@ - (NSString *) messageBody - (NSString *) signature { - NSString *signature, *mailSignature, *nl; + NSString *signature, *mailSignature, *nl, *space; signature = [[sourceMail mailAccountFolder] signature]; if ([signature length]) { - nl = (htmlComposition ? @"
" : @"\n"); - mailSignature = [NSString stringWithFormat: @"-- %@%@", nl, signature]; + nl = (htmlComposition ? @"
" : @"\n"); + space = (htmlComposition ? @" " : @" "); + mailSignature = [NSString stringWithFormat: @"--%@%@%@", space, nl, signature]; } else mailSignature = @""; diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 0e9888fecc..d92abb5b60 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -342,18 +342,34 @@ - (NSString *) cn - (NSMutableDictionary *) defaultIdentity { - NSMutableDictionary *currentIdentity, *defaultIdentity; - NSEnumerator *identities; + NSDictionary *defaultAccount, *currentIdentity; + NSMutableDictionary *defaultIdentity; + NSArray *identities; + NSString *defaultEmail; + unsigned int count, max; + defaultEmail = [NSString stringWithFormat: @"%@@%@", [self loginInDomain], [self domain]]; + defaultAccount = [[self mailAccounts] objectAtIndex: 0]; defaultIdentity = nil; - identities = [[self allIdentities] objectEnumerator]; - while (!defaultIdentity - && (currentIdentity = [identities nextObject])) - if ([[currentIdentity objectForKey: @"isDefault"] boolValue]) - defaultIdentity = currentIdentity; + identities = [defaultAccount objectForKey: @"identities"]; + max = [identities count]; - return defaultIdentity; + for (count = 0; count < max; count++) + { + currentIdentity = [identities objectAtIndex: count]; + if ([[currentIdentity objectForKey: @"isDefault"] boolValue]) + { + defaultIdentity = [NSMutableDictionary dictionaryWithDictionary: currentIdentity]; + break; + } + else if ([[currentIdentity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame) + { + defaultIdentity = [NSMutableDictionary dictionaryWithDictionary: currentIdentity]; + } + } + + return defaultIdentity; // can be nil } - (SOGoDateFormatter *) dateFormatterInContext: (WOContext *) context @@ -626,19 +642,20 @@ - (void) _migrateFolderSettings - (void) _appendSystemMailAccountWithDelegatedIdentities: (BOOL) appendDeletegatedIdentities { - NSString *fullName, *replyTo, *imapLogin, *imapServer, *cImapServer, *signature, - *encryption, *scheme, *action, *query, *customEmail, *defaultEmail, *sieveServer; + NSString *fullName, *imapLogin, *imapServer, *cImapServer, + *encryption, *scheme, *action, *query, *customEmail, *sieveServer; NSMutableDictionary *mailAccount, *identity, *mailboxes, *receipts, *security, *mailSettings; NSNumber *port; NSMutableArray *identities, *mails; NSArray *delegators, *delegates; NSURL *url, *cUrl; - unsigned int count, max, default_identity; + unsigned int count, max; //, default_identity; NSInteger defaultPort; NSUInteger index; + BOOL hasDefaultIdentity; - [self userDefaults]; - [self userSettings]; + [self userDefaults]; // set _defaults + [self userSettings]; // set _settings mailSettings = [_settings objectForKey: @"Mail"]; mailAccount = [NSMutableDictionary new]; @@ -715,89 +732,75 @@ - (void) _appendSystemMailAccountWithDelegatedIdentities: (BOOL) appendDeletegat } // 5. Identities - defaultEmail = [NSString stringWithFormat: @"%@@%@", [self loginInDomain], [self domain]]; - default_identity = 0; identities = [NSMutableArray new]; + [identities addObjectsFromArray: [_defaults mailIdentities]]; mails = [NSMutableArray arrayWithArray: [self allEmails]]; [mailAccount setObject: [mails objectAtIndex: 0] forKey: @"name"]; + max = [identities count]; + hasDefaultIdentity = NO; + fullName = [self cn]; + if ([fullName length] == 0) + fullName = login; - replyTo = [_defaults mailReplyTo]; - - max = [mails count]; - - /* custom from */ - if ([[self domainDefaults] mailCustomFromEnabled]) + // Sanitize identities + for (count = 0; count < max; count++) { - [self userDefaults]; - customEmail = [_defaults mailCustomEmail]; - fullName = [_defaults mailCustomFullName]; - if ([customEmail length] > 0 || [fullName length] > 0) + identity = [NSMutableDictionary dictionaryWithDictionary: [identities objectAtIndex: count]]; + customEmail = [identity objectForKey: @"email"]; + if ([customEmail length]) { - if ([customEmail length] == 0) - customEmail = [mails objectAtIndex: 0]; - else if ([fullName length] == 0) + if (![[self domainDefaults] mailCustomFromEnabled]) { - // Custom email but default fullname; if the custom email is - // one of the user's emails, remove the duplicated entry + // No custom from -- enforce a valid email index = [mails indexOfObject: customEmail]; - if (index != NSNotFound) + if (index == NSNotFound) { - [mails removeObjectAtIndex: index]; - max--; + [identity setObject: [self systemEmail] forKey: @"email"]; } } - - if ([fullName length] == 0) - { - fullName = [self cn]; - if ([fullName length] == 0) - fullName = login; - } - - identity = [NSMutableDictionary new]; - [identity setObject: customEmail forKey: @"email"]; + } + else + { + // Email must be defined + [identity setObject: [self systemEmail] forKey: @"email"]; + } + if (![[self domainDefaults] mailCustomFromEnabled]) + { + // No custom from -- enforce a valid fullname and remove reply-to [identity setObject: fullName forKey: @"fullName"]; - - if ([replyTo length] > 0) - [identity setObject: replyTo forKey: @"replyTo"]; - - signature = [_defaults mailSignature]; - if (signature) - [identity setObject: signature forKey: @"signature"]; - [identities addObject: identity]; - - if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame) - default_identity = [identities count]-1; - - [identity release]; + [identity removeObjectForKey: @"replyTo"]; } + if (!appendDeletegatedIdentities) + { + [identity setObject: [NSNumber numberWithBool: YES] forKey: @"isReadOnly"]; + } + if ([[identity objectForKey: @"isDefault"] boolValue]) + { + if (hasDefaultIdentity || !appendDeletegatedIdentities) + [identity removeObjectForKey: @"isDefault"]; // only one possible default identity + else + hasDefaultIdentity = YES; + } + [identities replaceObjectAtIndex: count withObject: identity]; } - for (count = 0; count < max; count++) + if (![identities count]) { - identity = [NSMutableDictionary new]; - fullName = [self cn]; - if (![fullName length]) - fullName = login; - [identity setObject: fullName forKey: @"fullName"]; - [identity setObject: [[mails objectAtIndex: count] stringByTrimmingSpaces] - forKey: @"email"]; - - if ([replyTo length] > 0) - [identity setObject: replyTo forKey: @"replyTo"]; - - signature = [_defaults mailSignature]; - if (signature) - [identity setObject: signature forKey: @"signature"]; + // Create a default identity + identity = [NSMutableDictionary dictionaryWithObjectsAndKeys: + fullName, @"fullName", + [self systemEmail], @"email", nil]; + if (appendDeletegatedIdentities) + { + [identity setObject: [NSNumber numberWithBool: YES] forKey: @"isDefault"]; + hasDefaultIdentity = YES; + } + else + { + [identity setObject: [NSNumber numberWithBool: YES] forKey: @"isReadOnly"]; + } [identities addObject: identity]; - - if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame) - default_identity = [identities count]-1; - - [identity release]; } - [[identities objectAtIndex: default_identity] setObject: [NSNumber numberWithBool: YES] - forKey: @"isDefault"]; /* identities from delegators */ if (appendDeletegatedIdentities) @@ -980,11 +983,19 @@ - (NSArray *) allIdentities - (NSDictionary *) primaryIdentity { - NSDictionary *defaultAccount; + NSArray *identities; + NSDictionary *defaultIdentity, *defaultAccount; - defaultAccount = [[self mailAccounts] objectAtIndex: 0]; + defaultIdentity = [self defaultIdentity]; + + if (!defaultIdentity && [[self mailAccounts] count]) + { + defaultAccount = [[self mailAccounts] objectAtIndex: 0]; + identities = [defaultAccount objectForKey: @"identities"]; + defaultIdentity = [identities objectAtIndex: 0]; + } - return [[defaultAccount objectForKey: @"identities"] objectAtIndex: 0]; + return defaultIdentity; } /* folders */ diff --git a/SoObjects/SOGo/SOGoUserDefaults.h b/SoObjects/SOGo/SOGoUserDefaults.h index bbfd963765..be811341b0 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.h +++ b/SoObjects/SOGo/SOGoUserDefaults.h @@ -1,6 +1,6 @@ /* SOGoUserDefaults.h - this file is part of SOGo * - * Copyright (C) 2011-2017 Inverse inc. + * Copyright (C) 2011-2020 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -157,21 +157,9 @@ extern NSString *SOGoWeekStartFirstFullWeek; - (void) setMailReplyPlacement: (NSString *) newValue; - (NSString *) mailReplyPlacement; -- (void) setMailSignature: (NSString *) newValue; -- (NSString *) mailSignature; - - (void) setMailSignaturePlacement: (NSString *) newValue; - (NSString *) mailSignaturePlacement; -- (void) setMailCustomFullName: (NSString *) newValue; -- (NSString *) mailCustomFullName; - -- (void) setMailCustomEmail: (NSString *) newValue; -- (NSString *) mailCustomEmail; - -- (void) setMailReplyTo: (NSString *) newValue; -- (NSString *) mailReplyTo; - - (void) setAllowUserReceipt: (BOOL) allow; - (BOOL) allowUserReceipt; - (void) setUserReceiptNonRecipientAction: (NSString *) action; @@ -194,6 +182,9 @@ extern NSString *SOGoWeekStartFirstFullWeek; - (void) setMailCertificateAlwaysEncrypt: (BOOL) newValue; - (BOOL) mailCertificateAlwaysEncrypt; +- (void) setMailIdentities: (NSArray *) newIdentites; +- (NSArray *) mailIdentities; + - (void) setAuxiliaryMailAccounts: (NSArray *) newAccounts; - (NSArray *) auxiliaryMailAccounts; diff --git a/SoObjects/SOGo/SOGoUserDefaults.m b/SoObjects/SOGo/SOGoUserDefaults.m index 35e0647c45..ccd98d429d 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.m +++ b/SoObjects/SOGo/SOGoUserDefaults.m @@ -20,6 +20,7 @@ #import #import +#import #import #import @@ -114,33 +115,55 @@ - (BOOL) _migrateLastModule return rc; } -- (BOOL) _migrateSignature +- (BOOL) _migrateMailIdentities { BOOL rc; - NSString *signature; - NSArray *mailAccounts, *identities; - NSDictionary *identity; + NSArray *mailIdentities; + NSMutableDictionary *identity; + NSString *fullName, *email, *replyTo, *signature; - mailAccounts = [self arrayForKey: @"MailAccounts"]; - if (mailAccounts) + mailIdentities = [self mailIdentities]; + if (mailIdentities) { - rc = YES; - if ([mailAccounts count] > 0) + rc = NO; + } + else + { + identity = [NSMutableDictionary dictionaryWithCapacity: 4]; + fullName = [self stringForKey: @"SOGoMailCustomFullName"]; + email = [self stringForKey: @"SOGoMailCustomEmail"]; + replyTo = [self stringForKey: @"SOGoMailReplyTo"]; + signature = [self stringForKey: @"SOGoMailSignature"]; + + if ([fullName length]) + [identity setObject: fullName forKey: @"fullName"]; + if ([email length]) + [identity setObject: email forKey: @"email"]; + if ([replyTo length]) + [identity setObject: replyTo forKey: @"replyTo"]; + if ([signature length]) + [identity setObject: signature forKey: @"signature"]; + + if ([identity count]) { - identities = [[mailAccounts objectAtIndex: 0] - objectForKey: @"identifies"]; - if ([identities count] > 0) - { - identity = [identities objectAtIndex: 0]; - signature = [identity objectForKey: @"signature"]; - if ([signature length]) - [self setObject: signature forKey: @"MailSignature"]; - } + [identity setObject: [NSNumber numberWithBool: YES] forKey: @"isDefault"]; + [self setMailIdentities: [NSArray arrayWithObject: identity]]; } - [self removeObjectForKey: @"MailAccounts"]; + + /** + * Keep old attributes for now because v2 doesn't handle identities + * + if (fullName) + [self removeObjectForKey: @"SOGoMailCustomFullName"]; + if (email) + [self removeObjectForKey: @"SOGoMailCustomEmail"]; + if (replyTo) + [self removeObjectForKey: @"SOGoMailReplyTo"]; + if (signature) + [self removeObjectForKey: @"SOGoMailSignature"]; + */ + rc = YES; } - else - rc = NO; return rc; } @@ -211,7 +234,8 @@ - (BOOL) migrate /* we must not use a boolean operation, otherwise subsequent migrations will not be invoked in the case where rc = YES. */ return ([self _migrateLastModule] - | [self _migrateSignature] + // | [self _migrateSignature] + | [self _migrateMailIdentities] | [self _migrateCalendarCategories] | [self migrateOldDefaultsWithDictionary: migratedKeys] | [super migrate]); @@ -629,18 +653,6 @@ - (NSString *) mailReplyPlacement return [self stringForKey: @"SOGoMailReplyPlacement"]; } -- (void) setMailSignature: (NSString *) newValue -{ - if ([newValue length] == 0) - newValue = nil; - [self setObject: newValue forKey: @"SOGoMailSignature"]; -} - -- (NSString *) mailSignature -{ - return [self stringForKey: @"SOGoMailSignature"]; -} - - (void) setMailSignaturePlacement: (NSString *) newValue { [self setObject: newValue forKey: @"SOGoMailSignaturePlacement"]; @@ -660,45 +672,6 @@ - (NSString *) mailSignaturePlacement return signaturePlacement; } -- (void) setMailCustomFullName: (NSString *) newValue -{ - if ([newValue length] == 0) - newValue = nil; - [self setObject: [newValue stringByTrimmingSpaces] - forKey: @"SOGoMailCustomFullName"]; -} - -- (NSString *) mailCustomFullName -{ - return [self stringForKey: @"SOGoMailCustomFullName"]; -} - -- (void) setMailCustomEmail: (NSString *) newValue -{ - if ([newValue length] == 0) - newValue = nil; - [self setObject: [newValue stringByTrimmingSpaces] - forKey: @"SOGoMailCustomEmail"]; -} - -- (NSString *) mailCustomEmail -{ - return [self stringForKey: @"SOGoMailCustomEmail"]; -} - -- (void) setMailReplyTo: (NSString *) newValue -{ - if ([newValue length] == 0) - newValue = nil; - [self setObject: [newValue stringByTrimmingSpaces] - forKey: @"SOGoMailReplyTo"]; -} - -- (NSString *) mailReplyTo -{ - return [self stringForKey: @"SOGoMailReplyTo"]; -} - - (void) setAllowUserReceipt: (BOOL) allow { [self setBool: allow forKey: @"SOGoMailReceiptAllow"]; @@ -784,6 +757,16 @@ - (BOOL) mailCertificateAlwaysEncrypt return [self boolForKey: @"SOGoMailCertificateAlwaysEncrypt"]; } +- (void) setMailIdentities: (NSArray *) newIdentites +{ + [self setObject: newIdentites forKey: @"SOGoMailIdentities"]; +} + +- (NSArray *) mailIdentities +{ + return [self arrayForKey: @"SOGoMailIdentities"]; +} + - (void) setAuxiliaryMailAccounts: (NSArray *) newAccounts { [self setObject: newAccounts forKey: @"AuxiliaryMailAccounts"]; diff --git a/SoObjects/SOGo/SOGoUserFolder.m b/SoObjects/SOGo/SOGoUserFolder.m index 162157e49b..ef9847c133 100644 --- a/SoObjects/SOGo/SOGoUserFolder.m +++ b/SoObjects/SOGo/SOGoUserFolder.m @@ -674,18 +674,18 @@ - (NSString *) davResourceId return [NSString stringWithFormat: @"urn:uuid:%@", nameInContainer]; } -- (NSException *) setDavSignature: (NSString *) newSignature -{ - SOGoUserDefaults *ud; - SOGoUser *user; - - user = [SOGoUser userWithLogin: [self ownerInContext: nil]]; - ud = [user userDefaults]; - [ud setMailSignature: newSignature]; - [ud synchronize]; - - return nil; -} +// - (NSException *) setDavSignature: (NSString *) newSignature +// { +// SOGoUserDefaults *ud; +// SOGoUser *user; + +// user = [SOGoUser userWithLogin: [self ownerInContext: nil]]; +// ud = [user userDefaults]; +// [ud setMailSignature: newSignature]; +// [ud synchronize]; + +// return nil; +// } #warning unused stub - (BOOL) collectionDavKey: (NSString *) key diff --git a/UI/MailerUI/English.lproj/Localizable.strings b/UI/MailerUI/English.lproj/Localizable.strings index 8dcd1c00ac..205567085d 100644 --- a/UI/MailerUI/English.lproj/Localizable.strings +++ b/UI/MailerUI/English.lproj/Localizable.strings @@ -104,7 +104,7 @@ "To" = "To"; "Cc" = "Cc"; "Bcc" = "Bcc"; -"Reply-To" = "Reply-To"; +"Reply-To" = "Reply-To"; "Add address" = "Add address"; "Body" = "Body"; "Open" = "Open"; @@ -121,6 +121,7 @@ "Edit Draft..." = "Edit Draft..."; "Load Images" = "Load Images"; "Return Receipt" = "Return Receipt"; +"Choose which identity to send this message from" = "Choose which identity to send this message from"; "The sender of this message has asked to be notified when you read this message. Do you with to notify the sender?" = "The sender of this message has asked to be notified when you read this message. Do you wish to notify the sender?"; "Return Receipt (displayed) - %@"= "Return Receipt (displayed) - %@"; "This is a Return Receipt for the mail that you sent to %@.\n\nNote: This Return Receipt only acknowledges that the message was displayed on the recipient's computer. There is no guarantee that the recipient has read or understood the message contents." = "This is a Return Receipt for the mail that you sent to %@.\n\nNote: This Return Receipt only acknowledges that the message was displayed on the recipient's computer. There is no guarantee that the recipient has read or understood the message contents."; diff --git a/UI/MailerUI/UIxMailAccountActions.m b/UI/MailerUI/UIxMailAccountActions.m index 49514cbd9b..22080b9da7 100644 --- a/UI/MailerUI/UIxMailAccountActions.m +++ b/UI/MailerUI/UIxMailAccountActions.m @@ -100,7 +100,7 @@ - (WOResponse *) listAllMailboxesAction - (WOResponse *) composeAction { - NSString *value, *signature, *nl; + NSString *value, *signature, *nl, *space; SOGoDraftObject *newDraftMessage; NSMutableDictionary *headers; NSDictionary *data; @@ -108,7 +108,7 @@ - (WOResponse *) composeAction SOGoDraftsFolder *drafts; SOGoUserDefaults *ud; id mailTo; - BOOL save; + BOOL save, isHTML; drafts = [[self clientObject] draftsFolderInContext: context]; newDraftMessage = [drafts newDraft]; @@ -142,9 +142,11 @@ - (WOResponse *) composeAction { ud = [[context activeUser] userDefaults]; [newDraftMessage setIsHTML: [[ud mailComposeMessageType] isEqualToString: @"html"]]; - nl = ([newDraftMessage isHTML] ? @"
" : @"\n"); + isHTML = [newDraftMessage isHTML]; + nl = (isHTML? @"
" : @"\n"); + space = (isHTML ? @" " : @" "); - [newDraftMessage setText: [NSString stringWithFormat: @"%@%@-- %@%@", nl, nl, nl, signature]]; + [newDraftMessage setText: [NSString stringWithFormat: @"%@%@--%@%@%@", nl, nl, space, nl, signature]]; save = YES; } if (save) diff --git a/UI/MailerUI/UIxMailEditor.m b/UI/MailerUI/UIxMailEditor.m index aac35dab2a..a5d5d61bf4 100644 --- a/UI/MailerUI/UIxMailEditor.m +++ b/UI/MailerUI/UIxMailEditor.m @@ -280,7 +280,7 @@ - (NSString *) from } if (!from) { - from = [self _emailFromIdentity: [identities objectAtIndex: 0]]; + from = [self _emailFromIdentity: [[context activeUser] defaultIdentity]]; [from retain]; } } @@ -301,19 +301,11 @@ - (NSString *) replyTo // if ([[[[self clientObject] mailAccountFolder] nameInContainer] intValue] == 0) { - SOGoUserDefaults *ud; - - ud = [[context activeUser] userDefaults]; - value = [ud mailReplyTo]; + value = [[[context activeUser] defaultIdentity] objectForKey: @"replyTo"]; } else { - NSArray *identities; - - identities = [[[self clientObject] mailAccountFolder] identities]; - - if ([identities count]) - value = [[identities objectAtIndex: 0] objectForKey: @"replyTo"]; + value = [[[[self clientObject] mailAccountFolder] defaultIdentity] objectForKey: @"replyTo"]; } return value; @@ -757,11 +749,12 @@ - (BOOL) hasAttachments [self setSourceFolder: [co sourceFolder]]; data = [NSMutableDictionary dictionaryWithObjectsAndKeys: - [self from], @"from", [self localeCode], @"locale", [NSNumber numberWithBool: [self isHTML]], @"isHTML", text, @"text", nil]; + if ((value = [self from])) + [data setObject: value forKey: @"from"]; if ((value = [self replyTo])) [data setObject: value forKey: @"replyTo"]; if ((value = [self to])) diff --git a/UI/PreferencesUI/English.lproj/Localizable.strings b/UI/PreferencesUI/English.lproj/Localizable.strings index 9e7fd8e319..84628e7c41 100644 --- a/UI/PreferencesUI/English.lproj/Localizable.strings +++ b/UI/PreferencesUI/English.lproj/Localizable.strings @@ -212,6 +212,9 @@ "Email" = "Email"; "Reply To Email" = "Reply To Email"; "Signature" = "Signature"; +"Identities" = "Identities"; +"Default Identity" = "Default Identity"; +"New Identity" = "New Identity"; "(Click to create)" = "(Click to create)"; "Please enter your signature below" = "Please enter your signature below"; "Please specify a valid sender address." = "Please specify a valid sender address."; diff --git a/UI/PreferencesUI/UIxJSONPreferences.m b/UI/PreferencesUI/UIxJSONPreferences.m index b058a3ba3c..bff6ed6f2c 100644 --- a/UI/PreferencesUI/UIxJSONPreferences.m +++ b/UI/PreferencesUI/UIxJSONPreferences.m @@ -426,10 +426,13 @@ - (NSString *) jsonDefaults } if (account) [accounts insertObject: account atIndex: 0]; + [values setObject: accounts forKey: @"AuxiliaryMailAccounts"]; + + // Ignore parameters as they are injected in the system mail account ([SOGoUser mailAccounts]) + [values removeObjectForKey: @"SOGoMailIdentities"]; [values removeObjectForKey: @"SOGoMailCertificate"]; [values removeObjectForKey: @"SOGoMailCertificateAlwaysSign"]; [values removeObjectForKey: @"SOGoMailCertificateAlwaysEncrypt"]; - [values setObject: accounts forKey: @"AuxiliaryMailAccounts"]; // Add the domain's default vacation subject if user has not specified a custom subject vacationOptions = [defaults vacationOptions]; diff --git a/UI/PreferencesUI/UIxPreferences.m b/UI/PreferencesUI/UIxPreferences.m index 740ae5e6e2..1206bcf487 100644 --- a/UI/PreferencesUI/UIxPreferences.m +++ b/UI/PreferencesUI/UIxPreferences.m @@ -1110,63 +1110,6 @@ - (BOOL) mailAuxiliaryUserAccountsEnabled return [[user domainDefaults] mailAuxiliaryUserAccountsEnabled]; } -// -// Used internally -// -- (void) _extractMainIdentity: (NSDictionary *) identity - inDictionary: (NSMutableDictionary *) target - -{ - /* We perform some validation here as we have no guaranty on the input - validity. */ - NSString *value; - - if ([identity isKindOfClass: [NSDictionary class]]) - { - value = [identity objectForKey: @"signature"]; - - if (value) - [target setObject: value forKey: @"SOGoMailSignature"]; - else - [target removeObjectForKey: @"SOGoMailSignature"]; - - if (mailCustomFromEnabled) - { - value = [[identity objectForKey: @"email"] - stringByTrimmingSpaces]; - - /* We make sure that the "custom" value is different from the system email */ - if ([value length] == 0 - || [[user systemEmail] isEqualToString: value]) - value = nil; - - if (value) - [target setObject: value forKey: @"SOGoMailCustomEmail"]; - else - [target removeObjectForKey: @"SOGoMailCustomEmail"]; - - value = [[identity objectForKey: @"fullName"] - stringByTrimmingSpaces]; - if ([value length] == 0 - || [[user cn] isEqualToString: value]) - value = nil; - - if (value) - [target setObject: value forKey: @"SOGoMailCustomFullName"]; - else - [target removeObjectForKey: @"SOGoMailCustomFullName"]; - } - - value = [[identity objectForKey: @"replyTo"] - stringByTrimmingSpaces]; - - if (value && [value length] > 0) - [target setObject: value forKey: @"SOGoMailReplyTo"]; - else - [target removeObjectForKey: @"SOGoMailReplyTo"]; - } -} - // // Used internally // @@ -1229,20 +1172,6 @@ - (void) _extractMainSecurityPreferences: (NSDictionary *) security } } -// -// Used internally -// -- (void) _extractMainCustomFrom: (NSDictionary *) account -{ -} - -// -// Used internally -// -- (void) _extractMainReplyTo: (NSDictionary *) account -{ -} - // // Used internally // @@ -1259,7 +1188,7 @@ - (BOOL) _validateAccountIdentities: (NSArray *) identities if (!knownKeys) { knownKeys = [NSArray arrayWithObjects: @"fullName", @"email", - @"signature", @"replyTo", nil]; + @"signature", @"replyTo", @"isDefault", nil]; [knownKeys retain]; } @@ -1367,9 +1296,8 @@ - (void) _extractMainAccountSettings: (NSDictionary *) account if ([account isKindOfClass: [NSDictionary class]]) { identities = [account objectForKey: @"identities"]; - if ([identities isKindOfClass: [NSArray class]] - && [identities count] > 0) - [self _extractMainIdentity: [identities objectAtIndex: 0] inDictionary: target]; + if ([self _validateAccountIdentities: identities]) + [target setObject: identities forKey: @"SOGoMailIdentities"]; [self _extractMainReceiptsPreferences: [account objectForKey: @"receipts"] inDictionary: target]; [self _extractMainSecurityPreferences: [account objectForKey: @"security"] inDictionary: target]; } @@ -1575,10 +1503,6 @@ - (NSString *) forwardEnabled if ([accounts count] > 0) { // The first account is the main system account. The following mapping is required: - // - identities[0].signature => SOGoMailSignature - // - identities[0].email => SOGoMailCustomEmail - // - identities[0].fullName => SOGoMailCustomFullName - // - identities[0].replyTo => SOGoMailReplyTo // - receipts.receiptAction => SOGoMailReceiptAllow // - receipts.receiptNonRecipientAction => SOGoMailReceiptNonRecipientAction // - receipts.receiptOutsideDomainAction => SOGoMailReceiptOutsideDomainAction diff --git a/UI/PreferencesUI/product.plist b/UI/PreferencesUI/product.plist index 8f7fabfe8d..bb1ff8766b 100644 --- a/UI/PreferencesUI/product.plist +++ b/UI/PreferencesUI/product.plist @@ -27,10 +27,6 @@ protectedBy = "View"; pageName = "UIxFilterEditor"; }; - identities = { - protectedBy = "View"; - pageName = "UIxIdentities"; - }; activeExternalSieveScripts = { protectedBy = "View"; pageName = "UIxJSONPreferences"; diff --git a/UI/Templates/MailerUI/UIxMailEditor.wox b/UI/Templates/MailerUI/UIxMailEditor.wox index 2ffb798cc3..fa29a9a1ed 100644 --- a/UI/Templates/MailerUI/UIxMailEditor.wox +++ b/UI/Templates/MailerUI/UIxMailEditor.wox @@ -28,14 +28,35 @@ person - - - - {{identity}} - - -
+ + +
+
+ {{ identity.full }} +
+
+ +
+
+
+
+
This field is required
+
+
diff --git a/UI/Templates/PreferencesUI/UIxAccountEditor.wox b/UI/Templates/PreferencesUI/UIxAccountEditor.wox index f7f22b05e9..e208ca1569 100644 --- a/UI/Templates/PreferencesUI/UIxAccountEditor.wox +++ b/UI/Templates/PreferencesUI/UIxAccountEditor.wox @@ -94,45 +94,100 @@ - - - - - -
- - - - - - - - + +
+ + + +
+
+ person +
+
+
+
+ + + + + +
+
+
+ +
+
+ + delete + + + {{ identity.isDefault ? 'favorite' : 'favorite_border' }} + +
+
+ + + + + + + + {{ address }} + + + + + + + + + + +
+ + +
+
+
+
+ + + +
- - - - - -
- - -
+
diff --git a/UI/WebServerResources/angular-material b/UI/WebServerResources/angular-material index bf5aa3511e..3746148548 160000 --- a/UI/WebServerResources/angular-material +++ b/UI/WebServerResources/angular-material @@ -1 +1 @@ -Subproject commit bf5aa3511ef8f849d54818ce1e2ac37f179f6f23 +Subproject commit 3746148548a304f664c09fcad16ab48a42e7810f diff --git a/UI/WebServerResources/js/Common/sgCkeditor.component.js b/UI/WebServerResources/js/Common/sgCkeditor.component.js index 0a574d5bea..24f22a186c 100644 --- a/UI/WebServerResources/js/Common/sgCkeditor.component.js +++ b/UI/WebServerResources/js/Common/sgCkeditor.component.js @@ -323,6 +323,7 @@ } this.$onDestroy = function () { + editorElement.classList.add('ng-cloak'); editor.destroy(); } @@ -349,12 +350,19 @@ }); } - // vm.ngModelCtrl.$render(); + editorElement.classList.remove('ng-cloak'); + vm.ngModelCtrl.$render(); } function onEditorChange () { var html = editor.getData(); - var text = editor.document.getBody().getText(); + var body = editor.document.getBody(); + var text; + + if (_.isEmpty(body)) + return; + else + text = body.getText(); if (text === '\n') { text = ''; diff --git a/UI/WebServerResources/js/Mailer/Account.service.js b/UI/WebServerResources/js/Mailer/Account.service.js index de214c2004..503c8d7fd4 100644 --- a/UI/WebServerResources/js/Mailer/Account.service.js +++ b/UI/WebServerResources/js/Mailer/Account.service.js @@ -13,10 +13,16 @@ if (typeof futureAccountData.then !== 'function') { angular.extend(this, futureAccountData); _.forEach(this.identities, function(identity) { - if (identity.fullName) + if (identity.fullName && identity.email) identity.full = identity.fullName + ' <' + identity.email + '>'; - else + else if (identity.email) identity.full = '<' + identity.email + '>'; + else + identity.full = ''; + if (identity.signature) { + var element = angular.element('
' + identity.signature + '
'); + identity.textSignature = _.map(element.contents(), 'textContent').join(' ').trim(); + } }); Account.$log.debug('Account: ' + JSON.stringify(futureAccountData, undefined, 2)); } @@ -315,6 +321,27 @@ }); }; + /** + * @function getTextSignature + * @memberof Account.prototype + * @desc Create a plain text representation of the signature for the specified identity index. + * @returns a plain text version of the signature + */ + Account.prototype.getTextSignature = function(index) { + if (index < this.identities.length) { + var identity = this.identities[index]; + if (identity.signature) { + var element = angular.element('
' + identity.signature + '
'); + identity.textSignature = _.map(element.contents(), 'textContent').join(' ').trim(); + } else { + identity.textSignature = ''; + } + return identity.textSignature; + } else { + throw Error('Index of identity is out of range'); + } + }; + /** * @function $certificate * @memberof Account.prototype @@ -451,4 +478,28 @@ }); }; + /** + * @function $omit + * @memberof Account.prototype + * @desc Return a sanitized object used to send to the server. + * @return an object literal copy of the Account instance + */ + Account.prototype.$omit = function () { + var account = {}, identities = []; + + angular.forEach(this, function(value, key) { + if (key != 'constructor' && key !='identities' && key[0] != '$') { + account[key] = angular.copy(value); + } + }); + + _.forEach(this.identities, function (identity) { + if (!identity.isReadOnly) + identities.push(_.pick(identity, ['email', 'fullName', 'replyTo', 'signature', 'isDefault'])); + }); + account.identities = identities; + + return account; + }; + })(); diff --git a/UI/WebServerResources/js/Mailer/MessageEditorController.js b/UI/WebServerResources/js/Mailer/MessageEditorController.js index 7d4cf4e0ff..6b35e65d67 100644 --- a/UI/WebServerResources/js/Mailer/MessageEditorController.js +++ b/UI/WebServerResources/js/Mailer/MessageEditorController.js @@ -12,7 +12,7 @@ this.$onInit = function() { $scope.isPopup = stateParent.isPopup; - this.addRecipient = addRecipient; + this.account = stateAccount; this.autocomplete = {to: {}, cc: {}, bcc: {}}; this.autosave = null; this.autosaveDrafts = autosaveDrafts; @@ -21,7 +21,9 @@ this.isFullscreen = false; this.hideBcc = (stateMessage.editable.bcc.length === 0); this.hideCc = (stateMessage.editable.cc.length === 0); - this.identities = _.uniq(_.map(stateAccount.identities, 'full')); + this.identities = stateAccount.identities; + this.fromIdentity = stateMessage.editable.from; + this.identitySearchText = ''; this.message = stateMessage; this.recipientSeparatorKeys = [ $mdConstant.KEY_CODE.ENTER, @@ -283,11 +285,11 @@ }); } - function addRecipient(contact, field) { + this.addRecipient = function (contact, field) { var recipients, recipient, list, i, address; var emailRE = /([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)/i; - recipients = vm.message.editable[field]; + recipients = this.message.editable[field]; if (angular.isString(contact)) { // Examples that are handled: @@ -351,11 +353,66 @@ return recipient; else return null; - } + }; + + this.setFromIdentity = function (identity) { + var node, children, nl, space, signature, previousIdentity; + + if (identity) + this.message.editable.from = identity.full; + + if (this.composeType == "html") { + nl = '
'; + space = ' '; + } else { + nl = '\n'; + space = ' '; + } + + if (identity && identity.signature) + signature = nl + nl + '--' + space + nl + identity.signature; + else + signature = ''; + + previousIdentity = _.find(this.identities, function (currentIdentity, index) { + if (currentIdentity.signature) { + var currentSignature = new RegExp(nl + ' ?' + nl + '--' + space + nl + currentIdentity.signature); + if (vm.message.editable.text.search(currentSignature) >= 0) { + vm.message.editable.text = vm.message.editable.text.replace(currentSignature, signature); + return true; + } + } + return false; + }); + + if (!previousIdentity && signature.length > 0) { + // Must place signature at proper place + if (!this.isNew() && this.replyPlacement == 'above') { + var quotedMessageIndex = this.message.editable.text.search(new RegExp(nl + '.+?:( ?' + nl + '){2}(> |
= 0) { + this.message.editable.text = + this.message.editable.text.slice(0, quotedMessageIndex) + + signature + + this.message.editable.text.slice(quotedMessageIndex); + } else { + this.message.editable.text = signature + this.message.editable.text; + } + } else { + this.message.editable.text += signature; + } + } + }; + + this.identitySearch = function (query) { + var q = query ? query : ''; + return _.filter(stateAccount.identities, function(identity) { + return identity.full.toLowerCase().indexOf(q.toLowerCase()) >= 0; + }); + }; this.expandGroup = function(contact, field) { var recipients, i, j; - recipients = vm.message.editable[field]; + recipients = this.message.editable[field]; i = recipients.indexOf(contact); recipients.splice(i, 1); for (j = 0; j < contact.members.length; j++) { @@ -391,8 +448,7 @@ if (this.firstFocus) { onCompletePromise().then(function(element) { var textContent = angular.element(textArea).val(), - hasSignature = (Preferences.defaults.SOGoMailSignature && - Preferences.defaults.SOGoMailSignature.length > 0), + hasSignature = /\n-- \n/.test(textContent), signatureLength = 0, sigLimit, caretPosition; @@ -402,8 +458,9 @@ element.find('md-dialog-content')[0].scrollTop = 0; } else { + // Search for signature starting from bottom if (hasSignature) { - sigLimit = textContent.lastIndexOf("--"); + sigLimit = textContent.lastIndexOf("-- "); if (sigLimit > -1) signatureLength = (textContent.length - sigLimit); } @@ -447,7 +504,7 @@ if (x === null) { break; } - if (x.getText() == '--') { + if (/--(%20|%A0|%C2%A0)/.test(encodeURI(x.getText()))) { node = x.getPrevious().getPrevious(); break; } diff --git a/UI/WebServerResources/js/Preferences/AccountDialogController.js b/UI/WebServerResources/js/Preferences/AccountDialogController.js index 7cc764257b..df5a999678 100644 --- a/UI/WebServerResources/js/Preferences/AccountDialogController.js +++ b/UI/WebServerResources/js/Preferences/AccountDialogController.js @@ -7,23 +7,23 @@ /** * @ngInject */ - AccountDialogController.$inject = ['$timeout', '$mdDialog', 'FileUploader', 'Dialog', 'sgSettings', 'Account', 'defaults', 'account', 'accountId', 'mailCustomFromEnabled']; - function AccountDialogController($timeout, $mdDialog, FileUploader, Dialog, Settings, Account, defaults, account, accountId, mailCustomFromEnabled) { - var vm = this, - accountObject = new Account({ id: accountId, security: account.security }); - - vm.defaultPort = 143; - vm.defaults = defaults; - vm.account = account; - vm.accountId = accountId; - vm.customFromIsReadonly = customFromIsReadonly; - vm.onBeforeUploadCertificate = onBeforeUploadCertificate; - vm.removeCertificate = removeCertificate; - vm.importCertificate = importCertificate; - vm.cancel = cancel; - vm.save = save; - vm.hostnameRE = accountId > 0 ? /^(?!(127\.0\.0\.1|localhost(?:\.localdomain)?)$)/ : /./; - vm.ckConfig = { + AccountDialogController.$inject = ['$timeout', '$window', '$mdConstant', '$mdDialog', 'FileUploader', 'Dialog', 'sgSettings', 'defaults', 'account', 'accountId', 'mailCustomFromEnabled']; + function AccountDialogController($timeout, $window, $mdConstant, $mdDialog, FileUploader, Dialog, Settings, defaults, account, accountId, mailCustomFromEnabled) { + var vm = this; + + this.defaultPort = 143; + this.defaults = defaults; + this.account = account; + this.accountId = accountId; + this.hostnameRE = accountId > 0 ? /^(?!(127\.0\.0\.1|localhost(?:\.localdomain)?)$)/ : /./; + this.addressesSearchText = ''; + this.emailSeparatorKeys = [ + $mdConstant.KEY_CODE.ENTER, + $mdConstant.KEY_CODE.TAB, + $mdConstant.KEY_CODE.COMMA, + $mdConstant.KEY_CODE.SEMICOLON + ]; + this.ckConfig = { 'autoGrow_minHeight': 70, 'toolbar': [['Bold', 'Italic', '-', 'Link', 'Font','FontSize','-','TextColor', @@ -31,14 +31,14 @@ language: defaults.LocaleCode }; - if (!vm.account.encryption) - vm.account.encryption = "none"; - else if (vm.account.encryption == "ssl") - vm.defaultPort = 993; + if (!this.account.encryption) + this.account.encryption = "none"; + else if (this.account.encryption == "ssl") + this.defaultPort = 993; _loadCertificate(); - vm.uploader = new FileUploader({ + this.uploader = new FileUploader({ url: [Settings.activeUser('folderURL') + 'Mail', accountId, 'importCertificate'].join('/'), autoUpload: false, queueLimit: 1, @@ -58,9 +58,65 @@ } }); + this.hasIdentities = function () { + return _.filter(this.account.identities, vm.isEditableIdentity).length > 0; + }; + + this.isEditableIdentity = function (identity) { + return !identity.isReadOnly; + }; + + this.selectIdentity = function (index) { + if (this.selectedIdentity == index) { + this.selectedIdentity = null; + } else { + this.selectedIdentity = index; + } + }; + + this.setDefaultIdentity = function ($event, $index) { + _.forEach(this.account.identities, function(identity, i) { + if (i == $index) + identity.isDefault = !identity.isDefault; + else + delete identity.isDefault; + }); + $event.stopPropagation(); + return false; + }; + + this.canRemoveIdentity = function (index) { + return (index == this.selectedIdentity) && (this.account.identities.length > 1); + }; + + this.removeIdentity = function (index) { + this.account.identities.splice(index, 1); + this.selectedIdentity = null; + }; + + this.addIdentity = function () { + var firstReadonlyIndex = _.findIndex(this.account.identities, { isReadOnly: 1 }); + var identity = {}; + + if (this.customFromIsReadonly()) + identity.fullName = this.account.identities[0].fullName; + this.account.identities.splice(Math.max(firstReadonlyIndex, 0), 0, identity); + this.selectedIdentity = firstReadonlyIndex; + }; + + this.showCkEditor = function ($index) { + return this.selectedIdentity == $index && this.defaults.SOGoMailComposeMessageType == 'html'; + }; + + this.filterEmailAddresses = function ($query) { + return _.filter($window.defaultEmailAddresses, function (address) { + return address.toLowerCase().indexOf($query.toLowerCase()) >= 0; + }); + }; + function _loadCertificate() { if (vm.account.security && vm.account.security.hasCertificate) - accountObject.$certificate().then(function(crt) { + vm.account.$certificate().then(function(crt) { vm.certificate = crt; }, function() { delete vm.account.security.hasCertificate; @@ -73,35 +129,33 @@ return isP12File; } - function customFromIsReadonly() { + this.customFromIsReadonly = function () { if (accountId > 0) return false; return !mailCustomFromEnabled; - } + }; - function importCertificate() { - vm.uploader.queue[0].formData = [{ password: vm.certificatePassword }]; - vm.uploader.uploadItem(0); - } + this.importCertificate = function () { + this.uploader.queue[0].formData = [{ password: this.certificatePassword }]; + this.uploader.uploadItem(0); + }; - function onBeforeUploadCertificate(form) { - vm.form = form; - vm.uploader.clearQueue(); - } + this.onBeforeUploadCertificate = function (form) { + this.form = form; + this.uploader.clearQueue(); + }; - function removeCertificate() { - accountObject.$removeCertificate().then(function() { - delete vm.account.security.hasCertificate; - }); - } + this.removeCertificate = function () { + this.account.$removeCertificate(); + }; - function cancel() { + this.cancel = function () { $mdDialog.cancel(); - } + }; - function save() { + this.save = function () { $mdDialog.hide(); - } + }; } angular diff --git a/UI/WebServerResources/js/Preferences/PreferencesController.js b/UI/WebServerResources/js/Preferences/PreferencesController.js index 4990e84d28..eebf1e8cc6 100644 --- a/UI/WebServerResources/js/Preferences/PreferencesController.js +++ b/UI/WebServerResources/js/Preferences/PreferencesController.js @@ -98,28 +98,25 @@ }; this.addMailAccount = function(ev, form) { - var account; - - this.preferences.defaults.AuxiliaryMailAccounts.push({}); - - account = _.last(this.preferences.defaults.AuxiliaryMailAccounts); - angular.extend(account, - { - isNew: true, - name: "", - identities: [ - { - fullName: "", - email: "" - } - ], - receipts: { - receiptAction: "ignore", - receiptNonRecipientAction: "ignore", - receiptOutsideDomainAction: "ignore", - receiptAnyAction: "ignore" - } - }); + var account, index; + + account = new Account({ + isNew: true, + name: "", + identities: [ + { + fullName: "", + email: "" + } + ], + receipts: { + receiptAction: "ignore", + receiptNonRecipientAction: "ignore", + receiptOutsideDomainAction: "ignore", + receiptAnyAction: "ignore" + } + }); + index = this.preferences.defaults.AuxiliaryMailAccounts.length; $mdDialog.show({ controller: 'AccountDialogController', @@ -133,14 +130,13 @@ mailCustomFromEnabled: $window.mailCustomFromEnabled } }).then(function() { + vm.preferences.defaults.AuxiliaryMailAccounts.push(account.$omit()); form.$setDirty(); - }).catch(function() { - vm.preferences.defaults.AuxiliaryMailAccounts.pop(); }); }; this.editMailAccount = function(event, index, form) { - var account = this.preferences.defaults.AuxiliaryMailAccounts[index]; + var account = new Account(this.preferences.defaults.AuxiliaryMailAccounts[index]); $mdDialog.show({ controller: 'AccountDialogController', controllerAs: '$AccountDialogController', @@ -153,10 +149,8 @@ mailCustomFromEnabled: $window.mailCustomFromEnabled } }).then(function() { - vm.preferences.defaults.AuxiliaryMailAccounts[index] = account; + vm.preferences.defaults.AuxiliaryMailAccounts[index] = account.$omit(); form.$setDirty(); - }, function() { - // Cancel }); }; diff --git a/UI/WebServerResources/scss/components/autocomplete/autocomplete.scss b/UI/WebServerResources/scss/components/autocomplete/autocomplete.scss index e627b1b4b7..361b6abdc1 100644 --- a/UI/WebServerResources/scss/components/autocomplete/autocomplete.scss +++ b/UI/WebServerResources/scss/components/autocomplete/autocomplete.scss @@ -80,6 +80,11 @@ $list-item-dense-line-height: 1.05 !default; } } +md-autocomplete .ng-invalid:not(.ng-empty) { + text-decoration: underline; + color: $colorRedA700 !important; +} + @media (max-width: $layout-breakpoint-xs) { // Enlarge the autocompletion menu on small devices to fit the entire screen .md-autocomplete-suggestions-container {