diff --git a/SOPE/GDLContentStore/GCSAlarmsFolder.h b/SOPE/GDLContentStore/GCSAlarmsFolder.h index 21d57b9ba..f92d62973 100644 --- a/SOPE/GDLContentStore/GCSAlarmsFolder.h +++ b/SOPE/GDLContentStore/GCSAlarmsFolder.h @@ -48,15 +48,17 @@ - (NSArray *) recordsForEntriesFromDate: (NSCalendarDate *) fromDate toDate: (NSCalendarDate *) toDate; -- (void) writeRecordForEntryWithCName: (NSString *) cname - inCalendarAtPath: (NSString *) path - forUID: (NSString *) uid - recurrenceId: (NSCalendarDate *) recId - alarmNumber: (NSNumber *) alarmNbr - andAlarmDate: (NSCalendarDate *) alarmDate; +- (void)writeRecordForEntryWithCName:(NSString *)cname + inCalendarAtPath:(NSString *)path + forUID:(NSString *)uid + recurrenceId:(NSCalendarDate *)recId + alarmNumber:(NSNumber *)alarmNbr + andAlarmDate:(NSCalendarDate *)alarmDate + external:(BOOL)isExternal; -- (void) deleteRecordForEntryWithCName: (NSString *) cname - inCalendarAtPath: (NSString *) path; +- (void)deleteRecordForEntryWithCName:(NSString *)cname + inCalendarAtPath:(NSString *)path + external:(BOOL)isExternal; @end diff --git a/SOPE/GDLContentStore/GCSAlarmsFolder.m b/SOPE/GDLContentStore/GCSAlarmsFolder.m index 44e540308..8a7f761c2 100644 --- a/SOPE/GDLContentStore/GCSAlarmsFolder.m +++ b/SOPE/GDLContentStore/GCSAlarmsFolder.m @@ -228,8 +228,8 @@ static NSString *alarmsFolderURLString = nil; entity = [self _storeTableEntityForChannel: tc]; qualifier = [[EOSQLQualifier alloc] initWithEntity: entity qualifierFormat: - @"c_path='%@' AND c_name='%@'", - path, cname]; + @"c_path LIKE '%@%%' AND c_name='%@'", + [path asSafeSQLLikeString], cname]; [qualifier autorelease]; [context beginTransaction]; @@ -331,6 +331,7 @@ static NSString *alarmsFolderURLString = nil; recurrenceId: (NSCalendarDate *) recId alarmNumber: (NSNumber *) alarmNbr andAlarmDate: (NSCalendarDate *) alarmDate + external: (BOOL) isExternal { NSDictionary *record, *newRecord; NSException *error; @@ -343,22 +344,41 @@ static NSString *alarmsFolderURLString = nil; if (tc) { context = [tc adaptorContext]; - newRecord = [self _newRecordWithCName: cname - inCalendarAtPath: path - forUID: uid - recurrenceId: recId - alarmNumber: alarmNbr - andAlarmDate: alarmDate]; - record = [self recordForEntryWithCName: cname + if (!isExternal) + newRecord = [self _newRecordWithCName: cname + inCalendarAtPath: path + forUID: uid + recurrenceId: recId + alarmNumber: alarmNbr + andAlarmDate: alarmDate]; + else + newRecord = [self _newRecordWithCName: cname + inCalendarAtPath: [NSString stringWithFormat: @"EXTERNAL:%@", path] + forUID: uid + recurrenceId: recId + alarmNumber: alarmNbr + andAlarmDate: alarmDate]; + + if (!isExternal) + record = [self recordForEntryWithCName: cname inCalendarAtPath: path]; + else + record = [self recordForEntryWithCName: cname + inCalendarAtPath: @"EXTERNAL:"]; entity = [self _storeTableEntityForChannel: tc]; [context beginTransaction]; if (record) { - qualifier = [[EOSQLQualifier alloc] initWithEntity: entity - qualifierFormat: - @"c_path='%@' AND c_name='%@'", - path, cname]; + if (!isExternal) + qualifier = [[EOSQLQualifier alloc] initWithEntity: entity + qualifierFormat: + @"c_path='%@' AND c_name='%@'", + path, cname]; + else + qualifier = [[EOSQLQualifier alloc] initWithEntity: entity + qualifierFormat: + @"c_path LIKE 'EXTERNAL:%@%%' AND c_name='%@'", + [path asSafeSQLLikeString], cname]; [qualifier autorelease]; error = [tc updateRowX: newRecord describedByQualifier: qualifier]; } @@ -378,6 +398,7 @@ static NSString *alarmsFolderURLString = nil; - (void) deleteRecordForEntryWithCName: (NSString *) cname inCalendarAtPath: (NSString *) path + external: (BOOL) isExternal { EOAdaptorChannel *tc; EOAdaptorContext *context; @@ -390,10 +411,17 @@ static NSString *alarmsFolderURLString = nil; { context = [tc adaptorContext]; entity = [self _storeTableEntityForChannel: tc]; - qualifier = [[EOSQLQualifier alloc] initWithEntity: entity + if (!isExternal) + qualifier = [[EOSQLQualifier alloc] initWithEntity: entity qualifierFormat: - @"c_path='%@' AND c_name='%@'", + @"c_path = '%@' AND c_name='%@'", path, cname]; + else + qualifier = [[EOSQLQualifier alloc] initWithEntity: entity + qualifierFormat: + @"(c_path = '%@' OR c_path LIKE 'EXTERNAL:%%') AND c_name='%@'", + path, cname]; + [qualifier autorelease]; [context beginTransaction]; error = [tc deleteRowsDescribedByQualifierX: qualifier]; diff --git a/SoObjects/Appointments/SOGoEMailAlarmsManager.m b/SoObjects/Appointments/SOGoEMailAlarmsManager.m index 835a775c6..ca68b4a08 100644 --- a/SoObjects/Appointments/SOGoEMailAlarmsManager.m +++ b/SoObjects/Appointments/SOGoEMailAlarmsManager.m @@ -70,7 +70,7 @@ af = [[GCSFolderManager defaultFolderManager] alarmsFolder]; path = [[component container] ocsPath]; [af deleteRecordForEntryWithCName: [component nameInContainer] - inCalendarAtPath: path]; + inCalendarAtPath: path external: YES]; } - (iCalCalendar *) _lookupCalendarMatchingRecord: (NSDictionary *) record @@ -219,18 +219,57 @@ for (count = 0; count < max; count++) { record = [records objectAtIndex: count]; - alarm = [self _lookupAlarmMatchingRecord: record + + if (record && [record objectForKey: @"c_path"] && [[record objectForKey: @"c_path"] hasPrefix:@"EXTERNAL"]) { + alarm = [[iCalAlarm alloc] init]; + // [alarm autorelease]; + } else { + alarm = [self _lookupAlarmMatchingRecord: record withOwner: &owner withEntity: &entity]; + } if (alarm) { - container = [self _lookupContainerMatchingRecord: record]; - [alarms addObject: alarm]; - [metadata addObject: [NSDictionary dictionaryWithObjectsAndKeys: owner, @"owner", + if (record && [record objectForKey: @"c_path"] && [[record objectForKey: @"c_path"] hasPrefix:@"EXTERNAL"]) { + NSString *path; + NSArray *parts; + NSMutableDictionary *mRecord; + + mRecord = [NSMutableDictionary dictionaryWithDictionary: record]; + parts = [[mRecord objectForKey: @"c_path"] componentsSeparatedByString:@":"]; + path = [parts objectAtIndex: 1]; + [mRecord setObject: path forKey: @"c_path"]; + container = [self _lookupContainerMatchingRecord: mRecord]; + [self _extractOwner: &owner + fromPath: [mRecord objectForKey: @"c_path"]]; + + // alarm = [iCalAlarm alarmForEvent: self + // owner: owner + // action: reminderAction + // unit: reminderUnit + // quantity: reminderQuantity + // reference: reminderReference + // reminderRelation: reminderRelation + // emailAttendees: reminderEmailAttendees + // emailOrganizer: reminderEmailOrganizer]; + + [alarms addObject: alarm]; + + [metadata addObject: [NSDictionary dictionaryWithObjectsAndKeys: owner, @"owner", + mRecord, @"record", + container, @"container", + [NSNumber numberWithBool: YES], @"isExternal", + [[parts objectAtIndex: 2] componentsSeparatedByString: @","], @"externalEmailList", + nil]]; + } else { + container = [self _lookupContainerMatchingRecord: record]; + [alarms addObject: alarm]; + [metadata addObject: [NSDictionary dictionaryWithObjectsAndKeys: owner, @"owner", record, @"record", container, @"container", entity, @"entity", nil]]; + } } } diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.m b/SoObjects/Appointments/iCalEntityObject+SOGo.m index 26949f88a..3e83dec97 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.m @@ -52,6 +52,7 @@ NSCalendarDate *iCalDistantFuture = nil; NSNumber *iCalDistantFutureNumber = nil; +BOOL hasAttendeesAlarm; // As SOGo is single process, this shall not be an issue @implementation iCalEntityObject (SOGoExtensions) @@ -286,6 +287,7 @@ NSNumber *iCalDistantFutureNumber = nil; reminderRelation = [alarm objectForKey: @"relation"]; reminderEmailAttendees = [[alarm objectForKey: @"attendees"] boolValue]; reminderEmailOrganizer = [[alarm objectForKey: @"organizer"] boolValue]; + hasAttendeesAlarm = [[alarm objectForKey: @"attendees"] boolValue]; anAlarm = [iCalAlarm alarmForEvent: self owner: owner action: reminderAction @@ -688,7 +690,7 @@ NSNumber *iCalDistantFutureNumber = nil; forContainer: (id) theContainer nameInContainer: (NSString *) nameInContainer { - NSCalendarDate *nextAlarmDate; + NSCalendarDate *nextAlarmDate, *nextAlarmDateExternal; GCSAlarmsFolder *af; SOGoUser *alarmOwner; NSString *path; @@ -732,25 +734,29 @@ NSNumber *iCalDistantFutureNumber = nil; || ([webstatus caseInsensitiveCompare: @"TRIGGERED"] != NSOrderedSame)) nextAlarmDate = [anAlarm nextAlarmDate]; + nextAlarmDateExternal = nextAlarmDate; } else if ((anAlarm = [self firstEmailAlarm]) && af) { nextAlarmDate = [anAlarm nextAlarmDate]; + nextAlarmDateExternal = nextAlarmDate; email_alarm_number = [[self alarms] indexOfObject: anAlarm]; + // The email alarm is too old, let's just remove it if ([nextAlarmDate earlierDate: [NSDate date]] == nextAlarmDate || ![anAlarm userIsAttendee: alarmOwner]) - nextAlarmDate = nil; - else - { - [af writeRecordForEntryWithCName: nameInContainer - inCalendarAtPath: path - forUID: [self uid] - recurrenceId: nil - alarmNumber: [NSNumber numberWithInt: email_alarm_number] - andAlarmDate: nextAlarmDate]; - } + nextAlarmDate = nil; + else + { + [af writeRecordForEntryWithCName: nameInContainer + inCalendarAtPath: path + forUID: [self uid] + recurrenceId: nil + alarmNumber: [NSNumber numberWithInt: email_alarm_number] + andAlarmDate: nextAlarmDate + external: NO]; + } } } // Recurring event/task @@ -841,6 +847,7 @@ NSNumber *iCalDistantFutureNumber = nil; { nextAlarmDate = [NSDate dateWithTimeIntervalSince1970: c_nextalarm]; + nextAlarmDateExternal = nextAlarmDate; email_alarm_number = [[self alarms] indexOfObject: anAlarm]; if ([anAlarm userIsAttendee: alarmOwner]) @@ -849,7 +856,8 @@ NSNumber *iCalDistantFutureNumber = nil; forUID: [self uid] recurrenceId: [self recurrenceId] alarmNumber: [NSNumber numberWithInt: email_alarm_number] - andAlarmDate: nextAlarmDate]; + andAlarmDate: nextAlarmDate + external: NO]; else nextAlarmDate = nil; } @@ -858,6 +866,56 @@ NSNumber *iCalDistantFutureNumber = nil; } // for ( ... ) } // if (theContainer) } + + + // Add attendees external email addresses to the alarm. + int i; + SOGoUserManager *um; + iCalPerson *currentAttendee, *organizer; + NSArray *elements; + NSMutableArray *externalAttendees; + iCalAlarm *alarm; + SOGoUser *organizerUser; + + + alarm = [anAlarm parent]; + organizer = [alarm organizer]; + + if ([alarmOwner hasEmail: [organizer rfc822Email]]) { + if (hasAttendeesAlarm) { + externalAttendees = [[NSMutableArray alloc] init]; + um = [SOGoUserManager sharedUserManager]; + for (i = 0; i < [[alarm attendees] count]; i++) { + currentAttendee = [[alarm attendees] objectAtIndex: i]; + elements = [um fetchContactsMatching: [currentAttendee rfc822Email] inDomain: [alarmOwner domain]]; + if ([elements count] == 0) { + [externalAttendees addObject:[currentAttendee rfc822Email]]; + } + } + + if ([externalAttendees count] > 0) { + [af deleteRecordForEntryWithCName: nameInContainer + inCalendarAtPath: [theContainer ocsPath] external: YES]; + [af writeRecordForEntryWithCName: nameInContainer + inCalendarAtPath: [NSString stringWithFormat:@"%@:%@", path, [externalAttendees componentsJoinedByString: @","]] + forUID: [self uid] + recurrenceId: nil + alarmNumber: [NSNumber numberWithInt: email_alarm_number] + andAlarmDate: nextAlarmDateExternal + + external: YES]; + } + else + [af deleteRecordForEntryWithCName: nameInContainer + inCalendarAtPath: [theContainer ocsPath] external: YES]; + [externalAttendees release]; + } else { + [af deleteRecordForEntryWithCName: nameInContainer + inCalendarAtPath: [theContainer ocsPath] external: YES]; + } + + } + // End add attendees external email addresses to the alarm. } // Don't update c_nextalarm in the quick table if it's not an email alarm @@ -872,8 +930,12 @@ NSNumber *iCalDistantFutureNumber = nil; // Delete old email alarms if (!nextAlarmDate && email_alarm_number >= 0) - [af deleteRecordForEntryWithCName: nameInContainer - inCalendarAtPath: [theContainer ocsPath]]; + if (hasAttendeesAlarm) + [af deleteRecordForEntryWithCName: nameInContainer + inCalendarAtPath: [theContainer ocsPath] external: NO]; + else + [af deleteRecordForEntryWithCName: nameInContainer + inCalendarAtPath: [theContainer ocsPath] external: YES]; } } diff --git a/Tools/SOGoEAlarmsNotifier.m b/Tools/SOGoEAlarmsNotifier.m index 9062686bf..a825ae98f 100644 --- a/Tools/SOGoEAlarmsNotifier.m +++ b/Tools/SOGoEAlarmsNotifier.m @@ -23,6 +23,7 @@ #import #import #import +#import #import #import @@ -148,6 +149,27 @@ systemMessage: YES]; } +- (NSArray *) _buildInternalEmailsList: (NSArray *)metadata +{ + int i; + NSString *ownerId; + SOGoUser *owner; + NSMutableArray *results = [[NSMutableArray alloc] init]; + [results autorelease]; + + for (i = 0 ; i < [metadata count] ; i++) { + ownerId = [[metadata objectAtIndex: i] objectForKey: @"owner"]; + owner = [SOGoUser userWithLogin: ownerId]; + if (owner + && [owner primaryIdentity] + && [[owner primaryIdentity] objectForKey:@"email"]) { + [results addObject: [[[owner primaryIdentity] objectForKey:@"email"] lowercaseString]]; + } + } + return results; +} + + - (void) _processAlarm: (iCalAlarm *) alarm withOwner: (NSString *) ownerId andContainerPath: (NSString *) containerPath @@ -168,6 +190,10 @@ SOGoAppointmentFolder *folder; SOGoUserFolder *userFolder; + BOOL isOrganizer; + iCalPerson *person; + int i; + owner = [SOGoUser userWithLogin: ownerId]; mailer = [SOGoMailer mailerWithDomainDefaults: [owner domainDefaults]]; @@ -198,6 +224,15 @@ [p setApt: [alarm parent]]; [p setAttendees: [[alarm parent] attendees]]; + if ([owner primaryIdentity] + && [[owner primaryIdentity] objectForKey:@"email"] + && [p organizer] + && [[p organizer] rfc822Email] + && [[[[owner primaryIdentity] objectForKey:@"email"] lowercaseString] isEqualToString: [[[p organizer] rfc822Email] lowercaseString]]) + isOrganizer = YES; + else + isOrganizer = NO; + content = [[p getBody] dataUsingEncoding: NSUTF8StringEncoding]; subject = [p getSubject]; @@ -228,6 +263,7 @@ NSDictionary *d; NSMutableArray *metadata; NSString *credsFilename; + NSAutoreleasePool *pool; SOGoCredentialsFile *cf; SOGoEMailAlarmsManager *eaMgr; iCalEntityObject *entity; @@ -259,6 +295,7 @@ eaMgr = [NSClassFromString (@"SOGoEMailAlarmsManager") sharedEMailAlarmsManager]; + pool = [[NSAutoreleasePool alloc] init]; metadata = [[NSMutableArray alloc] init]; startDate = [NSCalendarDate calendarDate]; toDate = [startDate addYear: 0 month: 0 day: 0 @@ -270,12 +307,14 @@ toDate: toDate withMetadata: metadata]; + max = [alarms count]; - for (count = 0; count < max; count++) + for (count = 0; count < max; count++) { [self _processAlarm: [alarms objectAtIndex: count] withOwner: [[metadata objectAtIndex: count] objectForKey: @"owner"] andContainerPath: [[[metadata objectAtIndex: count] objectForKey: @"record"] objectForKey: @"c_path"]]; + } // We now update the next alarm date (if any, for recurring // events or tasks for example). This will also delete any email @@ -295,6 +334,7 @@ fm = [GCSFolderManager defaultFolderManager]; cm = [fm channelManager]; [cm releaseAllChannels]; + [pool release]; return YES; }