2 Copyright (C) 2004-2005 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #import <Foundation/NSCalendarDate.h>
24 #import <NGObjWeb/NSException+HTTP.h>
25 #import <NGObjWeb/WOContext+SoObjects.h>
26 #import <NGExtensions/NSNull+misc.h>
27 #import <NGExtensions/NSObject+Logs.h>
28 #import <NGCards/iCalCalendar.h>
29 #import <NGCards/iCalEvent.h>
30 #import <NGCards/iCalEventChanges.h>
31 #import <NGCards/iCalPerson.h>
33 #import <SoObjects/SOGo/iCalEntityObject+Utilities.h>
34 #import <SoObjects/SOGo/LDAPUserManager.h>
35 #import <SoObjects/SOGo/NSArray+Utilities.h>
36 #import <SoObjects/SOGo/SOGoObject.h>
37 #import <SoObjects/SOGo/SOGoPermissions.h>
38 #import <SoObjects/SOGo/SOGoUser.h>
39 #import <SoObjects/SOGo/WORequest+SOGo.h>
41 #import "NSArray+Appointments.h"
42 #import "SOGoAppointmentFolder.h"
43 #import "iCalEventChanges+SOGo.h"
44 #import "iCalEntityObject+SOGo.h"
45 #import "iCalPerson+SOGo.h"
47 #import "SOGoAppointmentObject.h"
49 @implementation SOGoAppointmentObject
51 - (NSString *) componentTag
56 - (SOGoAppointmentObject *) _lookupEvent: (NSString *) eventUID
57 forUID: (NSString *) uid
59 SOGoAppointmentFolder *folder;
60 SOGoAppointmentObject *object;
61 NSString *possibleName;
63 folder = [container lookupCalendarFolderForUID: uid];
64 object = [folder lookupName: nameInContainer
65 inContext: context acquire: NO];
66 if ([object isKindOfClass: [NSException class]])
68 possibleName = [folder resourceNameForEventUID: eventUID];
71 object = [folder lookupName: nameInContainer
72 inContext: context acquire: NO];
73 if ([object isKindOfClass: [NSException class]])
79 object = [SOGoAppointmentObject objectWithName: nameInContainer
85 - (void) _addOrUpdateEvent: (iCalEvent *) event
86 forUID: (NSString *) uid
88 SOGoAppointmentObject *object;
89 NSString *iCalString, *userLogin;
91 userLogin = [[context activeUser] login];
92 if (![uid isEqualToString: userLogin])
94 object = [self _lookupEvent: [event uid] forUID: uid];
95 iCalString = [[event parent] versitString];
96 [object saveContentString: iCalString];
100 - (void) _removeEventFromUID: (NSString *) uid
102 SOGoAppointmentFolder *folder;
103 SOGoAppointmentObject *object;
106 userLogin = [[context activeUser] login];
107 if (![uid isEqualToString: userLogin])
109 folder = [container lookupCalendarFolderForUID: uid];
110 object = [folder lookupName: nameInContainer
111 inContext: context acquire: NO];
112 if (![object isKindOfClass: [NSException class]])
117 - (void) _handleRemovedUsers: (NSArray *) attendees
119 NSEnumerator *enumerator;
120 iCalPerson *currentAttendee;
121 NSString *currentUID;
123 enumerator = [attendees objectEnumerator];
124 while ((currentAttendee = [enumerator nextObject]))
126 currentUID = [currentAttendee uid];
128 [self _removeEventFromUID: currentUID];
132 - (void) _requireResponseFromAttendees: (NSArray *) attendees
134 NSEnumerator *enumerator;
135 iCalPerson *currentAttendee;
137 enumerator = [attendees objectEnumerator];
138 while ((currentAttendee = [enumerator nextObject]))
140 [currentAttendee setRsvp: @"TRUE"];
141 [currentAttendee setParticipationStatus: iCalPersonPartStatNeedsAction];
145 - (void) _handleSequenceUpdateInEvent: (iCalEvent *) newEvent
146 ignoringAttendees: (NSArray *) attendees
147 fromOldEvent: (iCalEvent *) oldEvent
149 NSMutableArray *updateAttendees, *updateUIDs;
150 NSEnumerator *enumerator;
151 iCalPerson *currentAttendee;
152 NSString *currentUID;
154 updateAttendees = [NSMutableArray arrayWithArray: [newEvent attendees]];
155 [updateAttendees removeObjectsInArray: attendees];
157 updateUIDs = [NSMutableArray arrayWithCapacity: [updateAttendees count]];
158 enumerator = [updateAttendees objectEnumerator];
159 while ((currentAttendee = [enumerator nextObject]))
161 currentUID = [currentAttendee uid];
163 [self _addOrUpdateEvent: newEvent
167 [self sendEMailUsingTemplateNamed: @"Update"
168 forOldObject: oldEvent
169 andNewObject: [newEvent itipEntryWithMethod: @"request"]
170 toAttendees: updateAttendees];
173 - (void) _handleAddedUsers: (NSArray *) attendees
174 fromEvent: (iCalEvent *) newEvent
176 NSEnumerator *enumerator;
177 iCalPerson *currentAttendee;
178 NSString *currentUID;
180 enumerator = [attendees objectEnumerator];
181 while ((currentAttendee = [enumerator nextObject]))
183 currentUID = [currentAttendee uid];
185 [self _addOrUpdateEvent: newEvent
190 - (void) _handleUpdatedEvent: (iCalEvent *) newEvent
191 fromOldEvent: (iCalEvent *) oldEvent
194 iCalEventChanges *changes;
196 changes = [newEvent getChangesRelativeToEvent: oldEvent];
197 attendees = [changes deletedAttendees];
198 if ([attendees count])
200 [self _handleRemovedUsers: attendees];
201 [self sendEMailUsingTemplateNamed: @"Deletion"
202 forOldObject: oldEvent
203 andNewObject: [newEvent itipEntryWithMethod: @"cancel"]
204 toAttendees: attendees];
207 attendees = [changes insertedAttendees];
208 if ([changes sequenceShouldBeIncreased])
210 [newEvent increaseSequence];
211 [self _requireResponseFromAttendees: [newEvent attendees]];
212 [self _handleSequenceUpdateInEvent: newEvent
213 ignoringAttendees: attendees
214 fromOldEvent: oldEvent];
217 [self _requireResponseFromAttendees: attendees];
219 if ([attendees count])
221 [self _handleAddedUsers: attendees fromEvent: newEvent];
222 [self sendEMailUsingTemplateNamed: @"Invitation"
223 forOldObject: oldEvent
224 andNewObject: [newEvent itipEntryWithMethod: @"request"]
225 toAttendees: attendees];
229 - (void) saveComponent: (iCalEvent *) newEvent
234 [[newEvent parent] setMethod: @""];
235 if ([newEvent userIsOrganizer: [context activeUser]])
237 oldEvent = [self component: NO secure: NO];
239 [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent];
242 attendees = [newEvent attendeesWithoutUser: [context activeUser]];
243 if ([attendees count])
245 [self _handleAddedUsers: attendees fromEvent: newEvent];
246 [self sendEMailUsingTemplateNamed: @"Invitation"
248 andNewObject: [newEvent itipEntryWithMethod: @"request"]
249 toAttendees: attendees];
252 if (![[newEvent attendees] count])
253 [[newEvent uniqueChildWithTag: @"organizer"] setValue: 0
258 [super saveComponent: newEvent];
261 - (NSException *) _updateAttendee: (iCalPerson *) attendee
262 forEventUID: (NSString *) eventUID
263 withSequence: (NSNumber *) sequence
264 forUID: (NSString *) uid
266 SOGoAppointmentObject *eventObject;
268 iCalPerson *otherAttendee;
269 NSString *iCalString;
274 eventObject = [self _lookupEvent: eventUID forUID: uid];
275 if (![eventObject isNew])
277 event = [eventObject component: NO secure: NO];
278 if ([[event sequence] compare: sequence]
281 otherAttendee = [event findParticipant: [context activeUser]];
282 [otherAttendee setPartStat: [attendee partStat]];
283 iCalString = [[event parent] versitString];
284 error = [eventObject saveContentString: iCalString];
291 - (NSException *) _handleAttendee: (iCalPerson *) attendee
292 statusChange: (NSString *) newStatus
293 inEvent: (iCalEvent *) event
295 NSString *newContent, *currentStatus, *organizerUID;
300 currentStatus = [attendee partStat];
301 if ([currentStatus caseInsensitiveCompare: newStatus]
304 [attendee setPartStat: newStatus];
305 newContent = [[event parent] versitString];
306 ex = [self saveContentString: newContent];
307 if (!(ex || [event userIsOrganizer: [context activeUser]]))
309 if ([[attendee rsvp] isEqualToString: @"true"])
310 [self sendResponseToOrganizer];
311 organizerUID = [[event organizer] uid];
313 ex = [self _updateAttendee: attendee
314 forEventUID: [event uid]
315 withSequence: [event sequence]
316 forUID: organizerUID];
323 - (NSException *) changeParticipationStatus: (NSString *) _status
326 iCalPerson *attendee;
331 event = [self component: NO secure: NO];
334 attendee = [event findParticipant: [context activeUser]];
336 ex = [self _handleAttendee: attendee statusChange: _status
339 ex = [NSException exceptionWithHTTPStatus: 404 /* Not Found */
340 reason: @"user does not participate in this "
344 ex = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
345 reason: @"unable to parse event record"];
350 - (void) prepareDelete
353 SOGoUser *currentUser;
356 if ([[context request] handledByDefaultHandler])
358 currentUser = [context activeUser];
359 event = [self component: NO secure: NO];
360 if ([event userIsOrganizer: currentUser])
362 attendees = [event attendeesWithoutUser: currentUser];
363 if ([attendees count])
365 [self _handleRemovedUsers: attendees];
366 [self sendEMailUsingTemplateNamed: @"Deletion"
368 andNewObject: [event itipEntryWithMethod: @"cancel"]
369 toAttendees: attendees];
372 else if ([event userIsParticipant: currentUser])
373 [self changeParticipationStatus: @"DECLINED"];
379 - (NSString *) outlookMessageClass
381 return @"IPM.Appointment";
384 @end /* SOGoAppointmentObject */