diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 0e219c764..3d1c1b6fe 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -1,5 +1,6 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG + Copyright (C) 2007-2008 Inverse inc. This file is part of OpenGroupware.org. @@ -62,6 +63,7 @@ #import #import #import +#import #import #import diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 44e9736ce..cf83d2f73 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -40,6 +40,7 @@ #import #import #import +#import #import #import #import @@ -169,7 +170,7 @@ possibleName = [folder resourceNameForEventUID: eventUID]; if (possibleName) { - object = [folder lookupName: nameInContainer + object = [folder lookupName: possibleName inContext: context acquire: NO]; if ([object isKindOfClass: [NSException class]]) object = nil; @@ -362,9 +363,11 @@ } - (NSException *) _updateAttendee: (iCalPerson *) attendee + ownerUser: (SOGoUser *) theOwnerUser forEventUID: (NSString *) eventUID withSequence: (NSNumber *) sequence forUID: (NSString *) uid + shouldAddSentBy: (BOOL) b { SOGoAppointmentObject *eventObject; iCalEvent *event; @@ -381,8 +384,23 @@ if ([[event sequence] compare: sequence] == NSOrderedSame) { - otherAttendee = [event findParticipant: [context activeUser]]; + SOGoUser *currentUser; + + //otherAttendee = [event findParticipant: [context activeUser]]; + otherAttendee = [event findParticipant: theOwnerUser]; [otherAttendee setPartStat: [attendee partStat]]; + + // If one has accepted / declined an invitation on behalf of + // the attendee, we add the user to the SENT-BY attribute. + currentUser = [context activeUser]; + if (b && ![[currentUser login] isEqualToString: [theOwnerUser login]]) + { + NSString *currentEmail; + currentEmail = [[currentUser allEmails] objectAtIndex: 0]; + [otherAttendee addAttribute: @"SENT-BY" + value: [NSString stringWithFormat: @"\"MAILTO:%@\"", currentEmail]]; + } + iCalString = [[event parent] versitString]; error = [eventObject saveContentString: iCalString]; } @@ -392,10 +410,11 @@ } - (NSException *) _handleAttendee: (iCalPerson *) attendee + ownerUser: (SOGoUser *) theOwnerUser statusChange: (NSString *) newStatus inEvent: (iCalEvent *) event { - NSString *newContent, *currentStatus, *organizerUID; + NSString *newContent, *currentStatus, *currentUser, *organizerUID; NSException *ex; SOGoUser *ownerUser; @@ -406,20 +425,70 @@ != NSOrderedSame) { [attendee setPartStat: newStatus]; + + // If one has accepted / declined an invitation on behalf of + // the attendee, we add the user to the SENT-BY attribute. + currentUser = [context activeUser]; + if (![[currentUser login] isEqualToString: [theOwnerUser login]]) + { + NSString *currentEmail; + currentEmail = [[currentUser allEmails] objectAtIndex: 0]; + [attendee addAttribute: @"SENT-BY" + value: [NSString stringWithFormat: @"\"MAILTO:%@\"", currentEmail]]; + } + + // We generate the updated iCalendar file and we save it + // in the database. newContent = [[event parent] versitString]; ex = [self saveContentString: newContent]; + + ownerUser = [SOGoUser userWithLogin: owner roles: nil]; if (!(ex || [event userIsOrganizer: ownerUser])) { if ([[attendee rsvp] isEqualToString: @"true"] && [event isStillRelevant]) [self sendResponseToOrganizer: event]; + organizerUID = [[event organizer] uid]; if (organizerUID) ex = [self _updateAttendee: attendee + ownerUser: theOwnerUser forEventUID: [event uid] withSequence: [event sequence] - forUID: organizerUID]; + forUID: organizerUID + shouldAddSentBy: YES]; + } + + // We update the calendar of all participants that are + // local to the system. This is useful in case user A accepts + // invitation from organizer B and users C, D, E who are also + // attendees need to verify if A has accepted. + NSArray *attendees; + iCalPerson *att; + NSString *uid; + int i; + + attendees = [event attendees]; + + for (i = 0; i < [attendees count]; i++) + { + att = [attendees objectAtIndex: i]; + + if (att == attendee) continue; + + uid = [[LDAPUserManager sharedUserManager] + getUIDForEmail: [att rfc822Email]]; + + if (uid) + { + [self _updateAttendee: attendee + ownerUser: theOwnerUser + forEventUID: [event uid] + withSequence: [event sequence] + forUID: uid + shouldAddSentBy: YES]; + } } } @@ -512,6 +581,7 @@ { iCalPerson *localAttendee; iCalEvent *event; + SOGoUser *ownerUser; event = [self component: NO secure: NO]; localAttendee = [event findParticipantWithEmail: [attendee rfc822Email]]; @@ -519,6 +589,46 @@ { [localAttendee setPartStat: [attendee partStat]]; [self saveComponent: event]; + + /// TEST /// + NSArray *attendees; + iCalPerson *att; + NSString *uid; + int i; + + // We update for the organizer + ownerUser = [context activeUser]; + + [self _updateAttendee: attendee + ownerUser: ownerUser + forEventUID: [event uid] + withSequence: [event sequence] + forUID: [[SOGoUser userWithLogin: owner roles: nil] login] + shouldAddSentBy: NO]; + + attendees = [event attendees]; + + for (i = 0; i < [attendees count]; i++) + { + att = [attendees objectAtIndex: i]; + + if (att == attendee) continue; + + uid = [[LDAPUserManager sharedUserManager] + getUIDForEmail: [att rfc822Email]]; + + if (uid) + { + [self _updateAttendee: attendee + ownerUser: ownerUser + forEventUID: [event uid] + withSequence: [event sequence] + forUID: uid + shouldAddSentBy: NO]; + } + } + + /// TEST /// } else [self errorWithFormat: @"attendee not found: '%@'", attendee]; @@ -579,7 +689,9 @@ ownerUser = [SOGoUser userWithLogin: owner roles: nil]; attendee = [event findParticipant: ownerUser]; if (attendee) - ex = [self _handleAttendee: attendee statusChange: _status + ex = [self _handleAttendee: attendee + ownerUser: ownerUser + statusChange: _status inEvent: event]; else ex = [NSException exceptionWithHTTPStatus: 404 /* Not Found */ diff --git a/SoObjects/Appointments/SOGoAptMailEnglishDeletion.wo/SOGoAptMailEnglishDeletion.html b/SoObjects/Appointments/SOGoAptMailEnglishDeletion.wo/SOGoAptMailEnglishDeletion.html index 7dcf57b36..80644f86e 100644 --- a/SoObjects/Appointments/SOGoAptMailEnglishDeletion.wo/SOGoAptMailEnglishDeletion.html +++ b/SoObjects/Appointments/SOGoAptMailEnglishDeletion.wo/SOGoAptMailEnglishDeletion.html @@ -1,2 +1,2 @@ <#IsSubject>Event Cancelled: <#summary/> -<#IsBody><#organizer/> has cancelled this event: <#summary/>. \ No newline at end of file +<#IsBody><#organizer/> <#HasSentBy>(sent by <#sentBy/>) has cancelled this event: <#summary/>. \ No newline at end of file diff --git a/SoObjects/Appointments/SOGoAptMailEnglishDeletion.wo/SOGoAptMailEnglishDeletion.wod b/SoObjects/Appointments/SOGoAptMailEnglishDeletion.wo/SOGoAptMailEnglishDeletion.wod index 6d3d52eca..92428f429 100644 --- a/SoObjects/Appointments/SOGoAptMailEnglishDeletion.wo/SOGoAptMailEnglishDeletion.wod +++ b/SoObjects/Appointments/SOGoAptMailEnglishDeletion.wo/SOGoAptMailEnglishDeletion.wod @@ -16,3 +16,12 @@ summary: WOString { value = summary; escapeHTML = NO; } + +HasSentBy: WOConditional { + condition = hasSentBy; +} + +sentBy: WOString { + value = sentBy; + escapeHTML = NO; +} \ No newline at end of file diff --git a/SoObjects/Appointments/SOGoAptMailEnglishICalReply.wo/SOGoAptMailEnglishICalReply.html b/SoObjects/Appointments/SOGoAptMailEnglishICalReply.wo/SOGoAptMailEnglishICalReply.html index 895612404..f1c04f883 100644 --- a/SoObjects/Appointments/SOGoAptMailEnglishICalReply.wo/SOGoAptMailEnglishICalReply.html +++ b/SoObjects/Appointments/SOGoAptMailEnglishICalReply.wo/SOGoAptMailEnglishICalReply.html @@ -1,2 +1,2 @@ <#IsSubject>Event Invitation Reply: <#summary/> -<#IsBody><#attendee/> has <#HasAccepted>accepted<#HasDeclined>declined<#HasNotAcceptedNotDeclined>not yet decided upon your event invitation. +<#IsBody><#attendee/> has <#HasSentBy>(sent by <#sentBy/>) <#HasAccepted>accepted<#HasDeclined>declined<#HasNotAcceptedNotDeclined>not yet decided upon your event invitation. diff --git a/SoObjects/Appointments/SOGoAptMailEnglishICalReply.wo/SOGoAptMailEnglishICalReply.wod b/SoObjects/Appointments/SOGoAptMailEnglishICalReply.wo/SOGoAptMailEnglishICalReply.wod index 8c2373518..5c54f3352 100644 --- a/SoObjects/Appointments/SOGoAptMailEnglishICalReply.wo/SOGoAptMailEnglishICalReply.wod +++ b/SoObjects/Appointments/SOGoAptMailEnglishICalReply.wo/SOGoAptMailEnglishICalReply.wod @@ -28,3 +28,12 @@ HasDeclined: WOConditional { HasNotAcceptedNotDeclined: WOConditional { condition = hasNotAcceptedNotDeclined; } + +HasSentBy: WOConditional { + condition = hasSentBy; +} + +sentBy: WOString { + value = sentBy; + escapeHTML = NO; +} \ No newline at end of file diff --git a/SoObjects/Appointments/SOGoAptMailEnglishInvitation.wo/SOGoAptMailEnglishInvitation.html b/SoObjects/Appointments/SOGoAptMailEnglishInvitation.wo/SOGoAptMailEnglishInvitation.html index 322580d2b..a8805ccb6 100644 --- a/SoObjects/Appointments/SOGoAptMailEnglishInvitation.wo/SOGoAptMailEnglishInvitation.html +++ b/SoObjects/Appointments/SOGoAptMailEnglishInvitation.wo/SOGoAptMailEnglishInvitation.html @@ -1,2 +1,2 @@ <#IsSubject>Event Invitation: <#summary/> -<#IsBody><#organizer/> has invited you to <#summary/>. +<#IsBody><#organizer/> <#HasSentBy>(sent by <#sentBy/>) has invited you to <#summary/>. diff --git a/SoObjects/Appointments/SOGoAptMailEnglishInvitation.wo/SOGoAptMailEnglishInvitation.wod b/SoObjects/Appointments/SOGoAptMailEnglishInvitation.wo/SOGoAptMailEnglishInvitation.wod index 6d3d52eca..92428f429 100644 --- a/SoObjects/Appointments/SOGoAptMailEnglishInvitation.wo/SOGoAptMailEnglishInvitation.wod +++ b/SoObjects/Appointments/SOGoAptMailEnglishInvitation.wo/SOGoAptMailEnglishInvitation.wod @@ -16,3 +16,12 @@ summary: WOString { value = summary; escapeHTML = NO; } + +HasSentBy: WOConditional { + condition = hasSentBy; +} + +sentBy: WOString { + value = sentBy; + escapeHTML = NO; +} \ No newline at end of file diff --git a/SoObjects/Appointments/SOGoAptMailICalReply.m b/SoObjects/Appointments/SOGoAptMailICalReply.m index bcc334ef8..45e9d1784 100644 --- a/SoObjects/Appointments/SOGoAptMailICalReply.m +++ b/SoObjects/Appointments/SOGoAptMailICalReply.m @@ -98,7 +98,24 @@ static NSCharacterSet *wsSet = nil; - (NSString *) attendeeName { - return [attendee cn]; + NSString *name; + + name = [attendee cn]; + + if (name && [name length]) + return name; + + return [attendee rfc822Email]; +} + +- (BOOL) hasSentBy +{ + return [attendee hasSentBy]; +} + +- (NSString *) sentBy +{ + return [attendee sentBy]; } - (BOOL) hasAccepted diff --git a/SoObjects/Appointments/SOGoAptMailNotification.m b/SoObjects/Appointments/SOGoAptMailNotification.m index 6c6952028..d62cc16f1 100644 --- a/SoObjects/Appointments/SOGoAptMailNotification.m +++ b/SoObjects/Appointments/SOGoAptMailNotification.m @@ -108,6 +108,16 @@ static NSTimeZone *UTC = nil; return organizerName; } +- (BOOL) hasSentBy +{ + return [[apt organizer] hasSentBy]; +} + +- (NSString *) sentBy +{ + return [[apt organizer] sentBy]; +} + /* Helpers */ /* Generate Response */ diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index cc0f5bcd6..94a3c5bd3 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -46,6 +46,7 @@ #import #import #import +#import #import #import @@ -451,20 +452,6 @@ _occurenceHasID (iCalRepeatableEntityObject *occurence, NSString *recID) return [[SOGoUser userWithLogin: uid roles: nil] timeZone]; } -- (void) _setupSentByIfNeeded: (iCalPerson *) person -{ - SOGoUser *currentUser; - NSString *currentEmail; - - currentUser = [context activeUser]; - if (![[currentUser login] isEqualToString: owner]) - { - currentEmail = [[currentUser allEmails] objectAtIndex: 0]; - [person addAttribute: @"SENT-BY" - value: [NSString stringWithFormat: @"MAILTO:%@", currentEmail]]; - } -} - - (void) sendEMailUsingTemplateNamed: (NSString *) newPageName forObject: (iCalRepeatableEntityObject *) object toAttendees: (NSArray *) attendees @@ -491,11 +478,12 @@ _occurenceHasID (iCalRepeatableEntityObject *occurence, NSString *recID) { /* sender */ ownerUser = [SOGoUser userWithLogin: owner roles: nil]; - currentUser = [context activeUser]; - shortSenderEmail = [[currentUser allEmails] objectAtIndex: 0]; - senderEmail = [NSString stringWithFormat: @"%@ <%@>", - [ownerUser cn], shortSenderEmail]; - [self _setupSentByIfNeeded: [object organizer]]; + //currentUser = [context activeUser]; + //shortSenderEmail = [[currentUser allEmails] objectAtIndex: 0]; + // senderEmail = [NSString stringWithFormat: @"%@ <%@>", + // [ownerUser cn], shortSenderEmail]; + shortSenderEmail = [[object organizer] rfc822Email]; + senderEmail = [[object organizer] mailAddress]; // NSLog (@"sending '%@' from %@", // [(iCalCalendar *) [object parent] method], senderEmail); /* generate iCalString once */ @@ -695,7 +683,6 @@ _occurenceHasID (iCalRepeatableEntityObject *occurence, NSString *recID) { organizer = [event organizer]; attendee = [event findParticipant: ownerUser]; - [self _setupSentByIfNeeded: attendee]; [event setAttendees: [NSArray arrayWithObject: attendee]]; [self sendIMIPReplyForEvent: event to: organizer]; } diff --git a/SoObjects/Appointments/SOGoUserFolder+Appointments.m b/SoObjects/Appointments/SOGoUserFolder+Appointments.m index 211c72460..1924f128d 100644 --- a/SoObjects/Appointments/SOGoUserFolder+Appointments.m +++ b/SoObjects/Appointments/SOGoUserFolder+Appointments.m @@ -31,6 +31,7 @@ #import #import +#import #import #import diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.m b/SoObjects/Appointments/iCalEntityObject+SOGo.m index de04fd87e..ef2438155 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.m @@ -95,14 +95,22 @@ _computeAllDayOffset() return isParticipant; } -#warning user could be a delegate, we will need to handle that someday - (BOOL) userIsOrganizer: (SOGoUser *) user { - NSString *orgMail; + NSString *mail; + BOOL b; - orgMail = [[self organizer] rfc822Email]; + mail = [[self organizer] rfc822Email]; + b = [user hasEmail: mail]; + if (b) return YES; - return [user hasEmail: orgMail]; + // We check for the SENT-BY + mail = [[self organizer] sentBy]; + + if ([mail length]) + return [user hasEmail: mail]; + + return NO; } - (NSArray *) attendeeUIDs diff --git a/SoObjects/Appointments/iCalPerson+SOGo.h b/SoObjects/Appointments/iCalPerson+SOGo.h index a22955046..5cb18138b 100644 --- a/SoObjects/Appointments/iCalPerson+SOGo.h +++ b/SoObjects/Appointments/iCalPerson+SOGo.h @@ -1,6 +1,6 @@ /* iCalPerson+SOGo.h - this file is part of SOGo * - * Copyright (C) 2007 Inverse inc. + * Copyright (C) 2007-2008 Inverse inc. * * Author: Wolfgang Sourdeau * @@ -33,6 +33,7 @@ - (NSString *) mailAddress; - (NSString *) uid; +- (BOOL) hasSentBy; @end diff --git a/SoObjects/Appointments/iCalPerson+SOGo.m b/SoObjects/Appointments/iCalPerson+SOGo.m index 0ab626ccf..224802de8 100644 --- a/SoObjects/Appointments/iCalPerson+SOGo.m +++ b/SoObjects/Appointments/iCalPerson+SOGo.m @@ -1,6 +1,6 @@ /* iCalPerson+SOGo.m - this file is part of SOGo * - * Copyright (C) 2007 Inverse inc. + * Copyright (C) 2007-2008 Inverse inc. * * Author: Wolfgang Sourdeau * @@ -50,4 +50,30 @@ static LDAPUserManager *um = nil; return [um getUIDForEmail: [self rfc822Email]]; } +- (BOOL) hasSentBy +{ + NSString *mail; + + mail = [self value: 0 ofAttribute: @"SENT-BY"]; + + return ([mail length] > 7); +} + +- (NSString *) sentBy +{ + NSString *mail; + + mail = [self value: 0 ofAttribute: @"SENT-BY"]; + + if ([mail length] > 7) + { + if ([mail characterAtIndex: 0] && [mail hasSuffix: @"\""]) + mail = [mail substringWithRange: NSMakeRange(1, [mail length]-2)]; + + return [mail substringFromIndex: 7]; + } + + return @""; +} + @end diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile index 7b9f8e670..f79ca6692 100644 --- a/SoObjects/SOGo/GNUmakefile +++ b/SoObjects/SOGo/GNUmakefile @@ -53,6 +53,7 @@ SOGo_HEADER_FILES = \ SOGoWebDAVValue.h \ SOGoMailer.h \ SOGoUser.h \ + SOGoUserManager.h \ \ NSDictionary+BSJSONAdditions.h \ NSScanner+BSJSONAdditions.h \ @@ -98,6 +99,7 @@ SOGo_OBJC_FILES = \ SOGoWebDAVValue.m \ SOGoMailer.m \ SOGoUser.m \ + SOGoUserManager.m \ \ NSDictionary+BSJSONAdditions.m \ NSScanner+BSJSONAdditions.m \ diff --git a/SoObjects/SOGo/SOGoDAVAuthenticator.m b/SoObjects/SOGo/SOGoDAVAuthenticator.m index 0b59e1459..6c8f59ce5 100644 --- a/SoObjects/SOGo/SOGoDAVAuthenticator.m +++ b/SoObjects/SOGo/SOGoDAVAuthenticator.m @@ -32,6 +32,7 @@ #import "LDAPUserManager.h" #import "SOGoPermissions.h" #import "SOGoUser.h" +#import "SOGoUserManager.h" #import "SOGoDAVAuthenticator.h" diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index 80e2a8b5b..6ec796f5d 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -60,6 +60,7 @@ #import "SOGoParentFolder.h" #import "SOGoPermissions.h" #import "SOGoUser.h" +#import "SOGoUserManager.h" #import "SOGoWebDAVAclManager.h" #import "WORequest+SOGo.h" diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index 51fcfae78..952ec7379 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -61,6 +61,7 @@ #import "SOGoCache.h" #import "SOGoPermissions.h" #import "SOGoUser.h" +#import "SOGoUserManager.h" #import "SOGoUserFolder.h" #import "SOGoWebDAVAclManager.h" #import "SOGoWebDAVValue.h" diff --git a/SoObjects/SOGo/SOGoUser.h b/SoObjects/SOGo/SOGoUser.h index f0e8a30d1..973896a70 100644 --- a/SoObjects/SOGo/SOGoUser.h +++ b/SoObjects/SOGo/SOGoUser.h @@ -48,6 +48,7 @@ @class SOGoAppointmentFolder; @class SOGoAppointmentFolders; @class SOGoDateFormatter; +@class SOGoUserDefaults; @class SOGoUserFolder; extern NSString *SOGoWeekStartJanuary1; @@ -62,8 +63,8 @@ extern NSString *SOGoWeekStartFirstFullWeek; @interface SOGoUser : SoUser { - NSUserDefaults *userDefaults; - NSUserDefaults *userSettings; + SOGoUserDefaults *userDefaults; + SOGoUserDefaults *userSettings; NSArray *allEmails; NSString *language; NSString *currentPassword; @@ -111,7 +112,10 @@ extern NSString *SOGoWeekStartFirstFullWeek; /* defaults */ +- (void) setUserDefaultsFromDictionary: (NSDictionary *) theDictionary; - (NSUserDefaults *) userDefaults; + +- (void) setUserSettingsFromDictionary: (NSDictionary *) theDictionary; - (NSUserDefaults *) userSettings; - (NSString *) language; diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 25c04f8e9..4ba77512e 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -470,6 +470,14 @@ _timeValue (NSString *key) } /* defaults */ +- (void) setUserDefaultsFromDictionary: (NSDictionary *) theDictionary +{ + if (!userDefaults) + userDefaults = [[SOGoUserDefaults alloc] initWithTableURL: SOGoProfileURL + uid: login + fieldName: @"c_defaults"]; + [userDefaults setValues: theDictionary]; +} - (NSUserDefaults *) userDefaults { @@ -493,6 +501,15 @@ _timeValue (NSString *key) return userDefaults; } +- (void) setUserSettingsFromDictionary: (NSDictionary *) theDictionary +{ + if (!userSettings) + userSettings = [[SOGoUserDefaults alloc] initWithTableURL: SOGoProfileURL + uid: login + fieldName: @"c_settings"]; + [userSettings setValues: theDictionary]; +} + - (NSUserDefaults *) userSettings { if (!userSettings) diff --git a/SoObjects/SOGo/SOGoUserDefaults.h b/SoObjects/SOGo/SOGoUserDefaults.h index c98a16e9f..d7fb827e4 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.h +++ b/SoObjects/SOGo/SOGoUserDefaults.h @@ -36,12 +36,9 @@ @interface SOGoUserDefaults : NSObject { - NSUserDefaults *parent; NSURL *url; NSString *uid; NSString *fieldName; - - NSArray *fieldNames; NSMutableDictionary *values; NSCalendarDate *lastFetch; @@ -58,6 +55,7 @@ fieldName: (NSString *) fieldName; /* value access */ +- (void) setValues: (NSDictionary *) theValues; - (void) setObject: (id) value forKey: (NSString *) key; diff --git a/SoObjects/SOGo/SOGoUserDefaults.m b/SoObjects/SOGo/SOGoUserDefaults.m index 5ed9e5a67..74633ec4a 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.m +++ b/SoObjects/SOGo/SOGoUserDefaults.m @@ -20,6 +20,7 @@ */ #import +#import #import #import #import @@ -50,7 +51,6 @@ static NSString *uidColumnName = @"c_uid"; if (tableURL && [userID length] > 0 && [defaultsFieldName length] > 0) { - parent = [[NSUserDefaults standardUserDefaults] retain]; fieldName = [defaultsFieldName copy]; url = [tableURL copy]; uid = [userID copy]; @@ -77,7 +77,6 @@ static NSString *uidColumnName = @"c_uid"; { [values release]; [lastFetch release]; - [parent release]; [url release]; [uid release]; [fieldName release]; @@ -101,11 +100,6 @@ static NSString *uidColumnName = @"c_uid"; return fieldName; } -- (NSUserDefaults *) parentDefaults -{ - return parent; -} - /* operation */ - (BOOL) primaryFetchProfile @@ -275,6 +269,18 @@ static NSString *uidColumnName = @"c_uid"; [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; else { + NSMutableDictionary *d; + + d = [[NSMutableDictionary alloc] init]; + [d addEntriesFromDictionary: values]; + [d setObject: uid forKey: @"uid"]; + + [[NSDistributedNotificationCenter defaultCenter] + postNotificationName: ([fieldName isEqualToString: @"c_defaults"] ? @"SOGoUserDefaultsHaveChanged" : @"SOGoUserSettingsHaveChanged") + object: nil + userInfo: d]; + [d release]; + if ([[channel adaptorContext] hasOpenTransaction]) { ex = [channel evaluateExpressionX: @"COMMIT TRANSACTION"]; @@ -315,6 +321,11 @@ static NSString *uidColumnName = @"c_uid"; } /* value access */ +- (void) setValues: (NSDictionary *) theValues +{ + [values removeAllObjects]; + [values addEntriesFromDictionary: theValues]; +} - (void) setObject: (id) value forKey: (NSString *) key diff --git a/SoObjects/SOGo/SOGoUserFolder.m b/SoObjects/SOGo/SOGoUserFolder.m index 3026658d4..2749aaa7d 100644 --- a/SoObjects/SOGo/SOGoUserFolder.m +++ b/SoObjects/SOGo/SOGoUserFolder.m @@ -52,6 +52,7 @@ #import "LDAPUserManager.h" #import "SOGoPermissions.h" #import "SOGoUser.h" +#import "SOGoUserManager.h" #import "SOGoUserFolder.h" diff --git a/SoObjects/SOGo/SOGoUserManager.h b/SoObjects/SOGo/SOGoUserManager.h new file mode 100644 index 000000000..f4e016de4 --- /dev/null +++ b/SoObjects/SOGo/SOGoUserManager.h @@ -0,0 +1,47 @@ +/* SOGoUserManager.h - this file is part of SOGo + * + * Copyright (C) 2008 Inverse inc. + * + * Author: Ludovic Marcotte + * + * 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 SOGOUSERMANAGER_H +#define SOGOUSERMANAGER_H + +#import + +@class NSArray; +@class NSMutableDictionary; +@class NSString; +@class NSTimer; +@class SOGoUser; + +@interface SOGoUserManager : NSObject +{ + @private + NSMutableDictionary *_cache; + NSTimer *_cleanupTimer; +} + ++ (id) sharedManager; +- (SOGoUser *) userWithLogin: (NSString *) theLogin + roles: (NSArray *) theRoles; + +@end + +#endif diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m new file mode 100644 index 000000000..39a1baa44 --- /dev/null +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -0,0 +1,189 @@ +/* SOGoUserManager.m - this file is part of SOGo + * + * Copyright (C) 2008 Inverse inc. + * + * Author: Ludovic Marcotte + * + * 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. + */ + +#include "SOGoUserManager.h" + +#include +#include +#include +#include +#include +#include + +#import + +#include "SOGoUser.h" + +// We define the default value for cleaning up cached +// users' preferences. This value should be relatively high +// to avoid useless database calls. +static NSTimeInterval cleanupInterval = 1800; + +@implementation SOGoUserManager + +- (id) init +{ + NSString *cleanupSetting; + + if ((self = [super init])) + { + _cache = [[NSMutableDictionary alloc] init]; + } + + // We register ourself for notifications + [[NSDistributedNotificationCenter defaultCenter] + addObserver: self + selector: @selector(_userDefaultsHaveChanged:) + name: @"SOGoUserDefaultsHaveChanged" + object: nil]; + + [[NSDistributedNotificationCenter defaultCenter] + addObserver: self + selector: @selector(_userSettingsHaveChanged:) + name: @"SOGoUserSettingsHaveChanged" + object: nil]; + + // We fire our timer that will cleanup cache entries + cleanupSetting = [[NSUserDefaults standardUserDefaults] + objectForKey: @"SOGoUserManagerCleanupInterval"]; + + if (cleanupSetting && [cleanupSetting doubleValue] > 0.0) + cleanupInterval = [cleanupSetting doubleValue]; + + _cleanupTimer = [NSTimer scheduledTimerWithTimeInterval: cleanupInterval + target: self + selector: @selector(_cleanupSources) + userInfo: nil + repeats: YES]; + [self logWithFormat: @"cleanup interval set every %f seconds", + cleanupInterval]; + + return self; +} + +- (void) dealloc +{ + [_cache release]; + [super dealloc]; +} + ++ (id) sharedManager +{ + static id sharedManager = nil; + + if (!sharedManager) + sharedManager = [self new]; + + return sharedManager; +} + +- (SOGoUser *) userWithLogin: (NSString *) theLogin + roles: (NSArray *) theRoles +{ + NSDate *cleanupDate; + SOGoUser *user; + id entry; + + cleanupDate = [[NSDate date] addTimeInterval: cleanupInterval]; + entry = [_cache objectForKey: theLogin]; + + if (!entry) + { + user = [SOGoUser userWithLogin: theLogin roles: theRoles]; + + entry = [NSMutableDictionary dictionaryWithObjectsAndKeys: user, @"user", cleanupDate, @"cleanupDate", nil]; + [_cache setObject: entry forKey: theLogin]; + } + else + { + [entry setObject: cleanupDate forKey: @"cleanupDate"]; + user = [entry objectForKey: @"user"]; + } + + [user setPrimaryRoles: theRoles]; + + return user; +} + +// +// Private stuff +// +- (void) _userDefaultsHaveChanged: (NSNotification *) theNotification +{ + NSMutableDictionary *d; + SOGoUser *user; + + d = [[NSMutableDictionary alloc] init]; + [d addEntriesFromDictionary: [theNotification userInfo]]; + + user = [self userWithLogin: [d objectForKey: @"uid"] + roles: nil]; + + [d removeObjectForKey: @"uid"]; + [user setUserDefaultsFromDictionary: d]; + [d release]; +} + +- (void) _userSettingsHaveChanged: (NSNotification *) theNotification +{ + NSMutableDictionary *d; + SOGoUser *user; + + d = [[NSMutableDictionary alloc] init]; + [d addEntriesFromDictionary: [theNotification userInfo]]; + + user = [self userWithLogin: [d objectForKey: @"uid"] + roles: nil]; + + [d removeObjectForKey: @"uid"]; + [user setUserSettingsFromDictionary: d]; + [d release]; +} + +- (void) _cleanupSources +{ + NSEnumerator *userIDs; + NSString *currentID; + NSDictionary *currentUser; + NSDate *now; + unsigned int count; + + now = [NSDate date]; + count = 0; + userIDs = [[_cache allKeys] objectEnumerator]; + + while ((currentID = [userIDs nextObject])) + { + currentUser = [_cache objectForKey: currentID]; + if ([now earlierDate: + [currentUser objectForKey: @"cleanupDate"]] == now) + { + [_cache removeObjectForKey: currentID]; + count++; + } + } + + if (count) + [self logWithFormat: @"cleaned %d users records from cache", count]; +} + +@end diff --git a/SoObjects/SOGo/SOGoWebAuthenticator.m b/SoObjects/SOGo/SOGoWebAuthenticator.m index 42699608f..23a0a3d61 100644 --- a/SoObjects/SOGo/SOGoWebAuthenticator.m +++ b/SoObjects/SOGo/SOGoWebAuthenticator.m @@ -40,6 +40,7 @@ #import "LDAPUserManager.h" #import "SOGoPermissions.h" #import "SOGoUser.h" +#import "SOGoUserManager.h" #import "SOGoWebAuthenticator.h" diff --git a/UI/Scheduler/UIxComponentEditor.h b/UI/Scheduler/UIxComponentEditor.h index 65f112513..75c05bf3c 100644 --- a/UI/Scheduler/UIxComponentEditor.h +++ b/UI/Scheduler/UIxComponentEditor.h @@ -1,6 +1,6 @@ /* UIxComponentEditor.h - this file is part of SOGo * - * Copyright (C) 2006 Inverse inc. + * Copyright (C) 2006-2008 Inverse inc. * * Author: Wolfgang Sourdeau * @@ -41,8 +41,8 @@ NSString *saveURL; NSMutableArray *calendarList; - NSMutableArray *organizerList; - NSDictionary *organizerIdentity; + //NSMutableArray *organizerList; + //NSDictionary *organizerIdentity; /* individual values */ NSCalendarDate *cycleUntilDate; diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index 1694af013..c50930322 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -1,6 +1,6 @@ /* UIxComponentEditor.m - this file is part of SOGo * - * Copyright (C) 2006 Inverse inc. + * Copyright (C) 2006-2008 Inverse inc. * * Author: Wolfgang Sourdeau * @@ -81,7 +81,7 @@ iRANGE(2); #define REPEAT(X) \ - (NSString *) repeat##X { return repeat##X; } \ -- (void) setRepeat##X: (NSString *) theValue { NSLog(@"setRepeat%d %@", X, theValue); ASSIGN(repeat##X, theValue); } \ +- (void) setRepeat##X: (NSString *) theValue { ASSIGN(repeat##X, theValue); } \ #define RANGE(X) \ - (NSString *) range##X { return range##X; } \ @@ -98,7 +98,7 @@ iRANGE(2); [self setIsCycleEndNever]; componentOwner = @""; organizer = nil; - organizerIdentity = nil; + //organizerIdentity = nil; attendeesNames = nil; attendeesUIDs = nil; attendeesEmails = nil; @@ -128,7 +128,7 @@ iRANGE(2); [title release]; [location release]; [organizer release]; - [organizerIdentity release]; + //[organizerIdentity release]; [comment release]; [priority release]; [categories release]; @@ -460,75 +460,101 @@ iRANGE(2); return [organizer mailAddress]; } -- (BOOL) canBeOrganizer -{ - NSString *owner; - SOGoObject *co; - SOGoUser *currentUser; - BOOL hasOrganizer; - SoSecurityManager *sm; +// - (BOOL) canBeOrganizer +// { +// NSString *owner; +// SOGoObject *co; +// SOGoUser *currentUser; +// BOOL hasOrganizer; +// SoSecurityManager *sm; - co = [self clientObject]; - owner = [co ownerInContext: context]; - currentUser = [context activeUser]; +// co = [self clientObject]; +// owner = [co ownerInContext: context]; +// currentUser = [context activeUser]; - hasOrganizer = ([[organizer value: 0] length] > 0); +// hasOrganizer = ([[organizer value: 0] length] > 0); - sm = [SoSecurityManager sharedSecurityManager]; +// sm = [SoSecurityManager sharedSecurityManager]; - return ([co isNew] - || (([owner isEqualToString: [currentUser login]] - || ![sm validatePermission: SOGoCalendarPerm_ModifyComponent - onObject: co - inContext: context]) - && (!hasOrganizer || [component userIsOrganizer: currentUser]))); -} +// return ([co isNew] +// || (([owner isEqualToString: [currentUser login]] +// || ![sm validatePermission: SOGoCalendarPerm_ModifyComponent +// onObject: co +// inContext: context]) +// && (!hasOrganizer || [component userIsOrganizer: currentUser]))); +// } - (BOOL) hasOrganizer { - return ([[organizer value: 0] length] && ![self canBeOrganizer]); -} + // We check if there's an organizer and if it's not ourself + NSString *value; -- (void) setOrganizerIdentity: (NSDictionary *) newOrganizerIdentity -{ - ASSIGN (organizerIdentity, newOrganizerIdentity); -} + value = [organizer rfc822Email]; -- (NSDictionary *) organizerIdentity -{ - NSArray *allIdentities; - NSEnumerator *identities; - NSDictionary *currentIdentity; - NSString *orgEmail; - - orgEmail = [organizer rfc822Email]; - if (!organizerIdentity) + if ([value length]) { - if ([orgEmail length]) - { - allIdentities = [[context activeUser] allIdentities]; - identities = [allIdentities objectEnumerator]; - while (!organizerIdentity - && ((currentIdentity = [identities nextObject]))) - if ([[currentIdentity objectForKey: @"email"] - caseInsensitiveCompare: orgEmail] - == NSOrderedSame) - ASSIGN (organizerIdentity, currentIdentity); - } + NSDictionary *currentIdentity; + NSEnumerator *identities; + NSArray *allIdentities; + + allIdentities = [[context activeUser] allIdentities]; + identities = [allIdentities objectEnumerator]; + currentIdentity = nil; + + while ((currentIdentity = [identities nextObject])) + if ([[currentIdentity objectForKey: @"email"] + caseInsensitiveCompare: value] + == NSOrderedSame) + return NO; + + return YES; } - return organizerIdentity; + return NO; + + //return ([[organizer value: 0] length] && ![self canBeOrganizer]); } -- (NSArray *) organizerList -{ - return [[context activeUser] allIdentities]; -} +//- (void) setOrganizerIdentity: (NSDictionary *) newOrganizerIdentity +//{ +// ASSIGN (organizerIdentity, newOrganizerIdentity); +//} -- (NSString *) itemOrganizerText -{ - return [item keysWithFormat: @"%{fullName} <%{email}>"]; -} +// - (NSDictionary *) organizerIdentity +// { +// NSArray *allIdentities; +// NSEnumerator *identities; +// NSDictionary *currentIdentity; +// NSString *orgEmail; + +// orgEmail = [organizer rfc822Email]; +// if (!organizerIdentity) +// { +// if ([orgEmail length]) +// { +// allIdentities = [[context activeUser] allIdentities]; +// identities = [allIdentities objectEnumerator]; +// while (!organizerIdentity +// && ((currentIdentity = [identities nextObject]))) +// if ([[currentIdentity objectForKey: @"email"] +// caseInsensitiveCompare: orgEmail] +// == NSOrderedSame) +// ASSIGN (organizerIdentity, currentIdentity); +// } +// } + +// return organizerIdentity; +// } + +//- (NSArray *) organizerList +//{ +// return [[context activeUser] allIdentities]; +//} + +//- (NSString *) itemOrganizerText +//{ +// return [item keysWithFormat: @"%{fullName} <%{email}>"]; +//} - (void) setAttendeesNames: (NSString *) newAttendeesNames { @@ -833,7 +859,7 @@ iRANGE(2); - (void) setComponentCalendar: (SOGoAppointmentFolder *) _componentCalendar { - ASSIGN (componentCalendar, _componentCalendar); + ASSIGN(componentCalendar, _componentCalendar); } /* priorities */ @@ -1286,13 +1312,38 @@ RANGE(2); NSString *owner, *login; BOOL isOwner, hasOrganizer, hasAttendees; - owner = [[self clientObject] ownerInContext: context]; + //owner = [[self clientObject] ownerInContext: context]; + owner = [componentCalendar ownerInContext: context]; login = [[context activeUser] login]; isOwner = [owner isEqualToString: login]; hasAttendees = ([[component attendees] count] > 0); organizerEmail = [[component organizer] email]; hasOrganizer = ([organizerEmail length] > 0); +#if 1 + ASSIGN (organizer, [iCalPerson elementWithTag: @"organizer"]); + [component setOrganizer: organizer]; + + if (hasAttendees) + { + SOGoUser *user; + id identity; + + user = [SOGoUser userWithLogin: owner roles: nil]; + identity = [user defaultIdentity]; + [organizer setCn: [identity objectForKey: @"fullName"]]; + [organizer setEmail: [identity objectForKey: @"email"]]; + + if (!isOwner) + { + NSString *currentEmail; + + currentEmail = [[[context activeUser] allEmails] objectAtIndex: 0]; + [organizer addAttribute: @"SENT-BY" + value: [NSString stringWithFormat: @"\"MAILTO:%@\"", currentEmail]]; + } + } +#else if (hasOrganizer) { if (isOwner && !hasAttendees) @@ -1311,6 +1362,7 @@ RANGE(2); [component setOrganizer: organizer]; } } +#endif } - (void) _handleCustomRRule: (iCalRecurrenceRule *) theRule diff --git a/UI/Templates/SchedulerUI/UIxComponentEditor.wox b/UI/Templates/SchedulerUI/UIxComponentEditor.wox index d7511c4d7..3c7434889 100644 --- a/UI/Templates/SchedulerUI/UIxComponentEditor.wox +++ b/UI/Templates/SchedulerUI/UIxComponentEditor.wox @@ -61,12 +61,12 @@ /> - + -->