diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 4bd77e0e5..61e4f071a 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2007-2014 Inverse inc. + Copyright (C) 2007-2019 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo. @@ -37,6 +37,7 @@ #import #import #import +#import #import #import #import @@ -50,6 +51,7 @@ #import #import #import +#import #import #import #import @@ -3392,7 +3394,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir NSMutableDictionary *timezones, *uids; NSString *tzId, *uid, *originalUid; iCalEntityObject *element; - iCalDateTime *startDate; iCalTimeZone *timezone; iCalCalendar *masterCalendar; iCalEvent *event; @@ -3436,92 +3437,29 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir timezone = nil; element = [components objectAtIndex: i]; - // Use the timezone of the start date. - startDate = (iCalDateTime *) [element uniqueChildWithTag: @"dtstart"]; - if (startDate) - { - tzId = [startDate value: 0 ofAttribute: @"tzid"]; - if ([tzId length]) - timezone = [timezones valueForKey: tzId]; - else - { - // If the start date is a "floating time", let's use the user's timezone - // during the import for both the start and end dates. This is similar - // to what we do in SOGoAppointmentObject: -_adjustFloatingTimeInRequestCalendar: - NSString *s; - - s = [[startDate valuesAtIndex: 0 forKey: @""] objectAtIndex: 0]; - - if ([element isKindOfClass: iCalEventK] && - ![(iCalEvent *)element isAllDay] && - ![s hasSuffix: @"Z"] && - ![s hasSuffix: @"z"]) - { - iCalDateTime *endDate; - int delta; - - timezone = [iCalTimeZone timeZoneForName: [[[self->context activeUser] userDefaults] timeZoneName]]; - [calendar addTimeZone: timezone]; - - delta = [[timezone periodForDate: [startDate dateTime]] secondsOffsetFromGMT]; - event = (iCalEvent *)element; - - [event setStartDate: [[event startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]]; - [startDate setTimeZone: timezone]; - endDate = (iCalDateTime *) [element uniqueChildWithTag: @"dtend"]; - - if (endDate) - { - [event setEndDate: [[event endDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]]; - [endDate setTimeZone: timezone]; - } - } - } - - if ([element isKindOfClass: iCalEventK]) + if ([element isKindOfClass: iCalEventK]) + { + event = (iCalEvent *)element; + timezone = [event adjustInContext: self->context]; + + if ([event recurrenceId]) { - event = (iCalEvent *)element; - if (![event hasEndDate] && ![event hasDuration]) + // Event is an occurrence of a repeating event + if ((uid = [uids valueForKey: [event uid]])) { - // 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 %@ for UID = %@", [event duration], [event uid]]; - } - // - // We check for broken all-day events (like the ones coming from the "WebCalendar" tool) where - // the start date is equal to the end date. This clearly violates the RFC: - // - // 3.8.2.2. Date-Time End - // The value MUST be later in time than the value of the "DTSTART" property. - // - if ([event isAllDay] && [[event startDate] isEqual: [event endDate]]) - { - [event setEndDate: [[event startDate] dateByAddingYears: 0 months: 0 days: 1 hours: 0 minutes: 0 seconds: 0]]; - [self errorWithFormat: @"Fixed broken all-day event; setting end date to %@ for UID = %@", [event endDate], [event uid]]; - } - if ([event recurrenceId]) - { - // Event is an occurrence of a repeating event - if ((uid = [uids valueForKey: [event uid]])) + SOGoAppointmentObject *master = [self lookupName: uid + inContext: context + acquire: NO]; + if (master) { - SOGoAppointmentObject *master = [self lookupName: uid - inContext: context - acquire: NO]; - if (master) - { - // Associate the occurrence to the master event and skip the actual import process - masterCalendar = [master calendar: NO secure: NO]; - [masterCalendar addToEvents: event]; - if (timezone) - [masterCalendar addTimeZone: timezone]; - [master saveCalendar: masterCalendar]; - continue; - } + // Associate the occurrence to the master event and skip the actual import process + masterCalendar = [master calendar: NO secure: NO]; + [masterCalendar addToEvents: event]; + if (timezone) + [masterCalendar addTimeZone: timezone]; + [master saveCalendar: masterCalendar]; + continue; } } } diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 97a286405..4cd5e7a6c 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -54,6 +54,7 @@ #import #import "iCalCalendar+SOGo.h" +#import "iCalEvent+SOGo.h" #import "iCalEventChanges+SOGo.h" #import "iCalEntityObject+SOGo.h" #import "iCalPerson+SOGo.h" @@ -1930,6 +1931,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { NSArray *allEvents; iCalEvent *event; + iCalTimeZone *tz; NSUInteger i; int j; @@ -1939,15 +1941,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { event = [allEvents objectAtIndex: i]; - if (![event hasEndDate] && ![event hasDuration]) - { - // No end date, no duration - if ([event isAllDay]) - [event setDuration: @"P1D"]; - else - [event setDuration: @"PT1H"]; - [self warnWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]]; - } + tz = [event adjustInContext: context]; + if (tz) + [rqCalendar addTimeZone: tz]; if ([event organizer]) { @@ -1994,53 +1990,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent } } -// -// This is similar to what we do in SOGoAppointmentFolder: -importCalendar: -// -- (void) _adjustFloatingTimeInRequestCalendar: (iCalCalendar *) rqCalendar -{ - iCalDateTime *startDate, *endDate; - NSString *startDateAsString; - SOGoUserDefaults *ud; - NSArray *allEvents; - iCalTimeZone *tz; - iCalEvent *event; - int i, delta; - - allEvents = [rqCalendar events]; - for (i = 0; i < [allEvents count]; i++) - { - event = [allEvents objectAtIndex: i]; - - if ([event isAllDay]) - continue; - - startDate = (iCalDateTime *)[event uniqueChildWithTag: @"dtstart"]; - startDateAsString = [[startDate valuesAtIndex: 0 forKey: @""] objectAtIndex: 0]; - - if (![startDate timeZone] && - ![startDateAsString hasSuffix: @"Z"] && - ![startDateAsString hasSuffix: @"z"]) - { - ud = [[context activeUser] userDefaults]; - tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; - if ([rqCalendar addTimeZone: tz]) - { - delta = [[tz periodForDate: [startDate dateTime]] secondsOffsetFromGMT]; - - [event setStartDate: [[event startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]]; - [startDate setTimeZone: tz]; - - endDate = (iCalDateTime *) [event uniqueChildWithTag: @"dtend"]; - if (endDate) - { - [event setEndDate: [[event endDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]]; - [endDate setTimeZone: tz]; - } - } - } - } -} - (void) _decomposeGroupsInRequestCalendar: (iCalCalendar *) rqCalendar { @@ -2163,7 +2112,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent [self _adjustEventsInRequestCalendar: calendar]; [self adjustClassificationInRequestCalendar: calendar]; [self _adjustPartStatInRequestCalendar: calendar]; - [self _adjustFloatingTimeInRequestCalendar: calendar]; } // diff --git a/SoObjects/Appointments/iCalEvent+SOGo.h b/SoObjects/Appointments/iCalEvent+SOGo.h index e0172b8a3..ea4682d82 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.h +++ b/SoObjects/Appointments/iCalEvent+SOGo.h @@ -1,6 +1,6 @@ /* iCalEvent+SOGo.h - this file is part of SOGo * - * Copyright (C) 2007-2015 Inverse inc. + * Copyright (C) 2007-2019 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 @@ -23,6 +23,7 @@ #import +@class iCalTimeZone; @class NSMutableDictionary; @interface iCalEvent (SOGoExtensions) @@ -30,6 +31,7 @@ - (BOOL) isStillRelevant; - (NSTimeInterval) occurenceInterval; - (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate; +- (iCalTimeZone *) adjustInContext: (WOContext *) context; @end diff --git a/SoObjects/Appointments/iCalEvent+SOGo.m b/SoObjects/Appointments/iCalEvent+SOGo.m index d297bead8..15509f86d 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.m +++ b/SoObjects/Appointments/iCalEvent+SOGo.m @@ -27,9 +27,10 @@ #import #import -#import #import #import +#import +#import #import #import @@ -68,7 +69,7 @@ // - (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent container: (id) theContainer - nameInContainer: (NSString *) nameInContainer + nameInContainer: (NSString *) nameInContainer { NSMutableDictionary *row; NSCalendarDate *startDate, *endDate; @@ -125,15 +126,15 @@ boolTmp = ((isAllDay) ? 1 : 0); [row setObject: [NSNumber numberWithInt: boolTmp] - forKey: @"c_isallday"]; + forKey: @"c_isallday"]; boolTmp = ((([self isRecurrent] || [self recurrenceId])) ? 1 : 0); [row setObject: [NSNumber numberWithInt: boolTmp] - forKey: @"c_iscycle"]; + forKey: @"c_iscycle"]; boolTmp = (([self isOpaque]) ? 1 : 0); [row setObject: [NSNumber numberWithInt: boolTmp] - forKey: @"c_isopaque"]; + forKey: @"c_isopaque"]; [row setObject: [NSNumber numberWithInt: [self priorityNumber]] - forKey: @"c_priority"]; + forKey: @"c_priority"]; [row setObject: title forKey: @"c_title"]; if ([location isNotNull]) [row setObject: location forKey: @"c_location"]; @@ -151,8 +152,8 @@ startDate = [timeZone computedDateForDate: startDate]; } [row setObject: [self quickRecordDateAsNumber: startDate - withOffset: 0 - forAllDay: isAllDay] + withOffset: 0 + forAllDay: isAllDay] forKey: @"c_startdate"]; } if ([endDate isNotNull]) @@ -167,8 +168,8 @@ endDate = [timeZone computedDateForDate: endDate]; } [row setObject: [self quickRecordDateAsNumber: endDate - withOffset: ((isAllDay) ? -1 : 0) - forAllDay: isAllDay] + withOffset: ((isAllDay) ? -1 : 0) + forAllDay: isAllDay] forKey: @"c_enddate"]; } @@ -274,7 +275,7 @@ [row setObject: [self comment] forKey: @"c_description"]; else [row setObject: [NSNull null] forKey: @"c_description"]; - + return row; } @@ -305,11 +306,11 @@ // 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]; + months:0 + days:0 + hours:0 + minutes:0 + seconds:offset]; [rule setUntilDate: untilDate]; } } @@ -324,11 +325,11 @@ // 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]; + months:0 + days:0 + hours:0 + minutes:0 + seconds:offset]; [rule setUntilDate: untilDate]; } } @@ -444,4 +445,97 @@ } } +- (iCalTimeZone *) adjustInContext: (WOContext *) context +{ + iCalDateTime *startDate, *endDate; + iCalTimeZone *timezone; + NSCalendarDate *date; + NSString *startDateAsString, *timezoneId; + SOGoUserDefaults *ud; + int delta; + + delta = 0; + timezone = nil; + + startDate = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"]; + endDate = (iCalDateTime *) [self uniqueChildWithTag: @"dtend"]; + + if (![startDate dateTime]) + { + if ([endDate dateTime]) + { + // End date but no start date + delta = 60*60; // 1 hour + [self setStartDate: [[self endDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]]; + } + else + { + // No start date, no end date; start the event at the first "working" hour + date = [[NSCalendarDate calendarDate] beginOfDayForUser: [context activeUser]]; + [self setStartDate: date]; + } + startDate = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"]; + [self errorWithFormat: @"Fixed event with no start date; setting start date to %@ for UID %@", [startDate dateTime], [self uid]]; + } + + if ([startDate dateTime]) + { + timezoneId = [startDate value: 0 ofAttribute: @"tzid"]; + if ([timezoneId length]) + { + timezone = [iCalTimeZone timeZoneForName: timezoneId]; + } + else + { + startDateAsString = [[startDate valuesAtIndex: 0 forKey: @""] objectAtIndex: 0]; + if (![startDateAsString hasSuffix: @"Z"] && + ![startDateAsString hasSuffix: @"z"]) + { + // The start date is a "floating time", let's use the user's timezone + // for both the start and end dates. + ud = [[context activeUser] userDefaults]; + timezone = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; + delta = [[timezone periodForDate: [startDate dateTime]] secondsOffsetFromGMT]; + + [self setStartDate: [[self startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]]; + [startDate setTimeZone: timezone]; + + if ([endDate dateTime]) + { + [self setEndDate: [[self endDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]]; + [endDate setTimeZone: timezone]; + } + } + } + } + + if (![endDate dateTime] && ![self hasDuration]) + { + // No end date, no duration + if ([self isAllDay]) + [self setDuration: @"P1D"]; + else + [self setDuration: @"PT1H"]; + + [self errorWithFormat: @"Fixed event with no end date; setting duration to %@ for UID %@", [self duration], [self uid]]; + } + + // + // We check for broken all-day events (like the ones coming from the "WebCalendar" tool) where + // the start date is equal to the end date. This clearly violates the RFC: + // + // 3.8.2.2. Date-Time End + // The value MUST be later in time than the value of the "DTSTART" property. + // + if ([self isAllDay] && [[self startDate] isEqual: [self endDate]]) + { + [self setEndDate: [[self startDate] dateByAddingYears: 0 months: 0 days: 1 hours: 0 minutes: 0 seconds: 0]]; + [self errorWithFormat: @"Fixed broken all-day event; setting end date to %@ for UID %@", [self endDate], [self uid]]; + } + + + return timezone; + +} + @end diff --git a/SoObjects/SOGo/NSCalendarDate+SOGo.h b/SoObjects/SOGo/NSCalendarDate+SOGo.h index c57cd80e6..33805232b 100644 --- a/SoObjects/SOGo/NSCalendarDate+SOGo.h +++ b/SoObjects/SOGo/NSCalendarDate+SOGo.h @@ -1,31 +1,32 @@ /* NSCalendarDate+SOGo.h - this file is part of SOGo - Copyright (C) 2000-2004 SKYRIX Software AG - - This file is part of OGo - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ + * + * Copyright (C) 2019 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ #ifndef NSCALENDARDATE_SCHEDULER_H #define NSCALENDARDATE_SCHEDULER_H #import +@class NSCalendarDate; @class NSString; @class NSTimeZone; +@class SOGoUser; @interface NSCalendarDate (SOGoExtensions) @@ -34,6 +35,7 @@ inTimeZone: (NSTimeZone *) timeZone; - (BOOL) isDateInSameMonth: (NSCalendarDate *) _other; +- (NSCalendarDate *) beginOfDayForUser: (SOGoUser *) user; - (NSString *) shortDateString; - (NSString *) rfc822DateString; diff --git a/SoObjects/SOGo/NSCalendarDate+SOGo.m b/SoObjects/SOGo/NSCalendarDate+SOGo.m index dc9ba8b4c..b2d8c37a7 100644 --- a/SoObjects/SOGo/NSCalendarDate+SOGo.m +++ b/SoObjects/SOGo/NSCalendarDate+SOGo.m @@ -1,29 +1,31 @@ /* NSCalendarDate+SOGo.m - this file is part of SOGo - Copyright (C) 2000-2004 SKYRIX Software AG - - This file is part of OGo - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ + * + * Copyright (C) 2019 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ #import #import #import +#import +#import + #import "NSCalendarDate+SOGo.h" static NSString *rfc822Days[] = {@"Sun", @"Mon", @"Tue", @"Wed", @"Thu", @@ -96,6 +98,26 @@ static NSString *rfc822Months[] = {@"", @"Jan", @"Feb", @"Mar", @"Apr", return str; } +- (NSCalendarDate *) beginOfDayForUser: (SOGoUser *) user +{ + NSCalendarDate *date; + NSTimeZone *timeZone; + SOGoUserDefaults *ud; + + ud = [user userDefaults]; + timeZone = [ud timeZone]; + [self setTimeZone: timeZone]; + date = [self beginOfDay]; + date = [date addYear: 0 + month: 0 + day: 0 + hour: 0 - [date hourOfDay] + [ud dayStartHour] + minute: 0 - [date minuteOfHour] + second: 0]; + + return date; +} + - (NSString *) rfc822DateString { int timeZoneShift, tzSeconds;