From 4da8c21fc89157bece94220c58c29be1a6524ffe Mon Sep 17 00:00:00 2001 From: znek Date: Tue, 12 Jul 2005 13:56:19 +0000 Subject: [PATCH] Added Mail notifications for various events git-svn-id: http://svn.opengroupware.org/SOGo/trunk@720 d1b88da0-ebda-0310-925b-ed51d893ca5b --- SOGo/SoObjects/Appointments/ChangeLog | 19 + SOGo/SoObjects/Appointments/GNUmakefile | 14 +- .../Appointments/SOGoAppointmentObject.m | 371 ++++++++++++++++-- .../Appointments/SOGoAptMailInvitation.m | 33 ++ .../SOGoAptMailInvitation.html | 7 + .../SOGoAptMailInvitation.wod | 34 ++ .../Appointments/SOGoAptMailNotification.h | 64 +++ .../Appointments/SOGoAptMailNotification.m | 125 ++++++ .../Appointments/SOGoAptMailUpdate.m | 33 ++ .../SOGoAptMailUpdate.html | 8 + .../SOGoAptMailUpdate.wod | 46 +++ SOGo/SoObjects/Appointments/Version | 2 +- SOGo/SoObjects/SOGo/AgenorUserManager.h | 6 + SOGo/SoObjects/SOGo/AgenorUserManager.m | 33 ++ SOGo/SoObjects/SOGo/ChangeLog | 6 + SOGo/SoObjects/SOGo/Version | 2 +- SOGo/UI/Scheduler/ChangeLog | 5 + SOGo/UI/Scheduler/UIxCalView.h | 2 + SOGo/UI/Scheduler/Version | 2 +- SOGo/UI/Templates/ChangeLog | 5 + SOGo/UI/Templates/UIxAppointmentEditor.wox | 25 +- 21 files changed, 778 insertions(+), 64 deletions(-) create mode 100644 SOGo/SoObjects/Appointments/SOGoAptMailInvitation.m create mode 100644 SOGo/SoObjects/Appointments/SOGoAptMailInvitation.wo/SOGoAptMailInvitation.html create mode 100644 SOGo/SoObjects/Appointments/SOGoAptMailInvitation.wo/SOGoAptMailInvitation.wod create mode 100644 SOGo/SoObjects/Appointments/SOGoAptMailNotification.h create mode 100644 SOGo/SoObjects/Appointments/SOGoAptMailNotification.m create mode 100644 SOGo/SoObjects/Appointments/SOGoAptMailUpdate.m create mode 100644 SOGo/SoObjects/Appointments/SOGoAptMailUpdate.wo/SOGoAptMailUpdate.html create mode 100644 SOGo/SoObjects/Appointments/SOGoAptMailUpdate.wo/SOGoAptMailUpdate.wod diff --git a/SOGo/SoObjects/Appointments/ChangeLog b/SOGo/SoObjects/Appointments/ChangeLog index 9efea09c..bde97919 100644 --- a/SOGo/SoObjects/Appointments/ChangeLog +++ b/SOGo/SoObjects/Appointments/ChangeLog @@ -1,3 +1,22 @@ +2005-07-12 Marcus Mueller + + * v0.9.37 + + * SOGoAptMailNotification.[hm]: new component forming the basis for + the mail notification templates + + * SOGoAptMailInvitation.[wo, m]: invitation mail template, sent to all + new attendees of an appointment + + * SOGoAptMailUpdate.[wo, m]: update mail template, sent to all + attendees when appointment time is changed + + * SOGoAppointmentObject.m: rewritten to use the iCalEventChanges object + from NGiCal to discover changes to saved appointments. Additionaly + email notifications are emitted on certain occasions (this is not + full iMip, yet). In case the appointment time changes, state for + all attendees is reset to NEEDS-ACTION. + 2005-07-08 Marcus Mueller * SOGoAppointmentFolder.m: added 'partmails' and 'partstates' to diff --git a/SOGo/SoObjects/Appointments/GNUmakefile b/SOGo/SoObjects/Appointments/GNUmakefile index 0cd04c8e..1cab3383 100644 --- a/SOGo/SoObjects/Appointments/GNUmakefile +++ b/SOGo/SoObjects/Appointments/GNUmakefile @@ -13,10 +13,18 @@ Appointments_OBJC_FILES = \ SOGoAppointmentFolder.m \ SOGoGroupAppointmentFolder.m \ SOGoFreeBusyObject.m \ + \ + SOGoAptMailNotification.m \ + SOGoAptMailInvitation.m \ + SOGoAptMailUpdate.m \ + +Appointments_RESOURCE_FILES += \ + Version \ + product.plist \ -Appointments_RESOURCE_FILES += \ - Version \ - product.plist +Appointments_COMPONENTS += \ + SOGoAptMailInvitation.wo \ + SOGoAptMailUpdate.wo \ -include GNUmakefile.preamble include $(GNUSTEP_MAKEFILES)/bundle.make diff --git a/SOGo/SoObjects/Appointments/SOGoAppointmentObject.m b/SOGo/SoObjects/Appointments/SOGoAppointmentObject.m index 66f149b8..92b216bb 100644 --- a/SOGo/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SOGo/SoObjects/Appointments/SOGoAppointmentObject.m @@ -24,13 +24,40 @@ #include #include #include +#include +#include +#include +#include "SOGoAptMailNotification.h" #include "common.h" +@interface NSMutableArray (iCalPersonConvenience) +- (void)removePerson:(iCalPerson *)_person; +@end + +@interface SOGoAppointmentObject (PrivateAPI) +- (NSString *)homePageURLForPerson:(iCalPerson *)_person; +- (NSTimeZone *)viewTimeZoneForPerson:(iCalPerson *)_person; + +- (void)sendEMailUsingTemplateNamed:(NSString *)_pageName + forOldAppointment:(SOGoAppointment *)_newApt + andNewAppointment:(SOGoAppointment *)_oldApt + toAttendees:(NSArray *)_attendees; + +- (void)sendInvitationEMailForAppointment:(SOGoAppointment *)_apt + toAttendees:(NSArray *)_attendees; +- (void)sendAppointmentUpdateEMailForOldAppointment:(SOGoAppointment *)_oldApt + newAppointment:(SOGoAppointment *)_newApt + toAttendees:(NSArray *)_attendees; +- (void)sendRemovalEMailForAppointment:(SOGoAppointment *)_apt + toAttendees:(NSArray *)_attendees; +@end + @implementation SOGoAppointmentObject static id parser = nil; static SaxObjectDecoder *sax = nil; static NGLogger *logger = nil; +static NSTimeZone *MET = nil; + (void)initialize { NGLoggerManager *lm; @@ -54,6 +81,8 @@ static NGLogger *logger = nil; [parser setContentHandler:sax]; [parser setErrorHandler:sax]; + + MET = [[NSTimeZone timeZoneWithAbbreviation:@"MET"] retain]; } - (void)dealloc { @@ -240,18 +269,25 @@ static NGLogger *logger = nil; - delete in removed folders - send iMIP mail for all folders not found */ - SOGoAppointment *oldApt, *newApt; - NSString *oldContent; - NSArray *oldUIDs, *newUIDs; - NSMutableArray *storeUIDs, *removedUIDs; - unsigned i, count; - NSException *storeError, *delError; + AgenorUserManager *um; + SOGoAppointment *oldApt, *newApt; + iCalEventChanges *changes; + iCalPerson *organizer; + NSString *oldContent, *uid; + NSArray *uids, *props; + NSMutableArray *attendees, *storeUIDs, *removedUIDs; + NSException *storeError, *delError; + BOOL didChangeAppointmentTime; + didChangeAppointmentTime = NO; + if ([_iCal length] == 0) { return [NSException exceptionWithHTTPStatus:400 /* Bad Request */ reason:@"got no iCalendar content to store!"]; } - + + um = [AgenorUserManager sharedUserManager]; + /* handle old content */ oldContent = [self iCalString]; /* if nil, this is a new appointment */ @@ -271,9 +307,6 @@ static NGLogger *logger = nil; // TODO } - oldUIDs = [oldApt isNotNull] - ? [self attendeeUIDsFromAppointment:oldApt] - : nil; /* handle new content */ @@ -282,50 +315,102 @@ static NGLogger *logger = nil; return [NSException exceptionWithHTTPStatus:400 /* Bad Request */ reason:@"could not parse iCalendar content!"]; } - if ((newUIDs = [self attendeeUIDsFromAppointment:newApt]) == nil) - [self debugWithFormat:@"got no UIDs from appointment: %@", newApt]; /* diff */ - count = [oldUIDs count]; - removedUIDs = [NSMutableArray arrayWithCapacity:count]; - storeUIDs = [NSMutableArray arrayWithCapacity:count]; - for (i = 0; i < count; i++) { - NSString *uid; - - uid = [oldUIDs objectAtIndex:i]; - if ([newUIDs containsObject:uid]) - [storeUIDs addObject:uid]; /* old ID is still available */ - else - [removedUIDs addObject:uid]; /* old ID is not available anymore */ + changes = [iCalEventChanges changesFromEvent:[oldApt event] + toEvent:[newApt event]]; + + 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]; + + /* preserve organizer */ + + organizer = [[newApt event] organizer]; + uid = [um getUIDForICalPerson:organizer]; + if (uid) { + if (![storeUIDs containsObject:uid]) + [storeUIDs addObject:uid]; + [removedUIDs removeObject:uid]; } - count = [newUIDs count]; - for (i = 0; i < count; i++) { - NSString *uid; - - uid = [newUIDs objectAtIndex:i]; - if ([storeUIDs containsObject:uid]) /* old ID is still available */ - continue; - if ([removedUIDs containsObject:uid]) /* old ID is not available anymore */ - continue; + + /* organizer might have changed completely */ + + if ((oldApt != nil) && ([props containsObject:@"organizer"])) { + uid = [um getUIDForICalPerson:[[oldApt event] 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; - /* new ID which is not part of the old set => store a new */ - [storeUIDs addObject:uid]; + 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 iCalString]; + didChangeAppointmentTime = YES; } - - [self debugWithFormat: - @"UID ops:\n new: %@\n old: %@\n store: %@\n remove: %@", - newUIDs, oldUIDs, storeUIDs, removedUIDs]; - - /* perform */ - + + /* 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 */ + + attendees = [NSMutableArray arrayWithArray:[changes insertedAttendees]]; + [attendees removePerson:organizer]; + [self sendInvitationEMailForAppointment:newApt + toAttendees:attendees]; + + if (didChangeAppointmentTime) { + attendees = [NSMutableArray arrayWithArray:[[newApt event] attendees]]; + [attendees removeObjectsInArray:[changes insertedAttendees]]; + [attendees removePerson:organizer]; + [self sendAppointmentUpdateEMailForOldAppointment:oldApt + newAppointment:newApt + toAttendees:attendees]; + } + + attendees = [NSMutableArray arrayWithArray:[changes deletedAttendees]]; + [attendees removePerson:organizer]; + [self sendRemovalEMailForAppointment:newApt + toAttendees:attendees]; + return nil; } @@ -379,4 +464,204 @@ static NGLogger *logger = nil; return @"IPM.Appointment"; } +/* EMail Notifications */ + +- (NSString *)homePageURLForPerson:(iCalPerson *)_person { + static AgenorUserManager *um = nil; + static NSString *baseURL = nil; + NSString *uid; + + if (!um) { + um = [[AgenorUserManager sharedUserManager] retain]; + baseURL = @"http://agenor.opengroupware.org/foo/so"; + } + uid = [um getUIDForEmail:[_person rfc822Email]]; + if (!uid) return nil; + return [NSString stringWithFormat:@"%@/%@", baseURL, uid]; +} + +- (NSTimeZone *)viewTimeZoneForPerson:(iCalPerson *)_person { + /* TODO: get this from user config as soon as this is available and only + * fall back to default timeZone if config data is not available + */ + return MET; +} + + +- (void)sendEMailUsingTemplateNamed:(NSString *)_pageName + forOldAppointment:(SOGoAppointment *)_oldApt + andNewAppointment:(SOGoAppointment *)_newApt + toAttendees:(NSArray *)_attendees +{ + NSString *pageName; + iCalPerson *organizer; + NSString *cn, *sender, *iCalString; + NGSendMail *sendmail; + WOApplication *app; + unsigned i, count; + + if (![_attendees count]) return; // another job neatly done :-) + + /* sender */ + + organizer = [_newApt organizer]; + cn = [organizer cnWithoutQuotes]; + if (cn) { + sender = [NSString stringWithFormat:@"%@ <%@>", + cn, + [organizer rfc822Email]]; + } + else { + sender = [organizer rfc822Email]; + } + + /* generate iCalString once */ + iCalString = [_newApt iCalString]; + + /* get sendmail object */ + sendmail = [NGSendMail sharedSendMail]; + + /* get WOApplication instance */ + app = [WOApplication application]; + + /* create page name */ + pageName = [NSString stringWithFormat:@"SOGoAptMail%@", _pageName]; + + /* generate dynamic message content */ + + count = [_attendees count]; + for (i = 0; i < count; i++) { + iCalPerson *attendee; + NSString *recipient; + SOGoAptMailNotification *p; + NSString *subject, *text; + NGMutableHashMap *headerMap; + NGMimeMessage *msg; + NGMimeBodyPart *bodyPart; + NGMimeMultipartBody *body; + + attendee = [_attendees objectAtIndex:i]; + + /* construct recipient */ +#if 1 + cn = [attendee cn]; + if (cn) { + recipient = [NSString stringWithFormat:@"%@ <%@>", + cn, + [attendee rfc822Email]]; + } + else { + recipient = [attendee rfc822Email]; + } +#else + recipient = @"Marcus Mueller "; +#endif + + /* construct message content */ + p = [app pageWithName:pageName]; + [p setNewApt:_newApt]; + [p setOldApt:_oldApt]; + [p setHomePageURL:[self homePageURLForPerson:attendee]]; + [p setViewTZ:[self viewTimeZoneForPerson:attendee]]; + subject = [p getSubject]; + text = [p getBody]; + + /* 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"]; + [headerMap setObject:[NSCalendarDate date] forKey:@"date"]; + [headerMap setObject:subject forKey:@"Subject"]; + msg = [NGMimeMessage messageWithHeader:headerMap]; + + /* multipart body */ + body = [[NGMimeMultipartBody alloc] initWithPart:msg]; + + /* text part */ + headerMap = [NGMutableHashMap hashMapWithCapacity:1]; + [headerMap setObject:@"text/plain" forKey:@"content-type"]; + bodyPart = [NGMimeBodyPart bodyPartWithHeader:headerMap]; + [bodyPart setBody:text]; + + /* attach text part to multipart body */ + [body addBodyPart:bodyPart]; + + /* calendar part */ + headerMap = [NGMutableHashMap hashMapWithCapacity:1]; + [headerMap setObject:@"text/calendar" forKey:@"content-type"]; + bodyPart = [NGMimeBodyPart bodyPartWithHeader:headerMap]; + [bodyPart setBody:iCalString]; + + /* attach calendar part to multipart body */ + [body addBodyPart:bodyPart]; + + /* attach multipart body to message */ + [msg setBody:body]; + [body release]; + + /* send the damn thing */ +#if 1 + [sendmail sendMimePart:msg + toRecipients:[NSArray arrayWithObject:[attendee rfc822Email]] + sender:[organizer rfc822Email]]; +#else + [sendmail sendMimePart:msg + toRecipients:[NSArray arrayWithObject:@"mm@skyrix.com"] + sender:[organizer rfc822Email]]; +#endif + } +} + +- (void)sendInvitationEMailForAppointment:(SOGoAppointment *)_apt + toAttendees:(NSArray *)_attendees +{ + if (![_attendees count]) return; // another job neatly done :-) + + [self sendEMailUsingTemplateNamed:@"Invitation" + forOldAppointment:nil + andNewAppointment:_apt + toAttendees:_attendees]; +} + +- (void)sendAppointmentUpdateEMailForOldAppointment:(SOGoAppointment *)_oldApt + newAppointment:(SOGoAppointment *)_newApt + toAttendees:(NSArray *)_attendees +{ + if (![_attendees count]) return; + + [self sendEMailUsingTemplateNamed:@"Update" + forOldAppointment:_oldApt + andNewAppointment:_newApt + toAttendees:_attendees]; +} + +- (void)sendRemovalEMailForAppointment:(SOGoAppointment *)_apt + toAttendees:(NSArray *)_attendees +{ + if (![_attendees count]) return; +} + @end /* SOGoAppointmentObject */ + +@implementation NSMutableArray (iCalPersonConvenience) + +- (void)removePerson:(iCalPerson *)_person { + int i; + + for (i = [self count] - 1; i >= 0; i--) { + iCalPerson *p; + + p = [self objectAtIndex:i]; + if ([p hasSameEmailAddress:_person]) + [self removeObjectAtIndex:i]; + } +} + +@end /* NSMutableArray (iCalPersonConvenience) */ diff --git a/SOGo/SoObjects/Appointments/SOGoAptMailInvitation.m b/SOGo/SoObjects/Appointments/SOGoAptMailInvitation.m new file mode 100644 index 00000000..4a0b6718 --- /dev/null +++ b/SOGo/SoObjects/Appointments/SOGoAptMailInvitation.m @@ -0,0 +1,33 @@ +/* + Copyright (C) 2000-2005 SKYRIX Software AG + + This file is part of OpenGroupware.org. + + OGo is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + OGo is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with OGo; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +#include "SOGoAptMailNotification.h" + +@interface SOGoAptMailInvitation : SOGoAptMailNotification +{ + +} + +@end + +@implementation SOGoAptMailInvitation + +@end diff --git a/SOGo/SoObjects/Appointments/SOGoAptMailInvitation.wo/SOGoAptMailInvitation.html b/SOGo/SoObjects/Appointments/SOGoAptMailInvitation.wo/SOGoAptMailInvitation.html new file mode 100644 index 00000000..edb87f43 --- /dev/null +++ b/SOGo/SoObjects/Appointments/SOGoAptMailInvitation.wo/SOGoAptMailInvitation.html @@ -0,0 +1,7 @@ +<#IsSubject>Apt le <#AptStartDate /> at <#AptStartTime /> +<#IsBody> +You're invited by <#Organizer /> to a meeting. +<#HasHomePageURL> +Please make a decision for this invitation at <#HomePageURL />. + + \ No newline at end of file diff --git a/SOGo/SoObjects/Appointments/SOGoAptMailInvitation.wo/SOGoAptMailInvitation.wod b/SOGo/SoObjects/Appointments/SOGoAptMailInvitation.wo/SOGoAptMailInvitation.wod new file mode 100644 index 00000000..69c6c1c7 --- /dev/null +++ b/SOGo/SoObjects/Appointments/SOGoAptMailInvitation.wo/SOGoAptMailInvitation.wod @@ -0,0 +1,34 @@ +AptStartDate: WOString { + value = newStartDate; + dateformat = "%d/%m/%y"; + escapeHTML = NO; +} + +AptStartTime: WOString { + value = newStartDate; + dateformat = "%H:%M"; + escapeHTML = NO; +} + +Organizer: WOString { + value = newApt.organizer.cnWithoutQuotes; + escapeHTML = NO; +} + +HasHomePageURL: WOConditional { + condition = homePageURL.length; +} + +HomePageURL: WOString { + value = homePageURL; + escapeHTML = NO; +} + +IsSubject: WOConditional { + condition = isSubject; +} + +IsBody: WOConditional { + condition = isSubject; + negate = YES; +} diff --git a/SOGo/SoObjects/Appointments/SOGoAptMailNotification.h b/SOGo/SoObjects/Appointments/SOGoAptMailNotification.h new file mode 100644 index 00000000..4829e0ae --- /dev/null +++ b/SOGo/SoObjects/Appointments/SOGoAptMailNotification.h @@ -0,0 +1,64 @@ +/* + Copyright (C) 2000-2005 SKYRIX Software AG + + This file is part of OpenGroupware.org. + + OGo is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + OGo is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with OGo; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +#ifndef __Appointments_SOGoAptMailNotification_H_ +#define __Appointments_SOGoAptMailNotification_H_ + +#include + +@class NSString; + +@interface SOGoAptMailNotification : WOComponent +{ + id oldApt; + id newApt; + NSString *homePageURL; + NSTimeZone *viewTZ; + NSCalendarDate *oldStartDate; + NSCalendarDate *newStartDate; + BOOL isSubject; +} + +- (id)oldApt; +- (void)setOldApt:(id)_oldApt; + +- (id)newApt; +- (void)setNewApt:(id)_newApt; + +- (NSString *)homePageURL; +- (void)setHomePageURL:(NSString *)_homePageURL; + +- (NSTimeZone *)viewTZ; +- (void)setViewTZ:(NSTimeZone *)_viewTZ; + +/* Helpers */ + +- (NSCalendarDate *)oldStartDate; +- (NSCalendarDate *)newStartDate; + +/* Content Generation */ + +- (NSString *)getSubject; +- (NSString *)getBody; + +@end + +#endif /* __Appointments_SOGoAptMailNotification_H_ */ diff --git a/SOGo/SoObjects/Appointments/SOGoAptMailNotification.m b/SOGo/SoObjects/Appointments/SOGoAptMailNotification.m new file mode 100644 index 00000000..a7a11f2b --- /dev/null +++ b/SOGo/SoObjects/Appointments/SOGoAptMailNotification.m @@ -0,0 +1,125 @@ +/* + Copyright (C) 2000-2005 SKYRIX Software AG + + This file is part of OpenGroupware.org. + + OGo is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + OGo is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with OGo; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +#include "SOGoAptMailNotification.h" +#include +#include "common.h" + +@interface SOGoAptMailNotification (PrivateAPI) +- (BOOL)isSubject; +- (void)setIsSubject:(BOOL)_isSubject; +@end + +@implementation SOGoAptMailNotification + +static NSCharacterSet *wsSet = nil; +static NSTimeZone *MET = nil; + ++ (void)initialize { + static BOOL didInit = NO; + + if (didInit) return; + didInit = YES; + + wsSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] retain]; + MET = [[NSTimeZone timeZoneWithAbbreviation:@"MET"] retain]; +} + +- (void)dealloc { + [self->oldApt release]; + [self->newApt release]; + [self->homePageURL release]; + [self->viewTZ release]; + + [self->oldStartDate release]; + [self->newStartDate release]; + [super dealloc]; +} + +- (id)oldApt { + return self->oldApt; +} +- (void)setOldApt:(id)_oldApt { + ASSIGN(self->oldApt, _oldApt); +} + +- (id)newApt { + return self->newApt; +} +- (void)setNewApt:(id)_newApt { + ASSIGN(self->newApt, _newApt); +} + +- (NSString *)homePageURL { + return self->homePageURL; +} +- (void)setHomePageURL:(NSString *)_homePageURL { + ASSIGN(self->homePageURL, _homePageURL); +} + +- (NSTimeZone *)viewTZ { + if (self->viewTZ) return self->viewTZ; + return MET; +} +- (void)setViewTZ:(NSTimeZone *)_viewTZ { + ASSIGN(self->viewTZ, _viewTZ); +} + +- (BOOL)isSubject { + return self->isSubject; +} +- (void)setIsSubject:(BOOL)_isSubject { + self->isSubject = _isSubject; +} + + +/* Helpers */ + +- (NSCalendarDate *)oldStartDate { + if (!self->oldStartDate) { + ASSIGN(self->oldStartDate, [[self oldApt] startDate]); + [self->oldStartDate setTimeZone:[self viewTZ]]; + } + return self->oldStartDate; +} + +- (NSCalendarDate *)newStartDate { + if (!self->newStartDate) { + ASSIGN(self->newStartDate, [[self newApt] startDate]); + [self->newStartDate setTimeZone:[self viewTZ]]; + } + return self->newStartDate; +} + +/* Generate Response */ + +- (NSString *)getSubject { + [self setIsSubject:YES]; + return [[[self generateResponse] contentAsString] + stringByTrimmingCharactersInSet:wsSet]; +} + +- (NSString *)getBody { + [self setIsSubject:NO]; + return [[self generateResponse] contentAsString]; +} + +@end diff --git a/SOGo/SoObjects/Appointments/SOGoAptMailUpdate.m b/SOGo/SoObjects/Appointments/SOGoAptMailUpdate.m new file mode 100644 index 00000000..fcbfaac2 --- /dev/null +++ b/SOGo/SoObjects/Appointments/SOGoAptMailUpdate.m @@ -0,0 +1,33 @@ +/* + Copyright (C) 2000-2005 SKYRIX Software AG + + This file is part of OpenGroupware.org. + + OGo is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + OGo is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with OGo; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +#include "SOGoAptMailNotification.h" + +@interface SOGoAptMailUpdate : SOGoAptMailNotification +{ + +} + +@end + +@implementation SOGoAptMailUpdate + +@end diff --git a/SOGo/SoObjects/Appointments/SOGoAptMailUpdate.wo/SOGoAptMailUpdate.html b/SOGo/SoObjects/Appointments/SOGoAptMailUpdate.wo/SOGoAptMailUpdate.html new file mode 100644 index 00000000..8a8544cf --- /dev/null +++ b/SOGo/SoObjects/Appointments/SOGoAptMailUpdate.wo/SOGoAptMailUpdate.html @@ -0,0 +1,8 @@ +<#IsSubject>Apt for the <#OldAptStartDate /> at <#OldAptStartTime /> changed +<#IsBody> +This appointment, previously set for <#OldAptStartDate /> +at <#OldAptStartTime /> is now scheduled for <#NewAptStartDate /> at <#NewAptStartTime /> +<#HasHomePageURL> +Please make a decision for these new settings at <#HomePageURL />. + + \ No newline at end of file diff --git a/SOGo/SoObjects/Appointments/SOGoAptMailUpdate.wo/SOGoAptMailUpdate.wod b/SOGo/SoObjects/Appointments/SOGoAptMailUpdate.wo/SOGoAptMailUpdate.wod new file mode 100644 index 00000000..4ca5da74 --- /dev/null +++ b/SOGo/SoObjects/Appointments/SOGoAptMailUpdate.wo/SOGoAptMailUpdate.wod @@ -0,0 +1,46 @@ +OldAptStartDate: WOString { + value = oldStartDate; + dateformat = "%d/%m/%y"; + escapeHTML = NO; +} + +OldAptStartTime: WOString { + value = oldStartDate; + dateformat = "%H:%M"; + escapeHTML = NO; +} + +NewAptStartDate: WOString { + value = newStartDate; + dateformat = "%d/%m/%y"; + escapeHTML = NO; +} + +NewAptStartTime: WOString { + value = newStartDate; + dateformat = "%H:%M"; + escapeHTML = NO; +} + +Organizer: WOString { + value = newApt.organizer.cnWithoutQuotes; + escapeHTML = NO; +} + +HasHomePageURL: WOConditional { + condition = homePageURL.length; +} + +HomePageURL: WOString { + value = homePageURL; + escapeHTML = NO; +} + +IsSubject: WOConditional { + condition = isSubject; +} + +IsBody: WOConditional { + condition = isSubject; + negate = YES; +} diff --git a/SOGo/SoObjects/Appointments/Version b/SOGo/SoObjects/Appointments/Version index b1ee3cc9..7300dc6e 100644 --- a/SOGo/SoObjects/Appointments/Version +++ b/SOGo/SoObjects/Appointments/Version @@ -1,6 +1,6 @@ # Version file -SUBMINOR_VERSION:=36 +SUBMINOR_VERSION:=37 # v0.9.32 requires libGDLContentStore v4.5.26 # v0.9.28 requires libNGiCal v4.5.47 diff --git a/SOGo/SoObjects/SOGo/AgenorUserManager.h b/SOGo/SoObjects/SOGo/AgenorUserManager.h index aa6a3aac..421ee1f2 100644 --- a/SOGo/SoObjects/SOGo/AgenorUserManager.h +++ b/SOGo/SoObjects/SOGo/AgenorUserManager.h @@ -32,6 +32,7 @@ @class NSString, NSArray, NSURL, NSUserDefaults; @class SOGoLRUCache; +@class iCalPerson; @interface AgenorUserManager : NSObject { @@ -48,6 +49,11 @@ - (NSString *)getUIDForEmail:(NSString *)_email; - (NSString *)getEmailForUID:(NSString *)_uid; +- (NSString *)getUIDForICalPerson:(iCalPerson *)_person; +/* may insert NSNulls into returned array if _mapStrictly -> YES */ +- (NSArray *)getUIDsForICalPersons:(NSArray *)_persons + applyStrictMapping:(BOOL)_mapStrictly; + /* i.e. BUTTO Hercule, CETE Lyon/DI/ET/TEST */ - (NSString *)getCNForUID:(NSString *)_uid; diff --git a/SOGo/SoObjects/SOGo/AgenorUserManager.m b/SOGo/SoObjects/SOGo/AgenorUserManager.m index 4bff3b52..082f9660 100644 --- a/SOGo/SoObjects/SOGo/AgenorUserManager.m +++ b/SOGo/SoObjects/SOGo/AgenorUserManager.m @@ -23,6 +23,7 @@ #include "AgenorUserDefaults.h" #include #include +#include #include "SOGoLRUCache.h" @interface AgenorUserManager (PrivateAPI) @@ -47,6 +48,7 @@ static BOOL debugOn = NO; static BOOL useLDAP = NO; static NSString *ldapHost = nil; static NSString *ldapBaseDN = nil; +static NSNull *sharedNull = nil; static NSString *fallbackIMAP4Server = nil; static NSString *defaultMailDomain = @"equipement.gouv.fr"; static NSString *shareLDAPClass = @"mineqMelBoite"; @@ -85,6 +87,8 @@ static NSArray *fromEMailAttrs = nil; fromEMailAttrs = [[NSArray alloc] initWithObjects:mailEmissionAttrName, nil]; + sharedNull = [[NSNull null] retain]; + /* profile database URL */ if ((tmp = [ud stringForKey:@"AgenorProfileURL"]) == nil) @@ -267,6 +271,35 @@ static NSArray *fromEMailAttrs = nil; return uid; } +- (NSString *)getUIDForICalPerson:(iCalPerson *)_person { + return [self getUIDForEmail:[_person rfc822Email]]; +} + + /* may insert NSNulls into returned array */ +- (NSArray *)getUIDsForICalPersons:(NSArray *)_persons + applyStrictMapping:(BOOL)_mapStrictly +{ + NSMutableArray *ma; + unsigned i, count; + + count = [_persons count]; + ma = [[[NSMutableArray alloc] initWithCapacity:count] autorelease]; + for (i = 0; i < count; i++) { + iCalPerson *p; + id uid; + + p = [_persons objectAtIndex:i]; + uid = [self getUIDForICalPerson:p]; + if (uid) { + [ma addObject:uid]; + } + else if (!uid && _mapStrictly) { + [ma addObject:sharedNull]; + } + } + return ma; +} + - (NSString *)primaryGetEmailForAgenorUID:(NSString *)_uid { NGLdapConnection *conn; EOQualifier *q; diff --git a/SOGo/SoObjects/SOGo/ChangeLog b/SOGo/SoObjects/SOGo/ChangeLog index 89ee183e..453d72d3 100644 --- a/SOGo/SoObjects/SOGo/ChangeLog +++ b/SOGo/SoObjects/SOGo/ChangeLog @@ -1,3 +1,8 @@ +2005-07-12 Marcus Mueller + + * AgenorUserManager.[hm]: new API for extracting UIDs from iCalPersons + (v0.9.46) + 2005-07-12 Helge Hess * v0.9.45 @@ -28,6 +33,7 @@ * added AgenorUserDefaults class (incomplete) as a wrapper for the profile data of Agenor users +>>>>>>> .r719 2005-07-08 Helge Hess * v0.9.42 diff --git a/SOGo/SoObjects/SOGo/Version b/SOGo/SoObjects/SOGo/Version index 2bea963b..09850cc9 100644 --- a/SOGo/SoObjects/SOGo/Version +++ b/SOGo/SoObjects/SOGo/Version @@ -1,6 +1,6 @@ # version file -SUBMINOR_VERSION:=45 +SUBMINOR_VERSION:=46 # v0.9.34 requires libGDLContentStore v4.5.26 # v0.9.26 requires libOGoContentStore v0.9.13 diff --git a/SOGo/UI/Scheduler/ChangeLog b/SOGo/UI/Scheduler/ChangeLog index 71a3dc46..1d86f5a5 100644 --- a/SOGo/UI/Scheduler/ChangeLog +++ b/SOGo/UI/Scheduler/ChangeLog @@ -1,3 +1,8 @@ +2005-07-11 Marcus Mueller + + * UIxCalView.h: added -setAppointments: to the public API + (for subclassers) (v0.9.128) + 2005-07-08 Marcus Mueller * v0.9.127 diff --git a/SOGo/UI/Scheduler/UIxCalView.h b/SOGo/UI/Scheduler/UIxCalView.h index cb3486e1..f0b1a316 100644 --- a/SOGo/UI/Scheduler/UIxCalView.h +++ b/SOGo/UI/Scheduler/UIxCalView.h @@ -40,6 +40,8 @@ /* accessors */ - (NSArray *)appointments; +- (void)setAppointments:(NSArray *)_apts; + - (NSArray *)allDayApts; - (id)appointment; - (BOOL)isMyApt; diff --git a/SOGo/UI/Scheduler/Version b/SOGo/UI/Scheduler/Version index 762af823..b5a7494a 100644 --- a/SOGo/UI/Scheduler/Version +++ b/SOGo/UI/Scheduler/Version @@ -1,6 +1,6 @@ # Version file -SUBMINOR_VERSION:=127 +SUBMINOR_VERSION:=128 # v0.9.123 requires Appointments v0.9.35 # v0.9.123 requires SOGoUI v0.9.24 diff --git a/SOGo/UI/Templates/ChangeLog b/SOGo/UI/Templates/ChangeLog index 6c75b528..79013140 100644 --- a/SOGo/UI/Templates/ChangeLog +++ b/SOGo/UI/Templates/ChangeLog @@ -1,3 +1,8 @@ +2005-07-12 Marcus Mueller + + * UIxAppointmentEditor.wox: moved the 'checkForConflicts' button near + the 'save' button as requested + 2005-07-08 Marcus Mueller * UIxCalDayOverview.wox, UIxCalWeekOverview.wox, diff --git a/SOGo/UI/Templates/UIxAppointmentEditor.wox b/SOGo/UI/Templates/UIxAppointmentEditor.wox index a99719bc..ee4d18b3 100644 --- a/SOGo/UI/Templates/UIxAppointmentEditor.wox +++ b/SOGo/UI/Templates/UIxAppointmentEditor.wox @@ -200,21 +200,6 @@ - - - - : - - - - - - - - @@ -315,6 +300,16 @@ + + + + + + + -- 2.39.5