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
233 SOGoUser *currentUser;
235 [[newEvent parent] setMethod: @""];
236 currentUser = [context activeUser];
237 if ([newEvent userIsOrganizer: currentUser])
239 oldEvent = [self component: NO secure: NO];
241 [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent];
244 attendees = [newEvent attendeesWithoutUser: [context activeUser]];
245 if ([attendees count])
247 [self _handleAddedUsers: attendees fromEvent: newEvent];
248 [self sendEMailUsingTemplateNamed: @"Invitation"
250 andNewObject: [newEvent itipEntryWithMethod: @"request"]
251 toAttendees: attendees];
254 if (![[newEvent attendees] count]
255 && [[self ownerInContext: context]
256 isEqualToString: [currentUser login]])
257 [[newEvent uniqueChildWithTag: @"organizer"] setValue: 0
262 [super saveComponent: newEvent];
265 - (NSException *) _updateAttendee: (iCalPerson *) attendee
266 forEventUID: (NSString *) eventUID
267 withSequence: (NSNumber *) sequence
268 forUID: (NSString *) uid
270 SOGoAppointmentObject *eventObject;
272 iCalPerson *otherAttendee;
273 NSString *iCalString;
278 eventObject = [self _lookupEvent: eventUID forUID: uid];
279 if (![eventObject isNew])
281 event = [eventObject component: NO secure: NO];
282 if ([[event sequence] compare: sequence]
285 otherAttendee = [event findParticipant: [context activeUser]];
286 [otherAttendee setPartStat: [attendee partStat]];
287 iCalString = [[event parent] versitString];
288 error = [eventObject saveContentString: iCalString];
295 - (NSException *) _handleAttendee: (iCalPerson *) attendee
296 statusChange: (NSString *) newStatus
297 inEvent: (iCalEvent *) event
299 NSString *newContent, *currentStatus, *organizerUID;
304 currentStatus = [attendee partStat];
305 if ([currentStatus caseInsensitiveCompare: newStatus]
308 [attendee setPartStat: newStatus];
309 newContent = [[event parent] versitString];
310 ex = [self saveContentString: newContent];
311 if (!(ex || [event userIsOrganizer: [context activeUser]]))
313 if ([[attendee rsvp] isEqualToString: @"true"])
314 [self sendResponseToOrganizer];
315 organizerUID = [[event organizer] uid];
317 ex = [self _updateAttendee: attendee
318 forEventUID: [event uid]
319 withSequence: [event sequence]
320 forUID: organizerUID];
327 - (NSException *) changeParticipationStatus: (NSString *) _status
330 iCalPerson *attendee;
335 event = [self component: NO secure: NO];
338 attendee = [event findParticipant: [context activeUser]];
340 ex = [self _handleAttendee: attendee statusChange: _status
343 ex = [NSException exceptionWithHTTPStatus: 404 /* Not Found */
344 reason: @"user does not participate in this "
348 ex = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
349 reason: @"unable to parse event record"];
354 - (void) prepareDelete
357 SOGoUser *currentUser;
360 if ([[context request] handledByDefaultHandler])
362 currentUser = [context activeUser];
363 event = [self component: NO secure: NO];
364 if ([event userIsOrganizer: currentUser])
366 attendees = [event attendeesWithoutUser: currentUser];
367 if ([attendees count])
369 [self _handleRemovedUsers: attendees];
370 [self sendEMailUsingTemplateNamed: @"Deletion"
372 andNewObject: [event itipEntryWithMethod: @"cancel"]
373 toAttendees: attendees];
376 else if ([event userIsParticipant: currentUser])
377 [self changeParticipationStatus: @"DECLINED"];
383 - (NSString *) outlookMessageClass
385 return @"IPM.Appointment";
388 @end /* SOGoAppointmentObject */