2006-06-15 21:34:10 +02:00
/ *
2013-01-21 20:11:38 +01:00
Copyright ( C ) 2007 -2013 Inverse inc .
2006-06-15 21:34:10 +02:00
Copyright ( C ) 2004 -2005 SKYRIX Software AG
2011-04-25 12:31:08 +02:00
This file is part of SOGo
2006-06-15 21:34:10 +02:00
2009-04-30 23:17:55 +02:00
SOGo is free software ; you can redistribute it and / or modify it under
2006-06-15 21:34:10 +02:00
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation ; either version 2 , or ( at your option ) any
later version .
2009-04-30 23:17:55 +02:00
SOGo is distributed in the hope that it will be useful , but WITHOUT ANY
2006-06-15 21:34:10 +02:00
WARRANTY ; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE . See the GNU Lesser General Public
License for more details .
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
Free Software Foundation , 59 Temple Place - Suite 330 , Boston , MA
02111 -1307 , USA .
* /
2008-07-05 00:25:27 +02:00
# import < Foundation / NSCalendarDate . h >
2008-07-04 18:06:09 +02:00
# import < Foundation / NSDictionary . h >
# import < Foundation / NSEnumerator . h >
2010-03-12 21:24:59 +01:00
# import < Foundation / NSTimeZone . h >
2010-04-21 16:35:58 +02:00
# import < Foundation / NSValue . h >
2007-09-06 22:51:59 +02:00
2007-08-07 20:37:31 +02:00
# import < NGObjWeb / NSException + HTTP . h >
2007-11-18 11:16:25 +01:00
# import < NGObjWeb / WOContext + SoObjects . h >
2012-07-13 22:37:16 +02:00
# import < NGObjWeb / WOResponse . h >
# import < NGExtensions / NGCalendarDateRange . h >
2007-08-07 20:37:31 +02:00
# import < NGExtensions / NSNull + misc . h >
# import < NGExtensions / NSObject + Logs . h >
2006-10-11 20:58:16 +02:00
# import < NGCards / iCalCalendar . h >
2010-08-11 16:52:11 +02:00
# import < NGCards / iCalDateTime . h >
2006-10-11 20:58:16 +02:00
# import < NGCards / iCalEvent . h >
2006-12-14 22:20:13 +01:00
# import < NGCards / iCalEventChanges . h >
# import < NGCards / iCalPerson . h >
2012-07-13 22:37:16 +02:00
# import < NGCards / iCalRecurrenceCalculator . h >
2009-08-10 22:59:49 +02:00
# import < NGCards / NSCalendarDate + NGCards . h >
2008-07-05 00:25:27 +02:00
# import < SaxObjC / XMLNamespaces . h >
2006-10-31 19:54:56 +01:00
2012-07-13 22:37:16 +02:00
# import < NGCards / NSString + NGCards . h >
2009-09-10 20:31:20 +02:00
2012-07-10 02:29:13 +02:00
# import < SOGo / SOGoConstants . h >
2010-02-02 22:42:17 +01:00
# import < SOGo / SOGoUserManager . h >
# import < SOGo / NSArray + Utilities . h >
2011-04-25 12:31:08 +02:00
# import < SOGo / NSDictionary + Utilities . h >
2010-02-02 22:42:17 +01:00
# import < SOGo / NSObject + DAV . h >
# import < SOGo / SOGoObject . h >
# import < SOGo / SOGoPermissions . h >
# import < SOGo / SOGoGroup . h >
# import < SOGo / SOGoUser . h >
2010-04-20 21:45:16 +02:00
# import < SOGo / SOGoDomainDefaults . h >
2010-02-02 22:42:17 +01:00
# import < SOGo / SOGoWebDAVValue . h >
# import < SOGo / WORequest + SOGo . h >
2006-10-31 19:54:56 +01:00
2012-07-13 22:31:41 +02:00
# import "iCalCalendar+SOGo.h"
2007-11-18 11:16:25 +01:00
# import "iCalEventChanges+SOGo.h"
# import "iCalEntityObject+SOGo.h"
# import "iCalPerson+SOGo.h"
2008-07-17 23:12:43 +02:00
# import "NSArray+Appointments.h"
# import "SOGoAppointmentFolder.h"
# import "SOGoAppointmentOccurence.h"
2008-11-22 08:20:22 +01:00
# import "SOGoCalendarComponent.h"
2007-09-15 00:01:02 +02:00
2007-08-07 20:37:31 +02:00
# import "SOGoAppointmentObject.h"
2006-06-15 21:34:10 +02:00
@ implementation SOGoAppointmentObject
2007-02-15 21:59:02 +01:00
- ( NSString * ) componentTag
2006-11-07 19:15:42 +01:00
{
2007-02-15 21:59:02 +01:00
return @ "vevent" ;
2006-06-15 21:34:10 +02:00
}
2008-07-17 23:12:43 +02:00
- ( SOGoComponentOccurence * ) occurence : ( iCalRepeatableEntityObject * ) occ
{
2009-03-16 22:08:19 +01:00
NSArray * allEvents ;
allEvents = [ [ occ parent ] events ] ;
return [ SOGoAppointmentOccurence
occurenceWithComponent : occ
withMasterComponent : [ allEvents objectAtIndex : 0 ]
inContainer : self ] ;
2008-07-17 23:12:43 +02:00
}
2011-01-14 03:54:33 +01:00
/ * *
2011-06-01 19:49:12 +02:00
* Return a new exception in the recurrent event .
2011-01-14 03:54:33 +01:00
* @ param theRecurrenceID the ID of the occurence .
* @ return a new occurence .
* /
- ( iCalRepeatableEntityObject * ) newOccurenceWithID : ( NSString * ) theRecurrenceID
2008-07-17 23:12:43 +02:00
{
2010-03-12 21:24:59 +01:00
iCalEvent * newOccurence , * master ;
NSCalendarDate * date , * firstDate ;
2008-12-30 17:57:46 +01:00
unsigned int interval , nbrDays ;
2008-07-17 23:12:43 +02:00
2011-01-14 03:54:33 +01:00
newOccurence = ( iCalEvent * ) [ super newOccurenceWithID : theRecurrenceID ] ;
2008-07-17 23:12:43 +02:00
date = [ newOccurence recurrenceId ] ;
2010-03-12 21:24:59 +01:00
master = [ self component : NO secure : NO ] ;
firstDate = [ master startDate ] ;
interval = [ [ master endDate ]
timeIntervalSinceDate : firstDate ] ;
2008-12-30 17:57:46 +01:00
if ( [ newOccurence isAllDay ] )
{
2013-01-21 20:11:38 +01:00
nbrDays = ( ( float ) abs ( interval ) / 86400 ) ;
2008-12-30 17:57:46 +01:00
[ newOccurence setAllDayWithStartDate : date
2011-07-08 02:22:10 +02:00
duration : nbrDays ] ;
2008-12-30 17:57:46 +01:00
}
else
2010-03-12 21:24:59 +01:00
{
[ newOccurence setStartDate : date ] ;
[ newOccurence setEndDate : [ date addYear : 0
month : 0
day : 0
hour : 0
minute : 0
second : interval ] ] ;
}
2011-07-08 02:22:10 +02:00
2008-07-17 23:12:43 +02:00
return newOccurence ;
}
2012-07-13 22:31:41 +02:00
- ( iCalRepeatableEntityObject * ) lookupOccurrence : ( NSString * ) recID
{
return [ [ self calendar : NO secure : NO ] eventWithRecurrenceID : recID ] ;
}
2007-11-18 11:16:25 +01:00
- ( SOGoAppointmentObject * ) _lookupEvent : ( NSString * ) eventUID
forUID : ( NSString * ) uid
2007-01-31 21:15:28 +01:00
{
2007-11-18 11:16:25 +01:00
SOGoAppointmentFolder * folder ;
SOGoAppointmentObject * object ;
2009-03-23 22:19:55 +01:00
NSArray * folders ;
NSEnumerator * e ;
2007-11-18 11:16:25 +01:00
NSString * possibleName ;
2009-03-23 22:19:55 +01:00
object = nil ;
folders = [ container lookupCalendarFoldersForUID : uid ] ;
e = [ folders objectEnumerator ] ;
while ( object = = nil && ( folder = [ e nextObject ] ) )
2007-02-15 21:59:02 +01:00
{
2009-03-23 22:19:55 +01:00
object = [ folder lookupName : nameInContainer
inContext : context
acquire : NO ] ;
2014-05-28 17:39:25 +02:00
if ( [ object isKindOfClass : [ NSException class ] ] || [ object isNew ] )
2007-11-18 11:16:25 +01:00
{
2009-03-23 22:19:55 +01:00
possibleName = [ folder resourceNameForEventUID : eventUID ] ;
if ( possibleName )
{
object = [ folder lookupName : possibleName
inContext : context acquire : NO ] ;
2014-05-28 17:39:25 +02:00
if ( [ object isKindOfClass : [ NSException class ] ] || [ object isNew ] )
2009-03-23 22:19:55 +01:00
object = nil ;
}
else
2007-11-18 11:16:25 +01:00
object = nil ;
}
2006-06-15 21:34:10 +02:00
}
2007-02-15 21:59:02 +01:00
2007-11-18 11:16:25 +01:00
if ( ! object )
2008-07-14 18:47:10 +02:00
{
2009-05-06 23:57:21 +02:00
// Create the event in the user ' s personal calendar .
2010-07-15 19:05:29 +02:00
folder = [ [ SOGoUser userWithLogin : uid ]
personalCalendarFolderInContext : context ] ;
2008-07-14 18:47:10 +02:00
object = [ SOGoAppointmentObject objectWithName : nameInContainer
inContainer : folder ] ;
[ object setIsNew : YES ] ;
}
2006-06-15 21:34:10 +02:00
2007-11-18 11:16:25 +01:00
return object ;
}
2006-06-15 21:34:10 +02:00
2011-03-23 21:14:33 +01:00
//
//
//
2008-11-26 13:20:26 +01:00
- ( void ) _addOrUpdateEvent : ( iCalEvent * ) theEvent
forUID : ( NSString * ) theUID
owner : ( NSString * ) theOwner
2007-08-24 00:11:19 +02:00
{
2008-11-26 13:20:26 +01:00
if ( ! [ theUID isEqualToString : theOwner ] )
2007-11-08 00:35:26 +01:00
{
2009-06-02 23:51:17 +02:00
SOGoAppointmentObject * attendeeObject ;
2008-11-26 13:20:26 +01:00
NSString * iCalString ;
2012-07-13 22:59:09 +02:00
iCalString = nil ;
2009-06-02 23:51:17 +02:00
attendeeObject = [ self _lookupEvent : [ theEvent uid ] forUID : theUID ] ;
2008-12-20 00:04:26 +01:00
// We must add an occurence to a non - existing event . We have
// to handle this with care , as in the postCalDAVEventRequestTo : from :
2009-06-02 23:51:17 +02:00
if ( [ attendeeObject isNew ] && [ theEvent recurrenceId ] )
2008-12-20 00:04:26 +01:00
{
2009-06-02 23:51:17 +02:00
iCalEvent * ownerEvent ;
2008-12-20 00:04:26 +01:00
iCalPerson * person ;
SOGoUser * user ;
2009-06-02 23:51:17 +02:00
// We check if the attendee that was added to a single occurence is
// present in the master component . If not , we add it with a participation
// status set to "DECLINED" .
ownerEvent = [ [ [ theEvent parent ] events ] objectAtIndex : 0 ] ;
2012-07-13 22:59:09 +02:00
user = [ SOGoUser userWithLogin : theUID ] ;
if ( ! [ ownerEvent userAsAttendee : user ] )
2008-12-20 00:04:26 +01:00
{
2009-06-02 23:51:17 +02:00
// Update the master event in the owner ' s calendar with the
// status of the new attendee set as "DECLINED" .
2012-07-13 22:59:09 +02:00
person = [ iCalPerson elementWithTag : @ "attendee" ] ;
[ person setCn : [ user cn ] ] ;
[ person setEmail : [ [ user allEmails ] objectAtIndex : 0 ] ] ;
[ person setParticipationStatus : iCalPersonPartStatDeclined ] ;
[ person setRsvp : @ "TRUE" ] ;
[ person setRole : @ "REQ-PARTICIPANT" ] ;
2009-06-02 23:51:17 +02:00
[ ownerEvent addToAttendees : person ] ;
2012-07-13 22:59:09 +02:00
iCalString = [ [ ownerEvent parent ] versitString ] ;
2008-12-20 00:04:26 +01:00
}
2012-07-13 22:59:09 +02:00
}
2008-12-20 00:04:26 +01:00
else
{
2009-08-11 04:06:27 +02:00
// TODO : if [ theEvent recurrenceId ] , only update this occurrence
// in attendee ' s calendar
// TODO : when updating the master event , handle exception dates
// in attendee ' s calendar ( add exception dates and remove matching
// occurrences ) - - see _updateRecurrenceIDsWithEvent :
2008-12-20 00:04:26 +01:00
iCalString = [ [ theEvent parent ] versitString ] ;
}
2009-06-02 23:51:17 +02:00
// Save the event in the attendee ' s calendar
2012-07-13 22:59:09 +02:00
if ( iCalString )
[ attendeeObject saveContentString : iCalString ] ;
2006-06-15 21:34:10 +02:00
}
}
2007-01-31 21:15:28 +01:00
2011-03-23 21:14:33 +01:00
//
//
//
2008-11-26 13:20:26 +01:00
- ( void ) _removeEventFromUID : ( NSString * ) theUID
owner : ( NSString * ) theOwner
2008-12-04 00:08:37 +01:00
withRecurrenceId : ( NSCalendarDate * ) recurrenceId
2007-11-07 16:58:43 +01:00
{
2008-11-26 13:20:26 +01:00
if ( ! [ theUID isEqualToString : theOwner ] )
2007-11-07 16:58:43 +01:00
{
2008-11-26 13:20:26 +01:00
SOGoAppointmentFolder * folder ;
SOGoAppointmentObject * object ;
2008-12-05 17:11:41 +01:00
iCalEntityObject * currentOccurence ;
2008-12-04 00:08:37 +01:00
iCalRepeatableEntityObject * event ;
iCalCalendar * calendar ;
2008-12-05 17:11:41 +01:00
NSCalendarDate * currentId ;
NSString * calendarContent ;
2009-04-01 15:49:24 +02:00
NSArray * occurences ;
2008-12-05 17:11:41 +01:00
int max , count ;
2009-03-23 22:19:55 +01:00
// Invitations are always written to the personal folder ; it ' s not necessay
// to look into all folders of the user
2010-07-15 19:05:29 +02:00
folder = [ [ SOGoUser userWithLogin : theUID ]
personalCalendarFolderInContext : context ] ;
2007-11-18 11:16:25 +01:00
object = [ folder lookupName : nameInContainer
2012-07-13 22:59:09 +02:00
inContext : context acquire : NO ] ;
2007-11-18 11:16:25 +01:00
if ( ! [ object isKindOfClass : [ NSException class ] ] )
2008-12-04 00:08:37 +01:00
{
if ( recurrenceId = = nil )
[ object delete ] ;
else
{
2008-12-05 17:11:41 +01:00
calendar = [ object calendar : NO secure : NO ] ;
2009-03-23 22:19:55 +01:00
2008-12-05 17:11:41 +01:00
// If recurrenceId is defined , remove the occurence from
// the repeating event .
occurences = [ calendar events ] ;
max = [ occurences count ] ;
count = 1 ;
while ( count < max )
2008-12-04 00:08:37 +01:00
{
2008-12-05 17:11:41 +01:00
currentOccurence = [ occurences objectAtIndex : count ] ;
currentId = [ currentOccurence recurrenceId ] ;
if ( [ currentId compare : recurrenceId ] = = NSOrderedSame )
{
[ [ calendar children ] removeObject : currentOccurence ] ;
break ;
}
count + + ;
2008-12-04 00:08:37 +01:00
}
2008-12-05 17:11:41 +01:00
// Add an date exception .
2008-12-04 00:08:37 +01:00
event = ( iCalRepeatableEntityObject * ) [ calendar firstChildWithTag : [ object componentTag ] ] ;
[ event addToExceptionDates : recurrenceId ] ;
2009-03-23 22:19:55 +01:00
2008-12-05 17:11:41 +01:00
[ event increaseSequence ] ;
2009-03-23 22:19:55 +01:00
2008-12-04 00:08:37 +01:00
// We generate the updated iCalendar file and we save it
// in the database .
calendarContent = [ calendar versitString ] ;
[ object saveContentString : calendarContent ] ;
}
}
2006-06-15 21:34:10 +02:00
}
}
2011-03-23 21:14:33 +01:00
//
//
//
2007-11-18 11:16:25 +01:00
- ( void ) _handleRemovedUsers : ( NSArray * ) attendees
2008-12-04 00:08:37 +01:00
withRecurrenceId : ( NSCalendarDate * ) recurrenceId
2007-09-06 22:51:59 +02:00
{
2007-11-18 11:16:25 +01:00
NSEnumerator * enumerator ;
iCalPerson * currentAttendee ;
NSString * currentUID ;
2007-09-06 22:51:59 +02:00
2007-11-18 11:16:25 +01:00
enumerator = [ attendees objectEnumerator ] ;
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
currentUID = [ currentAttendee uid ] ;
if ( currentUID )
2008-11-26 13:20:26 +01:00
[ self _removeEventFromUID : currentUID
2008-12-04 00:08:37 +01:00
owner : owner
withRecurrenceId : recurrenceId ] ;
2007-11-18 11:16:25 +01:00
}
2007-09-06 22:51:59 +02:00
}
2006-06-15 21:34:10 +02:00
2011-03-23 21:14:33 +01:00
//
//
//
2010-07-16 17:03:16 +02:00
- ( void ) _removeDelegationChain : ( iCalPerson * ) delegate
inEvent : ( iCalEvent * ) event
2006-09-28 19:23:39 +02:00
{
2010-07-16 17:03:16 +02:00
NSString * delegatedTo , * mailTo ;
delegatedTo = [ delegate delegatedTo ] ;
if ( [ delegatedTo length ] > 0 )
{
mailTo = [ delegatedTo rfc822Email ] ;
delegate = [ event findAttendeeWithEmail : mailTo ] ;
if ( delegate )
{
[ self _removeDelegationChain : delegate
inEvent : event ] ;
[ event removeFromAttendees : delegate ] ;
}
else
[ self errorWithFormat :
@ "broken chain: delegate with email '%@' was not found" ,
mailTo ] ;
}
}
2011-03-23 21:14:33 +01:00
//
// This method returns YES when any attendee has been removed
// and NO otherwise .
//
2010-07-16 17:03:16 +02:00
- ( BOOL ) _requireResponseFromAttendees : ( iCalEvent * ) event
{
NSArray * attendees ;
2007-11-18 11:16:25 +01:00
iCalPerson * currentAttendee ;
2012-01-10 18:42:33 +01:00
BOOL listHasChanged = NO ;
2010-07-16 17:03:16 +02:00
int count , max ;
2006-06-15 21:34:10 +02:00
2010-07-16 17:03:16 +02:00
attendees = [ event attendees ] ;
max = [ attendees count ] ;
for ( count = 0 ; count < max ; count + + )
2007-11-18 11:16:25 +01:00
{
2010-07-16 17:03:16 +02:00
currentAttendee = [ attendees objectAtIndex : count ] ;
if ( [ [ currentAttendee delegatedTo ] length ] > 0 )
{
[ self _removeDelegationChain : currentAttendee
inEvent : event ] ;
[ currentAttendee setDelegatedTo : nil ] ;
listHasChanged = YES ;
}
2007-11-18 11:16:25 +01:00
[ currentAttendee setRsvp : @ "TRUE" ] ;
[ currentAttendee setParticipationStatus : iCalPersonPartStatNeedsAction ] ;
}
2010-07-16 17:03:16 +02:00
return listHasChanged ;
2007-11-18 11:16:25 +01:00
}
2007-11-08 20:56:18 +01:00
2011-03-23 21:14:33 +01:00
//
//
//
2007-11-18 11:16:25 +01:00
- ( void ) _handleSequenceUpdateInEvent : ( iCalEvent * ) newEvent
ignoringAttendees : ( NSArray * ) attendees
fromOldEvent : ( iCalEvent * ) oldEvent
{
2012-02-03 16:40:36 +01:00
NSMutableArray * updateAttendees ;
2007-11-18 11:16:25 +01:00
NSEnumerator * enumerator ;
iCalPerson * currentAttendee ;
NSString * currentUID ;
2007-11-08 20:56:18 +01:00
2007-11-18 11:16:25 +01:00
updateAttendees = [ NSMutableArray arrayWithArray : [ newEvent attendees ] ] ;
[ updateAttendees removeObjectsInArray : attendees ] ;
2007-11-08 20:56:18 +01:00
2007-11-18 11:16:25 +01:00
enumerator = [ updateAttendees objectEnumerator ] ;
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
currentUID = [ currentAttendee uid ] ;
if ( currentUID )
[ self _addOrUpdateEvent : newEvent
2009-09-10 20:31:20 +02:00
forUID : currentUID
owner : owner ] ;
2007-11-18 11:16:25 +01:00
}
2006-06-15 21:34:10 +02:00
2008-11-18 01:06:37 +01:00
[ self sendEMailUsingTemplateNamed : @ "Update"
2012-02-24 00:27:59 +01:00
forObject : [ newEvent itipEntryWithMethod : @ "request" ]
previousObject : oldEvent
toAttendees : updateAttendees
withType : @ "calendar:invitation-update" ] ;
2007-11-18 11:16:25 +01:00
}
2006-06-15 21:34:10 +02:00
2011-04-25 12:31:08 +02:00
//
// This methods scans the list of attendees . If they are
// considered as resource , it checks for conflicting
// dates for the event .
//
// We check for between startDate + 1 second and
// endDate - 1 second
2011-03-23 21:14:33 +01:00
//
//
2011-04-25 12:31:08 +02:00
// It also CHANGES the participation status of resources
// depending on constraints defined on them .
2011-03-23 21:14:33 +01:00
//
2011-04-25 12:31:08 +02:00
// Note that it doesn ' t matter if it changes the participation
// status since in case of an error , nothing will get saved .
//
- ( NSException * ) _handleResourcesConflicts : ( NSArray * ) theAttendees
forEvent : ( iCalEvent * ) theEvent
2007-11-18 11:16:25 +01:00
{
2011-04-25 12:31:08 +02:00
iCalPerson * currentAttendee ;
2014-02-03 17:03:23 +01:00
NSMutableArray * attendees ;
2007-11-18 11:16:25 +01:00
NSEnumerator * enumerator ;
2011-04-25 12:31:08 +02:00
NSString * currentUID ;
2014-02-03 17:03:23 +01:00
SOGoUser * user , * currentUser , * ownerUser ;
2012-07-13 22:59:09 +02:00
2014-02-03 17:03:23 +01:00
// Build a list of the attendees uids
attendees = [ NSMutableArray arrayWithCapacity : [ theAttendees count ] ] ;
2011-04-25 12:31:08 +02:00
enumerator = [ theAttendees objectEnumerator ] ;
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
currentUID = [ currentAttendee uid ] ;
if ( currentUID )
2014-02-03 17:03:23 +01:00
{
[ attendees addObject : currentUID ] ;
}
}
// If the active user is not the owner of the calendar , check possible conflict when
// the owner is a resource
currentUser = [ context activeUser ] ;
if ( ! activeUserIsOwner && ! [ currentUser isSuperUser ] )
{
2014-03-14 14:20:27 +01:00
[ attendees addObject : owner ] ;
2014-02-03 17:03:23 +01:00
}
enumerator = [ attendees objectEnumerator ] ;
while ( ( currentUID = [ enumerator nextObject ] ) )
{
2011-04-25 12:31:08 +02:00
user = [ SOGoUser userWithLogin : currentUID ] ;
if ( [ user isResource ] )
{
SOGoAppointmentFolder * folder ;
NSCalendarDate * start , * end ;
2012-06-20 16:05:40 +02:00
NGCalendarDateRange * range ;
2011-04-25 12:31:08 +02:00
NSMutableArray * fbInfo ;
2012-06-20 16:05:40 +02:00
NSArray * allOccurences ;
BOOL must_delete ;
int i , j ;
2011-04-25 12:31:08 +02:00
2012-04-18 19:39:11 +02:00
// We get the start / end date for our conflict range . If the event to be added is recurring , we
// check for at least a year to start with .
2011-04-25 12:31:08 +02:00
start = [ [ theEvent startDate ] dateByAddingYears : 0 months : 0 days : 0 hours : 0 minutes : 0 seconds : 1 ] ;
2012-04-18 19:39:11 +02:00
end = [ [ theEvent endDate ] dateByAddingYears : ( [ theEvent isRecurrent ] ? 1 : 0 ) months : 0 days : 0 hours : 0 minutes : 0 seconds : -1 ] ;
2011-04-25 12:31:08 +02:00
2014-02-03 17:03:23 +01:00
folder = [ user personalCalendarFolderInContext : context ] ;
2011-04-25 12:31:08 +02:00
2012-05-15 16:36:52 +02:00
// Deny access to the resource if the ACLs don ' t allow the user
if ( ! [ folder aclSQLListingFilter ] )
{
NSDictionary * values ;
NSString * reason ;
values = [ NSDictionary dictionaryWithObjectsAndKeys :
[ user cn ] , @ "Cn" ,
[ user systemEmail ] , @ "SystemEmail" ] ;
reason = [ values keysWithFormat : [ self labelForKey : @ "Cannot access resource: \" % { Cn } % { SystemEmail } \ "" ] ] ;
return [ NSException exceptionWithHTTPStatus : 403 reason : reason ] ;
}
2011-04-25 12:31:08 +02:00
fbInfo = [ NSMutableArray arrayWithArray : [ folder fetchFreeBusyInfosFrom : start
to : end ] ] ;
// We first remove any occurences in the freebusy that corresponds to the
// current event . We do this to avoid raising a conflict if we move a 1 hour
// meeting from 12 : 00 -13 : 00 to 12 : 15 -13 : 15. We would overlap on ourself otherwise .
2012-06-20 16:05:40 +02:00
//
// We must also check here for repetitive events that don ' t overlap our event .
// We remove all events that don ' t overlap . The events here are already
// decomposed .
//
if ( [ theEvent isRecurrent ] )
allOccurences = [ theEvent recurrenceRangesWithinCalendarDateRange : [ NGCalendarDateRange calendarDateRangeWithStartDate : start
endDate : end ]
firstInstanceCalendarDateRange : [ NGCalendarDateRange calendarDateRangeWithStartDate : [ theEvent startDate ]
endDate : [ theEvent endDate ] ] ] ;
else
allOccurences = nil ;
2011-04-25 12:31:08 +02:00
for ( i = [ fbInfo count ] -1 ; i >= 0 ; i - - )
{
2012-06-20 16:05:40 +02:00
range = [ NGCalendarDateRange calendarDateRangeWithStartDate : [ [ fbInfo objectAtIndex : i ] objectForKey : @ "startDate" ]
endDate : [ [ fbInfo objectAtIndex : i ] objectForKey : @ "endDate" ] ] ;
2011-04-25 12:31:08 +02:00
if ( [ [ [ fbInfo objectAtIndex : i ] objectForKey : @ "c_uid" ] compare : [ theEvent uid ] ] = = NSOrderedSame )
2012-06-20 16:05:40 +02:00
{
[ fbInfo removeObjectAtIndex : i ] ;
continue ;
}
// No need to check if the event isn ' t recurrent here as it ' s handled correctly
// when we compute the "end" date .
if ( [ allOccurences count ] )
{
must_delete = YES ;
for ( j = 0 ; j < [ allOccurences count ] ; j + + )
{
if ( [ range doesIntersectWithDateRange : [ allOccurences objectAtIndex : j ] ] )
{
must_delete = NO ;
break ;
}
}
if ( must_delete )
[ fbInfo removeObjectAtIndex : i ] ;
}
2011-04-25 12:31:08 +02:00
}
2014-02-24 22:07:55 +01:00
// Find the attendee associated to the current UID
for ( i = 0 ; i < [ theAttendees count ] ; i + + )
{
currentAttendee = [ theAttendees objectAtIndex : i ] ;
if ( [ [ currentAttendee uid ] isEqualToString : currentUID ] )
break ;
else
currentAttendee = nil ;
}
2014-03-14 14:20:27 +01:00
if ( [ fbInfo count ] )
2014-02-24 22:07:55 +01:00
{
2014-03-14 14:20:27 +01:00
// If we always force the auto - accept if numberOfSimultaneousBookings = = 0 ( ie . , no limit
// is imposed ) or if numberOfSimultaneousBookings is greater than the number of
// overlapping events
if ( [ user numberOfSimultaneousBookings ] = = 0 ||
[ user numberOfSimultaneousBookings ] > [ fbInfo count ] )
2013-01-09 20:08:37 +01:00
{
2014-03-14 14:20:27 +01:00
if ( currentAttendee )
2014-02-24 22:07:55 +01:00
{
[ [ currentAttendee attributes ] removeObjectForKey : @ "RSVP" ] ;
[ currentAttendee setParticipationStatus : iCalPersonPartStatAccepted ] ;
}
}
else
{
2014-03-14 14:20:27 +01:00
iCalCalendar * calendar ;
NSDictionary * values ;
NSString * reason ;
iCalEvent * event ;
calendar = [ iCalCalendar parseSingleFromSource : [ [ fbInfo objectAtIndex : 0 ] objectForKey : @ "c_content" ] ] ;
event = [ [ calendar events ] lastObject ] ;
values = [ NSDictionary dictionaryWithObjectsAndKeys :
[ NSString stringWithFormat : @ "%d" , [ user numberOfSimultaneousBookings ] ] , @ "NumberOfSimultaneousBookings" ,
[ user cn ] , @ "Cn" ,
[ user systemEmail ] , @ "SystemEmail" ,
( [ event summary ] ? [ event summary ] : @ "" ) , @ "EventTitle" ,
[ [ fbInfo objectAtIndex : 0 ] objectForKey : @ "startDate" ] , @ "StartDate" ,
nil ] ;
reason = [ values keysWithFormat : [ self labelForKey : @ "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \" % { Cn } % { SystemEmail } \ ". The conflicting event is \" % { EventTitle } \ ", and starts on %{StartDate}." ] ] ;
return [ NSException exceptionWithHTTPStatus : 403
reason : reason ] ;
2013-01-09 20:08:37 +01:00
}
2014-02-24 22:07:55 +01:00
}
2014-03-14 14:20:27 +01:00
else if ( currentAttendee )
{
// No conflict , we auto - accept . We do this for resources automatically if no
// double - booking is observed . If it ' s not the desired behavior , just don ' t
// set the resource as one !
[ [ currentAttendee attributes ] removeObjectForKey : @ "RSVP" ] ;
[ currentAttendee setParticipationStatus : iCalPersonPartStatAccepted ] ;
}
2014-02-24 22:07:55 +01:00
}
2011-04-25 12:31:08 +02:00
}
return nil ;
}
//
//
//
- ( NSException * ) _handleAddedUsers : ( NSArray * ) attendees
fromEvent : ( iCalEvent * ) newEvent
{
2007-11-18 11:16:25 +01:00
iCalPerson * currentAttendee ;
2011-04-25 12:31:08 +02:00
NSEnumerator * enumerator ;
2007-11-18 11:16:25 +01:00
NSString * currentUID ;
2011-04-25 12:31:08 +02:00
NSException * e ;
// We check for conflicts
if ( ( e = [ self _handleResourcesConflicts : attendees forEvent : newEvent ] ) )
return e ;
2006-06-15 21:34:10 +02:00
2007-11-18 11:16:25 +01:00
enumerator = [ attendees objectEnumerator ] ;
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
2009-05-06 23:57:21 +02:00
currentUID = [ currentAttendee uid ] ;
if ( currentUID )
2007-11-18 11:16:25 +01:00
[ self _addOrUpdateEvent : newEvent
2009-09-10 20:31:20 +02:00
forUID : currentUID
owner : owner ] ;
2007-11-18 11:16:25 +01:00
}
2011-04-25 12:31:08 +02:00
return nil ;
2007-11-18 11:16:25 +01:00
}
2006-06-15 21:34:10 +02:00
2012-04-23 14:58:56 +02:00
//
//
//
- ( void ) _addOrDeleteAttendees : ( NSArray * ) theAttendees
inRecurrenceExceptionsForEvent : ( iCalEvent * ) theEvent
add : ( BOOL ) shouldAdd
{
NSArray * events ;
iCalEvent * e ;
int i , j ;
// We don ' t add / delete attendees to all recurrence exceptions if
// the modification was actually NOT made on the master event
if ( [ theEvent recurrenceId ] )
return ;
events = [ [ theEvent parent ] events ] ;
for ( i = 0 ; i < [ events count ] ; i + + )
{
e = [ events objectAtIndex : i ] ;
if ( [ e recurrenceId ] )
for ( j = 0 ; j < [ theAttendees count ] ; j + + )
if ( shouldAdd )
[ e addToAttendees : [ theAttendees objectAtIndex : j ] ] ;
else
[ e removeFromAttendees : [ theAttendees objectAtIndex : j ] ] ;
2012-07-13 22:59:09 +02:00
2012-04-23 14:58:56 +02:00
}
}
2009-04-30 23:17:55 +02:00
//
//
//
2011-04-25 12:31:08 +02:00
- ( NSException * ) _handleUpdatedEvent : ( iCalEvent * ) newEvent
fromOldEvent : ( iCalEvent * ) oldEvent
2007-11-18 11:16:25 +01:00
{
2012-07-10 02:29:13 +02:00
NSArray * addedAttendees , * deletedAttendees , * updatedAttendees ;
2007-11-18 11:16:25 +01:00
iCalEventChanges * changes ;
2011-04-25 12:31:08 +02:00
NSException * ex ;
2006-06-15 21:34:10 +02:00
2012-07-10 02:29:13 +02:00
addedAttendees = nil ;
deletedAttendees = nil ;
updatedAttendees = nil ;
2007-11-18 11:16:25 +01:00
changes = [ newEvent getChangesRelativeToEvent : oldEvent ] ;
2010-07-16 17:03:16 +02:00
if ( [ changes sequenceShouldBeIncreased ] )
{
// Set new attendees status to "needs action" and recompute changes when
2011-04-25 12:31:08 +02:00
// the list of attendees has changed . The list might have changed since
// by changing a major property of the event , we remove all the delegation
// chains to "other" attendees
2010-07-16 17:03:16 +02:00
if ( [ self _requireResponseFromAttendees : newEvent ] )
changes = [ newEvent getChangesRelativeToEvent : oldEvent ] ;
}
2011-04-25 12:31:08 +02:00
2012-07-10 02:29:13 +02:00
deletedAttendees = [ changes deletedAttendees ] ;
2012-04-23 14:58:56 +02:00
// We delete the attendees in all exception occurences , if
// the attendees were removed from the master event .
2012-07-10 02:29:13 +02:00
[ self _addOrDeleteAttendees : deletedAttendees
2012-04-23 14:58:56 +02:00
inRecurrenceExceptionsForEvent : newEvent
add : NO ] ;
2012-07-10 02:29:13 +02:00
if ( [ deletedAttendees count ] )
2007-11-18 11:16:25 +01:00
{
2012-07-10 02:29:13 +02:00
[ self _handleRemovedUsers : deletedAttendees
2011-06-01 19:49:12 +02:00
withRecurrenceId : [ newEvent recurrenceId ] ] ;
2007-11-18 11:16:25 +01:00
[ self sendEMailUsingTemplateNamed : @ "Deletion"
2011-06-01 19:49:12 +02:00
forObject : [ newEvent itipEntryWithMethod : @ "cancel" ]
previousObject : oldEvent
2012-07-10 02:29:13 +02:00
toAttendees : deletedAttendees
2012-02-24 00:27:59 +01:00
withType : @ "calendar:cancellation" ] ;
2007-11-18 11:16:25 +01:00
}
2011-06-01 19:49:12 +02:00
2011-04-25 12:31:08 +02:00
if ( ( ex = [ self _handleResourcesConflicts : [ newEvent attendees ]
2011-06-01 19:49:12 +02:00
forEvent : newEvent ] ) )
2011-04-25 12:31:08 +02:00
return ex ;
2012-07-10 02:29:13 +02:00
addedAttendees = [ changes insertedAttendees ] ;
2012-04-23 14:58:56 +02:00
// We insert the attendees in all exception occurences , if
// the attendees were added to the master event .
2012-07-10 02:29:13 +02:00
[ self _addOrDeleteAttendees : addedAttendees
2012-04-23 14:58:56 +02:00
inRecurrenceExceptionsForEvent : newEvent
add : YES ] ;
2007-11-18 11:16:25 +01:00
if ( [ changes sequenceShouldBeIncreased ] )
{
[ newEvent increaseSequence ] ;
2008-12-09 16:29:39 +01:00
// Update attendees calendars and send them an update
// notification by email
2007-11-18 11:16:25 +01:00
[ self _handleSequenceUpdateInEvent : newEvent
2012-07-10 02:29:13 +02:00
ignoringAttendees : addedAttendees
2011-06-01 19:49:12 +02:00
fromOldEvent : oldEvent ] ;
2007-11-18 11:16:25 +01:00
}
else
2008-11-29 00:10:41 +01:00
{
// If other attributes have changed , update the event
// in each attendee ' s calendar
if ( [ [ changes updatedProperties ] count ] )
{
NSEnumerator * enumerator ;
iCalPerson * currentAttendee ;
NSString * currentUID ;
2009-08-25 23:28:24 +02:00
updatedAttendees = [ newEvent attendees ] ;
enumerator = [ updatedAttendees objectEnumerator ] ;
2008-11-29 00:10:41 +01:00
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
currentUID = [ currentAttendee uid ] ;
if ( currentUID )
[ self _addOrUpdateEvent : newEvent
2009-09-10 20:31:20 +02:00
forUID : currentUID
owner : owner ] ;
2008-11-29 00:10:41 +01:00
}
}
}
2006-06-15 21:34:10 +02:00
2012-07-10 02:29:13 +02:00
if ( [ addedAttendees count ] )
2007-11-18 11:16:25 +01:00
{
2008-11-29 00:10:41 +01:00
// Send an invitation to new attendees
2012-07-10 02:29:13 +02:00
if ( ( ex = [ self _handleAddedUsers : addedAttendees fromEvent : newEvent ] ) )
2011-04-25 12:31:08 +02:00
return ex ;
2008-11-29 00:10:41 +01:00
[ self sendEMailUsingTemplateNamed : @ "Invitation"
2012-02-24 00:27:59 +01:00
forObject : [ newEvent itipEntryWithMethod : @ "request" ]
previousObject : oldEvent
2012-07-10 02:29:13 +02:00
toAttendees : addedAttendees
2012-02-24 00:27:59 +01:00
withType : @ "calendar:invitation" ] ;
2007-11-18 11:16:25 +01:00
}
2011-04-25 12:31:08 +02:00
2012-07-10 02:29:13 +02:00
[ self sendReceiptEmailForObject : newEvent
addedAttendees : addedAttendees
deletedAttendees : deletedAttendees
updatedAttendees : updatedAttendees
operation : EventUpdated ] ;
2011-04-25 12:31:08 +02:00
return nil ;
2007-11-18 11:16:25 +01:00
}
2006-06-15 21:34:10 +02:00
2010-08-11 16:52:11 +02:00
//
// Workflow : + - - - - - - - - - - - - - - - - - - - - - - +
// | |
// [ saveComponent : ] - - -> _handleAddedUsers : fromEvent : < - + |
// | | v
// + - - - - - - - - - - - -> _handleUpdatedEvent : fromOldEvent : - - -> _addOrUpdateEvent : forUID : owner : < - - - - - - - - - - - +
// | | ^ |
// v v | |
2012-04-19 18:29:43 +02:00
// _handleRemovedUsers : withRecurrenceId : _handleSequenceUpdateInEvent : ignoringAttendees : fromOldEvent : |
2010-08-11 16:52:11 +02:00
// | |
// | [ DELETEAction : ] |
// | | { _handleAdded / Updated . . . } < - - + |
// | v | |
// | [ prepareDeleteOccurence : ] [ PUTAction : ] |
// | | | | |
// v v v v |
// _removeEventFromUID : owner : withRecurrenceId : [ changeParticipationStatus : withDelegate : forRecurrenceId : ] |
// | | |
// | v |
// + - - - - - - - - - - - - - - - - - - - - - - - -> _handleAttendee : withDelegate : ownerUser : statusChange : inEvent : - - -> [ sendResponseToOrganizer : from : ]
// |
// v
// _updateAttendee : withDelegate : ownerUser : forEventUID : withRecurrenceId : withSequence : forUID : shouldAddSentBy :
//
//
2011-04-25 12:31:08 +02:00
- ( NSException * ) saveComponent : ( iCalEvent * ) newEvent
2007-11-18 11:16:25 +01:00
{
2009-08-25 23:28:24 +02:00
iCalEvent * oldEvent , * oldMasterEvent ;
2008-12-03 17:18:12 +01:00
NSCalendarDate * recurrenceId ;
NSString * recurrenceTime ;
2008-08-14 00:30:55 +02:00
SOGoUser * ownerUser ;
2011-04-25 12:31:08 +02:00
NSArray * attendees ;
NSException * ex ;
2012-07-13 22:59:09 +02:00
2007-11-18 11:16:25 +01:00
[ [ newEvent parent ] setMethod : @ "" ] ;
2009-08-25 23:28:24 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
2008-12-17 16:35:53 +01:00
2009-05-06 23:57:21 +02:00
[ self expandGroupsInEvent : newEvent ] ;
2014-02-03 17:03:23 +01:00
// We first update the event . It is important to do this initially
2011-03-30 01:25:40 +02:00
// as the event ' s UID might get modified .
[ super updateComponent : newEvent ] ;
2008-11-26 17:26:32 +01:00
2009-08-11 04:06:27 +02:00
if ( [ self isNew ] )
2007-11-18 11:16:25 +01:00
{
2009-08-11 04:06:27 +02:00
// New event - - send invitation to all attendees
attendees = [ newEvent attendeesWithoutUser : ownerUser ] ;
2014-02-24 13:55:21 +01:00
// We catch conflicts and abort the save process immediately
// in case of one with resources
if ( ( ex = [ self _handleAddedUsers : attendees fromEvent : newEvent ] ) )
return ex ;
2009-08-11 04:06:27 +02:00
if ( [ attendees count ] )
2007-11-08 20:56:18 +01:00
{
2009-08-11 04:06:27 +02:00
[ self sendEMailUsingTemplateNamed : @ "Invitation"
forObject : [ newEvent itipEntryWithMethod : @ "request" ]
previousObject : nil
2012-02-24 00:27:59 +01:00
toAttendees : attendees
withType : @ "calendar:invitation" ] ;
2007-11-08 20:56:18 +01:00
}
2012-07-10 02:29:13 +02:00
[ self sendReceiptEmailForObject : newEvent
addedAttendees : attendees
deletedAttendees : nil
updatedAttendees : nil
operation : EventCreated ] ;
2009-08-11 04:06:27 +02:00
}
else
{
2009-10-19 14:56:52 +02:00
BOOL hasOrganizer ;
2011-04-25 12:31:08 +02:00
2009-08-11 04:06:27 +02:00
// Event is modified - - sent update status to all attendees
// and modify their calendars .
recurrenceId = [ newEvent recurrenceId ] ;
if ( recurrenceId = = nil )
oldEvent = [ self component : NO secure : NO ] ;
2008-11-28 20:54:51 +01:00
else
{
2009-08-11 04:06:27 +02:00
// If recurrenceId is defined , find the specified occurence
// within the repeating vEvent .
recurrenceTime = [ NSString stringWithFormat : @ "%f" , [ recurrenceId timeIntervalSince1970 ] ] ;
2012-07-13 22:31:41 +02:00
oldEvent = ( iCalEvent * ) [ self lookupOccurrence : recurrenceTime ] ;
2009-08-11 04:06:27 +02:00
if ( oldEvent = = nil )
// If no occurence found , create one
2011-04-01 20:55:02 +02:00
oldEvent = ( iCalEvent * ) [ self newOccurenceWithID : recurrenceTime ] ;
2009-08-11 04:06:27 +02:00
}
2009-08-25 23:28:24 +02:00
2011-06-01 19:49:12 +02:00
oldMasterEvent = ( iCalEvent * ) [ [ oldEvent parent ] firstChildWithTag : [ self componentTag ] ] ;
2009-10-19 14:56:52 +02:00
hasOrganizer = [ [ [ oldMasterEvent organizer ] email ] length ] ;
if ( ! hasOrganizer || [ oldMasterEvent userIsOrganizer : ownerUser ] )
2011-04-25 12:31:08 +02:00
// The owner is the organizer of the event ; handle the modifications . We aslo
// catch conflicts just like when the events are created
if ( ( ex = [ self _handleUpdatedEvent : newEvent fromOldEvent : oldEvent ] ) )
return ex ;
2007-01-31 21:15:28 +01:00
}
2011-02-24 21:18:21 +01:00
2011-03-30 01:25:40 +02:00
[ super saveComponent : newEvent ] ;
2011-07-05 15:24:25 +02:00
[ self flush ] ;
2011-04-25 12:31:08 +02:00
return nil ;
2006-06-15 21:34:10 +02:00
}
2008-11-10 16:38:05 +01:00
//
// This method is used to update the status of an attendee .
//
// - theOwnerUser is owner of the calendar where the attendee
// participation state has changed .
// - uid is the actual UID of the user for whom we must
// update the calendar event ( with the participation change )
2010-08-19 19:40:28 +02:00
// - delegate is the delegate attendee if any
2008-11-10 16:38:05 +01:00
//
// This method is called multiple times , in order to update the
// status of the attendee in calendars for the particular event UID .
//
2007-11-18 11:16:25 +01:00
- ( NSException * ) _updateAttendee : ( iCalPerson * ) attendee
2009-08-27 18:20:41 +02:00
withDelegate : ( iCalPerson * ) delegate
2008-11-05 22:04:16 +01:00
ownerUser : ( SOGoUser * ) theOwnerUser
2007-11-18 11:16:25 +01:00
forEventUID : ( NSString * ) eventUID
2008-11-22 08:20:22 +01:00
withRecurrenceId : ( NSCalendarDate * ) recurrenceId
2007-11-18 11:16:25 +01:00
withSequence : ( NSNumber * ) sequence
forUID : ( NSString * ) uid
2008-11-05 22:04:16 +01:00
shouldAddSentBy : ( BOOL ) b
2007-11-08 20:56:18 +01:00
{
2007-11-18 11:16:25 +01:00
SOGoAppointmentObject * eventObject ;
2008-11-22 08:20:22 +01:00
iCalCalendar * calendar ;
iCalEntityObject * event ;
2009-08-27 18:20:41 +02:00
iCalPerson * otherAttendee , * otherDelegate ;
NSString * iCalString , * recurrenceTime , * delegateEmail ;
2007-11-08 20:56:18 +01:00
NSException * error ;
2009-08-27 18:20:41 +02:00
BOOL addDelegate , removeDelegate ;
2006-06-15 21:34:10 +02:00
2007-11-18 11:16:25 +01:00
error = nil ;
eventObject = [ self _lookupEvent : eventUID forUID : uid ] ;
if ( ! [ eventObject isNew ] )
2007-11-08 20:56:18 +01:00
{
2008-11-22 08:20:22 +01:00
if ( recurrenceId = = nil )
{
// We must update main event and all its occurences ( if any ) .
calendar = [ eventObject calendar : NO secure : NO ] ;
2008-12-03 17:18:12 +01:00
event = ( iCalEntityObject * ) [ calendar firstChildWithTag : [ self componentTag ] ] ;
2008-11-22 08:20:22 +01:00
}
else
{
// If recurrenceId is defined , find the specified occurence
// within the repeating vEvent .
recurrenceTime = [ NSString stringWithFormat : @ "%f" , [ recurrenceId timeIntervalSince1970 ] ] ;
2012-07-13 22:31:41 +02:00
event = [ eventObject lookupOccurrence : recurrenceTime ] ;
2008-11-22 08:20:22 +01:00
if ( event = = nil )
2008-12-01 17:24:15 +01:00
// If no occurence found , create one
2008-11-22 08:20:22 +01:00
event = [ eventObject newOccurenceWithID : recurrenceTime ] ;
}
2010-08-11 16:52:11 +02:00
if ( [ [ event sequence ] intValue ] <= [ sequence intValue ] )
2007-11-18 11:16:25 +01:00
{
2008-11-05 22:04:16 +01:00
SOGoUser * currentUser ;
2008-11-22 08:20:22 +01:00
2008-12-02 04:35:34 +01:00
currentUser = [ context activeUser ] ;
2010-05-05 15:56:19 +02:00
otherAttendee = [ event userAsAttendee : theOwnerUser ] ;
2009-08-27 18:20:41 +02:00
delegateEmail = [ otherAttendee delegatedTo ] ;
if ( [ delegateEmail length ] )
2009-09-10 20:31:20 +02:00
delegateEmail = [ delegateEmail rfc822Email ] ;
2009-08-27 18:20:41 +02:00
if ( [ delegateEmail length ] )
2010-05-05 15:56:19 +02:00
otherDelegate = [ event findAttendeeWithEmail : delegateEmail ] ;
2009-08-27 18:20:41 +02:00
else
otherDelegate = NO ;
2010-08-19 19:40:28 +02:00
/ * we handle the addition / deletion of delegate users * /
2009-08-27 18:20:41 +02:00
addDelegate = NO ;
removeDelegate = NO ;
if ( delegate )
{
if ( otherDelegate )
{
if ( ! [ delegate hasSameEmailAddress : otherDelegate ] )
{
removeDelegate = YES ;
addDelegate = YES ;
}
}
else
addDelegate = YES ;
}
else
{
if ( otherDelegate )
removeDelegate = YES ;
}
if ( removeDelegate )
2009-09-10 20:31:20 +02:00
{
while ( otherDelegate )
{
[ event removeFromAttendees : otherDelegate ] ;
2010-08-19 19:40:28 +02:00
// Verify if the delegate was already delegate
2009-09-10 20:31:20 +02:00
delegateEmail = [ otherDelegate delegatedTo ] ;
if ( [ delegateEmail length ] )
delegateEmail = [ delegateEmail rfc822Email ] ;
if ( [ delegateEmail length ] )
2010-05-05 15:56:19 +02:00
otherDelegate = [ event findAttendeeWithEmail : delegateEmail ] ;
2009-09-10 20:31:20 +02:00
else
otherDelegate = NO ;
}
}
2009-08-27 18:20:41 +02:00
if ( addDelegate )
[ event addToAttendees : delegate ] ;
2008-12-09 16:29:39 +01:00
[ otherAttendee setPartStat : [ attendee partStat ] ] ;
2009-08-27 18:20:41 +02:00
[ otherAttendee setDelegatedTo : [ attendee delegatedTo ] ] ;
[ otherAttendee setDelegatedFrom : [ attendee delegatedFrom ] ] ;
2012-11-30 19:57:55 +01:00
// Remove the RSVP attribute , as an action from the attendee
// was actually performed , and this confuses iCal ( bug #1850 )
[ [ otherAttendee attributes ] removeObjectForKey : @ "RSVP" ] ;
2008-12-09 16:29:39 +01:00
// If one has accepted / declined an invitation on behalf of
// the attendee , we add the user to the SENT - BY attribute .
if ( b && ! [ [ currentUser login ] isEqualToString : [ theOwnerUser login ] ] )
2008-11-13 14:41:43 +01:00
{
2009-08-11 18:52:10 +02:00
NSString * currentEmail , * quotedEmail ;
2008-12-09 16:29:39 +01:00
currentEmail = [ [ currentUser allEmails ] objectAtIndex : 0 ] ;
2009-08-11 18:52:10 +02:00
quotedEmail = [ NSString stringWithFormat : @ "\" MAILTO : % @ \ "" ,
currentEmail ] ;
[ otherAttendee setValue : 0 ofAttribute : @ "SENT-BY"
to : quotedEmail ] ;
2008-12-09 16:29:39 +01:00
}
else
{
// We must REMOVE any SENT - BY here . This is important since if A accepted
// the event for B and then , B changes by himself his participation status ,
// we don ' t want to keep the previous SENT - BY attribute there .
[ ( NSMutableDictionary * ) [ otherAttendee attributes ] removeObjectForKey : @ "SENT-BY" ] ;
2008-11-13 14:41:43 +01:00
}
2007-11-18 11:16:25 +01:00
}
2008-12-09 16:29:39 +01:00
// We generate the updated iCalendar file and we save it
// in the database .
iCalString = [ [ event parent ] versitString ] ;
error = [ eventObject saveContentString : iCalString ] ;
2007-11-18 11:16:25 +01:00
}
2008-12-09 16:29:39 +01:00
2007-11-18 11:16:25 +01:00
return error ;
}
2008-11-10 16:38:05 +01:00
//
2012-11-30 19:57:55 +01:00
// This method is invoked from the SOGo Web interface or from the DAV interface .
2008-11-10 16:38:05 +01:00
//
// - theOwnerUser is owner of the calendar where the attendee
// participation state has changed .
//
2007-11-18 11:16:25 +01:00
- ( NSException * ) _handleAttendee : ( iCalPerson * ) attendee
2009-08-27 18:20:41 +02:00
withDelegate : ( iCalPerson * ) delegate
2008-11-05 22:04:16 +01:00
ownerUser : ( SOGoUser * ) theOwnerUser
2007-11-18 11:16:25 +01:00
statusChange : ( NSString * ) newStatus
inEvent : ( iCalEvent * ) event
{
2012-07-13 22:59:09 +02:00
NSString * currentStatus , * organizerUID ;
2008-11-18 01:06:37 +01:00
SOGoUser * ownerUser , * currentUser ;
2008-11-10 16:38:05 +01:00
NSException * ex ;
2006-06-15 21:34:10 +02:00
2007-11-18 11:16:25 +01:00
ex = nil ;
currentStatus = [ attendee partStat ] ;
2009-09-10 20:31:20 +02:00
iCalPerson * otherAttendee , * otherDelegate ;
NSString * delegateEmail ;
BOOL addDelegate , removeDelegate ;
otherAttendee = attendee ;
delegateEmail = [ otherAttendee delegatedTo ] ;
if ( [ delegateEmail length ] )
delegateEmail = [ delegateEmail rfc822Email ] ;
if ( [ delegateEmail length ] )
2010-05-05 15:56:19 +02:00
otherDelegate = [ event findAttendeeWithEmail : delegateEmail ] ;
2009-09-10 20:31:20 +02:00
else
2010-08-19 19:40:28 +02:00
otherDelegate = nil ;
2009-09-10 20:31:20 +02:00
2010-08-19 19:40:28 +02:00
/ * We handle the addition / deletion of delegate users * /
2009-09-10 20:31:20 +02:00
addDelegate = NO ;
removeDelegate = NO ;
if ( delegate )
{
if ( otherDelegate )
{
2010-08-19 19:40:28 +02:00
// There was already a delegate
2009-09-10 20:31:20 +02:00
if ( ! [ delegate hasSameEmailAddress : otherDelegate ] )
{
2010-08-19 19:40:28 +02:00
// The delegate has changed
2009-09-10 20:31:20 +02:00
removeDelegate = YES ;
addDelegate = YES ;
}
}
else
2010-08-19 19:40:28 +02:00
// There was no previous delegate
2009-09-10 20:31:20 +02:00
addDelegate = YES ;
}
else
{
if ( otherDelegate )
2010-08-19 19:40:28 +02:00
// The user has removed the delegate
2009-09-10 20:31:20 +02:00
removeDelegate = YES ;
}
if ( addDelegate || removeDelegate
|| [ currentStatus caseInsensitiveCompare : newStatus ]
2007-11-18 11:16:25 +01:00
! = NSOrderedSame )
{
2012-11-30 19:57:55 +01:00
NSMutableArray * delegates ;
NSString * delegatedUID ;
delegatedUID = nil ;
2007-11-18 11:16:25 +01:00
[ attendee setPartStat : newStatus ] ;
2008-11-05 22:04:16 +01:00
// If one has accepted / declined an invitation on behalf of
// the attendee , we add the user to the SENT - BY attribute .
currentUser = [ context activeUser ] ;
if ( ! [ [ currentUser login ] isEqualToString : [ theOwnerUser login ] ] )
{
2009-08-11 18:52:10 +02:00
NSString * currentEmail , * quotedEmail ;
currentEmail = [ [ currentUser allEmails ] objectAtIndex : 0 ] ;
quotedEmail = [ NSString stringWithFormat : @ "\" MAILTO : % @ \ "" ,
currentEmail ] ;
[ attendee setValue : 0 ofAttribute : @ "SENT-BY"
to : quotedEmail ] ;
2008-11-05 22:04:16 +01:00
}
2008-11-13 14:41:43 +01:00
else
{
// We must REMOVE any SENT - BY here . This is important since if A accepted
// the event for B and then , B changes by himself his participation status ,
// we don ' t want to keep the previous SENT - BY attribute there .
[ ( NSMutableDictionary * ) [ attendee attributes ] removeObjectForKey : @ "SENT-BY" ] ;
}
2009-09-10 20:31:20 +02:00
[ attendee setDelegatedTo : [ delegate email ] ] ;
if ( removeDelegate )
{
2009-10-13 16:10:17 +02:00
delegates = [ NSMutableArray array ] ;
2009-09-10 20:31:20 +02:00
while ( otherDelegate )
{
[ delegates addObject : otherDelegate ] ;
2009-10-28 18:48:22 +01:00
delegatedUID = [ otherDelegate uid ] ;
2009-09-10 20:31:20 +02:00
if ( delegatedUID )
2010-08-19 19:40:28 +02:00
// Delegate attendee is a local user ; remove event from his calendar
2009-09-10 20:31:20 +02:00
[ self _removeEventFromUID : delegatedUID
owner : [ theOwnerUser login ]
withRecurrenceId : [ event recurrenceId ] ] ;
[ event removeFromAttendees : otherDelegate ] ;
// Verify if the delegate was already delegated
delegateEmail = [ otherDelegate delegatedTo ] ;
if ( [ delegateEmail length ] )
delegateEmail = [ delegateEmail rfc822Email ] ;
if ( [ delegateEmail length ] )
2010-05-05 15:56:19 +02:00
otherDelegate = [ event findAttendeeWithEmail : delegateEmail ] ;
2009-09-10 20:31:20 +02:00
else
otherDelegate = NO ;
}
[ self sendEMailUsingTemplateNamed : @ "Deletion"
forObject : [ event itipEntryWithMethod : @ "cancel" ]
previousObject : nil
2012-02-24 00:27:59 +01:00
toAttendees : delegates
withType : @ "calendar:cancellation" ] ;
2012-11-30 19:57:55 +01:00
} // if ( removeDelegate )
2009-09-10 20:31:20 +02:00
if ( addDelegate )
{
delegatedUID = [ delegate uid ] ;
delegates = [ NSArray arrayWithObject : delegate ] ;
[ event addToAttendees : delegate ] ;
if ( delegatedUID )
2010-08-19 19:40:28 +02:00
// Delegate attendee is a local user ; add event to his calendar
2009-09-10 20:31:20 +02:00
[ self _addOrUpdateEvent : event
forUID : delegatedUID
owner : [ theOwnerUser login ] ] ;
2008-11-05 22:04:16 +01:00
2009-09-10 20:31:20 +02:00
[ self sendEMailUsingTemplateNamed : @ "Invitation"
forObject : [ event itipEntryWithMethod : @ "request" ]
previousObject : nil
2012-02-24 00:27:59 +01:00
toAttendees : delegates
withType : @ "calendar:invitation" ] ;
2012-11-30 19:57:55 +01:00
} // if ( addDelegate )
2009-09-10 20:31:20 +02:00
2008-11-10 16:38:05 +01:00
// If the current user isn ' t the organizer of the event
// that has just been updated , we update the event and
// send a notification
2009-08-25 23:28:24 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
2008-08-09 18:47:13 +02:00
if ( ! ( ex || [ event userIsOrganizer : ownerUser ] ) )
2007-11-08 20:56:18 +01:00
{
2012-11-30 21:11:03 +01:00
if ( [ event isStillRelevant ] )
2008-11-10 16:38:05 +01:00
[ self sendResponseToOrganizer : event
2012-11-30 21:11:03 +01:00
from : ownerUser ] ;
2008-11-10 16:38:05 +01:00
2007-11-18 11:16:25 +01:00
organizerUID = [ [ event organizer ] uid ] ;
2008-11-22 08:20:22 +01:00
2011-03-23 21:14:33 +01:00
// Event is an exception to a recurring event ; retrieve organizer from master event
2008-11-22 08:20:22 +01:00
if ( ! organizerUID )
organizerUID = [ [ ( iCalEntityObject * ) [ [ event parent ] firstChildWithTag : [ self componentTag ] ] organizer ] uid ] ;
2007-11-18 11:16:25 +01:00
if ( organizerUID )
2008-12-09 16:29:39 +01:00
// Update the attendee in organizer ' s calendar .
2007-11-18 11:16:25 +01:00
ex = [ self _updateAttendee : attendee
2009-08-27 18:20:41 +02:00
withDelegate : delegate
ownerUser : theOwnerUser
forEventUID : [ event uid ]
withRecurrenceId : [ event recurrenceId ]
withSequence : [ event sequence ]
forUID : organizerUID
2008-11-05 22:04:16 +01:00
shouldAddSentBy : YES ] ;
}
2010-05-05 15:56:19 +02:00
// We update the calendar of all attendees that are
2008-11-05 22:04:16 +01:00
// local to the system . This is useful in case user A accepts
// invitation from organizer B and users C , D , E who are also
// attendees need to verify if A has accepted .
NSArray * attendees ;
iCalPerson * att ;
NSString * uid ;
int i ;
attendees = [ event attendees ] ;
for ( i = 0 ; i < [ attendees count ] ; i + + )
{
att = [ attendees objectAtIndex : i ] ;
2009-08-27 18:20:41 +02:00
uid = [ att uid ] ;
2009-09-10 20:31:20 +02:00
if ( uid
&& att ! = attendee
&& ! [ uid isEqualToString : delegatedUID ] )
2009-08-27 18:20:41 +02:00
[ self _updateAttendee : attendee
withDelegate : delegate
ownerUser : theOwnerUser
forEventUID : [ event uid ]
withRecurrenceId : [ event recurrenceId ]
withSequence : [ event sequence ]
forUID : uid
shouldAddSentBy : YES ] ;
2007-11-08 20:56:18 +01:00
}
2007-11-18 11:16:25 +01:00
}
return ex ;
}
2006-06-15 21:34:10 +02:00
2008-07-09 17:43:48 +02:00
- ( NSDictionary * ) _caldavSuccessCodeWithRecipient : ( NSString * ) recipient
{
NSMutableArray * element ;
NSDictionary * code ;
2009-10-13 16:10:17 +02:00
element = [ NSMutableArray array ] ;
2008-07-09 17:43:48 +02:00
[ element addObject : davElementWithContent ( @ "recipient" , XMLNS_CALDAV ,
recipient ) ] ;
[ element addObject : davElementWithContent ( @ "request-status" ,
XMLNS_CALDAV ,
@ "2.0;Success" ) ] ;
code = davElementWithContent ( @ "response" , XMLNS_CALDAV ,
element ) ;
return code ;
}
2008-11-26 13:20:26 +01:00
//
2010-08-11 16:52:11 +02:00
// Old CalDAV scheduling ( draft 4 and below ) methods . We keep them since we still
// advertise for its support but we do everything within the calendar - auto - scheduling code
2008-11-26 13:20:26 +01:00
//
2008-07-05 00:25:27 +02:00
- ( NSArray * ) postCalDAVEventRequestTo : ( NSArray * ) recipients
2008-11-10 16:38:05 +01:00
from : ( NSString * ) originator
2008-07-05 00:25:27 +02:00
{
NSEnumerator * recipientsEnum ;
2010-08-11 16:52:11 +02:00
NSMutableArray * elements ;
NSString * recipient ;
2008-11-18 21:59:08 +01:00
2008-07-05 00:25:27 +02:00
elements = [ NSMutableArray array ] ;
2009-08-25 23:28:24 +02:00
2008-07-05 00:25:27 +02:00
recipientsEnum = [ recipients objectEnumerator ] ;
2010-08-11 16:52:11 +02:00
2008-07-05 00:25:27 +02:00
while ( ( recipient = [ recipientsEnum nextObject ] ) )
2008-07-08 17:42:40 +02:00
if ( [ [ recipient lowercaseString ] hasPrefix : @ "mailto:" ] )
{
2010-08-11 16:52:11 +02:00
[ elements addObject : [ self _caldavSuccessCodeWithRecipient : recipient ] ] ;
2008-07-09 17:43:48 +02:00
}
return elements ;
}
- ( NSArray * ) postCalDAVEventCancelTo : ( NSArray * ) recipients
2008-11-10 16:38:05 +01:00
from : ( NSString * ) originator
2008-07-09 17:43:48 +02:00
{
NSEnumerator * recipientsEnum ;
2010-08-11 16:52:11 +02:00
NSMutableArray * elements ;
NSString * recipient ;
2008-07-09 17:43:48 +02:00
elements = [ NSMutableArray array ] ;
recipientsEnum = [ recipients objectEnumerator ] ;
2010-08-11 16:52:11 +02:00
2008-07-09 17:43:48 +02:00
while ( ( recipient = [ recipientsEnum nextObject ] ) )
if ( [ [ recipient lowercaseString ] hasPrefix : @ "mailto:" ] )
{
2010-08-11 16:52:11 +02:00
[ elements addObject : [ self _caldavSuccessCodeWithRecipient : recipient ] ] ;
2008-07-08 17:42:40 +02:00
}
return elements ;
}
- ( NSArray * ) postCalDAVEventReplyTo : ( NSArray * ) recipients
2008-11-10 16:38:05 +01:00
from : ( NSString * ) originator
2008-07-08 17:42:40 +02:00
{
NSEnumerator * recipientsEnum ;
2010-08-11 16:52:11 +02:00
NSMutableArray * elements ;
NSString * recipient ;
2008-07-08 17:42:40 +02:00
elements = [ NSMutableArray array ] ;
recipientsEnum = [ recipients objectEnumerator ] ;
2010-08-11 16:52:11 +02:00
2008-07-08 17:42:40 +02:00
while ( ( recipient = [ recipientsEnum nextObject ] ) )
if ( [ [ recipient lowercaseString ] hasPrefix : @ "mailto:" ] )
{
2010-08-11 16:52:11 +02:00
[ elements addObject : [ self _caldavSuccessCodeWithRecipient : recipient ] ] ;
2008-07-08 17:42:40 +02:00
}
2008-07-05 00:25:27 +02:00
return elements ;
}
2008-11-10 16:38:05 +01:00
//
2010-08-11 16:52:11 +02:00
//
2008-11-10 16:38:05 +01:00
//
2009-08-27 18:20:41 +02:00
- ( NSException * ) changeParticipationStatus : ( NSString * ) status
withDelegate : ( iCalPerson * ) delegate
2007-11-18 11:16:25 +01:00
{
2009-09-10 20:31:20 +02:00
return [ self changeParticipationStatus : status
withDelegate : delegate
2009-08-27 18:20:41 +02:00
forRecurrenceId : nil ] ;
2008-11-22 08:20:22 +01:00
}
2011-03-23 21:14:33 +01:00
//
//
//
2009-08-11 18:52:10 +02:00
- ( NSException * ) changeParticipationStatus : ( NSString * ) _status
2009-08-27 18:20:41 +02:00
withDelegate : ( iCalPerson * ) delegate
2009-08-11 18:52:10 +02:00
forRecurrenceId : ( NSCalendarDate * ) _recurrenceId
2008-11-22 08:20:22 +01:00
{
iCalCalendar * calendar ;
2007-11-18 11:16:25 +01:00
iCalEvent * event ;
iCalPerson * attendee ;
NSException * ex ;
2009-10-28 18:48:22 +01:00
SOGoUser * ownerUser , * delegatedUser ;
NSString * recurrenceTime , * delegatedUid ;
2008-08-09 17:21:25 +02:00
2008-11-22 08:20:22 +01:00
event = nil ;
2007-11-18 11:16:25 +01:00
ex = nil ;
2009-10-28 18:48:22 +01:00
delegatedUser = nil ;
2007-11-18 11:16:25 +01:00
2012-07-13 22:59:09 +02:00
calendar = [ [ self calendar : NO secure : NO ] mutableCopy ] ;
[ calendar autorelease ] ;
2012-07-14 04:41:29 +02:00
if ( _recurrenceId )
2008-11-22 08:20:22 +01:00
{
2012-07-14 04:41:29 +02:00
// If _recurrenceId is defined , find the specified occurence
// within the repeating vEvent .
recurrenceTime = [ NSString stringWithFormat : @ "%f" , [ _recurrenceId timeIntervalSince1970 ] ] ;
event = ( iCalEvent * ) [ self lookupOccurrence : recurrenceTime ] ;
if ( event = = nil )
// If no occurence found , create one
event = ( iCalEvent * ) [ self newOccurenceWithID : recurrenceTime ] ;
2008-11-22 08:20:22 +01:00
}
2012-07-14 04:41:29 +02:00
else
// No specific occurence specified ; return the first vEvent of
// the vCalendar .
event = ( iCalEvent * ) [ calendar firstChildWithTag : [ self componentTag ] ] ;
2007-11-18 11:16:25 +01:00
if ( event )
{
2009-08-27 18:20:41 +02:00
// ownerUser will actually be the owner of the calendar
2009-09-10 20:31:20 +02:00
// where the participation change on the event occurs . The particpation
// change will be on the attendee corresponding to the ownerUser .
2009-08-25 23:28:24 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
2012-07-14 04:41:29 +02:00
2010-05-05 15:56:19 +02:00
attendee = [ event userAsAttendee : ownerUser ] ;
2007-11-18 11:16:25 +01:00
if ( attendee )
2012-07-14 04:41:29 +02:00
{
if ( delegate
&& ! [ [ delegate email ] isEqualToString : [ attendee delegatedTo ] ] )
{
delegatedUid = [ delegate uid ] ;
if ( delegatedUid )
delegatedUser = [ SOGoUser userWithLogin : delegatedUid ] ;
if ( delegatedUser ! = nil && [ event userIsOrganizer : delegatedUser ] )
ex = [ NSException exceptionWithHTTPStatus : 403
reason : @ "delegate is organizer" ] ;
if ( [ event isAttendee : [ [ delegate email ] rfc822Email ] ] )
ex = [ NSException exceptionWithHTTPStatus : 403
2012-09-24 20:27:22 +02:00
reason : @ "delegate is a participant" ] ;
2012-07-14 04:41:29 +02:00
else if ( [ SOGoGroup groupWithEmail : [ [ delegate email ] rfc822Email ]
2009-11-29 05:19:32 +01:00
inDomain : [ ownerUser domain ] ] )
2012-07-14 04:41:29 +02:00
ex = [ NSException exceptionWithHTTPStatus : 403
reason : @ "delegate is a group" ] ;
}
if ( ex = = nil )
2012-11-30 19:57:55 +01:00
{
// Remove the RSVP attribute , as an action from the attendee
// was actually performed , and this confuses iCal ( bug #1850 )
[ [ attendee attributes ] removeObjectForKey : @ "RSVP" ] ;
ex = [ self _handleAttendee : attendee
withDelegate : delegate
ownerUser : ownerUser
statusChange : _status
inEvent : event ] ;
}
2012-07-14 04:41:29 +02:00
if ( ex = = nil )
{
// We generate the updated iCalendar file and we save it in
// the database . We do this ONLY when using SOGo from the
2014-01-31 21:05:33 +01:00
// Web interface or over ActiveSync .
// Over DAV , it ' ll be handled directly in PUTAction :
if ( ! [ context request ]
|| [ [ context request ] handledByDefaultHandler ]
|| [ [ [ context request ] requestHandlerKey ] isEqualToString : @ "Microsoft-Server-ActiveSync" ] )
2013-01-03 16:15:25 +01:00
ex = [ self saveContentString : [ [ event parent ] versitString ] ] ;
2012-07-14 04:41:29 +02:00
}
}
2007-11-18 11:16:25 +01:00
else
2008-11-22 08:20:22 +01:00
ex = [ NSException exceptionWithHTTPStatus : 404 // Not Found
2012-07-14 04:41:29 +02:00
reason : @ "user does not participate in this calendar event" ] ;
2007-11-08 20:56:18 +01:00
}
else
2008-11-22 08:20:22 +01:00
ex = [ NSException exceptionWithHTTPStatus : 500 // Server Error
2012-07-14 04:41:29 +02:00
reason : @ "unable to parse event record" ] ;
2008-11-22 08:20:22 +01:00
2007-11-18 11:16:25 +01:00
return ex ;
2006-06-15 21:34:10 +02:00
}
2010-08-17 16:10:01 +02:00
//
//
//
- ( BOOL ) _shouldScheduleEvent : ( iCalPerson * ) theOrganizer
{
NSString * v ;
BOOL b ;
b = YES ;
if ( theOrganizer && ( v = [ theOrganizer value : 0 ofAttribute : @ "SCHEDULE-AGENT" ] ) )
{
if ( [ v caseInsensitiveCompare : @ "NONE" ] = = NSOrderedSame ||
[ v caseInsensitiveCompare : @ "CLIENT" ] = = NSOrderedSame )
b = NO ;
}
return b ;
}
2010-08-11 16:52:11 +02:00
//
//
//
2008-07-17 23:12:43 +02:00
- ( void ) prepareDeleteOccurence : ( iCalEvent * ) occurence
2007-01-31 21:15:28 +01:00
{
2008-08-14 00:30:55 +02:00
SOGoUser * ownerUser , * currentUser ;
2008-12-01 22:15:07 +01:00
NSCalendarDate * recurrenceId ;
2012-07-11 16:06:41 +02:00
NSArray * attendees ;
iCalEvent * event ;
BOOL send_receipt ;
2010-08-11 16:52:11 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
event = [ self component : NO secure : NO ] ;
2012-07-11 16:06:41 +02:00
send_receipt = YES ;
2010-08-17 16:10:01 +02:00
if ( ! [ self _shouldScheduleEvent : [ event organizer ] ] )
return ;
2010-08-11 16:52:11 +02:00
if ( occurence = = nil )
2007-11-18 11:16:25 +01:00
{
2010-08-11 16:52:11 +02:00
// No occurence specified ; use the master event .
occurence = event ;
recurrenceId = nil ;
}
else
// Retrieve this occurence ID .
recurrenceId = [ occurence recurrenceId ] ;
if ( [ event userIsOrganizer : ownerUser ] )
{
// The organizer deletes an occurence .
currentUser = [ context activeUser ] ;
attendees = [ occurence attendeesWithoutUser : currentUser ] ;
2008-12-04 00:08:37 +01:00
# warning Make sure this is correct . .
2010-08-11 16:52:11 +02:00
if ( ! [ attendees count ] && event ! = occurence )
attendees = [ event attendeesWithoutUser : currentUser ] ;
if ( [ attendees count ] )
{
// Remove the event from all attendees calendars
// and send them an email .
[ self _handleRemovedUsers : attendees
withRecurrenceId : recurrenceId ] ;
[ self sendEMailUsingTemplateNamed : @ "Deletion"
2012-02-24 00:27:59 +01:00
forObject : [ occurence itipEntryWithMethod : @ "cancel" ]
previousObject : nil
toAttendees : attendees
withType : @ "calendar:cancellation" ] ;
2007-11-18 11:16:25 +01:00
}
}
2010-08-11 16:52:11 +02:00
else if ( [ occurence userIsAttendee : ownerUser ] )
2012-07-11 16:06:41 +02:00
{
// The current user deletes the occurence ; let the organizer know that
// the user has declined this occurence .
[ self changeParticipationStatus : @ "DECLINED" withDelegate : nil
forRecurrenceId : recurrenceId ] ;
send_receipt = NO ;
}
if ( send_receipt )
[ self sendReceiptEmailForObject : event
addedAttendees : nil
deletedAttendees : nil
updatedAttendees : nil
operation : EventDeleted ] ;
2006-06-15 21:34:10 +02:00
}
2006-10-31 19:54:56 +01:00
2010-01-22 17:33:35 +01:00
- ( NSException * ) prepareDelete
2008-07-17 23:12:43 +02:00
{
[ self prepareDeleteOccurence : nil ] ;
2010-01-22 17:33:35 +01:00
2010-08-19 19:40:28 +02:00
return [ super prepareDelete ] ;
2008-07-17 23:12:43 +02:00
}
2009-08-10 22:59:49 +02:00
- ( NSDictionary * ) _partStatsFromCalendar : ( iCalCalendar * ) calendar
2009-04-30 23:17:55 +02:00
{
2009-08-10 22:59:49 +02:00
NSMutableDictionary * partStats ;
2009-04-30 23:17:55 +02:00
NSArray * allEvents ;
2009-08-10 22:59:49 +02:00
int count , max ;
iCalEvent * currentEvent ;
2010-05-05 15:56:19 +02:00
iCalPerson * ownerAttendee ;
2009-08-10 22:59:49 +02:00
NSString * key ;
SOGoUser * ownerUser ;
2009-05-01 13:35:02 +02:00
2009-08-25 23:28:24 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
2009-04-30 23:17:55 +02:00
2009-08-10 22:59:49 +02:00
allEvents = [ calendar events ] ;
max = [ allEvents count ] ;
partStats = [ NSMutableDictionary dictionaryWithCapacity : max ] ;
for ( count = 0 ; count < max ; count + + )
{
currentEvent = [ allEvents objectAtIndex : count ] ;
2010-05-05 15:56:19 +02:00
ownerAttendee = [ currentEvent userAsAttendee : ownerUser ] ;
if ( ownerAttendee )
2009-08-10 22:59:49 +02:00
{
if ( count = = 0 )
key = @ "master" ;
else
key = [ [ currentEvent recurrenceId ] iCalFormattedDateTimeString ] ;
2010-05-05 15:56:19 +02:00
[ partStats setObject : ownerAttendee forKey : key ] ;
2009-08-10 22:59:49 +02:00
}
}
return partStats ;
}
2012-07-18 21:05:58 +02:00
- ( iCalCalendar * ) _setupResponseInRequestCalendar : ( iCalCalendar * ) rqCalendar
2009-08-10 22:59:49 +02:00
{
2012-07-13 22:59:09 +02:00
iCalCalendar * calendar ;
2009-08-10 22:59:49 +02:00
NSArray * keys ;
NSDictionary * partStats , * newPartStats ;
NSString * partStat , * key ;
int count , max ;
calendar = [ self calendar : NO secure : NO ] ;
partStats = [ self _partStatsFromCalendar : calendar ] ;
keys = [ partStats allKeys ] ;
max = [ keys count ] ;
if ( max > 0 )
{
2012-07-13 22:59:09 +02:00
newPartStats = [ self _partStatsFromCalendar : rqCalendar ] ;
2009-08-10 22:59:49 +02:00
if ( [ keys isEqualToArray : [ newPartStats allKeys ] ] )
{
for ( count = 0 ; count < max ; count + + )
{
key = [ keys objectAtIndex : count ] ;
partStat = [ [ newPartStats objectForKey : key ] partStat ] ;
[ [ partStats objectForKey : key ] setPartStat : partStat ] ;
}
}
}
2012-07-18 21:05:58 +02:00
return calendar ;
2009-08-10 22:59:49 +02:00
}
2009-04-30 23:17:55 +02:00
2012-07-13 22:59:09 +02:00
- ( void ) _adjustTransparencyInRequestCalendar : ( iCalCalendar * ) rqCalendar
2010-03-19 14:08:15 +01:00
{
NSArray * allEvents ;
iCalEvent * event ;
int i ;
2012-07-13 22:59:09 +02:00
allEvents = [ rqCalendar events ] ;
2010-03-19 14:08:15 +01:00
for ( i = 0 ; i < [ allEvents count ] ; i + + )
{
event = [ allEvents objectAtIndex : i ] ;
if ( [ event isAllDay ] && [ event isOpaque ] )
{
[ event setTransparency : @ "TRANSPARENT" ] ;
2012-07-13 22:59:09 +02:00
}
2010-03-19 14:08:15 +01:00
}
}
2011-04-16 03:35:06 +02:00
/ * *
* Verify vCalendar for any inconsistency or missing attributes .
* Currently only check if the events have an end date or a duration .
* @ param rq the HTTP PUT request
* /
2012-07-13 22:59:09 +02:00
- ( void ) _adjustEventsInRequestCalendar : ( iCalCalendar * ) rqCalendar
2011-04-16 03:35:06 +02:00
{
NSArray * allEvents ;
iCalEvent * event ;
NSUInteger i ;
2012-07-13 22:59:09 +02:00
allEvents = [ rqCalendar events ] ;
2011-04-16 03:35:06 +02:00
for ( i = 0 ; i < [ allEvents count ] ; i + + )
{
event = [ allEvents objectAtIndex : i ] ;
if ( ! [ event hasEndDate ] && ! [ event hasDuration ] )
{
// No end date , no duration
if ( [ event isAllDay ] )
[ event setDuration : @ "P1D" ] ;
else
[ event setDuration : @ "PT1H" ] ;
2012-07-13 22:59:09 +02:00
[ self warnWithFormat : @ "Invalid event: no end date; setting duration to %@" , [ event duration ] ] ;
2011-04-16 03:35:06 +02:00
}
}
}
2012-07-13 22:59:09 +02:00
- ( void ) _decomposeGroupsInRequestCalendar : ( iCalCalendar * ) rqCalendar
2009-08-10 22:59:49 +02:00
{
NSArray * allEvents ;
iCalEvent * event ;
int i ;
2009-04-30 23:17:55 +02:00
2009-08-10 22:59:49 +02:00
// The algorithm is pretty straightforward :
2009-04-30 23:17:55 +02:00
//
// We get all events
// We get all attendees
// If some are groups , we decompose them
// We regenerate the iCalendar string
//
2012-07-13 22:59:09 +02:00
allEvents = [ rqCalendar events ] ;
2009-04-30 23:17:55 +02:00
for ( i = 0 ; i < [ allEvents count ] ; i + + )
{
event = [ allEvents objectAtIndex : i ] ;
2012-07-13 22:59:09 +02:00
[ self expandGroupsInEvent : event ] ;
2009-04-30 23:17:55 +02:00
}
2009-08-10 22:59:49 +02:00
}
2010-08-11 16:52:11 +02:00
//
// If theRecurrenceId is nil , it returns immediately the
// first event that has a RECURRENCE - ID .
//
// Otherwise , it return values that matches .
//
- ( iCalEvent * ) _eventFromRecurrenceId : ( NSCalendarDate * ) theRecurrenceId
events : ( NSArray * ) allEvents
{
iCalEvent * event ;
int i ;
for ( i = 0 ; i < [ allEvents count ] ; i + + )
{
event = [ allEvents objectAtIndex : i ] ;
if ( [ event recurrenceId ] && ! theRecurrenceId )
return event ;
if ( [ event recurrenceId ] && [ [ event recurrenceId ] compare : theRecurrenceId ] = = NSOrderedSame )
return event ;
}
return nil ;
}
//
//
//
- ( NSCalendarDate * ) _addedExDate : ( iCalEvent * ) oldEvent
newEvent : ( iCalEvent * ) newEvent
{
NSArray * oldExDates , * newExDates ;
NSMutableArray * dates ;
int i ;
dates = [ NSMutableArray array ] ;
newExDates = [ newEvent childrenWithTag : @ "exdate" ] ;
for ( i = 0 ; i < [ newExDates count ] ; i + + )
[ dates addObject : [ [ newExDates objectAtIndex : i ] dateTime ] ] ;
oldExDates = [ oldEvent childrenWithTag : @ "exdate" ] ;
for ( i = 0 ; i < [ oldExDates count ] ; i + + )
[ dates removeObject : [ [ oldExDates objectAtIndex : i ] dateTime ] ] ;
return [ dates lastObject ] ;
}
//
//
//
- ( id ) DELETEAction : ( WOContext * ) _ctx
{
2010-08-19 19:40:28 +02:00
[ self prepareDelete ] ;
2010-08-11 16:52:11 +02:00
return [ super DELETEAction : _ctx ] ;
}
2009-08-10 22:59:49 +02:00
//
2012-07-13 22:59:09 +02:00
// This method is meant to be the common point of any save operation from web
// and DAV requests , as well as from code making use of SOGo as a library
// ( OpenChange )
2010-03-19 14:08:15 +01:00
//
2012-07-13 22:59:09 +02:00
- ( NSException * ) updateContentWithCalendar : ( iCalCalendar * ) calendar
fromRequest : ( WORequest * ) rq
2009-08-10 22:59:49 +02:00
{
2011-04-25 12:31:08 +02:00
NSException * ex ;
2009-08-10 22:59:49 +02:00
NSArray * roles ;
2012-07-13 22:59:09 +02:00
SOGoUser * ownerUser ;
2010-08-19 19:40:28 +02:00
2012-07-13 22:59:09 +02:00
if ( calendar = = fullCalendar
|| calendar = = safeCalendar
|| calendar = = originalCalendar )
[ NSException raise : NSInvalidArgumentException
format : @ "the 'calendar' argument must be a distinct instance"
@ " from the original object" ] ;
2012-04-24 13:47:32 +02:00
2012-07-13 22:59:09 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
2010-08-19 19:40:28 +02:00
2012-07-13 22:59:09 +02:00
roles = [ [ context activeUser ] rolesForObject : self
inContext : context ] ;
2010-08-11 16:52:11 +02:00
//
// We check if we gave only the "Respond To" right and someone is actually
// responding to one of our invitation . In this case , _setupResponseCalendarInRequest
// will only take the new attendee status and actually discard any other modifications .
//
2009-08-10 22:59:49 +02:00
if ( [ roles containsObject : @ "ComponentResponder" ]
&& ! [ roles containsObject : @ "ComponentModifier" ] )
2012-07-18 21:05:58 +02:00
calendar = [ self _setupResponseInRequestCalendar : calendar ] ;
2009-08-10 22:59:49 +02:00
else
{
if ( ! [ [ rq headersForKey : @ "X-SOGo" ]
2012-07-13 22:59:09 +02:00
containsObject : @ "NoGroupsDecomposition" ] )
[ self _decomposeGroupsInRequestCalendar : calendar ] ;
2010-03-19 14:08:15 +01:00
2012-07-13 22:59:09 +02:00
if ( [ [ ownerUser domainDefaults ] iPhoneForceAllDayTransparency ]
2010-03-19 14:08:15 +01:00
&& [ rq isIPhone ] )
{
2012-07-13 22:59:09 +02:00
[ self _adjustTransparencyInRequestCalendar : calendar ] ;
2010-03-19 14:08:15 +01:00
}
2011-04-16 03:35:06 +02:00
2012-07-13 22:59:09 +02:00
[ self _adjustEventsInRequestCalendar : calendar ] ;
2009-08-10 22:59:49 +02:00
}
2009-05-01 13:35:02 +02:00
2010-08-11 16:52:11 +02:00
//
// We first check if it ' s a new event
//
if ( [ self isNew ] )
{
2012-07-13 22:59:09 +02:00
iCalEvent * event ;
2012-07-10 02:29:13 +02:00
NSArray * attendees ;
2012-06-27 18:06:20 +02:00
NSString * eventUID ;
2010-08-17 16:10:01 +02:00
BOOL scheduling ;
2010-08-11 16:52:11 +02:00
2012-07-10 02:29:13 +02:00
attendees = nil ;
2010-08-19 19:40:28 +02:00
2010-08-11 16:52:11 +02:00
event = [ [ calendar events ] objectAtIndex : 0 ] ;
2012-06-27 18:06:20 +02:00
eventUID = [ event uid ] ;
2010-08-17 16:10:01 +02:00
scheduling = [ self _shouldScheduleEvent : [ event organizer ] ] ;
2012-06-27 18:06:20 +02:00
// make sure eventUID doesn ' t conflict with an existing event - see bug #1853
// TODO : send out a no - uid - conflict ( DAV : href ) xml element ( rfc4791 section 5.3 .2 .1 )
2012-07-13 22:59:09 +02:00
if ( [ container resourceNameForEventUID : eventUID ] )
2012-06-27 18:06:20 +02:00
{
2012-07-10 02:29:13 +02:00
return [ NSException exceptionWithHTTPStatus : 403
reason : [ NSString stringWithFormat : @ "Event UID already in use. (%s)" , eventUID ] ] ;
2012-06-27 18:06:20 +02:00
}
2010-08-17 16:30:09 +02:00
2010-08-11 16:52:11 +02:00
//
// New event and we ' re the organizer - - send invitation to all attendees
//
2010-08-17 16:10:01 +02:00
if ( scheduling && [ event userIsOrganizer : ownerUser ] )
2010-08-11 16:52:11 +02:00
{
attendees = [ event attendeesWithoutUser : ownerUser ] ;
if ( [ attendees count ] )
{
2011-04-25 12:31:08 +02:00
if ( ( ex = [ self _handleAddedUsers : attendees fromEvent : event ] ) )
return ex ;
2011-07-02 15:22:34 +02:00
else
{
// We might have auto - accepted resources here . If that ' s the
2012-04-18 19:39:11 +02:00
// case , let ' s regenerate the versitstring and replace the
2011-07-02 15:22:34 +02:00
// one from the request .
[ rq setContent : [ [ [ event parent ] versitString ] dataUsingEncoding : [ rq contentEncoding ] ] ] ;
}
2011-04-25 12:31:08 +02:00
2010-08-11 16:52:11 +02:00
[ self sendEMailUsingTemplateNamed : @ "Invitation"
2012-02-24 00:27:59 +01:00
forObject : [ event itipEntryWithMethod : @ "request" ]
previousObject : nil
toAttendees : attendees
withType : @ "calendar:invitation" ] ;
2010-08-11 16:52:11 +02:00
}
}
//
// We aren ' t the organizer but we ' re an attendee . That can happen when
// we receive an external invitation ( IMIP / ITIP ) and we accept it
// from a CUA - it gets added to a specific CalDAV calendar using a PUT
//
2010-08-17 16:10:01 +02:00
else if ( scheduling && [ event userIsAttendee : ownerUser ] )
2010-08-11 16:52:11 +02:00
{
[ self sendIMIPReplyForEvent : event
from : ownerUser
to : [ event organizer ] ] ;
}
2012-07-10 02:29:13 +02:00
[ self sendReceiptEmailForObject : event
addedAttendees : attendees
deletedAttendees : nil
updatedAttendees : nil
operation : EventCreated ] ;
2012-07-11 16:06:41 +02:00
} // if ( [ self isNew ] )
2010-08-11 16:52:11 +02:00
else
{
2012-07-13 22:59:09 +02:00
iCalCalendar * oldCalendar ;
2010-08-11 16:52:11 +02:00
iCalEvent * oldEvent , * newEvent ;
iCalEventChanges * changes ;
2013-03-07 15:29:56 +01:00
NSMutableArray * oldEvents , * newEvents ;
2010-08-11 16:52:11 +02:00
NSCalendarDate * recurrenceId ;
int i ;
//
// We check what has changed in the event and react accordingly .
//
2013-03-07 15:29:56 +01:00
newEvents = [ NSMutableArray arrayWithArray : [ calendar events ] ] ;
2010-08-11 16:52:11 +02:00
oldCalendar = [ self calendar : NO secure : NO ] ;
2013-03-07 15:29:56 +01:00
oldEvents = [ NSMutableArray arrayWithArray : [ oldCalendar events ] ] ;
2010-08-11 16:52:11 +02:00
recurrenceId = nil ;
for ( i = [ newEvents count ] -1 ; i >= 0 ; i - - )
{
newEvent = [ newEvents objectAtIndex : i ] ;
if ( [ newEvent recurrenceId ] )
{
// Find the corresponding RECURRENCE - ID in the old calendar
// If not present , we assume it was created before the PUT
oldEvent = [ self _eventFromRecurrenceId : [ newEvent recurrenceId ]
events : oldEvents ] ;
2011-04-01 20:55:02 +02:00
if ( oldEvent = = nil )
{
NSString * recurrenceTime ;
recurrenceTime = [ NSString stringWithFormat : @ "%f" , [ [ newEvent recurrenceId ] timeIntervalSince1970 ] ] ;
oldEvent = ( iCalEvent * ) [ self newOccurenceWithID : recurrenceTime ] ;
}
2010-08-11 16:52:11 +02:00
// If present , we look for changes
changes = [ iCalEventChanges changesFromEvent : oldEvent toEvent : newEvent ] ;
if ( [ changes sequenceShouldBeIncreased ] | [ changes hasAttendeeChanges ] )
{
// We found a RECURRENCE - ID with changes , we consider it
recurrenceId = [ newEvent recurrenceId ] ;
break ;
}
else
{
2013-03-07 15:29:56 +01:00
[ newEvents removeObject : newEvent ] ;
[ oldEvents removeObject : oldEvent ] ;
2010-08-11 16:52:11 +02:00
}
}
oldEvent = nil ;
newEvent = nil ;
}
// If no changes were observed , let ' s see if we have any left overs
// in the oldEvents or in the newEvents array
if ( ! oldEvent && ! newEvent )
{
// We check if we only have to deal with the MASTER event
if ( [ newEvents count ] = = [ oldEvents count ] )
{
oldEvent = [ oldEvents objectAtIndex : 0 ] ;
newEvent = [ newEvents objectAtIndex : 0 ] ;
}
// A RECURRENCE - ID was added
else if ( [ newEvents count ] > [ oldEvents count ] )
{
oldEvent = nil ;
newEvent = [ self _eventFromRecurrenceId : nil events : newEvents ] ;
recurrenceId = [ newEvent recurrenceId ] ;
}
// A RECURRENCE - ID was removed
else
{
oldEvent = [ self _eventFromRecurrenceId : nil events : oldEvents ] ;
newEvent = nil ;
recurrenceId = [ oldEvent recurrenceId ] ;
}
}
// We check if the PUT call is actually an PART - STATE change
// from one of the attendees - here ' s the logic :
//
// if owner = = organizer
//
// if [ context activeUser ] = = organizer
// [ send the invitation update ]
// else
// [ react on SENT - BY as someone else is acting for the organizer ]
//
//
if ( [ [ newEvent attendees ] count ] || [ [ oldEvent attendees ] count ] )
{
2011-03-23 21:14:33 +01:00
NSString * uid ;
2013-03-07 15:29:56 +01:00
// newEvent might be nil here , if we ' re deleting a RECURRENCE - ID with attendees
// If that ' s the case , we use the oldEvent for now just to obtain the organizer
// and we ' ll swap it back to nil once we ' re done .
if ( ! newEvent )
newEvent = oldEvent ;
2012-04-23 14:58:56 +02:00
// We fetch the organizer ' s uid . Sometimes , the recurrence - id will
// have it , sometimes not . If it doesn ' t , we fetch it from the master event .
uid = [ [ newEvent organizer ] uid ] ;
2013-03-07 15:29:56 +01:00
if ( ! uid && ! recurrenceId )
2011-03-23 21:14:33 +01:00
uid = [ [ [ [ [ newEvent parent ] events ] objectAtIndex : 0 ] organizer ] uid ] ;
2011-04-01 20:55:02 +02:00
2012-04-23 14:58:56 +02:00
// With Thunderbird 10 , if you create a recurring event with an exception
// occurence , and invite someone , the PUT will have the organizer in the
// recurrence - id and not in the master event . We must fix this , otherwise
// SOGo will break .
2013-03-07 15:29:56 +01:00
if ( ! recurrenceId && ! [ [ [ [ [ newEvent parent ] events ] objectAtIndex : 0 ] organizer ] uid ] )
2012-04-23 14:58:56 +02:00
[ [ [ [ newEvent parent ] events ] objectAtIndex : 0 ]
setOrganizer : [ newEvent organizer ] ] ;
2013-03-07 15:29:56 +01:00
if ( newEvent = = oldEvent )
newEvent = nil ;
2011-07-05 17:08:44 +02:00
if ( uid && [ uid caseInsensitiveCompare : owner ] = = NSOrderedSame )
2010-08-11 16:52:11 +02:00
{
2013-03-07 15:29:56 +01:00
// A RECCURENCE - ID was removed
if ( ! newEvent && oldEvent )
[ self prepareDeleteOccurence : oldEvent ] ;
// The master event was changed , A RECCURENCE - ID was added or modified
else if ( ( ex = [ self _handleUpdatedEvent : newEvent fromOldEvent : oldEvent ] ) )
2011-04-25 12:31:08 +02:00
return ex ;
2010-08-11 16:52:11 +02:00
}
//
// else = > attendee is responding
//
// if [ context activeUser ] = = attendee
// [ we change the PART - STATE ]
// else
// [ react on SENT - BY as someone else is acting for the attendee ]
else
{
iCalPerson * attendee , * delegate ;
NSString * delegateEmail ;
attendee = [ newEvent userAsAttendee : [ SOGoUser userWithLogin : owner ] ] ;
2012-07-11 16:06:41 +02:00
2011-10-03 21:00:50 +02:00
// We first check of the sequences are alright . We don ' t accept attendees
// accepting "old" invitations . If that ' s the case , we return a 403
if ( [ [ newEvent sequence ] intValue ] < [ [ oldEvent sequence ] intValue ] )
return [ NSException exceptionWithHTTPStatus : 403
reason : @ "sequences don't match" ] ;
2012-12-05 14:31:35 +01:00
// Remove the RSVP attribute , as an action from the attendee
// was actually performed , and this confuses iCal ( bug #1850 )
[ [ attendee attributes ] removeObjectForKey : @ "RSVP" ] ;
2011-10-03 21:00:50 +02:00
2010-08-11 16:52:11 +02:00
delegate = nil ;
delegateEmail = [ attendee delegatedTo ] ;
if ( [ delegateEmail length ] )
{
delegateEmail = [ delegateEmail substringFromIndex : 7 ] ;
if ( [ delegateEmail length ] )
delegate = [ newEvent findAttendeeWithEmail : delegateEmail ] ;
}
changes = [ iCalEventChanges changesFromEvent : oldEvent toEvent : newEvent ] ;
// The current user deletes the occurence ; let the organizer know that
// the user has declined this occurence .
if ( [ [ changes updatedProperties ] containsObject : @ "exdate" ] )
{
2011-04-01 20:55:02 +02:00
[ self changeParticipationStatus : @ "DECLINED"
2011-03-23 21:14:33 +01:00
withDelegate : nil // FIXME ( specify delegate ? )
2010-08-11 16:52:11 +02:00
forRecurrenceId : [ self _addedExDate : oldEvent newEvent : newEvent ] ] ;
2012-07-11 16:06:41 +02:00
}
2012-04-19 18:29:43 +02:00
else if ( attendee )
{
[ self changeParticipationStatus : [ attendee partStat ]
withDelegate : delegate
forRecurrenceId : recurrenceId ] ;
}
// All attendees and the organizer field were removed . Apple iCal does
// that when we remove the last attendee of an event .
//
// We must update previous ' s attendees ' calendars to actually
// remove the event in each of them .
else
{
[ self _handleRemovedUsers : [ changes deletedAttendees ]
withRecurrenceId : recurrenceId ] ;
}
2012-07-11 16:06:41 +02:00
}
} // if ( [ [ newEvent attendees ] count ] || [ [ oldEvent attendees ] count ] )
else
{
[ self sendReceiptEmailForObject : newEvent
addedAttendees : nil
deletedAttendees : nil
updatedAttendees : nil
operation : EventUpdated ] ;
2010-08-11 16:52:11 +02:00
}
2012-07-10 02:29:13 +02:00
} // else of if ( isNew ) . . .
2010-08-11 16:52:11 +02:00
2012-07-13 22:59:09 +02:00
unsigned int baseVersion ;
2012-04-24 13:47:32 +02:00
// We must NOT invoke [ super PUTAction : ] here as it ' ll resave
// the content string and we could have etag mismatches .
baseVersion = ( isNew ? 0 : version ) ;
2012-07-13 22:59:09 +02:00
ex = [ self saveContentString : [ calendar versitString ]
2012-04-24 13:47:32 +02:00
baseVersion : baseVersion ] ;
2012-07-13 22:59:09 +02:00
return ex ;
}
//
// If we see "X-SOGo: NoGroupsDecomposition" in the HTTP headers , we
// simply invoke super ' s PUTAction .
//
// We also check if we must force transparency on all day events
// from iPhone clients .
//
- ( id ) PUTAction : ( WOContext * ) _ctx
{
NSException * ex ;
NSString * etag ;
WORequest * rq ;
WOResponse * response ;
iCalCalendar * rqCalendar ;
rq = [ _ctx request ] ;
rqCalendar = [ iCalCalendar parseSingleFromSource : [ rq contentAsString ] ] ;
if ( ! [ self isNew ] )
{
//
// We must check for etag changes prior doing anything since an attendee could
// have changed its participation status and the organizer didn ' t get the
// copy and is trying to do a modification to the event .
//
ex = [ self matchesRequestConditionInContext : context ] ;
if ( ex )
return ex ;
}
ex = [ self updateContentWithCalendar : rqCalendar fromRequest : rq ] ;
2012-04-24 13:47:32 +02:00
if ( ex )
response = ( WOResponse * ) ex ;
else
{
2012-07-13 22:59:09 +02:00
response = [ _ctx response ] ;
2012-04-24 13:47:32 +02:00
if ( isNew )
[ response setStatus : 201 / * Created * / ] ;
else
[ response setStatus : 204 / * No Content * / ] ;
etag = [ self davEntityTag ] ;
if ( etag )
[ response setHeader : etag forKey : @ "etag" ] ;
}
2010-08-11 16:52:11 +02:00
return response ;
2009-04-30 23:17:55 +02:00
}
2006-06-15 21:34:10 +02:00
@ end / * SOGoAppointmentObject * /