#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 {
/* 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;
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;
}
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];
return nil;
}
- records = [self fixupRecords:records];
+ records = [self fixupRecords:records fetchRange:r];
if (debugOn)
[self logWithFormat:@"fetched %i records: %@", [records count], records];
return records;
appointments with an externally generated unique key.
*/
-@class NSString, NSArray, NSException;
+@class NSString, NSArray, NSException, iCalEvent;
@interface SOGoAppointmentObject : SOGoContentObject
{
/* accessors */
- (NSString *)iCalString;
+- (iCalEvent *)event;
/* folder management */
#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 {
+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
-lOGoContentStore \
-lGDLAccess \
-lNGObjWeb \
+ -lNGiCal \
-lNGMime \
-lNGStreams -lNGExtensions -lEOControl \
-lXmlRpc -lDOM -lSaxObjC
+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
"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 */
SchedulerUI_RESOURCE_FILES += \
skycalendar.html \
skycalendar.js \
+ cycles.plist \
# make
@class NSString;
@class iCalPerson;
+@class iCalRecurrenceRule;
@class SOGoAppointment;
@interface UIxAppointmentEditor : UIxComponent
/* individual values */
NSCalendarDate *startDate;
NSCalendarDate *endDate;
+ NSCalendarDate *cycleUntilDate;
NSString *title;
NSString *location;
NSString *comment;
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) */
</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">
#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)
queryString:nil];
}
+
/* fetching */
- (NSCalendarDate *)startDate {
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 {
}
- (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]];
# $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
--- /dev/null
+(
+ {
+ "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
skycalendar.js,
green_corner.gif,
invisible_space_2.gif,
+ cycles.plist,
);
factories = {
+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
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;
static BOOL didInit = NO;
if (didInit) return;
+ didInit = YES;
lm = [NGLoggerManager defaultLoggerManager];
logger = [lm loggerForClass:self];
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 {
[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];
# 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