mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-04-06 22:08:51 +00:00
1
NEWS
1
NEWS
@@ -5,6 +5,7 @@ New features
|
||||
- [core] Debian 10 (Buster) support for x86_64 (#4775)
|
||||
- [core] now possible to specify which domains you can forward your mails to
|
||||
- [core] added support for S/MIME opaque signing (#4582)
|
||||
- [web] optionally expand LDAP groups in attendees editor (#2506)
|
||||
|
||||
Enhancements
|
||||
- [web] avoid saving an empty calendar name
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2005-2017 Inverse inc.
|
||||
Copyright (C) 2005-2019 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
@@ -31,8 +31,10 @@
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSCalendarDate+SOGo.h>
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/SOGoGroup.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
#import <SOGo/SOGoUserManager.h>
|
||||
|
||||
#import <Contacts/NGVCard+SOGo.h>
|
||||
#import <Contacts/SOGoContactObject.h>
|
||||
@@ -384,6 +386,55 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) membersAction
|
||||
{
|
||||
NSArray *allUsers;
|
||||
NSDictionary *dict;
|
||||
NSEnumerator *emails;
|
||||
NSMutableArray *allUsersData, *allUserEmails;
|
||||
NSMutableDictionary *userData;
|
||||
NSString *email;
|
||||
SOGoObject <SOGoContactObject> *contact;
|
||||
SOGoUser *user;
|
||||
id <WOActionResults> result;
|
||||
unsigned int i, max;
|
||||
|
||||
result = nil;
|
||||
contact = [self clientObject];
|
||||
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: [contact nameInContainer]];
|
||||
|
||||
if ([[dict objectForKey: @"isGroup"] boolValue])
|
||||
{
|
||||
SOGoGroup *aGroup;
|
||||
|
||||
aGroup = [SOGoGroup groupWithIdentifier: [contact nameInContainer]
|
||||
inDomain: [[context activeUser] domain]];
|
||||
allUsers = [aGroup members]; // array of SOGoUser objects
|
||||
max = [allUsers count];
|
||||
allUsersData = [NSMutableArray arrayWithCapacity: max];
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
user = [allUsers objectAtIndex: i];
|
||||
allUserEmails = [NSMutableArray array];
|
||||
emails = [[user allEmails] objectEnumerator];
|
||||
while ((email = [emails nextObject])) {
|
||||
[allUserEmails addObject: [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
email, @"value", @"work", @"type", nil]];
|
||||
}
|
||||
userData = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[user loginInDomain], @"c_uid",
|
||||
[user cn], @"c_cn",
|
||||
allUserEmails, @"emails", nil];
|
||||
[allUsersData addObject: userData];
|
||||
}
|
||||
dict = [NSDictionary dictionaryWithObject: allUsersData forKey: @"members"];
|
||||
result = [self responseWithStatus: 200
|
||||
andString: [dict jsonRepresentation]];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) hasPhoto
|
||||
{
|
||||
return [[self clientObject] hasPhoto];
|
||||
|
||||
@@ -66,9 +66,14 @@
|
||||
<sg-avatar-image class="md-avatar"
|
||||
sg-email="currentAttendee.email"
|
||||
size="40">{{ editor.defaultIconForAttendee(currentAttendee) }}</sg-avatar-image>
|
||||
<div class="sg-tile-content sg-padded--right">
|
||||
<div class="sg-tile-content">
|
||||
<div class="sg-md-subhead"><div>{{currentAttendee.name}}</div></div>
|
||||
<div class="sg-md-body"><div>{{currentAttendee.email}}</div></div>
|
||||
<div class="sg-md-body">
|
||||
<div>{{currentAttendee.email}}</div>
|
||||
<md-button class="sg-tile-thread" md-colors="::{ color: 'accent-600'}" ng-if="currentAttendee.isGroup" ng-click="editor.expandAttendee(currentAttendee)">
|
||||
<md-icon class="md-rotate-180-ccw" md-colors="::{ color: 'accent-600'}">add_box</md-icon><span ng-bind="currentAttendee.members.length"></span>
|
||||
</md-button>
|
||||
</div>
|
||||
</div>
|
||||
<md-button class="md-icon-button"
|
||||
label:aria-label="Delete"
|
||||
|
||||
@@ -143,6 +143,8 @@
|
||||
this.categories = [];
|
||||
this.c_screenname = null;
|
||||
angular.extend(this, data);
|
||||
if (!this.pid)
|
||||
this.pid = this.container;
|
||||
if (!this.$$fullname)
|
||||
this.$$fullname = this.$fullname();
|
||||
if (!this.$$email)
|
||||
@@ -211,12 +213,12 @@
|
||||
|
||||
/**
|
||||
* @function $reload
|
||||
* @memberof Message.prototype
|
||||
* @desc Fetch the viewable message body along with other metadata such as the list of attachments.
|
||||
* @memberof Card.prototype
|
||||
* @desc Fetch all available attributes of the contact.
|
||||
* @returns a promise of the HTTP operation
|
||||
*/
|
||||
Card.prototype.$reload = function() {
|
||||
var futureCardData;
|
||||
var _this = this, futureCardData;
|
||||
|
||||
if (this.$futureCardData)
|
||||
return this;
|
||||
@@ -226,6 +228,28 @@
|
||||
return this.$unwrap(futureCardData);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $members
|
||||
* @memberof Card.prototype
|
||||
* @desc Fetch members of the LDAP group.
|
||||
* @returns a promise that resolves with the members
|
||||
*/
|
||||
Card.prototype.$members = function() {
|
||||
var _this = this;
|
||||
|
||||
if (this.members)
|
||||
return Card.$q.when(this.members);
|
||||
|
||||
if (this.isgroup) {
|
||||
return Card.$$resource.fetch([this.pid, this.id].join('/'), 'members').then(function(data) {
|
||||
_this.members = _.map(data.members, function(member) {
|
||||
return new Card(member);
|
||||
});
|
||||
return _this.members;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $save
|
||||
* @memberof Card.prototype
|
||||
@@ -383,7 +407,7 @@
|
||||
};
|
||||
|
||||
Card.prototype.$isList = function(options) {
|
||||
// isGroup attribute means it's a group of a LDAP source (not expandable on the client-side)
|
||||
// isGroup attribute means it's a group of a LDAP source (not automatically expanded on the client-side)
|
||||
var condition = (!options || !options.expandable || options.expandable && !this.isgroup);
|
||||
return this.c_component == 'vlist' && condition;
|
||||
};
|
||||
|
||||
@@ -181,6 +181,12 @@
|
||||
if (!_.find(this.attendees, function(o) {
|
||||
return o.email == attendee.email;
|
||||
})) {
|
||||
if (card.$isList()) {
|
||||
// LDAP list -- preload members
|
||||
card.$members().then(function(members) {
|
||||
attendee.members = members;
|
||||
});
|
||||
}
|
||||
attendee.image = Attendees.$gravatar(attendee.email, 32);
|
||||
if (this.component.attendees)
|
||||
this.component.attendees.push(attendee);
|
||||
|
||||
@@ -325,11 +325,26 @@
|
||||
};
|
||||
|
||||
function scrollToStart() {
|
||||
var dayElement = $element[0].querySelector('#freebusy_day_' + vm.component.start.getDayString());
|
||||
var scrollLeft = dayElement.offsetLeft - vm.attendeesEditor.containerElement.offsetLeft;
|
||||
vm.attendeesEditor.containerElement.scrollLeft = scrollLeft;
|
||||
var dayElement, scrollLeft;
|
||||
if (!vm.attendeesEditor.containerElement) {
|
||||
vm.attendeesEditor.containerElement = $element[0].querySelector('#freebusy');
|
||||
}
|
||||
if (vm.attendeesEditor.containerElement) {
|
||||
dayElement = $element[0].querySelector('#freebusy_day_' + vm.component.start.getDayString());
|
||||
scrollLeft = dayElement.offsetLeft - vm.attendeesEditor.containerElement.offsetLeft;
|
||||
vm.attendeesEditor.containerElement.scrollLeft = scrollLeft;
|
||||
}
|
||||
}
|
||||
|
||||
this.expandAttendee = function (attendee) {
|
||||
if (attendee.members.length > 0) {
|
||||
this.component.$attendees.remove(attendee);
|
||||
_.forEach(attendee.members, function (member) {
|
||||
vm.component.$attendees.add(member);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.removeAttendee = function (attendee, form) {
|
||||
this.component.$attendees.remove(attendee);
|
||||
if (this.component.$attendees.getLength() === 0)
|
||||
|
||||
@@ -31,11 +31,15 @@
|
||||
|
||||
$scope.$watch(
|
||||
function() {
|
||||
return $ctrl.component? [ _.pick($ctrl.component, watchedAttrs) ] : null;
|
||||
return $ctrl.component? {
|
||||
start: $ctrl.component.start,
|
||||
end: $ctrl.component.end,
|
||||
attendees: _.map($ctrl.component.attendees, 'email')
|
||||
} : null;
|
||||
},
|
||||
function(newId, oldId) {
|
||||
if ($ctrl.component) {
|
||||
// Component has changed
|
||||
function(newAttrs, oldAttrs) {
|
||||
if (newAttrs.attendees) {
|
||||
// Attendees have changed
|
||||
$q.all(_.values($ctrl.component.$attendees.$futureFreebusyData)).then(function() {
|
||||
$ctrl.onUpdate();
|
||||
});
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
});
|
||||
|
||||
this.parentController.onUpdate = function () {
|
||||
var freebusys = $ctrl.attendee.freebusy[$ctrl.day];
|
||||
var freebusys = $ctrl.attendee.uid ? $ctrl.attendee.freebusy[$ctrl.day] : null;
|
||||
|
||||
if (!$ctrl.attendee.uid) {
|
||||
_.forEach(hours, function(div) {
|
||||
@@ -92,7 +92,7 @@
|
||||
} else {
|
||||
quarters[index].classList.remove('event');
|
||||
}
|
||||
if (freebusys[hour][quarter]) {
|
||||
if (freebusys && freebusys[hour][quarter]) {
|
||||
busys[index].classList.remove('ng-hide');
|
||||
} else {
|
||||
busys[index].classList.add('ng-hide');
|
||||
|
||||
@@ -190,6 +190,7 @@ div.md-tile-left {
|
||||
min-height: auto;
|
||||
min-width: auto;
|
||||
padding: 0 3px !important;
|
||||
margin: 0;
|
||||
font-weight: $sg-font-medium;
|
||||
md-icon {
|
||||
font-size: sg-size(body);
|
||||
|
||||
Reference in New Issue
Block a user