]> err.no Git - scalable-opengroupware.org/blobdiff - SoObjects/Appointments/SOGoAppointmentObject.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1263 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / SoObjects / Appointments / SOGoAppointmentObject.m
index b2b8e1f97adf2f5f33ac97e96dd0535b04d929e5..12f97b38a0e13f33b7df263f62e9f49f95c719c8 100644 (file)
   02111-1307, USA.
 */
 
-#import "SOGoAppointmentObject.h"
+#import <Foundation/NSCalendarDate.h>
 
+#import <NGObjWeb/NSException+HTTP.h>
+#import <NGObjWeb/WOContext.h>
+#import <NGExtensions/NSNull+misc.h>
+#import <NGExtensions/NSObject+Logs.h>
 #import <NGCards/iCalCalendar.h>
 #import <NGCards/iCalEvent.h>
 #import <NGCards/iCalEventChanges.h>
 #import <NGCards/iCalPerson.h>
 
-#import <SOGo/AgenorUserManager.h>
-#import <SOGo/SOGoObject.h>
-#import <SOGo/SOGoPermissions.h>
-
-#import "iCalEntityObject+Agenor.h"
-
-#import "common.h"
+#import <SoObjects/SOGo/LDAPUserManager.h>
+#import <SoObjects/SOGo/NSArray+Utilities.h>
+#import <SoObjects/SOGo/SOGoObject.h>
+#import <SoObjects/SOGo/SOGoPermissions.h>
+#import <SoObjects/SOGo/WORequest+SOGo.h>
 
 #import "NSArray+Appointments.h"
+#import "SOGoAppointmentFolder.h"
+
+#import "SOGoAppointmentObject.h"
 
 @implementation SOGoAppointmentObject
 
@@ -46,7 +51,7 @@
 /* iCal handling */
 - (NSArray *) attendeeUIDsFromAppointment: (iCalEvent *) _apt
 {
-  AgenorUserManager *um;
+  LDAPUserManager *um;
   NSMutableArray *uids;
   NSArray *attendees;
   unsigned i, count;
   count = [attendees count];
   uids = [NSMutableArray arrayWithCapacity:count + 1];
   
-  um = [AgenorUserManager sharedUserManager];
+  um = [LDAPUserManager sharedUserManager];
   
   /* add organizer */
   
   email = [[_apt organizer] rfc822Email];
   if ([email isNotNull]) {
-    uid = [um getUIDForEmail:email];
+    uid = [um getUIDForEmail: email];
     if ([uid isNotNull]) {
       [uids addObject:uid];
     }
   return uids;
 }
 
-/* folder management */
-
-- (id)lookupHomeFolderForUID:(NSString *)_uid inContext:(id)_ctx {
-  // TODO: what does this do? lookup the home of the organizer?
-  return [[self container] lookupHomeFolderForUID:_uid inContext:_ctx];
-}
-- (NSArray *)lookupCalendarFoldersForUIDs:(NSArray *)_uids inContext:(id)_ctx {
-  return [[self container] lookupCalendarFoldersForUIDs:_uids inContext:_ctx];
-}
-
 /* store in all the other folders */
 
-- (NSException *)saveContentString:(NSString *)_iCal inUIDs:(NSArray *)_uids {
+- (NSException *) saveContentString: (NSString *) _iCal
+                            inUIDs: (NSArray *) _uids
+{
   NSEnumerator *e;
   id folder;
   NSException *allErrors = nil;
-  id ctx;
-
-  ctx = [[WOApplication application] context];
-  
-  e = [[self lookupCalendarFoldersForUIDs:_uids inContext:ctx]
-            objectEnumerator];
-  while ((folder = [e nextObject]) != nil) {
-    NSException           *error;
-    SOGoAppointmentObject *apt;
-    
-    if (![folder isNotNull]) /* no folder was found for given UID */
-      continue;
-
-    apt = [folder lookupName: [self nameInContainer] inContext:ctx
-                 acquire: NO];
-    if ([apt isKindOfClass: [NSException class]])
-      {
-        [self logWithFormat:@"Note: an exception occured finding '%@' in folder: %@",
-             [self nameInContainer], folder];
-        [self logWithFormat:@"the exception reason was: %@",
-              [(NSException *) apt reason]];
-        continue;
-      }
+  NSException           *error;
+  SOGoAppointmentObject *apt;
 
-    if (![apt isNotNull]) {
-      [self logWithFormat:@"Note: did not find '%@' in folder: %@",
-             [self nameInContainer], folder];
-      continue;
-    }
-    if ([apt isKindOfClass: [NSException class]]) {
-      [self logWithFormat:@"Exception: %@", [(NSException *) apt reason]];
-      continue;
-    }
-    
-    if ((error = [apt primarySaveContentString:_iCal]) != nil) {
-      [self logWithFormat:@"Note: failed to save iCal in folder: %@", folder];
-      // TODO: make compound
-      allErrors = error;
+  e = [[container lookupCalendarFoldersForUIDs:_uids inContext: context]
+       objectEnumerator];
+  while ((folder = [e nextObject]))
+    {
+      apt = [SOGoAppointmentObject objectWithName: nameInContainer
+                                  inContainer: folder];
+      error = [apt primarySaveContentString:_iCal];
+      if (error)
+       {
+         [self logWithFormat:@"Note: failed to save iCal in folder: %@", folder];
+         // TODO: make compound
+         allErrors = error;
+       }
     }
-  }
 
   return allErrors;
 }
 
-- (NSException *)deleteInUIDs:(NSArray *)_uids {
+- (NSException *) deleteInUIDs: (NSArray *) _uids
+{
   NSEnumerator *e;
   id folder;
   NSException *allErrors = nil;
-  id ctx;
+  NSException           *error;
+  SOGoAppointmentObject *apt;
   
-  ctx = [[WOApplication application] context];
-  
-  e = [[self lookupCalendarFoldersForUIDs:_uids inContext:ctx]
-            objectEnumerator];
-  while ((folder = [e nextObject])) {
-    NSException           *error;
-    SOGoAppointmentObject *apt;
-    
-    apt = [folder lookupName:[self nameInContainer] inContext:ctx
-                 acquire:NO];
-    if ([apt isKindOfClass: [NSException class]]) {
-      [self logWithFormat: @"%@", [(NSException *) apt reason]];
-      continue;
-    }
+  e = [[container lookupCalendarFoldersForUIDs:_uids inContext: context]
+       objectEnumerator];
+  while ((folder = [e nextObject]))
+    {
+      apt = [folder lookupName: [self nameInContainer]
+                   inContext: context
+                   acquire:NO];
+      if ([apt isKindOfClass: [NSException class]]) {
+       [self logWithFormat: @"%@", [(NSException *) apt reason]];
+       continue;
+      }
     
-    if ((error = [apt primaryDelete]) != nil) {
-      [self logWithFormat:@"Note: failed to delete in folder: %@", folder];
-      // TODO: make compound
-      allErrors = error;
+      if ((error = [apt primaryDelete]) != nil) {
+       [self logWithFormat:@"Note: failed to delete in folder: %@", folder];
+       // TODO: make compound
+       allErrors = error;
+      }
     }
-  }
+
   return allErrors;
 }
 
 /* "iCal multifolder saves" */
+- (BOOL) _aptIsStillRelevant: (iCalEvent *) appointment
+{
+  NSCalendarDate *now;
+
+  now = [NSCalendarDate calendarDate];
+
+  return ([[appointment endDate] earlierDate: now] == now);
+}
 
 - (NSException *) saveContentString: (NSString *) _iCal
                        baseSequence: (int) _v
      - delete in removed folders
      - send iMIP mail for all folders not found
   */
-  AgenorUserManager *um;
-  iCalCalendar *newCalendar;
+  LDAPUserManager *um;
   iCalEvent *oldApt, *newApt;
   iCalEventChanges *changes;
   iCalPerson *organizer;
   NSException *storeError, *delError;
   BOOL updateForcesReconsider;
   
-  updateForcesReconsider = NO;
-
-  if ([_iCal length] == 0) {
-    return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
-                       reason:@"got no iCalendar content to store!"];
-  }
+  if ([[context request] handledByDefaultHandler])
+    {
+      updateForcesReconsider = NO;
 
-  um = [AgenorUserManager sharedUserManager];
+      if ([_iCal length] == 0)
+       return [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
+                           reason: @"got no iCalendar content to store!"];
 
-  /* handle old content */
-  
-  oldContent = [self contentAsString]; /* if nil, this is a new appointment */
-  if ([oldContent length] == 0)
-    {
-    /* new appointment */
-      [self debugWithFormat:@"saving new appointment: %@", _iCal];
-      oldApt = nil;
-    }
-  else
-    oldApt = (iCalEvent *) [self component];
-  
-  /* compare sequence if requested */
+      um = [LDAPUserManager sharedUserManager];
 
-  if (_v != 0) {
-    // TODO
-  }
+      /* handle old content */
   
+      oldContent = [self contentAsString]; /* if nil, this is a new appointment */
+      if ([oldContent length] == 0)
+       {
+         /* new appointment */
+         [self debugWithFormat:@"saving new appointment: %@", _iCal];
+         oldApt = nil;
+       }
+      else
+       oldApt = (iCalEvent *) [self component: NO];
   
-  /* handle new content */
-  
-  newCalendar = [iCalCalendar parseSingleFromSource: _iCal];
-  newApt = (iCalEvent *) [newCalendar firstChildWithTag: [self componentTag]];
-  if (newApt == nil) {
-    return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
-                       reason:@"could not parse iCalendar content!"];
-  }
+      /* compare sequence if requested */
+      if (_v != 0) {
+       // TODO
+      }
   
-  /* diff */
+      /* handle new content */
   
-  changes = [iCalEventChanges changesFromEvent: oldApt
-                              toEvent: newApt];
-
-  uids = [um getUIDsForICalPersons:[changes deletedAttendees]
-                    applyStrictMapping:NO];
-  removedUIDs = [NSMutableArray arrayWithArray:uids];
-
-  uids = [um getUIDsForICalPersons:[newApt attendees]
-                    applyStrictMapping:NO];
-  storeUIDs = [NSMutableArray arrayWithArray:uids];
-  props = [changes updatedProperties];
-
-  /* detect whether sequence has to be increased */
-  if ([changes hasChanges])
-    [newApt increaseSequence];
-
-  /* preserve organizer */
-
-  organizer = [newApt organizer];
-  uid = [um getUIDForICalPerson:organizer];
-  if (uid) {
-    if (![storeUIDs containsObject:uid])
-      [storeUIDs addObject:uid];
-    [removedUIDs removeObject:uid];
-  }
-
-  /* organizer might have changed completely */
+      newApt = (iCalEvent *) [self component: NO];
+      if (!newApt)
+       return [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
+                           reason: @"could not parse iCalendar content!"];
 
-  if (oldApt && ([props containsObject: @"organizer"])) {
-    uid = [um getUIDForICalPerson:[oldApt organizer]];
-    if (uid) {
-      if (![storeUIDs containsObject:uid]) {
-        if (![removedUIDs containsObject:uid]) {
-          [removedUIDs addObject:uid];
-        }
-      }
-    }
-  }
-
-  [self debugWithFormat:@"UID ops:\n  store: %@\n  remove: %@",
-                        storeUIDs, removedUIDs];
-
-  /* if time did change, all participants have to re-decide ...
-   * ... exception from that rule: the organizer
-   */
-
-  if (oldApt != nil &&
-      ([props containsObject:@"startDate"] ||
-       [props containsObject:@"endDate"]   ||
-       [props containsObject:@"duration"]))
-  {
-    NSArray  *ps;
-    unsigned i, count;
+      /* diff */
+  
+      changes = [iCalEventChanges changesFromEvent: oldApt toEvent: newApt];
+      uids = [self getUIDsForICalPersons: [changes deletedAttendees]];
+      removedUIDs = [NSMutableArray arrayWithArray: uids];
+
+      uids = [self getUIDsForICalPersons: [newApt attendees]];
+      storeUIDs = [NSMutableArray arrayWithArray: uids];
+      props = [changes updatedProperties];
+
+      /* detect whether sequence has to be increased */
+      if ([changes hasChanges])
+       [newApt increaseSequence];
+
+      /* preserve organizer */
+
+      organizer = [newApt organizer];
+      uid = [self getUIDForICalPerson: organizer];
+      if (!uid)
+       uid = [self ownerInContext: nil];
+      if (uid)
+       {
+         [storeUIDs addObjectUniquely: uid];
+         [removedUIDs removeObject: uid];
+       }
+
+      /* organizer might have changed completely */
+
+      if (oldApt && ([props containsObject: @"organizer"]))
+       {
+         uid = [self getUIDForICalPerson: [oldApt organizer]];
+         if (uid && ![storeUIDs containsObject: uid])
+           [removedUIDs addObjectUniquely: uid];
+       }
+
+      [self debugWithFormat: @"UID ops:\n  store: %@\n  remove: %@",
+           storeUIDs, removedUIDs];
+
+      /* if time did change, all participants have to re-decide ...
+       * ... exception from that rule: the organizer
+       */
+
+      if (oldApt
+         && ([props containsObject: @"startDate"]
+             || [props containsObject: @"endDate"]
+             || [props containsObject: @"duration"]))
+       {
+         NSArray  *ps;
+         unsigned i, count;
     
-    ps    = [newApt attendees];
-    count = [ps count];
-    for (i = 0; i < count; i++) {
-      iCalPerson *p;
+         ps    = [newApt attendees];
+         count = [ps count];
+         for (i = 0; i < count; i++) {
+           iCalPerson *p;
       
-      p = [ps objectAtIndex:i];
-      if (![p hasSameEmailAddress:organizer])
-        [p setParticipationStatus:iCalPersonPartStatNeedsAction];
-    }
-    _iCal = [[newApt parent] versitString];
-    updateForcesReconsider = YES;
-  }
-
-  /* perform storing */
+           p = [ps objectAtIndex:i];
+           if (![p hasSameEmailAddress:organizer])
+             [p setParticipationStatus:iCalPersonPartStatNeedsAction];
+         }
+         _iCal = [[newApt parent] versitString];
+         updateForcesReconsider = YES;
+       }
 
-  storeError = [self saveContentString:_iCal inUIDs:storeUIDs];
-  delError = [self deleteInUIDs:removedUIDs];
+      /* perform storing */
 
-  // TODO: make compound
-  if (storeError != nil) return storeError;
-  if (delError   != nil) return delError;
-
-  /* email notifications */
-  if ([self sendEMailNotifications])
-    {
-      attendees = [NSMutableArray arrayWithArray: [changes insertedAttendees]];
-      [attendees removePerson: organizer];
-      [self sendEMailUsingTemplateNamed: @"Invitation"
-            forOldObject: nil
-            andNewObject: newApt
-            toAttendees: attendees];
-
-      if (updateForcesReconsider) {
-        attendees = [NSMutableArray arrayWithArray:[newApt attendees]];
-        [attendees removeObjectsInArray:[changes insertedAttendees]];
-        [attendees removePerson:organizer];
-        [self sendEMailUsingTemplateNamed: @"Update"
-              forOldObject: oldApt
-              andNewObject: newApt
-              toAttendees: attendees];
-      }
+      storeError = [self saveContentString: _iCal inUIDs: storeUIDs];
+      delError = [self deleteInUIDs: removedUIDs];
 
-      attendees = [NSMutableArray arrayWithArray:[changes deletedAttendees]];
-      [attendees removePerson: organizer];
-      if ([attendees count])
-        {
-          iCalEvent *canceledApt;
+      // TODO: make compound
+      if (storeError != nil) return storeError;
+      if (delError   != nil) return delError;
+
+      /* email notifications */
+      if ([self sendEMailNotifications]
+         && [self _aptIsStillRelevant: newApt])
+       {
+         iCalEvent *requestApt;
+
+         requestApt = [newApt copy];
+         [(iCalCalendar *) [requestApt parent] setMethod: @"request"];
+         attendees
+           = [NSMutableArray arrayWithArray: [changes insertedAttendees]];
+         [attendees removePerson: organizer];
+         [self sendEMailUsingTemplateNamed: @"Invitation"
+               forOldObject: nil
+               andNewObject: requestApt
+               toAttendees: attendees];
+         [requestApt release];
+
+         if (updateForcesReconsider)
+           {
+             iCalEvent *updatedApt;
+    
+             updatedApt = [newApt copy];
+             [(iCalCalendar *) [updatedApt parent] setMethod: @"request"];
+             attendees = [NSMutableArray arrayWithArray:[newApt attendees]];
+             [attendees removeObjectsInArray:[changes insertedAttendees]];
+             [attendees removePerson:organizer];
+             [self sendEMailUsingTemplateNamed: @"Update"
+                   forOldObject: oldApt
+                   andNewObject: updatedApt
+                   toAttendees: attendees];
+             [updatedApt release];
+           }
+
+         attendees
+           = [NSMutableArray arrayWithArray: [changes deletedAttendees]];
+         [attendees removePerson: organizer];
+         if ([attendees count])
+           {
+             iCalEvent *cancelledApt;
     
-          canceledApt = [newApt copy];
-          [(iCalCalendar *) [canceledApt parent] setMethod: @"cancel"];
-          [self sendEMailUsingTemplateNamed: @"Removal"
-                forOldObject: nil
-                andNewObject: canceledApt
-                toAttendees: attendees];
-          [canceledApt release];
-        }
+             cancelledApt = [newApt copy];
+             [(iCalCalendar *) [cancelledApt parent] setMethod: @"cancel"];
+             [self sendEMailUsingTemplateNamed: @"Removal"
+                   forOldObject: nil
+                   andNewObject: cancelledApt
+                   toAttendees: attendees];
+             [cancelledApt release];
+           }
+       }
     }
+  else
+    [self primarySaveContentString: _iCal];
 
   return nil;
 }
 
-- (NSException *)deleteWithBaseSequence:(int)_v {
+- (NSException *) deleteWithBaseSequence: (int)_v
+{
   /* 
      Note: We need to delete in all participants folders and send iMIP messages
            for all external accounts.
      - send iMIP mail for all folders not found
   */
   iCalEvent *apt;
-  NSArray *removedUIDs;
-  NSMutableArray *attendees;
+  NSMutableArray *attendees, *removedUIDs;
+  NSException *error;
 
+  if ([[context request] handledByDefaultHandler])
+    {
   /* load existing content */
-
-  apt = (iCalEvent *) [self component];
+      apt = (iCalEvent *) [self component: NO];
   
   /* compare sequence if requested */
 
 //     // TODO
 //   }
   
-  removedUIDs = [self attendeeUIDsFromAppointment:apt];
-
-  if ([self sendEMailNotifications])
-    {
-      /* send notification email to attendees excluding organizer */
-      attendees = [NSMutableArray arrayWithArray:[apt attendees]];
-      [attendees removePerson:[apt organizer]];
+      removedUIDs = [NSMutableArray arrayWithArray:
+                                     [self attendeeUIDsFromAppointment: apt]];
+      if (![removedUIDs containsObject: owner])
+       [removedUIDs addObject: owner];
+
+      if ([self sendEMailNotifications]
+         && [self _aptIsStillRelevant: apt])
+       {
+         /* send notification email to attendees excluding organizer */
+         attendees = [NSMutableArray arrayWithArray: [apt attendees]];
+         [attendees removePerson: [apt organizer]];
   
-      /* flag appointment as being canceled */
-      [(iCalCalendar *) [apt parent] setMethod: @"cancel"];
-      [apt increaseSequence];
-
-      /* remove all attendees to signal complete removal */
-      [apt removeAllAttendees];
-
-      /* send notification email */
-      [self sendEMailUsingTemplateNamed: @"Deletion"
-            forOldObject: nil
-            andNewObject: apt
-            toAttendees: attendees];
-    }
+         /* flag appointment as being cancelled */
+         [(iCalCalendar *) [apt parent] setMethod: @"cancel"];
+         [apt increaseSequence];
 
-  /* perform */
+         /* remove all attendees to signal complete removal */
+         [apt removeAllAttendees];
 
-  return [self deleteInUIDs:removedUIDs];
-}
+         /* send notification email */
+         [self sendEMailUsingTemplateNamed: @"Deletion"
+               forOldObject: nil
+               andNewObject: apt
+               toAttendees: attendees];
+       }
 
-- (NSException *) saveContentString: (NSString *) _iCalString
-{
-  return [self saveContentString: _iCalString baseSequence: 0];
-}
-
-- (NSException *) changeParticipationStatus: (NSString *) _status
-                                  inContext: (id) _ctx
-{
-  iCalEvent *apt;
-  iCalPerson *p;
-  NSString *newContent;
-  NSException *ex;
-  NSString *myEMail;
-  
-  ex = nil;
-
-  // TODO: do we need to use SOGoAppointment? (prefer iCalEvent?)
-  apt = (iCalEvent *) [self component];
-
-  if (apt)
-    {
-      myEMail = [[_ctx activeUser] email];
-      p = [apt findParticipantWithEmail: myEMail];
-      if (p)
-        {
-  // TODO: send iMIP reply mails?
-  
-          [p setPartStat:_status];
-          newContent = [[apt parent] versitString];
-          if (newContent)
-            {
-              ex = [self saveContentString:newContent];
-              if (ex)
-                // TODO: why is the exception wrapped?
-                /* Server Error */
-                ex = [NSException exceptionWithHTTPStatus: 500
-                                  reason: [ex reason]];
-            }
-          else
-            ex
-              = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
-                             reason: @"Could not generate iCalendar data ..."];
-        }
-      else
-        ex = [NSException exceptionWithHTTPStatus: 404 /* Not Found */
-                          reason: @"user does not participate in this "
-                          @"appointment"];
+      error = [self deleteInUIDs: removedUIDs];
     }
   else
-    ex = [NSException exceptionWithHTTPStatus:500 /* Server Error */
-                      reason:@"unable to parse appointment record"];
+    error = [self primaryDelete];
 
-  return ex;
+  return error;
 }
 
+- (NSException *) saveContentString: (NSString *) _iCalString
+{
+  return [self saveContentString: _iCalString baseSequence: 0];
+}
 
 /* message type */
 
   return @"IPM.Appointment";
 }
 
-- (NSException *) saveContentString: (NSString *) contentString
-                        baseVersion: (unsigned int) baseVersion
-{
-  NSString *newContentString, *oldContentString;
-  iCalCalendar *eventCalendar;
-  iCalEvent *event;
-  NSArray *organizers;
-
-  oldContentString = [self contentAsString];
-  if (oldContentString)
-    newContentString = contentString;
-  else
-    {
-      eventCalendar = [iCalCalendar parseSingleFromSource: contentString];
-      event = (iCalEvent *) [eventCalendar firstChildWithTag: [self componentTag]];
-      organizers = [event childrenWithTag: @"organizer"];
-      if ([organizers count])
-        newContentString = contentString;
-      else
-        {
-          [event setOrganizerWithUid: [[self container] ownerInContext: nil]];
-          newContentString = [eventCalendar versitString];
-        }
-    }
-
-  return [super saveContentString: newContentString
-                baseVersion: baseVersion];
-}
-
 @end /* SOGoAppointmentObject */
-
-
-