diff --git a/NEWS b/NEWS index d2e5a5268..1f35d9d73 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/SOPE/NGCards/iCalRecurrenceCalculator.h b/SOPE/NGCards/iCalRecurrenceCalculator.h index 18a937212..f11328496 100644 --- a/SOPE/NGCards/iCalRecurrenceCalculator.h +++ b/SOPE/NGCards/iCalRecurrenceCalculator.h @@ -46,6 +46,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir recurrenceRules: (NSArray *) _rRules exceptionRules: (NSArray *) _exRules + recurrenceDates: (NSArray *) _rDates exceptionDates: (NSArray *) _exDates; + (id) recurrenceCalculatorForRecurrenceRule: (iCalRecurrenceRule *) _rrule @@ -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; diff --git a/SOPE/NGCards/iCalRecurrenceCalculator.m b/SOPE/NGCards/iCalRecurrenceCalculator.m index cfccaaee1..7b4330467 100644 --- a/SOPE/NGCards/iCalRecurrenceCalculator.m +++ b/SOPE/NGCards/iCalRecurrenceCalculator.m @@ -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 withRules: (NSArray *) exrules withinRange: (NGCalendarDateRange *) limits @@ -159,8 +170,30 @@ static Class yearlyCalcClass = Nil; } + (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; @@ -178,7 +211,12 @@ static Class yearlyCalcClass = Nil; 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; @@ -217,16 +255,19 @@ static Class yearlyCalcClass = Nil; 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 diff --git a/SOPE/NGCards/iCalRepeatableEntityObject.h b/SOPE/NGCards/iCalRepeatableEntityObject.h index 52b2d3a93..3829055a3 100644 --- a/SOPE/NGCards/iCalRepeatableEntityObject.h +++ b/SOPE/NGCards/iCalRepeatableEntityObject.h @@ -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; diff --git a/SOPE/NGCards/iCalRepeatableEntityObject.m b/SOPE/NGCards/iCalRepeatableEntityObject.m index e85b7093d..acfa5c14a 100644 --- a/SOPE/NGCards/iCalRepeatableEntityObject.m +++ b/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. @@ -40,6 +40,8 @@ if ([classTag isEqualToString: @"RRULE"]) tagClass = [iCalRecurrenceRule class]; + else if ([classTag isEqualToString: @"RDATE"]) + tagClass = [iCalDateTime class]; else if ([classTag isEqualToString: @"EXDATE"]) tagClass = [iCalDateTime class]; else @@ -84,6 +86,108 @@ 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]]; @@ -284,7 +388,7 @@ - (BOOL) isRecurrent { - return [self hasRecurrenceRules]; + return [self hasRecurrenceRules] || [self hasRecurrenceDates]; } /* Matching */ @@ -303,10 +407,11 @@ 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]]; } @@ -315,27 +420,43 @@ 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]) { - 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 *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]) + { + NSEnumerator *rDates; + + rDates = [[self recurrenceDates] objectEnumerator]; + while ((rdate = [rDates nextObject])) + { + if (!date || ([date compare: rdate] == NSOrderedAscending)) + date = rdate; + } } return date; @@ -401,6 +522,7 @@ lastPossibleRecurrenceStartDateUsingFirstInstanceCalendarDateRange: (NGCalendarD firstInstanceCalendarDateRange: firstInstanceRange recurrenceRules: rules exceptionRules: nil + recurrenceDates: nil exceptionDates: nil]; if ([recurrences count] > 0) firstOccurrenceStartDate = [[recurrences objectAtIndex: 0] diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 0e59a6361..6c8d9f4d5 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -1216,10 +1216,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir { NSMutableDictionary *row, *fixedRow; - NSMutableArray *records; + NSMutableArray *records, *ranges; NSDictionary *cycleinfo; NGCalendarDateRange *firstRange, *recurrenceRange, *oneRange; - NSArray *rules, *exRules, *exDates, *ranges; + NSArray *rules, *exRules, *rDates, *exDates; NSArray *components; NSString *content; NSCalendarDate *checkStartDate, *checkEndDate, *firstStartDate, *firstEndDate; @@ -1268,6 +1268,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir } rules = [cycleinfo objectForKey: @"rules"]; exRules = [cycleinfo objectForKey: @"exRules"]; + rDates = [cycleinfo objectForKey: @"rDates"]; exDates = [cycleinfo objectForKey: @"exDates"]; eventTimeZone = nil; allDayTimeZone = nil; @@ -1333,8 +1334,9 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir tz = eventTimeZone ? eventTimeZone : allDayTimeZone; if (tz) { - // Adjust the exception dates + // Adjust the recurrence and exception dates exDates = [component exceptionDatesWithTimeZone: tz]; + rDates = [component recurrenceDatesWithTimeZone: tz]; // Adjust the recurrence rules "until" dates rules = [component recurrenceRulesWithTimeZone: tz]; @@ -1343,11 +1345,25 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir // Calculate the occurrences for the given range records = [NSMutableArray array]; - ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: recurrenceRange - firstInstanceCalendarDateRange: firstRange - recurrenceRules: rules - exceptionRules: exRules - exceptionDates: exDates]; + ranges = + [NSMutableArray arrayWithArray: + [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: recurrenceRange + firstInstanceCalendarDateRange: firstRange + 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]; for (count = 0; count < max; count++) { diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index 41d9bacce..e6170227f 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -328,6 +328,7 @@ [newOccurence autorelease]; [newOccurence removeAllRecurrenceRules]; [newOccurence removeAllExceptionRules]; + [newOccurence removeAllRecurrenceDates]; [newOccurence removeAllExceptionDates]; // It is important to set the organizer as some DAV clients (iCal diff --git a/SoObjects/Appointments/iCalCalendar+SOGo.m b/SoObjects/Appointments/iCalCalendar+SOGo.m index 9ac4342b8..0d1a2d7cd 100644 --- a/SoObjects/Appointments/iCalCalendar+SOGo.m +++ b/SoObjects/Appointments/iCalCalendar+SOGo.m @@ -49,7 +49,7 @@ /* master occurrence */ component = [components objectAtIndex: 0]; - if ([component hasRecurrenceRules] || [component recurrenceId]) + if ([component isRecurrent] || [component recurrenceId]) { // Skip the master event if required count = ([component recurrenceId] ? 0 : 1); diff --git a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m index cfe8e7647..19bb1a536 100644 --- a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m @@ -1,6 +1,6 @@ /* 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 * it under the terms of the GNU General Public License as published by @@ -30,6 +30,7 @@ #import +#import #import #import #import @@ -78,7 +79,7 @@ */ - (NSDictionary *) attributesInContext: (WOContext *) context { - NSArray *allComponents, *rules; + NSArray *allComponents, *rules, *dates; NSCalendarDate *untilDate; NSMutableDictionary *data, *repeat; NSString *frequency; @@ -86,6 +87,7 @@ SOGoUserDefaults *ud; iCalEvent *masterComponent; iCalRecurrenceRule *rule; + NSInteger i, count; data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]]; @@ -99,10 +101,12 @@ allComponents = [[self parent] todos]; masterComponent = [allComponents objectAtIndex: 0]; rules = [masterComponent recurrenceRules]; + dates = [masterComponent recurrenceDates]; } else { rules = [self recurrenceRules]; + dates = [self recurrenceDates]; } if ([rules count] > 0) @@ -129,7 +133,6 @@ { NSMutableArray *byMonthDay = [NSMutableArray arrayWithArray: [rule byMonthDay]]; NSMutableArray *byDay = [NSMutableArray array]; - NSInteger i, count; count = [byMonthDay count]; for (i = count - 1; i >= 0; i--) { @@ -154,6 +157,22 @@ [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; } @@ -166,8 +185,11 @@ { iCalRecurrenceRule *rule; iCalRecurrenceFrequency frequency; + NSArray *rdates; NSCalendarDate *date; SOGoUserDefaults *ud; + NSInteger i; + BOOL isAllDay; id repeat, o; [super setAttributes: data inContext: context]; @@ -177,6 +199,7 @@ return; repeat = [data objectForKey: @"repeat"]; + isAllDay = [[data objectForKey: @"isAllDay"] boolValue]; if ([repeat isKindOfClass: [NSDictionary class]]) { rule = [iCalRecurrenceRule new]; @@ -199,6 +222,26 @@ frequency = iCalRecurrenceFrequenceDaily; [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 { @@ -254,6 +297,10 @@ { [self removeAllRecurrenceRules]; } + else if ([self hasRecurrenceDates]) + { + [self removeAllRecurrenceDates]; + } } - (NSString *) cycleInfo @@ -275,6 +322,12 @@ if (rules) [cycleInfo setObject: rules forKey: @"exRules"]; + /* recurrence dates */ + rules = [self recurrenceDates]; + if ([rules count]) + [cycleInfo setObject: rules forKey: @"rDates"]; + + /* exception dates */ rules = [self exceptionDates]; if ([rules count]) [cycleInfo setObject: rules forKey: @"exDates"]; @@ -328,7 +381,7 @@ */ - (BOOL) doesOccurOnDate: (NSCalendarDate *) theOccurenceDate { - NSArray *ranges; + NSMutableArray *ranges; NGCalendarDateRange *checkRange, *firstRange; NSCalendarDate *startDate, *endDate; id firstStartDate, timeZone; @@ -352,11 +405,25 @@ endDate: endDate]; // Calculate the occurrences for the given date - ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: checkRange - firstInstanceCalendarDateRange: firstRange - recurrenceRules: [self recurrenceRulesWithTimeZone: timeZone] - exceptionRules: [self exceptionRulesWithTimeZone: timeZone] - exceptionDates: [self exceptionDatesWithTimeZone: timeZone]]; + ranges = + [NSMutableArray arrayWithArray: + [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: checkRange + firstInstanceCalendarDateRange: firstRange + 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]; } diff --git a/UI/Scheduler/English.lproj/Localizable.strings b/UI/Scheduler/English.lproj/Localizable.strings index 668fc8d3a..a56f28d55 100644 --- a/UI/Scheduler/English.lproj/Localizable.strings +++ b/UI/Scheduler/English.lproj/Localizable.strings @@ -571,6 +571,7 @@ vtodo_class2 = "(Confidential task)"; "More options" = "More options"; "Delete This Occurrence" = "Delete This Occurrence"; "Delete All Occurrences" = "Delete All Occurrences"; +"Add Recurrence Date" = "Add Recurrence Date"; "Add From" = "Add From"; "Add Due" = "Add Due"; "Import" = "Import"; diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index 7280ac391..82f038ba7 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -1,6 +1,6 @@ /* 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 * 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.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) {String[]} [repeat.dates] Recurrence dates (ISO8601) */ - (id ) viewAction { diff --git a/UI/Scheduler/UIxCalMainView.m b/UI/Scheduler/UIxCalMainView.m index 8742fa3fe..752e2b189 100644 --- a/UI/Scheduler/UIxCalMainView.m +++ b/UI/Scheduler/UIxCalMainView.m @@ -1,6 +1,6 @@ /* 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 * it under the terms of the GNU General Public License as published by @@ -476,6 +476,7 @@ @"weekly", @"monthly", @"yearly", + @"custom", nil]; [repeatItems retain]; } diff --git a/UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox b/UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox index cbeaf82e4..d1b5a5d49 100644 --- a/UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox +++ b/UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox @@ -179,7 +179,10 @@
- + diff --git a/UI/Templates/SchedulerUI/UIxRecurrenceEditor.wox b/UI/Templates/SchedulerUI/UIxRecurrenceEditor.wox index f8558fe91..178966298 100644 --- a/UI/Templates/SchedulerUI/UIxRecurrenceEditor.wox +++ b/UI/Templates/SchedulerUI/UIxRecurrenceEditor.wox @@ -144,4 +144,27 @@
+ +
+ +
+ + remove_circle + + + +
+
+ + add_circle + + +
+
diff --git a/UI/WebServerResources/js/Scheduler/ComponentController.js b/UI/WebServerResources/js/Scheduler/ComponentController.js index f7210fb82..088f23a96 100644 --- a/UI/WebServerResources/js/Scheduler/ComponentController.js +++ b/UI/WebServerResources/js/Scheduler/ComponentController.js @@ -222,6 +222,7 @@ vm.showAttendeesEditor = vm.component.attendees && vm.component.attendees.length; vm.toggleAttendeesEditor = toggleAttendeesEditor; //vm.searchText = null; + vm.changeFrequency = changeFrequency; vm.changeCalendar = changeCalendar; vm.cardFilter = cardFilter; vm.addAttendee = addAttendee; @@ -272,6 +273,11 @@ vm.component.repeat.month.type == 'bymonthday'; } + function changeFrequency() { + if (vm.component.repeat.frequency == 'custom') + vm.showRecurrenceEditor = true; + } + function changeCalendar() { var updateRequired = (vm.component.attendees && vm.component.attendees.length > 0); if (updateRequired)