mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-02-17 07:33:57 +00:00
Now possible to set alarms on event invitations
This commit is contained in:
@@ -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
1
NEWS
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
|
||||
//
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
37
SoObjects/Appointments/iCalAlarm+SOGo.h
Normal file
37
SoObjects/Appointments/iCalAlarm+SOGo.h
Normal 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
|
||||
127
SoObjects/Appointments/iCalAlarm+SOGo.m
Normal file
127
SoObjects/Appointments/iCalAlarm+SOGo.m
Normal 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
|
||||
@@ -248,6 +248,7 @@
|
||||
{
|
||||
response = (WOResponse*)[eventObject changeParticipationStatus: newStatus
|
||||
withDelegate: delegate
|
||||
alarm: nil
|
||||
forRecurrenceId: [chosenEvent recurrenceId]];
|
||||
// if (ex)
|
||||
// response = ex; //[self responseWithStatus: 500];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"; } )
|
||||
)
|
||||
@@ -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"; } )
|
||||
)
|
||||
@@ -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"; } )
|
||||
)
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
componentCalendar="componentCalendar"
|
||||
eventIsReadOnly="eventIsReadOnly"
|
||||
var:component="event"
|
||||
var:rsvpURL="rsvpURL"
|
||||
var:saveURL="saveURL">
|
||||
|
||||
<var:if condition="eventIsReadOnly" const:negate="YES">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user