Skip to content

Commit

Permalink
Add support for events with recurrence dates
Browse files Browse the repository at this point in the history
  • Loading branch information
cgx committed Dec 15, 2017
1 parent e61a55f commit 7f99514
Show file tree
Hide file tree
Showing 15 changed files with 337 additions and 48 deletions.
1 change: 1 addition & 0 deletions NEWS
Expand Up @@ -10,6 +10,7 @@ New features
- [web] register SOGo as a handler for the mailto scheme (#1223)
- [web] new events list view where events are grouped by day
- [web] user setting to always show mail editor inside current window or in popup window
- [web] add support for events with recurrence dates (RDATE)

Enhancements
- [web] follow requested URL after user authentication
Expand Down
4 changes: 2 additions & 2 deletions SOPE/NGCards/iCalRecurrenceCalculator.h
Expand Up @@ -46,6 +46,7 @@
firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir
recurrenceRules: (NSArray *) _rRules
exceptionRules: (NSArray *) _exRules
recurrenceDates: (NSArray *) _rDates
exceptionDates: (NSArray *) _exDates;

+ (id) recurrenceCalculatorForRecurrenceRule: (iCalRecurrenceRule *) _rrule
Expand All @@ -54,8 +55,7 @@
- (id) initWithRecurrenceRule: (iCalRecurrenceRule *) _rrule
firstInstanceCalendarDateRange: (NGCalendarDateRange *) _range;

- (NSArray *)
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *)_r;
- (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *)_r;
- (BOOL) doesRecurrWithinCalendarDateRange: (NGCalendarDateRange *) _range;

- (NGCalendarDateRange *) firstInstanceCalendarDateRange;
Expand Down
47 changes: 44 additions & 3 deletions SOPE/NGCards/iCalRecurrenceCalculator.m
Expand Up @@ -134,6 +134,17 @@ + (void) _fillRanges: (NSMutableArray *) ranges
}
}

+ (void) _fillRanges: (NSMutableArray *) ranges
fromDates: (NSArray *) rdates
withinRange: (NGCalendarDateRange *) limits
startingWithDate: (NGCalendarDateRange *) first
{
NSArray *dates;

dates = [self _ranges: rdates withinRange: limits startingWithDate: first];
[ranges addObjectsFromArray: dates];
}

+ (void) _removeExceptionsFromRanges: (NSMutableArray *) ranges
withRules: (NSArray *) exrules
withinRange: (NGCalendarDateRange *) limits
Expand All @@ -159,8 +170,30 @@ + (void) _removeExceptionsFromRanges: (NSMutableArray *) ranges
}

+ (NSArray *) _dates: (NSArray *) dateList
withinRange: (NGCalendarDateRange *) limits
withinRange: (NGCalendarDateRange *) limits
startingWithDate: (NGCalendarDateRange *) first
{
return [self _dates: dateList
withinRange: limits
startingWithDate: first
ranges: NO];
}


+ (NSArray *) _ranges: (NSArray *) dateList
withinRange: (NGCalendarDateRange *) limits
startingWithDate: (NGCalendarDateRange *) first
{
return [self _dates: dateList
withinRange: limits
startingWithDate: first
ranges: YES];
}

+ (NSArray *) _dates: (NSArray *) dateList
withinRange: (NGCalendarDateRange *) limits
startingWithDate: (NGCalendarDateRange *) first
ranges: (BOOL) returnRanges
{
NSMutableArray *newDates;
NSEnumerator *dates;
Expand All @@ -178,7 +211,12 @@ + (NSArray *) _dates: (NSArray *) dateList
currentRange = [NGCalendarDateRange calendarDateRangeWithStartDate: currentDate
endDate: [currentDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: [first duration]]];
if ([limits doesIntersectWithDateRange: currentRange])
[newDates addObject: currentDate];
{
if (returnRanges)
[newDates addObject: currentRange];
else
[newDates addObject: currentDate];
}
}

return newDates;
Expand Down Expand Up @@ -217,16 +255,19 @@ + (void) _removeExceptionDatesFromRanges: (NSMutableArray *) ranges
firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir
recurrenceRules: (NSArray *) _rRules
exceptionRules: (NSArray *) _exRules
recurrenceDates: (NSArray *) _rDates
exceptionDates: (NSArray *) _exDates
{
NSMutableArray *ranges;

ranges = [NSMutableArray arrayWithCapacity: 64];

if ([_rRules count] > 0)
if ([_rRules count] > 0 || [_rDates count] > 0)
{
[self _fillRanges: ranges fromRules: _rRules
withinRange: _r startingWithDate: _fir];
[self _fillRanges: ranges fromDates: _rDates
withinRange: _r startingWithDate: _fir];
[self _removeExceptionsFromRanges: ranges withRules: _exRules
withinRange: _r startingWithDate: _fir];
[self _removeExceptionDatesFromRanges: ranges withDates: _exDates
Expand Down
6 changes: 6 additions & 0 deletions SOPE/NGCards/iCalRepeatableEntityObject.h
Expand Up @@ -44,6 +44,12 @@
- (NSArray *)recurrenceRules;
- (NSArray *)recurrenceRulesWithTimeZone: (id) timezone;

- (void) removeAllRecurrenceDates;
- (void) addToRecurrenceDates: (NSCalendarDate *) _rdate;
- (BOOL) hasRecurrenceDates;
- (NSArray *) recurrenceDates;
- (NSArray *) recurrenceDatesWithTimeZone: (id) theTimeZone;

- (void)removeAllExceptionRules;
- (void)addToExceptionRules:(id)_rrule;
- (BOOL)hasExceptionRules;
Expand Down
166 changes: 144 additions & 22 deletions SOPE/NGCards/iCalRepeatableEntityObject.m
@@ -1,6 +1,6 @@
/*
Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2012 Inverse inc.
Copyright (C) 2017 Inverse inc.
This file is part of SOPE.
Expand Down Expand Up @@ -40,6 +40,8 @@ - (Class) classForTag: (NSString *) classTag

if ([classTag isEqualToString: @"RRULE"])
tagClass = [iCalRecurrenceRule class];
else if ([classTag isEqualToString: @"RDATE"])
tagClass = [iCalDateTime class];
else if ([classTag isEqualToString: @"EXDATE"])
tagClass = [iCalDateTime class];
else
Expand Down Expand Up @@ -84,6 +86,108 @@ - (NSArray *) recurrenceRulesWithTimeZone: (id) timezone
return [self rules: rules withTimeZone: timezone];
}

- (void) removeAllRecurrenceDates
{
[self removeChildren: [self childrenWithTag: @"rdate"]];
}

- (void) addToRecurrenceDates: (NSCalendarDate *) _rdate
{
iCalDateTime *dateTime;

dateTime = [iCalDateTime new];
[dateTime setTag: @"rdate"];
if ([self isKindOfClass: [iCalEvent class]] && [(iCalEvent *)self isAllDay])
[dateTime setDate: _rdate];
else
[dateTime setDateTime: _rdate];
[self addChild: dateTime];
[dateTime release];
}

- (BOOL) hasRecurrenceDates
{
return ([[self childrenWithTag: @"rdate"] count] > 0);
}

/**
* Returns the recurrence dates for the entity, but adjusted to the entity timezone.
* @param theTimeZone the timezone of the entity.
* @see [iCalTimeZone computedDatesForStrings:]
* @return the exception dates, adjusted to the timezone.
*/
- (NSArray *) recurrenceDatesWithTimeZone: (id) theTimeZone
{
NSArray *dates, *rDates;
NSEnumerator *dateList;
NSCalendarDate *rDate;
NSString *dateString;
int offset;
unsigned i;

if (theTimeZone)
{
dates = [NSMutableArray array];
dateList = [[self childrenWithTag: @"rdate"] objectEnumerator];

while ((dateString = [dateList nextObject]))
{
rDates = [(iCalDateTime*) dateString dateTimes];
for (i = 0; i < [rDates count]; i++)
{
rDate = [rDates objectAtIndex: i];

// Example: timezone is -0400, date is 2012-05-24 (00:00:00 +0000),
// and changes to 2012-05-24 04:00:00 +0000
if ([theTimeZone isKindOfClass: [iCalTimeZone class]])
{
rDate = [(iCalTimeZone *) theTimeZone computedDateForDate: rDate];
}
else
{
offset = [(NSTimeZone *) theTimeZone secondsFromGMTForDate: rDate];
rDate = (NSCalendarDate *) [rDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
seconds:-offset];
}
[(NSMutableArray *) dates addObject: rDate];
}
}
}
else
dates = [self recurrenceDates];

return dates;
}

/**
* Return the recurrence dates of the entity in GMT.
* @return an array of NSCalendarDate instances.
*/
- (NSArray *) recurrenceDates
{
NSArray *rDates;
NSMutableArray *dates;
NSEnumerator *dateList;
NSCalendarDate *rDate;
NSString *dateString;
unsigned i;

dates = [NSMutableArray array];
dateList = [[self childrenWithTag: @"rdate"] objectEnumerator];

while ((dateString = [dateList nextObject]))
{
rDates = [(iCalDateTime*) dateString dateTimes];
for (i = 0; i < [rDates count]; i++)
{
rDate = [rDates objectAtIndex: i];
[dates addObject: rDate];
}
}

return dates;
}

- (void) removeAllExceptionRules
{
[self removeChildren: [self exceptionRules]];
Expand Down Expand Up @@ -284,7 +388,7 @@ - (NSArray *) exceptionDatesWithTimeZone: (id) theTimeZone

- (BOOL) isRecurrent
{
return [self hasRecurrenceRules];
return [self hasRecurrenceRules] || [self hasRecurrenceDates];
}

/* Matching */
Expand All @@ -303,10 +407,11 @@ - (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *)_r
firstInstanceCalendarDateRange: (NGCalendarDateRange *)_fir
{
return [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: _r
firstInstanceCalendarDateRange: _fir
recurrenceRules: [self recurrenceRules]
exceptionRules: [self exceptionRules]
exceptionDates: [self exceptionDates]];
firstInstanceCalendarDateRange: _fir
recurrenceRules: [self recurrenceRules]
exceptionRules: [self exceptionRules]
recurrenceDates: [self recurrenceDates]
exceptionDates: [self exceptionDates]];
}


Expand All @@ -315,27 +420,43 @@ - (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *)_r
lastPossibleRecurrenceStartDateUsingFirstInstanceCalendarDateRange: (NGCalendarDateRange *)_r
{
NSCalendarDate *date;
NSEnumerator *rRules;
iCalRecurrenceRule *rule;
iCalRecurrenceCalculator *calc;
NSCalendarDate *rdate;

date = nil;

rRules = [[self recurrenceRules] objectEnumerator];
rule = [rRules nextObject];
while (rule && ![rule isInfinite] && !date)
if ([self hasRecurrenceRules])
{
NSEnumerator *rRules;
iCalRecurrenceRule *rule;
iCalRecurrenceCalculator *calc;

rRules = [[self recurrenceRules] objectEnumerator];
rule = [rRules nextObject];
while (rule && ![rule isInfinite] && !date)
{
calc = [iCalRecurrenceCalculator
recurrenceCalculatorForRecurrenceRule: rule
withFirstInstanceCalendarDateRange: _r];
rdate = [[calc lastInstanceCalendarDateRange] startDate];
if (!rdate)
date = [_r startDate];
else if (!date || ([date compare: rdate] == NSOrderedAscending))
date = rdate;
else
rule = [rRules nextObject];
}
}

if ([self hasRecurrenceDates])
{
calc = [iCalRecurrenceCalculator
recurrenceCalculatorForRecurrenceRule: rule
withFirstInstanceCalendarDateRange: _r];
rdate = [[calc lastInstanceCalendarDateRange] startDate];
if (!rdate)
date = [_r startDate];
else if (!date || ([date compare: rdate] == NSOrderedAscending))
date = rdate;
else
rule = [rRules nextObject];
NSEnumerator *rDates;

rDates = [[self recurrenceDates] objectEnumerator];
while ((rdate = [rDates nextObject]))
{
if (!date || ([date compare: rdate] == NSOrderedAscending))
date = rdate;
}
}

return date;
Expand Down Expand Up @@ -401,6 +522,7 @@ - (NSCalendarDate *) firstRecurrenceStartDateWithEndDate: (NSCalendarDate *) end
firstInstanceCalendarDateRange: firstInstanceRange
recurrenceRules: rules
exceptionRules: nil
recurrenceDates: nil
exceptionDates: nil];
if ([recurrences count] > 0)
firstOccurrenceStartDate = [[recurrences objectAtIndex: 0]
Expand Down

0 comments on commit 7f99514

Please sign in to comment.