Now possible to set alarms on event invitations

This commit is contained in:
Ludovic Marcotte
2014-12-16 09:20:27 -05:00
parent 3eac0f5261
commit fb6ef3aa8a
23 changed files with 461 additions and 281 deletions

View File

@@ -1391,7 +1391,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
participationStatus = @"DECLINED";
[appointmentObject changeParticipationStatus: participationStatus
withDelegate: nil];
withDelegate: nil
alarm: nil];
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];

1
NEWS
View File

@@ -4,6 +4,7 @@
New features
- Allow including or not freebusy info from subscribed calendars
- Now possible to set an autosave timer for draft messages
- Now possible to set alarms on event invitations (#76)
Enhancements
- updated CKEditor to version 4.4.6 and added the 'Source Area' plugin

View File

@@ -9,6 +9,7 @@ Appointments_PRINCIPAL_CLASS = SOGoAppointmentsProduct
Appointments_OBJC_FILES = \
Product.m \
NSArray+Appointments.m \
iCalAlarm+SOGo.m \
iCalCalendar+SOGo.m \
iCalEntityObject+SOGo.m \
iCalRepeatableEntityObject+SOGo.m \

View File

@@ -30,6 +30,7 @@
@class WORequest;
@class iCalAlarm;
@class iCalEvent;
@class iCalCalendar;
@@ -38,9 +39,12 @@
@interface SOGoAppointmentObject : SOGoCalendarComponent
- (NSException *) changeParticipationStatus: (NSString *) status
withDelegate: (iCalPerson *) delegate;
withDelegate: (iCalPerson *) delegate
alarm: (iCalAlarm *) alarm;
- (NSException *) changeParticipationStatus: (NSString *) status
withDelegate: (iCalPerson *) delegate
alarm: (iCalAlarm *) alarm
forRecurrenceId: (NSCalendarDate *) _recurrenceId;
//

View File

@@ -1309,6 +1309,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
return ex;
}
//
//
//
- (NSDictionary *) _caldavSuccessCodeWithRecipient: (NSString *) recipient
{
NSMutableArray *element;
@@ -1391,9 +1394,11 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
//
- (NSException *) changeParticipationStatus: (NSString *) status
withDelegate: (iCalPerson *) delegate
alarm: (iCalAlarm *) alarm
{
return [self changeParticipationStatus: status
withDelegate: delegate
alarm: alarm
forRecurrenceId: nil];
}
@@ -1402,6 +1407,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
//
- (NSException *) changeParticipationStatus: (NSString *) _status
withDelegate: (iCalPerson *) delegate
alarm: (iCalAlarm *) alarm
forRecurrenceId: (NSCalendarDate *) _recurrenceId
{
iCalCalendar *calendar;
@@ -1479,7 +1485,18 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
// Over DAV, it'll be handled directly in PUTAction:
if (![context request] || [[context request] handledByDefaultHandler]
|| [[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
ex = [self saveCalendar: [event parent]];
{
// If an alarm was specified, let's use it. This would happen if an attendee accepts/declines/etc. an
// event invitation and also sets an alarm along the way. This would happen ONLY from the web interface.
[event removeAllAlarms];
if (alarm)
{
[event addToAlarms: alarm];
}
ex = [self saveCalendar: [event parent]];
}
}
}
else
@@ -1512,17 +1529,17 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
[v caseInsensitiveCompare: @"CLIENT"] == NSOrderedSame)
b = NO;
}
//
// If we have to deal with Thunderbird/Lightning, we always send invitation
// reponses, as Lightning v2.6 (at least this version) sets SCHEDULE-AGENT
// to NONE/CLIENT when responding to an external invitation received by
// SOGo - so no invitation responses are ever sent by Lightning. See
// https://bugzilla.mozilla.org/show_bug.cgi?id=865726 and
// https://bugzilla.mozilla.org/show_bug.cgi?id=997784
//
userAgents = [[context request] headersForKey: @"User-Agent"];
//
// If we have to deal with Thunderbird/Lightning, we always send invitation
// reponses, as Lightning v2.6 (at least this version) sets SCHEDULE-AGENT
// to NONE/CLIENT when responding to an external invitation received by
// SOGo - so no invitation responses are ever sent by Lightning. See
// https://bugzilla.mozilla.org/show_bug.cgi?id=865726 and
// https://bugzilla.mozilla.org/show_bug.cgi?id=997784
//
userAgents = [[context request] headersForKey: @"User-Agent"];
for (i = 0; i < [userAgents count]; i++)
{
if ([[userAgents objectAtIndex: i] rangeOfString: @"Thunderbird"].location != NSNotFound &&
@@ -1533,7 +1550,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
}
}
return b;
}
@@ -1594,7 +1610,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
{
// The current user deletes the occurence; let the organizer know that
// the user has declined this occurence.
[self changeParticipationStatus: @"DECLINED" withDelegate: nil
[self changeParticipationStatus: @"DECLINED"
withDelegate: nil
alarm: nil
forRecurrenceId: recurrenceId];
send_receipt = NO;
}
@@ -2161,12 +2179,14 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
{
[self changeParticipationStatus: @"DECLINED"
withDelegate: nil // FIXME (specify delegate?)
alarm: nil
forRecurrenceId: [self _addedExDate: oldEvent newEvent: newEvent]];
}
else if (attendee)
{
[self changeParticipationStatus: [attendee partStat]
withDelegate: delegate
alarm: nil
forRecurrenceId: recurrenceId];
}
// All attendees and the organizer field were removed. Apple iCal does

View File

@@ -25,6 +25,7 @@
@class NSException;
@class iCalAlarm;
@class iCalCalendar;
@class iCalPerson;
@class iCalRepeatableEntityObject;
@@ -37,7 +38,8 @@
- (BOOL) isNew;
- (NSException *) changeParticipationStatus: (NSString *) newPartStat
withDelegate: (iCalPerson *) delegate;
withDelegate: (iCalPerson *) delegate
alarm: (iCalAlarm *) alarm;
@end

View File

@@ -215,6 +215,7 @@
they should be siblings or SOGoComponentOccurence the parent class of SOGoCalendarComponent...
- (NSException *) changeParticipationStatus: (NSString *) newStatus
withDelegate: (iCalPerson *) delegate
alarm: (iCalAlarm *) alarm
{
NSCalendarDate *date;
@@ -222,6 +223,7 @@
return [container changeParticipationStatus: newStatus
withDelegate: delegate
alarm: alarm
forRecurrenceId: date];
}

View File

@@ -0,0 +1,37 @@
/* iCalAlarm+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2014 Inverse inc.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import <NGCards/iCalAlarm.h>
@class iCalRepeatableEntityObject;
@interface iCalAlarm (SOGoExtensions)
+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity
owner: (NSString *) theOwner
action: (NSString *) reminderAction
unit: (NSString *) reminderUnit
quantity: (NSString *) reminderQuantity
reference: (NSString *) reminderReference
reminderRelation: (NSString *) reminderRelation
emailAttendees: (BOOL) reminderEmailAttendees
emailOrganizer: (BOOL) reminderEmailOrganizer;
@end

View File

@@ -0,0 +1,127 @@
/* iCalAlarm+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2014 Inverse inc.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import "iCalAlarm+SOGo.h"
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <SOGo/SOGoUser.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalTrigger.h>
@implementation iCalAlarm (SOGoExtensions)
- (void) _appendAttendees: (NSArray *) attendees
toEmailAlarm: (iCalAlarm *) alarm
{
NSMutableArray *aAttendees;
int count, max;
iCalPerson *currentAttendee, *aAttendee;
max = [attendees count];
aAttendees = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
currentAttendee = [attendees objectAtIndex: count];
aAttendee = [iCalPerson elementWithTag: @"attendee"];
[aAttendee setCn: [currentAttendee cn]];
[aAttendee setEmail: [currentAttendee rfc822Email]];
[aAttendees addObject: aAttendee];
}
[alarm setAttendees: aAttendees];
}
- (void) _appendOrganizerToEmailAlarm: (iCalAlarm *) alarm
owner: (NSString *) uid
{
NSDictionary *ownerIdentity;
iCalPerson *aAttendee;
ownerIdentity = [[SOGoUser userWithLogin: uid roles: nil]
defaultIdentity];
aAttendee = [iCalPerson elementWithTag: @"attendee"];
[aAttendee setCn: [ownerIdentity objectForKey: @"fullName"]];
[aAttendee setEmail: [ownerIdentity objectForKey: @"email"]];
[alarm addChild: aAttendee];
}
+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity
owner: (NSString *) theOwner
action: (NSString *) reminderAction
unit: (NSString *) reminderUnit
quantity: (NSString *) reminderQuantity
reference: (NSString *) reminderReference
reminderRelation: (NSString *) reminderRelation
emailAttendees: (BOOL) reminderEmailAttendees
emailOrganizer: (BOOL) reminderEmailOrganizer
{
iCalTrigger *aTrigger;
iCalAlarm *anAlarm;
NSString *aValue;
anAlarm = [[self alloc] init];
aTrigger = [iCalTrigger elementWithTag: @"TRIGGER"];
[aTrigger setValueType: @"DURATION"];
[anAlarm setTrigger: aTrigger];
if ([reminderAction length] > 0 && [reminderUnit length] > 0)
{
[anAlarm setAction: [reminderAction uppercaseString]];
if ([reminderAction isEqualToString: @"email"])
{
[anAlarm removeAllAttendees];
if (reminderEmailAttendees)
[anAlarm _appendAttendees: [theEntity attendees]
toEmailAlarm: anAlarm];
if (reminderEmailOrganizer)
[anAlarm _appendOrganizerToEmailAlarm: anAlarm owner: theOwner];
[anAlarm setSummary: [theEntity summary]];
[anAlarm setComment: [theEntity comment]];
}
if ([reminderReference caseInsensitiveCompare: @"BEFORE"] == NSOrderedSame)
aValue = [NSString stringWithString: @"-P"];
else
aValue = [NSString stringWithString: @"P"];
if ([reminderUnit caseInsensitiveCompare: @"MINUTES"] == NSOrderedSame ||
[reminderUnit caseInsensitiveCompare: @"HOURS"] == NSOrderedSame)
aValue = [aValue stringByAppendingString: @"T"];
aValue = [aValue stringByAppendingFormat: @"%i%@",
[reminderQuantity intValue],
[reminderUnit substringToIndex: 1]];
[aTrigger setSingleValue: aValue forKey: @""];
[aTrigger setRelationType: reminderRelation];
}
else
{
[anAlarm release];
anAlarm = nil;
}
return AUTORELEASE(anAlarm);
}
@end

View File

@@ -248,6 +248,7 @@
{
response = (WOResponse*)[eventObject changeParticipationStatus: newStatus
withDelegate: delegate
alarm: nil
forRecurrenceId: [chosenEvent recurrenceId]];
// if (ex)
// response = ex; //[self responseWithStatus: 500];

View File

@@ -52,9 +52,6 @@ SchedulerUI_RESOURCE_FILES += \
SchedulerUI_RESOURCE_FILES += \
Toolbars/SOGoAppointmentFolders.toolbar \
Toolbars/SOGoAppointmentObject.toolbar \
Toolbars/SOGoAppointmentObjectAccept.toolbar \
Toolbars/SOGoAppointmentObjectDecline.toolbar \
Toolbars/SOGoAppointmentObjectAcceptOrDecline.toolbar \
Toolbars/SOGoTaskObject.toolbar \
Toolbars/SOGoComponentClose.toolbar \
Toolbars/SOGoEmpty.toolbar

View File

@@ -1,7 +0,0 @@
( /* the toolbar groups -*-cperl-*- */
( { link = "#";
isSafe = NO;
label = "accept";
onclick = "return modifyEvent(this, 'accept');";
image = "tb-ab-properties-flat-24x24.png"; } )
)

View File

@@ -1,12 +0,0 @@
( /* the toolbar groups -*-cperl-*- */
( { link = "#";
isSafe = NO;
label = "accept";
onclick = "return modifyEvent(this, 'accept');";
image = "tb-ab-properties-flat-24x24.png"; },
{ link = "#";
isSafe = NO;
label = "decline";
onclick = "return modifyEvent(this, 'decline');";
image = "tb-mail-stop-flat-24x24.png"; } )
)

View File

@@ -1,7 +0,0 @@
( /* the toolbar groups -*-cperl-*- */
( { link = "#";
isSafe = NO;
label = "decline";
onclick = "return modifyEvent(this, 'decline');";
image = "tb-mail-stop-flat-24x24.png"; } )
)

View File

@@ -51,6 +51,7 @@
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <Appointments/iCalAlarm+SOGo.h>
#import <Appointments/iCalCalendar+SOGo.h>
#import <Appointments/iCalEntityObject+SOGo.h>
#import <Appointments/iCalPerson+SOGo.h>
@@ -80,7 +81,7 @@
isTransparent = NO;
sendAppointmentNotifications = YES;
componentCalendar = nil;
user = [[self context] activeUser];
ASSIGN (dateFormatter, [user dateFormatterInContext: context]);
}
@@ -111,6 +112,11 @@
return event;
}
- (NSString *) rsvpURL
{
return [NSString stringWithFormat: @"%@/rsvpAppointment",
[[self clientObject] baseURL]];
}
- (NSString *) saveURL
{
return [NSString stringWithFormat: @"%@/saveAsAppointment",
@@ -390,6 +396,137 @@
}
}
//
//
//
- (id <WOActionResults>) rsvpAction
{
iCalPerson *delegatedAttendee;
NSDictionary *message;
WOResponse *response;
WORequest *request;
iCalAlarm *anAlarm;
NSString *status;
int replyList, reminderList;
request = [context request];
message = [[request contentAsString] objectFromJSONString];
delegatedAttendee = nil;
anAlarm = nil;
status = nil;
replyList = [[message objectForKey: @"replyList"] intValue];
switch (replyList)
{
case 0:
status = @"ACCEPTED";
break;
case 1:
status = @"DECLINED";
break;
case 2:
status = @"NEEDS-ACTION";
break;
case 3:
status = @"TENTATIVE";
break;
case 4:
default:
{
NSString *delegatedEmail, *delegatedUid;
SOGoUser *user;
status = @"DELEGATED";
delegatedEmail = [[message objectForKey: @"delegatedTo"] stringByTrimmingSpaces];
if ([delegatedEmail length])
{
user = [context activeUser];
delegatedAttendee = [iCalPerson new];
[delegatedAttendee autorelease];
[delegatedAttendee setEmail: delegatedEmail];
delegatedUid = [delegatedAttendee uid];
if (delegatedUid)
{
SOGoUser *delegatedUser;
delegatedUser = [SOGoUser userWithLogin: delegatedUid];
[delegatedAttendee setCn: [delegatedUser cn]];
}
[delegatedAttendee setRole: @"REQ-PARTICIPANT"];
[delegatedAttendee setRsvp: @"TRUE"];
[delegatedAttendee setParticipationStatus: iCalPersonPartStatNeedsAction];
[delegatedAttendee setDelegatedFrom:
[NSString stringWithFormat: @"mailto:%@", [[user allEmails] objectAtIndex: 0]]];
}
else
return [NSException exceptionWithHTTPStatus: 400
reason: @"missing 'to' parameter"];
}
break;
}
// Extract the user alarm, if any
reminderList = [[message objectForKey: @"reminderList"] intValue];
if ([[message objectForKey: @"reminderList"] isEqualToString: @"WONoSelectionString"] || reminderList == 5 || reminderList == 10 || reminderList == 14)
{
// No selection, wipe alarm which will be done in changeParticipationStatus...
}
else if (reminderList == 15)
{
// Custom
anAlarm = [iCalAlarm alarmForEvent: [self event]
owner: [[self clientObject] ownerInContext: context]
action: [message objectForKey: @"reminderAction"]
unit: [message objectForKey: @"reminderUnit"]
quantity: [message objectForKey: @"reminderQuantity"]
reference: [message objectForKey: @"reminderReference"]
reminderRelation: [message objectForKey: @"reminderRelation"]
emailAttendees: [[message objectForKey: @"reminderEmailAttendees"] boolValue]
emailOrganizer: [[message objectForKey: @"reminderEmailOrganizer"] boolValue]];
}
else
{
// Standard
NSString *aValue;
aValue = [[UIxComponentEditor reminderValues] objectAtIndex: reminderList];
// Predefined alarm
if ([aValue length])
{
iCalTrigger *aTrigger;
anAlarm = [[[iCalAlarm alloc] init] autorelease];
aTrigger = [iCalTrigger elementWithTag: @"TRIGGER"];
[aTrigger setValueType: @"DURATION"];
[anAlarm setTrigger: aTrigger];
[anAlarm setAction: @"DISPLAY"];
[aTrigger setSingleValue: aValue forKey: @""];
}
}
response = (WOResponse *)[[self clientObject] changeParticipationStatus: status
withDelegate: delegatedAttendee
alarm: anAlarm];
if (!response)
response = [self responseWith204];
return response;
}
//
//
//
- (id <WOActionResults>) saveAction
{
SOGoAppointmentFolder *previousCalendar;
@@ -560,7 +697,7 @@
actionName = [[request requestHandlerPath] lastPathComponent];
return ([[self clientObject] conformsToProtocol: @protocol (SOGoComponentOccurence)]
&& [actionName hasPrefix: @"save"]);
&& ([actionName hasPrefix: @"save"] || [actionName hasPrefix: @"rsvp"]));
}
- (void) takeValuesFromRequest: (WORequest *) _rq
@@ -637,81 +774,4 @@
}
- (id) _statusChangeAction: (NSString *) newStatus
{
[[self clientObject] changeParticipationStatus: newStatus
withDelegate: nil];
return [self responseWith204];
}
- (id) acceptAction
{
return [self _statusChangeAction: @"ACCEPTED"];
}
- (id) declineAction
{
return [self _statusChangeAction: @"DECLINED"];
}
- (id) needsActionAction
{
return [self _statusChangeAction: @"NEEDS-ACTION"];
}
- (id) tentativeAction
{
return [self _statusChangeAction: @"TENTATIVE"];
}
- (id) delegateAction
{
// BOOL receiveUpdates;
NSString *delegatedEmail, *delegatedUid;
iCalPerson *delegatedAttendee;
SOGoUser *user;
WORequest *request;
WOResponse *response;
response = nil;
request = [context request];
delegatedEmail = [request formValueForKey: @"to"];
if ([delegatedEmail length])
{
user = [context activeUser];
delegatedAttendee = [iCalPerson new];
[delegatedAttendee autorelease];
[delegatedAttendee setEmail: delegatedEmail];
delegatedUid = [delegatedAttendee uid];
if (delegatedUid)
{
SOGoUser *delegatedUser;
delegatedUser = [SOGoUser userWithLogin: delegatedUid];
[delegatedAttendee setCn: [delegatedUser cn]];
}
[delegatedAttendee setRole: @"REQ-PARTICIPANT"];
[delegatedAttendee setRsvp: @"TRUE"];
[delegatedAttendee setParticipationStatus: iCalPersonPartStatNeedsAction];
[delegatedAttendee setDelegatedFrom:
[NSString stringWithFormat: @"mailto:%@", [[user allEmails] objectAtIndex: 0]]];
// receiveUpdates = [[request formValueForKey: @"receiveUpdates"] boolValue];
// if (receiveUpdates)
// [delegatedAttendee setRole: @"NON-PARTICIPANT"];
response = (WOResponse*)[[self clientObject] changeParticipationStatus: @"DELEGATED"
withDelegate: delegatedAttendee];
}
else
response = [NSException exceptionWithHTTPStatus: 400
reason: @"missing 'to' parameter"];
if (!response)
response = [self responseWith204];
return response;
}
@end

View File

@@ -38,6 +38,7 @@
id item;
id attendee;
NSString *rsvpURL;
NSString *saveURL;
NSMutableArray *calendarList;
NSDictionary *organizerProfile;
@@ -62,7 +63,7 @@
NSString *dateFormat;
NSMutableDictionary *jsonAttendees;
NSString *reminder;
NSString *reminderQuantity;
NSString *reminderUnit;
@@ -184,6 +185,8 @@
- (BOOL) isWriteableClientObject;
- (NSException *) validateObjectForStatusChange;
+ (NSArray *) reminderValues;
@end
#endif /* UIXCOMPONENTEDITOR_H */

View File

@@ -51,6 +51,7 @@
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSString+misc.h>
#import <Appointments/iCalAlarm+SOGo.h>
#import <Appointments/iCalEntityObject+SOGo.h>
#import <Appointments/iCalPerson+SOGo.h>
#import <Appointments/SOGoAppointmentFolder.h>
@@ -225,7 +226,7 @@ iRANGE(2);
[attendee release];
[jsonAttendees release];
[calendarList release];
[reminder release];
[reminderQuantity release];
[reminderUnit release];
@@ -683,12 +684,16 @@ iRANGE(2);
ASSIGN (ownerAsAttendee, [component findAttendeeWithEmail: (id)ownerEmail]);
}
}
// /* cycles */
// if ([component isRecurrent])
// {
// rrule = [[component recurrenceRules] objectAtIndex: 0];
// [self adjustCycleControlsForRRule: rrule];
// }
}
- (void) setRSVPURL: (NSString *) theURL
{
rsvpURL = theURL;
}
- (NSString *) rsvpURL
{
return rsvpURL;
}
- (void) setSaveURL: (NSString *) newSaveURL
@@ -2168,40 +2173,7 @@ RANGE(2);
}
}
- (void) _appendAttendees: (NSArray *) attendees
toEmailAlarm: (iCalAlarm *) alarm
{
NSMutableArray *aAttendees;
int count, max;
iCalPerson *currentAttendee, *aAttendee;
max = [attendees count];
aAttendees = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
currentAttendee = [attendees objectAtIndex: count];
aAttendee = [iCalPerson elementWithTag: @"attendee"];
[aAttendee setCn: [currentAttendee cn]];
[aAttendee setEmail: [currentAttendee rfc822Email]];
[aAttendees addObject: aAttendee];
}
[alarm setAttendees: aAttendees];
}
- (void) _appendOrganizerToEmailAlarm: (iCalAlarm *) alarm
{
NSString *uid;
NSDictionary *ownerIdentity;
iCalPerson *aAttendee;
uid = [[self clientObject] ownerInContext: context];
ownerIdentity = [[SOGoUser userWithLogin: uid roles: nil]
defaultIdentity];
aAttendee = [iCalPerson elementWithTag: @"attendee"];
[aAttendee setCn: [ownerIdentity objectForKey: @"fullName"]];
[aAttendee setEmail: [ownerIdentity objectForKey: @"email"]];
[alarm addChild: aAttendee];
}
- (void) takeValuesFromRequest: (WORequest *) _rq
inContext: (WOContext *) _ctx
@@ -2236,68 +2208,43 @@ RANGE(2);
[component removeAllAlarms];
else
{
iCalTrigger *aTrigger;
iCalAlarm *anAlarm;
NSString *aValue;
NSUInteger index;
anAlarm = [iCalAlarm new];
index = [reminderItems indexOfObject: reminder];
aTrigger = [iCalTrigger elementWithTag: @"TRIGGER"];
[aTrigger setValueType: @"DURATION"];
[anAlarm setTrigger: aTrigger];
aValue = [reminderValues objectAtIndex: index];
if ([aValue length]) {
// Predefined alarm
[anAlarm setAction: @"DISPLAY"];
[aTrigger setSingleValue: aValue forKey: @""];
}
else {
// Custom alarm
if ([reminderAction length] > 0 && [reminderUnit length] > 0)
{
[anAlarm setAction: [reminderAction uppercaseString]];
if ([reminderAction isEqualToString: @"email"])
{
[anAlarm removeAllAttendees];
if (reminderEmailAttendees)
[self _appendAttendees: [component attendees]
toEmailAlarm: anAlarm];
if (reminderEmailOrganizer)
[self _appendOrganizerToEmailAlarm: anAlarm];
[anAlarm setSummary: [component summary]];
[anAlarm setComment: [component comment]];
}
if ([reminderReference caseInsensitiveCompare: @"BEFORE"] == NSOrderedSame)
aValue = [NSString stringWithString: @"-P"];
else
aValue = [NSString stringWithString: @"P"];
if ([reminderUnit caseInsensitiveCompare: @"MINUTES"] == NSOrderedSame ||
[reminderUnit caseInsensitiveCompare: @"HOURS"] == NSOrderedSame)
aValue = [aValue stringByAppendingString: @"T"];
aValue = [aValue stringByAppendingFormat: @"%i%@",
[reminderQuantity intValue],
[reminderUnit substringToIndex: 1]];
[aTrigger setSingleValue: aValue forKey: @""];
[aTrigger setRelationType: reminderRelation];
}
else
{
[anAlarm release];
anAlarm = nil;
}
}
// Predefined alarm
if ([aValue length])
{
iCalTrigger *aTrigger;
anAlarm = [[[iCalAlarm alloc] init] autorelease];
aTrigger = [iCalTrigger elementWithTag: @"TRIGGER"];
[aTrigger setValueType: @"DURATION"];
[anAlarm setTrigger: aTrigger];
[anAlarm setAction: @"DISPLAY"];
[aTrigger setSingleValue: aValue forKey: @""];
}
else
{
// Custom alarm
anAlarm = [iCalAlarm alarmForEvent: component
owner: [[self clientObject] ownerInContext: context]
action: reminderAction
unit: reminderUnit
quantity: reminderQuantity
reference: reminderReference
reminderRelation: reminderRelation
emailAttendees: reminderEmailAttendees
emailOrganizer: reminderEmailOrganizer];
}
if (anAlarm)
{
[component removeAllAlarms];
[component addToAlarms: anAlarm];
[anAlarm release];
}
}
@@ -2597,4 +2544,9 @@ RANGE(2);
return response;
}
+ (NSArray *) reminderValues
{
return reminderValues;
}
@end

View File

@@ -241,6 +241,11 @@
pageName = "UIxAppointmentEditor";
actionName = "save";
};
rsvpAppointment = {
protectedBy = "RespondToComponent";
pageName = "UIxAppointmentEditor";
actionName = "rsvp";
};
saveAsAppointment = {
protectedBy = "ModifyComponent";
pageName = "UIxAppointmentEditor";
@@ -256,31 +261,6 @@
actionClass = "UIxComponentEditor";
actionName = "raw";
};
accept = {
protectedBy = "RespondToComponent";
pageName = "UIxAppointmentEditor";
actionName = "accept";
};
decline = {
protectedBy = "RespondToComponent";
pageName = "UIxAppointmentEditor";
actionName = "decline";
};
delegate = {
protectedBy = "RespondToComponent";
pageName = "UIxAppointmentEditor";
actionName = "delegate";
};
tentative = {
protectedBy = "RespondToComponent";
pageName = "UIxAppointmentEditor";
actionName = "tentative";
};
needsaction = {
protectedBy = "RespondToComponent";
pageName = "UIxAppointmentEditor";
actionName = "needsAction";
};
adjust = {
protectedBy = "ModifyComponent";
actionClass = "UIxAppointmentActions";

View File

@@ -11,6 +11,7 @@
componentCalendar="componentCalendar"
eventIsReadOnly="eventIsReadOnly"
var:component="event"
var:rsvpURL="rsvpURL"
var:saveURL="saveURL">
<var:if condition="eventIsReadOnly" const:negate="YES">

View File

@@ -182,6 +182,7 @@
</div>
</form>
</var:if>
<var:if condition="eventIsReadOnly">
<div class="popupMenu" id="contactsMenu">
<ul></ul>
@@ -234,7 +235,10 @@
></span>
</label>
</var:if>
<var:if condition="userHasRSVP">
<form var:href="rsvpURL" name="rsvpform">
<label><var:string label:value="Reply:" />
<span class="content"><var:popup list="replyList" item="item"
const:name="replyList"
@@ -250,7 +254,40 @@
<input type="checkbox" name="delegateReceiveUpdates" /> <var:string label:value="Keep sending me updates" /> -->
</span>
</label>
</var:if>
<input type="hidden" name="reminderQuantity"
id="reminderQuantity"
var:value="reminderQuantity"/>
<input type="hidden" name="reminderUnit"
id="reminderUnit"
var:value="reminderUnit"/>
<input type="hidden" name="reminderRelation"
id="reminderRelation"
var:value="reminderRelation"/>
<input type="hidden" name="reminderReference"
id="reminderReference"
var:value="reminderReference"/>
<input type="hidden" name="reminderAction"
id="reminderAction"
var:value="reminderAction"/>
<input type="hidden" name="reminderEmailOrganizer"
id="reminderEmailOrganizer"
var:value="reminderEmailOrganizer"/>
<input type="hidden" name="reminderEmailAttendees"
id="reminderEmailAttendees"
var:value="reminderEmailAttendees"/>
<label><var:string label:value="Reminder:" />
<span class="content"><var:popup list="reminderList" item="item"
const:disabledValue="-"
label:noSelectionString="reminder_NONE"
const:name="reminderList"
const:id="reminderList"
string="itemReminderText" var:selection="reminder"
/> <a href="#" id="reminderHref" style="display: none;"
><var:string label:value="Edit"/></a></span></label>
</form>
</var:if>
</div>
<var:if condition="hasAttendees">
<div id="attendeesDiv">

View File

@@ -529,20 +529,6 @@ function onMenuRawEvent(event) {
openGenericWindow.delay(0.1, url);
}
function modifyEvent(sender, modification, parameters) {
var currentLocation = '' + window.location;
var arr = currentLocation.split("/");
arr[arr.length-1] = modification;
document.modifyEventAjaxRequest = triggerAjaxRequest(arr.join("/"),
modifyEventCallback,
modification,
parameters,
{ "Content-type": "application/x-www-form-urlencoded" });
return false;
}
function closeInvitationWindow() {
var closeDiv = document.createElement("div");
document.body.appendChild(closeDiv);

View File

@@ -1,10 +1,8 @@
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
Copyright (C) 2005 SKYRIX Software AG
Copyright (C) 2006-2013 Inverse inc.
Copyright (C) 2006-2014 Inverse inc.
This file is part of SOGo.
SOGo is free software; you can redistribute it and/or modify it under

View File

@@ -386,26 +386,22 @@ function onPopupReminderWindow(event) {
}
function onOkButtonClick (e) {
var item = $("replyList");
var value = parseInt(item.options[item.selectedIndex].value);
var action = "";
var parameters = "";
if (value == 0)
action = 'accept';
else if (value == 1)
action = 'decline';
else if (value == 2)
action = 'needsaction';
else if (value == 3)
action = 'tentative';
else if (value == 4) {
var url = ApplicationBaseURL + "/" + activeCalendar + "/" + activeComponent;
delegateInvitation(url, modifyEventCallback);
}
var jsonData = Form.serialize(document.forms['rsvpform'], true);
if (action != "")
modifyEvent (item, action, parameters);
var input = $("delegatedTo");
if (input && input.readAttribute("uid") != null) {
jsonData['delegatedTo'] = input.readAttribute("uid");
}
triggerAjaxRequest(document.forms['rsvpform'].readAttribute("action"),
modifyEventCallback,
null,
Object.toJSON(jsonData),
{ "content-type": "application/json"}
);
return false;
}
function onCancelButtonClick (e) {