From 6b3d99424a59e2e1ab5f587c3d735f87028275f7 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Tue, 29 Mar 2011 23:25:40 +0000 Subject: [PATCH] See ChangeLog. Monotone-Parent: 5768aec2a733241a4cb94db69e98b57603adb638 Monotone-Revision: 553f303f1f7a6910069af8e49e3926e86bece4b7 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2011-03-29T23:25:40 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 20 ++++- .../Appointments/SOGoAppointmentFolder.m | 77 ++++++++++--------- .../Appointments/SOGoAppointmentObject.m | 17 ++-- .../Appointments/SOGoCalendarComponent.h | 4 +- .../Appointments/SOGoCalendarComponent.m | 9 ++- SoObjects/Appointments/iCalEvent+SOGo.h | 4 +- SoObjects/Appointments/iCalEvent+SOGo.m | 53 +++++++++++++ .../iCalRepeatableEntityObject+SOGo.m | 6 +- 8 files changed, 135 insertions(+), 55 deletions(-) diff --git a/ChangeLog b/ChangeLog index a1aec3963..e0db75c02 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2011-03-29 francis + + * SoObjects/Appointments/iCalEvent+SOGo.m + (-updateRecurrenceRulesUntilDate:): shifts the "until dates" of + the recurrence rules of the event with respect to the previous end + date of the event. + + * SoObjects/Appointments/SOGoCalendarComponent.m + (-updateComponent: ): splitted method "saveComponent:" to avoid + saving the component multiple times during the same session. + + * SoObjects/Appointments/SOGoAppointmentObject.m (-saveComponent): + make use of the new "updateComponent" method mentioned above. + + * SoObjects/Appointments/SOGoAppointmentFolder.m + (-_flattenCycleRecord:forRange:intoArray:): when the event has a + timezone, the "until dates" of recurrence rules must be adjusted. + 2011-03-28 Wolfgang Sourdeau * Tools/SOGoToolRemoveDoubles.m @@ -134,7 +152,7 @@ * SoObjects/Appointments/iCalEvent+SOGo.m (-quickRecord): an all-day event usually doesn't have a timezone. However, if it does, we must convert its dates to GMT. All-day events are "floating", in the - sense that are timezone-independant. + sense that they are timezone-independant. 2011-03-17 Francis Lachapelle diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index eb6ced14b..0baf774fc 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -45,6 +45,7 @@ #import #import #import +#import #import #import #import @@ -748,24 +749,14 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir NSDictionary *oldRecord; NGCalendarDateRange *newRecordRange; int recordIndex; - signed int daylightOffset; newRecord = nil; recurrenceId = [component recurrenceId]; - if ([timeZone isDaylightSavingTimeForDate: recurrenceId] != [timeZone isDaylightSavingTimeForDate: [fir startDate]]) - { - // For the event's recurrence id, compute the daylight saving time - // offset with respect to the first occurrence of the recurring event. - daylightOffset = (signed int)[timeZone secondsFromGMTForDate: [fir startDate]] - - (signed int)[timeZone secondsFromGMTForDate: recurrenceId]; - recurrenceId = [recurrenceId dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:daylightOffset]; - } - if ([dateRange containsDate: recurrenceId]) { recordIndex = [self _indexOfRecordMatchingDate: recurrenceId - inArray: ma]; + inArray: ma]; if (recordIndex > -1) { startDate = [component startDate]; @@ -793,8 +784,8 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir } else [self errorWithFormat: - @"missing exception record for recurrence-id: %@", - recurrenceId]; + @"missing exception record for recurrence-id %@ (uid %@)", + recurrenceId, [component uid]]; } else { @@ -822,6 +813,8 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir content = [row objectForKey: @"c_content"]; if ([content length]) { + // TODO : c_content could have already been parsed. + // @see _flattenCycleRecord:forRange:intoArray: elements = [iCalCalendar parseFromSource: content]; if ([elements count]) { @@ -830,9 +823,9 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir for (count = 1; count < max; count++) [self _appendCycleException: [components objectAtIndex: count] firstInstanceCalendarDateRange: fir - fromRow: row - forRange: dateRange - toArray: ma]; + fromRow: row + forRange: dateRange + toArray: ma]; } } } @@ -852,7 +845,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir NSMutableDictionary *row, *fixedRow; NSMutableArray *records; NSDictionary *cycleinfo; - NGCalendarDateRange *firstRange, *oneRange; + NGCalendarDateRange *firstRange, *recurrenceRange, *oneRange; NSArray *rules, *exRules, *exDates, *ranges; NSArray *elements, *components; NSString *content; @@ -860,7 +853,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir id firstStartDate, firstEndDate; NSCalendarDate *checkStartDate, *checkEndDate; iCalTimeZone *eventTimeZone; - unsigned i, count, offset; + unsigned count, max, offset; records = [NSMutableArray array]; ranges = nil; @@ -883,7 +876,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir rules = [cycleinfo objectForKey: @"rules"]; exRules = [cycleinfo objectForKey: @"exRules"]; exDates = [cycleinfo objectForKey: @"exDates"]; - + row = [self fixupRecord: theRecord]; [row removeObjectForKey: @"c_cycleinfo"]; [row setObject: sharedYes forKey: @"isRecurrentEvent"]; @@ -897,7 +890,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir components = [[elements objectAtIndex: 0] events]; if ([components count]) { - // Retrieve the range of the first event + // Retrieve the range of the first/master event component = [components objectAtIndex: 0]; firstStartDate = [component uniqueChildWithTag: @"dtstart"]; firstEndDate = [component uniqueChildWithTag: @"dtend"]; @@ -910,27 +903,37 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir // Adjust the range to check with respect to the event timezone (extracted from the start date) checkStartDate = [eventTimeZone computedDateForDate: [theRange startDate]]; checkEndDate = [eventTimeZone computedDateForDate: [theRange endDate]]; - theRange = [NGCalendarDateRange calendarDateRangeWithStartDate: checkStartDate - endDate: checkEndDate]; + recurrenceRange = [NGCalendarDateRange calendarDateRangeWithStartDate: checkStartDate + endDate: checkEndDate]; + + // Adjust the exception dates exDates = [eventTimeZone computedDatesForStrings: exDates]; + + // Adjust the recurrence rules "until" dates + rules = [component recurrenceRulesWithTimeZone: eventTimeZone]; + exRules = [component exceptionRulesWithTimeZone: eventTimeZone]; } - else if ([[theRecord objectForKey: @"c_isallday"] boolValue]) + else { - // The event lasts all-day and has no timezone (floating); we convert the range of the first event - // to the user's timezone - offset = [timeZone secondsFromGMTForDate: [firstRange startDate]]; - firstStartDate = (NSCalendarDate*)[[firstRange startDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 + recurrenceRange = theRange; + if ([[theRecord objectForKey: @"c_isallday"] boolValue]) + { + // The event lasts all-day and has no timezone (floating); we convert the range of the first event + // to the user's timezone + offset = [timeZone secondsFromGMTForDate: [firstRange startDate]]; + firstStartDate = (NSCalendarDate*)[[firstRange startDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 + seconds:-offset]; + firstEndDate = (NSCalendarDate*)[[firstRange endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:-offset]; - firstEndDate = (NSCalendarDate*)[[firstRange endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 - seconds:-offset]; - [firstStartDate setTimeZone: timeZone]; - [firstEndDate setTimeZone: timeZone]; - firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate - endDate: firstEndDate]; + [firstStartDate setTimeZone: timeZone]; + [firstEndDate setTimeZone: timeZone]; + firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate + endDate: firstEndDate]; + } } // Calculate the occurrences for the given range - ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: theRange + ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: recurrenceRange firstInstanceCalendarDateRange: firstRange recurrenceRules: rules exceptionRules: exRules @@ -939,10 +942,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir } } - count = [ranges count]; - for (i = 0; i < count; i++) + max = [ranges count]; + for (count = 0; count < max; count++) { - oneRange = [ranges objectAtIndex: i]; + oneRange = [ranges objectAtIndex: count]; fixedRow = [self fixupCycleRecord: row cycleRange: oneRange firstInstanceCalendarDateRange: firstRange diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 570aa9af2..2d2de8ee5 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -564,9 +564,9 @@ [self expandGroupsInEvent: newEvent]; - // We first save the event. It is important to this initially - // as the event's UID might get modified in SOGoCalendarComponent: -saveComponent: - [super saveComponent: newEvent]; + // We first update the event. It is important to this initially + // as the event's UID might get modified. + [super updateComponent: newEvent]; if ([self isNew]) { @@ -606,15 +606,12 @@ hasOrganizer = [[[oldMasterEvent organizer] email] length]; if (!hasOrganizer || [oldMasterEvent userIsOrganizer: ownerUser]) - { - // The owner is the organizer of the event; handle the modifications - [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent]; - - // The sequence has possibly been increased -- resave the event. - [super saveComponent: newEvent]; - } + // The owner is the organizer of the event; handle the modifications + [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent]; } + [super saveComponent: newEvent]; + [fullCalendar release]; fullCalendar = nil; [safeCalendar release]; diff --git a/SoObjects/Appointments/SOGoCalendarComponent.h b/SoObjects/Appointments/SOGoCalendarComponent.h index 3ad1a73c0..1cc355f7b 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.h +++ b/SoObjects/Appointments/SOGoCalendarComponent.h @@ -1,8 +1,9 @@ /* SOGoCalendarComponent.h - this file is part of SOGo * - * Copyright (C) 2006-2009 Inverse inc. + * Copyright (C) 2006-2011 Inverse inc. * * Author: Wolfgang Sourdeau + * Francis Lachapelle * * 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 @@ -58,6 +59,7 @@ - (NSException *) copyComponent: (iCalCalendar *) calendar toFolder: (SOGoGCSFolder *) newFolder; +- (void) updateComponent: (iCalRepeatableEntityObject *) newObject; - (void) saveComponent: (iCalRepeatableEntityObject *) newObject; /* mail notifications */ diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index 71df4b7a3..48694f44b 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -593,9 +593,9 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence, } } -- (void) saveComponent: (iCalRepeatableEntityObject *) newObject +- (void) updateComponent: (iCalRepeatableEntityObject *) newObject { - NSString *newiCalString, *newUid; + NSString *newUid; if (!isNew && [newObject isRecurrent]) @@ -625,6 +625,11 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence, [eaMgr handleAlarmsInCalendar: [newObject parent] fromComponent: self]; } +} + +- (void) saveComponent: (iCalRepeatableEntityObject *) newObject +{ + NSString *newiCalString; newiCalString = [[newObject parent] versitString]; diff --git a/SoObjects/Appointments/iCalEvent+SOGo.h b/SoObjects/Appointments/iCalEvent+SOGo.h index 6f82ca22a..d7e970b5d 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.h +++ b/SoObjects/Appointments/iCalEvent+SOGo.h @@ -1,8 +1,9 @@ /* iCalEvent+SOGo.h - this file is part of SOGo * - * Copyright (C) 2007-2009 Inverse inc. + * Copyright (C) 2007-2011 Inverse inc. * * Author: Wolfgang Sourdeau + * Francis Lachapelle * * 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 @@ -31,6 +32,7 @@ - (BOOL) isStillRelevant; - (NSMutableDictionary *) quickRecord; +- (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate; @end diff --git a/SoObjects/Appointments/iCalEvent+SOGo.m b/SoObjects/Appointments/iCalEvent+SOGo.m index 3084aca36..a645afc6a 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.m +++ b/SoObjects/Appointments/iCalEvent+SOGo.m @@ -24,6 +24,7 @@ #import #import #import +#import #import #import @@ -35,6 +36,7 @@ #import #import #import +#import #import #import @@ -286,4 +288,55 @@ return [[self endDate] timeIntervalSinceDate: [self startDate]]; } +/** + * Shift the "until dates" of the recurrence rules of the event + * with respect to the previous end date of the event. + * @param previousEndDate the previous end date of the event + */ +- (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate +{ + iCalRecurrenceRule *rule; + NSEnumerator *rules; + NSCalendarDate *untilDate; + int offset; + + // Recurrence rules + rules = [[self recurrenceRules] objectEnumerator]; + while ((rule = [rules nextObject])) + { + untilDate = [rule untilDate]; + if (untilDate) + { + // The until date must match the time of the end date + offset = [[self endDate] timeIntervalSinceDate: previousEndDate]; + untilDate = [untilDate dateByAddingYears:0 + months:0 + days:0 + hours:0 + minutes:0 + seconds:offset]; + [rule setUntilDate: untilDate]; + } + } + + // Exception rules + rules = [[self exceptionRules] objectEnumerator]; + while ((rule = [rules nextObject])) + { + untilDate = [rule untilDate]; + if (untilDate) + { + // The until date must match the time of the end date + offset = [[self endDate] timeIntervalSinceDate: previousEndDate]; + untilDate = [untilDate dateByAddingYears:0 + months:0 + days:0 + hours:0 + minutes:0 + seconds:offset]; + [rule setUntilDate: untilDate]; + } + } +} + @end diff --git a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m index 029c5a835..577cc8354 100644 --- a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m @@ -161,9 +161,9 @@ // Calculate the occurrences for the given date ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: checkRange firstInstanceCalendarDateRange: firstRange - recurrenceRules: [self recurrenceRules] - exceptionRules: [self exceptionRules] - exceptionDates: [self exceptionDatesWithEventTimeZone: eventTimeZone]]; + recurrenceRules: [self recurrenceRulesWithTimeZone: eventTimeZone] + exceptionRules: [self exceptionRulesWithTimeZone: eventTimeZone] + exceptionDates: [self exceptionDatesWithTimeZone: eventTimeZone]]; doesOccur = [ranges dateRangeArrayContainsDate: startDate]; }