Expose all email addresses in autocompletion

This change is immediately applicable to LDAP-based system address
books. However, personal SQL-based address books must have their quick
tables recreated. To do so, one must use sogo-tool to backup and restore
the user's data.

Resolves #3443, #3526
pull/210/head
Francis Lachapelle 2016-05-25 10:25:53 -04:00
parent 6dc80e1159
commit fd4b09428f
6 changed files with 103 additions and 28 deletions

3
NEWS
View File

@ -1,6 +1,9 @@
3.1.1 (2016-MM-DD) 3.1.1 (2016-MM-DD)
------------------ ------------------
Enhancements
- [web] expose all email addresses in autocompletion of message editor (#3443)
Bug fixes Bug fixes
- [web] fixed creation of chip on blur (sgTransformOnBlur directive) - [web] fixed creation of chip on blur (sgTransformOnBlur directive)
- [web] fixed composition of new messages from Contacts module - [web] fixed composition of new messages from Contacts module

View File

@ -45,7 +45,7 @@ objectclass ( 2.5.6.7 NAME 'organizationalPerson'
SUP person STRUCTURAL SUP person STRUCTURAL
MAY ( title $ x121Address $ registeredAddress $ destinationIndicator $ MAY ( title $ x121Address $ registeredAddress $ destinationIndicator $
preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
telephoneNumber $ internationaliSDNNumber $ telephoneNumber $ internationaliSDNNumber $
facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $
postalAddress $ physicalDeliveryOfficeName $ ou $ st $ l ) ) postalAddress $ physicalDeliveryOfficeName $ ou $ st $ l ) )
@ -64,10 +64,10 @@ objectclass ( 2.16.840.1.113730.3.2.2
userSMIMECertificate $ userPKCS12 ) userSMIMECertificate $ userPKCS12 )
) )
objectclass ( 1.3.6.1.4.1.13769.9.1 NAME 'mozillaAbPersonAlpha' objectclass ( 1.3.6.1.4.1.13769.9.1 NAME 'mozillaAbPersonAlpha'
SUP top AUXILIARY SUP top AUXILIARY
MUST ( cn ) MUST ( cn )
MAY( c $ MAY( c $
description $ description $
displayName $ displayName $
facsimileTelephoneNumber $ facsimileTelephoneNumber $
@ -257,7 +257,7 @@ convention:
given: [ldifRecord objectForKey: @"givenname"] given: [ldifRecord objectForKey: @"givenname"]
additional: nil prefixes: nil suffixes: nil]; additional: nil prefixes: nil suffixes: nil];
[self setNickname: [ldifRecord objectForKey: @"mozillanickname"]]; [self setNickname: [ldifRecord objectForKey: @"mozillanickname"]];
[self setTitle: [ldifRecord objectForKey: @"title"]]; [self setTitle: [ldifRecord objectForKey: @"title"]];
fn = [ldifRecord objectForKey: @"displayname"]; fn = [ldifRecord objectForKey: @"displayname"];
if (!fn) if (!fn)
@ -294,7 +294,7 @@ convention:
ou = [ldifRecord objectForKey: @"ou"]; ou = [ldifRecord objectForKey: @"ou"];
if ([ou isKindOfClass: [NSArray class]]) if ([ou isKindOfClass: [NSArray class]])
units = [NSArray arrayWithArray: ou]; units = [NSArray arrayWithArray: (NSArray *)ou];
else if (ou) else if (ou)
units = [NSArray arrayWithObject: ou]; units = [NSArray arrayWithObject: ou];
else else
@ -308,7 +308,7 @@ convention:
setSingleValue: [ldifRecord objectForKey: @"mozillahomeurl"] forKey: @""]; setSingleValue: [ldifRecord objectForKey: @"mozillahomeurl"] forKey: @""];
[[self elementWithTag: @"url" ofType: @"work"] [[self elementWithTag: @"url" ofType: @"work"]
setSingleValue: [ldifRecord objectForKey: @"mozillaworkurl"] forKey: @""]; setSingleValue: [ldifRecord objectForKey: @"mozillaworkurl"] forKey: @""];
[[self uniqueChildWithTag: @"x-aim"] [[self uniqueChildWithTag: @"x-aim"]
setSingleValue: [ldifRecord objectForKey: @"nsaimid"] setSingleValue: [ldifRecord objectForKey: @"nsaimid"]
forKey: @""]; forKey: @""];
@ -343,7 +343,7 @@ convention:
o = [ldifRecord objectForKey: @"vcardcategories"]; o = [ldifRecord objectForKey: @"vcardcategories"];
// We can either have an array (from SOGo's web gui) or a // We can either have an array (from SOGo's web gui) or a
// string (from a LDIF import) as the value here. // string (from a LDIF import) as the value here.
if ([o isKindOfClass: [NSArray class]]) if ([o isKindOfClass: [NSArray class]])
[self setCategories: o]; [self setCategories: o];
@ -356,7 +356,7 @@ convention:
/* VCARD -> LDIF */ /* VCARD -> LDIF */
- (NSString *) _simpleValueForType: (NSString *) aType - (NSString *) _simpleValueForType: (NSString *) aType
inArray: (NSArray *) anArray inArray: (NSArray *) anArray
excluding: (NSString *) aTypeToExclude excluding: (NSString *) aTypeToExclude
{ {
NSArray *elements; NSArray *elements;
NSString *value; NSString *value;
@ -377,7 +377,7 @@ convention:
if (!aTypeToExclude) if (!aTypeToExclude)
break; break;
if (![ce hasAttribute: @"type" havingValue: aTypeToExclude]) if (![ce hasAttribute: @"type" havingValue: aTypeToExclude])
break; break;
@ -609,8 +609,8 @@ convention:
to: [self _simpleValueForType: @"home" inArray: elements to: [self _simpleValueForType: @"home" inArray: elements
excluding: nil] excluding: nil]
inLDIFRecord: ldifRecord]; inLDIFRecord: ldifRecord];
// If we don't have a "work" or "home" URL but we still have // If we don't have a "work" or "home" URL but we still have
// an URL field present, let's add it to the "home" value // an URL field present, let's add it to the "home" value
if ([[ldifRecord objectForKey: @"mozillaworkurl"] length] == 0 && if ([[ldifRecord objectForKey: @"mozillaworkurl"] length] == 0 &&
[[ldifRecord objectForKey: @"mozillahomeurl"] length] == 0 && [[ldifRecord objectForKey: @"mozillahomeurl"] length] == 0 &&
@ -640,7 +640,7 @@ convention:
} }
} }
} }
[self _setValue: @"title" to: [self title] inLDIFRecord: ldifRecord]; [self _setValue: @"title" to: [self title] inLDIFRecord: ldifRecord];
[self _setupOrgFieldsInLDIFRecord: ldifRecord]; [self _setupOrgFieldsInLDIFRecord: ldifRecord];
@ -683,7 +683,7 @@ convention:
{ {
CardElement *org; CardElement *org;
NSString *company; NSString *company;
org = [self org]; org = [self org];
company = [org flattenedValueAtIndex: 0 forKey: @""]; company = [org flattenedValueAtIndex: 0 forKey: @""];
if ([company length] == 0) if ([company length] == 0)
@ -696,7 +696,7 @@ convention:
{ {
CardElement *n; CardElement *n;
NSString *fn, *firstName, *lastName, *org; NSString *fn, *firstName, *lastName, *org;
fn = [self fn]; fn = [self fn];
if ([fn length] == 0) if ([fn length] == 0)
{ {
@ -723,6 +723,29 @@ convention:
return fn; return fn;
} }
- (NSArray *) emails
{
NSArray *elements;
NSMutableArray *emails;
NSString *email;
int i;
emails = [NSMutableArray array];
elements = [self childrenWithTag: @"email"];
for (i = [elements count]-1; i >= 0; i--)
{
email = [[elements objectAtIndex: i] flattenedValuesForKey: @""];
if ([email caseInsensitiveCompare: [self preferredEMail]] == NSOrderedSame)
// First element of array is the preferred email address
[emails insertObject: email atIndex: 0];
else
[emails addObject: email];
}
return emails;
}
- (NSArray *) secondaryEmails - (NSArray *) secondaryEmails
{ {
NSMutableArray *emails; NSMutableArray *emails;
@ -735,11 +758,11 @@ convention:
[emails removeObjectsInArray: [self childrenWithTag: @"email" [emails removeObjectsInArray: [self childrenWithTag: @"email"
andAttribute: @"type" andAttribute: @"type"
havingValue: @"pref"]]; havingValue: @"pref"]];
for (i = [emails count]-1; i >= 0; i--) for (i = [emails count]-1; i >= 0; i--)
{ {
email = [[emails objectAtIndex: i] flattenedValuesForKey: @""]; email = [[emails objectAtIndex: i] flattenedValuesForKey: @""];
if ([email caseInsensitiveCompare: [self preferredEMail]] == NSOrderedSame) if ([email caseInsensitiveCompare: [self preferredEMail]] == NSOrderedSame)
[emails removeObjectAtIndex: i]; [emails removeObjectAtIndex: i];
} }
@ -771,7 +794,7 @@ convention:
if (!aTypeToExclude) if (!aTypeToExclude)
break; break;
if (![ce hasAttribute: @"type" havingValue: aTypeToExclude]) if (![ce hasAttribute: @"type" havingValue: aTypeToExclude])
break; break;
@ -824,7 +847,7 @@ convention:
andShortTimeString: nil andShortTimeString: nil
inTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]]; inTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
} }
return date; return date;
} }
@ -849,10 +872,12 @@ convention:
value = [self preferredTel]; value = [self preferredTel];
if (value) if (value)
[fields setObject: value forKey: @"c_telephonenumber"]; [fields setObject: value forKey: @"c_telephonenumber"];
value = [self preferredEMail]; v = [self emails];
if (![value isNotNull]) if ([v count] > 0)
value = @""; [fields setObject: [v componentsJoinedByString: @","]
[fields setObject: value forKey: @"c_mail"]; forKey: @"c_mail"];
else
[fields setObject: [NSNull null] forKey: @"c_mail"];
element = [self org]; element = [self org];
[fields setObject: [element flattenedValueAtIndex: 0 forKey: @""] [fields setObject: [element flattenedValueAtIndex: 0 forKey: @""]
forKey: @"c_o"]; forKey: @"c_o"];

View File

@ -264,9 +264,23 @@ static NSArray *folderListingFields = nil;
data = [contactRecord objectForKey: @"c_mail"]; data = [contactRecord objectForKey: @"c_mail"];
if ([data length]) if ([data length])
{ {
NSArray *values;
NSDictionary *email; NSDictionary *email;
email = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil]; NSMutableArray *emails;
[contactRecord setObject: [NSArray arrayWithObject: email] forKey: @"emails"]; NSString *type, *value;
int i, max;
values = [data componentsSeparatedByString: @","];
max = [values count];
emails = [NSMutableArray arrayWithCapacity: max];
for (i = 0; i < max; i++)
{
type = (i == 0)? @"pref" : @"home";
value = [values objectAtIndex: i];
email = [NSDictionary dictionaryWithObjectsAndKeys: type, @"type", value, @"value", nil];
[emails addObject: email];
}
[contactRecord setObject: emails forKey: @"emails"];
} }
else else
{ {

View File

@ -64,7 +64,7 @@
md-selected-item="editor.autocomplete.to.selected" md-selected-item="editor.autocomplete.to.selected"
md-items="user in editor.contactFilter(editor.autocomplete.to.searchText)" md-items="user in editor.contactFilter(editor.autocomplete.to.searchText)"
md-min-length="3" md-min-length="3"
md-delay="300" md-delay="150"
md-no-cache="true" md-no-cache="true"
label:placeholder="Add a recipient"> label:placeholder="Add a recipient">
<md-item-template> <md-item-template>

View File

@ -424,6 +424,27 @@
return this.refs.length - 1; return this.refs.length - 1;
}; };
/**
* @function explode
* @memberof Card.prototype
* @desc Create a new Card associated to each email address of this card.
* @return an array of Card instances
*/
Card.prototype.explode = function() {
var _this = this, cards = [], data;
if (this.emails.length > 1) {
data = this.$omit();
_.forEach(this.emails, function(email) {
var card = new Card(angular.extend({}, data, {emails: [email]}));
cards.push(card);
});
return cards;
}
else
return [this];
};
/** /**
* @function $reset * @function $reset
* @memberof Card.prototype * @memberof Card.prototype

View File

@ -197,8 +197,20 @@
} }
function contactFilter($query) { function contactFilter($query) {
AddressBook.$filterAll($query); return AddressBook.$filterAll($query).then(function(cards) {
return AddressBook.$cards; // Divide the matching cards by email addresses so the user can select
// the recipient address of her choice
var explodedCards = [];
_.forEach(_.invokeMap(cards, 'explode'), function(manyCards) {
_.forEach(manyCards, function(card) {
explodedCards.push(card);
});
});
// Remove duplicates
return _.uniqBy(explodedCards, function(card) {
return card.$$fullname + ' ' + card.$$email;
});
});
} }
function addRecipient(contact, field) { function addRecipient(contact, field) {