]> err.no Git - scalable-opengroupware.org/blob - SoObjects/Appointments/SOGoAppointmentObject.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1267 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   SOGoUser *currentUser;
234
235   [[newEvent parent] setMethod: @""];
236   currentUser = [context activeUser];
237   if ([newEvent userIsOrganizer: currentUser])
238     {
239       oldEvent = [self component: NO secure: NO];
240       if (oldEvent)
241         [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent];
242       else
243         {
244           attendees = [newEvent attendeesWithoutUser: [context activeUser]];
245           if ([attendees count])
246             {
247               [self _handleAddedUsers: attendees fromEvent: newEvent];
248               [self sendEMailUsingTemplateNamed: @"Invitation"
249                     forOldObject: nil
250                     andNewObject: [newEvent itipEntryWithMethod: @"request"]
251                     toAttendees: attendees];
252             }
253
254           if (![[newEvent attendees] count]
255               && [[self ownerInContext: context]
256                    isEqualToString: [currentUser login]])
257             [[newEvent uniqueChildWithTag: @"organizer"] setValue: 0
258                                                          to: @""];
259         }
260     }
261
262   [super saveComponent: newEvent];
263 }
264
265 - (NSException *) _updateAttendee: (iCalPerson *) attendee
266                       forEventUID: (NSString *) eventUID
267                      withSequence: (NSNumber *) sequence
268                            forUID: (NSString *) uid
269 {
270   SOGoAppointmentObject *eventObject;
271   iCalEvent *event;
272   iCalPerson *otherAttendee;
273   NSString *iCalString;
274   NSException *error;
275
276   error = nil;
277
278   eventObject = [self _lookupEvent: eventUID forUID: uid];
279   if (![eventObject isNew])
280     {
281       event = [eventObject component: NO secure: NO];
282       if ([[event sequence] compare: sequence]
283           == NSOrderedSame)
284         {
285           otherAttendee = [event findParticipant: [context activeUser]];
286           [otherAttendee setPartStat: [attendee partStat]];
287           iCalString = [[event parent] versitString];
288           error = [eventObject saveContentString: iCalString];
289         }
290     }
291
292   return error;
293 }
294
295 - (NSException *) _handleAttendee: (iCalPerson *) attendee
296                      statusChange: (NSString *) newStatus
297                           inEvent: (iCalEvent *) event
298 {
299   NSString *newContent, *currentStatus, *organizerUID;
300   NSException *ex;
301
302   ex = nil;
303
304   currentStatus = [attendee partStat];
305   if ([currentStatus caseInsensitiveCompare: newStatus]
306       != NSOrderedSame)
307     {
308       [attendee setPartStat: newStatus];
309       newContent = [[event parent] versitString];
310       ex = [self saveContentString: newContent];
311       if (!(ex || [event userIsOrganizer: [context activeUser]]))
312         {
313           if ([[attendee rsvp] isEqualToString: @"true"])
314             [self sendResponseToOrganizer];
315           organizerUID = [[event organizer] uid];
316           if (organizerUID)
317             ex = [self _updateAttendee: attendee
318                        forEventUID: [event uid]
319                        withSequence: [event sequence]
320                        forUID: organizerUID];
321         }
322     }
323
324   return ex;
325 }
326
327 - (NSException *) changeParticipationStatus: (NSString *) _status
328 {
329   iCalEvent *event;
330   iCalPerson *attendee;
331   NSException *ex;
332   
333   ex = nil;
334
335   event = [self component: NO secure: NO];
336   if (event)
337     {
338       attendee = [event findParticipant: [context activeUser]];
339       if (attendee)
340         ex = [self _handleAttendee: attendee statusChange: _status
341                    inEvent: event];
342       else
343         ex = [NSException exceptionWithHTTPStatus: 404 /* Not Found */
344                           reason: @"user does not participate in this "
345                           @"calendar event"];
346     }
347   else
348     ex = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
349                       reason: @"unable to parse event record"];
350
351   return ex;
352 }
353
354 - (void) prepareDelete
355 {
356   iCalEvent *event;
357   SOGoUser *currentUser;
358   NSArray *attendees;
359
360   if ([[context request] handledByDefaultHandler])
361     {
362       currentUser = [context activeUser];
363       event = [self component: NO secure: NO];
364       if ([event userIsOrganizer: currentUser])
365         {
366           attendees = [event attendeesWithoutUser: currentUser];
367           if ([attendees count])
368             {
369               [self _handleRemovedUsers: attendees];
370               [self sendEMailUsingTemplateNamed: @"Deletion"
371                     forOldObject: nil
372                     andNewObject: [event itipEntryWithMethod: @"cancel"]
373                     toAttendees: attendees];
374             }
375         }
376       else if ([event userIsParticipant: currentUser])
377         [self changeParticipationStatus: @"DECLINED"];
378     }
379 }
380
381 /* message type */
382
383 - (NSString *) outlookMessageClass
384 {
385   return @"IPM.Appointment";
386 }
387
388 @end /* SOGoAppointmentObject */