Fix handling of contact organizations

Fixes #4028
pull/234/head
Francis Lachapelle 2017-03-03 10:35:23 -05:00
parent 79a0d5e133
commit 25430a8128
8 changed files with 121 additions and 103 deletions

1
NEWS
View File

@ -16,6 +16,7 @@ Bug fixes
- [web] SOGoCalendarWeekdays must now be defined before saving preferences - [web] SOGoCalendarWeekdays must now be defined before saving preferences
- [web] fixed CAS session timeout handling during XHR requests (#1456) - [web] fixed CAS session timeout handling during XHR requests (#1456)
- [web] exposed default value of SOGoMailAutoSave (#4053) - [web] exposed default value of SOGoMailAutoSave (#4053)
- [web] fixed handling of contact organizations (#4028)
3.2.7 (2017-02-14) 3.2.7 (2017-02-14)
------------------ ------------------

View File

@ -1,6 +1,6 @@
/* NGVCard+SOGo.h - this file is part of SOGo /* NGVCard+SOGo.h - this file is part of SOGo
* *
* Copyright (C) 2009-2016 Inverse inc. * Copyright (C) 2009-2017 Inverse inc.
* *
* 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
@ -43,6 +43,9 @@
- (NSArray *) emails; - (NSArray *) emails;
- (NSArray *) secondaryEmails; - (NSArray *) secondaryEmails;
- (void) setOrganizations: (NSArray *) newOrganizations;
- (NSArray *) organizations;
- (NSString *) workPhone; - (NSString *) workPhone;
- (NSString *) homePhone; - (NSString *) homePhone;
- (NSString *) fax; - (NSString *) fax;

View File

@ -261,9 +261,9 @@ convention:
NSInteger year, yearOfToday, month, day; NSInteger year, yearOfToday, month, day;
CardElement *element; CardElement *element;
NSCalendarDate *now; NSCalendarDate *now;
NSArray *units; NSMutableArray *units;
NSString *fn, *ou; NSString *fn;
id o; id o, ou;
[self setNWithFamily: [ldifRecord objectForKey: @"sn"] [self setNWithFamily: [ldifRecord objectForKey: @"sn"]
given: [ldifRecord objectForKey: @"givenname"] given: [ldifRecord objectForKey: @"givenname"]
@ -306,13 +306,17 @@ convention:
ou = [ldifRecord objectForKey: @"ou"]; ou = [ldifRecord objectForKey: @"ou"];
if ([ou isKindOfClass: [NSArray class]]) if ([ou isKindOfClass: [NSArray class]])
units = [NSArray arrayWithArray: (NSArray *)ou]; units = [NSMutableArray arrayWithArray: (NSArray *)ou];
else if (ou) else if (ou)
units = [NSArray arrayWithObject: ou]; units = [NSMutableArray arrayWithObject: ou];
else else
units = nil; units = [NSMutableArray array];
[self setOrg: [ldifRecord objectForKey: @"o"] o = [ldifRecord objectForKey: @"o"];
units: units]; if ([o isKindOfClass: [NSArray class]])
[units addObjectsFromArray: (NSArray *)o];
else if (ou)
[units addObject: o];
[self setOrganizations: units];
[self _setPhoneValues: ldifRecord]; [self _setPhoneValues: ldifRecord];
[self _setEmails: ldifRecord]; [self _setEmails: ldifRecord];
@ -712,6 +716,53 @@ convention:
return company; return company;
} }
- (void) setOrganizations: (NSArray *) newOrganizations
{
CardElement *org;
NSArray *elements;
NSUInteger count, max;
// First remove all current org properties
elements = [self childrenWithTag: @"org"];
[self removeChildren: elements];
org = [self uniqueChildWithTag: @"org"];
max = [newOrganizations count];
for (count = 0; count < max; count++)
{
[org setSingleValue: [newOrganizations objectAtIndex: count]
atIndex: count forKey: @""];
}
}
- (NSArray *) organizations
{
CardElement *org;
NSArray *organizations;
NSEnumerator *orgs;
NSMutableArray *flattenedOrganizations;
NSString *value;
NSUInteger count, max;
// Get all org properties
orgs = [[self childrenWithTag: @"org"] objectEnumerator];
flattenedOrganizations = [NSMutableArray array];
while ((org = [orgs nextObject]))
{
// Get all values of each org property
organizations = [org valuesForKey: @""];
max = [organizations count];
for (count = 0; count < max; count++)
{
value = [[organizations objectAtIndex: count] lastObject];
if ([value length])
[flattenedOrganizations addObject: value];
}
}
return flattenedOrganizations;
}
- (NSString *) fullName - (NSString *) fullName
{ {
CardElement *n; CardElement *n;

View File

@ -1,6 +1,6 @@
/* /*
Copyright (C) 2004-2005 SKYRIX Software AG Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2005-2015 Inverse inc. Copyright (C) 2005-2017 Inverse inc.
This file is part of SOGo This file is part of SOGo
@ -339,7 +339,7 @@ static Class SOGoContactGCSEntryK = Nil;
{ {
CardElement *element; CardElement *element;
NSArray *elements, *values; NSArray *elements, *values;
NSMutableArray *units, *categories; NSMutableArray *orgs, *categories;
NSCalendarDate *date; NSCalendarDate *date;
id o; id o;
unsigned int i, year, month, day; unsigned int i, year, month, day;
@ -398,27 +398,13 @@ static Class SOGoContactGCSEntryK = Nil;
} }
} }
if ([[attributes objectForKey: @"orgUnits"] isKindOfClass: [NSArray class]]) if ([[attributes objectForKey: @"orgs"] isKindOfClass: [NSArray class]])
{ orgs = [NSMutableArray arrayWithArray: [attributes objectForKey: @"orgs"]];
elements = [card childrenWithTag: @"org"];
[card removeChildren: elements];
values = [attributes objectForKey: @"orgUnits"];
units = [NSMutableArray arrayWithCapacity: [values count]];
for (i = 0; i < [values count]; i++)
{
o = [values objectAtIndex: i];
if ([o isKindOfClass: [NSDictionary class]])
{
[units addObject: [o objectForKey: @"value"]];
}
}
}
else else
{ orgs = [NSMutableArray array];
units = nil; if ([[attributes objectForKey: @"org"] length])
} [orgs insertObject: [attributes objectForKey: @"org"] atIndex: 0];
[card setOrg: [attributes objectForKey: @"c_org"] [card setOrganizations: orgs];
units: units];
elements = [card childrenWithTag: @"tel"]; elements = [card childrenWithTag: @"tel"];
[card removeChildren: elements]; [card removeChildren: elements];
@ -520,7 +506,9 @@ static Class SOGoContactGCSEntryK = Nil;
* @apiParam {String} c_cn Fullname * @apiParam {String} c_cn Fullname
* @apiParam {String} c_screenname Screen Name (X-AIM for now) * @apiParam {String} c_screenname Screen Name (X-AIM for now)
* @apiParam {String} tz Timezone * @apiParam {String} tz Timezone
* @apiParam {String} note Note * @apiParam {String} org Main organization
* @apiParam {String[]} orgs Additional organizations
* @apiParam {String[]} notes Notes
* @apiParam {String[]} allCategories All available categories * @apiParam {String[]} allCategories All available categories
* @apiParam {Object[]} categories Categories assigned to the card * @apiParam {Object[]} categories Categories assigned to the card
* @apiParam {String} categories.value Category name * @apiParam {String} categories.value Category name

View File

@ -1,18 +1,18 @@
/* /*
Copyright (C) 2005-2016 Inverse inc. Copyright (C) 2005-2017 Inverse inc.
This file is part of SOGo. This file is part of SOGo.
SOGo is free software; you can redistribute it and/or modify it under SOGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any Free Software Foundation; either version 2, or (at your option) any
later version. later version.
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details. License for more details.
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
@ -69,7 +69,7 @@
categoryLabels = [[self labelForKey: @"contacts_category_labels"] componentsSeparatedByString: @","]; categoryLabels = [[self labelForKey: @"contacts_category_labels"] componentsSeparatedByString: @","];
if (!categoryLabels) if (!categoryLabels)
categoryLabels = [NSArray array]; categoryLabels = [NSArray array];
return [categoryLabels trimmedComponents]; return [categoryLabels trimmedComponents];
} }
@ -101,33 +101,6 @@
return cats; return cats;
} }
- (NSArray *) orgUnits
{
NSMutableArray *orgUnits;
NSArray *values;
CardElement *org;
NSString *service;
NSUInteger count, max;
org = [card org];
values = [org valuesForKey: @""];
max = [values count];
if (max > 1)
{
orgUnits = [NSMutableArray arrayWithCapacity: max];
for (count = 1; count < max; count++)
{
service = [org flattenedValueAtIndex: count forKey: @""];
if ([service length] > 0)
[orgUnits addObject: [NSDictionary dictionaryWithObject: service forKey: @"value"]];
}
}
else
orgUnits = nil;
return orgUnits;
}
- (NSArray *) categories - (NSArray *) categories
{ {
NSMutableArray *categories; NSMutableArray *categories;
@ -267,9 +240,13 @@
* @apiSuccess (Success 200) {String} [nickname] Nickname * @apiSuccess (Success 200) {String} [nickname] Nickname
* @apiSuccess (Success 200) {String} [c_sn] Lastname * @apiSuccess (Success 200) {String} [c_sn] Lastname
* @apiSuccess (Success 200) {String} [c_fn] Fullname * @apiSuccess (Success 200) {String} [c_fn] Fullname
* @apiSuccess (Success 200) {String} [title] Title
* @apiSuccess (Success 200) {String} [role] Role
* @apiSuccess (Success 200) {String} [c_screenname] Screen Name (X-AIM for now) * @apiSuccess (Success 200) {String} [c_screenname] Screen Name (X-AIM for now)
* @apiSuccess (Success 200) {String} [tz] Timezone * @apiSuccess (Success 200) {String} [tz] Timezone
* @apiSuccess (Success 200) {String} [notes] Notes * @apiSuccess (Success 200) {String} [org] Main organization
* @apiSuccess (Success 200) {String[]} [orgs] Additional organizations
* @apiSuccess (Success 200) {String[]} [notes] Notes
* @apiSuccess (Success 200) {String[]} allCategories All available categories * @apiSuccess (Success 200) {String[]} allCategories All available categories
* @apiSuccess (Success 200) {Object[]} [categories] Categories assigned to the card * @apiSuccess (Success 200) {Object[]} [categories] Categories assigned to the card
* @apiSuccess (Success 200) {String} categories.value Category name * @apiSuccess (Success 200) {String} categories.value Category name
@ -297,6 +274,7 @@
id <WOActionResults> result; id <WOActionResults> result;
id o; id o;
SOGoObject <SOGoContactObject> *contact; SOGoObject <SOGoContactObject> *contact;
NSArray *values;
NSMutableDictionary *data; NSMutableDictionary *data;
contact = [self clientObject]; contact = [self clientObject];
@ -341,12 +319,13 @@
o = [card role]; o = [card role];
if ([o length] > 0) if ([o length] > 0)
[data setObject: o forKey: @"role"]; [data setObject: o forKey: @"role"];
o = [self orgUnits]; values = [card organizations];
if ([o count] > 0) if ([values count])
[data setObject: o forKey: @"orgUnits"]; {
o = [card workCompany]; [data setObject: [values objectAtIndex: 0] forKey: @"org"];
if ([o length] > 0) if ([values count] > 1)
[data setObject: o forKey: @"c_org"]; [data setObject: [values subarrayWithRange: NSMakeRange(1, [values count] - 1)] forKey: @"orgs"];
}
o = [card birthday]; o = [card birthday];
if (o) if (o)
@ -376,7 +355,7 @@
result = [self responseWithStatus: 200 result = [self responseWithStatus: 200
andString: [data jsonRepresentation]]; andString: [data jsonRepresentation]];
return result; return result;
} }

View File

@ -78,7 +78,7 @@
<label> <label>
<var:string label:value="Organization"/> <var:string label:value="Organization"/>
</label> </label>
<input type="text" ng-model="editor.card.c_org"/> <input type="text" ng-model="editor.card.org"/>
</md-input-container> </md-input-container>
<md-input-container flex="40" flex-xs="100"> <md-input-container flex="40" flex-xs="100">
<label> <label>
@ -94,23 +94,23 @@
</md-input-container> </md-input-container>
</div> </div>
<!-- org units --> <!-- other orgs -->
<div class="attr" ng-repeat="unit in editor.card.orgUnits"> <div class="attr" ng-repeat="org in editor.card.orgs">
<div layout="row" layout-align="start center"> <div layout="row" layout-align="start center">
<md-button class="md-icon-button" type="button" ng-click="editor.card.$delete('orgUnits', $index)"> <md-button class="md-icon-button" type="button" ng-click="editor.card.$delete('orgs', $index)">
<md-icon>remove_circle</md-icon> <md-icon>remove_circle</md-icon>
</md-button> </md-button>
<md-input-container class="md-flex"> <md-input-container class="md-flex">
<label> <label>
<var:string label:value="Organization Unit"/> <var:string label:value="Organization Unit"/>
</label> </label>
<input type="text" ng-model="unit.value" <input type="text" ng-model="org.value"
sg-focus-on="orgUnit_{{$index}}"/> sg-focus-on="org_{{$index}}"/>
</md-input-container> </md-input-container>
</div> </div>
</div> </div>
<div class="md-layout-margin" layout="row" layout-align="start center"> <div class="md-layout-margin" layout="row" layout-align="start center">
<md-button class="md-icon-button" type="button" ng-click="editor.addOrgUnit($event)"> <md-button class="md-icon-button" type="button" ng-click="editor.addOrg($event)">
<md-icon>add_circle</md-icon> <md-icon>add_circle</md-icon>
</md-button> </md-button>
<label class="button-label"> <label class="button-label">

View File

@ -157,6 +157,8 @@
if (this.isgroup) if (this.isgroup)
this.c_component = 'vlist'; this.c_component = 'vlist';
this.$avatarIcon = this.$isList()? 'group' : 'person'; this.$avatarIcon = this.$isList()? 'group' : 'person';
if (data.orgs && data.orgs.length)
this.orgs = _.map(data.orgs, function(org) { return { 'value': org }; });
if (data.notes && data.notes.length) if (data.notes && data.notes.length)
this.notes = _.map(data.notes, function(note) { return { 'value': note }; }); this.notes = _.map(data.notes, function(note) { return { 'value': note }; });
else if (!this.notes || !this.notes.length) else if (!this.notes || !this.notes.length)
@ -300,8 +302,8 @@
names.push(this.c_sn); names.push(this.c_sn);
if (names.length > 0) if (names.length > 0)
fn = names.join(' '); fn = names.join(' ');
else if (this.c_org && this.c_org.length > 0) { else if (this.org && this.org.length > 0) {
fn = this.c_org; fn = this.org;
} }
else if (this.emails && this.emails.length > 0) { else if (this.emails && this.emails.length > 0) {
email = _.find(this.emails, function(i) { return i.value !== ''; }); email = _.find(this.emails, function(i) { return i.value !== ''; });
@ -317,12 +319,8 @@
var description = []; var description = [];
if (this.title) description.push(this.title); if (this.title) description.push(this.title);
if (this.role) description.push(this.role); if (this.role) description.push(this.role);
if (this.orgUnits && this.orgUnits.length > 0) if (this.org) description.push(this.org);
_.forEach(this.orgUnits, function(unit) { if (this.orgs) description = _.concat(description, _.map(this.orgs, 'value'));
if (unit.value !== '')
description.push(unit.value);
});
if (this.c_org) description.push(this.c_org);
if (this.description) description.push(this.description); if (this.description) description.push(this.description);
return description.join(', '); return description.join(', ');
@ -391,20 +389,14 @@
return this.c_component == 'vlist' && condition; return this.c_component == 'vlist' && condition;
}; };
Card.prototype.$addOrgUnit = function(orgUnit) { Card.prototype.$addOrg = function(org) {
if (angular.isUndefined(this.orgUnits)) { if (angular.isUndefined(this.orgs)) {
this.orgUnits = [{value: orgUnit}]; this.orgs = [org];
} }
else { else if (org != this.org && !_.includes(this.orgs, org)) {
for (var i = 0; i < this.orgUnits.length; i++) { this.orgs.push(org);
if (this.orgUnits[i].value == orgUnit) {
break;
}
}
if (i == this.orgUnits.length)
this.orgUnits.push({value: orgUnit});
} }
return this.orgUnits.length - 1; return this.orgs.length - 1;
}; };
// Card.prototype.$addCategory = function(category) { // Card.prototype.$addCategory = function(category) {
@ -615,6 +607,10 @@
card.birthday = ''; card.birthday = '';
} }
// We flatten the organizations to an array of strings
if (this.orgs)
card.orgs = _.map(this.orgs, 'value');
// We flatten the notes to an array of strings // We flatten the notes to an array of strings
if (this.notes) if (this.notes)
card.notes = _.map(this.notes, 'value'); card.notes = _.map(this.notes, 'value');

View File

@ -21,7 +21,7 @@
vm.categories = {}; vm.categories = {};
vm.userFilterResults = []; vm.userFilterResults = [];
vm.transformCategory = transformCategory; vm.transformCategory = transformCategory;
vm.addOrgUnit = addOrgUnit; vm.addOrg = addOrg;
vm.addBirthday = addBirthday; vm.addBirthday = addBirthday;
vm.addScreenName = addScreenName; vm.addScreenName = addScreenName;
vm.addEmail = addEmail; vm.addEmail = addEmail;
@ -71,9 +71,9 @@
else else
return input; return input;
} }
function addOrgUnit() { function addOrg() {
var i = vm.card.$addOrgUnit(''); var i = vm.card.$addOrg({ value: '' });
focus('orgUnit_' + i); focus('org_' + i);
} }
function addBirthday() { function addBirthday() {
vm.card.birthday = new Date(); vm.card.birthday = new Date();