]> err.no Git - scalable-opengroupware.org/commitdiff
first working fix for SOGo Bugs #966/#967
authorznek <znek@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Tue, 15 Feb 2005 21:49:57 +0000 (21:49 +0000)
committerznek <znek@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Tue, 15 Feb 2005 21:49:57 +0000 (21:49 +0000)
git-svn-id: http://svn.opengroupware.org/SOGo/trunk@569 d1b88da0-ebda-0310-925b-ed51d893ca5b

20 files changed:
SOGo/SoObjects/Appointments/SOGoAppointmentFolder.m
SOGo/SoObjects/Appointments/SOGoAppointmentObject.h
SOGo/SoObjects/Appointments/SOGoAppointmentObject.m
SOGo/SoObjects/ChangeLog
SOGo/SoObjects/SOGo/GNUmakefile.preamble
SOGo/UI/Scheduler/ChangeLog
SOGo/UI/Scheduler/English.lproj/default.strings
SOGo/UI/Scheduler/GNUmakefile
SOGo/UI/Scheduler/UIxAppointmentEditor.m
SOGo/UI/Scheduler/UIxAppointmentEditor.wox
SOGo/UI/Scheduler/UIxCalView.m
SOGo/UI/Scheduler/UIxDatePicker.m
SOGo/UI/Scheduler/Version
SOGo/UI/Scheduler/cycles.plist [new file with mode: 0644]
SOGo/UI/Scheduler/product.plist
SOGoLogic/ChangeLog
SOGoLogic/SOGoAppointment.h
SOGoLogic/SOGoAppointment.m
SOGoLogic/SOGoAppointmentICalRenderer.m
SOGoLogic/Version

index cf3df43be1fab93c4d55aad16167a589a09166a7..4c71f25689ad164047ad8e088d62709c1f80be3f 100644 (file)
@@ -24,7 +24,9 @@
 #include <SOGo/SOGoCustomGroupFolder.h>
 #include <SOGoLogic/AgenorUserManager.h>
 #include <OGoContentStore/OCSFolder.h>
+#include <SaxObjC/SaxObjC.h>
 #include <NGiCal/NGiCal.h>
+#include <NGExtensions/NGCalendarDateRange.h>
 #include "common.h"
 #include <unistd.h>
 #include <stdlib.h>
 @implementation SOGoAppointmentFolder
 
 static BOOL       debugOn = NO;
-static NSTimeZone *MET = nil;
+static NSTimeZone *MET    = nil;
 
 + (void)initialize {
-  if (MET == nil) MET = [[NSTimeZone timeZoneWithAbbreviation:@"MET"] retain];
+  static BOOL didInit = NO;
+
+  if (didInit) return;
+  didInit = YES;
+
+  MET = [[NSTimeZone timeZoneWithAbbreviation:@"MET"] retain];
 }
 
 + (NSString *)globallyUniqueObjectId {
@@ -126,36 +133,50 @@ static NSTimeZone *MET = nil;
 
 /* fetching */
 
-- (NSMutableDictionary *)fixupRecord:(NSDictionary *)_record {
+- (NSMutableDictionary *)fixupRecord:(NSDictionary *)_record
+  fetchRange:(NGCalendarDateRange *)_r
+{
   NSMutableDictionary *md;
   id tmp;
   
   md = [[_record mutableCopy] autorelease];
-  
-  if ((tmp = [_record objectForKey:@"startdate"])) {
-    tmp = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
+  if (![[_record valueForKey:@"iscycle"] intValue]) {
+    if ((tmp = [_record objectForKey:@"startdate"])) {
+      tmp = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
                                    (NSTimeInterval)[tmp unsignedIntValue]];
-    [tmp setTimeZone:[self viewTimeZone]];
-    if (tmp) [md setObject:tmp forKey:@"startDate"];
-    [tmp release];
-  }
-  else
-    [self logWithFormat:@"missing 'startdate' in record?"];
-  
-  if ((tmp = [_record objectForKey:@"enddate"])) {
-    tmp = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
+      [tmp setTimeZone:[self viewTimeZone]];
+      if (tmp) [md setObject:tmp forKey:@"startDate"];
+      [tmp release];
+    }
+    else
+      [self logWithFormat:@"missing 'startdate' in record?"];
+
+    if ((tmp = [_record objectForKey:@"enddate"])) {
+      tmp = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
                                    (NSTimeInterval)[tmp unsignedIntValue]];
+      [tmp setTimeZone:[self viewTimeZone]];
+      if (tmp) [md setObject:tmp forKey:@"endDate"];
+      [tmp release];
+    }
+    else
+      [self logWithFormat:@"missing 'enddate' in record?"];
+  }
+  else {
+    /* cycle is in _r */
+    tmp = [_r startDate];
+    [tmp setTimeZone:[self viewTimeZone]];
+    [md setObject:tmp forKey:@"startDate"];
+    tmp = [_r endDate];
     [tmp setTimeZone:[self viewTimeZone]];
-    if (tmp) [md setObject:tmp forKey:@"endDate"];
-    [tmp release];
+    [md setObject:tmp forKey:@"endDate"];
   }
-  else
-    [self logWithFormat:@"missing 'enddate' in record?"];
-  
   return md;
 }
 
-- (NSArray *)fixupRecords:(NSArray *)_records {
+- (NSArray *)fixupRecords:(NSArray *)_records
+  fetchRange:(NGCalendarDateRange *)_r
+{
   NSMutableArray *ma;
   unsigned i, count;
 
@@ -166,9 +187,33 @@ static NSTimeZone *MET = nil;
   ma = [NSMutableArray arrayWithCapacity:count];
   for (i = 0; i < count; i++) {
     id row;
-    
-    row = [self fixupRecord:[_records objectAtIndex:i]];
-    if (row) [ma addObject:row];
+
+    row = [_records objectAtIndex:i];
+    if ([[row valueForKey:@"iscycle"] intValue] != 0) {
+      NSString  *uid;
+      id        aptObject;
+      iCalEvent *apt;
+      NSArray   *ranges;
+      unsigned  k, rCount;
+      
+      uid        = [row valueForKey:@"uid"];
+      aptObject  = [self appointmentWithName:uid inContext:nil];
+      apt        = [aptObject event];
+      ranges     = [apt recurrenceRangesWithinCalendarDateRange:_r];
+      rCount     = [ranges count];
+      for (k = 0; k < rCount; k++) {
+        NGCalendarDateRange *rRange;
+        id fixedRow;
+
+        rRange   = [ranges objectAtIndex:k];
+        fixedRow = [self fixupRecord:row fetchRange:rRange];
+        if (fixedRow) [ma addObject:fixedRow];
+      }
+    }
+    else {
+      row = [self fixupRecord:row fetchRange:_r];
+      if (row) [ma addObject:row];
+    }
   }
   return ma;
 }
@@ -179,16 +224,20 @@ static NSTimeZone *MET = nil;
   from:(NSCalendarDate *)_startDate
   to:(NSCalendarDate *)_endDate 
 {
-  EOQualifier *qualifier;
-  NSArray     *records;
-  NSString    *sql;
-  
+  EOQualifier         *qualifier;
+  NSArray             *records;
+  NSString            *sql;
+  NGCalendarDateRange *r;
+
   if (_folder == nil) {
     [self errorWithFormat:@"(%s): missing folder for fetch!",
             __PRETTY_FUNCTION__];
     return nil;
   }
   
+  r = [NGCalendarDateRange calendarDateRangeWithStartDate:_startDate
+                           endDate:_endDate];
+
   if (debugOn)
     [self debugWithFormat:@"should fetch (%@=>%@) ...", _startDate, _endDate];
   
@@ -203,7 +252,7 @@ static NSTimeZone *MET = nil;
     return nil;
   }
   
-  records = [self fixupRecords:records];
+  records = [self fixupRecords:records fetchRange:r];
   if (debugOn)
     [self logWithFormat:@"fetched %i records: %@", [records count], records];
   return records;
index feb854d5b1117cd42ec0601ef52510e76ac2bd9e..e7cedb724d661de6ac9dc0aafa1f3961df91320b 100644 (file)
@@ -36,7 +36,7 @@
         appointments with an externally generated unique key.
 */
 
-@class NSString, NSArray, NSException;
+@class NSString, NSArray, NSException, iCalEvent;
 
 @interface SOGoAppointmentObject : SOGoContentObject
 {
@@ -45,6 +45,7 @@
 /* accessors */
 
 - (NSString *)iCalString;
+- (iCalEvent *)event;
 
 /* folder management */
 
index 2a10db1824f4c163242da4af75eb26750ceac7fe..d182a0c1d5fe3c0bb931dadeb64bc985bf19006a 100644 (file)
 
 #include "SOGoAppointmentObject.h"
 #include <SOGoLogic/AgenorUserManager.h>
+#include <SaxObjC/SaxObjC.h>
 #include <NGiCal/NGiCal.h>
 #include "common.h"
 
 @implementation SOGoAppointmentObject
 
+static id<NSObject,SaxXMLReader> parser  = nil;
+static SaxObjectDecoder          *sax    = nil;
+static NGLogger                  *logger = nil;
+
++ (void)initialize {
+  NGLoggerManager     *lm;
+  SaxXMLReaderFactory *factory;
+  static BOOL         didInit = NO;
+  
+  if (didInit) return;
+  didInit = YES;
+  
+  lm      = [NGLoggerManager defaultLoggerManager];
+  logger  = [lm loggerForClass:self];
+  
+  factory = [SaxXMLReaderFactory standardXMLReaderFactory];
+  parser  = [[factory createXMLReaderForMimeType:@"text/calendar"]
+    retain];
+  if (parser == nil)
+    [logger fatalWithFormat:@"did not find a parser for text/calendar!"];
+  sax = [[SaxObjectDecoder alloc] initWithMappingNamed:@"NGiCal"];
+  if (sax == nil)
+    [logger fatalWithFormat:@"could not create the iCal SAX handler!"];
+  
+  [parser setContentHandler:sax];
+  [parser setErrorHandler:sax];
+}
+
 - (void)dealloc {
   [super dealloc];
 }
   return [self contentAsString];
 }
 
+- (iCalEvent *)event {
+  NSString  *iCalString;
+  iCalEvent *event;
+
+  iCalString = [self iCalString];
+  if ([iCalString length] > 0) {
+    iCalCalendar *cal;
+
+    [parser parseFromSource:iCalString];
+    cal   = [sax rootObject];
+    [sax reset];
+    event = [[cal events] lastObject];
+    return event;
+  }
+  return nil;
+}
+
 /* iCal handling */
 
 - (NSArray *)attendeeUIDsFromAppointment:(SOGoAppointment *)_apt {
index 2fe96be7d1417739de71eb5462f134e9e6590bac..134463365c5b956652e0eebbef50d35f415d9c17 100644 (file)
@@ -1,3 +1,23 @@
+2005-02-15  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v0.9.25
+
+       * GNUmakefile.preamble: added libNGiCal as a dependency
+
+       * Appointments/SOGoAppointmentObject.[hm]: added -event as a 
+         convenience to retrieve a complete iCalEvent from the stored
+         representation. This is used by SOGoAppointmentFolder to perform
+         necessary range calculations on recurrent events.
+
+       * Appointments/SOGoAppointmentFolder.[hm]: extended -fixupRecord: and
+         -fixupRecords: to accept a fetchRange: as second parameter. This
+         is used for cyclic (recurrent) events to determine the required
+         'amount' of fixup that needs to be done. During -fixupRecords: its
+         correctly determined now whether the record describes a recurrent
+         event - if that's the case, the whole event is retrieved and the
+         record gets duplicated (memory efficiently) as necessary to resemble
+         the appropriate fetch information in the desired range.
+
 2004-12-22  Marcus Mueller  <znek@mulle-kybernetik.com>
 
        * v0.9.24
index d1cfafc578b881fa4ac8a7ab6720ed1aebaf0630..9d55e5434d411339f4ea55f156e42c1cf1f3b08f 100644 (file)
@@ -18,6 +18,7 @@ libSOGo_LIBRARIES_DEPEND_UPON += \
        -lOGoContentStore       \
        -lGDLAccess             \
        -lNGObjWeb              \
+       -lNGiCal                \
        -lNGMime                \
        -lNGStreams -lNGExtensions -lEOControl \
        -lXmlRpc -lDOM -lSaxObjC
index 5de688c4b537fa1ea98baa424b10152a32e76dfe..138177d6f6941fbf748d558d502cdd05c9958ae8 100644 (file)
@@ -1,3 +1,27 @@
+2005-02-15  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v0.9.113
+       
+       * UIxCalView.m: removed dead code
+
+2005-02-12  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v0.9.112
+
+       * UIxAppointmentEditor.[wox,m]: added recurrence selection/display.
+         The current UI is similar to that found in OGo, but inappropriate in
+         the context of SOGo (SOGo in theory supports all recurrence rules
+         described in RFC2445, thus needs a more complex UI in order to render
+         all rules appropriately)
+
+       * cycles.plist: property list with predefined recurrence rules for
+         the UIxAppointmentEditor
+
+       * English.lproj/default.strings: new labels for cycles and accompanied
+         UI
+
+       * UIxDatePicker.m: Bugfix for format edge case (when date is nil)
+
 2005-01-26  Marcus Mueller  <znek@mulle-kybernetik.com>
 
        * UIxDatePicker.m: corrected dateFormats for French locale. The
index fb0a5659b861c6c44d7399663fab2c772e141145..ab529c9e288261701f558e14b2982b49ded874a8 100644 (file)
 "Status"               = "Status";
 "Location"             = "Location";
 "Priority"             = "Priority";
+"Cycle"                        = "Cycle";
+"Cycle End"            = "Cycle End";
 "Categories"           = "Categories";
 "Classification"       = "Classification";
 "Duration"             = "Duration";
 "prio_8"               = "Low";
 "prio_9"               = "Low";
 
+/* Cycles */
+
+"cycle_once"           = "once";
+"cycle_daily"          = "daily";
+"cycle_weekly"         = "weekly";
+"cycle_2weeks"         = "all 2 weeks";
+"cycle_4weeks"         = "all 4 weeks";
+"cycle_monthly"                = "monthly";
+"cycle_weekday"                = "weekday";
+"cycle_yearly"         = "yearly";
 
 /* Appointment categories */
 
index 49065c1acd39f218ed3c9716293d04f294c96d19..51c7ae6c607d4a341a7d839e5d0ada2d43701aae 100644 (file)
@@ -110,6 +110,7 @@ SchedulerUI_LOCALIZED_RESOURCE_FILES += \
 SchedulerUI_RESOURCE_FILES +=          \
        skycalendar.html                \
        skycalendar.js                  \
+       cycles.plist                    \
 
 # make
 
index 67af9b86c02e1b0643488189b317f83eb5bc1663..402e1389f6f57298227e96a9a1da39b2d223c22d 100644 (file)
@@ -26,6 +26,7 @@
 
 @class NSString;
 @class iCalPerson;
+@class iCalRecurrenceRule;
 @class SOGoAppointment;
 
 @interface UIxAppointmentEditor : UIxComponent
@@ -37,6 +38,7 @@
   /* individual values */
   NSCalendarDate *startDate;
   NSCalendarDate *endDate;
+  NSCalendarDate *cycleUntilDate;
   NSString       *title;
   NSString       *location;
   NSString       *comment;
@@ -47,6 +49,7 @@
   NSString       *accessClass;
   BOOL           isPrivate;         /* default: NO */
   BOOL           checkForConflicts; /* default: NO */
+  NSDictionary   *cycle;
 }
 
 - (NSString *)iCalStringTemplate;
 
 - (void)setCheckForConflicts:(BOOL)_checkForConflicts;
 - (BOOL)checkForConflicts;
-  
+
+- (BOOL)hasCycle;
+- (iCalRecurrenceRule *)rrule;
+- (void)adjustCycleControlsForRRule:(iCalRecurrenceRule *)_rrule;
+- (NSDictionary *)cycleMatchingRRule:(iCalRecurrenceRule *)_rrule;
+
 - (NSString *)_completeURIForMethod:(NSString *)_method;
 
 - (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values
 #include "iCalPerson+UIx.h"
 #include "UIxComponent+Agenor.h"
 
+@interface iCalRecurrenceRule (SOGoExtensions)
+- (NSString *)cycleRepresentationForSOGo;
+@end
+
 @interface NSDate(UsedPrivates)
 - (NSString *)icalString; // TODO: this is in NGiCal
 @end
 }
 
 - (void)dealloc {
-  [self->iCalString   release];
-  [self->errorText    release];
-  [self->item         release];
-
-  [self->startDate    release];
-  [self->endDate      release];
-  [self->title        release];
-  [self->location     release];
-  [self->comment      release];
-  [self->participants release];
-  [self->resources    release];
-  [self->priority     release];
-  [self->categories   release];
-  [self->accessClass   release];
+  [self->iCalString     release];
+  [self->errorText      release];
+  [self->item           release];
+
+  [self->startDate      release];
+  [self->endDate        release];
+  [self->cycleUntilDate release];
+  [self->title          release];
+  [self->location       release];
+  [self->comment        release];
+  [self->participants   release];
+  [self->resources      release];
+  [self->priority       release];
+  [self->categories     release];
+  [self->accessClass    release];
+  [self->cycle          release];
   [super dealloc];
 }
 
   return self->checkForConflicts;
 }
 
+- (NSArray *)cycles {
+  static NSArray *cycles = nil;
+  
+  if (!cycles) {
+    NSBundle *bundle;
+    NSString *path;
+
+    bundle = [NSBundle bundleForClass:[self class]];
+    path   = [bundle pathForResource:@"cycles" ofType:@"plist"];
+    NSAssert(path != nil, @"Cannot find cycles.plist!");
+    cycles = [[NSArray arrayWithContentsOfFile:path] retain];
+    NSAssert(cycles != nil, @"Cannot instantiate cycles from cycles.plist!");
+  }
+  return cycles;
+}
+
+- (void)setCycle:(NSDictionary *)_cycle {
+  ASSIGN(self->cycle, _cycle);
+}
+- (NSDictionary *)cycle {
+  return self->cycle;
+}
+- (BOOL)hasCycle {
+  [self debugWithFormat:@"cycle: %@", self->cycle];
+  if (![self->cycle objectForKey:@"rule"])
+    return NO;
+  return YES;
+}
+- (NSString *)cycleLabel {
+  NSString *key;
+  
+  key = [self->item objectForKey:@"label"];
+  return [self labelForKey:key];
+}
+
+- (iCalRecurrenceRule *)rrule {
+  NSString           *ruleRep;
+  iCalRecurrenceRule *rule;
+
+  if (![self hasCycle])
+    return nil;
+  ruleRep = [self->cycle objectForKey:@"rule"];
+  rule    = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:ruleRep];
+
+  if (self->cycleUntilDate)
+    [rule setUntilDate:self->cycleUntilDate];
+  return rule;
+}
+
+- (void)adjustCycleControlsForRRule:(iCalRecurrenceRule *)_rrule {
+  NSDictionary *c;
+  
+  c = [self cycleMatchingRRule:_rrule];
+  [self setCycle:c];
+
+  [self->cycleUntilDate release];
+  self->cycleUntilDate = [[_rrule untilDate] copy];
+  [self->cycleUntilDate setTimeZone:[self viewTimeZone]];
+}
+
+/*
+ This method is necessary, because we have a fixed sets of cycles in the UI.
+ The model is able to represent arbitrary rules, however.
+ There SHOULD be a different UI, similar to iCal.app, to allow modelling
+ of more complex rules.
+ This method obviously cannot map all existing rules back to the fixed list
+ in cycles.plist. This should be fixed in a future version when interop
+ becomes more important.
+ */
+- (NSDictionary *)cycleMatchingRRule:(iCalRecurrenceRule *)_rrule {
+  NSString *cycleRep;
+  NSArray  *cycles;
+  unsigned i, count;
+
+  if (!_rrule)
+    return [[self cycles] objectAtIndex:0];
+
+  cycleRep = [_rrule cycleRepresentationForSOGo];
+  cycles   = [self cycles];
+  count    = [cycles count];
+  for (i = 1; i < count; i++) {
+    NSDictionary *c;
+    NSString     *cr;
+
+    c  = [cycles objectAtIndex:i];
+    cr = [c objectForKey:@"rule"];
+    if ([cr isEqualToString:cycleRep])
+      return c;
+  }
+  [self warnWithFormat:@"No default cycle for rrule found! -> %@", _rrule];
+  return nil;
+}
+
 
 /* transparency */
 
 }
 
 - (void)loadValuesFromAppointment:(SOGoAppointment *)_appointment {
-  NSString *s;
+  NSString           *s;
+  iCalRecurrenceRule *rrule;
 
   if ((self->startDate = [[_appointment startDate] copy]) == nil)
     self->startDate = [[[NSCalendarDate date] hour:11 minute:0] copy];
     [self setIsPrivate:NO];
   else
     [self setIsPrivate:YES]; /* we're possibly loosing information here */
+
+  /* cycles */
+  rrule = [_appointment recurrenceRule];
+  [self adjustCycleControlsForRRule:rrule];
 }
 
 - (void)saveValuesIntoAppointment:(SOGoAppointment *)_appointment {
   }
   [_appointment setAttendees:attendees];
 
-#if 0
-  [_appointment setOrganizer:[self getOrganizer]];
-#endif
+  /* cycles */
+  [_appointment setRecurrenceRule:[self rrule]];
 }
 
 - (void)loadValuesFromICalString:(NSString *)_ical {
 }
 
 @end /* UIxAppointmentEditor */
+
+@interface iCalRecurrenceRule (UsedPrivates)
+- (NSString *)freq;
+@end /* iCalRecurrenceRule (UsedPrivates) */
+
+@implementation iCalRecurrenceRule (SOGoExtensions)
+
+- (NSString *)cycleRepresentationForSOGo {
+  NSMutableString *s;
+  
+  s = [NSMutableString stringWithCapacity:20];
+  [s appendString:@"FREQ="];
+  [s appendString:[self freq]];
+  if ([self repeatInterval] != 1) {
+    [s appendFormat:@";INTERVAL=%d", [self repeatInterval]];
+  }
+  return s;
+}
+
+@end /* iCalRecurrenceRule (SOGoExtensions) */
index b05ca9250919ebeea9e585da8c0d517e4bd6beb4..aea0af045f65988d084727c28d1add936222833e 100644 (file)
                   </span>
                 </td>
               </tr>
+              <tr valign="top">
+                <td align="right" width="15%">
+                  <span class="aptview_text">
+                    <var:string label:value="Cycle" />:
+                  </span>
+                </td>
+                <td align="left" bgcolor="#FFFFF0">
+                  <span class="aptview_text">
+                    <table>
+                      <tr>
+                        <td>
+                          <var:popup list="cycles"
+                                     item="item"
+                                     label:string="$cycleLabel"
+                                     selection="cycle"
+                          />
+                        </td>
+                        <td><var:string label:value="Cycle End"
+                                        const:style="aptview_text"
+                            />:</td>
+                        <td>
+                          <!--
+                          <var:date-field date="cycleUntilDate"
+                                          label:format="dayLabelFormat" />
+                          -->
+                          <!--
+                          <var:component className="UIxDatePicker"
+                                         date="cycleUntilDate"
+                                         label="foo"
+                                         const:dateID="cycleUntilDate"
+                          />
+                         -->
+                        </td>
+                      </tr>
+                    </table>
+                  </span>
+                </td>
+              </tr>
               <tr valign="top">
                 <td align="right" width="15%">
                   <span class="aptview_text">
index 6d13a1469541eb63fc725b4be13058e760c6e55f..516fa638485a03e74d9b23bd454f0f8a8f0f9194 100644 (file)
@@ -7,6 +7,8 @@
 #include <NGObjWeb/SoUser.h>
 #include <SOGoUI/SOGoAptFormatter.h>
 #include <SOGoLogic/AgenorUserManager.h>
+#include <SOGoLogic/SOGoAppointment.h>
+#include <NGExtensions/NGCalendarDateRange.h>
 #include "UIxComponent+Agenor.h"
 
 @interface UIxCalView (PrivateAPI)
@@ -315,6 +317,7 @@ static BOOL shouldDisplayWeekend = NO;
                         queryString:nil];
 }
 
+
 /* fetching */
 
 - (NSCalendarDate *)startDate {
@@ -324,82 +327,20 @@ static BOOL shouldDisplayWeekend = NO;
   return [[self startDate] tomorrow];
 }
 
-- (NSArray *)_fetchCoreInfosForUIDs:(NSArray *)_uids 
-  uniqueWithSet:(NSMutableSet *)_uniquer 
-{
-  NSMutableArray *ma;
-  unsigned i, count;
-  
-  count = [_uids count];
-  ma    = [NSMutableArray arrayWithCapacity:count * 10];
-  
-  for (i = 0; i < count; i++) {
-    SOGoAppointmentFolder *folder;
-    NSArray               *res;
-    int                   j, rcount;
-
-    folder = [[self clientObject] lookupGroupCalendarFolderForUIDs:_uids
-                                  inContext:[self context]];
-    res = [folder fetchOverviewInfosFrom:[self startDate]
-                  to:[self endDate]];
-
-    /* perform uniquing */
-    for (j = 0, rcount = [res count]; j < rcount; j++) {
-      NSDictionary *record;
-      
-      record = [res objectAtIndex:j];
-  
-      if ([_uniquer containsObject:[record valueForKey:@"uid"]])
-        continue;
-
-      [ma addObject:record];
-      [_uniquer addObject:[record valueForKey:@"uid"]];
-    }
-  }
-  return ma;
-}
-
 - (NSArray *)fetchCoreInfos {
-  id      aptFolder;
-  NSArray *more;
-  id      uids;
-  
-  if (self->appointments)
-    return self->appointments;
-  
-  aptFolder = [self clientObject];
-  self->appointments =
-    [[aptFolder fetchOverviewInfosFrom:[self startDate]
-                to:[self endDate]] retain];
-
-  /* ZNeK: this is dead code, isn't it? -> check */
-  uids = [[[[self context] request] formValueForKey:@"uids"] stringValue];
-  uids = [uids length] > 0 ? [uids componentsSeparatedByString:@","] : nil;
-  if ([uids count] > 0) {
-    NSMutableSet *availUIDs;
-    id tmp;
-
-    [self errorWithFormat:@"fetchCoreInfos called for 'uids' form value"];
-
-    /* make appointments unique, prefer the own "versions" */
-    tmp = [self->appointments valueForKey:@"uid"];
-    availUIDs = [[NSMutableSet alloc] initWithArray:tmp];
-    
-    more = [self _fetchCoreInfosForUIDs:uids uniqueWithSet:availUIDs];
-    if (more > 0) {
-      NSArray *tmp;
-      
-      tmp = self->appointments;
-      self->appointments = [[tmp arrayByAddingObjectsFromArray:more] retain];
-      [tmp release];
-    }
-    
-    [availUIDs release];
+  if (!self->appointments) {
+    id             folder;
+    NSCalendarDate *sd, *ed;
+
+    folder             = [self clientObject];
+    sd                 = [self startDate];
+    ed                 = [self endDate];
+    self->appointments = [[folder fetchOverviewInfosFrom:sd to:ed] retain];
   }
-  
   return self->appointments;
 }
 
+
 /* date selection & conversion */
 
 - (NSDictionary *)todayQueryParameters {
index 57d49c7d0569a264c0c387f5ac3fea3f4f05fb56..65f94a012d0d78af6c173c70447af36144cc8856 100644 (file)
 }
 - (NSString *)formattedDateString {
   if ([self useISOFormats]) {
-    return [NSString stringWithFormat:@"%d-%02d-%02d",
+    return [NSString stringWithFormat:@"%04d-%02d-%02d",
                                         [[self year] intValue],
                                         [[self month] intValue],
                                         [[self day] intValue]];
index 5593445911f4e995850fccb67a19b24c5bcd66b8..537a34c6a5e4fb1b67f7083992b9bafa6ac7c60d 100644 (file)
@@ -1,7 +1,8 @@
 # $Id$
 
-SUBMINOR_VERSION:=111
+SUBMINOR_VERSION:=112
 
+# v0.9.112 requires SOGoLogic    v0.9.12
 # v0.9.107 requires WOExtensions v4.5.21
 # v0.9.105 requires NGObjWeb     v4.5.102
 # v0.9.101 requires NGiCal       v4.5.36
diff --git a/SOGo/UI/Scheduler/cycles.plist b/SOGo/UI/Scheduler/cycles.plist
new file mode 100644 (file)
index 0000000..32589b9
--- /dev/null
@@ -0,0 +1,29 @@
+(
+  {
+    "label" = "cycle_once";
+  },
+  {
+    "label" = "cycle_daily";
+    "rule"  = "FREQ=DAILY";
+  },
+  {
+    "label" = "cycle_weekly";
+    "rule"  = "FREQ=WEEKLY";
+  },
+  {
+    "label" = "cycle_2weeks";
+    "rule"  = "FREQ=WEEKLY;INTERVAL=2";
+  },
+  {
+    "label" = "cycle_4weeks";
+    "rule"  = "FREQ=WEEKLY;INTERVAL=4";
+  },
+  {
+    "label" = "cycle_monthly";
+    "rule"  = "FREQ=MONTHLY";
+  },
+  {
+    "label" = "cycle_yearly";
+    "rule"  = "FREQ=YEARLY";
+  }
+)
\ No newline at end of file
index 62fd572e7d058e4bfc6e0187953f33bef7aeee58..5c2c7b62cab68c58c54379da809ecac4b845edb5 100644 (file)
@@ -21,6 +21,7 @@
     skycalendar.js,
     green_corner.gif,
     invisible_space_2.gif,
+    cycles.plist,
   );
 
   factories = {
index 39f8d67e4b3adcf5c3e33730ae2abe7ad3ca9aa3..cc20d83cec1234a4fbba48fcbb2225eb3a59c621 100644 (file)
@@ -1,3 +1,12 @@
+2005-02-12  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v0.9.37
+
+       * SOGoAppointment.[hm]: added simplified API for getting/setting
+         recurrence rules
+
+       * SOGoAppointmentICalRenderer.m: added rendering of recurrence rule
+
 2005-01-26  Marcus Mueller  <znek@mulle-kybernetik.com>
 
        * AgenorUserManager.m: changed -getEmailForUID: to use 
index 774dd757b2ffb15b89bce7bfdc23a1865c78ed67..dfdd731f9054edfc1a46ae441aa8caa7df2cafb2 100644 (file)
@@ -33,8 +33,8 @@
   OGoContentStore.
 */
 
-@class NSString, NSArray, NSCalendarDate;
-@class iCalPerson, iCalEvent;
+@class NSString, NSArray, NSCalendarDate, NGCalendarDateRange;
+@class iCalPerson, iCalEvent, iCalRecurrenceRule;
 
 @interface SOGoAppointment : NSObject
 {
 /* attendees -> role == NON-PART */
 - (NSArray *)resources;
 
+/* simplified recurrence API */
+- (void)setRecurrenceRule:(iCalRecurrenceRule *)_rrule;
+- (iCalRecurrenceRule *)recurrenceRule;
+- (BOOL)hasRecurrenceRule;
+
+- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r;
+
 /* iCal generation */
 
 - (NSString *)iCalString;
index 0d5e9f11bc3ac3953af4b6cd1a4ee42c642edf8c..b0d5504d786b66e058a991b82bd4559a59d018c2 100644 (file)
@@ -43,6 +43,7 @@ static NGLogger                  *logger = nil;
   static BOOL         didInit = NO;
 
   if (didInit) return;
+  didInit = YES;
 
   lm      = [NGLoggerManager defaultLoggerManager];
   logger  = [lm loggerForClass:self];
@@ -389,6 +390,33 @@ static NGLogger                  *logger = nil;
   return nil; /* not found */
 }
 
+
+/*
+ NOTE: this is not the same API as used by NGiCal!
+ SOGo/OGo cannot deal with the complete NGiCal API properly, although
+ SOGo COULD do so in the future
+*/
+- (void)setRecurrenceRule:(iCalRecurrenceRule *)_rrule {
+  [_rrule retain];
+  [self->event removeAllRecurrenceRules];
+  if (_rrule)
+    [self->event addToRecurrenceRules:_rrule];
+  [_rrule release];
+}
+- (iCalRecurrenceRule *)recurrenceRule {
+  if ([self->event hasRecurrenceRules])
+    return [[self->event recurrenceRules] objectAtIndex:0];
+  return nil;
+}
+- (BOOL)hasRecurrenceRule {
+  return [self recurrenceRule] != nil;
+}
+
+- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r {
+  return [self->event recurrenceRangesWithinCalendarDateRange:_r];
+}
+
+
 /* description */
 
 - (void)appendAttributesToDescription:(NSMutableString *)_ms {
index ecba50dadc7e2801ef2c8a3d2bfeede9e7d8f2b4..d0e53fdc5c8d7123e73a7c24eb6e6f442c238bd3 100644 (file)
@@ -190,6 +190,13 @@ static unsigned DefaultICalStringCapacity = 1024;
   [s appendString:[_apt accessClass]];
   [s appendString:@"\r\n"];
   
+  /* recurrence rules */
+  if ([_apt hasRecurrenceRule]) {
+    [s appendString:@"RRULE:"];
+    [s appendString:[[_apt recurrenceRule] iCalRepresentation]];
+    [s appendString:@"\r\n"];
+  }
+
   [self addOrganizer:[_apt organizer] toString:s];
   [self addAttendees:[_apt attendees] toString:s];
   
index 338506abfc6c3088168b6587055bed48cf4db6b7..6fff702374923300894fbeb9ce06bf84d0ca2440 100644 (file)
@@ -1,7 +1,8 @@
 # Version file
 
-SUBMINOR_VERSION:=36
+SUBMINOR_VERSION:=37
 
+# v0.9.37 requires NGiCal        v4.5.39
 # v0.9.34 requires libFoundation v1.0.67
 # v0.9.32 requires NGiCal        v4.5.37
 # v0.9.31 requires NGiCal        v4.5.36