Add support for events with recurrence dates
parent
e61a55f30e
commit
7f99514744
1
NEWS
1
NEWS
|
@ -10,6 +10,7 @@ New features
|
||||||
- [web] register SOGo as a handler for the mailto scheme (#1223)
|
- [web] register SOGo as a handler for the mailto scheme (#1223)
|
||||||
- [web] new events list view where events are grouped by day
|
- [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] user setting to always show mail editor inside current window or in popup window
|
||||||
|
- [web] add support for events with recurrence dates (RDATE)
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
- [web] follow requested URL after user authentication
|
- [web] follow requested URL after user authentication
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir
|
firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir
|
||||||
recurrenceRules: (NSArray *) _rRules
|
recurrenceRules: (NSArray *) _rRules
|
||||||
exceptionRules: (NSArray *) _exRules
|
exceptionRules: (NSArray *) _exRules
|
||||||
|
recurrenceDates: (NSArray *) _rDates
|
||||||
exceptionDates: (NSArray *) _exDates;
|
exceptionDates: (NSArray *) _exDates;
|
||||||
|
|
||||||
+ (id) recurrenceCalculatorForRecurrenceRule: (iCalRecurrenceRule *) _rrule
|
+ (id) recurrenceCalculatorForRecurrenceRule: (iCalRecurrenceRule *) _rrule
|
||||||
|
@ -54,8 +55,7 @@
|
||||||
- (id) initWithRecurrenceRule: (iCalRecurrenceRule *) _rrule
|
- (id) initWithRecurrenceRule: (iCalRecurrenceRule *) _rrule
|
||||||
firstInstanceCalendarDateRange: (NGCalendarDateRange *) _range;
|
firstInstanceCalendarDateRange: (NGCalendarDateRange *) _range;
|
||||||
|
|
||||||
- (NSArray *)
|
- (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *)_r;
|
||||||
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *)_r;
|
|
||||||
- (BOOL) doesRecurrWithinCalendarDateRange: (NGCalendarDateRange *) _range;
|
- (BOOL) doesRecurrWithinCalendarDateRange: (NGCalendarDateRange *) _range;
|
||||||
|
|
||||||
- (NGCalendarDateRange *) firstInstanceCalendarDateRange;
|
- (NGCalendarDateRange *) firstInstanceCalendarDateRange;
|
||||||
|
|
|
@ -134,6 +134,17 @@ static Class yearlyCalcClass = Nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (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
|
+ (void) _removeExceptionsFromRanges: (NSMutableArray *) ranges
|
||||||
withRules: (NSArray *) exrules
|
withRules: (NSArray *) exrules
|
||||||
withinRange: (NGCalendarDateRange *) limits
|
withinRange: (NGCalendarDateRange *) limits
|
||||||
|
@ -159,8 +170,30 @@ static Class yearlyCalcClass = Nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSArray *) _dates: (NSArray *) dateList
|
+ (NSArray *) _dates: (NSArray *) dateList
|
||||||
withinRange: (NGCalendarDateRange *) limits
|
withinRange: (NGCalendarDateRange *) limits
|
||||||
startingWithDate: (NGCalendarDateRange *) first
|
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;
|
NSMutableArray *newDates;
|
||||||
NSEnumerator *dates;
|
NSEnumerator *dates;
|
||||||
|
@ -178,7 +211,12 @@ static Class yearlyCalcClass = Nil;
|
||||||
currentRange = [NGCalendarDateRange calendarDateRangeWithStartDate: currentDate
|
currentRange = [NGCalendarDateRange calendarDateRangeWithStartDate: currentDate
|
||||||
endDate: [currentDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: [first duration]]];
|
endDate: [currentDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: [first duration]]];
|
||||||
if ([limits doesIntersectWithDateRange: currentRange])
|
if ([limits doesIntersectWithDateRange: currentRange])
|
||||||
[newDates addObject: currentDate];
|
{
|
||||||
|
if (returnRanges)
|
||||||
|
[newDates addObject: currentRange];
|
||||||
|
else
|
||||||
|
[newDates addObject: currentDate];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newDates;
|
return newDates;
|
||||||
|
@ -217,16 +255,19 @@ static Class yearlyCalcClass = Nil;
|
||||||
firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir
|
firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir
|
||||||
recurrenceRules: (NSArray *) _rRules
|
recurrenceRules: (NSArray *) _rRules
|
||||||
exceptionRules: (NSArray *) _exRules
|
exceptionRules: (NSArray *) _exRules
|
||||||
|
recurrenceDates: (NSArray *) _rDates
|
||||||
exceptionDates: (NSArray *) _exDates
|
exceptionDates: (NSArray *) _exDates
|
||||||
{
|
{
|
||||||
NSMutableArray *ranges;
|
NSMutableArray *ranges;
|
||||||
|
|
||||||
ranges = [NSMutableArray arrayWithCapacity: 64];
|
ranges = [NSMutableArray arrayWithCapacity: 64];
|
||||||
|
|
||||||
if ([_rRules count] > 0)
|
if ([_rRules count] > 0 || [_rDates count] > 0)
|
||||||
{
|
{
|
||||||
[self _fillRanges: ranges fromRules: _rRules
|
[self _fillRanges: ranges fromRules: _rRules
|
||||||
withinRange: _r startingWithDate: _fir];
|
withinRange: _r startingWithDate: _fir];
|
||||||
|
[self _fillRanges: ranges fromDates: _rDates
|
||||||
|
withinRange: _r startingWithDate: _fir];
|
||||||
[self _removeExceptionsFromRanges: ranges withRules: _exRules
|
[self _removeExceptionsFromRanges: ranges withRules: _exRules
|
||||||
withinRange: _r startingWithDate: _fir];
|
withinRange: _r startingWithDate: _fir];
|
||||||
[self _removeExceptionDatesFromRanges: ranges withDates: _exDates
|
[self _removeExceptionDatesFromRanges: ranges withDates: _exDates
|
||||||
|
|
|
@ -44,6 +44,12 @@
|
||||||
- (NSArray *)recurrenceRules;
|
- (NSArray *)recurrenceRules;
|
||||||
- (NSArray *)recurrenceRulesWithTimeZone: (id) timezone;
|
- (NSArray *)recurrenceRulesWithTimeZone: (id) timezone;
|
||||||
|
|
||||||
|
- (void) removeAllRecurrenceDates;
|
||||||
|
- (void) addToRecurrenceDates: (NSCalendarDate *) _rdate;
|
||||||
|
- (BOOL) hasRecurrenceDates;
|
||||||
|
- (NSArray *) recurrenceDates;
|
||||||
|
- (NSArray *) recurrenceDatesWithTimeZone: (id) theTimeZone;
|
||||||
|
|
||||||
- (void)removeAllExceptionRules;
|
- (void)removeAllExceptionRules;
|
||||||
- (void)addToExceptionRules:(id)_rrule;
|
- (void)addToExceptionRules:(id)_rrule;
|
||||||
- (BOOL)hasExceptionRules;
|
- (BOOL)hasExceptionRules;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||||
Copyright (C) 2012 Inverse inc.
|
Copyright (C) 2017 Inverse inc.
|
||||||
|
|
||||||
This file is part of SOPE.
|
This file is part of SOPE.
|
||||||
|
|
||||||
|
@ -40,6 +40,8 @@
|
||||||
|
|
||||||
if ([classTag isEqualToString: @"RRULE"])
|
if ([classTag isEqualToString: @"RRULE"])
|
||||||
tagClass = [iCalRecurrenceRule class];
|
tagClass = [iCalRecurrenceRule class];
|
||||||
|
else if ([classTag isEqualToString: @"RDATE"])
|
||||||
|
tagClass = [iCalDateTime class];
|
||||||
else if ([classTag isEqualToString: @"EXDATE"])
|
else if ([classTag isEqualToString: @"EXDATE"])
|
||||||
tagClass = [iCalDateTime class];
|
tagClass = [iCalDateTime class];
|
||||||
else
|
else
|
||||||
|
@ -84,6 +86,108 @@
|
||||||
return [self rules: rules withTimeZone: 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
|
- (void) removeAllExceptionRules
|
||||||
{
|
{
|
||||||
[self removeChildren: [self exceptionRules]];
|
[self removeChildren: [self exceptionRules]];
|
||||||
|
@ -284,7 +388,7 @@
|
||||||
|
|
||||||
- (BOOL) isRecurrent
|
- (BOOL) isRecurrent
|
||||||
{
|
{
|
||||||
return [self hasRecurrenceRules];
|
return [self hasRecurrenceRules] || [self hasRecurrenceDates];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Matching */
|
/* Matching */
|
||||||
|
@ -303,10 +407,11 @@
|
||||||
firstInstanceCalendarDateRange: (NGCalendarDateRange *)_fir
|
firstInstanceCalendarDateRange: (NGCalendarDateRange *)_fir
|
||||||
{
|
{
|
||||||
return [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: _r
|
return [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: _r
|
||||||
firstInstanceCalendarDateRange: _fir
|
firstInstanceCalendarDateRange: _fir
|
||||||
recurrenceRules: [self recurrenceRules]
|
recurrenceRules: [self recurrenceRules]
|
||||||
exceptionRules: [self exceptionRules]
|
exceptionRules: [self exceptionRules]
|
||||||
exceptionDates: [self exceptionDates]];
|
recurrenceDates: [self recurrenceDates]
|
||||||
|
exceptionDates: [self exceptionDates]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -315,27 +420,43 @@
|
||||||
lastPossibleRecurrenceStartDateUsingFirstInstanceCalendarDateRange: (NGCalendarDateRange *)_r
|
lastPossibleRecurrenceStartDateUsingFirstInstanceCalendarDateRange: (NGCalendarDateRange *)_r
|
||||||
{
|
{
|
||||||
NSCalendarDate *date;
|
NSCalendarDate *date;
|
||||||
NSEnumerator *rRules;
|
|
||||||
iCalRecurrenceRule *rule;
|
|
||||||
iCalRecurrenceCalculator *calc;
|
|
||||||
NSCalendarDate *rdate;
|
NSCalendarDate *rdate;
|
||||||
|
|
||||||
date = nil;
|
date = nil;
|
||||||
|
|
||||||
rRules = [[self recurrenceRules] objectEnumerator];
|
if ([self hasRecurrenceRules])
|
||||||
rule = [rRules nextObject];
|
|
||||||
while (rule && ![rule isInfinite] && !date)
|
|
||||||
{
|
{
|
||||||
calc = [iCalRecurrenceCalculator
|
NSEnumerator *rRules;
|
||||||
recurrenceCalculatorForRecurrenceRule: rule
|
iCalRecurrenceRule *rule;
|
||||||
withFirstInstanceCalendarDateRange: _r];
|
iCalRecurrenceCalculator *calc;
|
||||||
rdate = [[calc lastInstanceCalendarDateRange] startDate];
|
|
||||||
if (!rdate)
|
rRules = [[self recurrenceRules] objectEnumerator];
|
||||||
date = [_r startDate];
|
rule = [rRules nextObject];
|
||||||
else if (!date || ([date compare: rdate] == NSOrderedAscending))
|
while (rule && ![rule isInfinite] && !date)
|
||||||
date = rdate;
|
{
|
||||||
else
|
calc = [iCalRecurrenceCalculator
|
||||||
rule = [rRules nextObject];
|
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])
|
||||||
|
{
|
||||||
|
NSEnumerator *rDates;
|
||||||
|
|
||||||
|
rDates = [[self recurrenceDates] objectEnumerator];
|
||||||
|
while ((rdate = [rDates nextObject]))
|
||||||
|
{
|
||||||
|
if (!date || ([date compare: rdate] == NSOrderedAscending))
|
||||||
|
date = rdate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return date;
|
return date;
|
||||||
|
@ -401,6 +522,7 @@ lastPossibleRecurrenceStartDateUsingFirstInstanceCalendarDateRange: (NGCalendarD
|
||||||
firstInstanceCalendarDateRange: firstInstanceRange
|
firstInstanceCalendarDateRange: firstInstanceRange
|
||||||
recurrenceRules: rules
|
recurrenceRules: rules
|
||||||
exceptionRules: nil
|
exceptionRules: nil
|
||||||
|
recurrenceDates: nil
|
||||||
exceptionDates: nil];
|
exceptionDates: nil];
|
||||||
if ([recurrences count] > 0)
|
if ([recurrences count] > 0)
|
||||||
firstOccurrenceStartDate = [[recurrences objectAtIndex: 0]
|
firstOccurrenceStartDate = [[recurrences objectAtIndex: 0]
|
||||||
|
|
|
@ -1216,10 +1216,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||||
|
|
||||||
{
|
{
|
||||||
NSMutableDictionary *row, *fixedRow;
|
NSMutableDictionary *row, *fixedRow;
|
||||||
NSMutableArray *records;
|
NSMutableArray *records, *ranges;
|
||||||
NSDictionary *cycleinfo;
|
NSDictionary *cycleinfo;
|
||||||
NGCalendarDateRange *firstRange, *recurrenceRange, *oneRange;
|
NGCalendarDateRange *firstRange, *recurrenceRange, *oneRange;
|
||||||
NSArray *rules, *exRules, *exDates, *ranges;
|
NSArray *rules, *exRules, *rDates, *exDates;
|
||||||
NSArray *components;
|
NSArray *components;
|
||||||
NSString *content;
|
NSString *content;
|
||||||
NSCalendarDate *checkStartDate, *checkEndDate, *firstStartDate, *firstEndDate;
|
NSCalendarDate *checkStartDate, *checkEndDate, *firstStartDate, *firstEndDate;
|
||||||
|
@ -1268,6 +1268,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||||
}
|
}
|
||||||
rules = [cycleinfo objectForKey: @"rules"];
|
rules = [cycleinfo objectForKey: @"rules"];
|
||||||
exRules = [cycleinfo objectForKey: @"exRules"];
|
exRules = [cycleinfo objectForKey: @"exRules"];
|
||||||
|
rDates = [cycleinfo objectForKey: @"rDates"];
|
||||||
exDates = [cycleinfo objectForKey: @"exDates"];
|
exDates = [cycleinfo objectForKey: @"exDates"];
|
||||||
eventTimeZone = nil;
|
eventTimeZone = nil;
|
||||||
allDayTimeZone = nil;
|
allDayTimeZone = nil;
|
||||||
|
@ -1333,8 +1334,9 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||||
tz = eventTimeZone ? eventTimeZone : allDayTimeZone;
|
tz = eventTimeZone ? eventTimeZone : allDayTimeZone;
|
||||||
if (tz)
|
if (tz)
|
||||||
{
|
{
|
||||||
// Adjust the exception dates
|
// Adjust the recurrence and exception dates
|
||||||
exDates = [component exceptionDatesWithTimeZone: tz];
|
exDates = [component exceptionDatesWithTimeZone: tz];
|
||||||
|
rDates = [component recurrenceDatesWithTimeZone: tz];
|
||||||
|
|
||||||
// Adjust the recurrence rules "until" dates
|
// Adjust the recurrence rules "until" dates
|
||||||
rules = [component recurrenceRulesWithTimeZone: tz];
|
rules = [component recurrenceRulesWithTimeZone: tz];
|
||||||
|
@ -1343,11 +1345,25 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||||
|
|
||||||
// Calculate the occurrences for the given range
|
// Calculate the occurrences for the given range
|
||||||
records = [NSMutableArray array];
|
records = [NSMutableArray array];
|
||||||
ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: recurrenceRange
|
ranges =
|
||||||
firstInstanceCalendarDateRange: firstRange
|
[NSMutableArray arrayWithArray:
|
||||||
recurrenceRules: rules
|
[iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: recurrenceRange
|
||||||
exceptionRules: exRules
|
firstInstanceCalendarDateRange: firstRange
|
||||||
exceptionDates: exDates];
|
recurrenceRules: rules
|
||||||
|
exceptionRules: exRules
|
||||||
|
recurrenceDates: rDates
|
||||||
|
exceptionDates: exDates]];
|
||||||
|
|
||||||
|
// Add the master occurrence when dealing with RDATES.
|
||||||
|
// However, the master event must not be flagged with X-MOZ-FAKED-MASTER.
|
||||||
|
if ([component hasRecurrenceDates] &&
|
||||||
|
![[[component uniqueChildWithTag: @"x-moz-faked-master"]
|
||||||
|
flattenedValuesForKey: @""] isEqualToString: @"1"] &&
|
||||||
|
[recurrenceRange doesIntersectWithDateRange: firstRange])
|
||||||
|
{
|
||||||
|
[ranges insertObject: firstRange atIndex: 0];
|
||||||
|
}
|
||||||
|
|
||||||
max = [ranges count];
|
max = [ranges count];
|
||||||
for (count = 0; count < max; count++)
|
for (count = 0; count < max; count++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -328,6 +328,7 @@
|
||||||
[newOccurence autorelease];
|
[newOccurence autorelease];
|
||||||
[newOccurence removeAllRecurrenceRules];
|
[newOccurence removeAllRecurrenceRules];
|
||||||
[newOccurence removeAllExceptionRules];
|
[newOccurence removeAllExceptionRules];
|
||||||
|
[newOccurence removeAllRecurrenceDates];
|
||||||
[newOccurence removeAllExceptionDates];
|
[newOccurence removeAllExceptionDates];
|
||||||
|
|
||||||
// It is important to set the organizer as some DAV clients (iCal
|
// It is important to set the organizer as some DAV clients (iCal
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
/* master occurrence */
|
/* master occurrence */
|
||||||
component = [components objectAtIndex: 0];
|
component = [components objectAtIndex: 0];
|
||||||
|
|
||||||
if ([component hasRecurrenceRules] || [component recurrenceId])
|
if ([component isRecurrent] || [component recurrenceId])
|
||||||
{
|
{
|
||||||
// Skip the master event if required
|
// Skip the master event if required
|
||||||
count = ([component recurrenceId] ? 0 : 1);
|
count = ([component recurrenceId] ? 0 : 1);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* iCalRepeatableEntityObject+SOGo.m - this file is part of SOGo
|
/* iCalRepeatableEntityObject+SOGo.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2007-2016 Inverse inc.
|
* Copyright (C) 2007-2017 Inverse inc.
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* This file is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#import <NGExtensions/NGCalendarDateRange.h>
|
#import <NGExtensions/NGCalendarDateRange.h>
|
||||||
|
|
||||||
|
#import <SoObjects/SOGo/CardElement+SOGo.h>
|
||||||
#import <SoObjects/SOGo/SOGoUser.h>
|
#import <SoObjects/SOGo/SOGoUser.h>
|
||||||
#import <SoObjects/SOGo/SOGoUserDefaults.h>
|
#import <SoObjects/SOGo/SOGoUserDefaults.h>
|
||||||
#import <SoObjects/SOGo/WOContext+SOGo.h>
|
#import <SoObjects/SOGo/WOContext+SOGo.h>
|
||||||
|
@ -78,7 +79,7 @@
|
||||||
*/
|
*/
|
||||||
- (NSDictionary *) attributesInContext: (WOContext *) context
|
- (NSDictionary *) attributesInContext: (WOContext *) context
|
||||||
{
|
{
|
||||||
NSArray *allComponents, *rules;
|
NSArray *allComponents, *rules, *dates;
|
||||||
NSCalendarDate *untilDate;
|
NSCalendarDate *untilDate;
|
||||||
NSMutableDictionary *data, *repeat;
|
NSMutableDictionary *data, *repeat;
|
||||||
NSString *frequency;
|
NSString *frequency;
|
||||||
|
@ -86,6 +87,7 @@
|
||||||
SOGoUserDefaults *ud;
|
SOGoUserDefaults *ud;
|
||||||
iCalEvent *masterComponent;
|
iCalEvent *masterComponent;
|
||||||
iCalRecurrenceRule *rule;
|
iCalRecurrenceRule *rule;
|
||||||
|
NSInteger i, count;
|
||||||
|
|
||||||
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
|
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
|
||||||
|
|
||||||
|
@ -99,10 +101,12 @@
|
||||||
allComponents = [[self parent] todos];
|
allComponents = [[self parent] todos];
|
||||||
masterComponent = [allComponents objectAtIndex: 0];
|
masterComponent = [allComponents objectAtIndex: 0];
|
||||||
rules = [masterComponent recurrenceRules];
|
rules = [masterComponent recurrenceRules];
|
||||||
|
dates = [masterComponent recurrenceDates];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rules = [self recurrenceRules];
|
rules = [self recurrenceRules];
|
||||||
|
dates = [self recurrenceDates];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([rules count] > 0)
|
if ([rules count] > 0)
|
||||||
|
@ -129,7 +133,6 @@
|
||||||
{
|
{
|
||||||
NSMutableArray *byMonthDay = [NSMutableArray arrayWithArray: [rule byMonthDay]];
|
NSMutableArray *byMonthDay = [NSMutableArray arrayWithArray: [rule byMonthDay]];
|
||||||
NSMutableArray *byDay = [NSMutableArray array];
|
NSMutableArray *byDay = [NSMutableArray array];
|
||||||
NSInteger i, count;
|
|
||||||
count = [byMonthDay count];
|
count = [byMonthDay count];
|
||||||
for (i = count - 1; i >= 0; i--)
|
for (i = count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
|
@ -154,6 +157,22 @@
|
||||||
[data setObject: repeat forKey: @"repeat"];
|
[data setObject: repeat forKey: @"repeat"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ([dates count] > 0)
|
||||||
|
{
|
||||||
|
NSMutableArray *rDates = [NSMutableArray array];
|
||||||
|
NSCalendarDate *rDate;
|
||||||
|
ud = [[context activeUser] userDefaults];
|
||||||
|
timeZone = [ud timeZone];
|
||||||
|
count = [dates count];
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
rDate = [dates objectAtIndex: i];
|
||||||
|
[rDate setTimeZone: timeZone];
|
||||||
|
[rDates addObject: [rDate iso8601DateString]];
|
||||||
|
}
|
||||||
|
[data setObject: [NSDictionary dictionaryWithObject: rDates forKey: @"dates"] forKey: @"repeat"];
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,8 +185,11 @@
|
||||||
{
|
{
|
||||||
iCalRecurrenceRule *rule;
|
iCalRecurrenceRule *rule;
|
||||||
iCalRecurrenceFrequency frequency;
|
iCalRecurrenceFrequency frequency;
|
||||||
|
NSArray *rdates;
|
||||||
NSCalendarDate *date;
|
NSCalendarDate *date;
|
||||||
SOGoUserDefaults *ud;
|
SOGoUserDefaults *ud;
|
||||||
|
NSInteger i;
|
||||||
|
BOOL isAllDay;
|
||||||
id repeat, o;
|
id repeat, o;
|
||||||
|
|
||||||
[super setAttributes: data inContext: context];
|
[super setAttributes: data inContext: context];
|
||||||
|
@ -177,6 +199,7 @@
|
||||||
return;
|
return;
|
||||||
|
|
||||||
repeat = [data objectForKey: @"repeat"];
|
repeat = [data objectForKey: @"repeat"];
|
||||||
|
isAllDay = [[data objectForKey: @"isAllDay"] boolValue];
|
||||||
if ([repeat isKindOfClass: [NSDictionary class]])
|
if ([repeat isKindOfClass: [NSDictionary class]])
|
||||||
{
|
{
|
||||||
rule = [iCalRecurrenceRule new];
|
rule = [iCalRecurrenceRule new];
|
||||||
|
@ -199,6 +222,26 @@
|
||||||
frequency = iCalRecurrenceFrequenceDaily;
|
frequency = iCalRecurrenceFrequenceDaily;
|
||||||
[rule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]];
|
[rule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]];
|
||||||
}
|
}
|
||||||
|
else if ([o caseInsensitiveCompare: @"CUSTOM"] == NSOrderedSame)
|
||||||
|
{
|
||||||
|
[self removeAllRecurrenceDates];
|
||||||
|
o = [repeat objectForKey: @"dates"];
|
||||||
|
if ([o isKindOfClass: [NSArray class]])
|
||||||
|
{
|
||||||
|
rdates = o;
|
||||||
|
for (i = 0; i < [rdates count]; i++)
|
||||||
|
{
|
||||||
|
o = [rdates objectAtIndex: i];
|
||||||
|
if ([o isKindOfClass: [NSDictionary class]])
|
||||||
|
{
|
||||||
|
date = [self dateFromString: [o objectForKey: @"date"] inContext: context];
|
||||||
|
if (!isAllDay)
|
||||||
|
[self adjustDate: &date withTimeString: [o objectForKey: @"time"] inContext: context];
|
||||||
|
[self addToRecurrenceDates: date];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -254,6 +297,10 @@
|
||||||
{
|
{
|
||||||
[self removeAllRecurrenceRules];
|
[self removeAllRecurrenceRules];
|
||||||
}
|
}
|
||||||
|
else if ([self hasRecurrenceDates])
|
||||||
|
{
|
||||||
|
[self removeAllRecurrenceDates];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *) cycleInfo
|
- (NSString *) cycleInfo
|
||||||
|
@ -275,6 +322,12 @@
|
||||||
if (rules)
|
if (rules)
|
||||||
[cycleInfo setObject: rules forKey: @"exRules"];
|
[cycleInfo setObject: rules forKey: @"exRules"];
|
||||||
|
|
||||||
|
/* recurrence dates */
|
||||||
|
rules = [self recurrenceDates];
|
||||||
|
if ([rules count])
|
||||||
|
[cycleInfo setObject: rules forKey: @"rDates"];
|
||||||
|
|
||||||
|
/* exception dates */
|
||||||
rules = [self exceptionDates];
|
rules = [self exceptionDates];
|
||||||
if ([rules count])
|
if ([rules count])
|
||||||
[cycleInfo setObject: rules forKey: @"exDates"];
|
[cycleInfo setObject: rules forKey: @"exDates"];
|
||||||
|
@ -328,7 +381,7 @@
|
||||||
*/
|
*/
|
||||||
- (BOOL) doesOccurOnDate: (NSCalendarDate *) theOccurenceDate
|
- (BOOL) doesOccurOnDate: (NSCalendarDate *) theOccurenceDate
|
||||||
{
|
{
|
||||||
NSArray *ranges;
|
NSMutableArray *ranges;
|
||||||
NGCalendarDateRange *checkRange, *firstRange;
|
NGCalendarDateRange *checkRange, *firstRange;
|
||||||
NSCalendarDate *startDate, *endDate;
|
NSCalendarDate *startDate, *endDate;
|
||||||
id firstStartDate, timeZone;
|
id firstStartDate, timeZone;
|
||||||
|
@ -352,11 +405,25 @@
|
||||||
endDate: endDate];
|
endDate: endDate];
|
||||||
|
|
||||||
// Calculate the occurrences for the given date
|
// Calculate the occurrences for the given date
|
||||||
ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: checkRange
|
ranges =
|
||||||
firstInstanceCalendarDateRange: firstRange
|
[NSMutableArray arrayWithArray:
|
||||||
recurrenceRules: [self recurrenceRulesWithTimeZone: timeZone]
|
[iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: checkRange
|
||||||
exceptionRules: [self exceptionRulesWithTimeZone: timeZone]
|
firstInstanceCalendarDateRange: firstRange
|
||||||
exceptionDates: [self exceptionDatesWithTimeZone: timeZone]];
|
recurrenceRules: [self recurrenceRulesWithTimeZone: timeZone]
|
||||||
|
exceptionRules: [self exceptionRulesWithTimeZone: timeZone]
|
||||||
|
recurrenceDates: [self recurrenceDatesWithTimeZone: timeZone]
|
||||||
|
exceptionDates: [self exceptionDatesWithTimeZone: timeZone]]];
|
||||||
|
|
||||||
|
// Add the master occurrence when dealing with RDATES.
|
||||||
|
// However, the master event must not be flagged with X-MOZ-FAKED-MASTER.
|
||||||
|
if ([self hasRecurrenceDates] &&
|
||||||
|
![[[self uniqueChildWithTag: @"x-moz-faked-master"]
|
||||||
|
flattenedValuesForKey: @""] isEqualToString: @"1"] &&
|
||||||
|
[checkRange doesIntersectWithDateRange: firstRange])
|
||||||
|
{
|
||||||
|
[ranges insertObject: firstRange atIndex: 0];
|
||||||
|
}
|
||||||
|
|
||||||
doesOccur = [ranges dateRangeArrayContainsDate: startDate];
|
doesOccur = [ranges dateRangeArrayContainsDate: startDate];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -571,6 +571,7 @@ vtodo_class2 = "(Confidential task)";
|
||||||
"More options" = "More options";
|
"More options" = "More options";
|
||||||
"Delete This Occurrence" = "Delete This Occurrence";
|
"Delete This Occurrence" = "Delete This Occurrence";
|
||||||
"Delete All Occurrences" = "Delete All Occurrences";
|
"Delete All Occurrences" = "Delete All Occurrences";
|
||||||
|
"Add Recurrence Date" = "Add Recurrence Date";
|
||||||
"Add From" = "Add From";
|
"Add From" = "Add From";
|
||||||
"Add Due" = "Add Due";
|
"Add Due" = "Add Due";
|
||||||
"Import" = "Import";
|
"Import" = "Import";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* UIxAppointmentEditor.m - this file is part of SOGo
|
/* UIxAppointmentEditor.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2007-2016 Inverse inc.
|
* Copyright (C) 2007-2017 Inverse inc.
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* This file is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -650,6 +650,7 @@
|
||||||
* @apiSuccess (Success 200) {Number} [repeat.days.occurence] Occurrence of a specific day within the monthly or yearly rule (values are -5 to 5)
|
* @apiSuccess (Success 200) {Number} [repeat.days.occurence] Occurrence of a specific day within the monthly or yearly rule (values are -5 to 5)
|
||||||
* @apiSuccess (Success 200) {Number[]} [repeat.months] List of months of the year (values are 1 to 12)
|
* @apiSuccess (Success 200) {Number[]} [repeat.months] List of months of the year (values are 1 to 12)
|
||||||
* @apiSuccess (Success 200) {Number[]} [repeat.monthdays] Days of the month (values are 1 to 31)
|
* @apiSuccess (Success 200) {Number[]} [repeat.monthdays] Days of the month (values are 1 to 31)
|
||||||
|
* @apiSuccess (Success 200) {String[]} [repeat.dates] Recurrence dates (ISO8601)
|
||||||
*/
|
*/
|
||||||
- (id <WOActionResults>) viewAction
|
- (id <WOActionResults>) viewAction
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* UIxCalMainView.m - this file is part of SOGo
|
/* UIxCalMainView.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2006-2016 Inverse inc.
|
* Copyright (C) 2006-2017 Inverse inc.
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* This file is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -476,6 +476,7 @@
|
||||||
@"weekly",
|
@"weekly",
|
||||||
@"monthly",
|
@"monthly",
|
||||||
@"yearly",
|
@"yearly",
|
||||||
|
@"custom",
|
||||||
nil];
|
nil];
|
||||||
[repeatItems retain];
|
[repeatItems retain];
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,7 +179,10 @@
|
||||||
<div layout="row" layout-align="start center">
|
<div layout="row" layout-align="start center">
|
||||||
<md-input-container class="md-block md-flex">
|
<md-input-container class="md-block md-flex">
|
||||||
<label><var:string label:value="Repeat"/></label>
|
<label><var:string label:value="Repeat"/></label>
|
||||||
<md-select ng-model="editor.component.repeat.frequency" ng-disabled="editor.component.occurrenceId">
|
<md-select
|
||||||
|
ng-model="editor.component.repeat.frequency"
|
||||||
|
ng-change="editor.changeFrequency($event)"
|
||||||
|
ng-disabled="editor.component.occurrenceId">
|
||||||
<var:foreach list="repeatList" item="item">
|
<var:foreach list="repeatList" item="item">
|
||||||
<md-option var:value="item"><var:string var:value="itemRepeatText"/></md-option>
|
<md-option var:value="item"><var:string var:value="itemRepeatText"/></md-option>
|
||||||
</var:foreach>
|
</var:foreach>
|
||||||
|
|
|
@ -144,4 +144,27 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- custom (rdates) -->
|
||||||
|
<div ng-if="editor.component.repeat.frequency == 'custom'">
|
||||||
|
<label class="button-label"><var:string label:value="On"/></label>
|
||||||
|
<div layout="row" layout-align="start end" ng-repeat="rdate in editor.component.repeat.dates">
|
||||||
|
<md-button class="md-icon-button" type="button" ng-click="editor.component.$deleteRecurrenceDate($index)">
|
||||||
|
<md-icon>remove_circle</md-icon>
|
||||||
|
</md-button>
|
||||||
|
<md-datepicker
|
||||||
|
ng-model="editor.component.repeat.dates[$index]"
|
||||||
|
label:md-placeholder="On"><!-- date picker --></md-datepicker>
|
||||||
|
<sg-timepicker
|
||||||
|
ng-model="editor.component.repeat.dates[$index]"
|
||||||
|
ng-hide="editor.component.isAllDay"><!-- time picker --></sg-timepicker>
|
||||||
|
</div>
|
||||||
|
<div layout="row" layout-align="start center">
|
||||||
|
<md-button class="md-icon-button" type="button" ng-click="editor.component.$addRecurrenceDate()">
|
||||||
|
<md-icon>add_circle</md-icon>
|
||||||
|
</md-button>
|
||||||
|
<label class="button-label">
|
||||||
|
<var:string label:value="Add Recurrence Date"/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</container>
|
</container>
|
||||||
|
|
|
@ -222,6 +222,7 @@
|
||||||
vm.showAttendeesEditor = vm.component.attendees && vm.component.attendees.length;
|
vm.showAttendeesEditor = vm.component.attendees && vm.component.attendees.length;
|
||||||
vm.toggleAttendeesEditor = toggleAttendeesEditor;
|
vm.toggleAttendeesEditor = toggleAttendeesEditor;
|
||||||
//vm.searchText = null;
|
//vm.searchText = null;
|
||||||
|
vm.changeFrequency = changeFrequency;
|
||||||
vm.changeCalendar = changeCalendar;
|
vm.changeCalendar = changeCalendar;
|
||||||
vm.cardFilter = cardFilter;
|
vm.cardFilter = cardFilter;
|
||||||
vm.addAttendee = addAttendee;
|
vm.addAttendee = addAttendee;
|
||||||
|
@ -272,6 +273,11 @@
|
||||||
vm.component.repeat.month.type == 'bymonthday';
|
vm.component.repeat.month.type == 'bymonthday';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function changeFrequency() {
|
||||||
|
if (vm.component.repeat.frequency == 'custom')
|
||||||
|
vm.showRecurrenceEditor = true;
|
||||||
|
}
|
||||||
|
|
||||||
function changeCalendar() {
|
function changeCalendar() {
|
||||||
var updateRequired = (vm.component.attendees && vm.component.attendees.length > 0);
|
var updateRequired = (vm.component.attendees && vm.component.attendees.length > 0);
|
||||||
if (updateRequired)
|
if (updateRequired)
|
||||||
|
|
Loading…
Reference in New Issue