From a6b8e42cbf955a7ac2b1c4d2537b41aee8d6b261 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Fri, 13 Jul 2012 13:00:02 +0000 Subject: [PATCH 01/10] Monotone-Parent: 29aefdeb26834742b8cd9f01120573b4dcbcc7ed Monotone-Revision: f9bb5a5f3c1e5dea5cb04ead39373ab637b9ee09 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2012-07-13T13:00:02 --- SOPE/GDLContentStore/GCSSessionsFolder.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SOPE/GDLContentStore/GCSSessionsFolder.m b/SOPE/GDLContentStore/GCSSessionsFolder.m index 21cee0ad9..8aafe3579 100644 --- a/SOPE/GDLContentStore/GCSSessionsFolder.m +++ b/SOPE/GDLContentStore/GCSSessionsFolder.m @@ -188,8 +188,7 @@ static NSString *sessionsFolderURLString = nil; queries = [tc specialQueries]; - sql = [NSString stringWithFormat: @"SELECT count(*) FROM %@", - [self _storeTableName]]; + sql = [NSString stringWithFormat: @"SELECT count(*) FROM %@", tableName]; if ([tc evaluateExpressionX: sql]) { sql = [queries createSessionsFolderWithName: tableName]; From ae84644bdc7f704e3aa65f0179df454270947c3b Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Fri, 13 Jul 2012 14:37:50 +0000 Subject: [PATCH 02/10] Monotone-Parent: f9bb5a5f3c1e5dea5cb04ead39373ab637b9ee09 Monotone-Revision: be001fad1d7b392001a6534b6758e7d78713c9a2 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2012-07-13T14:37:50 --- ChangeLog | 1 - 1 file changed, 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index dab6d37cd..97888f0a8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -112,7 +112,6 @@ * UI/Templates/ContactsUI/UIxContactView.wox: Show all addresses returned from secondaryEmails. This still need some css tweaks. - 2012-06-29 Jean Raby From 6a5ab405a26d13068a14c38bde4d39e04be7a5ca Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Fri, 13 Jul 2012 14:38:06 +0000 Subject: [PATCH 03/10] Updated NEWS file. Monotone-Parent: be001fad1d7b392001a6534b6758e7d78713c9a2 Monotone-Revision: c812fb58ff32ddf15bcafe1527b4871905de1415 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2012-07-13T14:38:06 --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index f650b54ca..4ef19df9b 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,10 @@ New Features - send and/or receive email notifications when a calendar is modified (new domain defaults SOGoNotifyOnPersonalModifications and SOGoNotifyOnExternalModifications) + - added the SOGoSearchMinimumWordLength domain default which controls the + minimal length required before trigging server-side search operations for + attendee completion, contact searches, etc. The default value is 2, which + means search operations are trigged once the 3rd character is typed. Enhancements - updated Czech, French translations From caf58a7c4a5dbf22a27635599cf9d254ef9ca71b Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 13 Jul 2012 20:25:47 +0000 Subject: [PATCH 04/10] Monotone-Parent: c812fb58ff32ddf15bcafe1527b4871905de1415 Monotone-Revision: 9475f39d99549bfa74e7f88be7e54d9650beb0d5 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-13T20:25:47 --- ChangeLog | 6 ++++++ Tests/Integration/test-caldav-scheduling.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 97888f0a8..baa404e10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2012-07-13 Wolfgang Sourdeau + + * Tests/Integration/test-caldav-scheduling.py + (CalDAVSchedulingTest.setUp): use the proper password for + attendee1_delegate. + 2012-07-12 Jean Raby * SoObjects/Mailer/SOGoMailForward.m (from, to, cc, reply-to): diff --git a/Tests/Integration/test-caldav-scheduling.py b/Tests/Integration/test-caldav-scheduling.py index 06b8e2991..71eddac96 100755 --- a/Tests/Integration/test-caldav-scheduling.py +++ b/Tests/Integration/test-caldav-scheduling.py @@ -115,7 +115,7 @@ class CalDAVSchedulingTest(unittest.TestCase): self.attendee1_client = webdavlib.WebDAVClient(hostname, port, attendee1_username, attendee1_password) self.attendee1_delegate_client = webdavlib.WebDAVClient(hostname, port, - attendee1_delegate_username, attendee1_password) + attendee1_delegate_username, attendee1_delegate_password) utility = utilities.TestUtility(self, self.client) (self.user_name, self.user_email) = utility.fetchUserInfo(username) From 82f3f1b3d939d1e54f86c032dfa1ad63c624a770 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 13 Jul 2012 20:31:41 +0000 Subject: [PATCH 05/10] Monotone-Parent: 9475f39d99549bfa74e7f88be7e54d9650beb0d5 Monotone-Revision: d953e1b8cae8bcdf2a3cff4219ea365af8391941 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-13T20:31:41 --- ChangeLog | 8 ++ SoObjects/Appointments/GNUmakefile | 1 + .../Appointments/SOGoAppointmentObject.m | 13 ++- .../Appointments/SOGoCalendarComponent.h | 3 +- .../Appointments/SOGoCalendarComponent.m | 42 +--------- SoObjects/Appointments/SOGoTaskObject.m | 6 ++ SoObjects/Appointments/iCalCalendar+SOGo.h | 39 +++++++++ SoObjects/Appointments/iCalCalendar+SOGo.m | 82 +++++++++++++++++++ 8 files changed, 152 insertions(+), 42 deletions(-) create mode 100644 SoObjects/Appointments/iCalCalendar+SOGo.h create mode 100644 SoObjects/Appointments/iCalCalendar+SOGo.m diff --git a/ChangeLog b/ChangeLog index baa404e10..24061285d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2012-07-13 Wolfgang Sourdeau + * SoObjects/Appointments/SOGoCalendarComponent.m + (-lookupOccurrence:): now a virtual method forcing the use by + subclasses of the new methods below. + + * SoObjects/Appointments/iCalCalendar+SOGo.m: new category module. + (-eventWithRecurrenceId, -todoWithRecurrenceId): new method that + return as recurrence based on the id passed as parameter. + * Tests/Integration/test-caldav-scheduling.py (CalDAVSchedulingTest.setUp): use the proper password for attendee1_delegate. diff --git a/SoObjects/Appointments/GNUmakefile b/SoObjects/Appointments/GNUmakefile index 46f79d469..c9b03f199 100644 --- a/SoObjects/Appointments/GNUmakefile +++ b/SoObjects/Appointments/GNUmakefile @@ -9,6 +9,7 @@ Appointments_PRINCIPAL_CLASS = SOGoAppointmentsProduct Appointments_OBJC_FILES = \ Product.m \ NSArray+Appointments.m \ + iCalCalendar+SOGo.m \ iCalEntityObject+SOGo.m \ iCalRepeatableEntityObject+SOGo.m \ iCalEvent+SOGo.m \ diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index eaa03b11b..30e4b44f7 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -53,6 +53,7 @@ #import #import +#import "iCalCalendar+SOGo.h" #import "iCalEventChanges+SOGo.h" #import "iCalEntityObject+SOGo.h" #import "iCalPerson+SOGo.h" @@ -121,6 +122,12 @@ return newOccurence; } +- (iCalRepeatableEntityObject *) lookupOccurrence: (NSString *) recID + +{ + return [[self calendar: NO secure: NO] eventWithRecurrenceID: recID]; +} + - (SOGoAppointmentObject *) _lookupEvent: (NSString *) eventUID forUID: (NSString *) uid { @@ -839,7 +846,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // If recurrenceId is defined, find the specified occurence // within the repeating vEvent. recurrenceTime = [NSString stringWithFormat: @"%f", [recurrenceId timeIntervalSince1970]]; - oldEvent = (iCalEvent*)[self lookupOccurence: recurrenceTime]; + oldEvent = (iCalEvent*)[self lookupOccurrence: recurrenceTime]; if (oldEvent == nil) // If no occurence found, create one oldEvent = (iCalEvent *)[self newOccurenceWithID: recurrenceTime]; @@ -906,7 +913,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // If recurrenceId is defined, find the specified occurence // within the repeating vEvent. recurrenceTime = [NSString stringWithFormat: @"%f", [recurrenceId timeIntervalSince1970]]; - event = [eventObject lookupOccurence: recurrenceTime]; + event = [eventObject lookupOccurrence: recurrenceTime]; if (event == nil) // If no occurence found, create one @@ -1348,7 +1355,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // If _recurrenceId is defined, find the specified occurence // within the repeating vEvent. recurrenceTime = [NSString stringWithFormat: @"%f", [_recurrenceId timeIntervalSince1970]]; - event = (iCalEvent*)[self lookupOccurence: recurrenceTime]; + event = (iCalEvent*)[self lookupOccurrence: recurrenceTime]; if (event == nil) // If no occurence found, create one diff --git a/SoObjects/Appointments/SOGoCalendarComponent.h b/SoObjects/Appointments/SOGoCalendarComponent.h index d334e7c57..fc111f5a6 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.h +++ b/SoObjects/Appointments/SOGoCalendarComponent.h @@ -86,7 +86,8 @@ - (NSArray *) getUIDsForICalPersons: (NSArray *) iCalPersons; /* recurrences */ -- (iCalRepeatableEntityObject *) lookupOccurence: (NSString *) recID; +/* same as above, but refers to the existing calendar component */ +- (iCalRepeatableEntityObject *) lookupOccurrence: (NSString *) recID; - (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) component; - (iCalRepeatableEntityObject *) newOccurenceWithID: (NSString *) recID; diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index 1385cbe9c..eb25f9876 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -269,45 +269,11 @@ return iCalString; } -static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence, - NSString *recID) +- (iCalRepeatableEntityObject *) lookupOccurrence: (NSString *) recID { - unsigned int seconds, recSeconds; - - seconds = [recID intValue]; - recSeconds = [[occurence recurrenceId] timeIntervalSince1970]; + [self subclassResponsibility: _cmd]; - return (seconds == recSeconds); -} - -- (iCalRepeatableEntityObject *) lookupOccurence: (NSString *) recID -{ - iCalRepeatableEntityObject *component, *occurence, *currentOccurence; - NSArray *occurences; - unsigned int count, max; - - occurence = nil; - - component = [self component: NO secure: NO]; - if ([component hasRecurrenceRules]) - { - occurences = [[self calendar: NO secure: NO] allObjects]; - max = [occurences count]; - count = 1; // skip master event - while (!occurence && count < max) - { - currentOccurence = [occurences objectAtIndex: count]; - if (_occurenceHasID (currentOccurence, recID)) - occurence = currentOccurence; - else - count++; - } - } - else if (_occurenceHasID (component, recID)) - /* The "master" event could be that occurrence. */ - occurence = component; - - return occurence; + return nil; } - (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) component @@ -397,7 +363,7 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence, else if ([lookupName hasPrefix: @"occurence"]) { recID = [lookupName substringFromIndex: 9]; - occurence = [self lookupOccurence: recID]; + occurence = [self lookupOccurrence: recID]; if (occurence) isNewOccurence = NO; else diff --git a/SoObjects/Appointments/SOGoTaskObject.m b/SoObjects/Appointments/SOGoTaskObject.m index 398db5119..5d70dabd0 100644 --- a/SoObjects/Appointments/SOGoTaskObject.m +++ b/SoObjects/Appointments/SOGoTaskObject.m @@ -32,6 +32,7 @@ #import +#import "iCalCalendar+SOGo.h" #import "NSArray+Appointments.h" #import "SOGoAptMailNotification.h" #import "SOGoAppointmentFolder.h" @@ -46,6 +47,11 @@ return @"vtodo"; } +- (iCalRepeatableEntityObject *) lookupOccurrence: (NSString *) recID +{ + return [[self calendar: NO secure: NO] todoWithRecurrenceID: recID]; +} + - (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) occ { NSArray *allTodos; diff --git a/SoObjects/Appointments/iCalCalendar+SOGo.h b/SoObjects/Appointments/iCalCalendar+SOGo.h new file mode 100644 index 000000000..eae3f8175 --- /dev/null +++ b/SoObjects/Appointments/iCalCalendar+SOGo.h @@ -0,0 +1,39 @@ +/* iCalCalendar+SOGo.h - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 ICALCALENDAR_SOGO_H +#define ICALCALENDAR_SOGO_H + +#import + +@class NSString; +@class iCalEvent; +@class iCalToDo; + +@interface iCalCalendar (SOGoExtensions) + +- (iCalEvent *) eventWithRecurrenceID: (NSString *) recID; +- (iCalToDo *) todoWithRecurrenceID: (NSString *) recID; + +@end + +#endif /* ICALCALENDAR_SOGO_H */ diff --git a/SoObjects/Appointments/iCalCalendar+SOGo.m b/SoObjects/Appointments/iCalCalendar+SOGo.m new file mode 100644 index 000000000..d50d30c1d --- /dev/null +++ b/SoObjects/Appointments/iCalCalendar+SOGo.m @@ -0,0 +1,82 @@ +/* iCalCalendar+SOGo.m - this file is part of $PROJECT_NAME_HERE$ + * + * Copyright (C) 2012 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 "iCalCalendar+SOGo.h" + +@implementation iCalCalendar (SOGoExtensions) + +- (id) _occurrence: (NSString *) recID + inArray: (NSArray *) components +{ + id occurrence; + iCalRepeatableEntityObject *component; + NSUInteger count, max, seconds, recSeconds; + + occurrence = nil; + + seconds = [recID intValue]; + + max = [components count]; + + /* master occurrence */ + component = [components objectAtIndex: 0]; + + if ([component hasRecurrenceRules]) + { + count = 1; // skip master event + while (!occurrence && count < max) + { + component = [components objectAtIndex: count]; + recSeconds = [[component recurrenceId] timeIntervalSince1970]; + if (recSeconds == seconds) + occurrence = component; + else + count++; + } + } + else + { + /* The "master" event could be that occurrence. */ + recSeconds = [[component recurrenceId] timeIntervalSince1970]; + if (recSeconds == seconds) + occurrence = component; + } + + return occurrence; +} + +- (iCalEvent *) eventWithRecurrenceID: (NSString *) recID +{ + return [self _occurrence: recID inArray: [self events]]; +} + +- (iCalToDo *) todoWithRecurrenceID: (NSString *) recID; +{ + return [self _occurrence: recID inArray: [self todos]]; +} + +@end From 5b1abf0d68db22ce08f5ef4f95b6dbe40306c00e Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 13 Jul 2012 20:34:13 +0000 Subject: [PATCH 06/10] fixed missing declaration update Monotone-Parent: d953e1b8cae8bcdf2a3cff4219ea365af8391941 Monotone-Revision: 11acebc5a76969e7e9f712ff0325cb10e3171669 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-13T20:34:13 --- SoObjects/Appointments/SOGoCalendarComponent.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SoObjects/Appointments/SOGoCalendarComponent.h b/SoObjects/Appointments/SOGoCalendarComponent.h index fc111f5a6..ebffdb7b0 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.h +++ b/SoObjects/Appointments/SOGoCalendarComponent.h @@ -24,6 +24,7 @@ #ifndef SOGOCALENDARCOMPONENT_H #define SOGOCALENDARCOMPONENT_H +#import #import #import "SOGoComponentOccurence.h" @@ -79,6 +80,7 @@ addedAttendees: (NSArray *) theAddedAttendees deletedAttendees: (NSArray *) theDeletedAttendees updatedAttendees: (NSArray *) theUpdatedAttendees; + operation: (SOGoEventOperation) theOperation; - (iCalPerson *) findParticipantWithUID: (NSString *) uid; From d40be7d196ef9379bd897ec9f004106ff7de95c4 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 13 Jul 2012 20:35:02 +0000 Subject: [PATCH 07/10] fixed missing declaration update Monotone-Parent: 11acebc5a76969e7e9f712ff0325cb10e3171669 Monotone-Revision: 7fb9c9db1f708e5ebd59004e2cd37cf85e5d94d6 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-13T20:35:02 --- SoObjects/Appointments/SOGoCalendarComponent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SoObjects/Appointments/SOGoCalendarComponent.h b/SoObjects/Appointments/SOGoCalendarComponent.h index ebffdb7b0..25b381582 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.h +++ b/SoObjects/Appointments/SOGoCalendarComponent.h @@ -79,7 +79,7 @@ - (void) sendReceiptEmailForObject: (iCalRepeatableEntityObject *) object addedAttendees: (NSArray *) theAddedAttendees deletedAttendees: (NSArray *) theDeletedAttendees - updatedAttendees: (NSArray *) theUpdatedAttendees; + updatedAttendees: (NSArray *) theUpdatedAttendees operation: (SOGoEventOperation) theOperation; - (iCalPerson *) findParticipantWithUID: (NSString *) uid; From cbd4bd907ae597885c5980d1a850454cf59f0113 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 13 Jul 2012 20:36:19 +0000 Subject: [PATCH 08/10] Monotone-Parent: 7fb9c9db1f708e5ebd59004e2cd37cf85e5d94d6 Monotone-Revision: 61a57c3431ce88c941b644d5a0ce21abb1f82f8c Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-13T20:36:19 --- ChangeLog | 2 ++ SoObjects/Appointments/SOGoCalendarComponent.h | 1 + SoObjects/Appointments/SOGoCalendarComponent.m | 13 +++++++------ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 24061285d..df985f619 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,8 @@ * SoObjects/Appointments/SOGoCalendarComponent.m (-lookupOccurrence:): now a virtual method forcing the use by subclasses of the new methods below. + (-saveCalendar:): new method that enables the saving of an + iCalendar object. * SoObjects/Appointments/iCalCalendar+SOGo.m: new category module. (-eventWithRecurrenceId, -todoWithRecurrenceId): new method that diff --git a/SoObjects/Appointments/SOGoCalendarComponent.h b/SoObjects/Appointments/SOGoCalendarComponent.h index 25b381582..3512339c2 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.h +++ b/SoObjects/Appointments/SOGoCalendarComponent.h @@ -62,6 +62,7 @@ toFolder: (SOGoGCSFolder *) newFolder; - (void) updateComponent: (iCalRepeatableEntityObject *) newObject; +- (NSException *) saveCalendar: (iCalCalendar *) newCalendar; - (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject; /* mail notifications */ diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index eb25f9876..20b0ea0e4 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -645,17 +645,18 @@ } } -- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject +- (NSException *) saveCalendar: (iCalCalendar *) newCalendar { - NSString *newiCalString; - - newiCalString = [[newObject parent] versitString]; - - [self saveContentString: newiCalString]; + [self saveContentString: [newCalendar versitString]]; return nil; } +- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject +{ + return [self saveCalendar: [newObject parent]]; +} + /* raw saving */ /* EMail Notifications */ From ee723f9694037b966531e0ab1f1ef98fa84f28f3 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 13 Jul 2012 20:37:16 +0000 Subject: [PATCH 09/10] fixed neglected warnings Monotone-Parent: 61a57c3431ce88c941b644d5a0ce21abb1f82f8c Monotone-Revision: 493dd9034b71a42f4fc3df70a482872a7c50cc22 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-13T20:37:16 --- SoObjects/Appointments/SOGoAppointmentObject.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 30e4b44f7..d5b9b5aec 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -28,6 +28,8 @@ #import #import +#import +#import #import #import #import @@ -35,10 +37,11 @@ #import #import #import +#import #import #import -#import +#import #import #import From 6cd17dc9b8bb1afab627ec1029fdb8ba8bdc5303 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 13 Jul 2012 20:59:09 +0000 Subject: [PATCH 10/10] Monotone-Parent: 493dd9034b71a42f4fc3df70a482872a7c50cc22 Monotone-Revision: f071f53cbc4a39b0b852c58e7e57332f05ef1e0c Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-13T20:59:09 --- ChangeLog | 6 + .../Appointments/SOGoAppointmentObject.h | 5 + .../Appointments/SOGoAppointmentObject.m | 251 ++++++++---------- 3 files changed, 115 insertions(+), 147 deletions(-) diff --git a/ChangeLog b/ChangeLog index df985f619..a32dc5420 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2012-07-13 Wolfgang Sourdeau + * SoObjects/Appointments/SOGoAppointmentObject.m + (-updateContentWithCalendar:fromRequest:): new method spawned from + the previous incarnation of PUTRequest:, designed to centralize + all the processed performed on an iCalendar instance, new or + derived from the original. + * SoObjects/Appointments/SOGoCalendarComponent.m (-lookupOccurrence:): now a virtual method forcing the use by subclasses of the new methods below. diff --git a/SoObjects/Appointments/SOGoAppointmentObject.h b/SoObjects/Appointments/SOGoAppointmentObject.h index 290f568b9..6c0aebd01 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.h +++ b/SoObjects/Appointments/SOGoAppointmentObject.h @@ -28,6 +28,8 @@ @class NSException; @class NSString; +@class WORequest; + @class iCalEvent; @class iCalCalendar; @@ -49,6 +51,9 @@ - (NSArray *) postCalDAVEventReplyTo: (NSArray *) recipients from: (NSString *) originator; - (NSArray *) postCalDAVEventCancelTo: (NSArray *) recipients from: (NSString *) originator; +- (NSException *) updateContentWithCalendar: (iCalCalendar *) calendar + fromRequest: (WORequest *) rq; + @end #endif /* __Appointments_SOGoAppointmentObject_H__ */ diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index d5b9b5aec..052024dbb 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -188,54 +188,37 @@ SOGoAppointmentObject *attendeeObject; NSString *iCalString; + iCalString = nil; attendeeObject = [self _lookupEvent: [theEvent uid] forUID: theUID]; // We must add an occurence to a non-existing event. We have // to handle this with care, as in the postCalDAVEventRequestTo:from: if ([attendeeObject isNew] && [theEvent recurrenceId]) { - SOGoAppointmentObject *ownerObject; - NSArray *attendees; iCalEvent *ownerEvent; iCalPerson *person; SOGoUser *user; - BOOL found; - int i; // We check if the attendee that was added to a single occurence is // present in the master component. If not, we add it with a participation // status set to "DECLINED". - user = [SOGoUser userWithLogin: theUID]; - person = [iCalPerson elementWithTag: @"attendee"]; - [person setCn: [user cn]]; - [person setEmail: [[user allEmails] objectAtIndex: 0]]; - [person setParticipationStatus: iCalPersonPartStatDeclined]; - [person setRsvp: @"TRUE"]; - [person setRole: @"REQ-PARTICIPANT"]; - - ownerObject = [self _lookupEvent: [theEvent uid] forUID: theOwner]; ownerEvent = [[[theEvent parent] events] objectAtIndex: 0]; - attendees = [ownerEvent attendees]; - found = NO; - - for (i = 0; i < [attendees count]; i++) - { - if ([[attendees objectAtIndex: i] hasSameEmailAddress: person]) - { - found = YES; - break; - } - } - - if (!found) + user = [SOGoUser userWithLogin: theUID]; + if (![ownerEvent userAsAttendee: user]) { // Update the master event in the owner's calendar with the // status of the new attendee set as "DECLINED". + person = [iCalPerson elementWithTag: @"attendee"]; + [person setCn: [user cn]]; + [person setEmail: [[user allEmails] objectAtIndex: 0]]; + [person setParticipationStatus: iCalPersonPartStatDeclined]; + [person setRsvp: @"TRUE"]; + [person setRole: @"REQ-PARTICIPANT"]; [ownerEvent addToAttendees: person]; - iCalString = [[ownerEvent parent] versitString]; - [ownerObject saveContentString: iCalString]; + + iCalString = [[ownerEvent parent] versitString]; } - } + } else { // TODO : if [theEvent recurrenceId], only update this occurrence @@ -249,7 +232,8 @@ } // Save the event in the attendee's calendar - [attendeeObject saveContentString: iCalString]; + if (iCalString) + [attendeeObject saveContentString: iCalString]; } } @@ -277,7 +261,7 @@ folder = [[SOGoUser userWithLogin: theUID] personalCalendarFolderInContext: context]; object = [folder lookupName: nameInContainer - inContext: context acquire: NO]; + inContext: context acquire: NO]; if (![object isKindOfClass: [NSException class]]) { if (recurrenceId == nil) @@ -458,7 +442,7 @@ NSEnumerator *enumerator; NSString *currentUID; SOGoUser *user; - + enumerator = [theAttendees objectEnumerator]; while ((currentAttendee = [enumerator nextObject])) @@ -654,7 +638,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent [e addToAttendees: [theAttendees objectAtIndex: j]]; else [e removeFromAttendees: [theAttendees objectAtIndex: j]]; - + } } @@ -801,7 +785,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent SOGoUser *ownerUser; NSArray *attendees; NSException *ex; - + [[newEvent parent] setMethod: @""]; ownerUser = [SOGoUser userWithLogin: owner]; @@ -1026,7 +1010,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent statusChange: (NSString *) newStatus inEvent: (iCalEvent *) event { - NSString *newContent, *currentStatus, *organizerUID; + NSString *currentStatus, *organizerUID; SOGoUser *ownerUser, *currentUser; NSException *ex; @@ -1171,14 +1155,18 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent #endif } + /* // We generate the updated iCalendar file and we save it in the database. // We do this ONLY when using SOGo from the Web interface. Over DAV, it'll // be handled directly in PUTAction: + if (![context request] || [[context request] handledByDefaultHandler]) { + NSString *newContent; newContent = [[event parent] versitString]; ex = [self saveContentString: newContent]; } + */ // If the current user isn't the organizer of the event // that has just been updated, we update the event and @@ -1350,7 +1338,8 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent ex = nil; delegatedUser = nil; - calendar = [self calendar: NO secure: NO]; + calendar = [[self calendar: NO secure: NO] mutableCopy]; + [calendar autorelease]; if (calendar) { if (_recurrenceId) @@ -1545,11 +1534,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent return partStats; } -#warning parseSingleFromSource is invoked far too many times: maybe we should use an additional ivar to store the new iCalendar -- (void) _setupResponseCalendarInRequest: (WORequest *) rq +- (void) _setupResponseInRequestCalendar: (iCalCalendar *) rqCalendar { - iCalCalendar *calendar, *putCalendar; - NSData *newContent; + iCalCalendar *calendar; NSArray *keys; NSDictionary *partStats, *newPartStats; NSString *partStat, *key; @@ -1561,8 +1548,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent max = [keys count]; if (max > 0) { - putCalendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]]; - newPartStats = [self _partStatsFromCalendar: putCalendar]; + newPartStats = [self _partStatsFromCalendar: rqCalendar]; if ([keys isEqualToArray: [newPartStats allKeys]]) { for (count = 0; count < max; count++) @@ -1573,37 +1559,23 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent } } } - - newContent = [[calendar versitString] - dataUsingEncoding: [rq contentEncoding]]; - [rq setContent: newContent]; } -- (void) _adjustTransparencyInRequest: (WORequest *) rq +- (void) _adjustTransparencyInRequestCalendar: (iCalCalendar *) rqCalendar { - iCalCalendar *calendar; NSArray *allEvents; iCalEvent *event; int i; - BOOL modified; - calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]]; - allEvents = [calendar events]; - modified = NO; - + allEvents = [rqCalendar events]; for (i = 0; i < [allEvents count]; i++) { event = [allEvents objectAtIndex: i]; - if ([event isAllDay] && [event isOpaque]) { [event setTransparency: @"TRANSPARENT"]; - modified = YES; - } + } } - - if (modified) - [rq setContent: [[calendar versitString] dataUsingEncoding: [rq contentEncoding]]]; } /** @@ -1611,17 +1583,13 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent * Currently only check if the events have an end date or a duration. * @param rq the HTTP PUT request */ -- (void) _adjustEventsInRequest: (WORequest *) rq +- (void) _adjustEventsInRequestCalendar: (iCalCalendar *) rqCalendar { - iCalCalendar *calendar; NSArray *allEvents; iCalEvent *event; NSUInteger i; - BOOL modified; - calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]]; - allEvents = [calendar events]; - modified = NO; + allEvents = [rqCalendar events]; for (i = 0; i < [allEvents count]; i++) { @@ -1634,28 +1602,16 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent [event setDuration: @"P1D"]; else [event setDuration: @"PT1H"]; - - modified = YES; - [self errorWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]]; + [self warnWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]]; } } - - if (modified) - [rq setContent: [[calendar versitString] dataUsingEncoding: [rq contentEncoding]]]; } -- (void) _decomposeGroupsInRequest: (WORequest *) rq +- (void) _decomposeGroupsInRequestCalendar: (iCalCalendar *) rqCalendar { - iCalCalendar *calendar; NSArray *allEvents; iCalEvent *event; int i; - BOOL modified; - - // If we decomposed at least one group, let's rewrite the content - // of the request. Otherwise, leave it as is in case this rewrite - // isn't totaly lossless. - calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]]; // The algorithm is pretty straightforward: // @@ -1664,20 +1620,12 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // If some are groups, we decompose them // We regenerate the iCalendar string // - allEvents = [calendar events]; - modified = NO; - + allEvents = [rqCalendar events]; for (i = 0; i < [allEvents count]; i++) { event = [allEvents objectAtIndex: i]; - modified |= [self expandGroupsInEvent: event]; + [self expandGroupsInEvent: event]; } - - // If we decomposed at least one group, let's rewrite the content - // of the request. Otherwise, leave it as is in case this rewrite - // isn't totaly lossless. - if (modified) - [rq setContent: [[calendar versitString] dataUsingEncoding: [rq contentEncoding]]]; } @@ -1741,25 +1689,28 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent } // -// If we see "X-SOGo: NoGroupsDecomposition" in the HTTP headers, we -// simply invoke super's PUTAction. +// This method is meant to be the common point of any save operation from web +// and DAV requests, as well as from code making use of SOGo as a library +// (OpenChange) // -// We also check if we must force transparency on all day events -// from iPhone clients. -// -- (id) PUTAction: (WOContext *) _ctx +- (NSException *) updateContentWithCalendar: (iCalCalendar *) calendar + fromRequest: (WORequest *) rq { NSException *ex; - NSString *etag; NSArray *roles; - WORequest *rq; - id response; + SOGoUser *ownerUser; - unsigned int baseVersion; + if (calendar == fullCalendar + || calendar == safeCalendar + || calendar == originalCalendar) + [NSException raise: NSInvalidArgumentException + format: @"the 'calendar' argument must be a distinct instance" + @" from the original object"]; - rq = [_ctx request]; - roles = [[context activeUser] rolesForObject: self inContext: context]; + ownerUser = [SOGoUser userWithLogin: owner]; + roles = [[context activeUser] rolesForObject: self + inContext: context]; // // We check if we gave only the "Respond To" right and someone is actually // responding to one of our invitation. In this case, _setupResponseCalendarInRequest @@ -1767,24 +1718,20 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // if ([roles containsObject: @"ComponentResponder"] && ![roles containsObject: @"ComponentModifier"]) - [self _setupResponseCalendarInRequest: rq]; + [self _setupResponseInRequestCalendar: calendar]; else { - SOGoUser *user; - - user = [SOGoUser userWithLogin: owner]; - if (![[rq headersForKey: @"X-SOGo"] - containsObject: @"NoGroupsDecomposition"]) - [self _decomposeGroupsInRequest: rq]; + containsObject: @"NoGroupsDecomposition"]) + [self _decomposeGroupsInRequestCalendar: calendar]; - if ([[user domainDefaults] iPhoneForceAllDayTransparency] + if ([[ownerUser domainDefaults] iPhoneForceAllDayTransparency] && [rq isIPhone]) { - [self _adjustTransparencyInRequest: rq]; + [self _adjustTransparencyInRequestCalendar: calendar]; } - [self _adjustEventsInRequest: rq]; + [self _adjustEventsInRequestCalendar: calendar]; } // @@ -1792,25 +1739,20 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // if ([self isNew]) { - iCalCalendar *calendar; - SOGoUser *ownerUser; - iCalEvent *event, *conflictingEvent; + iCalEvent *event; NSArray *attendees; - NSString *eventUID; BOOL scheduling; - calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]]; attendees = nil; event = [[calendar events] objectAtIndex: 0]; eventUID = [event uid]; - ownerUser = [SOGoUser userWithLogin: owner]; scheduling = [self _shouldScheduleEvent: [event organizer]]; // make sure eventUID doesn't conflict with an existing event - see bug #1853 // TODO: send out a no-uid-conflict (DAV:href) xml element (rfc4791 section 5.3.2.1) - if (conflictingEvent = [container resourceNameForEventUID: eventUID]) + if ([container resourceNameForEventUID: eventUID]) { return [NSException exceptionWithHTTPStatus: 403 reason: [NSString stringWithFormat: @"Event UID already in use. (%s)", eventUID]]; @@ -1862,33 +1804,21 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent } // if ([self isNew]) else { - iCalCalendar *oldCalendar, *newCalendar; + iCalCalendar *oldCalendar; iCalEvent *oldEvent, *newEvent; iCalEventChanges *changes; - NSMutableArray *oldEvents, *newEvents; + NSArray *oldEvents, *newEvents; NSCalendarDate *recurrenceId; - NSException *error; BOOL master; int i; - // - // We must check for etag changes prior doing anything since an attendee could - // have changed its participation status and the organizer didn't get the - // copy and is trying to do a modification to the event. - // - error = [self matchesRequestConditionInContext: _ctx]; - - if (error) - return (WOResponse *)error; - // // We check what has changed in the event and react accordingly. // - newCalendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]]; - newEvents = [NSMutableArray arrayWithArray: [newCalendar events]]; + newEvents = [calendar events]; oldCalendar = [self calendar: NO secure: NO]; - oldEvents = [NSMutableArray arrayWithArray: [oldCalendar events]]; + oldEvents = [oldCalendar events]; recurrenceId = nil; master = NO; @@ -1921,8 +1851,8 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent } else { - [newEvents removeObject: oldEvent]; - [oldEvents removeObject: newEvent]; + [calendar removeChild: oldEvent]; + [oldCalendar removeChild: newEvent]; } } @@ -1991,13 +1921,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent])) return ex; - else - { - // We might have auto-accepted resources here. If that's the - // case, let's regenerate the versitstring and replace the - // one from the request. - [rq setContent: [[[newEvent parent] versitString] dataUsingEncoding: [rq contentEncoding]]]; - } } // // else => attendee is responding @@ -2068,23 +1991,57 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent } } // else of if (isNew) ... + unsigned int baseVersion; // We must NOT invoke [super PUTAction:] here as it'll resave // the content string and we could have etag mismatches. - response = [_ctx response]; - baseVersion = (isNew ? 0 : version); - ex = [self saveContentString: [rq contentAsString] + ex = [self saveContentString: [calendar versitString] baseVersion: baseVersion]; + + return ex; +} + +// +// If we see "X-SOGo: NoGroupsDecomposition" in the HTTP headers, we +// simply invoke super's PUTAction. +// +// We also check if we must force transparency on all day events +// from iPhone clients. +// +- (id) PUTAction: (WOContext *) _ctx +{ + NSException *ex; + NSString *etag; + WORequest *rq; + WOResponse *response; + iCalCalendar *rqCalendar; + + rq = [_ctx request]; + rqCalendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]]; + + if (![self isNew]) + { + // + // We must check for etag changes prior doing anything since an attendee could + // have changed its participation status and the organizer didn't get the + // copy and is trying to do a modification to the event. + // + ex = [self matchesRequestConditionInContext: context]; + if (ex) + return ex; + } + + ex = [self updateContentWithCalendar: rqCalendar fromRequest: rq]; if (ex) response = (WOResponse *) ex; else { + response = [_ctx response]; if (isNew) [response setStatus: 201 /* Created */]; else [response setStatus: 204 /* No Content */]; - etag = [self davEntityTag]; if (etag) [response setHeader: etag forKey: @"etag"];