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/NSException.h>
23 #import <Foundation/NSUserDefaults.h>
25 #import <NGExtensions/NSObject+Logs.h>
26 #import <NGExtensions/NSNull+misc.h>
27 #import <NGCards/iCalCalendar.h>
28 #import <NGCards/iCalToDo.h>
29 #import <NGCards/iCalEventChanges.h>
30 #import <NGCards/iCalPerson.h>
31 #import <SOGo/LDAPUserManager.h>
33 #import <SoObjects/SOGo/SOGoMailer.h>
35 #import "NSArray+Appointments.h"
36 #import "SOGoAptMailNotification.h"
37 #import "SOGoAppointmentFolder.h"
39 #import "SOGoTaskObject.h"
41 @interface SOGoTaskObject (PrivateAPI)
43 - (NSString *) homePageURLForPerson: (iCalPerson *) _person;
47 @implementation SOGoTaskObject
49 static NSString *mailTemplateDefaultLanguage = nil;
53 static BOOL didInit = NO;
58 ud = [NSUserDefaults standardUserDefaults];
59 mailTemplateDefaultLanguage = [[ud stringForKey:@"SOGoDefaultLanguage"]
61 if (!mailTemplateDefaultLanguage)
62 mailTemplateDefaultLanguage = @"French";
65 - (NSString *) componentTag
72 - (NSArray *)attendeeUIDsFromTask:(iCalToDo *)_task {
77 NSString *email, *uid;
79 if (![_task isNotNull])
82 if ((attendees = [_task attendees]) == nil)
84 count = [attendees count];
85 uids = [NSMutableArray arrayWithCapacity:count + 1];
87 um = [LDAPUserManager sharedUserManager];
91 email = [[_task organizer] rfc822Email];
92 if ([email isNotNull]) {
93 uid = [um getUIDForEmail:email];
94 if ([uid isNotNull]) {
98 [self logWithFormat:@"Note: got no uid for organizer: '%@'", email];
103 for (i = 0; i < count; i++) {
106 person = [attendees objectAtIndex:i];
107 email = [person rfc822Email];
108 if (![email isNotNull]) continue;
110 uid = [um getUIDForEmail:email];
111 if (![uid isNotNull]) {
112 [self logWithFormat:@"Note: got no uid for email: '%@'", email];
115 if (![uids containsObject:uid])
116 [uids addObject:uid];
122 /* store in all the other folders */
124 - (NSException *)saveContentString:(NSString *)_iCal inUIDs:(NSArray *)_uids {
127 NSException *allErrors = nil;
129 e = [[container lookupCalendarFoldersForUIDs: _uids inContext: context]
131 while ((folder = [e nextObject]) != nil) {
133 SOGoTaskObject *task;
135 if (![folder isNotNull]) /* no folder was found for given UID */
138 task = [folder lookupName:[self nameInContainer] inContext: context
140 if ([task isKindOfClass: [NSException class]])
142 [self logWithFormat:@"Note: an exception occured finding '%@' in folder: %@",
143 [self nameInContainer], folder];
144 [self logWithFormat:@"the exception reason was: %@",
145 [(NSException *) task reason]];
149 if (![task isNotNull]) {
150 [self logWithFormat:@"Note: did not find '%@' in folder: %@",
151 [self nameInContainer], folder];
155 if ((error = [task primarySaveContentString:_iCal]) != nil) {
156 [self logWithFormat:@"Note: failed to save iCal in folder: %@", folder];
157 // TODO: make compound
163 // - (NSException *)deleteInUIDs:(NSArray *)_uids {
166 // NSException *allErrors = nil;
168 // e = [[container lookupCalendarFoldersForUIDs: _uids inContext: context]
169 // objectEnumerator];
170 // while ((folder = [e nextObject])) {
171 // NSException *error;
172 // SOGoTaskObject *task;
174 // task = [folder lookupName: [self nameInContainer]
175 // inContext: context
177 // if (![task isNotNull]) {
178 // [self logWithFormat:@"Note: did not find '%@' in folder: %@",
179 // [self nameInContainer], folder];
182 // if ([task isKindOfClass: [NSException class]]) {
183 // [self logWithFormat:@"Exception: %@", [(NSException *) task reason]];
187 // if ((error = [task primaryDelete]) != nil) {
188 // [self logWithFormat:@"Note: failed to delete in folder: %@", folder];
189 // // TODO: make compound
190 // allErrors = error;
196 /* "iCal multifolder saves" */
198 - (NSException *) saveContentString: (NSString *) _iCal
199 baseSequence: (int) _v
202 Note: we need to delete in all participants folders and send iMIP messages
203 for all external accounts.
206 - fetch stored content
208 - check if sequence matches (or if 0=ignore)
209 - extract old attendee list + organizer (make unique)
210 - parse new content (ensure that sequence is increased!)
211 - extract new attendee list + organizer (make unique)
212 - make a diff => new, same, removed
214 - delete in removed folders
215 - send iMIP mail for all folders not found
217 // LDAPUserManager *um;
218 // iCalCalendar *calendar;
219 // iCalToDo *oldApt, *newApt;
220 // // iCalToDoChanges *changes;
221 // iCalPerson *organizer;
222 // NSString *oldContent, *uid;
223 // NSArray *uids, *props;
224 // NSMutableArray *attendees, *storeUIDs, *removedUIDs;
225 NSException *storeError, *delError;
226 // BOOL updateForcesReconsider;
228 // updateForcesReconsider = NO;
230 // if ([_iCal length] == 0) {
231 // return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
232 // reason:@"got no iCalendar content to store!"];
235 // um = [LDAPUserManager sharedUserManager];
237 // /* handle old content */
239 // oldContent = [self contentAsString]; /* if nil, this is a new task */
240 // if ([oldContent length] == 0)
243 // [self debugWithFormat:@"saving new task: %@", _iCal];
248 // calendar = [iCalCalendar parseSingleFromSource: oldContent];
249 // oldApt = [self firstTaskFromCalendar: calendar];
252 // /* compare sequence if requested */
259 // /* handle new content */
261 // calendar = [iCalCalendar parseSingleFromSource: _iCal];
262 // newApt = [self firstTaskFromCalendar: calendar];
263 // if (newApt == nil) {
264 // return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
265 // reason:@"could not parse iCalendar content!"];
270 // changes = [iCalToDoChanges changesFromEvent: oldApt
273 // uids = [um getUIDsForICalPersons:[changes deletedAttendees]
274 // applyStrictMapping:NO];
275 // removedUIDs = [NSMutableArray arrayWithArray:uids];
277 // uids = [um getUIDsForICalPersons:[newApt attendees]
278 // applyStrictMapping:NO];
279 // storeUIDs = [NSMutableArray arrayWithArray:uids];
280 // props = [changes updatedProperties];
282 // /* detect whether sequence has to be increased */
283 // if ([changes hasChanges])
284 // [newApt increaseSequence];
286 // /* preserve organizer */
288 // organizer = [newApt organizer];
289 // uid = [um getUIDForICalPerson:organizer];
291 // if (![storeUIDs containsObject:uid])
292 // [storeUIDs addObject:uid];
293 // [removedUIDs removeObject:uid];
296 // /* organizer might have changed completely */
298 // if (oldApt && ([props containsObject: @"organizer"])) {
299 // uid = [um getUIDForICalPerson:[oldApt organizer]];
301 // if (![storeUIDs containsObject:uid]) {
302 // if (![removedUIDs containsObject:uid]) {
303 // [removedUIDs addObject:uid];
309 // [self debugWithFormat:@"UID ops:\n store: %@\n remove: %@",
310 // storeUIDs, removedUIDs];
312 // /* if time did change, all participants have to re-decide ...
313 // * ... exception from that rule: the organizer
316 // if (oldApt != nil &&
317 // ([props containsObject:@"startDate"] ||
318 // [props containsObject:@"endDate"] ||
319 // [props containsObject:@"duration"]))
322 // unsigned i, count;
324 // ps = [newApt attendees];
325 // count = [ps count];
326 // for (i = 0; i < count; i++) {
329 // p = [ps objectAtIndex:i];
330 // if (![p hasSameEmailAddress:organizer])
331 // [p setParticipationStatus:iCalPersonPartStatNeedsAction];
333 // _iCal = [[newApt parent] versitString];
334 // updateForcesReconsider = YES;
337 // /* perform storing */
339 storeError = [self primarySaveContentString: _iCal];
341 // storeError = [self saveContentString:_iCal inUIDs:storeUIDs];
342 // delError = [self deleteInUIDs:removedUIDs];
344 // TODO: make compound
345 if (storeError != nil) return storeError;
346 // if (delError != nil) return delError;
348 /* email notifications */
349 // if ([self sendEMailNotifications])
351 // attendees = [NSMutableArray arrayWithArray:[changes insertedAttendees]];
352 // [attendees removePerson:organizer];
353 // [self sendInvitationEMailForTask:newApt
354 // toAttendees:attendees];
356 // if (updateForcesReconsider) {
357 // attendees = [NSMutableArray arrayWithArray:[newApt attendees]];
358 // [attendees removeObjectsInArray:[changes insertedAttendees]];
359 // [attendees removePerson:organizer];
360 // [self sendEMailUsingTemplateNamed: @"Update"
361 // forOldObject: oldApt
362 // andNewObject: newApt
363 // toAttendees: attendees];
366 // attendees = [NSMutableArray arrayWithArray:[changes deletedAttendees]];
367 // [attendees removePerson: organizer];
368 // if ([attendees count]) {
369 // iCalToDo *cancelledApt;
371 // cancelledApt = [newApt copy];
372 // [(iCalCalendar *) [cancelledApt parent] setMethod: @"cancel"];
373 // [self sendEMailUsingTemplateNamed: @"Removal"
375 // andNewObject: cancelledApt
376 // toAttendees: attendees];
377 // [cancelledApt release];
384 - (NSException *)deleteWithBaseSequence:(int)_v {
386 Note: We need to delete in all participants folders and send iMIP messages
387 for all external accounts.
388 Delete is basically identical to save with all attendees and the
389 organizer being deleted.
392 - fetch stored content
394 - check if sequence matches (or if 0=ignore)
395 - extract old attendee list + organizer (make unique)
396 - delete in removed folders
397 - send iMIP mail for all folders not found
400 // NSArray *removedUIDs;
401 // NSMutableArray *attendees;
403 [self primaryDelete];
406 // /* load existing content */
408 // task = (iCalToDo *) [self component: NO];
410 // /* compare sequence if requested */
416 // removedUIDs = [self attendeeUIDsFromTask:task];
418 // if ([self sendEMailNotifications])
420 // /* send notification email to attendees excluding organizer */
421 // attendees = [NSMutableArray arrayWithArray:[task attendees]];
422 // [attendees removePerson:[task organizer]];
424 // /* flag task as being cancelled */
425 // [(iCalCalendar *) [task parent] setMethod: @"cancel"];
426 // [task increaseSequence];
428 // /* remove all attendees to signal complete removal */
429 // [task removeAllAttendees];
431 // /* send notification email */
432 // [self sendEMailUsingTemplateNamed: @"Deletion"
434 // andNewObject: task
435 // toAttendees: attendees];
440 // return [self deleteInUIDs:removedUIDs];
443 - (NSException *)saveContentString:(NSString *)_iCalString {
444 return [self saveContentString:_iCalString baseSequence:0];
449 - (NSString *)outlookMessageClass {
453 @end /* SOGoTaskObject */