]> err.no Git - scalable-opengroupware.org/blobdiff - SoObjects/Appointments/SOGoCalendarComponent.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1146 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / SoObjects / Appointments / SOGoCalendarComponent.m
index ffae08c17d25a3cc3e553b3af687976166855819..808275d36565508e0246d6298e9a2af208f93464 100644 (file)
  */
 
 #import <Foundation/NSString.h>
-
+#import <Foundation/NSUserDefaults.h>
+
+#import <NGObjWeb/NSException+HTTP.h>
+#import <NGObjWeb/SoSecurityManager.h>
+#import <NGObjWeb/WOApplication.h>
+#import <NGObjWeb/WOContext+SoObjects.h>
+#import <NGExtensions/NSObject+Logs.h>
+#import <NGExtensions/NGHashMap.h>
 #import <NGCards/iCalCalendar.h>
 #import <NGCards/iCalPerson.h>
 #import <NGCards/iCalRepeatableEntityObject.h>
-#import <NGMime/NGMime.h>
-#import <NGMail/NGMail.h>
-#import <NGMail/NGSendMail.h>
-
-#import <SOGo/AgenorUserManager.h>
+#import <NGMime/NGMimeBodyPart.h>
+#import <NGMime/NGMimeMultipartBody.h>
+#import <NGMail/NGMimeMessage.h>
 
-#import "common.h"
+#import <SoObjects/SOGo/LDAPUserManager.h>
+#import <SoObjects/SOGo/NSCalendarDate+SOGo.h>
+#import <SoObjects/SOGo/SOGoMailer.h>
+#import <SoObjects/SOGo/SOGoPermissions.h>
+#import <SoObjects/SOGo/SOGoUser.h>
+#import <SoObjects/Appointments/SOGoAppointmentFolder.h>
 
 #import "SOGoAptMailNotification.h"
+#import "iCalEntityObject+SOGo.h"
 #import "SOGoCalendarComponent.h"
 
 static NSString *mailTemplateDefaultLanguage = nil;
@@ -54,7 +65,7 @@ static BOOL sendEMailNotifications = NO;
       mailTemplateDefaultLanguage = [[ud stringForKey:@"SOGoDefaultLanguage"]
                                       retain];
       if (!mailTemplateDefaultLanguage)
-        mailTemplateDefaultLanguage = @"French";
+        mailTemplateDefaultLanguage = @"English";
 
       sendEMailNotifications
         = [ud boolForKey: @"SOGoAppointmentSendEMailNotifications"];
@@ -66,6 +77,7 @@ static BOOL sendEMailNotifications = NO;
   if ((self = [super init]))
     {
       calendar = nil;
+      calContent = nil;
     }
 
   return self;
@@ -75,6 +87,8 @@ static BOOL sendEMailNotifications = NO;
 {
   if (calendar)
     [calendar release];
+  if (calContent)
+    [calContent release];
   [super dealloc];
 }
 
@@ -83,23 +97,129 @@ static BOOL sendEMailNotifications = NO;
   return @"text/calendar";
 }
 
-- (iCalCalendar *) calendar
+- (NSString *) componentTag
+{
+  [self subclassResponsibility: _cmd];
+
+  return nil;
+}
+
+- (void) _filterComponent: (iCalEntityObject *) component
+{
+  [component setSummary: @""];
+  [component setComment: @""];
+  [component setUserComment: @""];
+  [component setLocation: @""];
+  [component setCategories: @""];
+  [component setUrl: @""];
+  [component removeAllAttendees];
+  [component removeAllAlarms];
+}
+
+- (NSString *) contentAsString
+{
+  iCalCalendar *tmpCalendar;
+  iCalRepeatableEntityObject *tmpComponent;
+//   NSArray *roles;
+//   NSString *uid;
+  SoSecurityManager *sm;
+
+  if (!calContent)
+    {
+//       uid = [[context activeUser] login];
+//       roles = [self aclsForUser: uid];
+//       if ([roles containsObject: SOGoCalendarRole_Organizer]
+//       || [roles containsObject: SOGoCalendarRole_Participant]
+//       || [roles containsObject: SOGoCalendarRole_ComponentViewer])
+//     calContent = content;
+//       else if ([roles containsObject: SOGoCalendarRole_ComponentDAndTViewer])
+//     {
+//       tmpCalendar = [[self calendar: NO] copy];
+//       tmpComponent = (iCalRepeatableEntityObject *)
+//         [tmpCalendar firstChildWithTag: [self componentTag]];
+//       [self _filterComponent: tmpComponent];
+//       calContent = [tmpCalendar versitString];
+//       [tmpCalendar release];
+//     }
+//       else
+//     calContent = nil;
+
+      sm = [SoSecurityManager sharedSecurityManager];
+      if (![sm validatePermission: SOGoCalendarPerm_ViewAllComponent
+              onObject: self inContext: context])
+       calContent = content;
+      else if (![sm validatePermission: SOGoCalendarPerm_ViewDAndT
+                   onObject: self inContext: context])
+       {
+         tmpCalendar = [[self calendar: NO] copy];
+         tmpComponent = (iCalRepeatableEntityObject *)
+           [tmpCalendar firstChildWithTag: [self componentTag]];
+         [self _filterComponent: tmpComponent];
+         calContent = [tmpCalendar versitString];
+         [tmpCalendar release];
+       }
+      else
+       calContent = nil;
+
+      [calContent retain];
+    }
+
+  return calContent;
+}
+
+- (NSException *) saveContentString: (NSString *) contentString
+                        baseVersion: (unsigned int) baseVersion
+{
+  NSException *result;
+
+  result = [super saveContentString: contentString
+                  baseVersion: baseVersion];
+  if (!result && calContent)
+    {
+      [calContent release];
+      calContent = nil;
+    }
+
+  return result;
+}
+
+- (iCalCalendar *) calendar: (BOOL) create
 {
-  NSString *iCalString;
+  NSString *iCalString, *componentTag;
+  CardGroup *newComponent;
 
   if (!calendar)
     {
-      iCalString = [self contentAsString];
-      if (iCalString)
+      iCalString = [super contentAsString];
+      if ([iCalString length] > 0)
+        calendar = [iCalCalendar parseSingleFromSource: iCalString];
+      else
         {
-          calendar = [iCalCalendar parseSingleFromSource: iCalString];
-          [calendar retain];
+          if (create)
+            {
+              calendar = [iCalCalendar groupWithTag: @"vcalendar"];
+              [calendar setVersion: @"2.0"];
+              [calendar setProdID: @"-//Inverse groupe conseil//SOGo 0.9//EN"];
+              componentTag = [[self componentTag] uppercaseString];
+              newComponent = [[calendar classForTag: componentTag]
+                               groupWithTag: componentTag];
+              [calendar addChild: newComponent];
+            }
         }
+      if (calendar)
+        [calendar retain];
     }
 
   return calendar;
 }
 
+- (iCalRepeatableEntityObject *) component: (BOOL) create
+{
+  return
+    (iCalRepeatableEntityObject *) [[self calendar: create]
+                                    firstChildWithTag: [self componentTag]];
+}
+
 /* raw saving */
 
 - (NSException *) primarySaveContentString: (NSString *) _iCalString
@@ -129,20 +249,18 @@ static BOOL sendEMailNotifications = NO;
 {
   NSString *baseURL;
   NSString *uid;
-  WOContext *ctx;
   NSArray *traversalObjects;
 
   /* generate URL from traversal stack */
-  ctx = [[WOApplication application] context];
-  traversalObjects = [ctx objectTraversalStack];
+  traversalObjects = [context objectTraversalStack];
   if ([traversalObjects count] > 0)
-    baseURL = [[traversalObjects objectAtIndex:0] baseURLInContext:ctx];
+    baseURL = [[traversalObjects objectAtIndex:0] baseURLInContext: context];
   else
     {
       baseURL = @"http://localhost/";
       [self warnWithFormat:@"Unable to create baseURL from context!"];
     }
-  uid = [[AgenorUserManager sharedUserManager]
+  uid = [[LDAPUserManager sharedUserManager]
           getUIDForEmail: [_person rfc822Email]];
 
   return ((uid)
@@ -150,11 +268,66 @@ static BOOL sendEMailNotifications = NO;
           : nil);
 }
 
+- (NSException *) changeParticipationStatus: (NSString *) _status
+{
+  iCalRepeatableEntityObject *component;
+  iCalPerson *person;
+  NSString *newContent;
+  NSException *ex;
+  NSString *myEMail;
+  
+  ex = nil;
+
+  component = [self component: NO];
+  if (component)
+    {
+      myEMail = [[context activeUser] primaryEmail];
+      person = [self findParticipantWithUID: owner];
+      if (person)
+        {
+         // TODO: send iMIP reply mails?
+          [person setPartStat: _status];
+          newContent = [[component 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 "
+                          @"calendar component"];
+    }
+  else
+    ex = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
+                      reason: @"unable to parse component record"];
+
+  return ex;
+}
+
 - (BOOL) sendEMailNotifications
 {
   return sendEMailNotifications;
 }
 
+- (NSTimeZone *) timeZoneForUser: (NSString *) email
+{
+  NSString *uid;
+
+  uid = [[LDAPUserManager sharedUserManager] getUIDForEmail: email];
+
+  return [[SOGoUser userWithLogin: uid roles: nil] timeZone];
+}
+
 - (void) sendEMailUsingTemplateNamed: (NSString *) _pageName
                         forOldObject: (iCalRepeatableEntityObject *) _oldObject
                         andNewObject: (iCalRepeatableEntityObject *) _newObject
@@ -162,14 +335,13 @@ static BOOL sendEMailNotifications = NO;
 {
   NSString *pageName;
   iCalPerson *organizer;
-  NSString *cn, *sender, *iCalString;
-  NGSendMail *sendmail;
+  NSString *cn, *email, *sender, *iCalString;
   WOApplication *app;
   unsigned i, count;
   iCalPerson *attendee;
   NSString *recipient;
   SOGoAptMailNotification *p;
-  NSString *subject, *text, *header;
+  NSString *mailDate, *subject, *text, *header;
   NGMutableHashMap *headerMap;
   NGMimeMessage *msg;
   NGMimeBodyPart *bodyPart;
@@ -191,9 +363,6 @@ static BOOL sendEMailNotifications = NO;
       /* generate iCalString once */
       iCalString = [[_newObject parent] versitString];
   
-      /* get sendmail object */
-      sendmail = [NGSendMail sharedSendMail];
-
       /* get WOApplication instance */
       app = [WOApplication application];
 
@@ -206,30 +375,32 @@ static BOOL sendEMailNotifications = NO;
 
           /* construct recipient */
           cn = [attendee cn];
+         email = [attendee rfc822Email];
           if (cn)
             recipient = [NSString stringWithFormat: @"%@ <%@>",
-                                  cn,
-                                  [attendee rfc822Email]];
+                                  cn, email];
           else
-            recipient = [attendee rfc822Email];
+            recipient = email;
 
+#warning this could be optimized in a class hierarchy common with the \
+          SOGoObject's acl notification mechanism
           /* create page name */
           // TODO: select user's default language?
           pageName = [NSString stringWithFormat: @"SOGoAptMail%@%@",
                                mailTemplateDefaultLanguage,
                                _pageName];
           /* construct message content */
-          p = [app pageWithName: pageName inContext: [WOContext context]];
+          p = [app pageWithName: pageName inContext: context];
           [p setNewApt: _newObject];
           [p setOldApt: _oldObject];
           [p setHomePageURL: [self homePageURLForPerson: attendee]];
-          [p setViewTZ: [self userTimeZone: cn]];
+          [p setViewTZ: [self timeZoneForUser: email]];
           subject = [p getSubject];
           text = [p getBody];
 
           /* construct message */
           headerMap = [NGMutableHashMap hashMapWithCapacity: 5];
-    
+          
           /* NOTE: multipart/alternative seems like the correct choice but
            * unfortunately Thunderbird doesn't offer the rich content alternative
            * at all. Mail.app shows the rich content alternative _only_
@@ -238,7 +409,8 @@ static BOOL sendEMailNotifications = NO;
           [headerMap setObject: @"multipart/mixed" forKey: @"content-type"];
           [headerMap setObject: sender forKey: @"From"];
           [headerMap setObject: recipient forKey: @"To"];
-          [headerMap setObject: [NSCalendarDate date] forKey: @"date"];
+         mailDate = [[NSCalendarDate date] rfc822DateString];
+          [headerMap setObject: mailDate forKey: @"date"];
           [headerMap setObject: subject forKey: @"Subject"];
           msg = [NGMimeMessage messageWithHeader: headerMap];
 
@@ -272,11 +444,190 @@ static BOOL sendEMailNotifications = NO;
           [body release];
 
           /* send the damn thing */
-          [sendmail sendMimePart: msg
-                    toRecipients: [NSArray arrayWithObject: [attendee rfc822Email]]
-                    sender: [organizer rfc822Email]];
+          [[SOGoMailer sharedMailer]
+           sendMimePart: msg
+           toRecipients: [NSArray arrayWithObject: email]
+           sender: [organizer rfc822Email]];
         }
     }
 }
 
+// - (BOOL) isOrganizerOrOwner: (SOGoUser *) user
+// {
+//   BOOL isOrganizerOrOwner;
+//   iCalRepeatableEntityObject *component;
+//   NSString *organizerEmail;
+
+//   component = [self component: NO];
+//   organizerEmail = [[component organizer] rfc822Email];
+//   if (component && [organizerEmail length] > 0)
+//     isOrganizerOrOwner = [user hasEmail: organizerEmail];
+//   else
+//     isOrganizerOrOwner
+//       = [[container ownerInContext: context] isEqualToString: [user login]];
+
+//   return isOrganizerOrOwner;
+// }
+
+- (iCalPerson *) findParticipantWithUID: (NSString *) uid
+{
+  SOGoUser *user;
+
+  user = [SOGoUser userWithLogin: uid roles: nil];
+
+  return [self findParticipant: user];
+}
+
+- (iCalPerson *) findParticipant: (SOGoUser *) user
+{
+  iCalPerson *participant, *currentParticipant;
+  iCalEntityObject *component;
+  NSEnumerator *participants;
+
+  participant = nil;
+  component = [self component: NO];
+  if (component)
+    {
+      participants = [[component participants] objectEnumerator];
+      currentParticipant = [participants nextObject];
+      while (currentParticipant && !participant)
+       if ([user hasEmail: [currentParticipant rfc822Email]])
+         participant = currentParticipant;
+       else
+         currentParticipant = [participants nextObject];
+    }
+
+  return participant;
+}
+
+- (iCalPerson *) iCalPersonWithUID: (NSString *) uid
+{
+  iCalPerson *person;
+  LDAPUserManager *um;
+  NSDictionary *contactInfos;
+
+  um = [LDAPUserManager sharedUserManager];
+  contactInfos = [um contactInfosForUserWithUIDorEmail: uid];
+
+  person = [iCalPerson new];
+  [person autorelease];
+  [person setCn: [contactInfos objectForKey: @"cn"]];
+  [person setEmail: [contactInfos objectForKey: @"c_email"]];
+
+  return person;
+}
+
+- (NSString *) getUIDForICalPerson: (iCalPerson *) person
+{
+  LDAPUserManager *um;
+
+  um = [LDAPUserManager sharedUserManager];
+
+  return [um getUIDForEmail: [person rfc822Email]];
+}
+
+- (NSArray *) getUIDsForICalPersons: (NSArray *) iCalPersons
+{
+  iCalPerson *currentPerson;
+  NSEnumerator *persons;
+  NSMutableArray *uids;
+  NSString *uid;
+  LDAPUserManager *um;
+
+  uids = [NSMutableArray array];
+
+  um = [LDAPUserManager sharedUserManager];
+  persons = [iCalPersons objectEnumerator];
+  currentPerson = [persons nextObject];
+  while (currentPerson)
+    {
+      uid = [um getUIDForEmail: [currentPerson rfc822Email]];
+      if (uid)
+       [uids addObject: uid];
+      currentPerson = [persons nextObject];
+    }
+
+  return uids;
+}
+
+- (NSString *) _roleOfOwner: (iCalRepeatableEntityObject *) component
+{
+  NSString *role;
+  iCalPerson *organizer;
+  SOGoUser *ownerUser;
+
+  if (component)
+    {
+      organizer = [component organizer];
+      if ([[organizer rfc822Email] length] > 0)
+       {
+         ownerUser = [SOGoUser userWithLogin: owner roles: nil];
+         if ([component userIsOrganizer: ownerUser])
+           role = SOGoCalendarRole_Organizer;
+         else if ([component userIsParticipant: ownerUser])
+           role = SOGoCalendarRole_Participant;
+         else
+           role = SOGoRole_None;
+       }
+      else
+       role = SOGoCalendarRole_Organizer;
+    }
+  else
+    role = SOGoCalendarRole_Organizer;
+  
+  return role;
+}
+
+- (NSString *) _compiledRoleForOwner: (NSString *) ownerRole
+                            andUser: (NSString *) userRole
+{
+  NSString *role;
+
+  if ([userRole isEqualToString: SOGoCalendarRole_ComponentModifier]
+      || ([userRole isEqualToString: SOGoCalendarRole_ComponentResponder]
+         && [ownerRole isEqualToString: SOGoCalendarRole_Participant]))
+    role = ownerRole;
+  else
+    role = SOGoRole_None;
+
+  return role;
+}
+
+- (NSArray *) aclsForUser: (NSString *) uid
+{
+  NSMutableArray *roles;
+  NSArray *superAcls;
+  iCalRepeatableEntityObject *component;
+  NSString *accessRole, *ownerRole;
+
+  roles = [NSMutableArray array];
+  superAcls = [super aclsForUser: uid];
+  if ([superAcls count] > 0)
+    [roles addObjectsFromArray: superAcls];
+
+  component = [self component: NO];
+  ownerRole = [self _roleOfOwner: component];
+  if ([owner isEqualToString: uid])
+    [roles addObject: ownerRole];
+  else
+    {
+      if (component)
+       {
+         accessRole = [container roleForComponentsWithAccessClass:
+                                   [component symbolicAccessClass]
+                                 forUser: uid];
+         if ([accessRole length] > 0)
+           {
+             [roles addObject: accessRole];
+             [roles addObject: [self _compiledRoleForOwner: ownerRole
+                                     andUser: accessRole]];
+           }
+       }
+      else if ([roles containsObject: SOGoRole_ObjectCreator])
+       [roles addObject: SOGoCalendarRole_Organizer];
+    }
+
+  return roles;
+}
+
 @end