]> err.no Git - scalable-opengroupware.org/blob - SoObjects/Appointments/SOGoAppointmentObject.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1265 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / SoObjects / Appointments / SOGoAppointmentObject.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #import <Foundation/NSCalendarDate.h>
23
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>
32
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>
40
41 #import "NSArray+Appointments.h"
42 #import "SOGoAppointmentFolder.h"
43 #import "iCalEventChanges+SOGo.h"
44 #import "iCalEntityObject+SOGo.h"
45 #import "iCalPerson+SOGo.h"
46
47 #import "SOGoAppointmentObject.h"
48
49 @implementation SOGoAppointmentObject
50
51 - (NSString *) componentTag
52 {
53   return @"vevent";
54 }
55
56 - (SOGoAppointmentObject *) _lookupEvent: (NSString *) eventUID
57                                   forUID: (NSString *) uid
58 {
59   SOGoAppointmentFolder *folder;
60   SOGoAppointmentObject *object;
61   NSString *possibleName;
62
63   folder = [container lookupCalendarFolderForUID: uid];
64   object = [folder lookupName: nameInContainer
65                    inContext: context acquire: NO];
66   if ([object isKindOfClass: [NSException class]])
67     {
68       possibleName = [folder resourceNameForEventUID: eventUID];
69       if (possibleName)
70         {
71           object = [folder lookupName: nameInContainer
72                            inContext: context acquire: NO];
73           if ([object isKindOfClass: [NSException class]])
74             object = nil;
75         }
76     }
77
78   if (!object)
79     object = [SOGoAppointmentObject objectWithName: nameInContainer
80                                     inContainer: folder];
81
82   return object;
83 }
84
85 - (void) _addOrUpdateEvent: (iCalEvent *) event
86                     forUID: (NSString *) uid
87 {
88   SOGoAppointmentObject *object;
89   NSString *iCalString, *userLogin;
90
91   userLogin = [[context activeUser] login];
92   if (![uid isEqualToString: userLogin])
93     {
94       object = [self _lookupEvent: [event uid] forUID: uid];
95       iCalString = [[event parent] versitString];
96       [object saveContentString: iCalString];
97     }
98 }
99
100 - (void) _removeEventFromUID: (NSString *) uid
101 {
102   SOGoAppointmentFolder *folder;
103   SOGoAppointmentObject *object;
104   NSString *userLogin;
105
106   userLogin = [[context activeUser] login];
107   if (![uid isEqualToString: userLogin])
108     {
109       folder = [container lookupCalendarFolderForUID: uid];
110       object = [folder lookupName: nameInContainer
111                        inContext: context acquire: NO];
112       if (![object isKindOfClass: [NSException class]])
113         [object delete];
114     }
115 }
116
117 - (void) _handleRemovedUsers: (NSArray *) attendees
118 {
119   NSEnumerator *enumerator;
120   iCalPerson *currentAttendee;
121   NSString *currentUID;
122
123   enumerator = [attendees objectEnumerator];
124   while ((currentAttendee = [enumerator nextObject]))
125     {
126       currentUID = [currentAttendee uid];
127       if (currentUID)
128         [self _removeEventFromUID: currentUID];
129     }
130 }
131
132 - (void) _requireResponseFromAttendees: (NSArray *) attendees
133 {
134   NSEnumerator *enumerator;
135   iCalPerson *currentAttendee;
136
137   enumerator = [attendees objectEnumerator];
138   while ((currentAttendee = [enumerator nextObject]))
139     {
140       [currentAttendee setRsvp: @"TRUE"];
141       [currentAttendee setParticipationStatus: iCalPersonPartStatNeedsAction];
142     }
143 }
144
145 - (void) _handleSequenceUpdateInEvent: (iCalEvent *) newEvent
146                     ignoringAttendees: (NSArray *) attendees
147                          fromOldEvent: (iCalEvent *) oldEvent
148 {
149   NSMutableArray *updateAttendees, *updateUIDs;
150   NSEnumerator *enumerator;
151   iCalPerson *currentAttendee;
152   NSString *currentUID;
153
154   updateAttendees = [NSMutableArray arrayWithArray: [newEvent attendees]];
155   [updateAttendees removeObjectsInArray: attendees];
156
157   updateUIDs = [NSMutableArray arrayWithCapacity: [updateAttendees count]];
158   enumerator = [updateAttendees objectEnumerator];
159   while ((currentAttendee = [enumerator nextObject]))
160     {
161       currentUID = [currentAttendee uid];
162       if (currentUID)
163         [self _addOrUpdateEvent: newEvent
164               forUID: currentUID];
165     }
166
167   [self sendEMailUsingTemplateNamed: @"Update"
168         forOldObject: oldEvent
169         andNewObject: [newEvent itipEntryWithMethod: @"request"]
170         toAttendees: updateAttendees];
171 }
172
173 - (void) _handleAddedUsers: (NSArray *) attendees
174                  fromEvent: (iCalEvent *) newEvent
175 {
176   NSEnumerator *enumerator;
177   iCalPerson *currentAttendee;
178   NSString *currentUID;
179
180   enumerator = [attendees objectEnumerator];
181   while ((currentAttendee = [enumerator nextObject]))
182     {
183       currentUID = [currentAttendee uid];
184       if (currentUID)
185         [self _addOrUpdateEvent: newEvent
186               forUID: currentUID];
187     }
188 }
189
190 - (void) _handleUpdatedEvent: (iCalEvent *) newEvent
191                 fromOldEvent: (iCalEvent *) oldEvent
192 {
193   NSArray *attendees;
194   iCalEventChanges *changes;
195
196   changes = [newEvent getChangesRelativeToEvent: oldEvent];
197   attendees = [changes deletedAttendees];
198   if ([attendees count])
199     {
200       [self _handleRemovedUsers: attendees];
201       [self sendEMailUsingTemplateNamed: @"Deletion"
202             forOldObject: oldEvent
203             andNewObject: [newEvent itipEntryWithMethod: @"cancel"]
204             toAttendees: attendees];
205     }
206
207   attendees = [changes insertedAttendees];
208   if ([changes sequenceShouldBeIncreased])
209     {
210       [newEvent increaseSequence];
211       [self _requireResponseFromAttendees: [newEvent attendees]];
212       [self _handleSequenceUpdateInEvent: newEvent
213             ignoringAttendees: attendees
214             fromOldEvent: oldEvent];
215     }
216   else
217     [self _requireResponseFromAttendees: attendees];
218
219   if ([attendees count])
220     {
221       [self _handleAddedUsers: attendees fromEvent: newEvent];
222       [self sendEMailUsingTemplateNamed: @"Invitation"
223             forOldObject: oldEvent
224             andNewObject: [newEvent itipEntryWithMethod: @"request"]
225             toAttendees: attendees];
226     }
227 }
228
229 - (void) saveComponent: (iCalEvent *) newEvent
230 {
231   iCalEvent *oldEvent;
232   NSArray *attendees;
233
234   [[newEvent parent] setMethod: @""];
235   if ([newEvent userIsOrganizer: [context activeUser]])
236     {
237       oldEvent = [self component: NO secure: NO];
238       if (oldEvent)
239         [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent];
240       else
241         {
242           attendees = [newEvent attendeesWithoutUser: [context activeUser]];
243           if ([attendees count])
244             {
245               [self _handleAddedUsers: attendees fromEvent: newEvent];
246               [self sendEMailUsingTemplateNamed: @"Invitation"
247                     forOldObject: nil
248                     andNewObject: [newEvent itipEntryWithMethod: @"request"]
249                     toAttendees: attendees];
250             }
251
252           if (![[newEvent attendees] count])
253             [[newEvent uniqueChildWithTag: @"organizer"] setValue: 0
254                                                          to: @""];
255         }
256     }
257
258   [super saveComponent: newEvent];
259 }
260
261 - (NSException *) _updateAttendee: (iCalPerson *) attendee
262                       forEventUID: (NSString *) eventUID
263                      withSequence: (NSNumber *) sequence
264                            forUID: (NSString *) uid
265 {
266   SOGoAppointmentObject *eventObject;
267   iCalEvent *event;
268   iCalPerson *otherAttendee;
269   NSString *iCalString;
270   NSException *error;
271
272   error = nil;
273
274   eventObject = [self _lookupEvent: eventUID forUID: uid];
275   if (![eventObject isNew])
276     {
277       event = [eventObject component: NO secure: NO];
278       if ([[event sequence] compare: sequence]
279           == NSOrderedSame)
280         {
281           otherAttendee = [event findParticipant: [context activeUser]];
282           [otherAttendee setPartStat: [attendee partStat]];
283           iCalString = [[event parent] versitString];
284           error = [eventObject saveContentString: iCalString];
285         }
286     }
287
288   return error;
289 }
290
291 - (NSException *) _handleAttendee: (iCalPerson *) attendee
292                      statusChange: (NSString *) newStatus
293                           inEvent: (iCalEvent *) event
294 {
295   NSString *newContent, *currentStatus, *organizerUID;
296   NSException *ex;
297
298   ex = nil;
299
300   currentStatus = [attendee partStat];
301   if ([currentStatus caseInsensitiveCompare: newStatus]
302       != NSOrderedSame)
303     {
304       [attendee setPartStat: newStatus];
305       newContent = [[event parent] versitString];
306       ex = [self saveContentString: newContent];
307       if (!(ex || [event userIsOrganizer: [context activeUser]]))
308         {
309           if ([[attendee rsvp] isEqualToString: @"true"])
310             [self sendResponseToOrganizer];
311           organizerUID = [[event organizer] uid];
312           if (organizerUID)
313             ex = [self _updateAttendee: attendee
314                        forEventUID: [event uid]
315                        withSequence: [event sequence]
316                        forUID: organizerUID];
317         }
318     }
319
320   return ex;
321 }
322
323 - (NSException *) changeParticipationStatus: (NSString *) _status
324 {
325   iCalEvent *event;
326   iCalPerson *attendee;
327   NSException *ex;
328   
329   ex = nil;
330
331   event = [self component: NO secure: NO];
332   if (event)
333     {
334       attendee = [event findParticipant: [context activeUser]];
335       if (attendee)
336         ex = [self _handleAttendee: attendee statusChange: _status
337                    inEvent: event];
338       else
339         ex = [NSException exceptionWithHTTPStatus: 404 /* Not Found */
340                           reason: @"user does not participate in this "
341                           @"calendar event"];
342     }
343   else
344     ex = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
345                       reason: @"unable to parse event record"];
346
347   return ex;
348 }
349
350 - (void) prepareDelete
351 {
352   iCalEvent *event;
353   SOGoUser *currentUser;
354   NSArray *attendees;
355
356   if ([[context request] handledByDefaultHandler])
357     {
358       currentUser = [context activeUser];
359       event = [self component: NO secure: NO];
360       if ([event userIsOrganizer: currentUser])
361         {
362           attendees = [event attendeesWithoutUser: currentUser];
363           if ([attendees count])
364             {
365               [self _handleRemovedUsers: attendees];
366               [self sendEMailUsingTemplateNamed: @"Deletion"
367                     forOldObject: nil
368                     andNewObject: [event itipEntryWithMethod: @"cancel"]
369                     toAttendees: attendees];
370             }
371         }
372       else if ([event userIsParticipant: currentUser])
373         [self changeParticipationStatus: @"DECLINED"];
374     }
375 }
376
377 /* message type */
378
379 - (NSString *) outlookMessageClass
380 {
381   return @"IPM.Appointment";
382 }
383
384 @end /* SOGoAppointmentObject */