See Changelog.

Monotone-Parent: 8f58a8aa696d8fbbb82f1cd571800676a04b96af
Monotone-Revision: b593ce96638476fad98d678453ca6af6112169fb

Monotone-Author: flachapelle@inverse.ca
Monotone-Date: 2011-01-14T02:54:33
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Francis Lachapelle 2011-01-14 02:54:33 +00:00
parent b6bc06b163
commit 64081b8fa7
13 changed files with 259 additions and 137 deletions

View File

@ -1,3 +1,26 @@
2011-01-13 Francis Lachapelle <flachapelle@inverse.ca>
* SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m
(-doesOccurOnDate:): the date to verify must be adjusted to the
event's timezone before performing the calculation of the
recurrence rule.
* SoObjects/Appointments/iCalEvent+SOGo.m (-firstOccurenceRange):
extract the original start and end dates from the event, based on
the event's timezone.
* SoObjects/Appointments/SOGoAppointmentFolder.m
(-fixupCycleRecord:cycleRange:firstInstanceCalendarDateRange:withEventTimeZone:):
the event dates must be first be adjusted to the event's timezone
before being adjusted to the user's timezone.
(-_flattenCycleRecord:forRange:intoArray:): the calculation of the
occurrences must be performed with respect to the event's
timezone, not the user's timezone.
* SoObjects/Appointments/SOGoAppointmentObject.m
(-newOccurenceWithID:): new occurrences are now independant of the
user's timezone.
2011-01-11 Ludovic Marcotte <lmarcotte@inverse.ca> 2011-01-11 Ludovic Marcotte <lmarcotte@inverse.ca>
* OpenChange/MAPIStoreContactsMessageTable.m * OpenChange/MAPIStoreContactsMessageTable.m

View File

@ -1,3 +1,13 @@
2011-01-13 Francis Lachapelle <flachapelle@inverse.ca>
* iCalTimeZone.m (-computedDateForDate:): Was
_computedDateTimeForDate. Adjusts a date with respect to this
vTimeZone.
* iCalDateTime.m (-dateTimes): when there's no timezone defined
(which is the case when parsing a vTimeZone!), don't fallback to
the system timezone.
2010-11-18 Wolfgang Sourdeau <wsourdeau@inverse.ca> 2010-11-18 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* NGVCard.m (-setVName, vName): removed useless and non-standard * NGVCard.m (-setVName, vName): removed useless and non-standard

View File

@ -161,6 +161,7 @@
currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]]; currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]];
r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate
endDate: currentEndDate]; endDate: currentEndDate];
if ([_r containsDateRange: r]) if ([_r containsDateRange: r])
[ranges addObject: r]; [ranges addObject: r];
} }

View File

@ -1,6 +1,6 @@
/* iCalDateTime.m - this file is part of SOPE /* iCalDateTime.m - this file is part of SOPE
* *
* Copyright (C) 2006 Inverse inc. * Copyright (C) 2006-2011 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
@ -161,7 +161,7 @@
NSString *date; NSString *date;
NSCalendarDate *initialDate, *dateTime; NSCalendarDate *initialDate, *dateTime;
NSMutableArray *dates; NSMutableArray *dates;
NSTimeZone *tz; //NSTimeZone *tz;
unsigned count, i; unsigned count, i;
count = [[self values] count]; count = [[self values] count];
@ -170,24 +170,28 @@
{ {
date = [self value: i]; date = [self value: i];
iTZ = [self timeZone]; iTZ = [self timeZone];
if (iTZ) if (iTZ)
dateTime = [iTZ dateForDateTimeString: date]; dateTime = [iTZ dateForDateTimeString: date];
else else
{ {
initialDate = [date asCalendarDate]; initialDate = [date asCalendarDate];
if (initialDate) if (initialDate)
dateTime = initialDate;
/*
{ {
if ([date hasSuffix: @"Z"] || [date hasSuffix: @"z"]) if ([date hasSuffix: @"Z"] || [date hasSuffix: @"z"])
dateTime = initialDate; dateTime = initialDate;
else else
{ {
/* same TODO as above */ // same TODO as above
tz = [NSTimeZone defaultTimeZone]; tz = [NSTimeZone defaultTimeZone];
dateTime = [initialDate addYear: 0 month: 0 day: 0 dateTime = [initialDate addYear: 0 month: 0 day: 0
hour: 0 minute: 0 hour: 0 minute: 0
second: -[tz secondsFromGMTForDate: initialDate]]; second: -[tz secondsFromGMTForDate: initialDate]];
} }
} }
*/
else else
dateTime = nil; dateTime = nil;
} }

View File

@ -1,6 +1,6 @@
/* iCalTimeZone.h - this file is part of SOPE /* iCalTimeZone.h - this file is part of SOPE
* *
* Copyright (C) 2006-2009 Inverse inc. * Copyright (C) 2006-2011 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
@ -28,10 +28,14 @@
#import "CardGroup.h" #import "CardGroup.h"
@class iCalTimeZonePeriod;
@interface iCalTimeZone : CardGroup @interface iCalTimeZone : CardGroup
+ (iCalTimeZone *) timeZoneForName: (NSString *) theName; + (iCalTimeZone *) timeZoneForName: (NSString *) theName;
- (NSString *) tzId; - (NSString *) tzId;
- (iCalTimeZonePeriod *) periodForDate: (NSCalendarDate *) date;
- (NSCalendarDate *) computedDateForDate: (NSCalendarDate *) theDate;
- (NSString *) dateTimeStringForDate: (NSCalendarDate *) date; - (NSString *) dateTimeStringForDate: (NSCalendarDate *) date;
- (NSString *) dateStringForDate: (NSCalendarDate *) date; - (NSString *) dateStringForDate: (NSCalendarDate *) date;
- (NSCalendarDate *) dateForDateTimeString: (NSString *) string; - (NSCalendarDate *) dateForDateTimeString: (NSString *) string;

View File

@ -1,9 +1,10 @@
/* iCalTimeZone.m - this file is part of SOPE /* iCalTimeZone.m - this file is part of SOPE
* *
* Copyright (C) 2006-2009 Inverse inc. * Copyright (C) 2006-2011 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Ludovic Marcotte <lmarcotte@inverse.ca> * Ludovic Marcotte <lmarcotte@inverse.ca>
* Francis Lachapelle <flachapelle@inverse.ca>
* *
* 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
@ -185,35 +186,38 @@ static NSMutableDictionary *cache;
period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"daylight"]; period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"daylight"];
} }
// NSLog (@"chosen period: '%@'", [period tag]);
return period; return period;
} }
- (NSCalendarDate *) _computedDateTimeForDate: (NSCalendarDate *) date /**
* Adjust a date with respect to this vTimeZone.
* @param theDate the date to adjust to the timezone.
* @return a new GMT date adjusted with the offset of the timezone.
*/
- (NSCalendarDate *) computedDateForDate: (NSCalendarDate *) theDate
{ {
NSCalendarDate *tmpDate; NSCalendarDate *tmpDate;
NSTimeZone *utc; NSTimeZone *utc;
utc = [NSTimeZone timeZoneWithName: @"GMT"]; utc = [NSTimeZone timeZoneWithName: @"GMT"];
tmpDate = [date copy]; tmpDate = [theDate copy];
[tmpDate autorelease]; [tmpDate autorelease];
[tmpDate setTimeZone: utc]; [tmpDate setTimeZone: utc];
return [tmpDate addYear: 0 month: 0 day: 0 return [tmpDate addYear: 0 month: 0 day: 0
hour: 0 minute: 0 hour: 0 minute: 0
second: [[self periodForDate: date] secondsOffsetFromGMT]]; second: [[self periodForDate: theDate] secondsOffsetFromGMT]];
} }
- (NSString *) dateTimeStringForDate: (NSCalendarDate *) date - (NSString *) dateTimeStringForDate: (NSCalendarDate *) date
{ {
return [[self _computedDateTimeForDate: date] return [[self computedDateForDate: date]
iCalFormattedDateTimeString]; iCalFormattedDateTimeString];
} }
- (NSString *) dateStringForDate: (NSCalendarDate *) date - (NSString *) dateStringForDate: (NSCalendarDate *) date
{ {
return [[self _computedDateTimeForDate: date] return [[self computedDateForDate: date]
iCalFormattedDateString]; iCalFormattedDateString];
} }

View File

@ -137,6 +137,7 @@
} }
tzStart = [self startDate]; tzStart = [self startDate];
[tzStart setTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]]; [tzStart setTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
tmpDate = [NSCalendarDate dateWithYear: [refDate yearOfCommonEra] tmpDate = [NSCalendarDate dateWithYear: [refDate yearOfCommonEra]
month: [[rrule namedValue: @"bymonth"] intValue] month: [[rrule namedValue: @"bymonth"] intValue]

View File

@ -186,6 +186,7 @@
currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]]; currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]];
r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate
endDate: currentEndDate]; endDate: currentEndDate];
if ([_r containsDateRange: r]) if ([_r containsDateRange: r])
[ranges addObject: r]; [ranges addObject: r];
} }

View File

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2007-2009 Inverse inc. Copyright (C) 2007-2011 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org. This file is part of OpenGroupware.org.
@ -46,6 +46,7 @@
#import <NGCards/iCalPerson.h> #import <NGCards/iCalPerson.h>
#import <NGCards/iCalRecurrenceCalculator.h> #import <NGCards/iCalRecurrenceCalculator.h>
#import <NGCards/iCalTimeZone.h> #import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalTimeZonePeriod.h>
#import <NGCards/NSString+NGCards.h> #import <NGCards/NSString+NGCards.h>
#import <NGExtensions/NGCalendarDateRange.h> #import <NGExtensions/NGCalendarDateRange.h>
#import <NGExtensions/NSNull+misc.h> #import <NGExtensions/NSNull+misc.h>
@ -576,33 +577,38 @@ static NSNumber *sharedYes = nil;
forKey: [currentRecord objectForKey: @"c_name"]]; forKey: [currentRecord objectForKey: @"c_name"]];
} }
- (NSMutableDictionary *) fixupRecord: (NSDictionary *) _record /**
* Set the timezone of the event start and end dates to the user's timezone.
* @param theRecord an dictionnary with the attributes of the event.
* @return a copy of theRecord with adjusted dates.
*/
- (NSMutableDictionary *) fixupRecord: (NSDictionary *) theRecord
{ {
NSMutableDictionary *md; NSMutableDictionary *record;
static NSString *fields[] = { @"c_startdate", @"startDate", static NSString *fields[] = { @"c_startdate", @"startDate",
@"c_enddate", @"endDate" }; @"c_enddate", @"endDate" };
unsigned int count; unsigned int count;
NSCalendarDate *date; NSCalendarDate *date;
NSNumber *dateValue; NSNumber *dateValue;
md = [[_record mutableCopy] autorelease]; record = [[theRecord mutableCopy] autorelease];
for (count = 0; count < 2; count++) for (count = 0; count < 2; count++)
{ {
dateValue = [_record objectForKey: fields[count * 2]]; dateValue = [theRecord objectForKey: fields[count * 2]];
if (dateValue) if (dateValue)
{ {
date = [NSCalendarDate dateWithTimeIntervalSince1970: [dateValue unsignedIntValue]]; date = [NSCalendarDate dateWithTimeIntervalSince1970: [dateValue unsignedIntValue]];
if (date) if (date)
{ {
[date setTimeZone: timeZone]; [date setTimeZone: timeZone];
[md setObject: date forKey: fields[count * 2 + 1]]; [record setObject: date forKey: fields[count * 2 + 1]];
} }
} }
else else
[self logWithFormat: @"missing '%@' in record?", fields[count * 2]]; [self logWithFormat: @"missing '%@' in record?", fields[count * 2]];
} }
return md; return record;
} }
- (NSArray *) fixupRecords: (NSArray *) theRecords - (NSArray *) fixupRecords: (NSArray *) theRecords
@ -629,42 +635,52 @@ static NSNumber *sharedYes = nil;
return ma; return ma;
} }
- (NSMutableDictionary *) fixupCycleRecord: (NSDictionary *) _record /**
cycleRange: (NGCalendarDateRange *) _r * Adjust the timezone of the start and end dates to the user's timezone.
firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir * The event is recurrent and the dates must first be adjusted with respect to
forViewRange: (NGCalendarDateRange *) _viewRange * the event's timezone.
* @param theRecord
* @param theCycle
* @param theFirstCycle
* @param theEventTimeZone
* @see fixupRecord:
* @return a copy of theRecord with adjusted dates.
*/
- (NSMutableDictionary *) fixupCycleRecord: (NSDictionary *) theRecord
cycleRange: (NGCalendarDateRange *) theCycle
firstInstanceCalendarDateRange: (NGCalendarDateRange *) theFirstCycle
withEventTimeZone: (iCalTimeZone *) theEventTimeZone
{ {
NSMutableDictionary *md; NSMutableDictionary *record;
NSNumber *dateSecs; NSNumber *dateSecs;
id tmp; id date;
int secondsOffsetFromGMT;
md = [[_record mutableCopy] autorelease];
/* cycle is in _r. We also have to override the c_startdate/c_enddate with the date values of record = [[theRecord mutableCopy] autorelease];
the reccurence since we use those when displaying events in SOGo Web */
tmp = [_r startDate]; date = [theCycle startDate];
[tmp setTimeZone: timeZone]; secondsOffsetFromGMT = (int) [[theEventTimeZone periodForDate: date] secondsOffsetFromGMT];
[md setObject: tmp forKey: @"startDate"]; date = [date dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -secondsOffsetFromGMT];
dateSecs = [NSNumber numberWithInt: [tmp timeIntervalSince1970]]; [date setTimeZone: timeZone];
[md setObject: dateSecs forKey: @"c_startdate"]; [record setObject: date forKey: @"startDate"];
dateSecs = [NSNumber numberWithInt: [date timeIntervalSince1970]];
[record setObject: dateSecs forKey: @"c_startdate"];
[record setObject: dateSecs forKey: @"c_recurrence_id"];
tmp = [_r endDate]; date = [theCycle endDate];
[tmp setTimeZone: timeZone]; secondsOffsetFromGMT = (int) [[theEventTimeZone periodForDate: date] secondsOffsetFromGMT];
[md setObject: tmp forKey: @"endDate"]; date = [date dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -secondsOffsetFromGMT];
dateSecs = [NSNumber numberWithInt: [tmp timeIntervalSince1970]]; [date setTimeZone: timeZone];
[md setObject: dateSecs forKey: @"c_enddate"]; [record setObject: date forKey: @"endDate"];
dateSecs = [NSNumber numberWithInt: [date timeIntervalSince1970]];
tmp = [_r startDate]; [record setObject: dateSecs forKey: @"c_enddate"];
dateSecs = [NSNumber numberWithInt: [tmp timeIntervalSince1970]];
[md setObject: dateSecs forKey: @"c_recurrence_id"];
// The first instance date is added to the dictionary so it can // The first instance date is added to the dictionary so it can
// be used by UIxCalListingActions to compute the DST offset. // be used by UIxCalListingActions to compute the DST offset.
tmp = [_fir startDate]; date = [theFirstCycle startDate];
[md setObject: tmp forKey: @"cycleStartDate"]; [record setObject: date forKey: @"cycleStartDate"];
return md; return record;
} }
- (int) _indexOfRecordMatchingDate: (NSCalendarDate *) matchDate - (int) _indexOfRecordMatchingDate: (NSCalendarDate *) matchDate
@ -805,26 +821,39 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
} }
} }
- (void) _flattenCycleRecord: (NSDictionary *) _row /**
forRange: (NGCalendarDateRange *) _r * Calculate and return the occurrences of the recurrent event for the given
intoArray: (NSMutableArray *) _ma * period.
* @param theRecord the event definition.
* @param theRange the period to look in.
* @param theRecords the array into which are copied the resulting occurrences.
*/
- (void) _flattenCycleRecord: (NSDictionary *) theRecord
forRange: (NGCalendarDateRange *) theRange
intoArray: (NSMutableArray *) theRecords
{ {
NSMutableDictionary *row, *fixedRow; NSMutableDictionary *row, *fixedRow;
NSMutableArray *recordArray; NSMutableArray *records;
NSDictionary *cycleinfo; NSDictionary *cycleinfo;
NSCalendarDate *startDate, *endDate; NSCalendarDate *startDate, *endDate;
NGCalendarDateRange *fir, *rRange; NGCalendarDateRange *firstRange, *oneRange;
NSArray *rules, *exRules, *exDates, *ranges; NSArray *rules, *exRules, *exDates, *ranges;
unsigned i, count; NSArray *elements, *components;
NSString *content; NSString *content;
iCalRepeatableEntityObject *component;
iCalDateTime *firstStartDate, *firstEndDate;
NSCalendarDate *checkStartDate, *checkEndDate;
iCalTimeZone *eventTimeZone;
unsigned i, count;
recordArray = [NSMutableArray array]; records = [NSMutableArray array];
ranges = nil;
content = [_row objectForKey: @"c_cycleinfo"]; content = [theRecord objectForKey: @"c_cycleinfo"];
if (![content isNotNull]) if (![content isNotNull])
{ {
[self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@", [self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@",
_row]; theRecord];
return; return;
} }
@ -832,45 +861,72 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
if (!cycleinfo) if (!cycleinfo)
{ {
[self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@", [self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@",
_row]; theRecord];
return; return;
} }
row = [self fixupRecord: _row]; row = [self fixupRecord: theRecord];
[row removeObjectForKey: @"c_cycleinfo"]; [row removeObjectForKey: @"c_cycleinfo"];
[row setObject: sharedYes forKey: @"isRecurrentEvent"]; [row setObject: sharedYes forKey: @"isRecurrentEvent"];
startDate = [row objectForKey: @"startDate"]; startDate = [row objectForKey: @"startDate"];
endDate = [row objectForKey: @"endDate"]; endDate = [row objectForKey: @"endDate"];
fir = [NGCalendarDateRange calendarDateRangeWithStartDate: startDate
endDate: endDate]; content = [theRecord objectForKey: @"c_content"];
rules = [cycleinfo objectForKey: @"rules"]; if ([content length])
exRules = [cycleinfo objectForKey: @"exRules"]; {
exDates = [cycleinfo objectForKey: @"exDates"]; elements = [iCalCalendar parseFromSource: content];
if ([elements count])
ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: _r {
firstInstanceCalendarDateRange: fir components = [[elements objectAtIndex: 0] events];
recurrenceRules: rules if ([components count])
exceptionRules: exRules {
exceptionDates: exDates]; // Retrieve the range of the first event
component = [components objectAtIndex: 0];
firstStartDate = (iCalDateTime*)[component uniqueChildWithTag: @"dtstart"];
firstEndDate = (iCalDateTime*)[component uniqueChildWithTag: @"dtend"];
eventTimeZone = [firstStartDate timeZone];
startDate = [eventTimeZone computedDateForDate: startDate];
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: [[[firstStartDate values] lastObject] asCalendarDate]
endDate: [[[firstEndDate values] lastObject] asCalendarDate]];
// 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];
// Calculate the occurrences for the given range
rules = [cycleinfo objectForKey: @"rules"];
exRules = [cycleinfo objectForKey: @"exRules"];
exDates = [cycleinfo objectForKey: @"exDates"];
ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: theRange
firstInstanceCalendarDateRange: firstRange
recurrenceRules: rules
exceptionRules: exRules
exceptionDates: exDates];
}
}
}
count = [ranges count]; count = [ranges count];
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
rRange = [ranges objectAtIndex: i]; oneRange = [ranges objectAtIndex: i];
fixedRow = [self fixupCycleRecord: row fixedRow = [self fixupCycleRecord: row
cycleRange: rRange cycleRange: oneRange
firstInstanceCalendarDateRange: fir firstInstanceCalendarDateRange: firstRange
forViewRange: _r]; withEventTimeZone: eventTimeZone];
if (fixedRow) if (fixedRow)
[recordArray addObject: fixedRow]; [records addObject: fixedRow];
} }
[self _appendCycleExceptionsFromRow: row [self _appendCycleExceptionsFromRow: row
firstInstanceCalendarDateRange: fir firstInstanceCalendarDateRange: firstRange
forRange: _r forRange: theRange
toArray: recordArray]; toArray: records];
[_ma addObjectsFromArray: recordArray]; [theRecords addObjectsFromArray: records];
} }
- (NSArray *) _flattenCycleRecords: (NSArray *) _records - (NSArray *) _flattenCycleRecords: (NSArray *) _records

View File

@ -1,6 +1,6 @@
/* /*
Copyright (C) 2007-2011 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2007-2009 Inverse inc.
This file is part of OpenGroupware.org. This file is part of OpenGroupware.org.
@ -80,33 +80,28 @@
inContainer: self]; inContainer: self];
} }
- (iCalRepeatableEntityObject *) newOccurenceWithID: (NSString *) recID /**
* Return a new exception in a the recurrent event.
* @param theRecurrenceID the ID of the occurence.
* @return a new occurence.
*/
- (iCalRepeatableEntityObject *) newOccurenceWithID: (NSString *) theRecurrenceID
{ {
iCalEvent *newOccurence, *master; iCalEvent *newOccurence, *master;
NSCalendarDate *date, *firstDate; NSCalendarDate *date, *firstDate;
unsigned int interval, nbrDays; unsigned int interval, nbrDays;
SOGoUserDefaults *ud; SOGoUserDefaults *ud;
NSTimeZone *timeZone; NSTimeZone *timeZone;
int daylightOffset;
ud = [[SOGoUser userWithLogin: owner] userDefaults]; ud = [[SOGoUser userWithLogin: owner] userDefaults];
timeZone = [ud timeZone]; timeZone = [ud timeZone];
newOccurence = (iCalEvent *) [super newOccurenceWithID: recID]; newOccurence = (iCalEvent *) [super newOccurenceWithID: theRecurrenceID];
date = [newOccurence recurrenceId]; date = [newOccurence recurrenceId];
master = [self component: NO secure: NO]; master = [self component: NO secure: NO];
firstDate = [master startDate]; firstDate = [master startDate];
// We are creating a new exception in a recurrent event -- compute the daylight
// saving time with respect to the first occurrence of the recurrent event.
daylightOffset = (int) ([timeZone secondsFromGMTForDate: firstDate]
- [timeZone secondsFromGMTForDate: date]);
if (daylightOffset)
date = [date dateByAddingYears: 0 months: 0 days: 0
hours:0 minutes: 0 seconds: daylightOffset];
[date setTimeZone: timeZone];
interval = [[master endDate] interval = [[master endDate]
timeIntervalSinceDate: firstDate]; timeIntervalSinceDate: firstDate];
if ([newOccurence isAllDay]) if ([newOccurence isAllDay])

View File

@ -1,8 +1,9 @@
/* iCalEvent+SOGo.m - this file is part of SOGo /* iCalEvent+SOGo.m - this file is part of SOGo
* *
* Copyright (C) 2007 Inverse inc. * Copyright (C) 2007-2011 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Francis Lachapelle <flachapelle@inverse.ca>
* *
* 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 +31,7 @@
#import <NGExtensions/NSObject+Logs.h> #import <NGExtensions/NSObject+Logs.h>
#import <NGCards/iCalAlarm.h> #import <NGCards/iCalAlarm.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalEvent.h> #import <NGCards/iCalEvent.h>
#import <NGCards/iCalPerson.h> #import <NGCards/iCalPerson.h>
#import <NGCards/iCalTrigger.h> #import <NGCards/iCalTrigger.h>
@ -240,10 +242,22 @@
return row; return row;
} }
/**
* Extract the start and end dates from the event, from which all recurrence
* calculations will be based on.
* @return the range of the first occurrence.
*/
- (NGCalendarDateRange *) firstOccurenceRange - (NGCalendarDateRange *) firstOccurenceRange
{ {
return [NGCalendarDateRange calendarDateRangeWithStartDate: [self startDate] iCalDateTime *firstStartDate, *firstEndDate;
endDate: [self endDate]]; NGCalendarDateRange *firstRange;
firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
firstEndDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtend"];
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: [[[firstStartDate values] lastObject] asCalendarDate]
endDate: [[[firstEndDate values] lastObject] asCalendarDate]];
return firstRange;
} }
- (unsigned int) occurenceInterval - (unsigned int) occurenceInterval

View File

@ -1,6 +1,6 @@
/* iCalRepeatableEntityObject+SOGo.m - this file is part of SOGo /*
Copyright (C) 2008-2011 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2008 Inverse inc.
This file is part of OpenGroupware.org. This file is part of OpenGroupware.org.
@ -26,8 +26,11 @@
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <Foundation/NSTimeZone.h> #import <Foundation/NSTimeZone.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalRecurrenceRule.h> #import <NGCards/iCalRecurrenceRule.h>
#import <NGCards/iCalRecurrenceCalculator.h> #import <NGCards/iCalRecurrenceCalculator.h>
#import <NGCards/iCalTimeZone.h>
#import <NGCards/NSString+NGCards.h>
#import <NGExtensions/NGCalendarDateRange.h> #import <NGExtensions/NGCalendarDateRange.h>
#import "iCalRepeatableEntityObject+SOGo.h" #import "iCalRepeatableEntityObject+SOGo.h"
@ -104,25 +107,39 @@
return 0; return 0;
} }
- (BOOL) doesOccurOnDate: (NSCalendarDate *) occurenceDate /**
* Checks if a date is part of the recurring entity.
* @param theOccurrenceDate the date to verify.
* @return true if the occurence date is part of the recurring entity.
*/
- (BOOL) doesOccurOnDate: (NSCalendarDate *) theOccurenceDate
{ {
NSArray *ranges; NSArray *ranges;
NGCalendarDateRange *checkRange; NGCalendarDateRange *checkRange, *firstRange;
NSCalendarDate *endDate; NSCalendarDate *startDate, *endDate;
iCalDateTime *firstStartDate;
BOOL doesOccur; BOOL doesOccur;
doesOccur = [self isRecurrent]; doesOccur = [self isRecurrent];
if (doesOccur) if (doesOccur)
{ {
endDate = [occurenceDate addTimeInterval: [self occurenceInterval]]; // Retrieve the range of the first event
checkRange = [NGCalendarDateRange calendarDateRangeWithStartDate: occurenceDate firstRange = [self firstOccurenceRange];
endDate: endDate];
// Set the range to check with respect to the event timezone (extracted from the start date)
firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
startDate = [[firstStartDate timeZone] computedDateForDate: theOccurenceDate];
endDate = [startDate addTimeInterval: [self occurenceInterval]];
checkRange = [NGCalendarDateRange calendarDateRangeWithStartDate: startDate
endDate: endDate];
// Calculate the occurrences for the given date
ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: checkRange ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: checkRange
firstInstanceCalendarDateRange: [self firstOccurenceRange] firstInstanceCalendarDateRange: firstRange
recurrenceRules: [self recurrenceRules] recurrenceRules: [self recurrenceRules]
exceptionRules: [self exceptionRules] exceptionRules: [self exceptionRules]
exceptionDates: [self exceptionDates]]; exceptionDates: [self exceptionDates]];
doesOccur = [ranges dateRangeArrayContainsDate: occurenceDate]; doesOccur = [ranges dateRangeArrayContainsDate: startDate];
} }
return doesOccur; return doesOccur;

View File

@ -1,8 +1,9 @@
/* UIxCalListingActions.m - this file is part of SOGo /* UIxCalListingActions.m - this file is part of SOGo
* *
* Copyright (C) 2006-2010 Inverse inc. * Copyright (C) 2006-2011 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Francis Lachapelle <flachapelle@inverse.ca>
* *
* 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
@ -34,6 +35,8 @@
#import <NGObjWeb/WOResponse.h> #import <NGObjWeb/WOResponse.h>
#import <NGCards/iCalPerson.h> #import <NGCards/iCalPerson.h>
#import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalDateTime.h>
#import <NGExtensions/NGCalendarDateRange.h> #import <NGExtensions/NGCalendarDateRange.h>
#import <NGExtensions/NSCalendarDate+misc.h> #import <NGExtensions/NSCalendarDate+misc.h>
@ -246,8 +249,12 @@ static NSArray *tasksFields = nil;
forKey: @"c_title"]; forKey: @"c_title"];
} }
/* TODO: shouldn't this be handled when creating the quick records ? */ /*
- (void) _fixDates: (NSMutableDictionary *) aRecord * Adjust the event start and end dates when there's a time change
* in the period covering the view for the user's timezone.
* @param theRecord the attributes of the event.
*/
- (void) _fixDates: (NSMutableDictionary *) theRecord
{ {
NSCalendarDate *aDate, *aStartDate; NSCalendarDate *aDate, *aStartDate;
NSNumber *aDateValue; NSNumber *aDateValue;
@ -272,41 +279,25 @@ static NSArray *tasksFields = nil;
http://www.sogo.nu/bugs/view.php?id=678 http://www.sogo.nu/bugs/view.php?id=678
... ...
*/ */
if (dayBasedView || [[aRecord objectForKey: @"c_isallday"] boolValue])
if (dayBasedView || [[theRecord objectForKey: @"c_isallday"] boolValue])
{ {
for (count = 0; count < 2; count++) for (count = 0; count < 2; count++)
{ {
aDateField = fields[count * 2]; aDateField = fields[count * 2];
aDate = [aRecord objectForKey: aDateField]; aDate = [theRecord objectForKey: aDateField];
daylightOffset = (int) ([userTimeZone secondsFromGMTForDate: aDate] daylightOffset = (int) ([userTimeZone secondsFromGMTForDate: aDate]
- [userTimeZone secondsFromGMTForDate: startDate]); - [userTimeZone secondsFromGMTForDate: startDate]);
if (daylightOffset) if (daylightOffset)
{ {
aDate = [aDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 aDate = [aDate dateByAddingYears: 0 months: 0 days: 0 hours: 0
minutes: 0 seconds: daylightOffset]; minutes: 0 seconds: daylightOffset];
[aRecord setObject: aDate forKey: aDateField]; [theRecord setObject: aDate forKey: aDateField];
aDateValue = [NSNumber numberWithInt: [aDate timeIntervalSince1970]]; aDateValue = [NSNumber numberWithInt: [aDate timeIntervalSince1970]];
[aRecord setObject: aDateValue forKey: fields[count * 2 + 1]]; [theRecord setObject: aDateValue forKey: fields[count * 2 + 1]];
} }
} }
} }
aDateValue = [aRecord objectForKey: @"c_recurrence_id"];
aDate = [aRecord objectForKey: @"cycleStartDate"];
if (aDateValue && aDate)
{
aStartDate = [aRecord objectForKey: @"startDate"];
if ([userTimeZone isDaylightSavingTimeForDate: aStartDate] !=
[userTimeZone isDaylightSavingTimeForDate: aDate])
{
// 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)[userTimeZone secondsFromGMTForDate: aStartDate]
- (signed int)[userTimeZone secondsFromGMTForDate: aDate];
aDateValue = [NSNumber numberWithInt: [aDateValue intValue] + daylightOffset];
[aRecord setObject: aDateValue forKey: @"c_recurrence_id"];
}
}
} }
- (NSArray *) _fetchFields: (NSArray *) fields - (NSArray *) _fetchFields: (NSArray *) fields
@ -341,7 +332,6 @@ static NSArray *tasksFields = nil;
component: component] objectEnumerator]; component: component] objectEnumerator];
owner = [currentFolder ownerInContext: context]; owner = [currentFolder ownerInContext: context];
ownerUser = [SOGoUser userWithLogin: owner]; ownerUser = [SOGoUser userWithLogin: owner];
/* TODO: this should be handled per-folder rather than per-event. */
isErasable = ([owner isEqualToString: userLogin] isErasable = ([owner isEqualToString: userLogin]
|| [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]); || [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]);
while ((newInfo = [currentInfos nextObject])) while ((newInfo = [currentInfos nextObject]))
@ -349,10 +339,12 @@ static NSArray *tasksFields = nil;
if ([fields containsObject: @"editable"]) if ([fields containsObject: @"editable"])
{ {
if (folderIsRemote) if (folderIsRemote)
// .ics subscriptions are not editable
[newInfo setObject: [NSNumber numberWithInt: 0] [newInfo setObject: [NSNumber numberWithInt: 0]
forKey: @"editable"]; forKey: @"editable"];
else else
{ {
// Identifies whether the active user can edit the event.
role = role =
[currentFolder roleForComponentsWithAccessClass: [currentFolder roleForComponentsWithAccessClass:
[[newInfo objectForKey: @"c_classification"] intValue] [[newInfo objectForKey: @"c_classification"] intValue]