Monotone-Parent: 463195ab0268a4a769eab22f23b6aecf0c87ad79
Monotone-Revision: 9abbb51cbabcad645190865841814453369fa85f Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2007-11-18T10:16:25 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
e3cdb8ecbf
commit
73bfada6bf
104
ChangeLog
104
ChangeLog
|
@ -1,5 +1,109 @@
|
|||
2007-11-18 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/Scheduler/UIxTaskEditor.m ([UIxTaskEditor -saveAction]):
|
||||
invoke saveComponent:.
|
||||
|
||||
* UI/Scheduler/UIxComponentEditor.m ([UIxComponentEditor -hasOrganizer])
|
||||
([UIxComponentEditor -organizerName]): new template accessor
|
||||
related to displaying the event's organizer.
|
||||
([-containsConflict:_component]): removed method.
|
||||
([UIxComponentEditor -takeValuesFromRequest:_rqinContext:_ctx]):
|
||||
set RSVP to "TRUE" on each attendee.
|
||||
|
||||
* UI/Scheduler/UIxAppointmentEditor.m ([UIxAppointmentEditor
|
||||
-saveAction]): invoke saveComponent:.
|
||||
|
||||
* UI/MailPartViewers/UIxMailPartICalViewer.m
|
||||
([UIxMailPartICalViewer -isLoggedInUserTheOrganizer]): make use of
|
||||
the -userIsOrganizer: category method.
|
||||
([-isLoggedInUserAnAttendee]): make use of -userIsParticipant:.
|
||||
([UIxMailPartICalViewer -hasSenderStatusChanged]): new template
|
||||
accessor that determines whether the "Update" button should be
|
||||
displayed.
|
||||
|
||||
* UI/MailPartViewers/UIxMailPartICalActions.m
|
||||
([UIxMailPartICalActions -deleteFromCalendarAction]): implemented
|
||||
action.
|
||||
([UIxMailPartICalActions -updateUserStatusAction]): implemented
|
||||
action.
|
||||
|
||||
* UI/Common/UIxPageFrame.m ([UIxPageFrame
|
||||
-setCssFiles:newCSSFiles]): new accessor that enables the
|
||||
sub-templates to specify extra CSS files to load.
|
||||
|
||||
* SoObjects/SOGo/SOGoUser.m ([SOGoUser
|
||||
-homeFolderInContext:context]): cache the home folder of the user
|
||||
object instead of the current user.
|
||||
|
||||
* SoObjects/SOGo/SOGoGCSFolder.m ([SOGoGCSFolder
|
||||
-deleteEntriesWithIds:ids]): invokes the "prepareDelete" optional
|
||||
method if the child object implements it.
|
||||
|
||||
* SoObjects/SOGo/SOGoContentObject.m ([-setContentString:])
|
||||
removed method.
|
||||
|
||||
* SoObjects/SOGo/LDAPSource.m ([LDAPSource
|
||||
-setBaseDN:newBaseDNIDField:newIDFieldCNField:newCNFieldUIDField:newUIDFieldmailFields:newMailFieldsandBindFields:newBindFields]):
|
||||
take a new "mailFields" parameter defining an array of fields
|
||||
where to look at when searching the user's emails. It defaults to
|
||||
the standard "mail" LDAP field.
|
||||
|
||||
* SoObjects/Appointments/SOGoAptMailICalReply.[hm]: new
|
||||
SoComponent implementing a template for ITIP replies.
|
||||
|
||||
* SoObjects/Appointments/iCalPerson+SOGo.m ([iCalPerson
|
||||
-mailAddress]): new method that returns a properly formatted email
|
||||
address for the specified person entry.
|
||||
([iCalPerson -uid]): new method that tests whether the user is
|
||||
known to the system and if so, returns its user id.
|
||||
|
||||
* SoObjects/Appointments/iCalPerson+SOGo.[hm]: new category module.
|
||||
|
||||
* SoObjects/Appointments/iCalEventChanges+SOGo.m
|
||||
([iCalEventChanges -sequenceShouldBeIncreased]): determine whether
|
||||
the changes involved need a sequence inscrease, based on the
|
||||
RFC2446 (ITIP).
|
||||
|
||||
* SoObjects/Appointments/iCalEventChanges+SOGo.[hm]: new category
|
||||
module.
|
||||
|
||||
* SoObjects/Appointments/iCalEvent+SOGo.m ([iCalEvent
|
||||
-isStillRelevant]): new overriden method determining the relevance
|
||||
of the current event based on its end date.
|
||||
|
||||
* SoObjects/Appointments/iCalEvent+SOGo.[hm]: new category module.
|
||||
|
||||
* SoObjects/Appointments/iCalEntityObject+SOGo.m
|
||||
([iCalEntityObject -attendeeUIDs]): new category methods that
|
||||
returns an array containing the uids of the system-know attendees.
|
||||
([iCalEntityObject -isStillRelevant]): new template method.
|
||||
([iCalEntityObject -itipEntryWithMethod:method]): clone the
|
||||
current entry calendar with the specified ITIP method.
|
||||
([iCalEntityObject -attendeesWithoutUser:user]): returns an array
|
||||
of attendees while making sure the specified user is not listed.
|
||||
|
||||
* SoObjects/Appointments/SOGoCalendarComponent.m
|
||||
([SOGoCalendarComponent -calendar:create:secure]): new name for
|
||||
-calendar:. Added a "secure" parameter that specifies whether a
|
||||
stripped calendar instance is needed or not. Also, we no longer
|
||||
cache the content to simplify handling of new data.
|
||||
([SOGoCalendarComponent -component:create:secure]): same as above.
|
||||
([SOGoCalendarComponent
|
||||
-sendEMailUsingTemplateNamed:_pageNameforOldObject:_oldObjectandNewObject:_newObjecttoAttendees:_attendees]):
|
||||
test whether the component is "still relevant" before sending an
|
||||
email...
|
||||
([SOGoCalendarComponent -sendResponseToOrganizer]): new method for
|
||||
sending ITIP replies.
|
||||
([SOGoCalendarComponent -getUIDsForICalPerson:iCalPerson]):
|
||||
removed method. Replaced with -[iCalPerson uid] category method.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentObject.[hm]: rewrote
|
||||
class. No longer override saveContentString:,
|
||||
saveContentString:baseSequence:, .... Implemented the
|
||||
saveComponent: and the prepareDelete methods instead. Those
|
||||
methods are called only from the web methods. This avoids the
|
||||
risks related to email sending and changes propagation.
|
||||
|
||||
* UI/Common/UIxTabItem.m: removed useless class module.
|
||||
|
||||
* UI/Common/UIxTabView.[hm]: removed useless class module.
|
||||
|
|
|
@ -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 *)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -11,16 +11,12 @@ CommonUI_LANGUAGES = English French German
|
|||
CommonUI_OBJC_FILES += \
|
||||
CommonUIProduct.m \
|
||||
UIxPageFrame.m \
|
||||
UIxPrintPageFrame.m \
|
||||
UIxAppNavView.m \
|
||||
\
|
||||
UIxAclEditor.m \
|
||||
UIxObjectActions.m \
|
||||
UIxFolderActions.m \
|
||||
UIxParentFolderActions.m \
|
||||
UIxElemBuilder.m \
|
||||
UIxTabView.m \
|
||||
UIxTabItem.m \
|
||||
UIxUserRightsEditor.m \
|
||||
\
|
||||
UIxToolbar.m \
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
NSString *toolbar;
|
||||
id item;
|
||||
BOOL isPopup;
|
||||
NSMutableArray *additionalCSSFiles;
|
||||
NSMutableArray *additionalJSFiles;
|
||||
}
|
||||
|
||||
|
|
|
@ -241,6 +241,29 @@
|
|||
return ([[self productJavaScriptURL] length] > 0);
|
||||
}
|
||||
|
||||
- (void) setCssFiles: (NSString *) newCSSFiles
|
||||
{
|
||||
NSEnumerator *cssFiles;
|
||||
NSString *currentFile, *filename;
|
||||
|
||||
[additionalCSSFiles release];
|
||||
additionalCSSFiles = [NSMutableArray new];
|
||||
|
||||
cssFiles
|
||||
= [[newCSSFiles componentsSeparatedByString: @","] objectEnumerator];
|
||||
while ((currentFile = [cssFiles nextObject]))
|
||||
{
|
||||
filename = [self urlForResourceFilename:
|
||||
[currentFile stringByTrimmingSpaces]];
|
||||
[additionalCSSFiles addObject: filename];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *) additionalCSSFiles
|
||||
{
|
||||
return additionalCSSFiles;
|
||||
}
|
||||
|
||||
- (void) setJsFiles: (NSString *) newJSFiles
|
||||
{
|
||||
NSEnumerator *jsFiles;
|
||||
|
|
|
@ -19,6 +19,7 @@ Attendees = "Attendees";
|
|||
request_info = "invites you to participate in a meeting.";
|
||||
"Add to calendar" = "Add to calendar";
|
||||
"Delete from calendar" = "Delete from calendar";
|
||||
"Update status" = "Update status";
|
||||
Accept = "Accept";
|
||||
Decline = "Decline";
|
||||
Tentative = "Tentative";
|
||||
|
|
|
@ -19,6 +19,7 @@ Attendees = "Invités";
|
|||
request_info = "vous invite à une réunion.";
|
||||
"Add to calendar" = "Ajouter à l'agenda";
|
||||
"Delete from calendar" = "Effacer de l'agenda";
|
||||
"Update status" = "Intégrer les modifications";
|
||||
Accept = "Accepter";
|
||||
Decline = "Decliner";
|
||||
Tentative = "Tentative";
|
||||
|
|
|
@ -19,6 +19,7 @@ Attendees = "Attendees";
|
|||
request_info = "invites you to participate in a meeting.";
|
||||
"Add to calendar" = "Add to calendar";
|
||||
"Delete from calendar" = "Delete from calendar";
|
||||
"Update status" = "Update status";
|
||||
Accept = "Accept";
|
||||
Decline = "Decline";
|
||||
Tentative = "Tentative";
|
||||
|
|
|
@ -26,12 +26,17 @@
|
|||
#import <NGObjWeb/WOResponse.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalEvent.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
|
||||
#import <UI/Common/WODirectAction+SOGo.h>
|
||||
|
||||
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
||||
|
||||
#import <SoObjects/Appointments/iCalPerson+SOGo.h>
|
||||
#import <SoObjects/Appointments/SOGoAppointmentObject.h>
|
||||
#import <SoObjects/Appointments/SOGoAppointmentFolder.h>
|
||||
#import <SoObjects/Mailer/SOGoMailObject.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
#import <SoObjects/SOGo/iCalEntityObject+Utilities.h>
|
||||
#import <SoObjects/Mailer/SOGoMailBodyPart.h>
|
||||
|
@ -58,12 +63,12 @@
|
|||
}
|
||||
|
||||
- (SOGoAppointmentObject *) _eventObjectWithUID: (NSString *) uid
|
||||
forUser: (SOGoUser *) user
|
||||
{
|
||||
SOGoAppointmentFolder *personalFolder;
|
||||
SOGoAppointmentObject *eventObject;
|
||||
|
||||
personalFolder
|
||||
= [[context activeUser] personalCalendarFolderInContext: context];
|
||||
personalFolder = [user personalCalendarFolderInContext: context];
|
||||
eventObject = [personalFolder lookupName: uid
|
||||
inContext: context acquire: NO];
|
||||
if (![eventObject isKindOfClass: [SOGoAppointmentObject class]])
|
||||
|
@ -73,6 +78,11 @@
|
|||
return eventObject;
|
||||
}
|
||||
|
||||
- (SOGoAppointmentObject *) _eventObjectWithUID: (NSString *) uid
|
||||
{
|
||||
return [self _eventObjectWithUID: uid forUser: [context activeUser]];
|
||||
}
|
||||
|
||||
- (iCalEvent *)
|
||||
_setupChosenEventAndEventObject: (SOGoAppointmentObject **) eventObject
|
||||
{
|
||||
|
@ -86,7 +96,7 @@
|
|||
chosenEvent = emailEvent;
|
||||
else
|
||||
{
|
||||
calendarEvent = (iCalEvent *) [*eventObject component: NO];
|
||||
calendarEvent = (iCalEvent *) [*eventObject component: NO secure: NO];
|
||||
if ([calendarEvent compare: emailEvent] == NSOrderedAscending)
|
||||
chosenEvent = emailEvent;
|
||||
else
|
||||
|
@ -99,14 +109,42 @@
|
|||
return chosenEvent;
|
||||
}
|
||||
|
||||
#warning this is code copied from SOGoAppointmentObject...
|
||||
- (void) _updateAttendee: (iCalPerson *) attendee
|
||||
withSequence: (NSNumber *) sequence
|
||||
andCalUID: (NSString *) calUID
|
||||
forUID: (NSString *) uid
|
||||
{
|
||||
SOGoAppointmentObject *eventObject;
|
||||
iCalEvent *event;
|
||||
iCalPerson *otherAttendee;
|
||||
NSString *iCalString;
|
||||
|
||||
eventObject = [self _eventObjectWithUID: calUID
|
||||
forUser: [SOGoUser userWithLogin: uid roles: nil]];
|
||||
if (![eventObject isNew])
|
||||
{
|
||||
event = [eventObject component: NO secure: NO];
|
||||
if ([[event sequence] compare: sequence]
|
||||
== NSOrderedSame)
|
||||
{
|
||||
otherAttendee
|
||||
= [event findParticipantWithEmail: [attendee rfc822Email]];
|
||||
[otherAttendee setPartStat: [attendee partStat]];
|
||||
iCalString = [[event parent] versitString];
|
||||
[eventObject saveContentString: iCalString];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (WOResponse *) _changePartStatusAction: (NSString *) newStatus
|
||||
{
|
||||
WOResponse *response;
|
||||
SOGoAppointmentObject *eventObject;
|
||||
iCalEvent *chosenEvent;
|
||||
iCalPerson *user;
|
||||
iCalCalendar *calendar;
|
||||
NSString *rsvp, *method;
|
||||
iCalCalendar *emailCalendar, *calendar;
|
||||
NSString *rsvp, *method, *organizerUID;
|
||||
|
||||
chosenEvent = [self _setupChosenEventAndEventObject: &eventObject];
|
||||
if (chosenEvent)
|
||||
|
@ -114,7 +152,8 @@
|
|||
user = [chosenEvent findParticipant: [context activeUser]];
|
||||
[user setPartStat: newStatus];
|
||||
calendar = [chosenEvent parent];
|
||||
method = [[calendar method] lowercaseString];
|
||||
emailCalendar = [[self _emailEvent] parent];
|
||||
method = [[emailCalendar method] lowercaseString];
|
||||
if ([method isEqualToString: @"request"])
|
||||
{
|
||||
[calendar setMethod: @""];
|
||||
|
@ -123,8 +162,12 @@
|
|||
else
|
||||
rsvp = nil;
|
||||
[eventObject saveContentString: [calendar versitString]];
|
||||
if (rsvp && [rsvp isEqualToString: @"true"])
|
||||
if ([rsvp isEqualToString: @"true"])
|
||||
[eventObject sendResponseToOrganizer];
|
||||
organizerUID = [[chosenEvent organizer] uid];
|
||||
if (organizerUID)
|
||||
[self _updateAttendee: user withSequence: [chosenEvent sequence]
|
||||
andCalUID: [chosenEvent uid] forUID: organizerUID];
|
||||
response = [self responseWith204];
|
||||
}
|
||||
else
|
||||
|
@ -146,6 +189,97 @@
|
|||
return [self _changePartStatusAction: @"DECLINED"];
|
||||
}
|
||||
|
||||
- (WOResponse *) deleteFromCalendarAction
|
||||
{
|
||||
iCalEvent *emailEvent;
|
||||
SOGoAppointmentObject *eventObject;
|
||||
WOResponse *response;
|
||||
|
||||
emailEvent = [self _emailEvent];
|
||||
if (emailEvent)
|
||||
{
|
||||
eventObject = [self _eventObjectWithUID: [emailEvent uid]];
|
||||
response = [self responseWith204];
|
||||
}
|
||||
else
|
||||
{
|
||||
response = [context response];
|
||||
[response setStatus: 409];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (iCalPerson *) _emailParticipantWithEvent: (iCalEvent *) event
|
||||
{
|
||||
NSString *emailFrom;
|
||||
SOGoMailObject *mailObject;
|
||||
NGImap4EnvelopeAddress *address;
|
||||
|
||||
mailObject = [[self clientObject] mailObject];
|
||||
address = [[mailObject fromEnvelopeAddresses] objectAtIndex: 0];
|
||||
emailFrom = [address baseEMail];
|
||||
|
||||
return [event findParticipantWithEmail: emailFrom];
|
||||
}
|
||||
|
||||
- (BOOL) _updateParticipantStatusInEvent: (iCalEvent *) calendarEvent
|
||||
fromEvent: (iCalEvent *) emailEvent
|
||||
inObject: (SOGoAppointmentObject *) eventObject
|
||||
{
|
||||
iCalPerson *calendarParticipant, *mailParticipant;
|
||||
NSString *partStat;
|
||||
BOOL result;
|
||||
|
||||
calendarParticipant = [self _emailParticipantWithEvent: calendarEvent];
|
||||
mailParticipant = [self _emailParticipantWithEvent: emailEvent];
|
||||
if (calendarParticipant && mailParticipant)
|
||||
{
|
||||
result = YES;
|
||||
partStat = [mailParticipant partStat];
|
||||
if ([partStat caseInsensitiveCompare: [calendarParticipant partStat]]
|
||||
!= NSOrderedSame)
|
||||
{
|
||||
[calendarParticipant setPartStat: [partStat uppercaseString]];
|
||||
[eventObject saveComponent: calendarEvent];
|
||||
}
|
||||
}
|
||||
else
|
||||
result = NO;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (WOResponse *) updateUserStatusAction
|
||||
{
|
||||
iCalEvent *emailEvent, *calendarEvent;
|
||||
SOGoAppointmentObject *eventObject;
|
||||
WOResponse *response;
|
||||
|
||||
response = nil;
|
||||
|
||||
emailEvent = [self _emailEvent];
|
||||
if (emailEvent)
|
||||
{
|
||||
eventObject = [self _eventObjectWithUID: [emailEvent uid]];
|
||||
calendarEvent = [eventObject component: NO secure: NO];
|
||||
if (([[emailEvent sequence] compare: [calendarEvent sequence]]
|
||||
!= NSOrderedDescending)
|
||||
&& ([self _updateParticipantStatusInEvent: calendarEvent
|
||||
fromEvent: emailEvent
|
||||
inObject: eventObject]))
|
||||
response = [self responseWith204];
|
||||
}
|
||||
|
||||
if (!response)
|
||||
{
|
||||
response = [context response];
|
||||
[response setStatus: 409];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// - (WOResponse *) markTentativeAction
|
||||
// {
|
||||
// return [self _changePartStatusAction: @"TENTATIVE"];
|
||||
|
|
|
@ -24,10 +24,12 @@
|
|||
|
||||
#import "UIxMailPartViewer.h"
|
||||
|
||||
@class SOGoDateFormatter;
|
||||
@class iCalEvent;
|
||||
@class iCalCalendar;
|
||||
|
||||
@class SOGoAppointmentObject;
|
||||
@class SOGoDateFormatter;
|
||||
|
||||
@interface UIxMailPartICalViewer : UIxMailPartViewer
|
||||
{
|
||||
iCalCalendar *inCalendar;
|
||||
|
@ -35,7 +37,7 @@
|
|||
id attendee;
|
||||
SOGoDateFormatter *dateFormatter;
|
||||
id item;
|
||||
id storedEventObject;
|
||||
SOGoAppointmentObject *storedEventObject;
|
||||
iCalEvent *storedEvent;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#import <NGObjWeb/WOResponse.h>
|
||||
|
||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
||||
|
@ -40,9 +39,11 @@
|
|||
|
||||
#import <SoObjects/SOGo/SOGoDateFormatter.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
#import <SoObjects/Appointments/iCalEntityObject+SOGo.h>
|
||||
#import <SoObjects/Appointments/SOGoAppointmentFolder.h>
|
||||
#import <SoObjects/Appointments/SOGoAppointmentObject.h>
|
||||
#import <SoObjects/Mailer/SOGoMailObject.h>
|
||||
#import <SoObjects/Mailer/SOGoMailBodyPart.h>
|
||||
|
||||
#import "UIxMailPartICalViewer.h"
|
||||
|
||||
|
@ -109,35 +110,32 @@
|
|||
|
||||
- (BOOL) couldParseCalendar
|
||||
{
|
||||
return [[self inCalendar] isNotNull];
|
||||
return (([self inCalendar]));
|
||||
}
|
||||
|
||||
- (iCalEvent *) inEvent
|
||||
{
|
||||
NSArray *events;
|
||||
|
||||
if (inEvent)
|
||||
return [inEvent isNotNull] ? inEvent : nil;
|
||||
|
||||
events = [[self inCalendar] events];
|
||||
if ([events count] > 0) {
|
||||
inEvent = [[events objectAtIndex:0] retain];
|
||||
return inEvent;
|
||||
}
|
||||
else {
|
||||
inEvent = [[NSNull null] retain];
|
||||
return nil;
|
||||
}
|
||||
if (!inEvent)
|
||||
{
|
||||
events = [[self inCalendar] events];
|
||||
if ([events count] > 0)
|
||||
inEvent = [[events objectAtIndex:0] retain];
|
||||
}
|
||||
|
||||
return inEvent;
|
||||
}
|
||||
|
||||
/* formatters */
|
||||
|
||||
- (SOGoDateFormatter *) dateFormatter
|
||||
{
|
||||
if (dateFormatter == nil) {
|
||||
dateFormatter = [[context activeUser] dateFormatterInContext: context];
|
||||
[dateFormatter retain];
|
||||
}
|
||||
if (!dateFormatter)
|
||||
{
|
||||
dateFormatter = [[context activeUser] dateFormatterInContext: context];
|
||||
[dateFormatter retain];
|
||||
}
|
||||
|
||||
return dateFormatter;
|
||||
}
|
||||
|
@ -146,7 +144,7 @@
|
|||
|
||||
- (void) setAttendee: (id) _attendee
|
||||
{
|
||||
ASSIGN(attendee, _attendee);
|
||||
ASSIGN (attendee, _attendee);
|
||||
}
|
||||
|
||||
- (id) attendee
|
||||
|
@ -220,7 +218,7 @@
|
|||
|
||||
/* calendar folder support */
|
||||
|
||||
- (id) calendarFolder
|
||||
- (SOGoAppointmentFolder *) calendarFolder
|
||||
{
|
||||
/* return scheduling calendar of currently logged-in user */
|
||||
SOGoUser *user;
|
||||
|
@ -234,49 +232,50 @@
|
|||
return [folder lookupName: @"personal" inContext: context acquire: NO];
|
||||
}
|
||||
|
||||
- (id) storedEventObject
|
||||
- (SOGoAppointmentObject *) storedEventObject
|
||||
{
|
||||
/* lookup object in the users Calendar */
|
||||
id calendar;
|
||||
SOGoAppointmentFolder *calendar;
|
||||
NSString *filename;
|
||||
|
||||
if (storedEventObject)
|
||||
return [storedEventObject isNotNull] ? storedEventObject : nil;
|
||||
|
||||
calendar = [self calendarFolder];
|
||||
if ([calendar isKindOfClass:[NSException class]]) {
|
||||
[self errorWithFormat:@"Did not find Calendar folder: %@", calendar];
|
||||
}
|
||||
else {
|
||||
NSString *filename;
|
||||
|
||||
filename = [calendar resourceNameForEventUID:[[self inEvent] uid]];
|
||||
if (filename) {
|
||||
// TODO: When we get an exception, this might be an auth issue meaning
|
||||
// that the UID indeed exists but that the user has no access to
|
||||
// the object.
|
||||
// Of course this is quite unusual for the private calendar though.
|
||||
id tmp;
|
||||
|
||||
tmp = [calendar lookupName:filename inContext:[self context] acquire:NO];
|
||||
if ([tmp isNotNull] && ![tmp isKindOfClass:[NSException class]])
|
||||
storedEventObject = [tmp retain];
|
||||
if (!storedEventObject)
|
||||
{
|
||||
calendar = [self calendarFolder];
|
||||
if ([calendar isKindOfClass: [NSException class]])
|
||||
[self errorWithFormat:@"Did not find Calendar folder: %@", calendar];
|
||||
else
|
||||
{
|
||||
filename = [calendar resourceNameForEventUID:[[self inEvent] uid]];
|
||||
if (filename)
|
||||
{
|
||||
storedEventObject = [calendar lookupName: filename
|
||||
inContext: [self context]
|
||||
acquire: NO];
|
||||
if ([storedEventObject isKindOfClass: [NSException class]])
|
||||
storedEventObject = nil;
|
||||
else
|
||||
[storedEventObject retain];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (storedEventObject == nil)
|
||||
storedEventObject = [[NSNull null] retain];
|
||||
|
||||
return storedEventObject;
|
||||
}
|
||||
|
||||
- (BOOL) isEventStoredInCalendar
|
||||
{
|
||||
return [[self storedEventObject] isNotNull];
|
||||
return (([self storedEventObject]));
|
||||
}
|
||||
|
||||
- (iCalEvent *) storedEvent
|
||||
{
|
||||
return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component: NO];
|
||||
if (!storedEvent)
|
||||
{
|
||||
storedEvent = [[self storedEventObject] component: NO secure: NO];
|
||||
[storedEvent retain];
|
||||
}
|
||||
|
||||
return storedEvent;
|
||||
}
|
||||
|
||||
/* organizer tracking */
|
||||
|
@ -294,34 +293,24 @@
|
|||
{
|
||||
iCalEvent *authorativeEvent;
|
||||
|
||||
if ([[self storedEvent] compare: [self inEvent]]
|
||||
== NSOrderedAscending)
|
||||
[self storedEvent];
|
||||
if (!storedEvent
|
||||
|| ([storedEvent compare: [self inEvent]] == NSOrderedAscending))
|
||||
authorativeEvent = inEvent;
|
||||
else
|
||||
authorativeEvent = storedEventObject;
|
||||
authorativeEvent = [self storedEvent];
|
||||
|
||||
return authorativeEvent;
|
||||
}
|
||||
|
||||
- (BOOL) isLoggedInUserTheOrganizer
|
||||
{
|
||||
iCalPerson *organizer;
|
||||
|
||||
organizer = [[self authorativeEvent] organizer];
|
||||
|
||||
return [[context activeUser] hasEmail: [organizer rfc822Email]];
|
||||
return [[self authorativeEvent] userIsOrganizer: [context activeUser]];
|
||||
}
|
||||
|
||||
- (BOOL) isLoggedInUserAnAttendee
|
||||
{
|
||||
NSString *loginEMail;
|
||||
|
||||
if ((loginEMail = [self loggedInUserEMail]) == nil) {
|
||||
[self warnWithFormat:@"Could not determine email of logged in user?"];
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [[self authorativeEvent] isParticipant:loginEMail];
|
||||
return [[self authorativeEvent] userIsParticipant: [context activeUser]];
|
||||
}
|
||||
|
||||
/* derived fields */
|
||||
|
@ -405,7 +394,34 @@
|
|||
|
||||
- (BOOL) isReplySenderAnAttendee
|
||||
{
|
||||
return [[self storedReplyAttendee] isNotNull];
|
||||
return (([self storedReplyAttendee]));
|
||||
}
|
||||
|
||||
- (iCalPerson *) _emailParticipantWithEvent: (iCalEvent *) event
|
||||
{
|
||||
NSString *emailFrom;
|
||||
SOGoMailObject *mailObject;
|
||||
NGImap4EnvelopeAddress *address;
|
||||
|
||||
mailObject = [[self clientObject] mailObject];
|
||||
address = [[mailObject fromEnvelopeAddresses] objectAtIndex: 0];
|
||||
emailFrom = [address baseEMail];
|
||||
|
||||
return [event findParticipantWithEmail: emailFrom];
|
||||
}
|
||||
|
||||
- (BOOL) hasSenderStatusChanged
|
||||
{
|
||||
iCalPerson *emailParticipant, *calendarParticipant;
|
||||
|
||||
[self inEvent];
|
||||
[self storedEvent];
|
||||
emailParticipant = [self _emailParticipantWithEvent: inEvent];
|
||||
calendarParticipant = [self _emailParticipantWithEvent: storedEvent];
|
||||
|
||||
return ([[emailParticipant partStat]
|
||||
caseInsensitiveCompare: [calendarParticipant partStat]]
|
||||
!= NSOrderedSame);
|
||||
}
|
||||
|
||||
@end /* UIxMailPartICalViewer */
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
actionClass = "UIxMailPartICalActions";
|
||||
actionName = "decline";
|
||||
};
|
||||
updateUserStatus = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxMailPartICalActions";
|
||||
actionName = "updateUserStatus";
|
||||
};
|
||||
/* tentative = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxMailPartICalAction";
|
||||
|
@ -29,12 +34,12 @@
|
|||
protectedBy = "View";
|
||||
actionClass = "UIxMailPartICalAction";
|
||||
actionName = "addToCalendar";
|
||||
};
|
||||
}; */
|
||||
deleteFromCalendar = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxMailPartICalAction";
|
||||
actionName = "deleteFromCalendar";
|
||||
}; */
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -264,7 +264,7 @@
|
|||
unsigned int minutes;
|
||||
iCalRecurrenceRule *rule;
|
||||
|
||||
event = (iCalEvent *) [[self clientObject] component: NO];
|
||||
event = (iCalEvent *) [[self clientObject] component: NO secure: YES];
|
||||
if (event)
|
||||
{
|
||||
startDate = [event startDate];
|
||||
|
@ -352,16 +352,7 @@
|
|||
|
||||
- (id <WOActionResults>) saveAction
|
||||
{
|
||||
SOGoAppointmentObject *clientObject;
|
||||
NSString *iCalString;
|
||||
|
||||
clientObject = [self clientObject];
|
||||
NSLog(@"saveAction, clientObject = %@", clientObject);
|
||||
|
||||
iCalString = [[clientObject calendar: NO] versitString];
|
||||
|
||||
NSLog(@"saveAction, iCalString = %@", iCalString);
|
||||
[clientObject saveContentString: iCalString];
|
||||
[[self clientObject] saveComponent: event];
|
||||
|
||||
return [self jsCloseWithRefreshMethod: @"refreshEventsAndDisplay()"];
|
||||
}
|
||||
|
@ -385,7 +376,7 @@
|
|||
iCalRecurrenceRule *rule;
|
||||
|
||||
clientObject = [self clientObject];
|
||||
event = (iCalEvent *) [clientObject component: YES];
|
||||
event = (iCalEvent *) [clientObject component: YES secure: NO];
|
||||
|
||||
[super takeValuesFromRequest: _rq inContext: _ctx];
|
||||
|
||||
|
@ -443,23 +434,18 @@
|
|||
|
||||
// TODO: add tentatively
|
||||
|
||||
- (id) acceptOrDeclineAction: (BOOL) accept
|
||||
- (id) acceptAction
|
||||
{
|
||||
[[self clientObject] changeParticipationStatus: (accept
|
||||
? @"ACCEPTED"
|
||||
: @"DECLINED")];
|
||||
[[self clientObject] changeParticipationStatus: @"ACCEPTED"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) acceptAction
|
||||
{
|
||||
return [self acceptOrDeclineAction: YES];
|
||||
}
|
||||
|
||||
- (id) declineAction
|
||||
{
|
||||
return [self acceptOrDeclineAction: NO];
|
||||
[[self clientObject] changeParticipationStatus: @"DECLINED"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import <SoObjects/Appointments/iCalPerson+SOGo.h>
|
||||
#import <SoObjects/Appointments/SOGoAppointmentFolder.h>
|
||||
#import <SoObjects/Appointments/SOGoAppointmentFolders.h>
|
||||
#import <SoObjects/Appointments/SOGoAppointmentObject.h>
|
||||
|
@ -245,6 +246,16 @@
|
|||
return url;
|
||||
}
|
||||
|
||||
- (BOOL) hasOrganizer
|
||||
{
|
||||
return (![organizer isVoid]);
|
||||
}
|
||||
|
||||
- (NSString *) organizerName
|
||||
{
|
||||
return [organizer mailAddress];
|
||||
}
|
||||
|
||||
- (void) setAttendeesNames: (NSString *) newAttendeesNames
|
||||
{
|
||||
ASSIGN (attendeesNames, newAttendeesNames);
|
||||
|
@ -694,29 +705,8 @@
|
|||
respondsToSelector: @selector(saveContentString:)];
|
||||
}
|
||||
|
||||
- (BOOL) containsConflict: (id) _component
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* access */
|
||||
|
||||
#if 0
|
||||
- (iCalPerson *) getOrganizer
|
||||
{
|
||||
iCalPerson *p;
|
||||
NSString *emailProp;
|
||||
|
||||
emailProp = [@"MAILTO:" stringByAppendingString:[self emailForUser]];
|
||||
p = [[[iCalPerson alloc] init] autorelease];
|
||||
[p setEmail:emailProp];
|
||||
[p setCn:[self cnForUser]];
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (BOOL) isMyComponent
|
||||
{
|
||||
return ([[context activeUser] hasEmail: [organizer rfc822Email]]);
|
||||
|
@ -845,6 +835,7 @@
|
|||
[currentAttendee setCn: [names objectAtIndex: count]];
|
||||
[currentAttendee setEmail: currentEmail];
|
||||
[currentAttendee setRole: @"REQ-PARTICIPANT"];
|
||||
[currentAttendee setRsvp: @"TRUE"];
|
||||
[currentAttendee
|
||||
setParticipationStatus: iCalPersonPartStatNeedsAction];
|
||||
}
|
||||
|
|
|
@ -282,7 +282,7 @@
|
|||
NSString *duration;
|
||||
unsigned int minutes;
|
||||
|
||||
todo = (iCalToDo *) [[self clientObject] component: NO];
|
||||
todo = (iCalToDo *) [[self clientObject] component: NO secure: YES];
|
||||
if (todo)
|
||||
{
|
||||
startDate = [todo startDate];
|
||||
|
@ -345,16 +345,23 @@
|
|||
|
||||
- (id <WOActionResults>) saveAction
|
||||
{
|
||||
SOGoTaskObject *clientObject;
|
||||
NSString *iCalString;
|
||||
|
||||
clientObject = [self clientObject];
|
||||
iCalString = [[clientObject calendar: NO] versitString];
|
||||
[clientObject saveContentString: iCalString];
|
||||
[[self clientObject] saveComponent: todo];
|
||||
|
||||
return [self jsCloseWithRefreshMethod: @"refreshTasks()"];
|
||||
}
|
||||
|
||||
// - (id <WOActionResults>) saveAction
|
||||
// {
|
||||
// SOGoTaskObject *clientObject;
|
||||
// NSString *iCalString;
|
||||
|
||||
// clientObject = [self clientObject];
|
||||
// iCalString = [[clientObject calendar: NO secure: NO] versitString];
|
||||
// [clientObject saveContentString: iCalString];
|
||||
|
||||
// return [self jsCloseWithRefreshMethod: @"refreshTasks()"];
|
||||
// }
|
||||
|
||||
- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request
|
||||
inContext: (WOContext*) context
|
||||
{
|
||||
|
@ -372,7 +379,7 @@
|
|||
SOGoTaskObject *clientObject;
|
||||
|
||||
clientObject = [self clientObject];
|
||||
todo = (iCalToDo *) [clientObject component: YES];
|
||||
todo = (iCalToDo *) [clientObject component: YES secure: NO];
|
||||
|
||||
[super takeValuesFromRequest: _rq inContext: _ctx];
|
||||
|
||||
|
@ -428,7 +435,7 @@
|
|||
NSString *newStatus, *iCalString;
|
||||
|
||||
clientObject = [self clientObject];
|
||||
todo = (iCalToDo *) [clientObject component: NO];
|
||||
todo = (iCalToDo *) [clientObject component: NO secure: NO];
|
||||
if (todo)
|
||||
{
|
||||
newStatus = [self queryParameterForKey: @"status"];
|
||||
|
@ -441,7 +448,7 @@
|
|||
[todo setStatus: @"IN-PROCESS"];
|
||||
}
|
||||
|
||||
iCalString = [[clientObject calendar: NO] versitString];
|
||||
iCalString = [[clientObject calendar: NO secure: NO] versitString];
|
||||
[clientObject saveContentString: iCalString];
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
>
|
||||
<!-- TODO: add iMIP actions -->
|
||||
|
||||
<input id="iCalendarAttachment" type="hidden"
|
||||
var:value="pathToAttachmentObject"/>
|
||||
|
||||
<var:if condition="couldParseCalendar" const:negate="1">
|
||||
<fieldset>
|
||||
<legend>Parsing Error</legend>
|
||||
|
@ -33,23 +36,24 @@
|
|||
</var:if>
|
||||
</legend>
|
||||
|
||||
<var:if condition="inCalendar.method" const:value="REQUEST">
|
||||
<var:if condition="inCalendar.method.uppercaseString" const:value="REQUEST">
|
||||
<!-- sent to attendees to propose or update a meeting -->
|
||||
<var:if condition="isLoggedInUserAnAttendee">
|
||||
<p class="uix_ical_toolbar" id="iCalendarToolbar">
|
||||
<input id="iCalendarAttachment" type="hidden"
|
||||
var:value="pathToAttachmentObject"/>
|
||||
<input id="iCalendarAccept" class="button"
|
||||
type="button" label:value="Accept"/>
|
||||
<input id="iCalendarDecline" class="button"
|
||||
type="button" label:value="Decline"/>
|
||||
<input id="iCalendarTentative" class="button"
|
||||
type="button" label:value="Tentative"/>
|
||||
<var:if condition="isEventStoredInCalendar" const:negate="YES">
|
||||
| <input id="iCalendarAddToCalendar" class="button"
|
||||
type="button" label:value="Add to calendar"/>
|
||||
</var:if>
|
||||
</p>
|
||||
<var:if condition="$storedReplyAttendee.partStatWithDefault"
|
||||
const:value="NEEDS-ACTION" const:negate="YES">
|
||||
<p class="uix_ical_toolbar" id="iCalendarToolbar">
|
||||
<input id="iCalendarAccept" class="button"
|
||||
type="button" label:value="Accept"/>
|
||||
<input id="iCalendarDecline" class="button"
|
||||
type="button" label:value="Decline"/>
|
||||
<!-- <input id="iCalendarTentative" class="button"
|
||||
type="button" label:value="Tentative"/> -->
|
||||
<var:if condition="isEventStoredInCalendar" const:negate="YES">
|
||||
| <input id="iCalendarAddToCalendar" class="button"
|
||||
type="button" label:value="Add to calendar"/>
|
||||
</var:if>
|
||||
</p>
|
||||
</var:if>
|
||||
|
||||
<p>
|
||||
<var:string label:value="Organizer" />
|
||||
|
@ -70,20 +74,20 @@
|
|||
</var:if>
|
||||
|
||||
|
||||
<var:if condition="inCalendar.method" const:value="REPLY">
|
||||
<var:if condition="inCalendar.method.uppercaseString" const:value="REPLY">
|
||||
<!-- sent to organizer to update the status of the participant -->
|
||||
<var:if condition="isReplySenderAnAttendee" const:negate="1">
|
||||
<p><var:string label:value="reply_info_no_attendee" /></p>
|
||||
</var:if>
|
||||
|
||||
<var:if condition="isReplySenderAnAttendee">
|
||||
<p class="uix_ical_toolbar">
|
||||
<a var:href="addStatusReplyLink"
|
||||
var:_newstat="$inReplyAttendee.partStatWithDefault"
|
||||
var:_sender="replySenderBaseEMail"
|
||||
label:string="do_update_status"/>
|
||||
</p>
|
||||
|
||||
<var:if condition="hasSenderStatusChanged"
|
||||
><p class="uix_ical_toolbar">
|
||||
<input id="iCalendarUpdateUserStatus" class="button"
|
||||
type="button" label:value="Update status"/>
|
||||
</p
|
||||
></var:if>
|
||||
|
||||
<!-- TODO: replies to events not in the calendar? -->
|
||||
|
||||
<p>
|
||||
|
@ -96,7 +100,7 @@
|
|||
</var:if>
|
||||
</var:if>
|
||||
|
||||
<var:if condition="inCalendar.method" const:value="CANCEL">
|
||||
<var:if condition="inCalendar.method.uppercaseString" const:value="CANCEL">
|
||||
<!-- sent to attendees to notify of the attendee being removed or the
|
||||
event being deleted -->
|
||||
<var:if condition="isEventStoredInCalendar">
|
||||
|
@ -113,13 +117,13 @@
|
|||
</var:if>
|
||||
|
||||
|
||||
<var:if condition="inCalendar.method" const:value="ADD">
|
||||
<var:if condition="inCalendar.method.uppercaseString" const:value="ADD">
|
||||
<!-- TODO -->
|
||||
<p><var:string label:value="add_info_text" /></p>
|
||||
</var:if>
|
||||
|
||||
|
||||
<var:if condition="inCalendar.method" const:value="PUBLISH">
|
||||
<var:if condition="inCalendar.method.uppercaseString" const:value="PUBLISH">
|
||||
<!-- none-scheduling event sent to someone for adding to the calendar -->
|
||||
<p><var:string label:value="publish_info_text" /></p>
|
||||
</var:if>
|
||||
|
@ -145,7 +149,6 @@
|
|||
-->
|
||||
</var:if>
|
||||
|
||||
|
||||
<!-- the user comment is used in replies -->
|
||||
<var:if condition="inEvent.userComment.isNotEmpty">
|
||||
<div class="linked_attachment_meta" style="background-color: white;">
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
const:popup="YES"
|
||||
title="name"
|
||||
var:toolbar="toolbar"
|
||||
const:cssFiles="UIxComponentEditor.css"
|
||||
const:jsFiles="skycalendar.js,UIxComponentEditor.js">
|
||||
|
||||
<script type="text/javascript">
|
||||
|
@ -54,7 +55,11 @@
|
|||
label:noSelectionString="prio_0"
|
||||
string="itemPriorityText" selection="priority"/>
|
||||
</span></span>
|
||||
<label id="attendeesLabel" style="display: none;"><var:string label:value="Attendees:"
|
||||
<var:if condition="hasOrganizer"
|
||||
><label id="organizerLabel"><var:string label:value="Organizer:"
|
||||
/><span class="content"><var:string value="organizerName"/></span></label>
|
||||
</var:if>
|
||||
<label id="attendeesLabel"><var:string label:value="Attendees:"
|
||||
/><span class="content"
|
||||
><a href="#" id="attendeesHref"><!-- space --></a></span></label>
|
||||
<hr />
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
<var:if condition="hasPageSpecificCSS"
|
||||
><link type="text/css" rel="stylesheet" var:href="pageCSSURL"
|
||||
/></var:if>
|
||||
<var:foreach list="additionalCSSFiles" item="item"
|
||||
><link type="text/css" rel="stylesheet" var:href="item"
|
||||
/>
|
||||
</var:foreach>
|
||||
<var:if-ie
|
||||
><link type="text/css" rel="stylesheet" rsrc:href="iefixes.css"
|
||||
/></var:if-ie>
|
||||
|
|
|
@ -755,6 +755,7 @@ function configureiCalLinksInMessage() {
|
|||
var buttons = { "iCalendarAccept": "accept",
|
||||
"iCalendarDecline": "decline",
|
||||
"iCalendarTentative": "tentative",
|
||||
"iCalendarUpdateUserStatus": "updateUserStatus",
|
||||
"iCalendarAddToCalendar": "addToCalendar",
|
||||
"iCalendarDeleteFromCalendar": "deleteFromCalendar" };
|
||||
|
||||
|
@ -772,11 +773,13 @@ function onICalendarButtonClick(event) {
|
|||
var link = $("iCalendarAttachment").value;
|
||||
if (link) {
|
||||
var urlstr = link + "/" + this.action;
|
||||
log ("click: " + urlstr);
|
||||
triggerAjaxRequest(urlstr, ICalendarButtonCallback,
|
||||
currentMailbox + "/"
|
||||
+ currentMessages[currentMailbox]);
|
||||
window.alert(urlstr);
|
||||
}
|
||||
else
|
||||
log("no link");
|
||||
}
|
||||
|
||||
function ICalendarButtonCallback(http) {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
#attendeesLabel
|
||||
{ display: none; }
|
Loading…
Reference in New Issue