]> err.no Git - scalable-opengroupware.org/commitdiff
fixed free/busy calculation, added freebusy.ifb object for access via DAV (helpful...
authorznek <znek@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Wed, 6 Jul 2005 20:56:09 +0000 (20:56 +0000)
committerznek <znek@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Wed, 6 Jul 2005 20:56:09 +0000 (20:56 +0000)
git-svn-id: http://svn.opengroupware.org/SOGo/trunk@675 d1b88da0-ebda-0310-925b-ed51d893ca5b

18 files changed:
SOGo/SoObjects/Appointments/ChangeLog
SOGo/SoObjects/Appointments/GNUmakefile
SOGo/SoObjects/Appointments/SOGoAppointmentFolder.h
SOGo/SoObjects/Appointments/SOGoAppointmentFolder.m
SOGo/SoObjects/Appointments/SOGoFreeBusyObject.h [new file with mode: 0644]
SOGo/SoObjects/Appointments/SOGoFreeBusyObject.m [new file with mode: 0644]
SOGo/SoObjects/Appointments/Version
SOGo/SoObjects/Appointments/product.plist
SOGo/SoObjects/SOGo/AgenorUserManager.h
SOGo/SoObjects/SOGo/AgenorUserManager.m
SOGo/SoObjects/SOGo/ChangeLog
SOGo/SoObjects/SOGo/SOGoAppointmentICalRenderer.m
SOGo/SoObjects/SOGo/SOGoUserFolder.h
SOGo/SoObjects/SOGo/SOGoUserFolder.m
SOGo/SoObjects/SOGo/Version
SOGo/UI/Scheduler/ChangeLog
SOGo/UI/Scheduler/UIxAppointmentProposal.m
SOGo/UI/Scheduler/Version

index 4e8bb904d10048d416b316326cb58bbe82fe4663..81ccb122b4305a28b918c89dd5766cea4b20b8e4 100644 (file)
@@ -1,3 +1,21 @@
+2005-07-06  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v0.9.35
+
+       * GNUMakefile: added SOGoFreeBusyObject.m
+
+       * product.plist: added SOGoFreeBusyObject
+
+       * SOGoFreeBusyObject.[hm]: new class resembling free/busy information
+         for a particular user. Because free/busy information isn't solely
+         based upon appointments, this is now a part of the user folder.
+         However the information contained therein is still based on
+         information provided by appointments, only. This should be fixed in
+         a later release.
+
+       * SOGoAppointmentFolder.m: new API to lookup freeBusyObjects for an
+         array of uids.
+
 2005-07-05  Marcus Mueller  <znek@mulle-kybernetik.com>
 
        * SOGoAppointmentFolder.m: fetch new priority field in core infos
index c4709e5dd2c14583404b167df70689884351e5dc..0cd04c8e80b2889bdbb2e3a37e76a01a8690b0ff 100644 (file)
@@ -12,6 +12,7 @@ Appointments_OBJC_FILES = \
        SOGoAppointmentObject.m         \
        SOGoAppointmentFolder.m         \
        SOGoGroupAppointmentFolder.m    \
+       SOGoFreeBusyObject.m            \
 
 Appointments_RESOURCE_FILES += \
        Version                 \
index 7019e76164e9b5554361f7501640a710fb5b3d53..9214e38a8be97fe5e234498098a0f3e6d9b68b29 100644 (file)
@@ -76,6 +76,7 @@
 - (id)lookupHomeFolderForUID:(NSString *)_uid inContext:(id)_ctx;
 
 - (NSArray *)lookupCalendarFoldersForUIDs:(NSArray *)_uids inContext:(id)_ctx;
+- (NSArray *)lookupFreeBusyObjectsForUIDs:(NSArray *)_uids inContext:(id)_ctx;
 
 - (NSArray *)uidsFromICalPersons:(NSArray *)_persons;
 - (NSArray *)lookupCalendarFoldersForICalPerson:(NSArray *)_persons
index d0da865f9c1eadd1bc0c4e4afd774c265fe41955..ebd259665486dca687d8636ebd8d746438ed230c 100644 (file)
@@ -382,7 +382,7 @@ static NSTimeZone *MET    = nil;
 {
   static NSArray *infos = nil; // TODO: move to a plist file
   if (infos == nil) {
-    infos = [[NSArray alloc] init];
+    infos = [[NSArray alloc] initWithObjects:@"partmails", @"partstates", nil];
   }
   return [self fetchFields:infos from:_startDate to:_endDate];
 }
@@ -500,6 +500,33 @@ static NSTimeZone *MET    = nil;
   return folders;
 }
 
+- (NSArray *)lookupFreeBusyObjectsForUIDs:(NSArray *)_uids inContext:(id)_ctx {
+  /* Note: can return NSNull objects in the array! */
+  NSMutableArray *objs;
+  NSEnumerator   *e;
+  NSString       *uid;
+  
+  if ([_uids count] == 0) return nil;
+  objs = [NSMutableArray arrayWithCapacity:16];
+  e    = [_uids objectEnumerator];
+  while ((uid = [e nextObject])) {
+    id obj;
+    
+    obj = [self lookupHomeFolderForUID:uid inContext:nil];
+    if ([obj isNotNull]) {
+      obj = [obj lookupName:@"freebusy.ifb" inContext:nil acquire:NO];
+      if ([obj isKindOfClass:[NSException class]])
+       obj = nil;
+    }
+    if (![obj isNotNull])
+      [self logWithFormat:@"Note: did not find freebusy.ifb for uid: '%@'", uid];
+    
+    /* Note: intentionally add 'null' folders to allow a mapping */
+    [objs addObject:obj ? obj : [NSNull null]];
+  }
+  return objs;
+}
+
 - (NSArray *)uidsFromICalPersons:(NSArray *)_persons {
   /* Note: can return NSNull objects in the array! */
   NSMutableArray    *uids;
diff --git a/SOGo/SoObjects/Appointments/SOGoFreeBusyObject.h b/SOGo/SoObjects/Appointments/SOGoFreeBusyObject.h
new file mode 100644 (file)
index 0000000..16994a0
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+  Copyright (C) 2000-2004 SKYRIX Software AG
+
+  This file is part of OGo
+
+  OGo is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with OGo; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+// $Id$
+
+
+#ifndef        __Appointments_SOGoFreeBusyObject_H_
+#define        __Appointments_SOGoFreeBusyObject_H_
+
+#include <SOGo/SOGoContentObject.h>
+
+/*
+ SOGoFreeBusyObject
+ Represents Free/Busy information for a single user as specified in RFC2445.
+*/
+
+@class NSArray, NSCalendarDate;
+
+@interface SOGoFreeBusyObject : SOGoContentObject
+{
+}
+
+/* accessors */
+
+- (NSString *)iCalString;
+
+- (NSString *)contentAsStringFrom:(NSCalendarDate *)_startDate
+  to:(NSCalendarDate *)_endDate;
+
+- (NSArray *)fetchFreebusyInfosFrom:(NSCalendarDate *)_startDate
+  to:(NSCalendarDate *)_endDate;
+
+@end
+
+#endif /* __Appointments_SOGoFreeBusyObject_H_ */
diff --git a/SOGo/SoObjects/Appointments/SOGoFreeBusyObject.m b/SOGo/SoObjects/Appointments/SOGoFreeBusyObject.m
new file mode 100644 (file)
index 0000000..d3021fd
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+  Copyright (C) 2000-2004 SKYRIX Software AG
+
+  This file is part of OGo
+
+  OGo is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with OGo; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+// $Id$
+
+#include "SOGoFreeBusyObject.h"
+#include "common.h"
+#include <SOGo/AgenorUserManager.h>
+#include <NGiCal/NGiCal.h>
+#include <NGiCal/iCalRenderer.h>
+
+@interface NSDate(UsedPrivates)
+- (NSString *)icalString; // declared in NGiCal
+@end
+
+@interface SOGoFreeBusyObject (PrivateAPI)
+- (NSString *)iCalStringForFreeBusyInfos:(NSArray *)_infos
+  from:(NSCalendarDate *)_startDate
+  to:(NSCalendarDate *)_endDate;
+@end
+
+@implementation SOGoFreeBusyObject
+
+- (NSString *)iCalString {
+  // for UI-X appointment viewer
+  return [self contentAsString];
+}
+
+- (NSString *)contentAsString {
+  NSCalendarDate *startDate, *endDate;
+  
+  startDate = [[[NSCalendarDate calendarDate] mondayOfWeek] beginOfDay];
+  endDate   = [startDate dateByAddingYears:0
+                                    months:0
+                                      days:7
+                                     hours:23
+                                   minutes:59
+                                   seconds:59];
+  return [self contentAsStringFrom:startDate to:endDate];
+}
+
+- (NSString *)contentAsStringFrom:(NSCalendarDate *)_startDate
+  to:(NSCalendarDate *)_endDate
+{
+  NSArray *infos;
+  
+  infos = [self fetchFreebusyInfosFrom:_startDate to:_endDate];
+  return [self iCalStringForFreeBusyInfos:infos from:_startDate to:_endDate];
+}
+
+- (NSArray *)fetchFreebusyInfosFrom:(NSCalendarDate *)_startDate
+  to:(NSCalendarDate *)_endDate
+{
+  id                userFolder, calFolder;
+  NSArray           *infos;
+  AgenorUserManager *um;
+  NSString          *email;
+  NSMutableArray    *filtered;
+  unsigned          i, count;
+
+  userFolder = [self container];
+  calFolder  = [userFolder lookupName:@"Calendar" inContext:nil acquire:NO];
+  infos      = [calFolder fetchFreebusyInfosFrom:_startDate to:_endDate];
+  um         = [AgenorUserManager sharedUserManager];
+  email      = [um getEmailForUID:[userFolder login]];
+  count      = [infos count];
+  filtered   = [[[NSMutableArray alloc] initWithCapacity:count] autorelease];
+
+  for (i = 0; i < count; i++) {
+    NSDictionary *info;
+    NSArray      *partmails;
+    unsigned     p, pCount;
+
+    info      = [infos objectAtIndex:i];
+    partmails = [[info objectForKey:@"partmails"]
+                       componentsSeparatedByString:@"\n"];
+    pCount    = [partmails count];
+    for (p = 0; p < pCount; p++) {
+      NSString *pEmail;
+      
+      pEmail = [partmails objectAtIndex:p];
+      if ([pEmail isEqualToString:email]) {
+        NSArray  *partstates;
+        NSString *state;
+
+        partstates = [[info objectForKey:@"partstates"]
+                            componentsSeparatedByString:@"\n"];
+        state      = [partstates objectAtIndex:p];
+        if ([state intValue] == iCalPersonPartStatAccepted) {
+          // TODO: add tentative apts as well, but put state and email
+          //       into info
+          [filtered addObject:info];
+        }
+        break;
+      }
+    }
+  }
+  return filtered;
+}
+
+/* Private API */
+
+- (NSString *)iCalStringForFreeBusyInfos:(NSArray *)_infos
+  from:(NSCalendarDate *)_startDate
+  to:(NSCalendarDate *)_endDate
+{
+  AgenorUserManager *um;
+  NSMutableString   *ms;
+  NSString          *uid, *x;
+  unsigned          i, count;
+
+  um  = [AgenorUserManager sharedUserManager];
+  uid = [[self container] login];
+  ms  = [[[NSMutableString alloc] initWithCapacity:128] autorelease];
+
+  /* preamble */
+  [ms appendString:@"BEGIN:VCALENDAR\r\n"];
+  [ms appendString:@"BEGIN:VFREEBUSY\r\n"];
+  /* PRODID */
+  [ms appendString:@"PRODID:SOGo/0.9\r\n"];
+  /* VERSION */
+  [ms appendString:@"VERSION:2.0\r\n"];
+  /* DTSTAMP */
+  [ms appendString:@"DTSTAMP:"];
+  [ms appendString:[[NSCalendarDate calendarDate] icalString]];
+  [ms appendString:@"\r\n"];
+  
+  /* ORGANIZER - strictly required but missing for now */
+
+  /* ATTENDEE */
+  [ms appendString:@"ATTENDEE"];
+  if ((x = [um getCNForUID:uid])) {
+    [ms appendString:@";CN=\""];
+    [ms appendString:[x iCalDQUOTESafeString]];
+    [ms appendString:@"\""];
+  }
+  if ((x = [um getEmailForUID:uid])) {
+    [ms appendString:@":"]; /* sic! */
+    [ms appendString:[x iCalSafeString]];
+  }
+  [ms appendString:@"\r\n"];
+
+  /* DTSTART */
+  [ms appendString:@"DTSTART:"];
+  [ms appendString:[_startDate icalString]];
+  [ms appendString:@"\r\n"];
+
+  /* DTEND */
+  [ms appendString:@"DTEND:"];
+  [ms appendString:[_endDate icalString]];
+  [ms appendString:@"\r\n"];
+
+  /* FREEBUSY */
+  count = [_infos count];
+  for (i = 0; i < count; i++) {
+    NSDictionary   *info;
+    NSCalendarDate *startDate, *endDate;
+
+    info      = [_infos objectAtIndex:i];
+    startDate = [info objectForKey:@"startDate"];
+    endDate   = [info objectForKey:@"endDate"];
+
+    /* NOTE: currently we cannot differentiate between all the types defined
+     *       in RFC2445, Section 4.2.9.
+     *       These are: FREE" / "BUSY" / "BUSY-UNAVAILABLE" / "BUSY-TENTATIVE"
+     */
+    
+    [ms appendString:@"FREEBUSY;FBTYPE=BUSY:"];
+    [ms appendString:[startDate icalString]];
+    [ms appendString:@"/"];
+    [ms appendString:[endDate icalString]];
+    [ms appendString:@"\r\n"];
+  }
+
+  /* postamble */
+  [ms appendString:@"END:VFREEBUSY\r\n"];
+  [ms appendString:@"END:VCALENDAR\r\n"];
+  return ms;
+}
+
+/* deliver content without need for view method */
+
+- (id)GETAction:(id)_ctx {
+  WOResponse *r;
+  NSData     *contentData;
+
+  contentData = [[self contentAsString] dataUsingEncoding:NSUTF8StringEncoding];
+
+  r = [(WOContext *)_ctx response];
+  [r setHeader:@"text/calendar" forKey:@"content-type"];
+  [r setContent:contentData];
+  [r setStatus:200];
+  return r;
+}
+
+@end
index 9b34cb773990a2d88acd31534fb04e9bbf189b3d..ffe201b88d57ecd693b8ded3795e26036c73444a 100644 (file)
@@ -1,6 +1,6 @@
 # Version file
 
-SUBMINOR_VERSION:=34
+SUBMINOR_VERSION:=35
 
 # v0.9.32 requires libGDLContentStore v4.5.26
 # v0.9.28 requires libNGiCal          v4.5.47
index 781de9bca3ee4628caca938f4cbbf513323ebfd8..8692cfd8d9455c1ca20b3145c1f62a76d8e5972b 100644 (file)
@@ -18,5 +18,9 @@
     SOGoAppointmentObject = {
       superclass    = "SOGoContentObject";
     };
+
+    SOGoFreeBusyObject = {
+      superclass    = "SOGoContentObject";
+    };
   };
 }
index 363c30fa09e840bb629044d1bf19ed8210fc3a3f..5f59108290394159be8d2092d53aba2439154592 100644 (file)
@@ -46,6 +46,8 @@
 /* i.e. amelie-ida01.melanie2.i2 */
 - (NSString *)getServerForUID:(NSString *)_uid;
 
+- (NSURL *)getFreeBusyURLForUID:(NSString *)_uid;
+
 @end
 
 #endif /* __AgenorUserManager_H_ */
index 7702a9d22444f9369e7d8e7573668bb7c74e0c3d..c73b6935bf7794b2758e103eb62e875c17e788be 100644 (file)
@@ -444,41 +444,41 @@ static NSString *defaultMailDomain   = @"equipement.gouv.fr";
 - (NSArray *)_serverCandidatesForMineqMelRoutage:(NGLdapAttribute *)attr {
   NSMutableArray *serverCandidates;
 
-      unsigned i, count;
-      
-      count = [attr count];
-      serverCandidates = [NSMutableArray arrayWithCapacity:count];
-      for(i = 0; i < count; i++) {
-        NSRange r;
-        NSString *route;
-
-        route = [attr stringValueAtIndex:i];
-        r = [route rangeOfString:@".melanie2.i2" options:NSBackwardsSearch];
+  unsigned i, count;
+
+  count = [attr count];
+  serverCandidates = [NSMutableArray arrayWithCapacity:count];
+  for(i = 0; i < count; i++) {
+    NSRange r;
+    NSString *route;
+
+    route = [attr stringValueAtIndex:i];
+    r = [route rangeOfString:@".melanie2.i2" options:NSBackwardsSearch];
+    if(r.length > 0) {
+      unsigned length;
+
+      /* be clever */
+      length = [route length];
+      r = NSMakeRange(0, length - r.length);
+      r = [route rangeOfString:@"@" options:NSBackwardsSearch range:r];
+      if(r.length > 0) {
+        unsigned start;
+        NSRange serverNameRange;
+        
+        start = NSMaxRange(r);
+        serverNameRange = NSMakeRange(start, length - start);
+        r = NSMakeRange(0, length - start);
+        r = [route rangeOfString:@"%" options:NSBackwardsSearch range:r];
         if(r.length > 0) {
-          unsigned length;
-
-          /* be clever */
-          length = [route length];
-          r = NSMakeRange(0, length - r.length);
-          r = [route rangeOfString:@"@" options:NSBackwardsSearch range:r];
-          if(r.length > 0) {
-            unsigned start;
-            NSRange serverNameRange;
-            
-            start = NSMaxRange(r);
-            serverNameRange = NSMakeRange(start, length - start);
-            r = NSMakeRange(0, length - start);
-            r = [route rangeOfString:@"%" options:NSBackwardsSearch range:r];
-            if(r.length > 0) {
-              NSString *serverName;
-              
-              serverName = [route substringWithRange:serverNameRange];
-              [serverCandidates addObject:serverName];
-            }
-          }
+          NSString *serverName;
+          
+          serverName = [route substringWithRange:serverNameRange];
+          [serverCandidates addObject:serverName];
         }
       }
-      return serverCandidates;
+    }
+  }
+  return serverCandidates;
 }
 
 - (NSString *)primaryGetServerForAgenorUID:(NSString *)_uid {
@@ -569,6 +569,10 @@ static NSString *defaultMailDomain   = @"equipement.gouv.fr";
   return server;
 }
 
+- (NSURL *)getFreeBusyURLForUID:(NSString *)_uid {
+  return nil;
+}
+
 /* debugging */
 
 - (BOOL)isDebuggingEnabled {
index 1fe25bae050c743b101c1ebb1ea3f9c9c5ddbdcf..c89cf327c4fe4a0fbc3ada6cb23be5ba16c0d6c3 100644 (file)
@@ -1,3 +1,16 @@
+2005-07-06  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v0.9.37
+
+       * SOGoUserFolder.m: added 'freebusy.ifb' as an object to the
+         collection for proper display via DAV.
+
+       * SOGoAppointmentICalRenderer.m: fixed header inclusion
+
+       * AgenorUserManager.[hm]: added proposed future API for discovering
+         URLs for free/busy information (implementation currently returns
+         nil)
+
 2005-07-05  Marcus Mueller  <znek@mulle-kybernetik.com>
 
        * SOGoAppointment.m: fixed a wrong -release (v0.9.36)
index d0e53fdc5c8d7123e73a7c24eb6e6f442c238bd3..e377d05f0d3f2c7c1a3e0ff019db9e93b45bd41f 100644 (file)
@@ -21,8 +21,8 @@
 
 #include "SOGoAppointmentICalRenderer.h"
 #include "SOGoAppointment.h"
-#include "NSString+iCal.h"
 #include <NGiCal/NGiCal.h>
+#include <NGiCal/iCalRenderer.h>
 #include "common.h"
 
 // TODO: the basic renderer should be part of NGiCal
index dce2605a21f2e9e853d2b0a2751509e09b67dddf..c071b7c25378b231b6536e5c12e0d7f735bb3284 100644 (file)
@@ -55,6 +55,8 @@
 - (NSString *)ocsUserPath;
 - (NSString *)ocsPrivateCalendarPath;
 
+- (id)lookupFreeBusyObject;
+
 @end
 
 #endif /* __SOGo_SOGoUserFolder_H__ */
index 92ab65076928d0b49bf9fb59d819fb798527d1f8..4156e6b850027d3eee4d320cef624050ad7593ef 100644 (file)
   return [folder autorelease];
 }
 
+- (id)freeBusyObject:(NSString *)_key inContext:(id)_ctx {
+  static Class fbClass = Nil;
+  id fb;
+
+  if (fbClass == Nil)
+    fbClass = NSClassFromString(@"SOGoFreeBusyObject");
+  if (fbClass == Nil) {
+    [self errorWithFormat:@"missing SOGoFreeBusyObject class!"];
+    return nil;
+  }
+  
+  fb = [[fbClass alloc] initWithName:_key inContainer:self];
+  return [fb autorelease];
+}
+
 - (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag {
   id obj;
   
 
   if ([_key isEqualToString:@"Mail"])
     return [self mailAccountsFolder:_key inContext:_ctx];
-  
+
+  if ([_key isEqualToString:@"freebusy.ifb"])
+    return [self freeBusyObject:_key inContext:_ctx];
+
   /* return 404 to stop acquisition */
   return [NSException exceptionWithHTTPStatus:404 /* Not Found */];
 }
 /* WebDAV */
 
 - (NSArray *)fetchContentObjectNames {
-  /* the SOGoUserFolder has no 'files', only subfolders */
-  return nil;
+  static NSArray *cos = nil;
+  
+  if (!cos) {
+    cos = [[NSArray alloc] initWithObjects:@"freebusy.ifb", nil];
+  }
+  return cos;
 }
 
 - (BOOL)davIsCollection {
index d4e9aac06e2d984a59b62ac6b454f2bbe16cda46..e46a17bd648ca8ba0c2687d047e04b9869008839 100644 (file)
@@ -1,6 +1,6 @@
 # version file
 
-SUBMINOR_VERSION:=36
+SUBMINOR_VERSION:=37
 
 # v0.9.34 requires libGDLContentStore v4.5.26
 # v0.9.26 requires libOGoContentStore v0.9.13
index a67b00ac574ea33f894d0c66601c0fa8e37a4903..e1caccf94e103d9a41279ce252276cb1540ccc22 100644 (file)
@@ -1,3 +1,9 @@
+2005-07-06  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * UIxAppointmentProposal.m: changed implementation of
+         -proposalSearchAction to use the new SOGoFreeBusyObject API
+         (v0.9.124)
+
 2005-07-05  Marcus Mueller  <znek@mulle-kybernetik.com>
 
        * v0.9.123
index 9bcb00d8ddb51c1da311f644027557e05d024e47..c6be40abec776bb906fa345e862ce0b4d37194e7 100644 (file)
@@ -55,6 +55,7 @@
 @end
 
 #include <SoObjects/Appointments/SOGoAppointmentFolder.h>
+#include <SoObjects/Appointments/SOGoFreeBusyObject.h>
 #include <NGExtensions/NGCalendarDateRange.h>
 #include <NGiCal/NGiCal.h>
 #include "common.h"
 }
 
 - (id)proposalSearchAction {
-  NSArray               *attendees, *uids;
-  SOGoAppointmentFolder *groupCalendar;
-  NSArray *infos;
-  NSArray *ranges;
-  
+  NSArray        *attendees, *uids, *fbos, *ranges;
+  unsigned       i, count;
+  NSMutableArray *allInfos;
+
   [self logWithFormat:@"search from %@ to %@", 
          [self startDate], [self endDate]];
   
     [self logWithFormat:@"Note: no UIDs selected."];
     return self;
   }
-  
-  groupCalendar = [[self clientObject] lookupGroupCalendarFolderForUIDs:uids
-                                      inContext:[self context]];
-  [self debugWithFormat:@"group calendar: %@", groupCalendar];
-  
-  if (![groupCalendar respondsToSelector:@selector(fetchFreebusyInfosFrom:to:)]) {
-    return [NSException exceptionWithHTTPStatus:500 /* Internal Error */
-                       reason:@"invalid folder to run proposal query on!"];
-  }
-  
-  infos = [groupCalendar fetchFreebusyInfosFrom:[self startDate]
-                         to:[self endDate]];
-  [self debugWithFormat:@"  process: %d events", [infos count]];
 
-  ranges = [infos arrayByCreatingDateRangesFromObjectsWithStartDateKey:
-                       @"startDate"
-                     andEndDateKey:@"endDate"];
+  fbos     = [[self clientObject] lookupFreeBusyObjectsForUIDs:uids
+                                  inContext:[self context]];
+  count    = [fbos count];
+  // wild guess at capacity
+  allInfos = [[[NSMutableArray alloc] initWithCapacity:count * 10] autorelease];
+
+  for (i = 0; i < count; i++) {
+    SOGoFreeBusyObject *fb;
+    NSArray            *infos;
+    
+    fb    = [fbos objectAtIndex:i];
+    if (fb != (SOGoFreeBusyObject *)[NSNull null]) {
+      infos = [fb fetchFreebusyInfosFrom:[self startDate] to:[self endDate]];
+      [allInfos addObjectsFromArray:infos];
+    }
+  }
+  [self debugWithFormat:@"  processing: %d infos", [allInfos count]];
+  ranges = [allInfos arrayByCreatingDateRangesFromObjectsWithStartDateKey:
+                       @"startDate"
+                     andEndDateKey:@"endDate"];
   ranges = [ranges arrayByCompactingContainedDateRanges];
   [self debugWithFormat:@"  ranges: %@", ranges];
   
index 82292a3edf9d40c3ba29c2a78a2ba858164bda2b..8d3f6d07c8e2271a71602722b54388c092af42bc 100644 (file)
@@ -1,7 +1,8 @@
 # Version file
 
-SUBMINOR_VERSION:=123
+SUBMINOR_VERSION:=124
 
+# v0.9.123 requires Appointments v0.9.35
 # v0.9.123 requires SOGoUI       v0.9.24
 # v0.9.115 requires NGiCal       v4.5.44
 # v0.9.113 requires libSOGo      v0.9.30