Merge pull request #279 from inverse-inc/feature/mail-identities
feat(mail): handle multiple mail identitiespull/282/head
commit
f246d88a5c
|
@ -995,7 +995,7 @@ static NSString *userAgent = nil;
|
|||
- (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail
|
||||
{
|
||||
NSDictionary *info, *attachment;
|
||||
NSString *signature, *nl;
|
||||
NSString *signature, *nl, *space;
|
||||
SOGoUserDefaults *ud;
|
||||
|
||||
[sourceMail fetchCoreInfos];
|
||||
|
@ -1031,8 +1031,9 @@ static NSString *userAgent = nil;
|
|||
signature = [[self mailAccountFolder] signature];
|
||||
if ([signature length])
|
||||
{
|
||||
nl = (isHTML ? @"<br/>" : @"\n");
|
||||
[self setText: [NSString stringWithFormat: @"%@%@-- %@%@", nl, nl, nl, signature]];
|
||||
nl = (isHTML ? @"<br />" : @"\n");
|
||||
space = (isHTML ? @" " : @" ");
|
||||
[self setText: [NSString stringWithFormat: @"%@%@--%@%@%@", nl, nl, space, nl, signature]];
|
||||
}
|
||||
attachment = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[sourceMail filenameForForward], @"filename",
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 @@ static NSString *inboxFolderName = @"INBOX";
|
|||
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;
|
||||
|
||||
|
|
|
@ -234,14 +234,15 @@
|
|||
|
||||
- (NSString *) signature
|
||||
{
|
||||
NSString *signature, *mailSignature, *nl;
|
||||
NSString *signature, *mailSignature, *nl, *space;
|
||||
|
||||
signature = [[sourceMail mailAccountFolder] signature];
|
||||
|
||||
if ([signature length])
|
||||
{
|
||||
nl = (htmlComposition ? @"<br/>" : @"\n");
|
||||
mailSignature = [NSString stringWithFormat: @"-- %@%@", nl, signature];
|
||||
nl = (htmlComposition ? @"<br />" : @"\n");
|
||||
space = (htmlComposition ? @" " : @" ");
|
||||
mailSignature = [NSString stringWithFormat: @"--%@%@%@", space, nl, signature];
|
||||
}
|
||||
else
|
||||
mailSignature = @"";
|
||||
|
|
|
@ -342,18 +342,34 @@
|
|||
|
||||
- (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) _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 @@
|
|||
}
|
||||
|
||||
// 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])
|
||||
{
|
||||
[self userDefaults];
|
||||
customEmail = [_defaults mailCustomEmail];
|
||||
fullName = [_defaults mailCustomFullName];
|
||||
if ([customEmail length] > 0 || [fullName length] > 0)
|
||||
{
|
||||
if ([customEmail length] == 0)
|
||||
customEmail = [mails objectAtIndex: 0];
|
||||
else if ([fullName length] == 0)
|
||||
{
|
||||
// Custom email but default fullname; if the custom email is
|
||||
// one of the user's emails, remove the duplicated entry
|
||||
index = [mails indexOfObject: customEmail];
|
||||
if (index != NSNotFound)
|
||||
{
|
||||
[mails removeObjectAtIndex: index];
|
||||
max--;
|
||||
}
|
||||
}
|
||||
|
||||
if ([fullName length] == 0)
|
||||
{
|
||||
fullName = [self cn];
|
||||
if ([fullName length] == 0)
|
||||
fullName = login;
|
||||
}
|
||||
|
||||
identity = [NSMutableDictionary new];
|
||||
[identity setObject: customEmail forKey: @"email"];
|
||||
[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];
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize identities
|
||||
for (count = 0; count < max; 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"];
|
||||
[identities addObject: identity];
|
||||
|
||||
if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame)
|
||||
default_identity = [identities count]-1;
|
||||
|
||||
[identity release];
|
||||
identity = [NSMutableDictionary dictionaryWithDictionary: [identities objectAtIndex: count]];
|
||||
customEmail = [identity objectForKey: @"email"];
|
||||
if ([customEmail length])
|
||||
{
|
||||
if (![[self domainDefaults] mailCustomFromEnabled])
|
||||
{
|
||||
// No custom from -- enforce a valid email
|
||||
index = [mails indexOfObject: customEmail];
|
||||
if (index == NSNotFound)
|
||||
{
|
||||
[identity setObject: [self systemEmail] 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"];
|
||||
[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];
|
||||
}
|
||||
|
||||
if (![identities count])
|
||||
{
|
||||
// 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];
|
||||
}
|
||||
[[identities objectAtIndex: default_identity] setObject: [NSNumber numberWithBool: YES]
|
||||
forKey: @"isDefault"];
|
||||
|
||||
/* identities from delegators */
|
||||
if (appendDeletegatedIdentities)
|
||||
|
@ -980,11 +983,19 @@
|
|||
|
||||
- (NSDictionary *) primaryIdentity
|
||||
{
|
||||
NSDictionary *defaultAccount;
|
||||
NSArray *identities;
|
||||
NSDictionary *defaultIdentity, *defaultAccount;
|
||||
|
||||
defaultAccount = [[self mailAccounts] objectAtIndex: 0];
|
||||
defaultIdentity = [self defaultIdentity];
|
||||
|
||||
return [[defaultAccount objectForKey: @"identities"] objectAtIndex: 0];
|
||||
if (!defaultIdentity && [[self mailAccounts] count])
|
||||
{
|
||||
defaultAccount = [[self mailAccounts] objectAtIndex: 0];
|
||||
identities = [defaultAccount objectForKey: @"identities"];
|
||||
defaultIdentity = [identities objectAtIndex: 0];
|
||||
}
|
||||
|
||||
return defaultIdentity;
|
||||
}
|
||||
|
||||
/* folders */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGImap4/NSString+Imap4.h>
|
||||
#import <NGObjWeb/WOApplication.h>
|
||||
|
@ -114,33 +115,55 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
|||
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)
|
||||
{
|
||||
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"];
|
||||
}
|
||||
}
|
||||
[self removeObjectForKey: @"MailAccounts"];
|
||||
rc = NO;
|
||||
}
|
||||
else
|
||||
rc = NO;
|
||||
{
|
||||
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])
|
||||
{
|
||||
[identity setObject: [NSNumber numberWithBool: YES] forKey: @"isDefault"];
|
||||
[self setMailIdentities: [NSArray arrayWithObject: identity]];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -211,7 +234,8 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
|||
/* 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 *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
|||
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 *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
|||
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 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
|||
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"];
|
||||
|
|
|
@ -674,18 +674,18 @@
|
|||
return [NSString stringWithFormat: @"urn:uuid:%@", nameInContainer];
|
||||
}
|
||||
|
||||
- (NSException *) setDavSignature: (NSString *) newSignature
|
||||
{
|
||||
SOGoUserDefaults *ud;
|
||||
SOGoUser *user;
|
||||
// - (NSException *) setDavSignature: (NSString *) newSignature
|
||||
// {
|
||||
// SOGoUserDefaults *ud;
|
||||
// SOGoUser *user;
|
||||
|
||||
user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
|
||||
ud = [user userDefaults];
|
||||
[ud setMailSignature: newSignature];
|
||||
[ud synchronize];
|
||||
// user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
|
||||
// ud = [user userDefaults];
|
||||
// [ud setMailSignature: newSignature];
|
||||
// [ud synchronize];
|
||||
|
||||
return nil;
|
||||
}
|
||||
// return nil;
|
||||
// }
|
||||
|
||||
#warning unused stub
|
||||
- (BOOL) collectionDavKey: (NSString *) key
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
|
||||
- (WOResponse *) composeAction
|
||||
{
|
||||
NSString *value, *signature, *nl;
|
||||
NSString *value, *signature, *nl, *space;
|
||||
SOGoDraftObject *newDraftMessage;
|
||||
NSMutableDictionary *headers;
|
||||
NSDictionary *data;
|
||||
|
@ -108,7 +108,7 @@
|
|||
SOGoDraftsFolder *drafts;
|
||||
SOGoUserDefaults *ud;
|
||||
id mailTo;
|
||||
BOOL save;
|
||||
BOOL save, isHTML;
|
||||
|
||||
drafts = [[self clientObject] draftsFolderInContext: context];
|
||||
newDraftMessage = [drafts newDraft];
|
||||
|
@ -142,9 +142,11 @@
|
|||
{
|
||||
ud = [[context activeUser] userDefaults];
|
||||
[newDraftMessage setIsHTML: [[ud mailComposeMessageType] isEqualToString: @"html"]];
|
||||
nl = ([newDraftMessage isHTML] ? @"<br/>" : @"\n");
|
||||
isHTML = [newDraftMessage isHTML];
|
||||
nl = (isHTML? @"<br />" : @"\n");
|
||||
space = (isHTML ? @" " : @" ");
|
||||
|
||||
[newDraftMessage setText: [NSString stringWithFormat: @"%@%@-- %@%@", nl, nl, nl, signature]];
|
||||
[newDraftMessage setText: [NSString stringWithFormat: @"%@%@--%@%@%@", nl, nl, space, nl, signature]];
|
||||
save = YES;
|
||||
}
|
||||
if (save)
|
||||
|
|
|
@ -280,7 +280,7 @@ static NSArray *infoKeys = nil;
|
|||
}
|
||||
if (!from)
|
||||
{
|
||||
from = [self _emailFromIdentity: [identities objectAtIndex: 0]];
|
||||
from = [self _emailFromIdentity: [[context activeUser] defaultIdentity]];
|
||||
[from retain];
|
||||
}
|
||||
}
|
||||
|
@ -301,19 +301,11 @@ static NSArray *infoKeys = nil;
|
|||
//
|
||||
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 @@ static NSArray *infoKeys = nil;
|
|||
[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]))
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -426,10 +426,13 @@ static SoProduct *preferencesProduct = nil;
|
|||
}
|
||||
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];
|
||||
|
|
|
@ -1110,63 +1110,6 @@ static NSArray *reminderValues = nil;
|
|||
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 @@ static NSArray *reminderValues = nil;
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Used internally
|
||||
//
|
||||
- (void) _extractMainCustomFrom: (NSDictionary *) account
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// Used internally
|
||||
//
|
||||
- (void) _extractMainReplyTo: (NSDictionary *) account
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// Used internally
|
||||
//
|
||||
|
@ -1259,7 +1188,7 @@ static NSArray *reminderValues = nil;
|
|||
if (!knownKeys)
|
||||
{
|
||||
knownKeys = [NSArray arrayWithObjects: @"fullName", @"email",
|
||||
@"signature", @"replyTo", nil];
|
||||
@"signature", @"replyTo", @"isDefault", nil];
|
||||
[knownKeys retain];
|
||||
}
|
||||
|
||||
|
@ -1367,9 +1296,8 @@ static NSArray *reminderValues = nil;
|
|||
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 @@ static NSArray *reminderValues = nil;
|
|||
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
|
||||
|
|
|
@ -27,10 +27,6 @@
|
|||
protectedBy = "View";
|
||||
pageName = "UIxFilterEditor";
|
||||
};
|
||||
identities = {
|
||||
protectedBy = "View";
|
||||
pageName = "UIxIdentities";
|
||||
};
|
||||
activeExternalSieveScripts = {
|
||||
protectedBy = "View";
|
||||
pageName = "UIxJSONPreferences";
|
||||
|
|
|
@ -28,14 +28,35 @@
|
|||
<sg-avatar-image class="hide-xs"
|
||||
sg-email="editor.message.editable.from"
|
||||
size="32">person</sg-avatar-image>
|
||||
<md-input-container class="sg-no-wrap">
|
||||
<label><var:string label:value="From"/></label>
|
||||
<md-select name="from"
|
||||
ng-model="editor.message.editable.from">
|
||||
<md-option ng-value="identity" ng-repeat="identity in editor.identities track by $index">{{identity}}</md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
<div flex="flex"><!-- spacer --></div>
|
||||
<md-autocomplete
|
||||
class="md-margin md-default-theme md-bg md-hue-1" flex="flex"
|
||||
required="required"
|
||||
md-input-name="from"
|
||||
md-require-match="true"
|
||||
md-selected-item="editor.fromIdentity"
|
||||
md-search-text="editor.identitySearchText"
|
||||
md-selected-item-change="editor.setFromIdentity(identity)"
|
||||
md-items="identity in editor.identitySearch(editor.identitySearchText)"
|
||||
md-item-text="identity.full"
|
||||
md-min-length="0"
|
||||
md-escape-options="clear"
|
||||
label:placeholder="Choose which identity to send this message from"
|
||||
md-menu-class="md-2-line">
|
||||
<md-item-template>
|
||||
<div class="sg-tile-content">
|
||||
<div class="sg-md-subhead">
|
||||
<span md-highlight-text="editor.identitySearchText"
|
||||
md-highlight-flags="gi">{{ identity.full }}</span>
|
||||
</div>
|
||||
<div class="sg-md-body">
|
||||
<i ng-bind-html="editor.account.getTextSignature($index)"><!-- signature --></i>
|
||||
</div>
|
||||
</div>
|
||||
</md-item-template>
|
||||
<div ng-messages="messageForm.from.$error">
|
||||
<div ng-message="required">This field is required</div>
|
||||
</div>
|
||||
</md-autocomplete>
|
||||
<md-button class="sg-icon-button" ng-click="editor.send()"
|
||||
ng-disabled="!(editor.message.editable.to.length > 0 || editor.message.editable.cc.length > 0 || editor.message.editable.bcc.length > 0) || editor.uploader.isUploading || messageForm.$invalid || messageForm.$submitted"
|
||||
sg-ripple-click="mailEditor">
|
||||
|
|
|
@ -94,45 +94,100 @@
|
|||
</md-input-container>
|
||||
</div>
|
||||
|
||||
<md-input-container class="md-block md-flex">
|
||||
<label><var:string label:value="Full Name"/></label>
|
||||
<input type="text" required="required"
|
||||
ng-disabled="$AccountDialogController.customFromIsReadonly()"
|
||||
ng-model="$AccountDialogController.account.identities[0].fullName"/>
|
||||
</md-input-container>
|
||||
|
||||
<div layout="row">
|
||||
<md-input-container class="md-block" flex="50">
|
||||
<label><var:string label:value="Email"/></label>
|
||||
<input type="email" required="required"
|
||||
ng-disabled="$AccountDialogController.customFromIsReadonly()"
|
||||
ng-model="$AccountDialogController.account.identities[0].email"/>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block" flex="50"
|
||||
ng-hide="$AccountDialogController.customFromIsReadonly()">
|
||||
<label><var:string label:value="Reply To Email"/></label>
|
||||
<input type="email"
|
||||
autocomplete="off"
|
||||
ng-model="$AccountDialogController.account.identities[0].replyTo"/>
|
||||
</md-input-container>
|
||||
<!-- identities -->
|
||||
<div class="pseudo-input-container" ng-if="$AccountDialogController.hasIdentities()">
|
||||
<label class="pseudo-input-label"><var:string label:value="Identities"/></label>
|
||||
<md-card ng-repeat="identity in $AccountDialogController.account.identities | filter:$AccountDialogController.isEditableIdentity"
|
||||
class="sg-collapsed"
|
||||
ng-class="{ 'sg-expanded': $index == $AccountDialogController.selectedIdentity }">
|
||||
<a class="md-flex md-button" ng-click="$AccountDialogController.selectIdentity($index)">
|
||||
<div layout="row" layout-align="start center">
|
||||
<div class="card-picture">
|
||||
<sg-avatar-image class="md-avatar"
|
||||
sg-email="identity.email"
|
||||
size="40">person</sg-avatar-image>
|
||||
</div>
|
||||
<div class="sg-tile-content">
|
||||
<div class="sg-md-subhead">
|
||||
<div>
|
||||
<span ng-bind="identity.fullName"><!-- fullName --></span>
|
||||
<span ng-show="identity.email">
|
||||
<var:entity const:name="nbsp"/>
|
||||
<var:entity const:name="lt"/><span ng-bind="identity.email"><!-- email --></span><var:entity const:name="gt"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sg-md-body">
|
||||
<i ng-bind-html="$AccountDialogController.account.getTextSignature($index)"><!-- signature --></i>
|
||||
</div>
|
||||
</div>
|
||||
<md-button class="sg-icon-button" type="button"
|
||||
ng-click="$AccountDialogController.removeIdentity($index)"
|
||||
ng-show="$AccountDialogController.canRemoveIdentity($index)">
|
||||
<md-icon>delete</md-icon>
|
||||
</md-button>
|
||||
<md-button class="sg-icon-button"
|
||||
label:aria-label="Default Identity"
|
||||
ng-click="$AccountDialogController.setDefaultIdentity($event, $index)">
|
||||
<md-icon ng-class="{ 'md-warn md-hue-2': identity.isDefault }">{{ identity.isDefault ? 'favorite' : 'favorite_border' }}</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</a>
|
||||
<md-card-content ng-show="$index == $AccountDialogController.selectedIdentity">
|
||||
<md-input-container class="md-block md-flex">
|
||||
<label><var:string label:value="Full Name"/></label>
|
||||
<input type="text" required="required"
|
||||
ng-disabled="$AccountDialogController.customFromIsReadonly()"
|
||||
ng-model="identity.fullName"/>
|
||||
</md-input-container>
|
||||
<md-autocomplete
|
||||
class="md-block" required="required"
|
||||
md-no-cache="true"
|
||||
md-search-text="identity.email"
|
||||
md-items="address in $AccountDialogController.filterEmailAddresses(identity.email)"
|
||||
md-escape-options="clear"
|
||||
md-require-match="$AccountDialogController.customFromIsReadonly()"
|
||||
md-min-length="0"
|
||||
label:md-floating-label="Email">
|
||||
<md-item-template>
|
||||
<span md-highlight-text="identity.email"
|
||||
md-highlight-flags="gi">{{ address }}</span>
|
||||
</md-item-template>
|
||||
</md-autocomplete>
|
||||
<md-input-container class="md-block"
|
||||
ng-hide="$AccountDialogController.customFromIsReadonly()">
|
||||
<label><var:string label:value="Reply To Email"/></label>
|
||||
<input type="email"
|
||||
autocomplete="off"
|
||||
ng-model="identity.replyTo"/>
|
||||
</md-input-container>
|
||||
<md-input-container
|
||||
class="md-block md-flex"
|
||||
ng-if="$AccountDialogController.defaults.SOGoMailComposeMessageType == 'text'">
|
||||
<label><var:string label:value="Signature"/></label>
|
||||
<textarea ng-model="identity.signature"><!-- signature --></textarea>
|
||||
</md-input-container>
|
||||
<div class="pseudo-input-container"
|
||||
ng-if="$AccountDialogController.defaults.SOGoMailComposeMessageType == 'html'">
|
||||
<label class="pseudo-input-label"><var:string label:value="Signature"/></label>
|
||||
<sg-ckeditor
|
||||
class="ng-cloak"
|
||||
config="$AccountDialogController.ckConfig"
|
||||
ck-margin="8px"
|
||||
ng-model="identity.signature"><!-- HTML editor --></sg-ckeditor>
|
||||
</div>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
<div layout="row" layout-align="end center">
|
||||
<md-button type="button"
|
||||
ng-click="$AccountDialogController.addIdentity()"
|
||||
label:aria-label="New Identity">
|
||||
<var:string label:value="New Identity"/>
|
||||
</md-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- To switch between a simple text editor and the CK/HTML editor, we use a ng-if and not
|
||||
a ng-class as it doesn't get initialized by the ckEditor class directive -->
|
||||
<md-input-container class="md-block md-flex"
|
||||
ng-if="$AccountDialogController.defaults.SOGoMailComposeMessageType == 'text'">
|
||||
<label><var:string label:value="Signature"/></label>
|
||||
<textarea
|
||||
ng-model="$AccountDialogController.account.identities[0].signature"><!-- plain text editor --></textarea>
|
||||
</md-input-container>
|
||||
<div class="pseudo-input-container"
|
||||
ng-if="$AccountDialogController.defaults.SOGoMailComposeMessageType == 'html'">
|
||||
<label class="pseudo-input-label"><var:string label:value="Signature"/></label>
|
||||
<sg-ckeditor
|
||||
config="$AccountDialogController.ckConfig"
|
||||
ck-margin="8px"
|
||||
ng-model="$AccountDialogController.account.identities[0].signature"><!-- HTML editor --></sg-ckeditor>
|
||||
</div>
|
||||
<div class="sg-padded--bottom"><!-- spacer --></div>
|
||||
|
||||
<md-input-container class="md-block md-input-has-value">
|
||||
<label><var:string label:value="When I receive a request for a return receipt"/></label>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit bf5aa3511ef8f849d54818ce1e2ac37f179f6f23
|
||||
Subproject commit 3746148548a304f664c09fcad16ab48a42e7810f
|
|
@ -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 = '';
|
||||
|
|
|
@ -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('<div>' + identity.signature + '</div>');
|
||||
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('<div>' + identity.signature + '</div>');
|
||||
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;
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
|
@ -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 = '<br />';
|
||||
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}(> |<blockquote type="cite")'));
|
||||
if (quotedMessageIndex >= 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;
|
||||
}
|
||||
|
|
|
@ -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 });
|
||||
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;
|
||||
|
||||
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 = {
|
||||
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
|
||||
|
|
|
@ -98,28 +98,25 @@
|
|||
};
|
||||
|
||||
this.addMailAccount = function(ev, form) {
|
||||
var account;
|
||||
var account, index;
|
||||
|
||||
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"
|
||||
}
|
||||
});
|
||||
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
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue