(feat) first pass at support for recurring events/tasks email alarms (fixes #1053)

pull/225/head
Ludovic Marcotte 2016-11-02 18:16:45 -04:00
parent 8cadf5cbbd
commit 78045b14be
17 changed files with 210 additions and 245 deletions

3
NEWS
View File

@ -1,6 +1,9 @@
3.2.2 (2016-11-DD) 3.2.2 (2016-11-DD)
------------------ ------------------
New features
- [core] support repetitive email alarms on tasks and events (#1053)
Bug fixes Bug fixes
- [web] fixed mail settings persistence when sorting by arrival date - [web] fixed mail settings persistence when sorting by arrival date

View File

@ -1,8 +1,6 @@
/* GCSAlarmsFolder.h - this file is part of $PROJECT_NAME_HERE$ /* GCSAlarmsFolder.h - this file is part of $PROJECT_NAME_HERE$
* *
* Copyright (C) 2010 Inverse inc. * Copyright (C) 2010-2016 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
* This file is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -60,8 +58,6 @@
- (void) deleteRecordForEntryWithCName: (NSString *) cname - (void) deleteRecordForEntryWithCName: (NSString *) cname
inCalendarAtPath: (NSString *) path; inCalendarAtPath: (NSString *) path;
- (void) deleteRecordsForEntriesUntilDate: (NSCalendarDate *) date;
@end @end
#endif /* GCSALARMSFOLDER_H */ #endif /* GCSALARMSFOLDER_H */

View File

@ -1,6 +1,6 @@
/* GCSAlarmsFolder.m - this file is part of SOGo /* GCSAlarmsFolder.m - this file is part of SOGo
* *
* Copyright (C) 2010-2014 Inverse inc. * Copyright (C) 2010-2016 Inverse inc.
* *
* This file is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -409,36 +409,4 @@ static NSString *alarmsFolderURLString = nil;
} }
} }
- (void) deleteRecordsForEntriesUntilDate: (NSCalendarDate *) date
{
EOAdaptorChannel *tc;
EOAdaptorContext *context;
EOEntity *entity;
EOSQLQualifier *qualifier;
NSException *error;
tc = [self _acquireStoreChannel];
if (tc)
{
context = [tc adaptorContext];
entity = [self _storeTableEntityForChannel: tc];
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
qualifierFormat:
@"c_alarm_date <= %d",
(int) [date timeIntervalSince1970]];
[qualifier autorelease];
[context beginTransaction];
error = [tc deleteRowsDescribedByQualifierX: qualifier];
if (error)
{
[context rollbackTransaction];
[self errorWithFormat:@"%s: cannot delete record: %@",
__PRETTY_FUNCTION__, error];
}
else
[context commitTransaction];
[self _releaseChannel: tc];
}
}
@end @end

View File

@ -1017,7 +1017,21 @@ andAttribute: (EOAttribute *)_attribute
|| *_baseVersion == [storedVersion unsignedIntValue]) || *_baseVersion == [storedVersion unsignedIntValue])
{ {
/* extract quick info */ /* extract quick info */
quickRow = [theComponent performSelector: @selector(quickRecordFromContent:container:) withObject: _content withObject: theContainer]; NSMethodSignature *aSignature;
NSInvocation *anInvocation;
SEL aSelector;
aSelector = @selector(quickRecordFromContent:container:nameInContainer:);
aSignature = [[theComponent class] instanceMethodSignatureForSelector: aSelector];
anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
[anInvocation setSelector: aSelector];
[anInvocation setTarget: theComponent];
[anInvocation setArgument:&_content atIndex: 2];
[anInvocation setArgument:&theContainer atIndex: 3];
[anInvocation setArgument:&_name atIndex: 4];
[anInvocation invoke];
[anInvocation getReturnValue: &quickRow];
if (quickRow) if (quickRow)
{ {
[quickRow setObject:_name forKey:@"c_name"]; [quickRow setObject:_name forKey:@"c_name"];

View File

@ -954,7 +954,7 @@ static Class iCalEventK = nil;
// alarm defined. // alarm defined.
if ([[component alarms] count]) if ([[component alarms] count])
{ {
alarm = [component firstDisplayOrAudioAlarm]; alarm = [component firstSupportedAlarm];
[row setObject: [NSNumber numberWithInt: [[alarm nextAlarmDate] timeIntervalSince1970]] [row setObject: [NSNumber numberWithInt: [[alarm nextAlarmDate] timeIntervalSince1970]]
forKey: @"c_nextalarm"]; forKey: @"c_nextalarm"];
} }
@ -1030,7 +1030,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
(endDate && [dateRange containsDate: endDate])) (endDate && [dateRange containsDate: endDate]))
{ {
// We must pass nil to :container here in order to avoid re-entrancy issues. // We must pass nil to :container here in order to avoid re-entrancy issues.
newRecord = [self _fixupRecord: [component quickRecordFromContent: nil container: nil]]; newRecord = [self _fixupRecord: [component quickRecordFromContent: nil container: nil nameInContainer: nil]];
[ma replaceObjectAtIndex: recordIndex withObject: newRecord]; [ma replaceObjectAtIndex: recordIndex withObject: newRecord];
} }
else else
@ -1047,7 +1047,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
// The recurrence id of the exception is outside the date range; // The recurrence id of the exception is outside the date range;
// simply add the exception to the records array. // simply add the exception to the records array.
// We must pass nil to :container here in order to avoid re-entrancy issues. // We must pass nil to :container here in order to avoid re-entrancy issues.
newRecord = [self _fixupRecord: [component quickRecordFromContent: nil container: nil]]; newRecord = [self _fixupRecord: [component quickRecordFromContent: nil container: nil nameInContainer: nil]];
if ([newRecord objectForKey: @"startDate"] && [newRecord objectForKey: @"endDate"]) { if ([newRecord objectForKey: @"startDate"] && [newRecord objectForKey: @"endDate"]) {
newRecordRange = [NGCalendarDateRange newRecordRange = [NGCalendarDateRange
calendarDateRangeWithStartDate: [newRecord objectForKey: @"startDate"] calendarDateRangeWithStartDate: [newRecord objectForKey: @"startDate"]

View File

@ -683,15 +683,6 @@
newUid = [newUid substringToIndex: [newUid length]-4]; newUid = [newUid substringToIndex: [newUid length]-4];
[newObject setUid: newUid]; [newObject setUid: newUid];
} }
if ([[SOGoSystemDefaults sharedSystemDefaults] enableEMailAlarms])
{
SOGoEMailAlarmsManager *eaMgr;
eaMgr = [SOGoEMailAlarmsManager sharedEMailAlarmsManager];
[eaMgr handleAlarmsInCalendar: [newObject parent]
fromComponent: self];
}
} }
- (NSException *) saveCalendar: (iCalCalendar *) newCalendar - (NSException *) saveCalendar: (iCalCalendar *) newCalendar
@ -1459,23 +1450,6 @@
return nil; return nil;
} }
- (id) PUTAction: (WOContext *) localContext
{
if ([[SOGoSystemDefaults sharedSystemDefaults] enableEMailAlarms])
{
SOGoEMailAlarmsManager *eaMgr;
iCalCalendar *putCalendar;
WORequest *rq;
rq = [localContext request];
putCalendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]];
eaMgr = [SOGoEMailAlarmsManager sharedEMailAlarmsManager];
[eaMgr handleAlarmsInCalendar: putCalendar
fromComponent: self];
}
return [super PUTAction: localContext];
}
// /* Overriding this method dramatically speeds up PROPFIND request, but may // /* Overriding this method dramatically speeds up PROPFIND request, but may
// otherwise be a bad idea... Wait and see. */ // otherwise be a bad idea... Wait and see. */

View File

@ -1,6 +1,6 @@
/* SOGoEMailAlarmsManager.h - this file is part of SOGo /* SOGoEMailAlarmsManager.h - this file is part of SOGo
* *
* Copyright (C) 2010-2014 Inverse inc. * Copyright (C) 2010-2016 Inverse inc.
* *
* This file is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -42,8 +42,7 @@
/* fetch and cleanup */ /* fetch and cleanup */
- (NSArray *) scheduledAlarmsFromDate: (NSCalendarDate *) fromDate - (NSArray *) scheduledAlarmsFromDate: (NSCalendarDate *) fromDate
toDate: (NSCalendarDate *) toDate toDate: (NSCalendarDate *) toDate
withOwners: (NSMutableArray **) owners; withMetadata: (NSMutableArray *) metadata;
- (void) deleteAlarmsUntilDate: (NSCalendarDate *) untilDate;
@end @end

View File

@ -1,6 +1,6 @@
/* SOGoEMailAlarmsManager.m - this file is part of SOGo /* SOGoEMailAlarmsManager.m - this file is part of SOGo
* *
* Copyright (C) 2010-2014 Inverse inc. * Copyright (C) 2010-2016 Inverse inc.
* *
* This file is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -35,87 +35,11 @@
#import <GDLContentStore/GCSFolderManager.h> #import <GDLContentStore/GCSFolderManager.h>
#import "SOGoAppointmentFolder.h" #import "SOGoAppointmentFolder.h"
#import "SOGoAppointmentFolders.h"
#import "SOGoCalendarComponent.h" #import "SOGoCalendarComponent.h"
#import "SOGoEMailAlarmsManager.h" #import "SOGoEMailAlarmsManager.h"
@interface iCalEntityObject (SOGoAlarmsExtension)
- (void) findSoonerEMailAlarmAfterDate: (NSCalendarDate *) refDate
inAlarm: (iCalAlarm **) alarm;
@end
@interface iCalCalendar (SOGoAlarmsExtension)
- (void) findSoonerEMailAlarmAfterDate: (NSCalendarDate *) refDate
inAlarm: (iCalAlarm **) alarm;
@end
@implementation iCalEntityObject (SOGoAlarmsExtension)
- (void) findSoonerEMailAlarmAfterDate: (NSCalendarDate *) refDate
inAlarm: (iCalAlarm **) alarm
{
int count, max;
NSArray *alarms;
iCalAlarm *currentAlarm;
NSString *action;
NSCalendarDate *soonerDate, *testDate;
soonerDate = [*alarm nextAlarmDate];
alarms = [self alarms];
max = [alarms count];
for (count = 0; count < max; count++)
{
currentAlarm = [alarms objectAtIndex: count];
action = [[currentAlarm action] uppercaseString];
if ([action isEqualToString: @"EMAIL"])
{
testDate = [currentAlarm nextAlarmDate];
if (testDate
&& [refDate earlierDate: testDate] == refDate
&& (!soonerDate
|| [soonerDate earlierDate: testDate] == testDate))
{
*alarm = currentAlarm;
soonerDate = testDate;
}
}
}
}
@end
@implementation iCalCalendar (SOGoAlarmsExtension)
- (void) findSoonerEMailAlarmAfterDate: (NSCalendarDate *) refDate
inAlarm: (iCalAlarm **) alarm
{
int count, max;
iCalEntityObject *child;
BOOL done;
/* Here we only search for email alarms in the first event. This
should be fixed sometime. */
done = NO;
max = [children count];
for (count = 0; !done && count < max; count++)
{
child = [children objectAtIndex: count];
if ([child isKindOfClass: [iCalEvent class]]
|| [child isKindOfClass: [iCalToDo class]])
{
[child findSoonerEMailAlarmAfterDate: refDate inAlarm: alarm];
done = YES;
}
}
}
@end
@implementation SOGoEMailAlarmsManager @implementation SOGoEMailAlarmsManager
+ (id) sharedEMailAlarmsManager + (id) sharedEMailAlarmsManager
@ -128,50 +52,11 @@
return sharedEMailAlarmsManager; return sharedEMailAlarmsManager;
} }
- (void) _writeAlarmCoordinates: (iCalAlarm *) alarm //
fromComponent: (SOGoCalendarComponent *) component // This method is called SOGoCalendarCompoent: -prepareDelete when
{ // a component is being deleted. We of course remove all associated
GCSAlarmsFolder *af; // when an event or task is being deleted.
NSString *path, *cname; //
iCalEntityObject *entity;
int alarmNbr;
af = [[GCSFolderManager defaultFolderManager] alarmsFolder];
path = [[component container] ocsPath];
cname = [component nameInContainer];
if (alarm)
{
entity = [alarm parent];
alarmNbr = [[entity alarms] indexOfObject: alarm];
[af writeRecordForEntryWithCName: cname
inCalendarAtPath: path
forUID: [entity uid]
recurrenceId: [entity recurrenceId]
alarmNumber: [NSNumber numberWithInt: alarmNbr]
andAlarmDate: [alarm nextAlarmDate]];
}
else
[af deleteRecordForEntryWithCName: cname
inCalendarAtPath: path];
}
- (void) handleAlarmsInCalendar: (iCalCalendar *) calendar
fromComponent: (SOGoCalendarComponent *) component
{
iCalAlarm *alarm;
NSCalendarDate *now;
now = [NSCalendarDate calendarDate];
alarm = nil;
[calendar findSoonerEMailAlarmAfterDate: now
inAlarm: &alarm];
[self _writeAlarmCoordinates: alarm
fromComponent: component];
}
- (void) deleteAlarmsFromComponent: (SOGoCalendarComponent *) component - (void) deleteAlarmsFromComponent: (SOGoCalendarComponent *) component
{ {
GCSAlarmsFolder *af; GCSAlarmsFolder *af;
@ -246,19 +131,52 @@
*owner = [parts objectAtIndex: 2]; *owner = [parts objectAtIndex: 2];
} }
- (void) _extractContainer: (NSString **) container
fromPath: (NSString *) path
{
NSArray *parts;
parts = [path componentsSeparatedByString: @"/"];
if ([parts count] > 4)
*container = [parts objectAtIndex: 4];
}
- (SOGoAppointmentFolder *) _lookupContainerMatchingRecord: (NSDictionary *) record
{
SOGoAppointmentFolders *folders;
NSString *container, *owner;
SOGoUserFolder *userFolder;
SOGoUser *user;
WOContext *context;
[self _extractOwner: &owner fromPath: [record objectForKey: @"c_path"]];
[self _extractContainer: &container fromPath: [record objectForKey: @"c_path"]];
user = [SOGoUser userWithLogin: owner];
userFolder = [SOGoUserFolder objectWithName: owner inContainer: nil];
context = [WOContext context];
[context setActiveUser: user];
folders = [userFolder lookupName: @"Calendar"
inContext: context
acquire: NO];
return [folders lookupName: container
inContext: context
acquire: NO];
}
- (iCalAlarm *) _lookupAlarmMatchingRecord: (NSDictionary *) record - (iCalAlarm *) _lookupAlarmMatchingRecord: (NSDictionary *) record
withOwner: (NSString **) owner withOwner: (NSString **) owner
withEntity: (iCalEntityObject **) entity
{ {
iCalCalendar *calendar; iCalCalendar *calendar;
iCalEntityObject *entity;
iCalAlarm *alarm; iCalAlarm *alarm;
NSArray *alarms; NSArray *alarms;
int alarmNbr; int alarmNbr;
calendar = [self _lookupCalendarMatchingRecord: record]; calendar = [self _lookupCalendarMatchingRecord: record];
entity = [self _lookupEntityMatchingRecord: record inCalendar: calendar]; *entity = [self _lookupEntityMatchingRecord: record inCalendar: calendar];
alarmNbr = [[record objectForKey: @"c_alarm_number"] intValue]; alarmNbr = [[record objectForKey: @"c_alarm_number"] intValue];
alarms = [entity alarms]; alarms = [*entity alarms];
if (alarmNbr < [alarms count]) if (alarmNbr < [alarms count])
{ {
alarm = [alarms objectAtIndex: alarmNbr]; alarm = [alarms objectAtIndex: alarmNbr];
@ -276,8 +194,10 @@
- (NSArray *) scheduledAlarmsFromDate: (NSCalendarDate *) fromDate - (NSArray *) scheduledAlarmsFromDate: (NSCalendarDate *) fromDate
toDate: (NSCalendarDate *) toDate toDate: (NSCalendarDate *) toDate
withOwners: (NSMutableArray **) owners withMetadata: (NSMutableArray *) metadata
{ {
SOGoAppointmentFolder *container;
iCalEntityObject *entity;
GCSAlarmsFolder *af; GCSAlarmsFolder *af;
iCalAlarm *alarm; iCalAlarm *alarm;
NSMutableArray *alarms; NSMutableArray *alarms;
@ -290,31 +210,25 @@
records = [af recordsForEntriesFromDate: fromDate toDate: toDate]; records = [af recordsForEntriesFromDate: fromDate toDate: toDate];
max = [records count]; max = [records count];
alarms = [NSMutableArray arrayWithCapacity: max]; alarms = [NSMutableArray arrayWithCapacity: max];
if (owners)
*owners = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++) for (count = 0; count < max; count++)
{ {
record = [records objectAtIndex: count]; record = [records objectAtIndex: count];
alarm = [self _lookupAlarmMatchingRecord: record alarm = [self _lookupAlarmMatchingRecord: record
withOwner: &owner]; withOwner: &owner
withEntity: &entity];
if (alarm) if (alarm)
{ {
container = [self _lookupContainerMatchingRecord: record];
[alarms addObject: alarm]; [alarms addObject: alarm];
if (owners) [metadata addObject: [NSDictionary dictionaryWithObjectsAndKeys: owner, @"owner",
[*owners addObject: owner]; record, @"record",
container, @"container",
entity, @"entity",
nil]];
} }
} }
return alarms; return alarms;
} }
- (void) deleteAlarmsUntilDate: (NSCalendarDate *) untilDate
{
GCSAlarmsFolder *af;
af = [[GCSFolderManager defaultFolderManager] alarmsFolder];
[af deleteRecordsForEntriesUntilDate: untilDate];
}
@end @end

View File

@ -80,7 +80,8 @@
} }
- (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent - (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
container: (id) theContainer container: (id) theContainer
nameInContainer: (NSString *) nameInContainer
{ {
CardGroup *element; CardGroup *element;
NSArray *elements; NSArray *elements;
@ -97,7 +98,7 @@
element = nil; element = nil;
} }
return [(id)element quickRecordFromContent: theContent container: theContainer]; return [(id)element quickRecordFromContent: theContent container: theContainer nameInContainer: nameInContainer];
} }
@end @end

View File

@ -1,6 +1,6 @@
/* iCalEntityObject+SOGo.h - this file is part of SOGo /* iCalEntityObject+SOGo.h - this file is part of SOGo
* *
* Copyright (C) 2007-2015 Inverse inc. * Copyright (C) 2007-2016 Inverse inc.
* *
* This file is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -65,13 +65,15 @@ extern NSNumber *iCalDistantFutureNumber;
forAllDay: (BOOL) allDay; forAllDay: (BOOL) allDay;
- (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent - (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
container: (id) theContainer; container: (id) theContainer
nameInContainer: (NSString *) nameInContainer;
- (iCalAlarm *) firstSupportedAlarm; - (iCalAlarm *) firstSupportedAlarm;
- (iCalAlarm *) firstDisplayOrAudioAlarm; - (iCalAlarm *) firstDisplayOrAudioAlarm;
- (void) updateNextAlarmDateInRow: (NSMutableDictionary *) row - (void) updateNextAlarmDateInRow: (NSMutableDictionary *) row
forContainer: (id) theContainer; forContainer: (id) theContainer
nameInContainer: (NSString *) nameInContainer;
@end @end

View File

@ -1,6 +1,6 @@
/* iCalEntityObject+SOGo.m - this file is part of SOGo /* iCalEntityObject+SOGo.m - this file is part of SOGo
* *
* Copyright (C) 2007-2015 Inverse inc. * Copyright (C) 2007-2016 Inverse inc.
* *
* This file is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -23,6 +23,9 @@
#import <Foundation/NSValue.h> #import <Foundation/NSValue.h>
#import <Foundation/NSTimeZone.h> #import <Foundation/NSTimeZone.h>
#import <GDLContentStore/GCSAlarmsFolder.h>
#import <GDLContentStore/GCSFolderManager.h>
#import <NGCards/iCalTrigger.h> #import <NGCards/iCalTrigger.h>
#import <NGCards/iCalRepeatableEntityObject.h> #import <NGCards/iCalRepeatableEntityObject.h>
#import <NGCards/NSString+NGCards.h> #import <NGCards/NSString+NGCards.h>
@ -37,6 +40,7 @@
#import <SOGo/NSArray+Utilities.h> #import <SOGo/NSArray+Utilities.h>
#import <SOGo/SOGoSource.h> #import <SOGo/SOGoSource.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoUser.h> #import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h> #import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserManager.h> #import <SOGo/SOGoUserManager.h>
@ -554,6 +558,7 @@ NSNumber *iCalDistantFutureNumber = nil;
- (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent - (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
container: (id) theContainer container: (id) theContainer
nameInContainer: (NSString *) nameInContainer
{ {
[self subclassResponsibility: _cmd]; [self subclassResponsibility: _cmd];
@ -612,7 +617,6 @@ NSNumber *iCalDistantFutureNumber = nil;
} }
return createdByData; return createdByData;
;
} }
// //
@ -659,10 +663,45 @@ NSNumber *iCalDistantFutureNumber = nil;
return nil; return nil;
} }
- (iCalAlarm *) firstEmailAlarm
{
iCalAlarm *anAlarm;
NSArray *alarms;
int i;
alarms = [self alarms];
for (i = 0; i < [alarms count]; i++)
{
anAlarm = [[self alarms] objectAtIndex: i];
if ([[anAlarm action] caseInsensitiveCompare: @"EMAIL"] == NSOrderedSame)
return anAlarm;
}
return nil;
}
- (void) updateNextAlarmDateInRow: (NSMutableDictionary *) row - (void) updateNextAlarmDateInRow: (NSMutableDictionary *) row
forContainer: (id) theContainer forContainer: (id) theContainer
nameInContainer: (NSString *) nameInContainer
{ {
NSCalendarDate *nextAlarmDate; NSCalendarDate *nextAlarmDate;
GCSAlarmsFolder *af;
NSString *path;
if ([[SOGoSystemDefaults sharedSystemDefaults] enableEMailAlarms])
{
af = [[GCSFolderManager defaultFolderManager] alarmsFolder];
path = [theContainer ocsPath];
}
else
{
af = nil;
path = nil;
}
nextAlarmDate = nil; nextAlarmDate = nil;
@ -680,7 +719,7 @@ NSNumber *iCalDistantFutureNumber = nil;
if (![(id)self isRecurrent]) if (![(id)self isRecurrent])
{ {
anAlarm = [self firstDisplayOrAudioAlarm]; anAlarm = [self firstDisplayOrAudioAlarm];
if (anAlarm) if ((anAlarm = [self firstDisplayOrAudioAlarm]))
{ {
webstatus = [[anAlarm trigger] value: 0 ofAttribute: @"x-webstatus"]; webstatus = [[anAlarm trigger] value: 0 ofAttribute: @"x-webstatus"];
if (!webstatus if (!webstatus
@ -688,10 +727,27 @@ NSNumber *iCalDistantFutureNumber = nil;
!= NSOrderedSame)) != NSOrderedSame))
nextAlarmDate = [anAlarm nextAlarmDate]; nextAlarmDate = [anAlarm nextAlarmDate];
} }
else else if ((anAlarm = [self firstEmailAlarm]) && af)
{ {
// TODO: handle email alarms here nextAlarmDate = [anAlarm nextAlarmDate];
}
// The email alarm is too old, let's just remove it
if ([nextAlarmDate earlierDate: [NSDate date]] == nextAlarmDate)
nextAlarmDate = nil;
else
{
int alarmNbr;
alarmNbr = [[self alarms] indexOfObject: anAlarm];
[af writeRecordForEntryWithCName: nameInContainer
inCalendarAtPath: path
forUID: [self uid]
recurrenceId: nil
alarmNumber: [NSNumber numberWithInt: alarmNbr]
andAlarmDate: nextAlarmDate];
}
}
} }
// Recurring event/task // Recurring event/task
else else
@ -765,22 +821,30 @@ NSNumber *iCalDistantFutureNumber = nil;
if ([[o alarms] count]) if ([[o alarms] count])
{ {
anAlarm = [self firstDisplayOrAudioAlarm]; if ((anAlarm = [self firstDisplayOrAudioAlarm]))
if (anAlarm)
{ {
webstatus = [[anAlarm trigger] value: 0 ofAttribute: @"x-webstatus"]; webstatus = [[anAlarm trigger] value: 0 ofAttribute: @"x-webstatus"];
if (!webstatus if (!webstatus
|| ([webstatus caseInsensitiveCompare: @"TRIGGERED"] || ([webstatus caseInsensitiveCompare: @"TRIGGERED"]
!= NSOrderedSame)) != NSOrderedSame))
v = delta; v = delta;
nextAlarmDate = [NSDate dateWithTimeIntervalSince1970: [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue]]; nextAlarmDate = [NSDate dateWithTimeIntervalSince1970: [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue]];
} }
else else if ((anAlarm = [self firstEmailAlarm]) && af)
{ {
// TODO: handle email alarms here int alarmNbr;
}
} nextAlarmDate = [NSDate dateWithTimeIntervalSince1970: [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue]];
alarmNbr = [[self alarms] indexOfObject: anAlarm];
[af writeRecordForEntryWithCName: nameInContainer
inCalendarAtPath: path
forUID: [self uid]
recurrenceId: [self recurrenceId]
alarmNumber: [NSNumber numberWithInt: alarmNbr]
andAlarmDate: nextAlarmDate];
}
}
} }
} // for ( ... ) } // for ( ... )
} // if (theContainer) } // if (theContainer)
@ -791,7 +855,14 @@ NSNumber *iCalDistantFutureNumber = nil;
[row setObject: [NSNumber numberWithInt: [nextAlarmDate timeIntervalSince1970]] [row setObject: [NSNumber numberWithInt: [nextAlarmDate timeIntervalSince1970]]
forKey: @"c_nextalarm"]; forKey: @"c_nextalarm"];
else else
[row setObject: [NSNumber numberWithInt: 0] forKey: @"c_nextalarm"]; {
[row setObject: [NSNumber numberWithInt: 0] forKey: @"c_nextalarm"];
// Delete old email alarms
if (af)
[af deleteRecordForEntryWithCName: nameInContainer
inCalendarAtPath: [theContainer ocsPath]];
}
} }
@end @end

View File

@ -68,6 +68,7 @@
// //
- (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent - (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
container: (id) theContainer container: (id) theContainer
nameInContainer: (NSString *) nameInContainer
{ {
NSMutableDictionary *row; NSMutableDictionary *row;
NSCalendarDate *startDate, *endDate; NSCalendarDate *startDate, *endDate;
@ -240,7 +241,7 @@
[partstates release]; [partstates release];
/* handle alarms */ /* handle alarms */
[self updateNextAlarmDateInRow: row forContainer: theContainer]; [self updateNextAlarmDateInRow: row forContainer: theContainer nameInContainer: nameInContainer];
/* handle categories */ /* handle categories */
categories = [self categories]; categories = [self categories];

View File

@ -220,6 +220,7 @@
- (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent - (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
container: (id) theContainer container: (id) theContainer
nameInContainer: (NSString *) nameInContainer
{ {
NSMutableDictionary *row; NSMutableDictionary *row;
NSCalendarDate *startDate, *dueDate, *completed; NSCalendarDate *startDate, *dueDate, *completed;
@ -350,7 +351,7 @@
[partstates release]; [partstates release];
/* handle alarms */ /* handle alarms */
[self updateNextAlarmDateInRow: row forContainer: theContainer]; [self updateNextAlarmDateInRow: row forContainer: theContainer nameInContainer: nameInContainer];
categories = [self categories]; categories = [self categories];
if ([categories count] > 0) if ([categories count] > 0)

View File

@ -898,6 +898,7 @@ convention:
- (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent - (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
container: (id) theContainer container: (id) theContainer
nameInContainer: (NSString *) nameInContainer
{ {
NSMutableDictionary *fields; NSMutableDictionary *fields;
CardElement *element; CardElement *element;

View File

@ -1,6 +1,6 @@
/* NGVCard+SOGo.m - this file is part of SOGo /* NGVCard+SOGo.m - this file is part of SOGo
* *
* Copyright (C) 2009-2014 Inverse inc. * Copyright (C) 2009-2016 Inverse inc.
* *
* This file is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -69,6 +69,8 @@
- (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent - (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
container: (id) theContainer container: (id) theContainer
nameInContainer: (NSString *) nameInContainer
{ {
NSMutableDictionary *fields; NSMutableDictionary *fields;
NSString *value; NSString *value;

View File

@ -1,6 +1,6 @@
/* SOGoEAlarmsNotifier.m - this file is part of SOGo /* SOGoEAlarmsNotifier.m - this file is part of SOGo
* *
* Copyright (C) 2011-2014 Inverse inc. * Copyright (C) 2011-2016 Inverse inc.
* *
* This file is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -31,12 +31,13 @@
#import <NGCards/iCalAlarm.h> #import <NGCards/iCalAlarm.h>
#import "SOGo/SOGoCredentialsFile.h"
#import <SOGo/NSCalendarDate+SOGo.h> #import <SOGo/NSCalendarDate+SOGo.h>
#import <SOGo/NSString+Utilities.h> #import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoCredentialsFile.h>
#import <SOGo/SOGoMailer.h> #import <SOGo/SOGoMailer.h>
#import <SOGo/SOGoProductLoader.h> #import <SOGo/SOGoProductLoader.h>
#import <SOGo/SOGoUser.h> #import <SOGo/SOGoUser.h>
#import <Appointments/iCalEntityObject+SOGo.h>
#import <Appointments/iCalPerson+SOGo.h> #import <Appointments/iCalPerson+SOGo.h>
#import <Appointments/SOGoEMailAlarmsManager.h> #import <Appointments/SOGoEMailAlarmsManager.h>
@ -179,12 +180,15 @@
- (BOOL) run - (BOOL) run
{ {
SOGoEMailAlarmsManager *eaMgr;
SOGoCredentialsFile *cf;
NSCalendarDate *startDate, *toDate; NSCalendarDate *startDate, *toDate;
NSArray *alarms; SOGoEMailAlarmsManager *eaMgr;
NSMutableArray *owners; NSMutableArray *metadata;
iCalEntityObject *entity;
SOGoCredentialsFile *cf;
NSString *credsFilename; NSString *credsFilename;
NSDictionary *d;
NSArray *alarms;
int count, max; int count, max;
if ([[NSUserDefaults standardUserDefaults] stringForKey: @"h"]) if ([[NSUserDefaults standardUserDefaults] stringForKey: @"h"])
@ -211,6 +215,7 @@
eaMgr = [NSClassFromString (@"SOGoEMailAlarmsManager") eaMgr = [NSClassFromString (@"SOGoEMailAlarmsManager")
sharedEMailAlarmsManager]; sharedEMailAlarmsManager];
metadata = [[NSMutableArray alloc] init];
startDate = [NSCalendarDate calendarDate]; startDate = [NSCalendarDate calendarDate];
toDate = [startDate addYear: 0 month: 0 day: 0 toDate = [startDate addYear: 0 month: 0 day: 0
hour: 0 minute: 0 hour: 0 minute: 0
@ -219,13 +224,26 @@
hour: 0 minute: -5 hour: 0 minute: -5
second: 0] second: 0]
toDate: toDate toDate: toDate
withOwners: &owners]; withMetadata: metadata];
max = [alarms count]; max = [alarms count];
for (count = 0; count < max; count++) for (count = 0; count < max; count++)
[self _processAlarm: [alarms objectAtIndex: count] [self _processAlarm: [alarms objectAtIndex: count]
withOwner: [owners objectAtIndex: count]]; withOwner: [[metadata objectAtIndex: count] objectForKey: @"owner"]];
// We now update the next alarm date (if any, for recurring
// events or tasks for example). This will also delete any emai
// alarms that are no longer relevant
max = [metadata count];
for (count = 0; count < max; count++)
{
d = [metadata objectAtIndex: count];
entity = [d objectForKey: @"entity"];
[entity quickRecordFromContent: nil
container: [d objectForKey: @"container"]
nameInContainer: [[d objectForKey: @"record"] objectForKey: @"c_name"]];
}
[eaMgr deleteAlarmsUntilDate: toDate];
return YES; return YES;
} }

View File

@ -552,7 +552,7 @@ static NSArray *reminderValues = nil;
alarmData = nil; alarmData = nil;
if ([component hasAlarms]) if ([component hasAlarms])
{ {
anAlarm = [component firstSupportedAlarm]; anAlarm = [component firstDisplayOrAudioAlarm];
trigger = [anAlarm trigger]; trigger = [anAlarm trigger];
if (![[trigger valueType] length] || [[trigger valueType] caseInsensitiveCompare: @"DURATION"] == NSOrderedSame) if (![[trigger valueType] length] || [[trigger valueType] caseInsensitiveCompare: @"DURATION"] == NSOrderedSame)
{ {