From 037e2e6c1f096ea0f57f9ccfa7a182f2f522cb93 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Sat, 16 Apr 2011 01:35:06 +0000 Subject: [PATCH] See ChangeLog. Monotone-Parent: ed8a21d5648d8bc72dc33ad272987d8963be1285 Monotone-Revision: 12a15fd8afcf3963e3ec84c6a630972534773465 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2011-04-16T01:35:06 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 14 ++ .../Appointments/SOGoAppointmentFolder.m | 121 +++++++++++------- .../Appointments/SOGoAppointmentObject.m | 40 ++++++ SoObjects/Appointments/iCalEvent+SOGo.h | 1 + SoObjects/Appointments/iCalEvent+SOGo.m | 18 ++- 5 files changed, 140 insertions(+), 54 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4b7a88454..1a5490181 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,19 @@ 2011-04-15 Francis Lachapelle + * SoObjects/Appointments/iCalEvent+SOGo.m (-firstOccurenceRange): + we now compute the end date by looking at the occurrence + interval. This way, we support events with an end date or a duration. + + * SoObjects/Appointments/SOGoAppointmentFolder.m + (-_flattenCycleRecord:forRange:intoArray:): also compute the end + date by looking at the occurrence interval. + (-importCalendar:): added a duration to events with no end date nor duration. + + * SoObjects/Appointments/SOGoAppointmentObject.m + (-_adjustEventsInRequest:): new method to verify the vCalendar for + any inconsistency or missing attributes. Currently, it only adds a + duration if no end date nor duration is found. + * UI/WebServerResources/ckeditor/config.js: Changed the default enter mode to use BR instead of P. This matches the behavior of Thunderbird. diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 0498d1bcf..2419a203b 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -73,6 +73,7 @@ #import #import "iCalRepeatableEntityObject+SOGo.h" +#import "iCalEvent+SOGo.h" #import "iCalPerson+SOGo.h" #import "SOGoAppointmentObject.h" #import "SOGoAppointmentFolders.h" @@ -849,9 +850,9 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir NSArray *rules, *exRules, *exDates, *ranges; NSArray *elements, *components; NSString *content; - iCalRepeatableEntityObject *component; id firstStartDate, firstEndDate; NSCalendarDate *checkStartDate, *checkEndDate; + iCalEvent *component; iCalTimeZone *eventTimeZone; unsigned count, max, offset; @@ -890,52 +891,54 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir // Retrieve the range of the first/master event component = [components objectAtIndex: 0]; firstStartDate = [component uniqueChildWithTag: @"dtstart"]; - firstEndDate = [component uniqueChildWithTag: @"dtend"]; - eventTimeZone = [(iCalDateTime*)firstStartDate timeZone]; - firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: [[[firstStartDate values] lastObject] asCalendarDate] - endDate: [[[firstEndDate values] lastObject] asCalendarDate]]; - - if (eventTimeZone) - { - // 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]]; - 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 - { - 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]; - [firstStartDate setTimeZone: timeZone]; - [firstEndDate setTimeZone: timeZone]; - firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate - endDate: firstEndDate]; - } - } - - // Calculate the occurrences for the given range + firstStartDate = [[[firstStartDate values] lastObject] asCalendarDate]; + firstEndDate = [firstStartDate addTimeInterval: [component occurenceInterval]]; + + firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate + endDate: firstEndDate]; + + eventTimeZone = [(iCalDateTime*)firstStartDate timeZone]; + if (eventTimeZone) + { + // 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]]; + 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 + { + 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]; + [firstStartDate setTimeZone: timeZone]; + [firstEndDate setTimeZone: timeZone]; + firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate + endDate: firstEndDate]; + } + } + + // Calculate the occurrences for the given range records = [NSMutableArray array]; - ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: recurrenceRange - firstInstanceCalendarDateRange: firstRange + ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: recurrenceRange + firstInstanceCalendarDateRange: firstRange recurrenceRules: rules - exceptionRules: exRules - exceptionDates: exDates]; + exceptionRules: exRules + exceptionDates: exDates]; max = [ranges count]; for (count = 0; count < max; count++) { @@ -947,15 +950,15 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir if (fixedRow) [records addObject: fixedRow]; } - + [self _appendCycleExceptionsFromRow: row firstInstanceCalendarDateRange: firstRange forRange: theRange toArray: records]; - + [theRecords addObjectsFromArray: records]; - } - } + } + } } else [self errorWithFormat:@"cyclic record doesn't have content -> %@", theRecord]; @@ -2607,6 +2610,11 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return ([object saveContentString: content] == nil); } +/** + * Import all components of a vCalendar. + * @param calendar the calendar to import + * @return the number of components imported + */ - (int) importCalendar: (iCalCalendar *) calendar { NSArray *vtimezones; @@ -2616,6 +2624,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir iCalEntityObject *element; iCalDateTime *startDate; iCalTimeZone *timezone; + iCalEvent *event; int imported, count, i; @@ -2651,6 +2660,20 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir { timezone = [startDate timeZone]; tz = [timezones valueForKey: [timezone tzId]]; + if ([element isKindOfClass: [iCalEvent class]]) + { + event = (iCalEvent *)element; + if (![event hasEndDate] && ![event hasDuration]) + { + // No end date, no duration + if ([event isAllDay]) + [event setDuration: @"P1D"]; + else + [event setDuration: @"PT1H"]; + + [self errorWithFormat: @"Importing event with no end date; setting duration to %@", [event duration]]; + } + } } if ([self importComponent: element timezone: (tz == nil? @"" : tz)]) diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index bec825629..29a308411 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -1336,6 +1336,44 @@ [rq setContent: [[calendar versitString] dataUsingEncoding: [rq contentEncoding]]]; } +/** + * Verify vCalendar for any inconsistency or missing attributes. + * Currently only check if the events have an end date or a duration. + * @param rq the HTTP PUT request + */ +- (void) _adjustEventsInRequest: (WORequest *) rq +{ + iCalCalendar *calendar; + NSArray *allEvents; + iCalEvent *event; + NSUInteger i; + BOOL modified; + + calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]]; + allEvents = [calendar events]; + modified = NO; + + for (i = 0; i < [allEvents count]; i++) + { + event = [allEvents objectAtIndex: i]; + + if (![event hasEndDate] && ![event hasDuration]) + { + // No end date, no duration + if ([event isAllDay]) + [event setDuration: @"P1D"]; + else + [event setDuration: @"PT1H"]; + + modified = YES; + [self errorWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]]; + } + } + + if (modified) + [rq setContent: [[calendar versitString] dataUsingEncoding: [rq contentEncoding]]]; +} + - (void) _decomposeGroupsInRequest: (WORequest *) rq { iCalCalendar *calendar; @@ -1471,6 +1509,8 @@ { [self _adjustTransparencyInRequest: rq]; } + + [self _adjustEventsInRequest: rq]; } // diff --git a/SoObjects/Appointments/iCalEvent+SOGo.h b/SoObjects/Appointments/iCalEvent+SOGo.h index d7e970b5d..9d2ee9dd4 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.h +++ b/SoObjects/Appointments/iCalEvent+SOGo.h @@ -31,6 +31,7 @@ @interface iCalEvent (SOGoExtensions) - (BOOL) isStillRelevant; +- (unsigned int) occurenceInterval; - (NSMutableDictionary *) quickRecord; - (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate; diff --git a/SoObjects/Appointments/iCalEvent+SOGo.m b/SoObjects/Appointments/iCalEvent+SOGo.m index a645afc6a..089592dba 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.m +++ b/SoObjects/Appointments/iCalEvent+SOGo.m @@ -272,14 +272,22 @@ */ - (NGCalendarDateRange *) firstOccurenceRange { - iCalDateTime *firstStartDate, *firstEndDate; + iCalDateTime *firstStartDate; + NSCalendarDate *start, *end; NGCalendarDateRange *firstRange; - firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"]; - firstEndDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtend"]; - firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: [[[firstStartDate values] lastObject] asCalendarDate] - endDate: [[[firstEndDate values] lastObject] asCalendarDate]]; + firstRange = nil; + firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"]; + if ([[firstStartDate values] count] > 0) + { + start = [[[firstStartDate values] lastObject] asCalendarDate]; + end = [start addTimeInterval: [self occurenceInterval]]; + + firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: start + endDate: end]; + } + return firstRange; }