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 >
2014-07-07 19:38:37 +02:00
#import < Foundation / NSPredicate . 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 >
2014-07-07 19:38:37 +02:00
#import < SOGo / SOGoUserSettings . 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
2014-07-18 19:57:18 +02:00
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
2014-07-18 19:57:18 +02:00
inContext : context
acquire : NO ] ;
2014-05-28 17:39:25 +02:00
if ( [ object isKindOfClass : [ NSException class ] ] || [ object isNew ] )
2014-07-18 19:57:18 +02:00
{
possibleName = [ folder resourceNameForEventUID : eventUID ] ;
if ( possibleName )
{
object = [ folder lookupName : possibleName
inContext : context acquire : NO ] ;
if ( [ object isKindOfClass : [ NSException class ] ] || [ object isNew ] )
object = nil ;
}
else
object = nil ;
}
2006-06-15 21:34:10 +02:00
}
2014-07-18 19:57:18 +02: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
2014-07-18 19:57:18 +02:00
inContainer : folder ] ;
2008-07-14 18:47:10 +02:00
[ 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
2014-07-18 19:57:18 +02:00
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 ;
2014-07-18 19:57:18 +02:00
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 ] )
2014-07-18 19:57:18 +02:00
{
iCalEvent * ownerEvent ;
iCalPerson * person ;
SOGoUser * user ;
/ / 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 ] ;
user = [ SOGoUser userWithLogin : theUID ] ;
if ( ![ ownerEvent userAsAttendee : user ] )
{
/ / 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 "] ;
2014-07-18 19:57:18 +02:00
[ ownerEvent addToAttendees : person ] ;
2012-07-13 22:59:09 +02:00
iCalString = [ [ ownerEvent parent ] versitString ] ;
2014-07-18 19:57:18 +02:00
}
}
2008-12-20 00:04:26 +01:00
else
2014-07-18 19:57:18 +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 :
iCalString = [ [ theEvent parent ] versitString ] ;
}
2008-12-20 00:04:26 +01:00
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 )
2014-07-18 19:57:18 +02:00
[ 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
2014-07-18 19:57:18 +02: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 ]
2014-07-18 19:57:18 +02:00
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 ] ] )
2014-07-18 19:57:18 +02:00
{
if ( recurrenceId == nil )
[ object delete ] ;
else
{
calendar = [ object calendar : NO secure : NO ] ;
/ / If recurrenceId is defined , remove the occurence from
/ / the repeating event .
occurences = [ calendar events ] ;
max = [ occurences count ] ;
count = 1 ;
while ( count < max )
{
currentOccurence = [ occurences objectAtIndex : count ] ;
currentId = [ currentOccurence recurrenceId ] ;
if ( [ currentId compare : recurrenceId ] == NSOrderedSame )
{
[ [ calendar children ] removeObject : currentOccurence ] ;
break ;
}
count + + ;
}
/ / Add an date exception .
event = ( iCalRepeatableEntityObject * ) [ calendar firstChildWithTag : [ object componentTag ] ] ;
[ event addToExceptionDates : recurrenceId ] ;
[ event increaseSequence ] ;
/ / 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 )
2014-07-18 19:57:18 +02:00
[ self _removeEventFromUID : currentUID
owner : owner
2008-12-04 00:08:37 +01:00
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
2014-07-18 19:57:18 +02:00
[ self errorWithFormat : @ "broken chain : delegate with email ' %@' was not found ", mailTo ] ;
2010-07-16 17:03:16 +02:00
}
}
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 )
2014-07-18 19:57:18 +02:00
[ self _addOrUpdateEvent : newEvent
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
2014-07-15 23:34:49 +02:00
/ / This method scans the list of attendees .
- ( NSException * ) _handleAttendeeAvailability : ( NSArray * ) theAttendees
2014-07-04 15:51:41 +02:00
forEvent : ( iCalEvent * ) theEvent
{
iCalPerson * currentAttendee ;
2014-07-07 23:42:43 +02:00
NSMutableArray * attendees , * unavailableAttendees , * whiteList ;
2014-07-04 15:51:41 +02:00
NSEnumerator * enumerator ;
2014-07-07 23:42:43 +02:00
NSPredicate * predicate ;
NSString * currentUID , * ownerUID ;
2014-07-04 15:51:41 +02:00
NSMutableString * reason ;
NSDictionary * values ;
2014-07-07 23:42:43 +02:00
NSMutableDictionary * value , * moduleSettings ;
SOGoUser * user ;
2014-07-07 19:38:37 +02:00
SOGoUserSettings * us ;
2014-07-04 15:51:41 +02:00
int count = 0 , i = 0 ;
2014-07-07 23:42:43 +02:00
2014-07-04 15:51:41 +02:00
2014-07-07 19:38:37 +02:00
/ / Build list of the attendees uids without ressources
2014-07-04 15:51:41 +02:00
attendees = [ NSMutableArray arrayWithCapacity : [ theAttendees count ] ] ;
unavailableAttendees = [ [ NSMutableArray alloc ] init ] ;
enumerator = [ theAttendees objectEnumerator ] ;
2014-07-07 23:42:43 +02:00
ownerUID = [ [ [ self context ] activeUser ] login ] ;
2014-07-07 19:38:37 +02:00
2014-07-04 15:51:41 +02:00
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
2014-07-18 19:57:18 +02:00
currentUID = [ currentAttendee uid ] ;
if ( currentUID )
2014-07-04 15:51:41 +02:00
{
2014-07-18 19:57:18 +02:00
user = [ SOGoUser userWithLogin : currentUID ] ;
us = [ user userSettings ] ;
moduleSettings = [ us objectForKey : @ "Calendar "] ;
/ / Check if the user prevented his account from beeing invited to events
if ( ![ user isResource ] && [ [ moduleSettings objectForKey : @ "PreventInvitations "] boolValue ] )
{
/ / Check if the user have a whiteList
whiteList = [ NSMutableArray arrayWithObject : [ moduleSettings objectForKey : @ "PreventInvitationsWhitelist "] ] ;
predicate = [ NSPredicate predicateWithFormat : @ "SELF CONTAINS [ cd ] %@", ownerUID];
[ whiteList filterUsingPredicate : predicate ] ;
/ / If the filter have a hit , do not add the currentUID to the unavailableAttendees array
if ( [ whiteList count ] == 0 )
{
values = [ NSDictionary dictionaryWithObject : [ user cn ] forKey : @ "Cn "] ;
[ unavailableAttendees addObject : values ] ;
}
}
2014-07-04 15:51:41 +02:00
}
}
2014-07-07 19:38:37 +02:00
2014-07-04 15:51:41 +02:00
count = [ unavailableAttendees count ] ;
2014-07-15 23:34:49 +02:00
if ( count > 0 )
2014-07-04 15:51:41 +02:00
{
2014-07-29 16:36:20 +02:00
reason = [ NSMutableString stringWithString : [ self labelForKey : @ "Inviting the following persons is prohibited : "] ] ;
2014-07-18 19:57:18 +02:00
/ / Add all the unavailable users in the warning message
for ( i = 0 ; i < count ; i + + )
{
value = [ unavailableAttendees objectAtIndex : i ] ;
[ reason appendString : [ value keysWithFormat : @ "\ n %{Cn}"]];
if ( i < count - 2 )
[ reason appendString : @ ", "] ;
}
[ unavailableAttendees release ] ;
return [ NSException exceptionWithHTTPStatus : 409 reason : reason ] ;
2014-07-04 15:51:41 +02:00
}
2014-07-10 17:13:07 +02:00
[ unavailableAttendees release ] ;
return nil ;
2014-07-04 15:51:41 +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 ;
2014-07-04 15:51:41 +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 ] ) )
{
2014-07-18 19:57:18 +02:00
currentUID = [ currentAttendee uid ] ;
if ( currentUID )
{
[ attendees addObject : currentUID ] ;
}
2014-02-03 17:03:23 +01:00
}
2014-07-18 19:57:18 +02:00
2014-02-03 17:03:23 +01:00
/ / 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-07-18 19:57:18 +02:00
{
[ attendees addObject : owner ] ;
}
2014-07-04 15:51:41 +02:00
2014-02-03 17:03:23 +01:00
enumerator = [ attendees objectEnumerator ] ;
while ( ( currentUID = [ enumerator nextObject ] ) )
2014-07-04 15:51:41 +02:00
{
2014-07-18 19:57:18 +02:00
user = [ SOGoUser userWithLogin : currentUID ] ;
2014-07-04 15:51:41 +02:00
2014-07-18 19:57:18 +02:00
if ( [ user isResource ] )
{
SOGoAppointmentFolder * folder ;
NSCalendarDate * start , * end ;
NGCalendarDateRange * range ;
NSMutableArray * fbInfo ;
NSArray * allOccurences ;
2014-07-04 15:51:41 +02:00
2014-07-18 19:57:18 +02:00
BOOL must_delete ;
int i , j ;
/ / 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 .
start = [ [ theEvent startDate ] dateByAddingYears : 0 months : 0 days : 0 hours : 0 minutes : 0 seconds : 1 ] ;
end = [ [ theEvent endDate ] dateByAddingYears : ( [ theEvent isRecurrent ] ? 1 : 0 ) months : 0 days : 0 hours : 0 minutes : 0 seconds : - 1 ] ;
folder = [ user personalCalendarFolderInContext : context ] ;
/ / Deny access to the resource if the ACLs don ' t allow the user
if ( ![ folder aclSQLListingFilter ] )
2014-07-04 15:51:41 +02:00
{
2014-07-18 19:57:18 +02:00
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 ] ;
2014-07-04 15:51:41 +02:00
}
2014-07-18 19:57:18 +02:00
fbInfo = [ NSMutableArray arrayWithArray : [ folder fetchFreeBusyInfosFrom : start
to : end ] ] ;
2014-07-04 15:51:41 +02:00
2014-07-18 19:57:18 +02:00
/ / 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 .
/ /
/ / 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 ;
2014-07-04 15:51:41 +02:00
2014-07-18 19:57:18 +02:00
for ( i = [ fbInfo count ] - 1 ; i > = 0 ; i - - )
{
range = [ NGCalendarDateRange calendarDateRangeWithStartDate : [ [ fbInfo objectAtIndex : i ] objectForKey : @ "startDate "]
endDate : [ [ fbInfo objectAtIndex : i ] objectForKey : @ "endDate "] ] ;
if ( [ [ [ fbInfo objectAtIndex : i ] objectForKey : @ "c_uid "] compare : [ theEvent uid ] ] == NSOrderedSame )
{
[ 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 ] ;
}
}
2014-07-04 15:51:41 +02:00
2014-07-18 19:57:18 +02: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 ;
}
if ( [ fbInfo count ] )
{
/ / 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 ] )
{
if ( currentAttendee )
{
[ [ currentAttendee attributes ] removeObjectForKey : @ "RSVP "] ;
[ currentAttendee setParticipationStatus : iCalPersonPartStatAccepted ] ;
}
}
else
{
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 ] ;
}
}
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-07-04 15:51:41 +02:00
}
2011-04-25 12:31:08 +02:00
}
2014-07-18 19:57:18 +02:00
2011-04-25 12:31:08 +02:00
return nil ;
}
/ /
/ /
/ /
- ( NSException * ) _handleAddedUsers : ( NSArray * ) attendees
2014-07-04 15:51:41 +02:00
fromEvent : ( iCalEvent * ) newEvent
2011-04-25 12:31:08 +02:00
{
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 ;
2014-07-04 15:51:41 +02:00
2011-04-25 12:31:08 +02:00
/ / We check for conflicts
if ( ( e = [ self _handleResourcesConflicts : attendees forEvent : newEvent ] ) )
return e ;
2014-07-15 23:34:49 +02:00
if ( ( e = [ self _handleAttendeeAvailability : attendees forEvent : newEvent ] ) )
2014-07-04 15:51:41 +02:00
return e ;
2007-11-18 11:16:25 +01:00
enumerator = [ attendees objectEnumerator ] ;
while ( ( currentAttendee = [ enumerator nextObject ] ) )
2014-07-18 19:57:18 +02:00
{
currentUID = [ currentAttendee uid ] ;
if ( currentUID )
2014-07-04 15:51:41 +02:00
[ self _addOrUpdateEvent : newEvent
forUID : currentUID
owner : owner ] ;
2014-07-18 19:57:18 +02:00
}
2014-07-04 15:51:41 +02: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 ] )
2014-07-18 19:57:18 +02:00
for ( j = 0 ; j < [ theAttendees count ] ; j + + )
if ( shouldAdd )
[ e addToAttendees : [ theAttendees objectAtIndex : j ] ] ;
else
[ e removeFromAttendees : [ theAttendees objectAtIndex : j ] ] ;
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
2014-07-04 15:51:41 +02:00
if ( ( ex = [ self _handleResourcesConflicts : [ newEvent attendees ] forEvent : newEvent ] ) )
return ex ;
2014-07-15 23:34:49 +02:00
if ( ( ex = [ self _handleAttendeeAvailability : [ newEvent attendees ] 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
2014-07-18 19:57:18 +02:00
add : YES ] ;
2012-04-23 14:58:56 +02:00
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 ] )
2014-07-18 19:57:18 +02:00
{
NSEnumerator * enumerator ;
iCalPerson * currentAttendee ;
NSString * currentUID ;
2009-08-25 23:28:24 +02:00
updatedAttendees = [ newEvent attendees ] ;
2014-07-18 19:57:18 +02:00
enumerator = [ updatedAttendees objectEnumerator ] ;
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
currentUID = [ currentAttendee uid ] ;
if ( currentUID )
[ self _addOrUpdateEvent : newEvent
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 ] ) )
2014-07-18 19:57:18 +02:00
return ex ;
2011-04-25 12:31:08 +02:00
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
}
2014-07-18 19:57:18 +02:00
[ self sendReceiptEmailForObject : newEvent
addedAttendees : addedAttendees
deletedAttendees : deletedAttendees
updatedAttendees : updatedAttendees
operation : EventUpdated ] ;
2012-07-10 02:29:13 +02: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
2010-08-11 16:52:11 +02:00
/ /
/ / Workflow : + - - - - - - - - - - - - - - - - - - - - - - +
/ / | |
/ / [ saveComponent : ] - - - > _handleAddedUsers : fromEvent : < - + |
/ / | | v
/ / + - - - - - - - - - - - - > _handleUpdatedEvent : fromOldEvent : - - - > _addOrUpdateEvent : forUID : owner : < - - - - - - - - - - - +
/ / | | ^ |
/ / v v | |
2014-07-04 15:51:41 +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-07-18 19:57:18 +02:00
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 ;
2014-07-18 19:57:18 +02:00
2009-08-11 04:06:27 +02:00
if ( [ attendees count ] )
2014-07-18 19:57:18 +02:00
{
[ self sendEMailUsingTemplateNamed : @ "Invitation "
forObject : [ newEvent itipEntryWithMethod : @ "request "]
previousObject : nil
toAttendees : attendees
2012-02-24 00:27:59 +01:00
withType : @ "calendar : invitation "] ;
2014-07-18 19:57:18 +02:00
}
2012-07-10 02:29:13 +02:00
[ self sendReceiptEmailForObject : newEvent
2014-07-18 19:57:18 +02:00
addedAttendees : attendees
deletedAttendees : nil
updatedAttendees : nil
operation : EventCreated ] ;
2009-08-11 04:06:27 +02:00
}
2014-07-18 19:57:18 +02:00
else
2009-08-11 04:06:27 +02:00
{
2009-10-19 14:56:52 +02:00
BOOL hasOrganizer ;
2014-07-18 19:57:18 +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 )
2014-07-18 19:57:18 +02:00
oldEvent = [ self component : NO secure : NO ] ;
2008-11-28 20:54:51 +01:00
else
2014-07-18 19:57:18 +02:00
{
/ / If recurrenceId is defined , find the specified occurence
/ / within the repeating vEvent .
recurrenceTime = [ NSString stringWithFormat : @ "%f", [recurrenceId timeIntervalSince1970]];
oldEvent = ( iCalEvent * ) [ self lookupOccurrence : recurrenceTime ] ;
if ( oldEvent == nil ) / / If no occurence found , create one
oldEvent = ( iCalEvent * ) [ self newOccurenceWithID : recurrenceTime ] ;
}
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 ] ;
2014-07-18 19:57:18 +02:00
2009-10-19 14:56:52 +02:00
if ( !hasOrganizer || [ oldMasterEvent userIsOrganizer : ownerUser ] )
2014-07-18 19:57:18 +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
}
2014-07-18 19:57:18 +02: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
2014-07-18 19:57:18 +02:00
forEventUID : ( NSString * ) eventUID
withRecurrenceId : ( NSCalendarDate * ) recurrenceId
withSequence : ( NSNumber * ) sequence
forUID : ( NSString * ) uid
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 )
2014-07-18 19:57:18 +02:00
{
/ / We must update main event and all its occurences ( if any ) .
calendar = [ eventObject calendar : NO secure : NO ] ;
event = ( iCalEntityObject * ) [ calendar firstChildWithTag : [ self componentTag ] ] ;
}
2008-11-22 08:20:22 +01:00
else
2014-07-18 19:57:18 +02:00
{
/ / If recurrenceId is defined , find the specified occurence
/ / within the repeating vEvent .
recurrenceTime = [ NSString stringWithFormat : @ "%f", [recurrenceId timeIntervalSince1970]];
event = [ eventObject lookupOccurrence : recurrenceTime ] ;
if ( event == nil )
/ / If no occurence found , create one
event = [ eventObject newOccurenceWithID : recurrenceTime ] ;
}
2010-08-11 16:52:11 +02:00
if ( [ [ event sequence ] intValue ] < = [ sequence intValue ] )
2014-07-18 19:57:18 +02:00
{
SOGoUser * currentUser ;
currentUser = [ context activeUser ] ;
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 ;
2014-07-18 19:57:18 +02:00
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
2014-07-18 19:57:18 +02:00
addDelegate = YES ;
2009-08-27 18:20:41 +02:00
}
else
{
if ( otherDelegate )
2014-07-18 19:57:18 +02:00
removeDelegate = YES ;
2009-08-27 18:20:41 +02:00
}
2014-07-18 19:57:18 +02:00
2009-08-27 18:20:41 +02:00
if ( removeDelegate )
2014-07-18 19:57:18 +02:00
{
while ( otherDelegate )
{
[ event removeFromAttendees : otherDelegate ] ;
/ / Verify if the delegate was already delegate
delegateEmail = [ otherDelegate delegatedTo ] ;
if ( [ delegateEmail length ] )
delegateEmail = [ delegateEmail rfc822Email ] ;
if ( [ delegateEmail length ] )
otherDelegate = [ event findAttendeeWithEmail : delegateEmail ] ;
else
otherDelegate = NO ;
}
}
2009-08-27 18:20:41 +02:00
if ( addDelegate )
[ event addToAttendees : delegate ] ;
2014-07-18 19:57:18 +02:00
[ otherAttendee setPartStat : [ attendee partStat ] ] ;
2009-08-27 18:20:41 +02:00
[ otherAttendee setDelegatedTo : [ attendee delegatedTo ] ] ;
[ otherAttendee setDelegatedFrom : [ attendee delegatedFrom ] ] ;
2014-07-18 19:57:18 +02:00
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 "] ;
2014-07-18 19:57:18 +02: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 ] ] )
{
NSString * currentEmail , * quotedEmail ;
currentEmail = [ [ currentUser allEmails ] objectAtIndex : 0 ] ;
quotedEmail = [ NSString stringWithFormat : @ "\ "MAILTO : %@\"", currentEmail];
[ otherAttendee setValue : 0 ofAttribute : @ "SENT - BY "
2009-08-11 18:52:10 +02:00
to : quotedEmail ] ;
2014-07-18 19:57:18 +02: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-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
}
2014-07-18 19:57:18 +02: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
2014-07-18 19:57:18 +02:00
statusChange : ( NSString * ) newStatus
inEvent : ( iCalEvent * ) event
2007-11-18 11:16:25 +01:00
{
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 )
2014-07-18 19:57:18 +02:00
{
/ / There was already a delegate
if ( ![ delegate hasSameEmailAddress : otherDelegate ] )
{
/ / The delegate has changed
removeDelegate = YES ;
addDelegate = YES ;
}
}
2009-09-10 20:31:20 +02:00
else
2014-07-18 19:57:18 +02:00
/ / There was no previous delegate
addDelegate = YES ;
2009-09-10 20:31:20 +02:00
}
else
{
if ( otherDelegate )
2014-07-18 19:57:18 +02:00
/ / The user has removed the delegate
removeDelegate = YES ;
2009-09-10 20:31:20 +02:00
}
if ( addDelegate || removeDelegate
2014-07-18 19:57:18 +02:00
|| [ currentStatus caseInsensitiveCompare : newStatus ] != NSOrderedSame )
2007-11-18 11:16:25 +01:00
{
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 ] ] )
2014-07-18 19:57:18 +02:00
{
2009-08-11 18:52:10 +02:00
NSString * currentEmail , * quotedEmail ;
currentEmail = [ [ currentUser allEmails ] objectAtIndex : 0 ] ;
2014-07-18 19:57:18 +02:00
quotedEmail = [ NSString stringWithFormat : @ "\ "MAILTO : %@\"", currentEmail];
2009-08-11 18:52:10 +02:00
[ attendee setValue : 0 ofAttribute : @ "SENT - BY "
to : quotedEmail ] ;
2014-07-18 19:57:18 +02:00
}
2008-11-13 14:41:43 +01:00
else
2014-07-18 19:57:18 +02:00
{
/ / 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 ] ] ;
2014-07-18 19:57:18 +02:00
2009-09-10 20:31:20 +02:00
if ( removeDelegate )
2014-07-18 19:57:18 +02:00
{
delegates = [ NSMutableArray array ] ;
while ( otherDelegate )
{
[ delegates addObject : otherDelegate ] ;
delegatedUID = [ otherDelegate uid ] ;
if ( delegatedUID )
/ / Delegate attendee is a local user ; remove event from his calendar
[ 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 ] )
otherDelegate = [ event findAttendeeWithEmail : delegateEmail ] ;
else
otherDelegate = NO ;
}
[ self sendEMailUsingTemplateNamed : @ "Deletion "
forObject : [ event itipEntryWithMethod : @ "cancel "]
previousObject : nil
toAttendees : delegates
2012-02-24 00:27:59 +01:00
withType : @ "calendar : cancellation "] ;
2014-07-18 19:57:18 +02:00
} / / if ( removeDelegate )
2009-09-10 20:31:20 +02:00
if ( addDelegate )
2014-07-18 19:57:18 +02:00
{
delegatedUID = [ delegate uid ] ;
delegates = [ NSArray arrayWithObject : delegate ] ;
[ event addToAttendees : delegate ] ;
if ( delegatedUID )
/ / Delegate attendee is a local user ; add event to his calendar
[ self _addOrUpdateEvent : event
forUID : delegatedUID
owner : [ theOwnerUser login ] ] ;
[ self sendEMailUsingTemplateNamed : @ "Invitation "
forObject : [ event itipEntryWithMethod : @ "request "]
previousObject : nil
toAttendees : delegates
2012-02-24 00:27:59 +01:00
withType : @ "calendar : invitation "] ;
2014-07-18 19:57:18 +02: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 ] ) )
2014-07-18 19:57:18 +02:00
{
if ( [ event isStillRelevant ] )
[ self sendResponseToOrganizer : event
2012-11-30 21:11:03 +01:00
from : ownerUser ] ;
2014-07-18 19:57:18 +02:00
organizerUID = [ [ event organizer ] uid ] ;
/ / Event is an exception to a recurring event ; retrieve organizer from master event
if ( !organizerUID )
organizerUID = [ [ ( iCalEntityObject * ) [ [ event parent ] firstChildWithTag : [ self componentTag ] ] organizer ] uid ] ;
if ( organizerUID )
/ / Update the attendee in organizer ' s calendar .
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
2014-07-18 19:57:18 +02: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 ;
2014-07-18 19:57:18 +02:00
2008-11-05 22:04:16 +01:00
attendees = [ event attendees ] ;
for ( i = 0 ; i < [ attendees count ] ; i + + )
2014-07-18 19:57:18 +02:00
{
att = [ attendees objectAtIndex : i ] ;
uid = [ att uid ] ;
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 ] ;
2014-07-18 19:57:18 +02:00
}
2007-11-18 11:16:25 +01:00
}
2014-07-18 19:57:18 +02: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 ] ;
2014-07-18 19:57:18 +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 ) ;
2008-07-09 17:43:48 +02:00
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
2014-07-18 19:57:18 +02: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 : "] )
{
2014-07-18 19:57:18 +02:00
[ elements addObject : [ self _caldavSuccessCodeWithRecipient : recipient ] ] ;
2008-07-09 17:43:48 +02:00
}
return elements ;
}
- ( NSArray * ) postCalDAVEventCancelTo : ( NSArray * ) recipients
2014-07-18 19:57:18 +02: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 : "] )
{
2014-07-18 19:57:18 +02:00
[ elements addObject : [ self _caldavSuccessCodeWithRecipient : recipient ] ] ;
2008-07-08 17:42:40 +02:00
}
return elements ;
}
- ( NSArray * ) postCalDAVEventReplyTo : ( NSArray * ) recipients
2014-07-18 19:57:18 +02: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 : "] )
{
2014-07-18 19:57:18 +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
2014-07-18 19:57:18 +02:00
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
{
2014-07-18 19:57:18 +02:00
if ( delegate && ![ [ delegate email ] isEqualToString : [ attendee delegatedTo ] ] )
2012-07-14 04:41:29 +02:00
{
delegatedUid = [ delegate uid ] ;
if ( delegatedUid )
delegatedUser = [ SOGoUser userWithLogin : delegatedUid ] ;
if ( delegatedUser != nil && [ event userIsOrganizer : delegatedUser ] )
ex = [ NSException exceptionWithHTTPStatus : 403
2014-07-18 19:57:18 +02:00
reason : @ "delegate is organizer "] ;
2012-07-14 04:41:29 +02:00
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 :
2014-07-18 19:57:18 +02:00
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
}
2014-07-18 19:57:18 +02:00
else
ex = [ NSException exceptionWithHTTPStatus : 500 / / Server Error
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
{
2014-06-20 17:25:02 +02:00
NSArray * userAgents ;
2010-08-17 16:10:01 +02:00
NSString * v ;
BOOL b ;
2014-06-20 17:25:02 +02:00
int i ;
2010-08-17 16:10:01 +02:00
b = YES ;
if ( theOrganizer && ( v = [ theOrganizer value : 0 ofAttribute : @ "SCHEDULE - AGENT "] ) )
{
if ( [ v caseInsensitiveCompare : @ "NONE "] == NSOrderedSame ||
2014-07-18 19:57:18 +02:00
[ v caseInsensitiveCompare : @ "CLIENT "] == NSOrderedSame )
b = NO ;
2010-08-17 16:10:01 +02:00
}
2014-07-18 19:57:18 +02:00
/ /
/ / 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 "] ;
2014-06-20 17:25:02 +02:00
for ( i = 0 ; i < [ userAgents count ] ; i + + )
{
if ( [ [ userAgents objectAtIndex : i ] rangeOfString : @ "Thunderbird "] . location != NSNotFound &&
[ [ userAgents objectAtIndex : i ] rangeOfString : @ "Lightning "] . location != NSNotFound )
{
b = YES ;
break ;
}
}
2010-08-17 16:10:01 +02:00
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 )
2014-07-18 19:57:18 +02:00
attendees = [ event attendeesWithoutUser : currentUser ] ;
2010-08-11 16:52:11 +02:00
if ( [ attendees count ] )
2014-07-18 19:57:18 +02:00
{
/ / 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 "] ;
2014-07-18 19:57:18 +02:00
}
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
2014-07-18 19:57:18 +02:00
forRecurrenceId : recurrenceId ] ;
2012-07-11 16:06:41 +02:00
send_receipt = NO ;
}
2014-07-18 19:57:18 +02:00
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 ] )
2014-07-18 19:57:18 +02:00
[ event setTransparency : @ "TRANSPARENT "] ;
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 )
2014-07-18 19:57:18 +02:00
return event ;
2010-08-11 16:52:11 +02:00
if ( [ event recurrenceId ] && [ [ event recurrenceId ] compare : theRecurrenceId ] == NSOrderedSame )
2014-07-18 19:57:18 +02:00
return event ;
2010-08-11 16:52:11 +02:00
}
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
2014-07-18 19:57:18 +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 .
/ /
2014-07-18 19:57:18 +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
{
2014-07-18 19:57:18 +02:00
if ( ![ [ rq headersForKey : @ "X - SOGo "] containsObject : @ "NoGroupsDecomposition "] )
2012-07-13 22:59:09 +02:00
[ self _decomposeGroupsInRequestCalendar : calendar ] ;
2014-07-18 19:57:18 +02:00
if ( [ [ ownerUser domainDefaults ] iPhoneForceAllDayTransparency ] && [ rq isIPhone ] )
{
[ self _adjustTransparencyInRequestCalendar : calendar ] ;
}
2012-07-13 22:59:09 +02:00
[ self _adjustEventsInRequestCalendar : calendar ] ;
2009-08-10 22:59:49 +02:00
}
2014-07-18 19:57:18 +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
{
2014-07-18 19:57:18 +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 ] )
2014-07-18 19:57:18 +02:00
{
attendees = [ event attendeesWithoutUser : ownerUser ] ;
if ( [ attendees count ] )
{
if ( ( ex = [ self _handleAddedUsers : attendees fromEvent : event ] ) )
return ex ;
else
{
/ / We might have auto - accepted resources here . If that ' s the
/ / case , let ' s regenerate the versitstring and replace the
/ / one from the request .
[ rq setContent : [ [ [ event parent ] versitString ] dataUsingEncoding : [ rq contentEncoding ] ] ] ;
}
[ self sendEMailUsingTemplateNamed : @ "Invitation "
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 ] )
2014-07-18 19:57:18 +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 - - )
2014-07-18 19:57:18 +02:00
{
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 ] ;
if ( oldEvent == nil )
{
NSString * recurrenceTime ;
recurrenceTime = [ NSString stringWithFormat : @ "%f", [[newEvent recurrenceId] timeIntervalSince1970]];
oldEvent = ( iCalEvent * ) [ self newOccurenceWithID : recurrenceTime ] ;
}
/ / 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 ] ;
2014-07-18 19:57:18 +02:00
}
}
oldEvent = nil ;
newEvent = nil ;
}
2010-08-11 16:52:11 +02:00
/ / 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 )
2014-07-18 19:57:18 +02:00
{
/ / 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 ] ;
}
}
2010-08-11 16:52:11 +02:00
/ / We check if the PUT call is actually an PART - STATE change
/ / from one of the attendees - here ' s the logic :
/ /
2014-07-18 19:57:18 +02:00
/ / if owner == organizer
2010-08-11 16:52:11 +02:00
/ /
/ / if [ context activeUser ] == organizer
/ / [ send the invitation update ]
/ / else
/ / [ react on SENT - BY as someone else is acting for the organizer ]
2014-07-18 19:57:18 +02:00
/ /
/ /
2010-08-11 16:52:11 +02:00
if ( [ [ newEvent attendees ] count ] || [ [ oldEvent attendees ] count ] )
2014-07-18 19:57:18 +02: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 ;
2014-07-18 19:57:18 +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 ] ;
if ( !uid && !recurrenceId )
uid = [ [ [ [ [ newEvent parent ] events ] objectAtIndex : 0 ] organizer ] uid ] ;
/ / 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 .
if ( !recurrenceId && ![ [ [ [ [ newEvent parent ] events ] objectAtIndex : 0 ] organizer ] uid ] )
[ [ [ [ newEvent parent ] events ] objectAtIndex : 0 ] setOrganizer : [ newEvent organizer ] ] ;
2013-03-07 15:29:56 +01:00
if ( newEvent == oldEvent )
newEvent = nil ;
2014-07-18 19:57:18 +02:00
if ( uid && [ uid caseInsensitiveCompare : owner ] == NSOrderedSame )
{
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 ] ) )
2014-07-18 19:57:18 +02:00
return ex ;
}
/ /
/ / 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 ] ] ;
/ / 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
2011-10-03 21:00:50 +02:00
if ( [ [ newEvent sequence ] intValue ] < [ [ oldEvent sequence ] intValue ] )
2014-07-18 19:57:18 +02:00
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 )
2014-07-18 19:57:18 +02:00
[ [ attendee attributes ] removeObjectForKey : @ "RSVP "] ;
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 "] )
{
[ self changeParticipationStatus : @ "DECLINED "
withDelegate : nil / / FIXME ( specify delegate ?)
forRecurrenceId : [ self _addedExDate : oldEvent newEvent : newEvent ] ] ;
}
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 ] ;
}
}
} / / if ( [ [ newEvent attendees ] count ] || [ [ oldEvent attendees ] count ] )
2012-07-11 16:06:41 +02:00
else
2014-07-18 19:57:18 +02:00
{
[ self sendReceiptEmailForObject : newEvent
addedAttendees : nil
deletedAttendees : nil
updatedAttendees : nil
operation : EventUpdated ] ;
}
2012-07-10 02:29:13 +02:00
} / / else of if ( isNew ) ...
2014-07-18 19:57:18 +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 ) ;
2014-07-18 19:57:18 +02:00
2012-07-13 22:59:09 +02:00
ex = [ self saveContentString : [ calendar versitString ]
2014-07-18 19:57:18 +02:00
baseVersion : baseVersion ] ;
return ex ;
2012-07-13 22:59:09 +02:00
}
/ /
/ / 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 )
2014-07-18 19:57:18 +02:00
return ex ;
2012-07-13 22:59:09 +02:00
}
2014-07-18 19:57:18 +02:00
ex = [ self updateContentWithCalendar : rqCalendar fromRequest : rq ] ;
if ( ex )
response = ( WOResponse * ) ex ;
2012-04-24 13:47:32 +02:00
else
{
2012-07-13 22:59:09 +02:00
response = [ _ctx response ] ;
2012-04-24 13:47:32 +02:00
if ( isNew )
2014-07-18 19:57:18 +02:00
[ response setStatus : 201 / * Created * / ] ;
2012-04-24 13:47:32 +02:00
else
2014-07-18 19:57:18 +02:00
[ response setStatus : 204 / * No Content * / ] ;
2012-04-24 13:47:32 +02:00
etag = [ self davEntityTag ] ;
if ( etag )
2014-07-18 19:57:18 +02:00
[ response setHeader : etag forKey : @ "etag "] ;
2012-04-24 13:47:32 +02:00
}
2014-07-18 19:57:18 +02:00
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 * /