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
This commit is contained in:
Francis Lachapelle
2011-04-16 01:35:06 +00:00
parent 3f5e37c50d
commit 037e2e6c1f
5 changed files with 140 additions and 54 deletions

View File

@@ -1,5 +1,19 @@
2011-04-15 Francis Lachapelle <flachapelle@inverse.ca>
* 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.

View File

@@ -73,6 +73,7 @@
#import <SOGo/WOResponse+SOGo.h>
#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)])

View File

@@ -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];
}
//

View File

@@ -31,6 +31,7 @@
@interface iCalEvent (SOGoExtensions)
- (BOOL) isStillRelevant;
- (unsigned int) occurenceInterval;
- (NSMutableDictionary *) quickRecord;
- (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate;

View File

@@ -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;
}