mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-06-23 02:44:18 +00:00
feat(calendar): Add alarms for external users (wip)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSProcessInfo.h>
|
||||
#import <Foundation/NSUserDefaults.h>
|
||||
#import <Foundation/NSAutoreleasePool.h>
|
||||
|
||||
#import <NGExtensions/NGHashMap.h>
|
||||
#import <NGExtensions/NGQuotedPrintableCoding.h>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user