(feat) we now handle optional and non-required attendee states

pull/249/head
Ludovic Marcotte 2019-02-04 11:52:26 -05:00
parent 2901560df9
commit 249671543e
9 changed files with 79 additions and 14 deletions

3
NEWS
View File

@ -3,7 +3,8 @@
Enhancements Enhancements
- [web] create card from sender or recipient address (#3002, #4610) - [web] create card from sender or recipient address (#3002, #4610)
- [core] baseDN now accept dynamic domain values (#3685) - [core] baseDN now accept dynamic domain values (#3685 - sponsored by iRedMail)
- [core] we now handle optional and non-required attendee states
Bug fixes Bug fixes
- [web] fixed all-day event dates with different timezone - [web] fixed all-day event dates with different timezone

View File

@ -62,3 +62,8 @@ vtodo_class2 = "(Confidential task)";
/* Resources */ /* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Cannot access resource: \"%{Cn} %{SystemEmail}\""; "Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Cannot access resource: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}."; "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}.";
/* Participation role */
"Your participation is required to this event" = "Your participation is required to this event";
"Your participation is optional to this event" = "Your participation is optional to this event";
"Your participation is not required to this event" = "Your participation is not required to this event";

View File

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2007-2016 Inverse inc. Copyright (C) 2007-2019 Inverse inc.
This file is part of SOGo This file is part of SOGo
@ -187,9 +187,17 @@
{ {
SOGoAppointmentObject *attendeeObject; SOGoAppointmentObject *attendeeObject;
iCalCalendar *iCalendarToSave; iCalCalendar *iCalendarToSave;
iCalPerson *attendee;
SOGoUser *user;
iCalendarToSave = nil; iCalendarToSave = nil;
user = [SOGoUser userWithLogin: theUID];
attendeeObject = [self _lookupEvent: [newEvent uid] forUID: theUID]; attendeeObject = [self _lookupEvent: [newEvent uid] forUID: theUID];
attendee = [newEvent userAsAttendee: user];
// If the atttende's role is NON-PARTICIPANT, we write nothing to its calendar
if ([[attendee role] caseInsensitiveCompare: @"NON-PARTICIPANT"] == NSOrderedSame)
return;
if ([newEvent recurrenceId]) if ([newEvent recurrenceId])
{ {
@ -197,12 +205,11 @@
if ([attendeeObject isNew]) if ([attendeeObject isNew])
{ {
iCalEvent *ownerEvent; iCalEvent *ownerEvent;
SOGoUser *user;
// We check if the attendee that was added to a single occurence is // We check if the attendee that was added to a single occurence is
// present in the master component. If not, we create a calendar with // present in the master component. If not, we create a calendar with
// a single event for the occurence. // a single event for the occurence.
ownerEvent = [[[newEvent parent] events] objectAtIndex: 0]; ownerEvent = [[[newEvent parent] events] objectAtIndex: 0];
user = [SOGoUser userWithLogin: theUID];
if (![ownerEvent userAsAttendee: user]) if (![ownerEvent userAsAttendee: user])
{ {
@ -339,7 +346,7 @@
} }
} }
else else
[self errorWithFormat: @"Unable to find event with UID %@ in %@'s calendar - skipping delete operation", nameInContainer, theUID]; [self errorWithFormat: @"Unable to find event with UID %@ in %@'s calendar - skipping delete operation. This can be normal for NON-PARTICIPANT attendees.", nameInContainer, theUID];
} }
} }
@ -1171,6 +1178,10 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
NSException *error; NSException *error;
BOOL addDelegate, removeDelegate; BOOL addDelegate, removeDelegate;
// If the atttende's role is NON-PARTICIPANT, we write nothing to its calendar
if ([[attendee role] caseInsensitiveCompare: @"NON-PARTICIPANT"] == NSOrderedSame)
return;
error = nil; error = nil;
eventObject = [self _lookupEvent: eventUID forUID: uid]; eventObject = [self _lookupEvent: eventUID forUID: uid];

View File

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2010-2012 Inverse Copyright (C) 2010-2019 Inverse
This file is part of SOGo This file is part of SOGo

View File

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2006-2018 Inverse inc. Copyright (C) 2006-2019 Inverse inc.
This file is part of SOGo. This file is part of SOGo.
@ -28,6 +28,7 @@
@class NSMutableDictionary; @class NSMutableDictionary;
@class NSString; @class NSString;
@class NSTimeZone; @class NSTimeZone;
@class iCalPerson;
@class iCalRepeatableEntityObject; @class iCalRepeatableEntityObject;
@class SOGoDateFormatter; @class SOGoDateFormatter;
@ -46,6 +47,7 @@
NSCalendarDate *oldEndDate; NSCalendarDate *oldEndDate;
NSCalendarDate *newEndDate; NSCalendarDate *newEndDate;
NSString *organizerName; NSString *organizerName;
iCalPerson *currentAttendee;
NSMutableDictionary *values; NSMutableDictionary *values;
SOGoDateFormatter *dateFormatter; SOGoDateFormatter *dateFormatter;
} }
@ -61,6 +63,9 @@
- (void) setOrganizerName: (NSString *) theString; - (void) setOrganizerName: (NSString *) theString;
- (NSString *) organizerName; - (NSString *) organizerName;
- (void) setCurrentAttendee: (iCalPerson *) theAttendee;
- (iCalPerson *) currentAttendee;
- (NSCalendarDate *) oldStartDate; - (NSCalendarDate *) oldStartDate;
- (NSCalendarDate *) newStartDate; - (NSCalendarDate *) newStartDate;

View File

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2006-2013 Inverse inc. Copyright (C) 2006-2019 Inverse inc.
Copyright (C) 2000-2005 SKYRIX Software AG Copyright (C) 2000-2005 SKYRIX Software AG
This file is part of SOGo. This file is part of SOGo.
@ -48,6 +48,8 @@
apt = nil; apt = nil;
values = nil; values = nil;
dateFormatter = RETAIN([[context activeUser] dateFormatterInContext: context]); dateFormatter = RETAIN([[context activeUser] dateFormatterInContext: context]);
organizerName = nil;
currentAttendee = nil;
} }
return self; return self;
@ -59,6 +61,7 @@
[apt release]; [apt release];
[previousApt release]; [previousApt release];
[organizerName release]; [organizerName release];
[currentAttendee release];
[viewTZ release]; [viewTZ release];
[oldStartDate release]; [oldStartDate release];
[newStartDate release]; [newStartDate release];
@ -164,6 +167,16 @@
return organizerName; return organizerName;
} }
- (void) setCurrentAttendee: (iCalPerson *) theAttendee
{
ASSIGN(currentAttendee, theAttendee);
}
- (iCalPerson *) currentAttendee
{
return currentAttendee;
}
- (NSString *) sentByText - (NSString *) sentByText
{ {
NSDictionary *sentByValues; NSDictionary *sentByValues;
@ -293,4 +306,21 @@
return nil; return nil;
} }
- (NSString *) getParticipationRole
{
if ([[currentAttendee role] caseInsensitiveCompare: @"OPT-PARTICIPANT"] == NSOrderedSame)
{
return [self labelForKey: @"Your participation is optional to this event"
inContext: context];
}
else if ([[currentAttendee role] caseInsensitiveCompare: @"NON-PARTICIPANT"] == NSOrderedSame)
{
return [self labelForKey: @"Your participation is not required to this event"
inContext: context];
}
return [self labelForKey: @"Your participation is required to this event"
inContext: context];
}
@end @end

View File

@ -1,6 +1,6 @@
/* SOGoCalendarComponent.m - this file is part of SOGo /* SOGoCalendarComponent.m - this file is part of SOGo
* *
* Copyright (C) 2006-2016 Inverse inc. * Copyright (C) 2006-2019 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
@ -846,6 +846,7 @@
p = [app pageWithName: pageName inContext: context]; p = [app pageWithName: pageName inContext: context];
[p setApt: (iCalEvent *) object]; [p setApt: (iCalEvent *) object];
[p setPreviousApt: (iCalEvent *) previousObject]; [p setPreviousApt: (iCalEvent *) previousObject];
[p setCurrentAttendee: attendee];
if ([[object organizer] cn] && [[[object organizer] cn] length]) if ([[object organizer] cn] && [[[object organizer] cn] length])
{ {
@ -893,9 +894,10 @@
/* attach text part to multipart body */ /* attach text part to multipart body */
[body addBodyPart: bodyPart]; [body addBodyPart: bodyPart];
/* attach calendar part to multipart body */ /* attach calendar part to multipart body only if the participation role is not NON-PARTICIPANT */
[body addBodyPart: eventBodyPart]; if ([[attendee role] caseInsensitiveCompare: @"NON-PARTICIPANT"] != NSOrderedSame)
[body addBodyPart: eventBodyPart];
/* attach multipart body to message */ /* attach multipart body to message */
[msg setBody: body]; [msg setBody: body];
[body release]; [body release];

View File

@ -20,7 +20,12 @@ th, td { font-family: Lucida Grande, Bitstream VeraSans, Tahoma, sans-serif; fon
value="getSubject" const:escapeHTML="NO"/> <small style="font-size: 12px; color: #999;"><var:string value="getSubject" const:escapeHTML="NO"/> <small style="font-size: 12px; color: #999;"><var:string
value="organizerName" const:escapeHTML="NO"/><var:string value="sentByText" const:escapeHTML="NO"/></small></h1></td> value="organizerName" const:escapeHTML="NO"/><var:string value="sentByText" const:escapeHTML="NO"/></small></h1></td>
</tr> </tr>
<var:if condition="location.length" <tr>
<th></th>
<td><h1 style="font-size: 14px; font-weight: normal;
padding-bottom: 9px; border-bottom: 1px solid #ccc;"><var:string
value="getParticipationRole" const:escapeHTML="NO"/></h1></td>
</tr> <var:if condition="location.length"
><tr> ><tr>
<th align="right" style="font-weight: bold;"><var:string label:value="location_label" const:escapeHTML="NO"/></th> <th align="right" style="font-weight: bold;"><var:string label:value="location_label" const:escapeHTML="NO"/></th>
<td><var:string value="location" const:escapeHTML="NO"/></td> <td><var:string value="location" const:escapeHTML="NO"/></td>

View File

@ -20,6 +20,12 @@ th, td { font-family: Lucida Grande, Bitstream VeraSans, Tahoma, sans-serif; fon
value="getSubject" const:escapeHTML="NO"/> <small style="font-size: 12px; color: #999;"><var:string value="getSubject" const:escapeHTML="NO"/> <small style="font-size: 12px; color: #999;"><var:string
value="organizerName" const:escapeHTML="NO"/><var:string value="sentByText" const:escapeHTML="NO"/></small></h1></td> value="organizerName" const:escapeHTML="NO"/><var:string value="sentByText" const:escapeHTML="NO"/></small></h1></td>
</tr> </tr>
<tr>
<th></th>
<td><h1 style="font-size: 14px; font-weight: normal;
padding-bottom: 9px; border-bottom: 1px solid #ccc;"><var:string
value="getParticipationRole" const:escapeHTML="NO"/></h1></td>
</tr>
<tr> <tr>
<th></th> <th></th>
<td><var:string value="bodyStartText" const:escapeHTML="NO"/></td> <td><var:string value="bodyStartText" const:escapeHTML="NO"/></td>