propagate from branch 'ca.inverse.sogo.1_3_17' (head f071f53cbc4a39b0b852c58e7e57332f05ef1e0c)
to branch 'ca.inverse.sogo' (head 4117fa474ee2ae428f109a99cb50a33479342fa8) Monotone-Parent: 4117fa474ee2ae428f109a99cb50a33479342fa8 Monotone-Parent: f071f53cbc4a39b0b852c58e7e57332f05ef1e0c Monotone-Revision: c4c3d792d4b668f2d69a06c140f8d2223d8dbc7c Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-13T21:00:00 Monotone-Branch: ca.inverse.sogomaint-2.0.2
commit
0e276d2acc
23
ChangeLog
23
ChangeLog
|
@ -1,3 +1,25 @@
|
||||||
|
2012-07-13 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
(-saveCalendar:): new method that enables the saving of an
|
||||||
|
iCalendar object.
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
2012-07-12 Jean Raby <jraby@inverse.ca>
|
2012-07-12 Jean Raby <jraby@inverse.ca>
|
||||||
|
|
||||||
* SoObjects/Mailer/SOGoMailForward.m (from, to, cc, reply-to):
|
* SoObjects/Mailer/SOGoMailForward.m (from, to, cc, reply-to):
|
||||||
|
@ -113,7 +135,6 @@
|
||||||
Show all addresses returned from secondaryEmails.
|
Show all addresses returned from secondaryEmails.
|
||||||
This still need some css tweaks.
|
This still need some css tweaks.
|
||||||
|
|
||||||
|
|
||||||
2012-07-01 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
2012-07-01 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
|
|
||||||
* OpenChange/MAPIStoreRecurrenceUtils.m
|
* OpenChange/MAPIStoreRecurrenceUtils.m
|
||||||
|
|
4
NEWS
4
NEWS
|
@ -5,6 +5,10 @@ New Features
|
||||||
- send and/or receive email notifications when a calendar is modified (new
|
- send and/or receive email notifications when a calendar is modified (new
|
||||||
domain defaults SOGoNotifyOnPersonalModifications and
|
domain defaults SOGoNotifyOnPersonalModifications and
|
||||||
SOGoNotifyOnExternalModifications)
|
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
|
Enhancements
|
||||||
- updated Czech, French translations
|
- updated Czech, French translations
|
||||||
|
|
|
@ -188,8 +188,7 @@ static NSString *sessionsFolderURLString = nil;
|
||||||
|
|
||||||
queries = [tc specialQueries];
|
queries = [tc specialQueries];
|
||||||
|
|
||||||
sql = [NSString stringWithFormat: @"SELECT count(*) FROM %@",
|
sql = [NSString stringWithFormat: @"SELECT count(*) FROM %@", tableName];
|
||||||
[self _storeTableName]];
|
|
||||||
if ([tc evaluateExpressionX: sql])
|
if ([tc evaluateExpressionX: sql])
|
||||||
{
|
{
|
||||||
sql = [queries createSessionsFolderWithName: tableName];
|
sql = [queries createSessionsFolderWithName: tableName];
|
||||||
|
|
|
@ -9,6 +9,7 @@ Appointments_PRINCIPAL_CLASS = SOGoAppointmentsProduct
|
||||||
Appointments_OBJC_FILES = \
|
Appointments_OBJC_FILES = \
|
||||||
Product.m \
|
Product.m \
|
||||||
NSArray+Appointments.m \
|
NSArray+Appointments.m \
|
||||||
|
iCalCalendar+SOGo.m \
|
||||||
iCalEntityObject+SOGo.m \
|
iCalEntityObject+SOGo.m \
|
||||||
iCalRepeatableEntityObject+SOGo.m \
|
iCalRepeatableEntityObject+SOGo.m \
|
||||||
iCalEvent+SOGo.m \
|
iCalEvent+SOGo.m \
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
@class NSException;
|
@class NSException;
|
||||||
@class NSString;
|
@class NSString;
|
||||||
|
|
||||||
|
@class WORequest;
|
||||||
|
|
||||||
@class iCalEvent;
|
@class iCalEvent;
|
||||||
@class iCalCalendar;
|
@class iCalCalendar;
|
||||||
|
|
||||||
|
@ -49,6 +51,9 @@
|
||||||
- (NSArray *) postCalDAVEventReplyTo: (NSArray *) recipients from: (NSString *) originator;
|
- (NSArray *) postCalDAVEventReplyTo: (NSArray *) recipients from: (NSString *) originator;
|
||||||
- (NSArray *) postCalDAVEventCancelTo: (NSArray *) recipients from: (NSString *) originator;
|
- (NSArray *) postCalDAVEventCancelTo: (NSArray *) recipients from: (NSString *) originator;
|
||||||
|
|
||||||
|
- (NSException *) updateContentWithCalendar: (iCalCalendar *) calendar
|
||||||
|
fromRequest: (WORequest *) rq;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#endif /* __Appointments_SOGoAppointmentObject_H__ */
|
#endif /* __Appointments_SOGoAppointmentObject_H__ */
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
#import <NGObjWeb/NSException+HTTP.h>
|
#import <NGObjWeb/NSException+HTTP.h>
|
||||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||||
|
#import <NGObjWeb/WOResponse.h>
|
||||||
|
#import <NGExtensions/NGCalendarDateRange.h>
|
||||||
#import <NGExtensions/NSNull+misc.h>
|
#import <NGExtensions/NSNull+misc.h>
|
||||||
#import <NGExtensions/NSObject+Logs.h>
|
#import <NGExtensions/NSObject+Logs.h>
|
||||||
#import <NGCards/iCalCalendar.h>
|
#import <NGCards/iCalCalendar.h>
|
||||||
|
@ -35,10 +37,11 @@
|
||||||
#import <NGCards/iCalEvent.h>
|
#import <NGCards/iCalEvent.h>
|
||||||
#import <NGCards/iCalEventChanges.h>
|
#import <NGCards/iCalEventChanges.h>
|
||||||
#import <NGCards/iCalPerson.h>
|
#import <NGCards/iCalPerson.h>
|
||||||
|
#import <NGCards/iCalRecurrenceCalculator.h>
|
||||||
#import <NGCards/NSCalendarDate+NGCards.h>
|
#import <NGCards/NSCalendarDate+NGCards.h>
|
||||||
#import <SaxObjC/XMLNamespaces.h>
|
#import <SaxObjC/XMLNamespaces.h>
|
||||||
|
|
||||||
#import <SOPE/NGCards/NSString+NGCards.h>
|
#import <NGCards/NSString+NGCards.h>
|
||||||
|
|
||||||
#import <SOGo/SOGoConstants.h>
|
#import <SOGo/SOGoConstants.h>
|
||||||
#import <SOGo/SOGoUserManager.h>
|
#import <SOGo/SOGoUserManager.h>
|
||||||
|
@ -53,6 +56,7 @@
|
||||||
#import <SOGo/SOGoWebDAVValue.h>
|
#import <SOGo/SOGoWebDAVValue.h>
|
||||||
#import <SOGo/WORequest+SOGo.h>
|
#import <SOGo/WORequest+SOGo.h>
|
||||||
|
|
||||||
|
#import "iCalCalendar+SOGo.h"
|
||||||
#import "iCalEventChanges+SOGo.h"
|
#import "iCalEventChanges+SOGo.h"
|
||||||
#import "iCalEntityObject+SOGo.h"
|
#import "iCalEntityObject+SOGo.h"
|
||||||
#import "iCalPerson+SOGo.h"
|
#import "iCalPerson+SOGo.h"
|
||||||
|
@ -121,6 +125,12 @@
|
||||||
return newOccurence;
|
return newOccurence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (iCalRepeatableEntityObject *) lookupOccurrence: (NSString *) recID
|
||||||
|
|
||||||
|
{
|
||||||
|
return [[self calendar: NO secure: NO] eventWithRecurrenceID: recID];
|
||||||
|
}
|
||||||
|
|
||||||
- (SOGoAppointmentObject *) _lookupEvent: (NSString *) eventUID
|
- (SOGoAppointmentObject *) _lookupEvent: (NSString *) eventUID
|
||||||
forUID: (NSString *) uid
|
forUID: (NSString *) uid
|
||||||
{
|
{
|
||||||
|
@ -178,52 +188,35 @@
|
||||||
SOGoAppointmentObject *attendeeObject;
|
SOGoAppointmentObject *attendeeObject;
|
||||||
NSString *iCalString;
|
NSString *iCalString;
|
||||||
|
|
||||||
|
iCalString = nil;
|
||||||
attendeeObject = [self _lookupEvent: [theEvent uid] forUID: theUID];
|
attendeeObject = [self _lookupEvent: [theEvent uid] forUID: theUID];
|
||||||
|
|
||||||
// We must add an occurence to a non-existing event. We have
|
// We must add an occurence to a non-existing event. We have
|
||||||
// to handle this with care, as in the postCalDAVEventRequestTo:from:
|
// to handle this with care, as in the postCalDAVEventRequestTo:from:
|
||||||
if ([attendeeObject isNew] && [theEvent recurrenceId])
|
if ([attendeeObject isNew] && [theEvent recurrenceId])
|
||||||
{
|
{
|
||||||
SOGoAppointmentObject *ownerObject;
|
|
||||||
NSArray *attendees;
|
|
||||||
iCalEvent *ownerEvent;
|
iCalEvent *ownerEvent;
|
||||||
iCalPerson *person;
|
iCalPerson *person;
|
||||||
SOGoUser *user;
|
SOGoUser *user;
|
||||||
BOOL found;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// We check if the attendee that was added to a single occurence is
|
// 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
|
// present in the master component. If not, we add it with a participation
|
||||||
// status set to "DECLINED".
|
// 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];
|
ownerEvent = [[[theEvent parent] events] objectAtIndex: 0];
|
||||||
attendees = [ownerEvent attendees];
|
user = [SOGoUser userWithLogin: theUID];
|
||||||
found = NO;
|
if (![ownerEvent userAsAttendee: user])
|
||||||
|
|
||||||
for (i = 0; i < [attendees count]; i++)
|
|
||||||
{
|
|
||||||
if ([[attendees objectAtIndex: i] hasSameEmailAddress: person])
|
|
||||||
{
|
|
||||||
found = YES;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
{
|
{
|
||||||
// Update the master event in the owner's calendar with the
|
// Update the master event in the owner's calendar with the
|
||||||
// status of the new attendee set as "DECLINED".
|
// 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];
|
[ownerEvent addToAttendees: person];
|
||||||
iCalString = [[ownerEvent parent] versitString];
|
|
||||||
[ownerObject saveContentString: iCalString];
|
iCalString = [[ownerEvent parent] versitString];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -239,7 +232,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the event in the attendee's calendar
|
// Save the event in the attendee's calendar
|
||||||
[attendeeObject saveContentString: iCalString];
|
if (iCalString)
|
||||||
|
[attendeeObject saveContentString: iCalString];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +261,7 @@
|
||||||
folder = [[SOGoUser userWithLogin: theUID]
|
folder = [[SOGoUser userWithLogin: theUID]
|
||||||
personalCalendarFolderInContext: context];
|
personalCalendarFolderInContext: context];
|
||||||
object = [folder lookupName: nameInContainer
|
object = [folder lookupName: nameInContainer
|
||||||
inContext: context acquire: NO];
|
inContext: context acquire: NO];
|
||||||
if (![object isKindOfClass: [NSException class]])
|
if (![object isKindOfClass: [NSException class]])
|
||||||
{
|
{
|
||||||
if (recurrenceId == nil)
|
if (recurrenceId == nil)
|
||||||
|
@ -839,7 +833,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
// If recurrenceId is defined, find the specified occurence
|
// If recurrenceId is defined, find the specified occurence
|
||||||
// within the repeating vEvent.
|
// within the repeating vEvent.
|
||||||
recurrenceTime = [NSString stringWithFormat: @"%f", [recurrenceId timeIntervalSince1970]];
|
recurrenceTime = [NSString stringWithFormat: @"%f", [recurrenceId timeIntervalSince1970]];
|
||||||
oldEvent = (iCalEvent*)[self lookupOccurence: recurrenceTime];
|
oldEvent = (iCalEvent*)[self lookupOccurrence: recurrenceTime];
|
||||||
if (oldEvent == nil)
|
if (oldEvent == nil)
|
||||||
// If no occurence found, create one
|
// If no occurence found, create one
|
||||||
oldEvent = (iCalEvent *)[self newOccurenceWithID: recurrenceTime];
|
oldEvent = (iCalEvent *)[self newOccurenceWithID: recurrenceTime];
|
||||||
|
@ -906,7 +900,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
// If recurrenceId is defined, find the specified occurence
|
// If recurrenceId is defined, find the specified occurence
|
||||||
// within the repeating vEvent.
|
// within the repeating vEvent.
|
||||||
recurrenceTime = [NSString stringWithFormat: @"%f", [recurrenceId timeIntervalSince1970]];
|
recurrenceTime = [NSString stringWithFormat: @"%f", [recurrenceId timeIntervalSince1970]];
|
||||||
event = [eventObject lookupOccurence: recurrenceTime];
|
event = [eventObject lookupOccurrence: recurrenceTime];
|
||||||
|
|
||||||
if (event == nil)
|
if (event == nil)
|
||||||
// If no occurence found, create one
|
// If no occurence found, create one
|
||||||
|
@ -1016,7 +1010,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
statusChange: (NSString *) newStatus
|
statusChange: (NSString *) newStatus
|
||||||
inEvent: (iCalEvent *) event
|
inEvent: (iCalEvent *) event
|
||||||
{
|
{
|
||||||
NSString *newContent, *currentStatus, *organizerUID;
|
NSString *currentStatus, *organizerUID;
|
||||||
SOGoUser *ownerUser, *currentUser;
|
SOGoUser *ownerUser, *currentUser;
|
||||||
NSException *ex;
|
NSException *ex;
|
||||||
|
|
||||||
|
@ -1161,14 +1155,18 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// We generate the updated iCalendar file and we save it in the database.
|
// 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
|
// We do this ONLY when using SOGo from the Web interface. Over DAV, it'll
|
||||||
// be handled directly in PUTAction:
|
// be handled directly in PUTAction:
|
||||||
|
|
||||||
if (![context request] || [[context request] handledByDefaultHandler])
|
if (![context request] || [[context request] handledByDefaultHandler])
|
||||||
{
|
{
|
||||||
|
NSString *newContent;
|
||||||
newContent = [[event parent] versitString];
|
newContent = [[event parent] versitString];
|
||||||
ex = [self saveContentString: newContent];
|
ex = [self saveContentString: newContent];
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// If the current user isn't the organizer of the event
|
// If the current user isn't the organizer of the event
|
||||||
// that has just been updated, we update the event and
|
// that has just been updated, we update the event and
|
||||||
|
@ -1340,7 +1338,8 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
ex = nil;
|
ex = nil;
|
||||||
delegatedUser = nil;
|
delegatedUser = nil;
|
||||||
|
|
||||||
calendar = [self calendar: NO secure: NO];
|
calendar = [[self calendar: NO secure: NO] mutableCopy];
|
||||||
|
[calendar autorelease];
|
||||||
if (calendar)
|
if (calendar)
|
||||||
{
|
{
|
||||||
if (_recurrenceId)
|
if (_recurrenceId)
|
||||||
|
@ -1348,7 +1347,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
// If _recurrenceId is defined, find the specified occurence
|
// If _recurrenceId is defined, find the specified occurence
|
||||||
// within the repeating vEvent.
|
// within the repeating vEvent.
|
||||||
recurrenceTime = [NSString stringWithFormat: @"%f", [_recurrenceId timeIntervalSince1970]];
|
recurrenceTime = [NSString stringWithFormat: @"%f", [_recurrenceId timeIntervalSince1970]];
|
||||||
event = (iCalEvent*)[self lookupOccurence: recurrenceTime];
|
event = (iCalEvent*)[self lookupOccurrence: recurrenceTime];
|
||||||
|
|
||||||
if (event == nil)
|
if (event == nil)
|
||||||
// If no occurence found, create one
|
// If no occurence found, create one
|
||||||
|
@ -1535,11 +1534,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
return partStats;
|
return partStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
#warning parseSingleFromSource is invoked far too many times: maybe we should use an additional ivar to store the new iCalendar
|
- (void) _setupResponseInRequestCalendar: (iCalCalendar *) rqCalendar
|
||||||
- (void) _setupResponseCalendarInRequest: (WORequest *) rq
|
|
||||||
{
|
{
|
||||||
iCalCalendar *calendar, *putCalendar;
|
iCalCalendar *calendar;
|
||||||
NSData *newContent;
|
|
||||||
NSArray *keys;
|
NSArray *keys;
|
||||||
NSDictionary *partStats, *newPartStats;
|
NSDictionary *partStats, *newPartStats;
|
||||||
NSString *partStat, *key;
|
NSString *partStat, *key;
|
||||||
|
@ -1551,8 +1548,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
max = [keys count];
|
max = [keys count];
|
||||||
if (max > 0)
|
if (max > 0)
|
||||||
{
|
{
|
||||||
putCalendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]];
|
newPartStats = [self _partStatsFromCalendar: rqCalendar];
|
||||||
newPartStats = [self _partStatsFromCalendar: putCalendar];
|
|
||||||
if ([keys isEqualToArray: [newPartStats allKeys]])
|
if ([keys isEqualToArray: [newPartStats allKeys]])
|
||||||
{
|
{
|
||||||
for (count = 0; count < max; count++)
|
for (count = 0; count < max; count++)
|
||||||
|
@ -1563,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;
|
NSArray *allEvents;
|
||||||
iCalEvent *event;
|
iCalEvent *event;
|
||||||
int i;
|
int i;
|
||||||
BOOL modified;
|
|
||||||
|
|
||||||
calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]];
|
|
||||||
allEvents = [calendar events];
|
|
||||||
modified = NO;
|
|
||||||
|
|
||||||
|
allEvents = [rqCalendar events];
|
||||||
for (i = 0; i < [allEvents count]; i++)
|
for (i = 0; i < [allEvents count]; i++)
|
||||||
{
|
{
|
||||||
event = [allEvents objectAtIndex: i];
|
event = [allEvents objectAtIndex: i];
|
||||||
|
|
||||||
if ([event isAllDay] && [event isOpaque])
|
if ([event isAllDay] && [event isOpaque])
|
||||||
{
|
{
|
||||||
[event setTransparency: @"TRANSPARENT"];
|
[event setTransparency: @"TRANSPARENT"];
|
||||||
modified = YES;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modified)
|
|
||||||
[rq setContent: [[calendar versitString] dataUsingEncoding: [rq contentEncoding]]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1601,17 +1583,13 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
* Currently only check if the events have an end date or a duration.
|
* Currently only check if the events have an end date or a duration.
|
||||||
* @param rq the HTTP PUT request
|
* @param rq the HTTP PUT request
|
||||||
*/
|
*/
|
||||||
- (void) _adjustEventsInRequest: (WORequest *) rq
|
- (void) _adjustEventsInRequestCalendar: (iCalCalendar *) rqCalendar
|
||||||
{
|
{
|
||||||
iCalCalendar *calendar;
|
|
||||||
NSArray *allEvents;
|
NSArray *allEvents;
|
||||||
iCalEvent *event;
|
iCalEvent *event;
|
||||||
NSUInteger i;
|
NSUInteger i;
|
||||||
BOOL modified;
|
|
||||||
|
|
||||||
calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]];
|
allEvents = [rqCalendar events];
|
||||||
allEvents = [calendar events];
|
|
||||||
modified = NO;
|
|
||||||
|
|
||||||
for (i = 0; i < [allEvents count]; i++)
|
for (i = 0; i < [allEvents count]; i++)
|
||||||
{
|
{
|
||||||
|
@ -1624,28 +1602,16 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
[event setDuration: @"P1D"];
|
[event setDuration: @"P1D"];
|
||||||
else
|
else
|
||||||
[event setDuration: @"PT1H"];
|
[event setDuration: @"PT1H"];
|
||||||
|
[self warnWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]];
|
||||||
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
|
- (void) _decomposeGroupsInRequestCalendar: (iCalCalendar *) rqCalendar
|
||||||
{
|
{
|
||||||
iCalCalendar *calendar;
|
|
||||||
NSArray *allEvents;
|
NSArray *allEvents;
|
||||||
iCalEvent *event;
|
iCalEvent *event;
|
||||||
int i;
|
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:
|
// The algorithm is pretty straightforward:
|
||||||
//
|
//
|
||||||
|
@ -1654,20 +1620,12 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
// If some are groups, we decompose them
|
// If some are groups, we decompose them
|
||||||
// We regenerate the iCalendar string
|
// We regenerate the iCalendar string
|
||||||
//
|
//
|
||||||
allEvents = [calendar events];
|
allEvents = [rqCalendar events];
|
||||||
modified = NO;
|
|
||||||
|
|
||||||
for (i = 0; i < [allEvents count]; i++)
|
for (i = 0; i < [allEvents count]; i++)
|
||||||
{
|
{
|
||||||
event = [allEvents objectAtIndex: 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]]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1731,25 +1689,28 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// If we see "X-SOGo: NoGroupsDecomposition" in the HTTP headers, we
|
// This method is meant to be the common point of any save operation from web
|
||||||
// simply invoke super's PUTAction.
|
// 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
|
- (NSException *) updateContentWithCalendar: (iCalCalendar *) calendar
|
||||||
// from iPhone clients.
|
fromRequest: (WORequest *) rq
|
||||||
//
|
|
||||||
- (id) PUTAction: (WOContext *) _ctx
|
|
||||||
{
|
{
|
||||||
NSException *ex;
|
NSException *ex;
|
||||||
NSString *etag;
|
|
||||||
NSArray *roles;
|
NSArray *roles;
|
||||||
WORequest *rq;
|
SOGoUser *ownerUser;
|
||||||
id response;
|
|
||||||
|
|
||||||
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];
|
ownerUser = [SOGoUser userWithLogin: owner];
|
||||||
roles = [[context activeUser] rolesForObject: self inContext: context];
|
|
||||||
|
|
||||||
|
roles = [[context activeUser] rolesForObject: self
|
||||||
|
inContext: context];
|
||||||
//
|
//
|
||||||
// We check if we gave only the "Respond To" right and someone is actually
|
// We check if we gave only the "Respond To" right and someone is actually
|
||||||
// responding to one of our invitation. In this case, _setupResponseCalendarInRequest
|
// responding to one of our invitation. In this case, _setupResponseCalendarInRequest
|
||||||
|
@ -1757,24 +1718,20 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
//
|
//
|
||||||
if ([roles containsObject: @"ComponentResponder"]
|
if ([roles containsObject: @"ComponentResponder"]
|
||||||
&& ![roles containsObject: @"ComponentModifier"])
|
&& ![roles containsObject: @"ComponentModifier"])
|
||||||
[self _setupResponseCalendarInRequest: rq];
|
[self _setupResponseInRequestCalendar: calendar];
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SOGoUser *user;
|
|
||||||
|
|
||||||
user = [SOGoUser userWithLogin: owner];
|
|
||||||
|
|
||||||
if (![[rq headersForKey: @"X-SOGo"]
|
if (![[rq headersForKey: @"X-SOGo"]
|
||||||
containsObject: @"NoGroupsDecomposition"])
|
containsObject: @"NoGroupsDecomposition"])
|
||||||
[self _decomposeGroupsInRequest: rq];
|
[self _decomposeGroupsInRequestCalendar: calendar];
|
||||||
|
|
||||||
if ([[user domainDefaults] iPhoneForceAllDayTransparency]
|
if ([[ownerUser domainDefaults] iPhoneForceAllDayTransparency]
|
||||||
&& [rq isIPhone])
|
&& [rq isIPhone])
|
||||||
{
|
{
|
||||||
[self _adjustTransparencyInRequest: rq];
|
[self _adjustTransparencyInRequestCalendar: calendar];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self _adjustEventsInRequest: rq];
|
[self _adjustEventsInRequestCalendar: calendar];
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -1782,25 +1739,20 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
//
|
//
|
||||||
if ([self isNew])
|
if ([self isNew])
|
||||||
{
|
{
|
||||||
iCalCalendar *calendar;
|
iCalEvent *event;
|
||||||
SOGoUser *ownerUser;
|
|
||||||
iCalEvent *event, *conflictingEvent;
|
|
||||||
NSArray *attendees;
|
NSArray *attendees;
|
||||||
|
|
||||||
NSString *eventUID;
|
NSString *eventUID;
|
||||||
BOOL scheduling;
|
BOOL scheduling;
|
||||||
|
|
||||||
calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]];
|
|
||||||
attendees = nil;
|
attendees = nil;
|
||||||
|
|
||||||
event = [[calendar events] objectAtIndex: 0];
|
event = [[calendar events] objectAtIndex: 0];
|
||||||
eventUID = [event uid];
|
eventUID = [event uid];
|
||||||
ownerUser = [SOGoUser userWithLogin: owner];
|
|
||||||
scheduling = [self _shouldScheduleEvent: [event organizer]];
|
scheduling = [self _shouldScheduleEvent: [event organizer]];
|
||||||
|
|
||||||
// make sure eventUID doesn't conflict with an existing event - see bug #1853
|
// 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)
|
// 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
|
return [NSException exceptionWithHTTPStatus: 403
|
||||||
reason: [NSString stringWithFormat: @"Event UID already in use. (%s)", eventUID]];
|
reason: [NSString stringWithFormat: @"Event UID already in use. (%s)", eventUID]];
|
||||||
|
@ -1852,33 +1804,21 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
} // if ([self isNew])
|
} // if ([self isNew])
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
iCalCalendar *oldCalendar, *newCalendar;
|
iCalCalendar *oldCalendar;
|
||||||
iCalEvent *oldEvent, *newEvent;
|
iCalEvent *oldEvent, *newEvent;
|
||||||
iCalEventChanges *changes;
|
iCalEventChanges *changes;
|
||||||
NSMutableArray *oldEvents, *newEvents;
|
NSArray *oldEvents, *newEvents;
|
||||||
NSCalendarDate *recurrenceId;
|
NSCalendarDate *recurrenceId;
|
||||||
NSException *error;
|
|
||||||
BOOL master;
|
BOOL master;
|
||||||
int i;
|
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.
|
// We check what has changed in the event and react accordingly.
|
||||||
//
|
//
|
||||||
newCalendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]];
|
newEvents = [calendar events];
|
||||||
newEvents = [NSMutableArray arrayWithArray: [newCalendar events]];
|
|
||||||
|
|
||||||
oldCalendar = [self calendar: NO secure: NO];
|
oldCalendar = [self calendar: NO secure: NO];
|
||||||
oldEvents = [NSMutableArray arrayWithArray: [oldCalendar events]];
|
oldEvents = [oldCalendar events];
|
||||||
recurrenceId = nil;
|
recurrenceId = nil;
|
||||||
master = NO;
|
master = NO;
|
||||||
|
|
||||||
|
@ -1911,8 +1851,8 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[newEvents removeObject: oldEvent];
|
[calendar removeChild: oldEvent];
|
||||||
[oldEvents removeObject: newEvent];
|
[oldCalendar removeChild: newEvent];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1981,13 +1921,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
{
|
{
|
||||||
if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent]))
|
if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent]))
|
||||||
return ex;
|
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
|
// else => attendee is responding
|
||||||
|
@ -2058,23 +1991,57 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
}
|
}
|
||||||
} // else of if (isNew) ...
|
} // else of if (isNew) ...
|
||||||
|
|
||||||
|
unsigned int baseVersion;
|
||||||
// We must NOT invoke [super PUTAction:] here as it'll resave
|
// We must NOT invoke [super PUTAction:] here as it'll resave
|
||||||
// the content string and we could have etag mismatches.
|
// the content string and we could have etag mismatches.
|
||||||
response = [_ctx response];
|
|
||||||
|
|
||||||
baseVersion = (isNew ? 0 : version);
|
baseVersion = (isNew ? 0 : version);
|
||||||
|
|
||||||
ex = [self saveContentString: [rq contentAsString]
|
ex = [self saveContentString: [calendar versitString]
|
||||||
baseVersion: baseVersion];
|
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)
|
if (ex)
|
||||||
response = (WOResponse *) ex;
|
response = (WOResponse *) ex;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
response = [_ctx response];
|
||||||
if (isNew)
|
if (isNew)
|
||||||
[response setStatus: 201 /* Created */];
|
[response setStatus: 201 /* Created */];
|
||||||
else
|
else
|
||||||
[response setStatus: 204 /* No Content */];
|
[response setStatus: 204 /* No Content */];
|
||||||
|
|
||||||
etag = [self davEntityTag];
|
etag = [self davEntityTag];
|
||||||
if (etag)
|
if (etag)
|
||||||
[response setHeader: etag forKey: @"etag"];
|
[response setHeader: etag forKey: @"etag"];
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#ifndef SOGOCALENDARCOMPONENT_H
|
#ifndef SOGOCALENDARCOMPONENT_H
|
||||||
#define SOGOCALENDARCOMPONENT_H
|
#define SOGOCALENDARCOMPONENT_H
|
||||||
|
|
||||||
|
#import <SOGo/SOGoConstants.h>
|
||||||
#import <SOGo/SOGoContentObject.h>
|
#import <SOGo/SOGoContentObject.h>
|
||||||
|
|
||||||
#import "SOGoComponentOccurence.h"
|
#import "SOGoComponentOccurence.h"
|
||||||
|
@ -61,6 +62,7 @@
|
||||||
toFolder: (SOGoGCSFolder *) newFolder;
|
toFolder: (SOGoGCSFolder *) newFolder;
|
||||||
|
|
||||||
- (void) updateComponent: (iCalRepeatableEntityObject *) newObject;
|
- (void) updateComponent: (iCalRepeatableEntityObject *) newObject;
|
||||||
|
- (NSException *) saveCalendar: (iCalCalendar *) newCalendar;
|
||||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject;
|
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject;
|
||||||
|
|
||||||
/* mail notifications */
|
/* mail notifications */
|
||||||
|
@ -78,7 +80,8 @@
|
||||||
- (void) sendReceiptEmailForObject: (iCalRepeatableEntityObject *) object
|
- (void) sendReceiptEmailForObject: (iCalRepeatableEntityObject *) object
|
||||||
addedAttendees: (NSArray *) theAddedAttendees
|
addedAttendees: (NSArray *) theAddedAttendees
|
||||||
deletedAttendees: (NSArray *) theDeletedAttendees
|
deletedAttendees: (NSArray *) theDeletedAttendees
|
||||||
updatedAttendees: (NSArray *) theUpdatedAttendees;
|
updatedAttendees: (NSArray *) theUpdatedAttendees
|
||||||
|
operation: (SOGoEventOperation) theOperation;
|
||||||
|
|
||||||
- (iCalPerson *) findParticipantWithUID: (NSString *) uid;
|
- (iCalPerson *) findParticipantWithUID: (NSString *) uid;
|
||||||
|
|
||||||
|
@ -86,7 +89,8 @@
|
||||||
- (NSArray *) getUIDsForICalPersons: (NSArray *) iCalPersons;
|
- (NSArray *) getUIDsForICalPersons: (NSArray *) iCalPersons;
|
||||||
|
|
||||||
/* recurrences */
|
/* recurrences */
|
||||||
- (iCalRepeatableEntityObject *) lookupOccurence: (NSString *) recID;
|
/* same as above, but refers to the existing calendar component */
|
||||||
|
- (iCalRepeatableEntityObject *) lookupOccurrence: (NSString *) recID;
|
||||||
- (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) component;
|
- (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) component;
|
||||||
- (iCalRepeatableEntityObject *) newOccurenceWithID: (NSString *) recID;
|
- (iCalRepeatableEntityObject *) newOccurenceWithID: (NSString *) recID;
|
||||||
|
|
||||||
|
|
|
@ -269,45 +269,11 @@
|
||||||
return iCalString;
|
return iCalString;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence,
|
- (iCalRepeatableEntityObject *) lookupOccurrence: (NSString *) recID
|
||||||
NSString *recID)
|
|
||||||
{
|
{
|
||||||
unsigned int seconds, recSeconds;
|
[self subclassResponsibility: _cmd];
|
||||||
|
|
||||||
seconds = [recID intValue];
|
return nil;
|
||||||
recSeconds = [[occurence recurrenceId] timeIntervalSince1970];
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) component
|
- (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) component
|
||||||
|
@ -397,7 +363,7 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence,
|
||||||
else if ([lookupName hasPrefix: @"occurence"])
|
else if ([lookupName hasPrefix: @"occurence"])
|
||||||
{
|
{
|
||||||
recID = [lookupName substringFromIndex: 9];
|
recID = [lookupName substringFromIndex: 9];
|
||||||
occurence = [self lookupOccurence: recID];
|
occurence = [self lookupOccurrence: recID];
|
||||||
if (occurence)
|
if (occurence)
|
||||||
isNewOccurence = NO;
|
isNewOccurence = NO;
|
||||||
else
|
else
|
||||||
|
@ -679,17 +645,18 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject
|
- (NSException *) saveCalendar: (iCalCalendar *) newCalendar
|
||||||
{
|
{
|
||||||
NSString *newiCalString;
|
[self saveContentString: [newCalendar versitString]];
|
||||||
|
|
||||||
newiCalString = [[newObject parent] versitString];
|
|
||||||
|
|
||||||
[self saveContentString: newiCalString];
|
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject
|
||||||
|
{
|
||||||
|
return [self saveCalendar: [newObject parent]];
|
||||||
|
}
|
||||||
|
|
||||||
/* raw saving */
|
/* raw saving */
|
||||||
|
|
||||||
/* EMail Notifications */
|
/* EMail Notifications */
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#import <SoObjects/SOGo/SOGoMailer.h>
|
#import <SoObjects/SOGo/SOGoMailer.h>
|
||||||
|
|
||||||
|
#import "iCalCalendar+SOGo.h"
|
||||||
#import "NSArray+Appointments.h"
|
#import "NSArray+Appointments.h"
|
||||||
#import "SOGoAptMailNotification.h"
|
#import "SOGoAptMailNotification.h"
|
||||||
#import "SOGoAppointmentFolder.h"
|
#import "SOGoAppointmentFolder.h"
|
||||||
|
@ -46,6 +47,11 @@
|
||||||
return @"vtodo";
|
return @"vtodo";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (iCalRepeatableEntityObject *) lookupOccurrence: (NSString *) recID
|
||||||
|
{
|
||||||
|
return [[self calendar: NO secure: NO] todoWithRecurrenceID: recID];
|
||||||
|
}
|
||||||
|
|
||||||
- (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) occ
|
- (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) occ
|
||||||
{
|
{
|
||||||
NSArray *allTodos;
|
NSArray *allTodos;
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* iCalCalendar+SOGo.h - this file is part of SOGo
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Inverse inc
|
||||||
|
*
|
||||||
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
|
*
|
||||||
|
* 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 <NGCards/iCalCalendar.h>
|
||||||
|
|
||||||
|
@class NSString;
|
||||||
|
@class iCalEvent;
|
||||||
|
@class iCalToDo;
|
||||||
|
|
||||||
|
@interface iCalCalendar (SOGoExtensions)
|
||||||
|
|
||||||
|
- (iCalEvent *) eventWithRecurrenceID: (NSString *) recID;
|
||||||
|
- (iCalToDo *) todoWithRecurrenceID: (NSString *) recID;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif /* ICALCALENDAR_SOGO_H */
|
|
@ -0,0 +1,82 @@
|
||||||
|
/* iCalCalendar+SOGo.m - this file is part of $PROJECT_NAME_HERE$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Inverse inc
|
||||||
|
*
|
||||||
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
|
*
|
||||||
|
* 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 <Foundation/NSArray.h>
|
||||||
|
#import <Foundation/NSString.h>
|
||||||
|
|
||||||
|
#import <NGCards/iCalRepeatableEntityObject.h>
|
||||||
|
|
||||||
|
#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
|
|
@ -115,7 +115,7 @@ class CalDAVSchedulingTest(unittest.TestCase):
|
||||||
self.attendee1_client = webdavlib.WebDAVClient(hostname, port,
|
self.attendee1_client = webdavlib.WebDAVClient(hostname, port,
|
||||||
attendee1_username, attendee1_password)
|
attendee1_username, attendee1_password)
|
||||||
self.attendee1_delegate_client = webdavlib.WebDAVClient(hostname, port,
|
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)
|
utility = utilities.TestUtility(self, self.client)
|
||||||
(self.user_name, self.user_email) = utility.fetchUserInfo(username)
|
(self.user_name, self.user_email) = utility.fetchUserInfo(username)
|
||||||
|
|
Loading…
Reference in New Issue