Use address books search fields in Contacts module
Searches can now be scoped to one or multiple fields. Those fields are now dynamic and can be defined using SearchFieldNames in external contacts sources (SQL and LDAP).pull/218/merge
parent
aed62cab26
commit
eb90760b39
|
@ -948,15 +948,15 @@ The returned value *must be unique across the whole SOGo installation*
|
||||||
since it is used to identify the user in the `folder_info` database
|
since it is used to identify the user in the `folder_info` database
|
||||||
table.
|
table.
|
||||||
|
|
||||||
|MailFieldNames
|
|MailFieldNames (optional)
|
||||||
|An array of fields that returns the user's email addresses (defaults to
|
|An array of fields that returns the user's email addresses (defaults to
|
||||||
`mail` when unset). Note that SOGo will always automatically strip the
|
`mail` when unset). Note that SOGo will always automatically strip the
|
||||||
protocol value from the attribute if the attribute name is `proxyAddresses`.
|
protocol value from the attribute if the attribute name is `proxyAddresses`.
|
||||||
|
|
||||||
|SearchFieldNames
|
|SearchFieldNames (optional)
|
||||||
|An array of fields to to match against the search string when filtering
|
|An array of fields to match against the search string when filtering
|
||||||
users (defaults to `sn`, `displayName`, and `telephoneNumber` when
|
users (defaults to `sn`, `displayName`, `cn`, `mail`, and `telephoneNumber`
|
||||||
unset).
|
when unset).
|
||||||
|
|
||||||
|IMAPHostFieldName (optional)
|
|IMAPHostFieldName (optional)
|
||||||
|The field that returns either an URI to the IMAP server as described
|
|The field that returns either an URI to the IMAP server as described
|
||||||
|
@ -1612,7 +1612,7 @@ SQL source:
|
||||||
|
|
||||||
[cols="3,47a,50"]
|
[cols="3,47a,50"]
|
||||||
|=======================================================================
|
|=======================================================================
|
||||||
.20+^|D |SOGoUserSources
|
.21+^|D |SOGoUserSources
|
||||||
|Parameter used to set the SQL and/or LDAP sources used for
|
|Parameter used to set the SQL and/or LDAP sources used for
|
||||||
authentication and global address books. Multiple sources can be
|
authentication and global address books. Multiple sources can be
|
||||||
specified as an array of dictionaries. A dictionary that defines a SQL
|
specified as an array of dictionaries. A dictionary that defines a SQL
|
||||||
|
@ -1688,6 +1688,10 @@ additional email addresses (beside the `mail` column) for each user.
|
||||||
Values must be unique and not appear in more than one column.
|
Values must be unique and not appear in more than one column.
|
||||||
Space-separated values allowed in all *additional* columns (besides in `mail`).
|
Space-separated values allowed in all *additional* columns (besides in `mail`).
|
||||||
|
|
||||||
|
|SearchFieldNames (optional)
|
||||||
|
|An array of fields to match against the search string when filtering
|
||||||
|
users (defaults to `c_cn` and `mail` when unset).
|
||||||
|
|
||||||
|IMAPHostFieldName (optional)
|
|IMAPHostFieldName (optional)
|
||||||
|The field that returns the IMAP hostname for the user.
|
|The field that returns the IMAP hostname for the user.
|
||||||
|
|
||||||
|
|
2
NEWS
2
NEWS
|
@ -5,6 +5,7 @@ New features
|
||||||
- [core] can now invite attendees to exceptions only (#2561)
|
- [core] can now invite attendees to exceptions only (#2561)
|
||||||
- [core] add support for module constraints in SQL sources
|
- [core] add support for module constraints in SQL sources
|
||||||
- [core] add support for listRequiresDot in SQL sources
|
- [core] add support for listRequiresDot in SQL sources
|
||||||
|
- [web] add support for SearchFieldNames in SQL sources
|
||||||
- [web] display freebusy information of owner in appointment editor
|
- [web] display freebusy information of owner in appointment editor
|
||||||
- [web] register SOGo as a handler for the mailto scheme (#1223)
|
- [web] register SOGo as a handler for the mailto scheme (#1223)
|
||||||
- [web] new events list view where events are grouped by day
|
- [web] new events list view where events are grouped by day
|
||||||
|
@ -15,6 +16,7 @@ Enhancements
|
||||||
- [web] added Simplified Chinese (zh_CN) translation - thanks to Thomas Kuiper
|
- [web] added Simplified Chinese (zh_CN) translation - thanks to Thomas Kuiper
|
||||||
- [web] now also give modify permission when selecting all calendar rights
|
- [web] now also give modify permission when selecting all calendar rights
|
||||||
- [web] allow edition of IMAP flags associated to mail labels
|
- [web] allow edition of IMAP flags associated to mail labels
|
||||||
|
- [web] search scope of address book is now respected
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
- [core] yearly repeating events are not shown in web calendar (#4237)
|
- [core] yearly repeating events are not shown in web calendar (#4237)
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
@protocol SOGoContactFolder <NSObject>
|
@protocol SOGoContactFolder <NSObject>
|
||||||
|
|
||||||
- (NSArray *) lookupContactsWithFilter: (NSString *) filter
|
- (NSArray *) lookupContactsWithFilter: (NSString *) filter
|
||||||
onCriteria: (NSString *) criteria
|
onCriteria: (NSArray *) criteria
|
||||||
sortBy: (NSString *) sortKey
|
sortBy: (NSString *) sortKey
|
||||||
ordering: (NSComparisonResult) sortOrdering
|
ordering: (NSComparisonResult) sortOrdering
|
||||||
inDomain: (NSString *) domain;
|
inDomain: (NSString *) domain;
|
||||||
|
|
|
@ -459,7 +459,7 @@ Class SOGoContactSourceFolderK;
|
||||||
folder = [sortedFolders objectAtIndex: i];
|
folder = [sortedFolders objectAtIndex: i];
|
||||||
//NSLog(@" Address book: %@ (%@)", [folder displayName], [folder class]);
|
//NSLog(@" Address book: %@ (%@)", [folder displayName], [folder class]);
|
||||||
contacts = [folder lookupContactsWithFilter: theFilter
|
contacts = [folder lookupContactsWithFilter: theFilter
|
||||||
onCriteria: @"name_or_address"
|
onCriteria: nil
|
||||||
sortBy: @"c_cn"
|
sortBy: @"c_cn"
|
||||||
ordering: NSOrderedAscending
|
ordering: NSOrderedAscending
|
||||||
inDomain: domain];
|
inDomain: domain];
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
}
|
}
|
||||||
- (void) fixupContactRecord: (NSMutableDictionary *) contactRecord;
|
- (void) fixupContactRecord: (NSMutableDictionary *) contactRecord;
|
||||||
- (EOQualifier *) qualifierForFilter: (NSString *) filter
|
- (EOQualifier *) qualifierForFilter: (NSString *) filter
|
||||||
onCriteria: (NSString *) criteria;
|
onCriteria: (NSArray *) criteria;
|
||||||
- (NSDictionary *) lookupContactWithName: (NSString *) aName;
|
- (NSDictionary *) lookupContactWithName: (NSString *) aName;
|
||||||
- (NSArray *) lookupContactsWithQualifier: (EOQualifier *) qualifier;
|
- (NSArray *) lookupContactsWithQualifier: (EOQualifier *) qualifier;
|
||||||
- (NSArray *) lookupContactsFields: (NSArray *) fields
|
- (NSArray *) lookupContactsFields: (NSArray *) fields
|
||||||
|
|
|
@ -79,6 +79,20 @@ static NSArray *folderListingFields = nil;
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSArray *) searchFields
|
||||||
|
{
|
||||||
|
static NSArray *searchFields = nil;
|
||||||
|
|
||||||
|
if (!searchFields)
|
||||||
|
{
|
||||||
|
// "name" expands to c_sn, c_givenname and c_cn
|
||||||
|
searchFields = [NSArray arrayWithObjects: @"name", @"c_mail", @"c_categories", @"c_o", nil];
|
||||||
|
[searchFields retain];
|
||||||
|
}
|
||||||
|
|
||||||
|
return searchFields;
|
||||||
|
}
|
||||||
|
|
||||||
- (Class) objectClassForContent: (NSString *) content
|
- (Class) objectClassForContent: (NSString *) content
|
||||||
{
|
{
|
||||||
CardGroup *cardEntry;
|
CardGroup *cardEntry;
|
||||||
|
@ -183,39 +197,42 @@ static NSArray *folderListingFields = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (EOQualifier *) qualifierForFilter: (NSString *) filter
|
- (EOQualifier *) qualifierForFilter: (NSString *) filter
|
||||||
onCriteria: (NSString *) criteria
|
onCriteria: (NSArray *) criteria
|
||||||
{
|
{
|
||||||
NSString *qs;
|
NSEnumerator *criteriaList;
|
||||||
|
NSMutableArray *filters;
|
||||||
|
NSString *filterFormat, *currentCriteria, *qs;
|
||||||
EOQualifier *qualifier;
|
EOQualifier *qualifier;
|
||||||
|
|
||||||
|
qualifier = nil;
|
||||||
if ([filter length] > 0)
|
if ([filter length] > 0)
|
||||||
{
|
{
|
||||||
filter = [filter asSafeSQLString];
|
filter = [filter asSafeSQLString];
|
||||||
if ([criteria isEqualToString: @"name_or_address"])
|
filters = [NSMutableArray array];
|
||||||
qs = [NSString stringWithFormat:
|
filterFormat = [NSString stringWithFormat: @"(%%@ isCaseInsensitiveLike: '%%%%%@%%%%')", filter];
|
||||||
@"(c_sn isCaseInsensitiveLike: '%%%@%%') OR "
|
if (criteria)
|
||||||
@"(c_givenname isCaseInsensitiveLike: '%%%@%%') OR "
|
criteriaList = [criteria objectEnumerator];
|
||||||
@"(c_cn isCaseInsensitiveLike: '%%%@%%') OR "
|
|
||||||
@"(c_mail isCaseInsensitiveLike: '%%%@%%')",
|
|
||||||
filter, filter, filter, filter];
|
|
||||||
else if ([criteria isEqualToString: @"category"])
|
|
||||||
qs = [NSString stringWithFormat:
|
|
||||||
@"(c_categories isCaseInsensitiveLike: '%%%@%%')",
|
|
||||||
filter];
|
|
||||||
else if ([criteria isEqualToString: @"organization"])
|
|
||||||
qs = [NSString stringWithFormat:
|
|
||||||
@"(c_o isCaseInsensitiveLike: '%%%@%%')",
|
|
||||||
filter];
|
|
||||||
else
|
else
|
||||||
qs = @"(1 == 0)";
|
criteriaList = [[self searchFields] objectEnumerator];
|
||||||
|
|
||||||
if (qs)
|
while (( currentCriteria = [criteriaList nextObject] ))
|
||||||
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
|
{
|
||||||
else
|
if ([currentCriteria isEqualToString: @"name"])
|
||||||
qualifier = nil;
|
{
|
||||||
|
[filters addObject: @"c_sn"];
|
||||||
|
[filters addObject: @"c_givenname"];
|
||||||
|
[filters addObject: @"c_cn"];
|
||||||
|
}
|
||||||
|
else if ([[self searchFields] containsObject: currentCriteria])
|
||||||
|
[filters addObject: currentCriteria];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([filters count])
|
||||||
|
{
|
||||||
|
qs = [[[filters uniqueObjects] stringsWithFormat: filterFormat] componentsJoinedByString: @" OR "];
|
||||||
|
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
qualifier = nil;
|
|
||||||
|
|
||||||
return qualifier;
|
return qualifier;
|
||||||
}
|
}
|
||||||
|
@ -357,7 +374,7 @@ static NSArray *folderListingFields = nil;
|
||||||
* The domain is therefore ignored.
|
* The domain is therefore ignored.
|
||||||
*/
|
*/
|
||||||
- (NSArray *) lookupContactsWithFilter: (NSString *) filter
|
- (NSArray *) lookupContactsWithFilter: (NSString *) filter
|
||||||
onCriteria: (NSString *) criteria
|
onCriteria: (NSArray *) criteria
|
||||||
sortBy: (NSString *) sortKey
|
sortBy: (NSString *) sortKey
|
||||||
ordering: (NSComparisonResult) sortOrdering
|
ordering: (NSComparisonResult) sortOrdering
|
||||||
inDomain: (NSString *) domain
|
inDomain: (NSString *) domain
|
||||||
|
|
|
@ -115,6 +115,11 @@
|
||||||
return isPersonalSource;
|
return isPersonalSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSArray *) searchFields
|
||||||
|
{
|
||||||
|
return [source searchFields];
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL) listRequiresDot
|
- (BOOL) listRequiresDot
|
||||||
{
|
{
|
||||||
return [source listRequiresDot];
|
return [source listRequiresDot];
|
||||||
|
@ -391,7 +396,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *) lookupContactsWithFilter: (NSString *) filter
|
- (NSArray *) lookupContactsWithFilter: (NSString *) filter
|
||||||
onCriteria: (NSString *) criteria
|
onCriteria: (NSArray *) criteria
|
||||||
sortBy: (NSString *) sortKey
|
sortBy: (NSString *) sortKey
|
||||||
ordering: (NSComparisonResult) sortOrdering
|
ordering: (NSComparisonResult) sortOrdering
|
||||||
inDomain: (NSString *) domain
|
inDomain: (NSString *) domain
|
||||||
|
@ -401,10 +406,10 @@
|
||||||
|
|
||||||
result = nil;
|
result = nil;
|
||||||
|
|
||||||
if (([filter length] > 0 && [criteria isEqualToString: @"name_or_address"])
|
if ([filter length] > 0 || ![source listRequiresDot])
|
||||||
|| ![source listRequiresDot])
|
|
||||||
{
|
{
|
||||||
records = [source fetchContactsMatching: filter
|
records = [source fetchContactsMatching: filter
|
||||||
|
withCriteria: criteria
|
||||||
inDomain: domain];
|
inDomain: domain];
|
||||||
[childRecords setObjects: records
|
[childRecords setObjects: records
|
||||||
forKeys: [records objectsForKey: @"c_name"
|
forKeys: [records objectsForKey: @"c_name"
|
||||||
|
|
|
@ -74,8 +74,6 @@
|
||||||
|
|
||||||
NSDictionary *modulesConstraints;
|
NSDictionary *modulesConstraints;
|
||||||
|
|
||||||
NSMutableArray *searchAttributes;
|
|
||||||
|
|
||||||
BOOL passwordPolicy;
|
BOOL passwordPolicy;
|
||||||
BOOL updateSambaNTLMPasswords;
|
BOOL updateSambaNTLMPasswords;
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,9 @@ static Class NSStringK;
|
||||||
mailFields = [NSArray arrayWithObject: @"mail"];
|
mailFields = [NSArray arrayWithObject: @"mail"];
|
||||||
[mailFields retain];
|
[mailFields retain];
|
||||||
contactMapping = nil;
|
contactMapping = nil;
|
||||||
searchFields = [NSArray arrayWithObjects: @"sn", @"displayname", @"telephonenumber", nil];
|
// "mail" expands to all entries of MailFieldNames
|
||||||
|
// "name" expands to sn, displayname and cn
|
||||||
|
searchFields = [NSArray arrayWithObjects: @"name", @"mail", @"telephonenumber", nil];
|
||||||
[searchFields retain];
|
[searchFields retain];
|
||||||
groupObjectClasses = [NSArray arrayWithObjects: @"group", @"groupofnames", @"groupofuniquenames", @"posixgroup", nil];
|
groupObjectClasses = [NSArray arrayWithObjects: @"group", @"groupofnames", @"groupofuniquenames", @"posixgroup", nil];
|
||||||
[groupObjectClasses retain];
|
[groupObjectClasses retain];
|
||||||
|
@ -105,7 +107,6 @@ static Class NSStringK;
|
||||||
_userPasswordAlgorithm = nil;
|
_userPasswordAlgorithm = nil;
|
||||||
listRequiresDot = YES;
|
listRequiresDot = YES;
|
||||||
|
|
||||||
searchAttributes = nil;
|
|
||||||
passwordPolicy = NO;
|
passwordPolicy = NO;
|
||||||
updateSambaNTLMPasswords = NO;
|
updateSambaNTLMPasswords = NO;
|
||||||
|
|
||||||
|
@ -149,7 +150,6 @@ static Class NSStringK;
|
||||||
[sourceID release];
|
[sourceID release];
|
||||||
[modulesConstraints release];
|
[modulesConstraints release];
|
||||||
[_scope release];
|
[_scope release];
|
||||||
[searchAttributes release];
|
|
||||||
[domain release];
|
[domain release];
|
||||||
[kindField release];
|
[kindField release];
|
||||||
[multipleBookingsField release];
|
[multipleBookingsField release];
|
||||||
|
@ -373,6 +373,11 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||||
return listRequiresDot;
|
return listRequiresDot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSArray *) searchFields
|
||||||
|
{
|
||||||
|
return searchFields;
|
||||||
|
}
|
||||||
|
|
||||||
- (void) setContactMapping: (NSDictionary *) newMapping
|
- (void) setContactMapping: (NSDictionary *) newMapping
|
||||||
andObjectClasses: (NSArray *) newObjectClasses
|
andObjectClasses: (NSArray *) newObjectClasses
|
||||||
{
|
{
|
||||||
|
@ -761,41 +766,57 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||||
* @return a EOQualifier matching the filter
|
* @return a EOQualifier matching the filter
|
||||||
*/
|
*/
|
||||||
- (EOQualifier *) _qualifierForFilter: (NSString *) filter
|
- (EOQualifier *) _qualifierForFilter: (NSString *) filter
|
||||||
|
onCriteria: (NSArray *) criteria
|
||||||
{
|
{
|
||||||
|
NSEnumerator *criteriaList;
|
||||||
NSMutableArray *fields;
|
NSMutableArray *fields;
|
||||||
NSString *fieldFormat, *searchFormat, *escapedFilter;
|
NSString *fieldFormat, *currentCriteria, *searchFormat, *escapedFilter;
|
||||||
EOQualifier *qualifier;
|
EOQualifier *qualifier;
|
||||||
NSMutableString *qs;
|
NSMutableString *qs;
|
||||||
|
|
||||||
escapedFilter = SafeLDAPCriteria(filter);
|
escapedFilter = SafeLDAPCriteria(filter);
|
||||||
if ([escapedFilter length] > 0)
|
qs = [NSMutableString string];
|
||||||
|
|
||||||
|
if (([escapedFilter length] == 0 && !listRequiresDot) || [escapedFilter isEqualToString: @"."])
|
||||||
{
|
{
|
||||||
qs = [NSMutableString string];
|
[qs appendFormat: @"(%@='*')", CNField];
|
||||||
if ([escapedFilter isEqualToString: @"."])
|
}
|
||||||
[qs appendFormat: @"(%@='*')", CNField];
|
else
|
||||||
|
{
|
||||||
|
fieldFormat = [NSString stringWithFormat: @"(%%@='*%@*')", escapedFilter];
|
||||||
|
if (criteria)
|
||||||
|
criteriaList = [criteria objectEnumerator];
|
||||||
else
|
else
|
||||||
|
criteriaList = [[self searchFields] objectEnumerator];
|
||||||
|
|
||||||
|
fields = [NSMutableArray array];
|
||||||
|
while (( currentCriteria = [criteriaList nextObject] ))
|
||||||
{
|
{
|
||||||
fieldFormat = [NSString stringWithFormat: @"(%%@='*%@*')", escapedFilter];
|
if ([currentCriteria isEqualToString: @"name"])
|
||||||
fields = [NSMutableArray arrayWithArray: searchFields];
|
{
|
||||||
[fields addObjectsFromArray: mailFields];
|
[fields addObject: @"sn"];
|
||||||
[fields addObject: CNField];
|
[fields addObject: @"displayname"];
|
||||||
searchFormat = [[[fields uniqueObjects] stringsWithFormat: fieldFormat]
|
[fields addObject: @"cn"];
|
||||||
componentsJoinedByString: @" OR "];
|
}
|
||||||
[qs appendString: searchFormat];
|
else if ([currentCriteria isEqualToString: @"mail"])
|
||||||
|
{
|
||||||
|
// Expand to all mail fields
|
||||||
|
[fields addObject: currentCriteria];
|
||||||
|
[fields addObjectsFromArray: mailFields];
|
||||||
|
}
|
||||||
|
else if ([[self searchFields] containsObject: currentCriteria])
|
||||||
|
[fields addObject: currentCriteria];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_filter && [_filter length])
|
searchFormat = [[[fields uniqueObjects] stringsWithFormat: fieldFormat] componentsJoinedByString: @" OR "];
|
||||||
[qs appendFormat: @" AND %@", _filter];
|
[qs appendString: searchFormat];
|
||||||
|
}
|
||||||
|
|
||||||
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
|
if (_filter && [_filter length])
|
||||||
}
|
[qs appendFormat: @" AND %@", _filter];
|
||||||
else if (!listRequiresDot)
|
|
||||||
{
|
if ([qs length])
|
||||||
qs = [NSMutableString stringWithFormat: @"(%@='*')", CNField];
|
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
|
||||||
if ([_filter length])
|
|
||||||
[qs appendFormat: @" AND %@", _filter];
|
|
||||||
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
qualifier = nil;
|
qualifier = nil;
|
||||||
|
|
||||||
|
@ -832,6 +853,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||||
return [EOQualifier qualifierWithQualifierFormat: qs];
|
return [EOQualifier qualifierWithQualifierFormat: qs];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
- (NSArray *) _constraintsFields
|
- (NSArray *) _constraintsFields
|
||||||
{
|
{
|
||||||
NSMutableArray *fields;
|
NSMutableArray *fields;
|
||||||
|
@ -845,6 +867,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||||
|
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/* This is required for SQL sources when DomainFieldName is enabled.
|
/* This is required for SQL sources when DomainFieldName is enabled.
|
||||||
* For LDAP, simply discard the domain and call the original method */
|
* For LDAP, simply discard the domain and call the original method */
|
||||||
|
@ -1202,6 +1225,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *) fetchContactsMatching: (NSString *) match
|
- (NSArray *) fetchContactsMatching: (NSString *) match
|
||||||
|
withCriteria: (NSArray *) criteria
|
||||||
inDomain: (NSString *) domain
|
inDomain: (NSString *) domain
|
||||||
{
|
{
|
||||||
NGLdapConnection *ldapConnection;
|
NGLdapConnection *ldapConnection;
|
||||||
|
@ -1216,7 +1240,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||||
if ([match length] > 0 || !listRequiresDot)
|
if ([match length] > 0 || !listRequiresDot)
|
||||||
{
|
{
|
||||||
ldapConnection = [self _ldapConnection];
|
ldapConnection = [self _ldapConnection];
|
||||||
qualifier = [self _qualifierForFilter: match];
|
qualifier = [self _qualifierForFilter: match onCriteria: criteria];
|
||||||
attributes = [NSArray arrayWithObject: @"*"];
|
attributes = [NSArray arrayWithObject: @"*"];
|
||||||
|
|
||||||
if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame)
|
if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame)
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
inDomain: (NSString *) domain;
|
inDomain: (NSString *) domain;
|
||||||
|
|
||||||
- (NSString *) domain;
|
- (NSString *) domain;
|
||||||
|
- (NSArray *) searchFields;
|
||||||
|
|
||||||
/* requires a "." to obtain the full list of contacts */
|
/* requires a "." to obtain the full list of contacts */
|
||||||
- (void) setListRequiresDot: (BOOL) aBool;
|
- (void) setListRequiresDot: (BOOL) aBool;
|
||||||
|
@ -63,6 +64,7 @@
|
||||||
- (NSArray *) allEntryIDs;
|
- (NSArray *) allEntryIDs;
|
||||||
- (NSArray *) allEntryIDsVisibleFromDomain: (NSString *) domain;
|
- (NSArray *) allEntryIDsVisibleFromDomain: (NSString *) domain;
|
||||||
- (NSArray *) fetchContactsMatching: (NSString *) filter
|
- (NSArray *) fetchContactsMatching: (NSString *) filter
|
||||||
|
withCriteria: (NSArray *) criteria
|
||||||
inDomain: (NSString *) domain;
|
inDomain: (NSString *) domain;
|
||||||
|
|
||||||
- (void) setSourceID: (NSString *) newSourceID;
|
- (void) setSourceID: (NSString *) newSourceID;
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
NSString *_authenticationFilter;
|
NSString *_authenticationFilter;
|
||||||
NSArray *_loginFields;
|
NSArray *_loginFields;
|
||||||
NSArray *_mailFields;
|
NSArray *_mailFields;
|
||||||
|
NSArray *_searchFields;
|
||||||
NSString *_imapLoginField;
|
NSString *_imapLoginField;
|
||||||
NSString *_imapHostField;
|
NSString *_imapHostField;
|
||||||
NSString *_sieveHostField;
|
NSString *_sieveHostField;
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/* SQLSource.h - this file is part of SOGo
|
/* SQLSource.h - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009-2012 Inverse inc.
|
* Copyright (C) 2009-2017 Inverse inc.
|
||||||
*
|
*
|
||||||
* Authors: Ludovic Marcotte <lmarcotte@inverse.ca>
|
* This file is part of SOGo.
|
||||||
* Francis Lachapelle <flachapelle@inverse.ca>
|
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -35,6 +34,7 @@
|
||||||
|
|
||||||
#import <SOGo/SOGoSystemDefaults.h>
|
#import <SOGo/SOGoSystemDefaults.h>
|
||||||
|
|
||||||
|
#import "NSArray+Utilities.h"
|
||||||
#import "NSString+Utilities.h"
|
#import "NSString+Utilities.h"
|
||||||
#import "NSString+Crypto.h"
|
#import "NSString+Crypto.h"
|
||||||
|
|
||||||
|
@ -90,6 +90,9 @@
|
||||||
_authenticationFilter = nil;
|
_authenticationFilter = nil;
|
||||||
_loginFields = nil;
|
_loginFields = nil;
|
||||||
_mailFields = nil;
|
_mailFields = nil;
|
||||||
|
// "mail" expands to all entries of MailFieldNames
|
||||||
|
_searchFields = [NSArray arrayWithObjects: @"c_cn", @"mail", nil];
|
||||||
|
[_searchFields retain];
|
||||||
_userPasswordAlgorithm = nil;
|
_userPasswordAlgorithm = nil;
|
||||||
_viewURL = nil;
|
_viewURL = nil;
|
||||||
_kindField = nil;
|
_kindField = nil;
|
||||||
|
@ -109,6 +112,7 @@
|
||||||
[_authenticationFilter release];
|
[_authenticationFilter release];
|
||||||
[_loginFields release];
|
[_loginFields release];
|
||||||
[_mailFields release];
|
[_mailFields release];
|
||||||
|
[_searchFields release];
|
||||||
[_userPasswordAlgorithm release];
|
[_userPasswordAlgorithm release];
|
||||||
[_viewURL release];
|
[_viewURL release];
|
||||||
[_kindField release];
|
[_kindField release];
|
||||||
|
@ -140,6 +144,8 @@
|
||||||
ASSIGN(_multipleBookingsField, [udSource objectForKey: @"MultipleBookingsFieldName"]);
|
ASSIGN(_multipleBookingsField, [udSource objectForKey: @"MultipleBookingsFieldName"]);
|
||||||
ASSIGN(_domainField, [udSource objectForKey: @"DomainFieldName"]);
|
ASSIGN(_domainField, [udSource objectForKey: @"DomainFieldName"]);
|
||||||
ASSIGN(_modulesConstraints, [udSource objectForKey: @"ModulesConstraints"]);
|
ASSIGN(_modulesConstraints, [udSource objectForKey: @"ModulesConstraints"]);
|
||||||
|
if ([udSource objectForKey: @"SearchFieldNames"])
|
||||||
|
ASSIGN(_searchFields, [udSource objectForKey: @"SearchFieldNames"]);
|
||||||
if ([udSource objectForKey: @"prependPasswordScheme"])
|
if ([udSource objectForKey: @"prependPasswordScheme"])
|
||||||
_prependPasswordScheme = [[udSource objectForKey: @"prependPasswordScheme"] boolValue];
|
_prependPasswordScheme = [[udSource objectForKey: @"prependPasswordScheme"] boolValue];
|
||||||
else
|
else
|
||||||
|
@ -157,7 +163,7 @@
|
||||||
|
|
||||||
#warning this domain code has no effect yet
|
#warning this domain code has no effect yet
|
||||||
if ([sourceDomain length])
|
if ([sourceDomain length])
|
||||||
ASSIGN (_domain, sourceDomain);
|
ASSIGN(_domain, sourceDomain);
|
||||||
|
|
||||||
if (!_viewURL)
|
if (!_viewURL)
|
||||||
{
|
{
|
||||||
|
@ -173,6 +179,11 @@
|
||||||
return _domain;
|
return _domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSArray *) searchFields
|
||||||
|
{
|
||||||
|
return _searchFields;
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL) _isPassword: (NSString *) plainPassword
|
- (BOOL) _isPassword: (NSString *) plainPassword
|
||||||
equalTo: (NSString *) encryptedPassword
|
equalTo: (NSString *) encryptedPassword
|
||||||
{
|
{
|
||||||
|
@ -369,6 +380,7 @@
|
||||||
return didChange;
|
return didChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
- (NSString *) _whereClauseFromArray: (NSArray *) theArray
|
- (NSString *) _whereClauseFromArray: (NSArray *) theArray
|
||||||
value: (NSString *) theValue
|
value: (NSString *) theValue
|
||||||
exact: (BOOL) theBOOL
|
exact: (BOOL) theBOOL
|
||||||
|
@ -388,6 +400,7 @@
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
- (void) _fillConstraintsForModule: (NSString *) module
|
- (void) _fillConstraintsForModule: (NSString *) module
|
||||||
intoRecord: (NSMutableDictionary *) record
|
intoRecord: (NSMutableDictionary *) record
|
||||||
|
@ -562,7 +575,7 @@
|
||||||
[emails addObjectsFromArray: [s componentsSeparatedByString: @" "]];
|
[emails addObjectsFromArray: [s componentsSeparatedByString: @" "]];
|
||||||
}
|
}
|
||||||
|
|
||||||
[response setObject: emails forKey: @"c_emails"];
|
[response setObject: [emails uniqueObjects] forKey: @"c_emails"];
|
||||||
if (_imapHostField)
|
if (_imapHostField)
|
||||||
{
|
{
|
||||||
value = [response objectForKey: _imapHostField];
|
value = [response objectForKey: _imapHostField];
|
||||||
|
@ -792,14 +805,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *) fetchContactsMatching: (NSString *) filter
|
- (NSArray *) fetchContactsMatching: (NSString *) filter
|
||||||
|
withCriteria: (NSArray *) criteria
|
||||||
inDomain: (NSString *) domain
|
inDomain: (NSString *) domain
|
||||||
{
|
{
|
||||||
EOAdaptorChannel *channel;
|
EOAdaptorChannel *channel;
|
||||||
NSMutableArray *results;
|
NSEnumerator *criteriaList;
|
||||||
|
NSMutableArray *fields, *results;
|
||||||
GCSChannelManager *cm;
|
GCSChannelManager *cm;
|
||||||
NSException *ex;
|
NSException *ex;
|
||||||
NSMutableString *sql;
|
NSMutableString *sql;
|
||||||
NSString *lowerFilter;
|
NSString *lowerFilter, *filterFormat, *currentCriteria, *qs;
|
||||||
|
|
||||||
results = [NSMutableArray array];
|
results = [NSMutableArray array];
|
||||||
|
|
||||||
|
@ -809,22 +824,40 @@
|
||||||
channel = [cm acquireOpenChannelForURL: _viewURL];
|
channel = [cm acquireOpenChannelForURL: _viewURL];
|
||||||
if (channel)
|
if (channel)
|
||||||
{
|
{
|
||||||
lowerFilter = [filter lowercaseString];
|
fields = [NSMutableArray array];
|
||||||
lowerFilter = [lowerFilter stringByReplacingString: @"'" withString: @"''"];
|
if ([filter length])
|
||||||
|
|
||||||
sql = [NSMutableString stringWithFormat: (@"SELECT *"
|
|
||||||
@" FROM %@"
|
|
||||||
@" WHERE"
|
|
||||||
@" (LOWER(c_cn) LIKE '%%%@%%'"
|
|
||||||
@" OR LOWER(mail) LIKE '%%%@%%'"),
|
|
||||||
[_viewURL gcsTableName],
|
|
||||||
lowerFilter, lowerFilter];
|
|
||||||
|
|
||||||
if (_mailFields && [_mailFields count] > 0)
|
|
||||||
{
|
{
|
||||||
[sql appendString: [self _whereClauseFromArray: _mailFields value: lowerFilter exact: NO]];
|
lowerFilter = [filter lowercaseString];
|
||||||
|
lowerFilter = [lowerFilter asSafeSQLString];
|
||||||
|
filterFormat = [NSString stringWithFormat: @"LOWER(%%@) LIKE '%%%%%@%%%%'", lowerFilter];
|
||||||
|
if (criteria)
|
||||||
|
criteriaList = [criteria objectEnumerator];
|
||||||
|
else
|
||||||
|
criteriaList = [[self searchFields] objectEnumerator];
|
||||||
|
|
||||||
|
while (( currentCriteria = [criteriaList nextObject] ))
|
||||||
|
{
|
||||||
|
if ([currentCriteria isEqualToString: @"mail"])
|
||||||
|
{
|
||||||
|
// Expand to all mail fields
|
||||||
|
[fields addObject: currentCriteria];
|
||||||
|
if (_mailFields)
|
||||||
|
[fields addObjectsFromArray: _mailFields];
|
||||||
|
}
|
||||||
|
else if ([[self searchFields] containsObject: currentCriteria])
|
||||||
|
[fields addObject: currentCriteria];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sql = [NSMutableString stringWithFormat: @"SELECT * FROM %@ WHERE (", [_viewURL gcsTableName]];
|
||||||
|
|
||||||
|
if ([fields count])
|
||||||
|
{
|
||||||
|
qs = [[[fields uniqueObjects] stringsWithFormat: filterFormat] componentsJoinedByString: @" OR "];
|
||||||
|
[sql appendString: qs];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
[sql appendString: @"1 = 1"];
|
||||||
[sql appendString: @")"];
|
[sql appendString: @")"];
|
||||||
|
|
||||||
if (_domainField)
|
if (_domainField)
|
||||||
|
@ -832,8 +865,7 @@
|
||||||
if ([domain length])
|
if ([domain length])
|
||||||
{
|
{
|
||||||
EOQualifier *domainQualifier;
|
EOQualifier *domainQualifier;
|
||||||
domainQualifier =
|
domainQualifier = [self _visibleDomainsQualifierFromDomain: domain];
|
||||||
[self _visibleDomainsQualifierFromDomain: domain];
|
|
||||||
if (domainQualifier)
|
if (domainQualifier)
|
||||||
{
|
{
|
||||||
[sql appendFormat: @" AND ("];
|
[sql appendFormat: @" AND ("];
|
||||||
|
|
|
@ -45,6 +45,30 @@
|
||||||
"Carbon Copy (Cc)" = "Carbon Copy (Cc)";
|
"Carbon Copy (Cc)" = "Carbon Copy (Cc)";
|
||||||
"Blind Carbon Copy (Bcc)" = "Blind Carbon Copy (Bcc)";
|
"Blind Carbon Copy (Bcc)" = "Blind Carbon Copy (Bcc)";
|
||||||
|
|
||||||
|
/* Search scope: name fields */
|
||||||
|
"name" = "Name";
|
||||||
|
|
||||||
|
/* Search scope: name fields */
|
||||||
|
"c_cn" = "Name";
|
||||||
|
|
||||||
|
/* Search scope: mail fields */
|
||||||
|
"mail" = "Mail";
|
||||||
|
|
||||||
|
/* Search scope: mail fields */
|
||||||
|
"c_mail" = "Mail";
|
||||||
|
|
||||||
|
/* Search scope: telephone field */
|
||||||
|
"telephonenumber" = "Telephone";
|
||||||
|
|
||||||
|
/* Search scope: categories field */
|
||||||
|
"c_categories" = "Categories";
|
||||||
|
|
||||||
|
/* Search scope: categories field */
|
||||||
|
"vcardcategories" = "Categories";
|
||||||
|
|
||||||
|
/* Search scope: organization field */
|
||||||
|
"c_o" = "Organization";
|
||||||
|
|
||||||
/* Subheader of empty addressbook */
|
/* Subheader of empty addressbook */
|
||||||
"No contact" = "No contact";
|
"No contact" = "No contact";
|
||||||
|
|
||||||
|
|
|
@ -199,7 +199,7 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
|
||||||
folder = [sortedFolders objectAtIndex: i];
|
folder = [sortedFolders objectAtIndex: i];
|
||||||
//NSLog(@" Address book: %@ (%@)", [folder displayName], [folder class]);
|
//NSLog(@" Address book: %@ (%@)", [folder displayName], [folder class]);
|
||||||
contacts = [folder lookupContactsWithFilter: searchText
|
contacts = [folder lookupContactsWithFilter: searchText
|
||||||
onCriteria: @"name_or_address"
|
onCriteria: nil
|
||||||
sortBy: @"c_cn"
|
sortBy: @"c_cn"
|
||||||
ordering: NSOrderedAscending
|
ordering: NSOrderedAscending
|
||||||
inDomain: domain];
|
inDomain: domain];
|
||||||
|
@ -343,6 +343,7 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
|
||||||
&& [currentFolder listRequiresDot]], @"listRequiresDot",
|
&& [currentFolder listRequiresDot]], @"listRequiresDot",
|
||||||
acls, @"acls",
|
acls, @"acls",
|
||||||
urls, @"urls",
|
urls, @"urls",
|
||||||
|
[currentFolder searchFields], @"searchFields",
|
||||||
nil];
|
nil];
|
||||||
[foldersAttrs addObject: folderAttrs];
|
[foldersAttrs addObject: folderAttrs];
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
|
|
||||||
@interface UIxContactsListActions : SOGoDirectAction
|
@interface UIxContactsListActions : SOGoDirectAction
|
||||||
{
|
{
|
||||||
|
NSDictionary *requestData;
|
||||||
|
|
||||||
NSDictionary *currentContact;
|
NSDictionary *currentContact;
|
||||||
|
|
||||||
NSArray *contactInfos;
|
NSArray *contactInfos;
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
{
|
{
|
||||||
if ((self = [super init]))
|
if ((self = [super init]))
|
||||||
{
|
{
|
||||||
|
requestData = nil;
|
||||||
contactInfos = nil;
|
contactInfos = nil;
|
||||||
sortedIDs = nil;
|
sortedIDs = nil;
|
||||||
}
|
}
|
||||||
|
@ -63,6 +64,7 @@
|
||||||
|
|
||||||
- (void) dealloc
|
- (void) dealloc
|
||||||
{
|
{
|
||||||
|
[requestData release];
|
||||||
[contactInfos release];
|
[contactInfos release];
|
||||||
[sortedIDs release];
|
[sortedIDs release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
|
@ -70,6 +72,20 @@
|
||||||
|
|
||||||
/* accessors */
|
/* accessors */
|
||||||
|
|
||||||
|
- (NSDictionary *) requestData
|
||||||
|
{
|
||||||
|
WORequest *rq;
|
||||||
|
|
||||||
|
if (!requestData)
|
||||||
|
{
|
||||||
|
rq = [context request];
|
||||||
|
requestData = [[rq contentAsString] objectFromJSONString];
|
||||||
|
[requestData retain];
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestData;
|
||||||
|
}
|
||||||
|
|
||||||
- (NSString *) defaultSortKey
|
- (NSString *) defaultSortKey
|
||||||
{
|
{
|
||||||
return @"c_cn";
|
return @"c_cn";
|
||||||
|
@ -78,7 +94,6 @@
|
||||||
- (NSString *) sortKey
|
- (NSString *) sortKey
|
||||||
{
|
{
|
||||||
NSString *s;
|
NSString *s;
|
||||||
WORequest *rq;
|
|
||||||
static NSArray *sortKeys = nil;
|
static NSArray *sortKeys = nil;
|
||||||
|
|
||||||
if (!sortKeys)
|
if (!sortKeys)
|
||||||
|
@ -88,8 +103,7 @@
|
||||||
[sortKeys retain];
|
[sortKeys retain];
|
||||||
}
|
}
|
||||||
|
|
||||||
rq = [context request];
|
s = [[self requestData] objectForKey: @"sort"];
|
||||||
s = [rq formValueForKey: @"sort"];
|
|
||||||
if (![s length] || ![sortKeys containsObject: s])
|
if (![s length] || ![sortKeys containsObject: s])
|
||||||
s = [self defaultSortKey];
|
s = [self defaultSortKey];
|
||||||
|
|
||||||
|
@ -102,8 +116,8 @@
|
||||||
NSString *ascending, *sort;
|
NSString *ascending, *sort;
|
||||||
SOGoUserSettings *us;
|
SOGoUserSettings *us;
|
||||||
|
|
||||||
sort = [[context request] formValueForKey: @"sort"];
|
sort = [[self requestData] objectForKey: @"sort"];
|
||||||
ascending = [[context request] formValueForKey: @"asc"];
|
ascending = [[self requestData] objectForKey: @"asc"];
|
||||||
|
|
||||||
if ([sort length])
|
if ([sort length])
|
||||||
{
|
{
|
||||||
|
@ -125,14 +139,13 @@
|
||||||
- (NSArray *) contactInfos
|
- (NSArray *) contactInfos
|
||||||
{
|
{
|
||||||
id <SOGoContactFolder> folder;
|
id <SOGoContactFolder> folder;
|
||||||
NSString *ascending, *searchText, *valueText;
|
NSString *ascending, *valueText;
|
||||||
NSArray *results, *fields;
|
NSArray *results, *searchFields, *fields;
|
||||||
NSMutableArray *filteredContacts, *headers;
|
NSMutableArray *filteredContacts, *headers;
|
||||||
NSDictionary *contact;
|
NSDictionary *data, *contact;
|
||||||
BOOL excludeLists;
|
BOOL excludeLists;
|
||||||
NSComparisonResult ordering;
|
NSComparisonResult ordering;
|
||||||
NSUInteger max, count;
|
NSUInteger max, count;
|
||||||
WORequest *rq;
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
[self saveSortValue];
|
[self saveSortValue];
|
||||||
|
@ -140,23 +153,26 @@
|
||||||
if (!contactInfos)
|
if (!contactInfos)
|
||||||
{
|
{
|
||||||
folder = [self clientObject];
|
folder = [self clientObject];
|
||||||
rq = [context request];
|
data = [self requestData];
|
||||||
|
|
||||||
ascending = [rq formValueForKey: @"asc"];
|
ascending = [data objectForKey: @"asc"];
|
||||||
ordering = ((![ascending length] || [ascending boolValue])
|
ordering = ((!ascending || [ascending boolValue])
|
||||||
? NSOrderedAscending : NSOrderedDescending);
|
? NSOrderedAscending : NSOrderedDescending);
|
||||||
|
|
||||||
searchText = [rq formValueForKey: @"search"];
|
searchFields = [data objectForKey: @"search"];
|
||||||
if ([searchText length] > 0)
|
if ([searchFields isKindOfClass: [NSArray class]] && [searchFields count] > 0)
|
||||||
valueText = [rq formValueForKey: @"value"];
|
valueText = [data objectForKey: @"value"];
|
||||||
else
|
else
|
||||||
valueText = nil;
|
{
|
||||||
|
searchFields = nil;
|
||||||
|
valueText = nil;
|
||||||
|
}
|
||||||
|
|
||||||
excludeLists = [[rq formValueForKey: @"excludeLists"] boolValue];
|
excludeLists = [[data objectForKey: @"excludeLists"] boolValue];
|
||||||
|
|
||||||
[contactInfos release];
|
[contactInfos release];
|
||||||
results = [folder lookupContactsWithFilter: valueText
|
results = [folder lookupContactsWithFilter: valueText
|
||||||
onCriteria: searchText
|
onCriteria: searchFields
|
||||||
sortBy: [self sortKey]
|
sortBy: [self sortKey]
|
||||||
ordering: ordering
|
ordering: ordering
|
||||||
inDomain: [[context activeUser] domain]];
|
inDomain: [[context activeUser] domain]];
|
||||||
|
@ -204,16 +220,15 @@
|
||||||
- (NSArray *) sortedIDs
|
- (NSArray *) sortedIDs
|
||||||
{
|
{
|
||||||
id <SOGoContactFolder> folder;
|
id <SOGoContactFolder> folder;
|
||||||
NSString *ascending, *searchText, *valueText;
|
NSString *ascending, *valueText;
|
||||||
NSArray *fields, *records;
|
NSArray *searchFields, *fields, *records;
|
||||||
NSDictionary *record;
|
NSDictionary *data, *record;
|
||||||
NSEnumerator *recordsList;
|
NSEnumerator *recordsList;
|
||||||
NSMutableArray *ids;
|
NSMutableArray *ids;
|
||||||
BOOL excludeLists;
|
BOOL excludeLists;
|
||||||
EOKeyValueQualifier *kvQualifier;
|
EOKeyValueQualifier *kvQualifier;
|
||||||
EOSortOrdering *ordering;
|
EOSortOrdering *ordering;
|
||||||
EOQualifier *qualifier;
|
EOQualifier *qualifier;
|
||||||
WORequest *rq;
|
|
||||||
SEL compare;
|
SEL compare;
|
||||||
|
|
||||||
folder = [self clientObject];
|
folder = [self clientObject];
|
||||||
|
@ -221,12 +236,12 @@
|
||||||
if (!sortedIDs && [folder isKindOfClass: [SOGoContactGCSFolder class]])
|
if (!sortedIDs && [folder isKindOfClass: [SOGoContactGCSFolder class]])
|
||||||
{
|
{
|
||||||
fields = [NSArray arrayWithObjects: @"c_name", nil];
|
fields = [NSArray arrayWithObjects: @"c_name", nil];
|
||||||
rq = [context request];
|
data = [self requestData];
|
||||||
qualifier = nil;
|
qualifier = nil;
|
||||||
|
|
||||||
// ORDER BY clause
|
// ORDER BY clause
|
||||||
ascending = [rq formValueForKey: @"asc"];
|
ascending = [data valueForKey: @"asc"];
|
||||||
if (![ascending length] || [ascending boolValue])
|
if (!ascending || [ascending boolValue])
|
||||||
compare = EOCompareAscending;
|
compare = EOCompareAscending;
|
||||||
else
|
else
|
||||||
compare = EOCompareDescending;
|
compare = EOCompareDescending;
|
||||||
|
@ -234,14 +249,14 @@
|
||||||
selector: compare];
|
selector: compare];
|
||||||
|
|
||||||
// WHERE clause
|
// WHERE clause
|
||||||
searchText = [rq formValueForKey: @"search"];
|
searchFields = (NSArray *)[data objectForKey: @"search"];
|
||||||
if ([searchText length] > 0)
|
if ([searchFields count] > 0)
|
||||||
{
|
{
|
||||||
valueText = [rq formValueForKey: @"value"];
|
valueText = [data objectForKey: @"value"];
|
||||||
qualifier = [(SOGoContactGCSFolder *) folder qualifierForFilter: valueText
|
qualifier = [(SOGoContactGCSFolder *) folder qualifierForFilter: valueText
|
||||||
onCriteria: searchText];
|
onCriteria: searchFields];
|
||||||
}
|
}
|
||||||
excludeLists = [[rq formValueForKey: @"excludeLists"] boolValue];
|
excludeLists = [[data objectForKey: @"excludeLists"] boolValue];
|
||||||
if (excludeLists)
|
if (excludeLists)
|
||||||
{
|
{
|
||||||
kvQualifier = [[EOKeyValueQualifier alloc]
|
kvQualifier = [[EOKeyValueQualifier alloc]
|
||||||
|
@ -337,7 +352,7 @@
|
||||||
* @apiExample {curl} Example usage:
|
* @apiExample {curl} Example usage:
|
||||||
* curl -i http://localhost/SOGo/so/sogo1/Contacts/personal/view?search=name_or_address\&value=Bob
|
* curl -i http://localhost/SOGo/so/sogo1/Contacts/personal/view?search=name_or_address\&value=Bob
|
||||||
*
|
*
|
||||||
* @apiParam {Boolean} [partial] Send all contacts IDs and headers of a the first 50 contacts. Defaults to false.
|
* @apiParam {Boolean} [partial] Send all contacts IDs and headers of the first 50 contacts. Defaults to false.
|
||||||
* @apiParam {Boolean} [asc] Descending sort when false. Defaults to true (ascending).
|
* @apiParam {Boolean} [asc] Descending sort when false. Defaults to true (ascending).
|
||||||
* @apiParam {String} [sort] Sort field. Either c_cn, c_mail, c_screenname, c_o, or c_telephonenumber.
|
* @apiParam {String} [sort] Sort field. Either c_cn, c_mail, c_screenname, c_o, or c_telephonenumber.
|
||||||
* @apiParam {String} [search] Field criteria. Either name_or_address, category, or organization.
|
* @apiParam {String} [search] Field criteria. Either name_or_address, category, or organization.
|
||||||
|
@ -383,7 +398,7 @@
|
||||||
[self cardDavURL], @"cardDavURL",
|
[self cardDavURL], @"cardDavURL",
|
||||||
[self publicCardDavURL], @"publicCardDavURL",
|
[self publicCardDavURL], @"publicCardDavURL",
|
||||||
nil];
|
nil];
|
||||||
partial = [[context request] formValueForKey: @"partial"];
|
partial = [[self requestData] objectForKey: @"partial"];
|
||||||
|
|
||||||
if ([partial intValue] && [folder isKindOfClass: [SOGoContactGCSFolder class]])
|
if ([partial intValue] && [folder isKindOfClass: [SOGoContactGCSFolder class]])
|
||||||
{
|
{
|
||||||
|
@ -427,11 +442,9 @@
|
||||||
{
|
{
|
||||||
NSArray *ids, *headers;
|
NSArray *ids, *headers;
|
||||||
NSDictionary *data;
|
NSDictionary *data;
|
||||||
WORequest *request;
|
|
||||||
WOResponse *response;
|
WOResponse *response;
|
||||||
|
|
||||||
request = [context request];
|
data = [self requestData];
|
||||||
data = [[request contentAsString] objectFromJSONString];
|
|
||||||
if (![[data objectForKey: @"ids"] isKindOfClass: [NSArray class]] ||
|
if (![[data objectForKey: @"ids"] isKindOfClass: [NSArray class]] ||
|
||||||
[[data objectForKey: @"ids"] count] == 0)
|
[[data objectForKey: @"ids"] count] == 0)
|
||||||
{
|
{
|
||||||
|
@ -463,11 +476,9 @@
|
||||||
NSMutableDictionary *uniqueContacts;
|
NSMutableDictionary *uniqueContacts;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
NSSortDescriptor *commonNameDescriptor;
|
NSSortDescriptor *commonNameDescriptor;
|
||||||
WORequest *rq;
|
|
||||||
|
|
||||||
rq = [context request];
|
excludeLists = [[[self requestData] objectForKey: @"excludeLists"] boolValue];
|
||||||
excludeLists = [[rq formValueForKey: @"excludeLists"] boolValue];
|
searchText = [[self requestData] objectForKey: @"search"];
|
||||||
searchText = [rq formValueForKey: @"search"];
|
|
||||||
if ([searchText length] > 0)
|
if ([searchText length] > 0)
|
||||||
{
|
{
|
||||||
NS_DURING
|
NS_DURING
|
||||||
|
@ -482,7 +493,7 @@
|
||||||
domain = [[context activeUser] domain];
|
domain = [[context activeUser] domain];
|
||||||
uniqueContacts = [NSMutableDictionary dictionary];
|
uniqueContacts = [NSMutableDictionary dictionary];
|
||||||
contacts = [folder lookupContactsWithFilter: searchText
|
contacts = [folder lookupContactsWithFilter: searchText
|
||||||
onCriteria: @"name_or_address"
|
onCriteria: nil
|
||||||
sortBy: @"c_cn"
|
sortBy: @"c_cn"
|
||||||
ordering: NSOrderedAscending
|
ordering: NSOrderedAscending
|
||||||
inDomain: domain];
|
inDomain: domain];
|
||||||
|
|
|
@ -317,7 +317,8 @@
|
||||||
layout="row"
|
layout="row"
|
||||||
ng-show="addressbook.mode.search"
|
ng-show="addressbook.mode.search"
|
||||||
sg-search="addressbook.selectedFolder.$filter(searchText, { search: searchField })"
|
sg-search="addressbook.selectedFolder.$filter(searchText, { search: searchField })"
|
||||||
sg-allow-dot="addressbook.selectedFolder.listRequiresDot">
|
sg-allow-dot="addressbook.selectedFolder.listRequiresDot"
|
||||||
|
sg-search-fields="addressbook.selectedFolder.searchFields">
|
||||||
<md-button class="md-icon-button"
|
<md-button class="md-icon-button"
|
||||||
sg-search-cancel="addressbook.cancelSearch()"
|
sg-search-cancel="addressbook.cancelSearch()"
|
||||||
label:aria-label="Back">
|
label:aria-label="Back">
|
||||||
|
@ -330,10 +331,13 @@
|
||||||
</div>
|
</div>
|
||||||
</md-input-container>
|
</md-input-container>
|
||||||
<md-input-container flex="25">
|
<md-input-container flex="25">
|
||||||
<md-select label:aria-label="Search scope">
|
<label><var:string label:value="Search scope"/></label>
|
||||||
<md-option value="name_or_address" selected="selected"><var:string label:value="Name or Email"/></md-option>
|
<md-select multiple="multiple">
|
||||||
<md-option value="category"><var:string label:value="Category"/></md-option>
|
<md-optgroup label:label="Search scope">
|
||||||
<md-option value="organization"><var:string label:value="Organization"/></md-option>
|
<md-option
|
||||||
|
ng-value="field"
|
||||||
|
ng-repeat="field in ::addressbook.selectedFolder.searchFields">{{::field | loc}}</md-option>
|
||||||
|
</md-optgroup>
|
||||||
</md-select>
|
</md-select>
|
||||||
</md-input-container>
|
</md-input-container>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<md-input-container>
|
<md-input-container>
|
||||||
<input name="search" type="search"/>
|
<input name="search" type="search"/>
|
||||||
</md-input-container>
|
</md-input-container>
|
||||||
<md-select class="sg-toolbar-sort md-contrast-light">
|
<md-select multiple>
|
||||||
<md-option value="subject">Subject</md-option>
|
<md-option value="subject">Subject</md-option>
|
||||||
<md-option value="sender">sender</md-option>
|
<md-option value="sender">sender</md-option>
|
||||||
</md-select>
|
</md-select>
|
||||||
|
@ -67,6 +67,9 @@
|
||||||
// Associate the sg-allow-dot parameter (boolean) to the controller
|
// Associate the sg-allow-dot parameter (boolean) to the controller
|
||||||
controller.allowDot = $parse(iElement.attr('sg-allow-dot'))(scope);
|
controller.allowDot = $parse(iElement.attr('sg-allow-dot'))(scope);
|
||||||
|
|
||||||
|
// Associate the sg-search-fields parameter (array) to the controller
|
||||||
|
controller.fields = $parse(iElement.attr('sg-search-fields'))(scope);
|
||||||
|
|
||||||
// Associate callback to controller
|
// Associate callback to controller
|
||||||
controller.doSearch = $parse(iElement.attr('sg-search'));
|
controller.doSearch = $parse(iElement.attr('sg-search'));
|
||||||
|
|
||||||
|
@ -114,6 +117,14 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if ($element.attr('sg-search-fields')) {
|
||||||
|
var waitforFieldsOnce = $scope.$watch(vm.fields, function(value) {
|
||||||
|
// Select all fields by default
|
||||||
|
vm.searchField = _.clone(vm.fields);
|
||||||
|
waitforFieldsOnce();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Method to call on data changes
|
// Method to call on data changes
|
||||||
vm.onChange = function() {
|
vm.onChange = function() {
|
||||||
var form = $scope[vm.formName],
|
var form = $scope[vm.formName],
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
$Card: Card,
|
$Card: Card,
|
||||||
$$Acl: Acl,
|
$$Acl: Acl,
|
||||||
$Preferences: Preferences,
|
$Preferences: Preferences,
|
||||||
$query: {search: 'name_or_address', value: '', sort: 'c_cn', asc: 1},
|
$query: {value: '', sort: 'c_cn', asc: 1},
|
||||||
activeUser: Settings.activeUser(),
|
activeUser: Settings.activeUser(),
|
||||||
selectedFolder: null,
|
selectedFolder: null,
|
||||||
$refreshTimeout: null
|
$refreshTimeout: null
|
||||||
|
@ -497,7 +497,7 @@
|
||||||
query.value = search;
|
query.value = search;
|
||||||
|
|
||||||
return _this.$id().then(function(addressbookId) {
|
return _this.$id().then(function(addressbookId) {
|
||||||
var futureData = AddressBook.$$resource.fetch(addressbookId, 'view', query);
|
var futureData = AddressBook.$$resource.post(addressbookId, 'view', query);
|
||||||
|
|
||||||
if (dry) {
|
if (dry) {
|
||||||
return futureData.then(function(response) {
|
return futureData.then(function(response) {
|
||||||
|
|
Loading…
Reference in New Issue