mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-07-09 10:25:08 +00:00
Monotone-Parent: 463195ab0268a4a769eab22f23b6aecf0c87ad79
Monotone-Revision: 9abbb51cbabcad645190865841814453369fa85f Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2007-11-18T10:16:25 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
@@ -10,6 +10,9 @@ Appointments_OBJC_FILES = \
|
||||
Product.m \
|
||||
NSArray+Appointments.m \
|
||||
iCalEntityObject+SOGo.m \
|
||||
iCalEvent+SOGo.m \
|
||||
iCalEventChanges+SOGo.m \
|
||||
iCalPerson+SOGo.m \
|
||||
\
|
||||
SOGoCalendarComponent.m \
|
||||
SOGoAppointmentObject.m \
|
||||
@@ -24,6 +27,7 @@ Appointments_OBJC_FILES = \
|
||||
SOGoAptMailUpdate.m \
|
||||
SOGoAptMailRemoval.m \
|
||||
SOGoAptMailDeletion.m \
|
||||
SOGoAptMailICalReply.m \
|
||||
|
||||
Appointments_RESOURCE_FILES += \
|
||||
Version \
|
||||
@@ -31,14 +35,17 @@ Appointments_RESOURCE_FILES += \
|
||||
|
||||
Appointments_COMPONENTS += \
|
||||
SOGoAptMailEnglishInvitation.wo \
|
||||
SOGoAptMailEnglishICalReply.wo \
|
||||
SOGoAptMailEnglishUpdate.wo \
|
||||
SOGoAptMailEnglishRemoval.wo \
|
||||
SOGoAptMailEnglishDeletion.wo \
|
||||
SOGoAptMailFrenchInvitation.wo \
|
||||
SOGoAptMailFrenchICalReply.wo \
|
||||
SOGoAptMailFrenchUpdate.wo \
|
||||
SOGoAptMailFrenchRemoval.wo \
|
||||
SOGoAptMailFrenchDeletion.wo \
|
||||
SOGoAptMailGermanInvitation.wo \
|
||||
SOGoAptMailGermanICalReply.wo \
|
||||
SOGoAptMailGermanUpdate.wo \
|
||||
SOGoAptMailGermanRemoval.wo \
|
||||
SOGoAptMailGermanDeletion.wo \
|
||||
|
||||
@@ -46,12 +46,14 @@
|
||||
|
||||
@interface SOGoAppointmentObject : SOGoCalendarComponent
|
||||
|
||||
- (NSException *) changeParticipationStatus: (NSString *) _status;
|
||||
|
||||
/* "iCal multifolder saves" */
|
||||
|
||||
- (NSException *) saveContentString: (NSString *) _iCal
|
||||
baseSequence: (int) _v;
|
||||
- (NSException *) deleteWithBaseSequence: (int) _v;
|
||||
- (NSException *) saveContentString: (NSString *) _iCalString;
|
||||
// - (NSException *) saveContentString: (NSString *) _iCal
|
||||
// baseSequence: (int) _v;
|
||||
// - (NSException *) deleteWithBaseSequence: (int) _v;
|
||||
// - (NSException *) saveContentString: (NSString *) _iCalString;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
@@ -30,14 +30,19 @@
|
||||
#import <NGCards/iCalEventChanges.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
|
||||
#import <SoObjects/SOGo/iCalEntityObject+Utilities.h>
|
||||
#import <SoObjects/SOGo/LDAPUserManager.h>
|
||||
#import <SoObjects/SOGo/NSArray+Utilities.h>
|
||||
#import <SoObjects/SOGo/SOGoObject.h>
|
||||
#import <SoObjects/SOGo/SOGoPermissions.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
#import <SoObjects/SOGo/WORequest+SOGo.h>
|
||||
|
||||
#import "NSArray+Appointments.h"
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
#import "iCalEventChanges+SOGo.h"
|
||||
#import "iCalEntityObject+SOGo.h"
|
||||
#import "iCalPerson+SOGo.h"
|
||||
|
||||
#import "SOGoAppointmentObject.h"
|
||||
|
||||
@@ -48,387 +53,325 @@
|
||||
return @"vevent";
|
||||
}
|
||||
|
||||
/* iCal handling */
|
||||
- (NSArray *) attendeeUIDsFromAppointment: (iCalEvent *) _apt
|
||||
- (SOGoAppointmentObject *) _lookupEvent: (NSString *) eventUID
|
||||
forUID: (NSString *) uid
|
||||
{
|
||||
SOGoAppointmentFolder *folder;
|
||||
SOGoAppointmentObject *object;
|
||||
NSString *possibleName;
|
||||
|
||||
folder = [container lookupCalendarFolderForUID: uid];
|
||||
object = [folder lookupName: nameInContainer
|
||||
inContext: context acquire: NO];
|
||||
if ([object isKindOfClass: [NSException class]])
|
||||
{
|
||||
possibleName = [folder resourceNameForEventUID: eventUID];
|
||||
if (possibleName)
|
||||
{
|
||||
object = [folder lookupName: nameInContainer
|
||||
inContext: context acquire: NO];
|
||||
if ([object isKindOfClass: [NSException class]])
|
||||
object = nil;
|
||||
}
|
||||
}
|
||||
|
||||
if (!object)
|
||||
object = [SOGoAppointmentObject objectWithName: nameInContainer
|
||||
inContainer: folder];
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
- (void) _addOrUpdateEvent: (iCalEvent *) event
|
||||
forUID: (NSString *) uid
|
||||
{
|
||||
SOGoAppointmentObject *object;
|
||||
NSString *iCalString, *userLogin;
|
||||
|
||||
userLogin = [[context activeUser] login];
|
||||
if (![uid isEqualToString: userLogin])
|
||||
{
|
||||
object = [self _lookupEvent: [event uid] forUID: uid];
|
||||
iCalString = [[event parent] versitString];
|
||||
[object saveContentString: iCalString];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _removeEventFromUID: (NSString *) uid
|
||||
{
|
||||
SOGoAppointmentFolder *folder;
|
||||
SOGoAppointmentObject *object;
|
||||
NSString *userLogin;
|
||||
|
||||
userLogin = [[context activeUser] login];
|
||||
if (![uid isEqualToString: userLogin])
|
||||
{
|
||||
folder = [container lookupCalendarFolderForUID: uid];
|
||||
object = [folder lookupName: nameInContainer
|
||||
inContext: context acquire: NO];
|
||||
if (![object isKindOfClass: [NSException class]])
|
||||
[object delete];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _handleRemovedUsers: (NSArray *) attendees
|
||||
{
|
||||
NSEnumerator *enumerator;
|
||||
iCalPerson *currentAttendee;
|
||||
NSString *currentUID;
|
||||
|
||||
enumerator = [attendees objectEnumerator];
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
if (currentUID)
|
||||
[self _removeEventFromUID: currentUID];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _requireResponseFromAttendees: (NSArray *) attendees
|
||||
{
|
||||
NSEnumerator *enumerator;
|
||||
iCalPerson *currentAttendee;
|
||||
|
||||
enumerator = [attendees objectEnumerator];
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
[currentAttendee setRsvp: @"TRUE"];
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatNeedsAction];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _handleSequenceUpdateInEvent: (iCalEvent *) newEvent
|
||||
ignoringAttendees: (NSArray *) attendees
|
||||
fromOldEvent: (iCalEvent *) oldEvent
|
||||
{
|
||||
NSMutableArray *updateAttendees, *updateUIDs;
|
||||
NSEnumerator *enumerator;
|
||||
iCalPerson *currentAttendee;
|
||||
NSString *currentUID;
|
||||
|
||||
updateAttendees = [NSMutableArray arrayWithArray: [newEvent attendees]];
|
||||
[updateAttendees removeObjectsInArray: attendees];
|
||||
|
||||
updateUIDs = [NSMutableArray arrayWithCapacity: [updateAttendees count]];
|
||||
enumerator = [updateAttendees objectEnumerator];
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
if (currentUID)
|
||||
[self _addOrUpdateEvent: newEvent
|
||||
forUID: currentUID];
|
||||
}
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Update"
|
||||
forOldObject: oldEvent
|
||||
andNewObject: [newEvent itipEntryWithMethod: @"request"]
|
||||
toAttendees: updateAttendees];
|
||||
}
|
||||
|
||||
- (void) _handleAddedUsers: (NSArray *) attendees
|
||||
fromEvent: (iCalEvent *) newEvent
|
||||
{
|
||||
NSEnumerator *enumerator;
|
||||
iCalPerson *currentAttendee;
|
||||
NSString *currentUID;
|
||||
|
||||
enumerator = [attendees objectEnumerator];
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
if (currentUID)
|
||||
[self _addOrUpdateEvent: newEvent
|
||||
forUID: currentUID];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _handleUpdatedEvent: (iCalEvent *) newEvent
|
||||
fromOldEvent: (iCalEvent *) oldEvent
|
||||
{
|
||||
LDAPUserManager *um;
|
||||
NSMutableArray *uids;
|
||||
NSArray *attendees;
|
||||
unsigned i, count;
|
||||
NSString *email, *uid;
|
||||
|
||||
if (![_apt isNotNull])
|
||||
return nil;
|
||||
|
||||
if ((attendees = [_apt attendees]) == nil)
|
||||
return nil;
|
||||
count = [attendees count];
|
||||
uids = [NSMutableArray arrayWithCapacity:count + 1];
|
||||
|
||||
um = [LDAPUserManager sharedUserManager];
|
||||
|
||||
/* add organizer */
|
||||
|
||||
email = [[_apt organizer] rfc822Email];
|
||||
if ([email isNotNull]) {
|
||||
uid = [um getUIDForEmail: email];
|
||||
if ([uid isNotNull]) {
|
||||
[uids addObject:uid];
|
||||
}
|
||||
else
|
||||
[self logWithFormat:@"Note: got no uid for organizer: '%@'", email];
|
||||
}
|
||||
|
||||
/* add attendees */
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
iCalPerson *person;
|
||||
|
||||
person = [attendees objectAtIndex:i];
|
||||
email = [person rfc822Email];
|
||||
if (![email isNotNull]) continue;
|
||||
|
||||
uid = [um getUIDForEmail:email];
|
||||
if (![uid isNotNull]) {
|
||||
[self logWithFormat:@"Note: got no uid for email: '%@'", email];
|
||||
continue;
|
||||
}
|
||||
if (![uids containsObject:uid])
|
||||
[uids addObject:uid];
|
||||
}
|
||||
|
||||
return uids;
|
||||
}
|
||||
|
||||
/* store in all the other folders */
|
||||
|
||||
- (NSException *) saveContentString: (NSString *) _iCal
|
||||
inUIDs: (NSArray *) _uids
|
||||
{
|
||||
NSEnumerator *e;
|
||||
id folder;
|
||||
NSException *allErrors = nil;
|
||||
NSException *error;
|
||||
SOGoAppointmentObject *apt;
|
||||
|
||||
e = [[container lookupCalendarFoldersForUIDs:_uids inContext: context]
|
||||
objectEnumerator];
|
||||
while ((folder = [e nextObject]))
|
||||
{
|
||||
apt = [SOGoAppointmentObject objectWithName: nameInContainer
|
||||
inContainer: folder];
|
||||
error = [apt primarySaveContentString:_iCal];
|
||||
if (error)
|
||||
{
|
||||
[self logWithFormat:@"Note: failed to save iCal in folder: %@", folder];
|
||||
// TODO: make compound
|
||||
allErrors = error;
|
||||
}
|
||||
}
|
||||
|
||||
return allErrors;
|
||||
}
|
||||
|
||||
- (NSException *) deleteInUIDs: (NSArray *) _uids
|
||||
{
|
||||
NSEnumerator *e;
|
||||
id folder;
|
||||
NSException *allErrors = nil;
|
||||
NSException *error;
|
||||
SOGoAppointmentObject *apt;
|
||||
|
||||
e = [[container lookupCalendarFoldersForUIDs:_uids inContext: context]
|
||||
objectEnumerator];
|
||||
while ((folder = [e nextObject]))
|
||||
{
|
||||
apt = [folder lookupName: [self nameInContainer]
|
||||
inContext: context
|
||||
acquire:NO];
|
||||
if ([apt isKindOfClass: [NSException class]]) {
|
||||
[self logWithFormat: @"%@", [(NSException *) apt reason]];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((error = [apt primaryDelete]) != nil) {
|
||||
[self logWithFormat:@"Note: failed to delete in folder: %@", folder];
|
||||
// TODO: make compound
|
||||
allErrors = error;
|
||||
}
|
||||
}
|
||||
|
||||
return allErrors;
|
||||
}
|
||||
|
||||
/* "iCal multifolder saves" */
|
||||
- (BOOL) _aptIsStillRelevant: (iCalEvent *) appointment
|
||||
{
|
||||
NSCalendarDate *now;
|
||||
|
||||
now = [NSCalendarDate calendarDate];
|
||||
|
||||
return ([[appointment endDate] earlierDate: now] == now);
|
||||
}
|
||||
|
||||
- (NSException *) saveContentString: (NSString *) _iCal
|
||||
baseSequence: (int) _v
|
||||
{
|
||||
/*
|
||||
Note: we need to delete in all participants folders and send iMIP messages
|
||||
for all external accounts.
|
||||
|
||||
Steps:
|
||||
- fetch stored content
|
||||
- parse old content
|
||||
- check if sequence matches (or if 0=ignore)
|
||||
- extract old attendee list + organizer (make unique)
|
||||
- parse new content (ensure that sequence is increased!)
|
||||
- extract new attendee list + organizer (make unique)
|
||||
- make a diff => new, same, removed
|
||||
- write to new, same
|
||||
- delete in removed folders
|
||||
- send iMIP mail for all folders not found
|
||||
*/
|
||||
LDAPUserManager *um;
|
||||
iCalEvent *oldApt, *newApt;
|
||||
iCalEventChanges *changes;
|
||||
iCalPerson *organizer;
|
||||
NSString *oldContent, *uid;
|
||||
NSArray *uids, *props;
|
||||
NSMutableArray *attendees, *storeUIDs, *removedUIDs;
|
||||
NSException *storeError, *delError;
|
||||
BOOL updateForcesReconsider;
|
||||
|
||||
if ([[context request] handledByDefaultHandler])
|
||||
|
||||
changes = [newEvent getChangesRelativeToEvent: oldEvent];
|
||||
attendees = [changes deletedAttendees];
|
||||
if ([attendees count])
|
||||
{
|
||||
updateForcesReconsider = NO;
|
||||
[self _handleRemovedUsers: attendees];
|
||||
[self sendEMailUsingTemplateNamed: @"Deletion"
|
||||
forOldObject: oldEvent
|
||||
andNewObject: [newEvent itipEntryWithMethod: @"cancel"]
|
||||
toAttendees: attendees];
|
||||
}
|
||||
|
||||
if ([_iCal length] == 0)
|
||||
return [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
|
||||
reason: @"got no iCalendar content to store!"];
|
||||
attendees = [changes insertedAttendees];
|
||||
if ([changes sequenceShouldBeIncreased])
|
||||
{
|
||||
[newEvent increaseSequence];
|
||||
[self _requireResponseFromAttendees: [newEvent attendees]];
|
||||
[self _handleSequenceUpdateInEvent: newEvent
|
||||
ignoringAttendees: attendees
|
||||
fromOldEvent: oldEvent];
|
||||
}
|
||||
else
|
||||
[self _requireResponseFromAttendees: attendees];
|
||||
|
||||
um = [LDAPUserManager sharedUserManager];
|
||||
if ([attendees count])
|
||||
{
|
||||
[self _handleAddedUsers: attendees fromEvent: newEvent];
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
forOldObject: oldEvent
|
||||
andNewObject: [newEvent itipEntryWithMethod: @"request"]
|
||||
toAttendees: attendees];
|
||||
}
|
||||
}
|
||||
|
||||
/* handle old content */
|
||||
|
||||
oldContent = [self contentAsString]; /* if nil, this is a new appointment */
|
||||
if ([oldContent length] == 0)
|
||||
{
|
||||
/* new appointment */
|
||||
[self debugWithFormat:@"saving new appointment: %@", _iCal];
|
||||
oldApt = nil;
|
||||
}
|
||||
- (void) saveComponent: (iCalEvent *) newEvent
|
||||
{
|
||||
iCalEvent *oldEvent;
|
||||
NSArray *attendees;
|
||||
|
||||
[[newEvent parent] setMethod: @""];
|
||||
if ([newEvent userIsOrganizer: [context activeUser]])
|
||||
{
|
||||
oldEvent = [self component: NO secure: NO];
|
||||
if (oldEvent)
|
||||
[self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent];
|
||||
else
|
||||
oldApt = (iCalEvent *) [self component: NO];
|
||||
|
||||
/* compare sequence if requested */
|
||||
if (_v != 0) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/* handle new content */
|
||||
|
||||
newApt = (iCalEvent *) [self component: NO];
|
||||
if (!newApt)
|
||||
return [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
|
||||
reason: @"could not parse iCalendar content!"];
|
||||
|
||||
/* diff */
|
||||
|
||||
changes = [iCalEventChanges changesFromEvent: oldApt toEvent: newApt];
|
||||
uids = [self getUIDsForICalPersons: [changes deletedAttendees]];
|
||||
removedUIDs = [NSMutableArray arrayWithArray: uids];
|
||||
|
||||
uids = [self getUIDsForICalPersons: [newApt attendees]];
|
||||
storeUIDs = [NSMutableArray arrayWithArray: uids];
|
||||
props = [changes updatedProperties];
|
||||
|
||||
/* detect whether sequence has to be increased */
|
||||
if ([changes hasChanges])
|
||||
[newApt increaseSequence];
|
||||
|
||||
/* preserve organizer */
|
||||
|
||||
organizer = [newApt organizer];
|
||||
uid = [self getUIDForICalPerson: organizer];
|
||||
if (!uid)
|
||||
uid = [self ownerInContext: nil];
|
||||
if (uid)
|
||||
{
|
||||
[storeUIDs addObjectUniquely: uid];
|
||||
[removedUIDs removeObject: uid];
|
||||
}
|
||||
|
||||
/* organizer might have changed completely */
|
||||
|
||||
if (oldApt && ([props containsObject: @"organizer"]))
|
||||
{
|
||||
uid = [self getUIDForICalPerson: [oldApt organizer]];
|
||||
if (uid && ![storeUIDs containsObject: uid])
|
||||
[removedUIDs addObjectUniquely: uid];
|
||||
}
|
||||
|
||||
[self debugWithFormat: @"UID ops:\n store: %@\n remove: %@",
|
||||
storeUIDs, removedUIDs];
|
||||
|
||||
/* if time did change, all participants have to re-decide ...
|
||||
* ... exception from that rule: the organizer
|
||||
*/
|
||||
|
||||
if (oldApt
|
||||
&& ([props containsObject: @"startDate"]
|
||||
|| [props containsObject: @"endDate"]
|
||||
|| [props containsObject: @"duration"]))
|
||||
{
|
||||
NSArray *ps;
|
||||
unsigned i, count;
|
||||
|
||||
ps = [newApt attendees];
|
||||
count = [ps count];
|
||||
for (i = 0; i < count; i++) {
|
||||
iCalPerson *p;
|
||||
|
||||
p = [ps objectAtIndex:i];
|
||||
if (![p hasSameEmailAddress:organizer])
|
||||
[p setParticipationStatus:iCalPersonPartStatNeedsAction];
|
||||
}
|
||||
_iCal = [[newApt parent] versitString];
|
||||
updateForcesReconsider = YES;
|
||||
}
|
||||
|
||||
/* perform storing */
|
||||
|
||||
storeError = [self saveContentString: _iCal inUIDs: storeUIDs];
|
||||
delError = [self deleteInUIDs: removedUIDs];
|
||||
|
||||
// TODO: make compound
|
||||
if (storeError != nil) return storeError;
|
||||
if (delError != nil) return delError;
|
||||
|
||||
/* email notifications */
|
||||
if ([self sendEMailNotifications]
|
||||
&& [self _aptIsStillRelevant: newApt])
|
||||
{
|
||||
iCalEvent *requestApt;
|
||||
|
||||
requestApt = [newApt copy];
|
||||
[(iCalCalendar *) [requestApt parent] setMethod: @"request"];
|
||||
attendees
|
||||
= [NSMutableArray arrayWithArray: [changes insertedAttendees]];
|
||||
[attendees removePerson: organizer];
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
forOldObject: nil
|
||||
andNewObject: requestApt
|
||||
toAttendees: attendees];
|
||||
[requestApt release];
|
||||
|
||||
if (updateForcesReconsider)
|
||||
{
|
||||
iCalEvent *updatedApt;
|
||||
|
||||
updatedApt = [newApt copy];
|
||||
[(iCalCalendar *) [updatedApt parent] setMethod: @"request"];
|
||||
attendees = [NSMutableArray arrayWithArray:[newApt attendees]];
|
||||
[attendees removeObjectsInArray:[changes insertedAttendees]];
|
||||
[attendees removePerson:organizer];
|
||||
[self sendEMailUsingTemplateNamed: @"Update"
|
||||
forOldObject: oldApt
|
||||
andNewObject: updatedApt
|
||||
toAttendees: attendees];
|
||||
[updatedApt release];
|
||||
}
|
||||
|
||||
attendees
|
||||
= [NSMutableArray arrayWithArray: [changes deletedAttendees]];
|
||||
[attendees removePerson: organizer];
|
||||
attendees = [newEvent attendeesWithoutUser: [context activeUser]];
|
||||
if ([attendees count])
|
||||
{
|
||||
iCalEvent *cancelledApt;
|
||||
|
||||
cancelledApt = [newApt copy];
|
||||
[(iCalCalendar *) [cancelledApt parent] setMethod: @"cancel"];
|
||||
[self sendEMailUsingTemplateNamed: @"Removal"
|
||||
[self _handleAddedUsers: attendees fromEvent: newEvent];
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
forOldObject: nil
|
||||
andNewObject: cancelledApt
|
||||
andNewObject: [newEvent itipEntryWithMethod: @"request"]
|
||||
toAttendees: attendees];
|
||||
[cancelledApt release];
|
||||
}
|
||||
|
||||
if (![[newEvent attendees] count])
|
||||
[[newEvent uniqueChildWithTag: @"organizer"] setValue: 0
|
||||
to: @""];
|
||||
}
|
||||
}
|
||||
else
|
||||
[self primarySaveContentString: _iCal];
|
||||
|
||||
return nil;
|
||||
[super saveComponent: newEvent];
|
||||
}
|
||||
|
||||
- (NSException *) deleteWithBaseSequence: (int)_v
|
||||
- (NSException *) _updateAttendee: (iCalPerson *) attendee
|
||||
forEventUID: (NSString *) eventUID
|
||||
withSequence: (NSNumber *) sequence
|
||||
forUID: (NSString *) uid
|
||||
{
|
||||
/*
|
||||
Note: We need to delete in all participants folders and send iMIP messages
|
||||
for all external accounts.
|
||||
Delete is basically identical to save with all attendees and the
|
||||
organizer being deleted.
|
||||
|
||||
Steps:
|
||||
- fetch stored content
|
||||
- parse old content
|
||||
- check if sequence matches (or if 0=ignore)
|
||||
- extract old attendee list + organizer (make unique)
|
||||
- delete in removed folders
|
||||
- send iMIP mail for all folders not found
|
||||
*/
|
||||
iCalEvent *apt;
|
||||
NSMutableArray *attendees, *removedUIDs;
|
||||
SOGoAppointmentObject *eventObject;
|
||||
iCalEvent *event;
|
||||
iCalPerson *otherAttendee;
|
||||
NSString *iCalString;
|
||||
NSException *error;
|
||||
|
||||
if ([[context request] handledByDefaultHandler])
|
||||
error = nil;
|
||||
|
||||
eventObject = [self _lookupEvent: eventUID forUID: uid];
|
||||
if (![eventObject isNew])
|
||||
{
|
||||
/* load existing content */
|
||||
apt = (iCalEvent *) [self component: NO];
|
||||
|
||||
/* compare sequence if requested */
|
||||
|
||||
// if (_v != 0) {
|
||||
// // TODO
|
||||
// }
|
||||
|
||||
removedUIDs = [NSMutableArray arrayWithArray:
|
||||
[self attendeeUIDsFromAppointment: apt]];
|
||||
if (![removedUIDs containsObject: owner])
|
||||
[removedUIDs addObject: owner];
|
||||
|
||||
if ([self sendEMailNotifications]
|
||||
&& [self _aptIsStillRelevant: apt])
|
||||
event = [eventObject component: NO secure: NO];
|
||||
if ([[event sequence] compare: sequence]
|
||||
== NSOrderedSame)
|
||||
{
|
||||
/* send notification email to attendees excluding organizer */
|
||||
attendees = [NSMutableArray arrayWithArray: [apt attendees]];
|
||||
[attendees removePerson: [apt organizer]];
|
||||
|
||||
/* flag appointment as being cancelled */
|
||||
[(iCalCalendar *) [apt parent] setMethod: @"cancel"];
|
||||
[apt increaseSequence];
|
||||
|
||||
/* remove all attendees to signal complete removal */
|
||||
[apt removeAllAttendees];
|
||||
|
||||
/* send notification email */
|
||||
[self sendEMailUsingTemplateNamed: @"Deletion"
|
||||
forOldObject: nil
|
||||
andNewObject: apt
|
||||
toAttendees: attendees];
|
||||
otherAttendee = [event findParticipant: [context activeUser]];
|
||||
[otherAttendee setPartStat: [attendee partStat]];
|
||||
iCalString = [[event parent] versitString];
|
||||
error = [eventObject saveContentString: iCalString];
|
||||
}
|
||||
|
||||
error = [self deleteInUIDs: removedUIDs];
|
||||
}
|
||||
else
|
||||
error = [self primaryDelete];
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
- (NSException *) saveContentString: (NSString *) _iCalString
|
||||
- (NSException *) _handleAttendee: (iCalPerson *) attendee
|
||||
statusChange: (NSString *) newStatus
|
||||
inEvent: (iCalEvent *) event
|
||||
{
|
||||
return [self saveContentString: _iCalString baseSequence: 0];
|
||||
NSString *newContent, *currentStatus, *organizerUID;
|
||||
NSException *ex;
|
||||
|
||||
ex = nil;
|
||||
|
||||
currentStatus = [attendee partStat];
|
||||
if ([currentStatus caseInsensitiveCompare: newStatus]
|
||||
!= NSOrderedSame)
|
||||
{
|
||||
[attendee setPartStat: newStatus];
|
||||
newContent = [[event parent] versitString];
|
||||
ex = [self saveContentString: newContent];
|
||||
if (!(ex || [event userIsOrganizer: [context activeUser]]))
|
||||
{
|
||||
if ([[attendee rsvp] isEqualToString: @"true"])
|
||||
[self sendResponseToOrganizer];
|
||||
organizerUID = [[event organizer] uid];
|
||||
if (organizerUID)
|
||||
ex = [self _updateAttendee: attendee
|
||||
forEventUID: [event uid]
|
||||
withSequence: [event sequence]
|
||||
forUID: organizerUID];
|
||||
}
|
||||
}
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
- (NSException *) changeParticipationStatus: (NSString *) _status
|
||||
{
|
||||
iCalEvent *event;
|
||||
iCalPerson *attendee;
|
||||
NSException *ex;
|
||||
|
||||
ex = nil;
|
||||
|
||||
event = [self component: NO secure: NO];
|
||||
if (event)
|
||||
{
|
||||
attendee = [event findParticipant: [context activeUser]];
|
||||
if (attendee)
|
||||
ex = [self _handleAttendee: attendee statusChange: _status
|
||||
inEvent: event];
|
||||
else
|
||||
ex = [NSException exceptionWithHTTPStatus: 404 /* Not Found */
|
||||
reason: @"user does not participate in this "
|
||||
@"calendar event"];
|
||||
}
|
||||
else
|
||||
ex = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
|
||||
reason: @"unable to parse event record"];
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
- (void) prepareDelete
|
||||
{
|
||||
iCalEvent *event;
|
||||
SOGoUser *currentUser;
|
||||
NSArray *attendees;
|
||||
|
||||
if ([[context request] handledByDefaultHandler])
|
||||
{
|
||||
currentUser = [context activeUser];
|
||||
event = [self component: NO secure: NO];
|
||||
if ([event userIsOrganizer: currentUser])
|
||||
{
|
||||
attendees = [event attendeesWithoutUser: currentUser];
|
||||
if ([attendees count])
|
||||
{
|
||||
[self _handleRemovedUsers: attendees];
|
||||
[self sendEMailUsingTemplateNamed: @"Deletion"
|
||||
forOldObject: nil
|
||||
andNewObject: [event itipEntryWithMethod: @"cancel"]
|
||||
toAttendees: attendees];
|
||||
}
|
||||
}
|
||||
else if ([event userIsParticipant: currentUser])
|
||||
[self changeParticipationStatus: @"DECLINED"];
|
||||
}
|
||||
}
|
||||
|
||||
/* message type */
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<#IsSubject>Re: rendez-vous le <#AptStartDate/> à <#AptStartTime/></#IsSubject>
|
||||
<#IsBody>
|
||||
<#Attendee/> has <#HasAccepted>accepted</#HasAccepted><#HasDeclined>declined</#HasDeclined> your invitation.
|
||||
</#IsBody>
|
||||
@@ -0,0 +1,33 @@
|
||||
AptStartDate: WOString {
|
||||
value = startDate;
|
||||
dateformat = "%d/%m/%y";
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
AptStartTime: WOString {
|
||||
value = startDate;
|
||||
dateformat = "%H:%M";
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
Attendee: WOString {
|
||||
value = attendee.cnWithoutQuotes;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
HasAccepted: WOConditional {
|
||||
condition = hasAccepted;
|
||||
}
|
||||
|
||||
HasDeclined: WOConditional {
|
||||
condition = hasDeclined;
|
||||
}
|
||||
|
||||
IsSubject: WOConditional {
|
||||
condition = isSubject;
|
||||
}
|
||||
|
||||
IsBody: WOConditional {
|
||||
condition = isSubject;
|
||||
negate = YES;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<#IsSubject>Re: rendez-vous le <#AptStartDate/> à <#AptStartTime/></#IsSubject>
|
||||
<#IsBody>
|
||||
<#Attendee/> a <#HasAccepted>accepté</#HasAccepted><#HasDeclined>décliné</#HasDeclined> votre invitation.
|
||||
</#IsBody>
|
||||
@@ -0,0 +1,33 @@
|
||||
AptStartDate: WOString {
|
||||
value = startDate;
|
||||
dateformat = "%d/%m/%y";
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
AptStartTime: WOString {
|
||||
value = startDate;
|
||||
dateformat = "%H:%M";
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
Attendee: WOString {
|
||||
value = attendee.cnWithoutQuotes;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
HasAccepted: WOConditional {
|
||||
condition = hasAccepted;
|
||||
}
|
||||
|
||||
HasDeclined: WOConditional {
|
||||
condition = hasDeclined;
|
||||
}
|
||||
|
||||
IsSubject: WOConditional {
|
||||
condition = isSubject;
|
||||
}
|
||||
|
||||
IsBody: WOConditional {
|
||||
condition = isSubject;
|
||||
negate = YES;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<#IsSubject>Re: rendez-vous le <#AptStartDate/> à <#AptStartTime/></#IsSubject>
|
||||
<#IsBody>
|
||||
<#Attendee/> has <#HasAccepted>accepted</#HasAccepted><#HasDeclined>declined</#HasDeclined> your invitation.
|
||||
</#IsBody>
|
||||
@@ -0,0 +1,33 @@
|
||||
AptStartDate: WOString {
|
||||
value = startDate;
|
||||
dateformat = "%d/%m/%y";
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
AptStartTime: WOString {
|
||||
value = startDate;
|
||||
dateformat = "%H:%M";
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
Attendee: WOString {
|
||||
value = attendee.cnWithoutQuotes;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
HasAccepted: WOConditional {
|
||||
condition = hasAccepted;
|
||||
}
|
||||
|
||||
HasDeclined: WOConditional {
|
||||
condition = hasDeclined;
|
||||
}
|
||||
|
||||
IsSubject: WOConditional {
|
||||
condition = isSubject;
|
||||
}
|
||||
|
||||
IsBody: WOConditional {
|
||||
condition = isSubject;
|
||||
negate = YES;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/* SOGoAptMailICalReply.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse groupe conseil
|
||||
*
|
||||
* 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 SOGOAPTMAILICALREPLY_H
|
||||
#define SOGOAPTMAILICALREPLY_H
|
||||
|
||||
#import <NGObjWeb/SoComponent.h>
|
||||
|
||||
@class NSString;
|
||||
@class NSCalendarDate;
|
||||
|
||||
@class iCalPerson;
|
||||
@class iCalEntityObject;
|
||||
|
||||
/*
|
||||
* NOTE: We inherit from SoComponent in order to get the correct
|
||||
* resourceManager required for this product
|
||||
*/
|
||||
@interface SOGoAptMailICalReply : SoComponent
|
||||
{
|
||||
iCalEntityObject *apt;
|
||||
iCalPerson *attendee;
|
||||
NSString *homePageURL;
|
||||
BOOL isSubject;
|
||||
}
|
||||
|
||||
- (void) setApt: (iCalEntityObject *) newApt;
|
||||
- (iCalEntityObject *) apt;
|
||||
|
||||
- (void) setAttendee: (iCalPerson *) newAttendee;
|
||||
- (iCalPerson *) attendee;
|
||||
|
||||
/* Content Generation */
|
||||
|
||||
- (NSString *) getSubject;
|
||||
- (NSString *) getBody;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOAPTMAILICALREPLY_H */
|
||||
@@ -0,0 +1,178 @@
|
||||
/* SOGoAptMailICalReply - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse groupe conseil
|
||||
*
|
||||
* 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/NSCharacterSet.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGObjWeb/WOActionResults.h>
|
||||
#import <NGObjWeb/WOMessage.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <NGCards/iCalEntityObject.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
|
||||
#import <SoObjects/SOGo/NSString+Utilities.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
|
||||
#import "SOGoAptMailICalReply.h"
|
||||
|
||||
@interface SOGoAptMailICalReply (PrivateAPI)
|
||||
|
||||
- (BOOL) isSubject;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailICalReply
|
||||
|
||||
static NSCharacterSet *wsSet = nil;
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
static BOOL didInit = NO;
|
||||
|
||||
if (!didInit)
|
||||
{
|
||||
didInit = YES;
|
||||
wsSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] retain];
|
||||
}
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
apt = nil;
|
||||
attendee = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[apt release];
|
||||
[attendee release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) setApt: (iCalEntityObject *) newApt
|
||||
{
|
||||
ASSIGN (apt, newApt);
|
||||
}
|
||||
|
||||
- (iCalEntityObject *) apt
|
||||
{
|
||||
return apt;
|
||||
}
|
||||
|
||||
- (void) setAttendee: (iCalPerson *) newAttendee
|
||||
{
|
||||
ASSIGN (attendee, newAttendee);
|
||||
}
|
||||
|
||||
- (iCalPerson *) attendee
|
||||
{
|
||||
return attendee;
|
||||
}
|
||||
|
||||
- (BOOL) hasAccepted
|
||||
{
|
||||
NSString *partStat;
|
||||
|
||||
partStat = [[attendee partStat] lowercaseString];
|
||||
|
||||
return [partStat isEqualToString: @"accepted"];
|
||||
}
|
||||
|
||||
- (BOOL) hasDeclined
|
||||
{
|
||||
NSString *partStat;
|
||||
|
||||
partStat = [[attendee partStat] lowercaseString];
|
||||
|
||||
return [partStat isEqualToString: @"declined"];
|
||||
}
|
||||
|
||||
- (NSCalendarDate *) startDate
|
||||
{
|
||||
NSCalendarDate *date;
|
||||
SOGoUser *user;
|
||||
|
||||
date = [apt startDate];
|
||||
user = [[self context] activeUser];
|
||||
[date setTimeZone: [user timeZone]];
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
- (BOOL) isSubject
|
||||
{
|
||||
return isSubject;
|
||||
}
|
||||
|
||||
/* Generate Response */
|
||||
|
||||
- (NSString *) getSubject
|
||||
{
|
||||
NSString *subject;
|
||||
|
||||
isSubject = YES;
|
||||
subject = [[[self generateResponse] contentAsString]
|
||||
stringByTrimmingCharactersInSet: wsSet];
|
||||
if (!subject)
|
||||
{
|
||||
[self errorWithFormat:@"Failed to properly generate subject! Please check "
|
||||
@"template for component '%@'!",
|
||||
[self name]];
|
||||
subject = @"ERROR: missing subject!";
|
||||
}
|
||||
|
||||
return [subject asQPSubjectString: @"utf-8"];
|
||||
}
|
||||
|
||||
- (NSString *) getBody
|
||||
{
|
||||
isSubject = NO;
|
||||
return [[self generateResponse] contentAsString];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface SOGoAptMailEnglishICalReply : SOGoAptMailICalReply
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailEnglishICalReply
|
||||
@end
|
||||
|
||||
@interface SOGoAptMailFrenchICalReply : SOGoAptMailICalReply
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailFrenchICalReply
|
||||
@end
|
||||
|
||||
@interface SOGoAptMailGermanICalReply : SOGoAptMailICalReply
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailGermanICalReply
|
||||
@end
|
||||
@@ -35,21 +35,19 @@
|
||||
@class SOGoUser;
|
||||
|
||||
@interface SOGoCalendarComponent : SOGoContentObject
|
||||
{
|
||||
iCalCalendar *calendar;
|
||||
NSString *calContent;
|
||||
}
|
||||
|
||||
- (NSString *) componentTag;
|
||||
- (iCalCalendar *) calendar: (BOOL) create;
|
||||
- (iCalRepeatableEntityObject *) component: (BOOL) create;
|
||||
|
||||
- (NSException *) primarySaveContentString: (NSString *) _iCalString;
|
||||
- (NSException *) primaryDelete;
|
||||
- (iCalCalendar *) calendar: (BOOL) create
|
||||
secure: (BOOL) secure;
|
||||
- (id) component: (BOOL) create secure: (BOOL) secure;
|
||||
|
||||
- (NSException *) delete;
|
||||
// - (NSException *) primarySaveContentString: (NSString *) _iCalString;
|
||||
// - (NSException *) primaryDelete;
|
||||
|
||||
- (NSException *) changeParticipationStatus: (NSString *) _status;
|
||||
// - (NSException *) delete;
|
||||
|
||||
- (void) saveComponent: (iCalRepeatableEntityObject *) newObject;
|
||||
|
||||
/* mail notifications */
|
||||
- (BOOL) sendEMailNotifications;
|
||||
@@ -64,7 +62,6 @@
|
||||
- (iCalPerson *) findParticipantWithUID: (NSString *) uid;
|
||||
|
||||
- (iCalPerson *) iCalPersonWithUID: (NSString *) uid;
|
||||
- (NSString *) getUIDForICalPerson: (iCalPerson *) person;
|
||||
- (NSArray *) getUIDsForICalPersons: (NSArray *) iCalPersons;
|
||||
|
||||
@end
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NGHashMap.h>
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalEvent.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalRepeatableEntityObject.h>
|
||||
#import <NGMime/NGMimeBodyPart.h>
|
||||
@@ -42,10 +43,13 @@
|
||||
#import <SoObjects/SOGo/SOGoMailer.h>
|
||||
#import <SoObjects/SOGo/SOGoPermissions.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
#import <SoObjects/SOGo/WORequest+SOGo.h>
|
||||
#import <SoObjects/Appointments/SOGoAppointmentFolder.h>
|
||||
|
||||
#import "SOGoAptMailICalReply.h"
|
||||
#import "SOGoAptMailNotification.h"
|
||||
#import "iCalEntityObject+SOGo.h"
|
||||
#import "iCalPerson+SOGo.h"
|
||||
#import "SOGoCalendarComponent.h"
|
||||
|
||||
static BOOL sendEMailNotifications = NO;
|
||||
@@ -67,26 +71,6 @@ static BOOL sendEMailNotifications = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
calendar = nil;
|
||||
calContent = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
if (calendar)
|
||||
[calendar release];
|
||||
if (calContent)
|
||||
[calContent release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString *) davContentType
|
||||
{
|
||||
return @"text/calendar";
|
||||
@@ -111,16 +95,15 @@ static BOOL sendEMailNotifications = NO;
|
||||
[component removeAllAlarms];
|
||||
}
|
||||
|
||||
- (NSString *) contentAsString
|
||||
- (NSString *) secureContentAsString
|
||||
{
|
||||
iCalCalendar *tmpCalendar;
|
||||
iCalRepeatableEntityObject *tmpComponent;
|
||||
// NSArray *roles;
|
||||
// NSString *uid;
|
||||
SoSecurityManager *sm;
|
||||
NSString *iCalString;
|
||||
|
||||
if (!calContent)
|
||||
{
|
||||
// uid = [[context activeUser] login];
|
||||
// roles = [self aclsForUser: uid];
|
||||
// if ([roles containsObject: SOGoCalendarRole_Organizer]
|
||||
@@ -139,113 +122,76 @@ static BOOL sendEMailNotifications = NO;
|
||||
// else
|
||||
// calContent = nil;
|
||||
|
||||
sm = [SoSecurityManager sharedSecurityManager];
|
||||
if (![sm validatePermission: SOGoCalendarPerm_ViewAllComponent
|
||||
onObject: self inContext: context])
|
||||
calContent = content;
|
||||
else if (![sm validatePermission: SOGoCalendarPerm_ViewDAndT
|
||||
onObject: self inContext: context])
|
||||
sm = [SoSecurityManager sharedSecurityManager];
|
||||
if (![sm validatePermission: SOGoCalendarPerm_ViewAllComponent
|
||||
onObject: self inContext: context])
|
||||
iCalString = content;
|
||||
else if (![sm validatePermission: SOGoCalendarPerm_ViewDAndT
|
||||
onObject: self inContext: context])
|
||||
{
|
||||
tmpCalendar = [[self calendar: NO secure: NO] copy];
|
||||
tmpComponent = (iCalRepeatableEntityObject *)
|
||||
[tmpCalendar firstChildWithTag: [self componentTag]];
|
||||
[self _filterComponent: tmpComponent];
|
||||
iCalString = [tmpCalendar versitString];
|
||||
[tmpCalendar release];
|
||||
}
|
||||
else
|
||||
iCalString = nil;
|
||||
|
||||
return iCalString;
|
||||
}
|
||||
|
||||
- (iCalCalendar *) calendar: (BOOL) create secure: (BOOL) secure
|
||||
{
|
||||
NSString *componentTag;
|
||||
CardGroup *newComponent;
|
||||
iCalCalendar *calendar;
|
||||
NSString *iCalString;
|
||||
|
||||
if (secure)
|
||||
iCalString = [self secureContentAsString];
|
||||
else
|
||||
iCalString = content;
|
||||
|
||||
if ([iCalString length] > 0)
|
||||
calendar = [iCalCalendar parseSingleFromSource: iCalString];
|
||||
else
|
||||
{
|
||||
if (create)
|
||||
{
|
||||
tmpCalendar = [[self calendar: NO] copy];
|
||||
tmpComponent = (iCalRepeatableEntityObject *)
|
||||
[tmpCalendar firstChildWithTag: [self componentTag]];
|
||||
[self _filterComponent: tmpComponent];
|
||||
calContent = [tmpCalendar versitString];
|
||||
[tmpCalendar release];
|
||||
calendar = [iCalCalendar groupWithTag: @"vcalendar"];
|
||||
[calendar setVersion: @"2.0"];
|
||||
[calendar setProdID: @"-//Inverse groupe conseil//SOGo 0.9//EN"];
|
||||
componentTag = [[self componentTag] uppercaseString];
|
||||
newComponent = [[calendar classForTag: componentTag]
|
||||
groupWithTag: componentTag];
|
||||
[calendar addChild: newComponent];
|
||||
}
|
||||
else
|
||||
calContent = nil;
|
||||
|
||||
[calContent retain];
|
||||
}
|
||||
|
||||
return calContent;
|
||||
}
|
||||
|
||||
- (void) setContentString: (NSString *) newContent
|
||||
{
|
||||
[super setContentString: newContent];
|
||||
ASSIGN (calendar, nil);
|
||||
ASSIGN (calContent, nil);
|
||||
}
|
||||
|
||||
// - (NSException *) saveContentString: (NSString *) contentString
|
||||
// baseVersion: (unsigned int) baseVersion
|
||||
// {
|
||||
// NSException *result;
|
||||
|
||||
// result = [super saveContentString: contentString
|
||||
// baseVersion: baseVersion];
|
||||
// if (!result && calContent)
|
||||
// {
|
||||
// [calContent release];
|
||||
// calContent = nil;
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// }
|
||||
|
||||
- (iCalCalendar *) calendar: (BOOL) create
|
||||
{
|
||||
NSString *iCalString, *componentTag;
|
||||
CardGroup *newComponent;
|
||||
|
||||
if (!calendar)
|
||||
{
|
||||
iCalString = [super contentAsString];
|
||||
if ([iCalString length] > 0)
|
||||
calendar = [iCalCalendar parseSingleFromSource: iCalString];
|
||||
else
|
||||
{
|
||||
if (create)
|
||||
{
|
||||
calendar = [iCalCalendar groupWithTag: @"vcalendar"];
|
||||
[calendar setVersion: @"2.0"];
|
||||
[calendar setProdID: @"-//Inverse groupe conseil//SOGo 0.9//EN"];
|
||||
componentTag = [[self componentTag] uppercaseString];
|
||||
newComponent = [[calendar classForTag: componentTag]
|
||||
groupWithTag: componentTag];
|
||||
[calendar addChild: newComponent];
|
||||
}
|
||||
}
|
||||
if (calendar)
|
||||
[calendar retain];
|
||||
calendar = nil;
|
||||
}
|
||||
|
||||
return calendar;
|
||||
}
|
||||
|
||||
- (iCalRepeatableEntityObject *) component: (BOOL) create
|
||||
- (id) component: (BOOL) create secure: (BOOL) secure
|
||||
{
|
||||
return
|
||||
(iCalRepeatableEntityObject *) [[self calendar: create]
|
||||
firstChildWithTag: [self componentTag]];
|
||||
return [[self calendar: create secure: secure]
|
||||
firstChildWithTag: [self componentTag]];
|
||||
}
|
||||
|
||||
- (void) saveComponent: (iCalRepeatableEntityObject *) newObject
|
||||
{
|
||||
NSString *newiCalString;
|
||||
|
||||
newiCalString = [[newObject parent] versitString];
|
||||
|
||||
[self saveContentString: newiCalString];
|
||||
}
|
||||
|
||||
/* raw saving */
|
||||
|
||||
- (NSException *) primarySaveContentString: (NSString *) _iCalString
|
||||
{
|
||||
return [super saveContentString: _iCalString];
|
||||
}
|
||||
|
||||
- (NSException *) primaryDelete
|
||||
{
|
||||
return [super delete];
|
||||
}
|
||||
|
||||
- (NSException *) deleteWithBaseSequence: (int) a
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSException *) delete
|
||||
{
|
||||
return [self deleteWithBaseSequence: 0];
|
||||
}
|
||||
|
||||
/* EMail Notifications */
|
||||
- (NSString *) homePageURLForPerson: (iCalPerson *) _person
|
||||
{
|
||||
@@ -262,58 +208,13 @@ static BOOL sendEMailNotifications = NO;
|
||||
baseURL = @"http://localhost/";
|
||||
[self warnWithFormat:@"Unable to create baseURL from context!"];
|
||||
}
|
||||
uid = [[LDAPUserManager sharedUserManager]
|
||||
getUIDForEmail: [_person rfc822Email]];
|
||||
uid = [_person uid];
|
||||
|
||||
return ((uid)
|
||||
? [NSString stringWithFormat:@"%@%@", baseURL, uid]
|
||||
: nil);
|
||||
}
|
||||
|
||||
- (NSException *) changeParticipationStatus: (NSString *) _status
|
||||
{
|
||||
iCalRepeatableEntityObject *component;
|
||||
iCalPerson *person;
|
||||
NSString *newContent;
|
||||
NSException *ex;
|
||||
|
||||
ex = nil;
|
||||
|
||||
component = [self component: NO];
|
||||
if (component)
|
||||
{
|
||||
person = [self findParticipantWithUID: owner];
|
||||
if (person)
|
||||
{
|
||||
// TODO: send iMIP reply mails?
|
||||
[person setPartStat: _status];
|
||||
newContent = [[component parent] versitString];
|
||||
if (newContent)
|
||||
{
|
||||
ex = [self saveContentString: newContent];
|
||||
if (ex)
|
||||
// TODO: why is the exception wrapped?
|
||||
/* Server Error */
|
||||
ex = [NSException exceptionWithHTTPStatus: 500
|
||||
reason: [ex reason]];
|
||||
}
|
||||
else
|
||||
ex
|
||||
= [NSException exceptionWithHTTPStatus: 500 /* Server Error */
|
||||
reason: @"Could not generate iCalendar data ..."];
|
||||
}
|
||||
else
|
||||
ex = [NSException exceptionWithHTTPStatus: 404 /* Not Found */
|
||||
reason: @"user does not participate in this "
|
||||
@"calendar component"];
|
||||
}
|
||||
else
|
||||
ex = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
|
||||
reason: @"unable to parse component record"];
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
- (BOOL) sendEMailNotifications
|
||||
{
|
||||
return sendEMailNotifications;
|
||||
@@ -335,7 +236,7 @@ static BOOL sendEMailNotifications = NO;
|
||||
{
|
||||
NSString *pageName;
|
||||
iCalPerson *organizer;
|
||||
NSString *cn, *email, *sender, *iCalString;
|
||||
NSString *email, *sender, *iCalString;
|
||||
WOApplication *app;
|
||||
unsigned i, count;
|
||||
iCalPerson *attendee;
|
||||
@@ -347,115 +248,196 @@ static BOOL sendEMailNotifications = NO;
|
||||
NGMimeBodyPart *bodyPart;
|
||||
NGMimeMultipartBody *body;
|
||||
|
||||
if ([_attendees count])
|
||||
if (sendEMailNotifications
|
||||
&& [_newObject isStillRelevant])
|
||||
{
|
||||
/* sender */
|
||||
|
||||
organizer = [_newObject organizer];
|
||||
cn = [organizer cnWithoutQuotes];
|
||||
if (cn)
|
||||
sender = [NSString stringWithFormat:@"%@ <%@>",
|
||||
cn,
|
||||
[organizer rfc822Email]];
|
||||
else
|
||||
sender = [organizer rfc822Email];
|
||||
|
||||
/* generate iCalString once */
|
||||
iCalString = [[_newObject parent] versitString];
|
||||
|
||||
/* get WOApplication instance */
|
||||
app = [WOApplication application];
|
||||
|
||||
/* generate dynamic message content */
|
||||
|
||||
count = [_attendees count];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
attendee = [_attendees objectAtIndex:i];
|
||||
if (count)
|
||||
{
|
||||
/* sender */
|
||||
organizer = [_newObject organizer];
|
||||
sender = [organizer mailAddress];
|
||||
|
||||
/* construct recipient */
|
||||
cn = [attendee cn];
|
||||
email = [attendee rfc822Email];
|
||||
if (cn)
|
||||
recipient = [NSString stringWithFormat: @"%@ <%@>",
|
||||
cn, email];
|
||||
else
|
||||
recipient = email;
|
||||
NSLog (@"sending '%@' from %@",
|
||||
[(iCalCalendar *) [_newObject parent] method], organizer);
|
||||
|
||||
language = [[context activeUser] language];
|
||||
/* generate iCalString once */
|
||||
iCalString = [[_newObject parent] versitString];
|
||||
|
||||
/* get WOApplication instance */
|
||||
app = [WOApplication application];
|
||||
|
||||
/* generate dynamic message content */
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
attendee = [_attendees objectAtIndex: i];
|
||||
if (![[attendee uid] isEqualToString: owner])
|
||||
{
|
||||
/* construct recipient */
|
||||
recipient = [attendee mailAddress];
|
||||
email = [attendee rfc822Email];
|
||||
|
||||
NSLog (@"recipient: %@", recipient);
|
||||
language = [[context activeUser] language];
|
||||
#warning this could be optimized in a class hierarchy common with the \
|
||||
SOGoObject acl notification mechanism
|
||||
/* create page name */
|
||||
// TODO: select user's default language?
|
||||
pageName = [NSString stringWithFormat: @"SOGoAptMail%@%@",
|
||||
language,
|
||||
_pageName];
|
||||
/* construct message content */
|
||||
p = [app pageWithName: pageName inContext: context];
|
||||
[p setNewApt: _newObject];
|
||||
[p setOldApt: _oldObject];
|
||||
[p setHomePageURL: [self homePageURLForPerson: attendee]];
|
||||
[p setViewTZ: [self timeZoneForUser: email]];
|
||||
subject = [p getSubject];
|
||||
text = [p getBody];
|
||||
SOGoObject acl notification mechanism
|
||||
/* create page name */
|
||||
pageName = [NSString stringWithFormat: @"SOGoAptMail%@%@",
|
||||
language, _pageName];
|
||||
/* construct message content */
|
||||
p = [app pageWithName: pageName inContext: context];
|
||||
[p setNewApt: _newObject];
|
||||
[p setOldApt: _oldObject];
|
||||
[p setHomePageURL: [self homePageURLForPerson: attendee]];
|
||||
[p setViewTZ: [self timeZoneForUser: email]];
|
||||
subject = [p getSubject];
|
||||
text = [p getBody];
|
||||
|
||||
/* construct message */
|
||||
headerMap = [NGMutableHashMap hashMapWithCapacity: 5];
|
||||
/* construct message */
|
||||
headerMap = [NGMutableHashMap hashMapWithCapacity: 5];
|
||||
|
||||
/* NOTE: multipart/alternative seems like the correct choice but
|
||||
* unfortunately Thunderbird doesn't offer the rich content alternative
|
||||
* at all. Mail.app shows the rich content alternative _only_
|
||||
* so we'll stick with multipart/mixed for the time being.
|
||||
*/
|
||||
[headerMap setObject: @"multipart/mixed" forKey: @"content-type"];
|
||||
[headerMap setObject: sender forKey: @"From"];
|
||||
[headerMap setObject: recipient forKey: @"To"];
|
||||
mailDate = [[NSCalendarDate date] rfc822DateString];
|
||||
[headerMap setObject: mailDate forKey: @"date"];
|
||||
[headerMap setObject: subject forKey: @"Subject"];
|
||||
msg = [NGMimeMessage messageWithHeader: headerMap];
|
||||
/* NOTE: multipart/alternative seems like the correct choice but
|
||||
* unfortunately Thunderbird doesn't offer the rich content alternative
|
||||
* at all. Mail.app shows the rich content alternative _only_
|
||||
* so we'll stick with multipart/mixed for the time being.
|
||||
*/
|
||||
[headerMap setObject: @"multipart/mixed" forKey: @"content-type"];
|
||||
[headerMap setObject: sender forKey: @"From"];
|
||||
[headerMap setObject: recipient forKey: @"To"];
|
||||
mailDate = [[NSCalendarDate date] rfc822DateString];
|
||||
[headerMap setObject: mailDate forKey: @"date"];
|
||||
[headerMap setObject: subject forKey: @"Subject"];
|
||||
msg = [NGMimeMessage messageWithHeader: headerMap];
|
||||
|
||||
/* multipart body */
|
||||
body = [[NGMimeMultipartBody alloc] initWithPart: msg];
|
||||
/* multipart body */
|
||||
body = [[NGMimeMultipartBody alloc] initWithPart: msg];
|
||||
|
||||
/* text part */
|
||||
headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
|
||||
[headerMap setObject: @"text/plain; charset=utf-8"
|
||||
forKey: @"content-type"];
|
||||
bodyPart = [NGMimeBodyPart bodyPartWithHeader: headerMap];
|
||||
[bodyPart setBody: [text dataUsingEncoding: NSUTF8StringEncoding]];
|
||||
|
||||
/* attach text part to multipart body */
|
||||
[body addBodyPart: bodyPart];
|
||||
|
||||
/* text part */
|
||||
headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
|
||||
[headerMap setObject: @"text/plain; charset=utf-8"
|
||||
forKey: @"content-type"];
|
||||
bodyPart = [NGMimeBodyPart bodyPartWithHeader: headerMap];
|
||||
[bodyPart setBody: [text dataUsingEncoding: NSUTF8StringEncoding]];
|
||||
/* calendar part */
|
||||
header = [NSString stringWithFormat: @"text/calendar; method=%@;"
|
||||
@" charset=utf-8",
|
||||
[(iCalCalendar *) [_newObject parent] method]];
|
||||
headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
|
||||
[headerMap setObject:header forKey: @"content-type"];
|
||||
bodyPart = [NGMimeBodyPart bodyPartWithHeader: headerMap];
|
||||
[bodyPart setBody: [iCalString dataUsingEncoding: NSUTF8StringEncoding]];
|
||||
|
||||
/* attach text part to multipart body */
|
||||
[body addBodyPart: bodyPart];
|
||||
/* attach calendar part to multipart body */
|
||||
[body addBodyPart: bodyPart];
|
||||
|
||||
/* calendar part */
|
||||
header = [NSString stringWithFormat: @"text/calendar; method=%@;"
|
||||
@" charset=utf-8",
|
||||
[(iCalCalendar *) [_newObject parent] method]];
|
||||
headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
|
||||
[headerMap setObject:header forKey: @"content-type"];
|
||||
bodyPart = [NGMimeBodyPart bodyPartWithHeader: headerMap];
|
||||
[bodyPart setBody: [iCalString dataUsingEncoding: NSUTF8StringEncoding]];
|
||||
/* attach multipart body to message */
|
||||
[msg setBody: body];
|
||||
[body release];
|
||||
|
||||
/* attach calendar part to multipart body */
|
||||
[body addBodyPart: bodyPart];
|
||||
|
||||
/* attach multipart body to message */
|
||||
[msg setBody: body];
|
||||
[body release];
|
||||
|
||||
/* send the damn thing */
|
||||
[[SOGoMailer sharedMailer]
|
||||
sendMimePart: msg
|
||||
toRecipients: [NSArray arrayWithObject: email]
|
||||
sender: [organizer rfc822Email]];
|
||||
}
|
||||
/* send the damn thing */
|
||||
[[SOGoMailer sharedMailer]
|
||||
sendMimePart: msg
|
||||
toRecipients: [NSArray arrayWithObject: email]
|
||||
sender: [organizer rfc822Email]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) sendResponseToOrganizer
|
||||
{
|
||||
#warning THIS IS A STUB
|
||||
NSString *pageName, *language, *mailDate, *email;
|
||||
WOApplication *app;
|
||||
iCalPerson *organizer, *attendee;
|
||||
NSString *iCalString;
|
||||
iCalEvent *event;
|
||||
SOGoAptMailICalReply *p;
|
||||
NGMutableHashMap *headerMap;
|
||||
NGMimeMessage *msg;
|
||||
NGMimeBodyPart *bodyPart;
|
||||
NGMimeMultipartBody *body;
|
||||
NSData *bodyData;
|
||||
|
||||
event = [[self component: NO secure: NO] itipEntryWithMethod: @"reply"];
|
||||
if (![event userIsOrganizer: [context activeUser]])
|
||||
{
|
||||
organizer = [event organizer];
|
||||
attendee = [event findParticipant: [context activeUser]];
|
||||
[event setAttendees: [NSArray arrayWithObject: attendee]];
|
||||
|
||||
/* get WOApplication instance */
|
||||
app = [WOApplication application];
|
||||
|
||||
language = [[context activeUser] language];
|
||||
/* create page name */
|
||||
pageName
|
||||
= [NSString stringWithFormat: @"SOGoAptMail%@ICalReply", language];
|
||||
/* construct message content */
|
||||
p = [app pageWithName: pageName inContext: context];
|
||||
[p setApt: event];
|
||||
[p setAttendee: attendee];
|
||||
|
||||
/* construct message */
|
||||
headerMap = [NGMutableHashMap hashMapWithCapacity: 5];
|
||||
|
||||
/* NOTE: multipart/alternative seems like the correct choice but
|
||||
* unfortunately Thunderbird doesn't offer the rich content alternative
|
||||
* at all. Mail.app shows the rich content alternative _only_
|
||||
* so we'll stick with multipart/mixed for the time being.
|
||||
*/
|
||||
[headerMap setObject: @"multipart/mixed" forKey: @"content-type"];
|
||||
[headerMap setObject: [attendee mailAddress] forKey: @"From"];
|
||||
[headerMap setObject: [organizer mailAddress] forKey: @"To"];
|
||||
mailDate = [[NSCalendarDate date] rfc822DateString];
|
||||
[headerMap setObject: mailDate forKey: @"date"];
|
||||
[headerMap setObject: [p getSubject] forKey: @"Subject"];
|
||||
msg = [NGMimeMessage messageWithHeader: headerMap];
|
||||
|
||||
NSLog (@"sending 'REPLY' from %@ to %@",
|
||||
[attendee mailAddress], [organizer mailAddress]);
|
||||
|
||||
/* multipart body */
|
||||
body = [[NGMimeMultipartBody alloc] initWithPart: msg];
|
||||
|
||||
/* text part */
|
||||
headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
|
||||
[headerMap setObject: @"text/plain; charset=utf-8"
|
||||
forKey: @"content-type"];
|
||||
bodyPart = [NGMimeBodyPart bodyPartWithHeader: headerMap];
|
||||
bodyData = [[p getBody] dataUsingEncoding: NSUTF8StringEncoding];
|
||||
[bodyPart setBody: bodyData];
|
||||
|
||||
/* attach text part to multipart body */
|
||||
[body addBodyPart: bodyPart];
|
||||
|
||||
/* calendar part */
|
||||
headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
|
||||
[headerMap setObject: @"text/calendar; method=REPLY; charset=utf-8"
|
||||
forKey: @"content-type"];
|
||||
bodyPart = [NGMimeBodyPart bodyPartWithHeader: headerMap];
|
||||
iCalString = [[event parent] versitString];
|
||||
[bodyPart setBody: [iCalString dataUsingEncoding: NSUTF8StringEncoding]];
|
||||
|
||||
/* attach calendar part to multipart body */
|
||||
[body addBodyPart: bodyPart];
|
||||
|
||||
/* attach multipart body to message */
|
||||
[msg setBody: body];
|
||||
[body release];
|
||||
|
||||
/* send the damn thing */
|
||||
email = [organizer rfc822Email];
|
||||
[[SOGoMailer sharedMailer]
|
||||
sendMimePart: msg
|
||||
toRecipients: [NSArray arrayWithObject: email]
|
||||
sender: [attendee rfc822Email]];
|
||||
}
|
||||
}
|
||||
|
||||
// - (BOOL) isOrganizerOrOwner: (SOGoUser *) user
|
||||
@@ -481,7 +463,7 @@ static BOOL sendEMailNotifications = NO;
|
||||
SOGoUser *user;
|
||||
|
||||
user = [SOGoUser userWithLogin: uid roles: nil];
|
||||
component = [self component: NO];
|
||||
component = [self component: NO secure: NO];
|
||||
|
||||
return [component findParticipant: user];
|
||||
}
|
||||
@@ -503,31 +485,20 @@ static BOOL sendEMailNotifications = NO;
|
||||
return person;
|
||||
}
|
||||
|
||||
- (NSString *) getUIDForICalPerson: (iCalPerson *) person
|
||||
{
|
||||
LDAPUserManager *um;
|
||||
|
||||
um = [LDAPUserManager sharedUserManager];
|
||||
|
||||
return [um getUIDForEmail: [person rfc822Email]];
|
||||
}
|
||||
|
||||
- (NSArray *) getUIDsForICalPersons: (NSArray *) iCalPersons
|
||||
{
|
||||
iCalPerson *currentPerson;
|
||||
NSEnumerator *persons;
|
||||
NSMutableArray *uids;
|
||||
NSString *uid;
|
||||
LDAPUserManager *um;
|
||||
|
||||
uids = [NSMutableArray array];
|
||||
|
||||
um = [LDAPUserManager sharedUserManager];
|
||||
persons = [iCalPersons objectEnumerator];
|
||||
currentPerson = [persons nextObject];
|
||||
while (currentPerson)
|
||||
{
|
||||
uid = [um getUIDForEmail: [currentPerson rfc822Email]];
|
||||
uid = [currentPerson uid];
|
||||
if (uid)
|
||||
[uids addObject: uid];
|
||||
currentPerson = [persons nextObject];
|
||||
@@ -591,7 +562,7 @@ static BOOL sendEMailNotifications = NO;
|
||||
if ([superAcls count] > 0)
|
||||
[roles addObjectsFromArray: superAcls];
|
||||
|
||||
component = [self component: NO];
|
||||
component = [self component: NO secure: NO];
|
||||
ownerRole = [self _roleOfOwner: component];
|
||||
if ([owner isEqualToString: uid])
|
||||
[roles addObject: ownerRole];
|
||||
|
||||
@@ -44,13 +44,6 @@
|
||||
|
||||
@interface SOGoTaskObject : SOGoCalendarComponent
|
||||
|
||||
/* "iCal multifolder saves" */
|
||||
|
||||
- (NSException *) saveContentString: (NSString *) _iCal
|
||||
baseSequence: (int) _v;
|
||||
- (NSException *) deleteWithBaseSequence: (int) _v;
|
||||
- (NSException *) saveContentString: (NSString *) _iCalString;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __Appointmentss_SOGoTaskObject_H__ */
|
||||
|
||||
@@ -38,415 +38,17 @@
|
||||
|
||||
#import "SOGoTaskObject.h"
|
||||
|
||||
@interface SOGoTaskObject (PrivateAPI)
|
||||
|
||||
- (NSString *) homePageURLForPerson: (iCalPerson *) _person;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SOGoTaskObject
|
||||
|
||||
static NSString *mailTemplateDefaultLanguage = nil;
|
||||
|
||||
+ (void)initialize {
|
||||
NSUserDefaults *ud;
|
||||
static BOOL didInit = NO;
|
||||
|
||||
if (didInit) return;
|
||||
didInit = YES;
|
||||
|
||||
ud = [NSUserDefaults standardUserDefaults];
|
||||
mailTemplateDefaultLanguage = [[ud stringForKey:@"SOGoDefaultLanguage"]
|
||||
retain];
|
||||
if (!mailTemplateDefaultLanguage)
|
||||
mailTemplateDefaultLanguage = @"French";
|
||||
}
|
||||
|
||||
- (NSString *) componentTag
|
||||
{
|
||||
return @"vtodo";
|
||||
}
|
||||
|
||||
/* iCal handling */
|
||||
|
||||
- (NSArray *)attendeeUIDsFromTask:(iCalToDo *)_task {
|
||||
LDAPUserManager *um;
|
||||
NSMutableArray *uids;
|
||||
NSArray *attendees;
|
||||
unsigned i, count;
|
||||
NSString *email, *uid;
|
||||
|
||||
if (![_task isNotNull])
|
||||
return nil;
|
||||
|
||||
if ((attendees = [_task attendees]) == nil)
|
||||
return nil;
|
||||
count = [attendees count];
|
||||
uids = [NSMutableArray arrayWithCapacity:count + 1];
|
||||
|
||||
um = [LDAPUserManager sharedUserManager];
|
||||
|
||||
/* add organizer */
|
||||
|
||||
email = [[_task organizer] rfc822Email];
|
||||
if ([email isNotNull]) {
|
||||
uid = [um getUIDForEmail:email];
|
||||
if ([uid isNotNull]) {
|
||||
[uids addObject:uid];
|
||||
}
|
||||
else
|
||||
[self logWithFormat:@"Note: got no uid for organizer: '%@'", email];
|
||||
}
|
||||
|
||||
/* add attendees */
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
iCalPerson *person;
|
||||
|
||||
person = [attendees objectAtIndex:i];
|
||||
email = [person rfc822Email];
|
||||
if (![email isNotNull]) continue;
|
||||
|
||||
uid = [um getUIDForEmail:email];
|
||||
if (![uid isNotNull]) {
|
||||
[self logWithFormat:@"Note: got no uid for email: '%@'", email];
|
||||
continue;
|
||||
}
|
||||
if (![uids containsObject:uid])
|
||||
[uids addObject:uid];
|
||||
}
|
||||
|
||||
return uids;
|
||||
}
|
||||
|
||||
/* store in all the other folders */
|
||||
|
||||
- (NSException *)saveContentString:(NSString *)_iCal inUIDs:(NSArray *)_uids {
|
||||
NSEnumerator *e;
|
||||
id folder;
|
||||
NSException *allErrors = nil;
|
||||
|
||||
e = [[container lookupCalendarFoldersForUIDs: _uids inContext: context]
|
||||
objectEnumerator];
|
||||
while ((folder = [e nextObject]) != nil) {
|
||||
NSException *error;
|
||||
SOGoTaskObject *task;
|
||||
|
||||
if (![folder isNotNull]) /* no folder was found for given UID */
|
||||
continue;
|
||||
|
||||
task = [folder lookupName:[self nameInContainer] inContext: context
|
||||
acquire:NO];
|
||||
if ([task isKindOfClass: [NSException class]])
|
||||
{
|
||||
[self logWithFormat:@"Note: an exception occured finding '%@' in folder: %@",
|
||||
[self nameInContainer], folder];
|
||||
[self logWithFormat:@"the exception reason was: %@",
|
||||
[(NSException *) task reason]];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (![task isNotNull]) {
|
||||
[self logWithFormat:@"Note: did not find '%@' in folder: %@",
|
||||
[self nameInContainer], folder];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((error = [task primarySaveContentString:_iCal]) != nil) {
|
||||
[self logWithFormat:@"Note: failed to save iCal in folder: %@", folder];
|
||||
// TODO: make compound
|
||||
allErrors = error;
|
||||
}
|
||||
}
|
||||
return allErrors;
|
||||
}
|
||||
// - (NSException *)deleteInUIDs:(NSArray *)_uids {
|
||||
// NSEnumerator *e;
|
||||
// id folder;
|
||||
// NSException *allErrors = nil;
|
||||
|
||||
// e = [[container lookupCalendarFoldersForUIDs: _uids inContext: context]
|
||||
// objectEnumerator];
|
||||
// while ((folder = [e nextObject])) {
|
||||
// NSException *error;
|
||||
// SOGoTaskObject *task;
|
||||
|
||||
// task = [folder lookupName: [self nameInContainer]
|
||||
// inContext: context
|
||||
// acquire: NO];
|
||||
// if (![task isNotNull]) {
|
||||
// [self logWithFormat:@"Note: did not find '%@' in folder: %@",
|
||||
// [self nameInContainer], folder];
|
||||
// continue;
|
||||
// }
|
||||
// if ([task isKindOfClass: [NSException class]]) {
|
||||
// [self logWithFormat:@"Exception: %@", [(NSException *) task reason]];
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// if ((error = [task primaryDelete]) != nil) {
|
||||
// [self logWithFormat:@"Note: failed to delete in folder: %@", folder];
|
||||
// // TODO: make compound
|
||||
// allErrors = error;
|
||||
// }
|
||||
// }
|
||||
// return allErrors;
|
||||
// }
|
||||
|
||||
/* "iCal multifolder saves" */
|
||||
|
||||
- (NSException *) saveContentString: (NSString *) _iCal
|
||||
baseSequence: (int) _v
|
||||
{
|
||||
/*
|
||||
Note: we need to delete in all participants folders and send iMIP messages
|
||||
for all external accounts.
|
||||
|
||||
Steps:
|
||||
- fetch stored content
|
||||
- parse old content
|
||||
- check if sequence matches (or if 0=ignore)
|
||||
- extract old attendee list + organizer (make unique)
|
||||
- parse new content (ensure that sequence is increased!)
|
||||
- extract new attendee list + organizer (make unique)
|
||||
- make a diff => new, same, removed
|
||||
- write to new, same
|
||||
- delete in removed folders
|
||||
- send iMIP mail for all folders not found
|
||||
*/
|
||||
// LDAPUserManager *um;
|
||||
// iCalCalendar *calendar;
|
||||
// iCalToDo *oldApt, *newApt;
|
||||
// // iCalToDoChanges *changes;
|
||||
// iCalPerson *organizer;
|
||||
// NSString *oldContent, *uid;
|
||||
// NSArray *uids, *props;
|
||||
// NSMutableArray *attendees, *storeUIDs, *removedUIDs;
|
||||
NSException *storeError, *delError;
|
||||
// BOOL updateForcesReconsider;
|
||||
|
||||
// updateForcesReconsider = NO;
|
||||
|
||||
// if ([_iCal length] == 0) {
|
||||
// return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
|
||||
// reason:@"got no iCalendar content to store!"];
|
||||
// }
|
||||
|
||||
// um = [LDAPUserManager sharedUserManager];
|
||||
|
||||
// /* handle old content */
|
||||
|
||||
// oldContent = [self contentAsString]; /* if nil, this is a new task */
|
||||
// if ([oldContent length] == 0)
|
||||
// {
|
||||
// /* new task */
|
||||
// [self debugWithFormat:@"saving new task: %@", _iCal];
|
||||
// oldApt = nil;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// calendar = [iCalCalendar parseSingleFromSource: oldContent];
|
||||
// oldApt = [self firstTaskFromCalendar: calendar];
|
||||
// }
|
||||
|
||||
// /* compare sequence if requested */
|
||||
|
||||
// if (_v != 0) {
|
||||
// // TODO
|
||||
// }
|
||||
|
||||
|
||||
// /* handle new content */
|
||||
|
||||
// calendar = [iCalCalendar parseSingleFromSource: _iCal];
|
||||
// newApt = [self firstTaskFromCalendar: calendar];
|
||||
// if (newApt == nil) {
|
||||
// return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
|
||||
// reason:@"could not parse iCalendar content!"];
|
||||
// }
|
||||
|
||||
// /* diff */
|
||||
|
||||
// changes = [iCalToDoChanges changesFromEvent: oldApt
|
||||
// toEvent: newApt];
|
||||
|
||||
// uids = [um getUIDsForICalPersons:[changes deletedAttendees]
|
||||
// applyStrictMapping:NO];
|
||||
// removedUIDs = [NSMutableArray arrayWithArray:uids];
|
||||
|
||||
// uids = [um getUIDsForICalPersons:[newApt attendees]
|
||||
// applyStrictMapping:NO];
|
||||
// storeUIDs = [NSMutableArray arrayWithArray:uids];
|
||||
// props = [changes updatedProperties];
|
||||
|
||||
// /* detect whether sequence has to be increased */
|
||||
// if ([changes hasChanges])
|
||||
// [newApt increaseSequence];
|
||||
|
||||
// /* preserve organizer */
|
||||
|
||||
// organizer = [newApt organizer];
|
||||
// uid = [um getUIDForICalPerson:organizer];
|
||||
// if (uid) {
|
||||
// if (![storeUIDs containsObject:uid])
|
||||
// [storeUIDs addObject:uid];
|
||||
// [removedUIDs removeObject:uid];
|
||||
// }
|
||||
|
||||
// /* organizer might have changed completely */
|
||||
|
||||
// if (oldApt && ([props containsObject: @"organizer"])) {
|
||||
// uid = [um getUIDForICalPerson:[oldApt organizer]];
|
||||
// if (uid) {
|
||||
// if (![storeUIDs containsObject:uid]) {
|
||||
// if (![removedUIDs containsObject:uid]) {
|
||||
// [removedUIDs addObject:uid];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// [self debugWithFormat:@"UID ops:\n store: %@\n remove: %@",
|
||||
// storeUIDs, removedUIDs];
|
||||
|
||||
// /* if time did change, all participants have to re-decide ...
|
||||
// * ... exception from that rule: the organizer
|
||||
// */
|
||||
|
||||
// if (oldApt != nil &&
|
||||
// ([props containsObject:@"startDate"] ||
|
||||
// [props containsObject:@"endDate"] ||
|
||||
// [props containsObject:@"duration"]))
|
||||
// {
|
||||
// NSArray *ps;
|
||||
// unsigned i, count;
|
||||
|
||||
// ps = [newApt attendees];
|
||||
// count = [ps count];
|
||||
// for (i = 0; i < count; i++) {
|
||||
// iCalPerson *p;
|
||||
|
||||
// p = [ps objectAtIndex:i];
|
||||
// if (![p hasSameEmailAddress:organizer])
|
||||
// [p setParticipationStatus:iCalPersonPartStatNeedsAction];
|
||||
// }
|
||||
// _iCal = [[newApt parent] versitString];
|
||||
// updateForcesReconsider = YES;
|
||||
// }
|
||||
|
||||
// /* perform storing */
|
||||
|
||||
storeError = [self primarySaveContentString: _iCal];
|
||||
|
||||
// storeError = [self saveContentString:_iCal inUIDs:storeUIDs];
|
||||
// delError = [self deleteInUIDs:removedUIDs];
|
||||
|
||||
// TODO: make compound
|
||||
if (storeError != nil) return storeError;
|
||||
// if (delError != nil) return delError;
|
||||
|
||||
/* email notifications */
|
||||
// if ([self sendEMailNotifications])
|
||||
// {
|
||||
// attendees = [NSMutableArray arrayWithArray:[changes insertedAttendees]];
|
||||
// [attendees removePerson:organizer];
|
||||
// [self sendInvitationEMailForTask:newApt
|
||||
// toAttendees:attendees];
|
||||
|
||||
// if (updateForcesReconsider) {
|
||||
// attendees = [NSMutableArray arrayWithArray:[newApt attendees]];
|
||||
// [attendees removeObjectsInArray:[changes insertedAttendees]];
|
||||
// [attendees removePerson:organizer];
|
||||
// [self sendEMailUsingTemplateNamed: @"Update"
|
||||
// forOldObject: oldApt
|
||||
// andNewObject: newApt
|
||||
// toAttendees: attendees];
|
||||
// }
|
||||
|
||||
// attendees = [NSMutableArray arrayWithArray:[changes deletedAttendees]];
|
||||
// [attendees removePerson: organizer];
|
||||
// if ([attendees count]) {
|
||||
// iCalToDo *cancelledApt;
|
||||
|
||||
// cancelledApt = [newApt copy];
|
||||
// [(iCalCalendar *) [cancelledApt parent] setMethod: @"cancel"];
|
||||
// [self sendEMailUsingTemplateNamed: @"Removal"
|
||||
// forOldObject: nil
|
||||
// andNewObject: cancelledApt
|
||||
// toAttendees: attendees];
|
||||
// [cancelledApt release];
|
||||
// }
|
||||
// }
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSException *)deleteWithBaseSequence:(int)_v {
|
||||
/*
|
||||
Note: We need to delete in all participants folders and send iMIP messages
|
||||
for all external accounts.
|
||||
Delete is basically identical to save with all attendees and the
|
||||
organizer being deleted.
|
||||
|
||||
Steps:
|
||||
- fetch stored content
|
||||
- parse old content
|
||||
- check if sequence matches (or if 0=ignore)
|
||||
- extract old attendee list + organizer (make unique)
|
||||
- delete in removed folders
|
||||
- send iMIP mail for all folders not found
|
||||
*/
|
||||
// iCalToDo *task;
|
||||
// NSArray *removedUIDs;
|
||||
// NSMutableArray *attendees;
|
||||
|
||||
[self primaryDelete];
|
||||
|
||||
return nil;
|
||||
// /* load existing content */
|
||||
|
||||
// task = (iCalToDo *) [self component: NO];
|
||||
|
||||
// /* compare sequence if requested */
|
||||
|
||||
// if (_v != 0) {
|
||||
// // TODO
|
||||
// }
|
||||
|
||||
// removedUIDs = [self attendeeUIDsFromTask:task];
|
||||
|
||||
// if ([self sendEMailNotifications])
|
||||
// {
|
||||
// /* send notification email to attendees excluding organizer */
|
||||
// attendees = [NSMutableArray arrayWithArray:[task attendees]];
|
||||
// [attendees removePerson:[task organizer]];
|
||||
|
||||
// /* flag task as being cancelled */
|
||||
// [(iCalCalendar *) [task parent] setMethod: @"cancel"];
|
||||
// [task increaseSequence];
|
||||
|
||||
// /* remove all attendees to signal complete removal */
|
||||
// [task removeAllAttendees];
|
||||
|
||||
// /* send notification email */
|
||||
// [self sendEMailUsingTemplateNamed: @"Deletion"
|
||||
// forOldObject: nil
|
||||
// andNewObject: task
|
||||
// toAttendees: attendees];
|
||||
// }
|
||||
|
||||
// /* perform */
|
||||
|
||||
// return [self deleteInUIDs:removedUIDs];
|
||||
}
|
||||
|
||||
- (NSException *)saveContentString:(NSString *)_iCalString {
|
||||
return [self saveContentString:_iCalString baseSequence:0];
|
||||
}
|
||||
|
||||
/* message type */
|
||||
|
||||
- (NSString *)outlookMessageClass {
|
||||
- (NSString *) outlookMessageClass
|
||||
{
|
||||
return @"IPM.Task";
|
||||
}
|
||||
|
||||
|
||||
@@ -25,11 +25,20 @@
|
||||
|
||||
#import <NGCards/iCalEntityObject.h>
|
||||
|
||||
@class SOGoUser;
|
||||
|
||||
@interface iCalEntityObject (SOGoExtensions)
|
||||
|
||||
- (BOOL) userIsParticipant: (SOGoUser *) user;
|
||||
- (BOOL) userIsOrganizer: (SOGoUser *) user;
|
||||
|
||||
- (NSArray *) attendeeUIDs;
|
||||
- (BOOL) isStillRelevant;
|
||||
|
||||
- (id) itipEntryWithMethod: (NSString *) method;
|
||||
|
||||
- (NSArray *) attendeesWithoutUser: (SOGoUser *) user;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* ICALENTITYOBJECT_SOGO_H */
|
||||
|
||||
@@ -23,11 +23,14 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
|
||||
#import <SoObjects/SOGo/NSArray+Utilities.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
|
||||
#import "iCalPerson+SOGo.h"
|
||||
|
||||
#import "iCalEntityObject+SOGo.h"
|
||||
|
||||
@implementation iCalEntityObject (SOGoExtensions)
|
||||
@@ -52,6 +55,7 @@
|
||||
return isParticipant;
|
||||
}
|
||||
|
||||
#warning user could be a delegate, we will need to handle that someday
|
||||
- (BOOL) userIsOrganizer: (SOGoUser *) user
|
||||
{
|
||||
NSString *orgMail;
|
||||
@@ -61,4 +65,66 @@
|
||||
return [user hasEmail: orgMail];
|
||||
}
|
||||
|
||||
- (NSArray *) attendeeUIDs
|
||||
{
|
||||
NSEnumerator *attendees;
|
||||
NSString *uid;
|
||||
iCalPerson *currentAttendee;
|
||||
NSMutableArray *uids;
|
||||
|
||||
uids = [NSMutableArray array];
|
||||
|
||||
attendees = [[self attendees] objectEnumerator];
|
||||
while ((currentAttendee = [attendees nextObject]))
|
||||
{
|
||||
uid = [currentAttendee uid];
|
||||
if (uid)
|
||||
[uids addObject: uid];
|
||||
}
|
||||
|
||||
return uids;
|
||||
}
|
||||
|
||||
#warning this method should be implemented in a category of iCalToDo
|
||||
- (BOOL) isStillRelevant
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id) itipEntryWithMethod: (NSString *) method
|
||||
{
|
||||
iCalCalendar *newCalendar;
|
||||
iCalEntityObject *newEntry;
|
||||
|
||||
newCalendar = [parent mutableCopy];
|
||||
[newCalendar autorelease];
|
||||
[newCalendar setMethod: method];
|
||||
newEntry = (iCalEntityObject *) [newCalendar firstChildWithTag: tag];
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
- (NSArray *) attendeesWithoutUser: (SOGoUser *) user
|
||||
{
|
||||
NSMutableArray *newAttendees;
|
||||
NSArray *oldAttendees;
|
||||
unsigned int count, max;
|
||||
iCalPerson *currentAttendee;
|
||||
NSString *userID;
|
||||
|
||||
userID = [user login];
|
||||
oldAttendees = [self attendees];
|
||||
max = [oldAttendees count];
|
||||
newAttendees = [NSMutableArray arrayWithCapacity: max];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentAttendee = [oldAttendees objectAtIndex: count];
|
||||
if (![[currentAttendee uid] isEqualToString: userID])
|
||||
[newAttendees addObject: currentAttendee];
|
||||
}
|
||||
|
||||
return newAttendees;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/* iCalEvent+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse groupe conseil
|
||||
*
|
||||
* 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 ICALEVENT_SOGO_H
|
||||
#define ICALEVENT_SOGO_H
|
||||
|
||||
#import <NGCards/iCalEvent.h>
|
||||
|
||||
@interface iCalEvent (SOGoExtensions)
|
||||
|
||||
- (BOOL) isStillRelevant;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* ICALEVENT_SOGO_H */
|
||||
@@ -0,0 +1,38 @@
|
||||
/* iCalEvent+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse groupe conseil
|
||||
*
|
||||
* 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/NSCalendarDate.h>
|
||||
|
||||
#import "iCalEvent+SOGo.h"
|
||||
|
||||
@implementation iCalEvent (SOGoExtensions)
|
||||
|
||||
- (BOOL) isStillRelevant
|
||||
{
|
||||
NSCalendarDate *now;
|
||||
|
||||
now = [NSCalendarDate calendarDate];
|
||||
|
||||
return ([[self endDate] earlierDate: now] == now);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,34 @@
|
||||
/* iCalEventChanges+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse groupe conseil
|
||||
*
|
||||
* 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 ICALEVENTCHANGES_SOGO_H
|
||||
#define ICALEVENTCHANGES_SOGO_H
|
||||
|
||||
#import <NGCards/iCalEventChanges.h>
|
||||
|
||||
@interface iCalEventChanges (SOGoExtensions)
|
||||
|
||||
- (BOOL) sequenceShouldBeIncreased;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* ICALEVENTCHANGES_SOGO_H */
|
||||
@@ -0,0 +1,52 @@
|
||||
/* iCalEventChanges+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse groupe conseil
|
||||
*
|
||||
* 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/NSEnumerator.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import "iCalEventChanges+SOGo.h"
|
||||
|
||||
@implementation iCalEventChanges (SOGoExtensions)
|
||||
|
||||
- (BOOL) sequenceShouldBeIncreased
|
||||
{
|
||||
NSString *properties[] = {@"organizer", @"startDate", @"endDate", /* vtask:
|
||||
@"due" */
|
||||
@"rdate", @"rrule", @"exdate", @"exrule",
|
||||
@"status", @"location", nil};
|
||||
NSString **currentProperty;
|
||||
BOOL updateRequired;
|
||||
|
||||
updateRequired = NO;
|
||||
|
||||
currentProperty = properties;
|
||||
while (!updateRequired && *currentProperty)
|
||||
if ([updatedProperties containsObject: *currentProperty])
|
||||
updateRequired = YES;
|
||||
else
|
||||
currentProperty++;
|
||||
|
||||
return updateRequired;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,39 @@
|
||||
/* iCalPerson+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse groupe conseil
|
||||
*
|
||||
* 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 ICALPERSON_SOGO_H
|
||||
#define ICALPERSON_SOGO_H
|
||||
|
||||
#import <NGCards/iCalPerson.h>
|
||||
|
||||
#import <SoObjects/SOGo/LDAPUserManager.h>
|
||||
|
||||
@class NSString;
|
||||
|
||||
@interface iCalPerson (SOGoExtension)
|
||||
|
||||
- (NSString *) mailAddress;
|
||||
- (NSString *) uid;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* ICALPERSON_SOGO_H */
|
||||
@@ -0,0 +1,53 @@
|
||||
/* iCalPerson+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse groupe conseil
|
||||
*
|
||||
* 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/NSString.h>
|
||||
|
||||
#import "iCalPerson+SOGo.h"
|
||||
|
||||
static LDAPUserManager *um = nil;
|
||||
|
||||
@implementation iCalPerson (SOGoExtension)
|
||||
|
||||
- (NSString *) mailAddress
|
||||
{
|
||||
NSString *cn, *email, *mailAddress;
|
||||
|
||||
cn = [self cnWithoutQuotes];
|
||||
email = [self rfc822Email];
|
||||
if ([cn length])
|
||||
mailAddress = [NSString stringWithFormat:@"%@ <%@>", cn, email];
|
||||
else
|
||||
mailAddress = email;
|
||||
|
||||
return mailAddress;
|
||||
}
|
||||
|
||||
- (NSString *) uid
|
||||
{
|
||||
if (!um)
|
||||
um = [LDAPUserManager sharedUserManager];
|
||||
|
||||
return [um getUIDForEmail: [self rfc822Email]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -41,6 +41,7 @@
|
||||
NSString *IDField; /* the first part of a user DN */
|
||||
NSString *CNField;
|
||||
NSString *UIDField;
|
||||
NSArray *mailFields;
|
||||
NSString *bindFields;
|
||||
|
||||
NGLdapConnection *ldapConnection;
|
||||
@@ -59,6 +60,7 @@
|
||||
IDField: (NSString *) newIDField
|
||||
CNField: (NSString *) newCNField
|
||||
UIDField: (NSString *) newUIDField
|
||||
mailFields: (NSArray *) newMailFields
|
||||
andBindFields: (NSString *) newBindFields;
|
||||
|
||||
- (BOOL) checkLogin: (NSString *) login
|
||||
|
||||
@@ -146,6 +146,8 @@ static int sizeLimit;
|
||||
IDField = @"cn"; /* the first part of a user DN */
|
||||
CNField = @"cn";
|
||||
UIDField = @"uid";
|
||||
mailFields = [NSArray arrayWithObject: @"mail"];
|
||||
[mailFields retain];
|
||||
bindFields = nil;
|
||||
|
||||
ldapConnection = nil;
|
||||
@@ -164,6 +166,7 @@ static int sizeLimit;
|
||||
[IDField release];
|
||||
[CNField release];
|
||||
[UIDField release];
|
||||
[mailFields release];
|
||||
[bindFields release];
|
||||
[ldapConnection release];
|
||||
[sourceID release];
|
||||
@@ -183,7 +186,8 @@ static int sizeLimit;
|
||||
[self setBaseDN: [udSource objectForKey: @"baseDN"]
|
||||
IDField: [udSource objectForKey: @"IDFieldName"]
|
||||
CNField: [udSource objectForKey: @"CNFieldName"]
|
||||
UIDField: [udSource objectForKey: @"UIDFieldName"]
|
||||
UIDField: [udSource objectForKey: @"UIDFieldName"]
|
||||
mailFields: [udSource objectForKey: @"MailFieldNames"]
|
||||
andBindFields: [udSource objectForKey: @"bindFields"]];
|
||||
|
||||
return self;
|
||||
@@ -205,6 +209,7 @@ static int sizeLimit;
|
||||
IDField: (NSString *) newIDField
|
||||
CNField: (NSString *) newCNField
|
||||
UIDField: (NSString *) newUIDField
|
||||
mailFields: (NSArray *) newMailFields
|
||||
andBindFields: (NSString *) newBindFields
|
||||
{
|
||||
ASSIGN (baseDN, newBaseDN);
|
||||
@@ -214,6 +219,8 @@ static int sizeLimit;
|
||||
ASSIGN (CNField, newCNField);
|
||||
if (UIDField)
|
||||
ASSIGN (UIDField, newUIDField);
|
||||
if (newMailFields)
|
||||
ASSIGN (mailFields, newMailFields);
|
||||
if (newBindFields)
|
||||
ASSIGN (bindFields, newBindFields);
|
||||
}
|
||||
@@ -346,8 +353,6 @@ static int sizeLimit;
|
||||
|
||||
- (NSArray *) _searchAttributes
|
||||
{
|
||||
NSArray *attrs;
|
||||
|
||||
if (!searchAttributes)
|
||||
{
|
||||
searchAttributes = [NSMutableArray new];
|
||||
@@ -355,15 +360,10 @@ static int sizeLimit;
|
||||
[searchAttributes addObject: CNField];
|
||||
if (UIDField)
|
||||
[searchAttributes addObject: UIDField];
|
||||
[searchAttributes addObjectsFromArray: mailFields];
|
||||
[searchAttributes addObjectsFromArray: commonSearchFields];
|
||||
}
|
||||
|
||||
// We also include our MailFieldNames in the search
|
||||
if ((attrs = [[[LDAPUserManager sharedUserManager] metadataForSourceID: sourceID] objectForKey: @"MailFieldNames"]))
|
||||
{
|
||||
[searchAttributes addObjectsFromArray: attrs];
|
||||
}
|
||||
|
||||
return searchAttributes;
|
||||
}
|
||||
|
||||
@@ -397,6 +397,25 @@ static int sizeLimit;
|
||||
return ids;
|
||||
}
|
||||
|
||||
- (void) _fillEmailsOfEntry: (NGLdapEntry *) ldapEntry
|
||||
intoContactEntry: (NSMutableDictionary *) contactEntry
|
||||
{
|
||||
NSEnumerator *emailFields;
|
||||
NSString *currentFieldName, *value;
|
||||
NSMutableArray *emails;
|
||||
|
||||
emails = [NSMutableArray new];
|
||||
emailFields = [mailFields objectEnumerator];
|
||||
while ((currentFieldName = [emailFields nextObject]))
|
||||
{
|
||||
value = [[ldapEntry attributeWithName: currentFieldName] stringValueAtIndex: 0];
|
||||
if (value)
|
||||
[emails addObject: value];
|
||||
}
|
||||
[emails autorelease];
|
||||
[contactEntry setObject: emails forKey: @"c_emails"];
|
||||
}
|
||||
|
||||
- (NSDictionary *) _convertLDAPEntryToContact: (NGLdapEntry *) ldapEntry
|
||||
{
|
||||
NSMutableDictionary *contactEntry;
|
||||
@@ -426,6 +445,7 @@ static int sizeLimit;
|
||||
if (!value)
|
||||
value = @"";
|
||||
[contactEntry setObject: value forKey: @"c_cn"];
|
||||
[self _fillEmailsOfEntry: ldapEntry intoContactEntry: contactEntry];
|
||||
|
||||
return contactEntry;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#import <Foundation/NSUserDefaults.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import "NSArray+Utilities.h"
|
||||
#import "LDAPSource.h"
|
||||
#import "LDAPUserManager.h"
|
||||
|
||||
@@ -289,9 +290,7 @@ static NSString *defaultMailDomain = nil;
|
||||
emails = [contact objectForKey: @"emails"];
|
||||
uid = [contact objectForKey: @"c_uid"];
|
||||
systemEmail = [NSString stringWithFormat: @"%@@%@", uid, defaultMailDomain];
|
||||
if ([emails containsObject: systemEmail])
|
||||
[emails removeObject: systemEmail];
|
||||
[emails addObject: systemEmail];
|
||||
[emails addObjectUniquely: systemEmail];
|
||||
[contact setObject: [emails objectAtIndex: 0] forKey: @"c_email"];
|
||||
}
|
||||
|
||||
@@ -302,8 +301,8 @@ static NSString *defaultMailDomain = nil;
|
||||
NSDictionary *userEntry;
|
||||
NSEnumerator *ldapSources;
|
||||
LDAPSource *currentSource;
|
||||
NSString *cn, *email, *c_uid;
|
||||
NSArray *attrs;
|
||||
NSString *cn, *c_uid;
|
||||
NSArray *c_emails;
|
||||
|
||||
emails = [NSMutableArray array];
|
||||
cn = nil;
|
||||
@@ -320,18 +319,9 @@ static NSString *defaultMailDomain = nil;
|
||||
cn = [userEntry objectForKey: @"c_cn"];
|
||||
if (!c_uid)
|
||||
c_uid = [userEntry objectForKey: @"c_uid"];
|
||||
|
||||
if ((attrs = [[sourcesMetadata objectForKey: [currentSource sourceID]] objectForKey: @"MailFieldNames"]))
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < [attrs count]; i++)
|
||||
{
|
||||
email = [userEntry objectForKey: [attrs objectAtIndex: i]];
|
||||
if (email && ![emails containsObject: email])
|
||||
[emails addObject: email];
|
||||
}
|
||||
}
|
||||
c_emails = [userEntry objectForKey: @"c_emails"];
|
||||
if ([c_emails count])
|
||||
[emails addObjectsFromArray: c_emails];
|
||||
}
|
||||
currentSource = [ldapSources nextObject];
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
/* content */
|
||||
|
||||
- (BOOL) isNew;
|
||||
- (void) setContentString: (NSString *) newContent;
|
||||
- (NSString *) contentAsString;
|
||||
- (NSException *) saveContentString: (NSString *) _str
|
||||
baseVersion: (unsigned int) _baseVersion;
|
||||
@@ -65,4 +64,10 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface SOGoContentObject (OptionalMethods)
|
||||
|
||||
- (void) prepareDelete;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __SOGo_SOGoContentObject_H__ */
|
||||
|
||||
@@ -138,11 +138,6 @@
|
||||
return content;
|
||||
}
|
||||
|
||||
- (void) setContentString: (NSString *) newContent
|
||||
{
|
||||
ASSIGN (content, newContent);
|
||||
}
|
||||
|
||||
- (NSException *) saveContentString: (NSString *) newContent
|
||||
baseVersion: (unsigned int) newBaseVersion
|
||||
{
|
||||
@@ -152,11 +147,12 @@
|
||||
|
||||
ex = nil;
|
||||
|
||||
[self setContentString: newContent];
|
||||
ASSIGN (content, newContent);
|
||||
|
||||
folder = [container ocsFolder];
|
||||
if (folder)
|
||||
{
|
||||
ex = [folder writeContent: content toName: nameInContainer
|
||||
ex = [folder writeContent: newContent toName: nameInContainer
|
||||
baseVersion: newBaseVersion];
|
||||
if (ex)
|
||||
[self errorWithFormat:@"write failed: %@", ex];
|
||||
|
||||
@@ -350,7 +350,11 @@ static NSString *defaultUserID = @"<default>";
|
||||
deleteObject = [self lookupName: currentID
|
||||
inContext: context acquire: NO];
|
||||
if (![deleteObject isKindOfClass: [NSException class]])
|
||||
[deleteObject delete];
|
||||
{
|
||||
if ([deleteObject respondsToSelector: @selector (prepareDelete)])
|
||||
[deleteObject prepareDelete];
|
||||
[deleteObject delete];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,17 +34,19 @@
|
||||
context.activeUser
|
||||
*/
|
||||
|
||||
@class NSString;
|
||||
@class NSArray;
|
||||
@class NSDictionary;
|
||||
@class NSString;
|
||||
@class NSTimeZone;
|
||||
@class NSURL;
|
||||
@class NSUserDefaults;
|
||||
@class NSTimeZone;
|
||||
|
||||
@class WOContext;
|
||||
|
||||
@class SOGoAppointmentFolder;
|
||||
@class SOGoAppointmentFolders;
|
||||
@class SOGoDateFormatter;
|
||||
@class WOContext;
|
||||
@class SOGoUserFolder;
|
||||
|
||||
extern NSString *SOGoWeekStartHideWeekNumbers;
|
||||
extern NSString *SOGoWeekStartJanuary1;
|
||||
@@ -68,6 +70,7 @@ extern NSString *SOGoWeekStartFirstFullWeek;
|
||||
NSTimeZone *userTimeZone;
|
||||
SOGoDateFormatter *dateFormatter;
|
||||
NSMutableArray *mailAccounts;
|
||||
SOGoUserFolder *homeFolder;
|
||||
}
|
||||
|
||||
+ (NSString *) language;
|
||||
@@ -119,7 +122,7 @@ extern NSString *SOGoWeekStartFirstFullWeek;
|
||||
|
||||
/* folders */
|
||||
|
||||
- (id) homeFolderInContext: (id) _ctx;
|
||||
- (SOGoUserFolder *) homeFolderInContext: (id) context;
|
||||
|
||||
- (SOGoAppointmentFolders *) calendarsFolderInContext: (WOContext *) context;
|
||||
- (SOGoAppointmentFolder *)
|
||||
|
||||
+12
-20
@@ -142,6 +142,7 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
||||
language = nil;
|
||||
currentPassword = nil;
|
||||
dateFormatter = nil;
|
||||
homeFolder = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -176,6 +177,7 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
||||
[allEmails release];
|
||||
[language release];
|
||||
[dateFormatter release];
|
||||
[homeFolder release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@@ -528,27 +530,17 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
||||
// TODO: those methods should check whether the traversal stack in the context
|
||||
// already contains proper folders to improve caching behaviour
|
||||
|
||||
- (id) homeFolderInContext: (id) _ctx
|
||||
- (SOGoUserFolder *) homeFolderInContext: (id) context
|
||||
{
|
||||
/* Note: watch out for cyclic references */
|
||||
// TODO: maybe we should add an [activeUser reset] method to SOPE
|
||||
id folder;
|
||||
|
||||
folder = [(WOContext *)_ctx objectForKey:@"ActiveUserHomeFolder"];
|
||||
if (folder != nil)
|
||||
return [folder isNotNull] ? folder : nil;
|
||||
|
||||
folder = [[WOApplication application] lookupName: [self login]
|
||||
inContext: _ctx
|
||||
acquire: NO];
|
||||
if ([folder isKindOfClass:[NSException class]])
|
||||
return folder;
|
||||
|
||||
[(WOContext *)_ctx setObject: ((folder)
|
||||
? folder
|
||||
: (id)[NSNull null])
|
||||
forKey: @"ActiveUserHomeFolder"];
|
||||
return folder;
|
||||
if (!homeFolder)
|
||||
{
|
||||
homeFolder = [[WOApplication application] lookupName: [self login]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
[homeFolder retain];
|
||||
}
|
||||
|
||||
return homeFolder;
|
||||
}
|
||||
|
||||
- (SOGoAppointmentFolders *) calendarsFolderInContext: (WOContext *) context
|
||||
|
||||
Reference in New Issue
Block a user