From 6e5d968de383c8ec4a46453088a8e96a3f7bf05e Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Wed, 22 Apr 2009 21:02:11 +0000 Subject: [PATCH] See ChangeLog Monotone-Parent: c4a5b31204ccd4c09e85262d9b5609b788a8380a Monotone-Revision: 9057f51730136ddbcf1d8e64c8029ea9a4e6c991 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2009-04-22T21:02:11 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 27 +++++ .../Appointments/SOGoAppointmentFolder.h | 3 + .../Appointments/SOGoAppointmentFolder.m | 30 +++++- SoObjects/Appointments/iCalEvent+SOGo.m | 49 +++------ SoObjects/Appointments/iCalToDo+SOGo.m | 46 +++++++- UI/Common/Dutch.lproj/Localizable.strings | 6 ++ UI/Common/English.lproj/Localizable.strings | 6 ++ UI/Common/French.lproj/Localizable.strings | 6 ++ UI/Common/German.lproj/Localizable.strings | 6 ++ UI/Common/Italian.lproj/Localizable.strings | 6 ++ UI/Common/Spanish.lproj/Localizable.strings | 6 ++ UI/Common/Welsh.lproj/Localizable.strings | 6 ++ UI/Scheduler/UIxAppointmentEditor.m | 17 +++ UI/Scheduler/UIxCalListingActions.m | 49 +++++---- UI/Scheduler/UIxTaskEditor.m | 61 ++++++++++- UI/Scheduler/product.plist | 5 + UI/WebServerResources/SchedulerUI.js | 9 +- UI/WebServerResources/generic.js | 102 ++++++++++++++++++ 18 files changed, 375 insertions(+), 65 deletions(-) diff --git a/ChangeLog b/ChangeLog index ea4e31396..a08db92b7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2009-04-22 Francis Lachapelle + + * UI/Scheduler/UIxCalListingActions.m ([UIxCalListingActions + -alarmsListAction]): only return the alarms active for the next 48 + hours and exclude any recurring event (for now). + + * UI/Scheduler/UIxAppointmentEditor.m ([UIxAppointmentEditor + -viewAction]): if the form parameter "resetAlarm" is true, set the + property X-WebStatus to "triggered" in the event's alarm. + + * UI/Scheduler/UIxTaskEditor.m ([UIxTaskEditor -viewAction]): new + method that returns some of the task properties. Currently only + used for the alarms. Behavior is similar to UIxAppointmentEditor. + + * SoObjects/Appointments/iCalEvent+SOGo.m ([iCalEvent + -quickRecord]): don't update the c_nextalarm field if the event is + recurrent or if the property X-WebStatus is set to "triggered" in + the alarm's trigger definition. + + * SoObjects/Appointments/iCalToDo+SOGo.m ([iCalToDo + -quickRecord]): idem. + + * SoObjects/Appointments/SOGoAppointmentFolder.m + ([SOGoAppointmentFolder -fetchAlarmInfosFrom:to:]): new method + that returns attributes of events and todos for which there's an + active alarm within the next 48 hours. + 2009-04-21 Ludovic Marcotte * Added Welsh translation - patches from the diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.h b/SoObjects/Appointments/SOGoAppointmentFolder.h index 30fa2e907..7c0f3f9ce 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.h +++ b/SoObjects/Appointments/SOGoAppointmentFolder.h @@ -96,6 +96,9 @@ - (NSArray *) fetchFreeBusyInfosFrom: (NSCalendarDate *) _startDate to: (NSCalendarDate *) _endDate; +- (NSArray *) fetchAlarmInfosFrom: (NSNumber *) _startUTCDate + to: (NSNumber *) _endUTCDate; + /* URL generation */ - (NSString *) baseURLForAptWithUID: (NSString *) _uid diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index fa634dc72..7d2ca1eed 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2007-2008 Inverse inc. + Copyright (C) 2007-2009 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of OpenGroupware.org. @@ -2290,6 +2290,34 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir includeProtectedInformation: NO]; } +- (NSArray *) fetchAlarmInfosFrom: (NSNumber *) _startUTCDate + to: (NSNumber *) _endUTCDate +{ + static NSArray *nameFields = nil; + EOQualifier *qualifier; + GCSFolder *folder; + NSArray *records; + NSString *sql; + + if (!nameFields) + nameFields = [[NSArray alloc] initWithObjects: @"c_name", @"c_nextalarm", @"c_iscycle", nil]; + + folder = [self ocsFolder]; + if (!folder) + { + [self errorWithFormat:@"(%s): missing folder for fetch!", + __PRETTY_FUNCTION__]; + return nil; + } + + sql = [NSString stringWithFormat: @"((c_nextalarm <= %u) AND (c_nextalarm >= %u)) OR ((c_nextalarm > 0) AND (c_enddate > %u))", + [_endUTCDate unsignedIntValue], [_startUTCDate unsignedIntValue], [_startUTCDate unsignedIntValue]]; + qualifier = [EOQualifier qualifierWithQualifierFormat: sql]; + records = [folder fetchFields: nameFields matchingQualifier: qualifier]; + + return records; +} + /* URL generation */ - (NSString *) baseURLForAptWithUID: (NSString *)_uid diff --git a/SoObjects/Appointments/iCalEvent+SOGo.m b/SoObjects/Appointments/iCalEvent+SOGo.m index 8fd95df50..767eb9df8 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.m +++ b/SoObjects/Appointments/iCalEvent+SOGo.m @@ -206,14 +206,18 @@ if ([self hasAlarms]) { // We currently have the following limitations for alarms: + // - the event must not be recurrent; // - only the first alarm is considered; // - the alarm's action must be of type DISPLAY; - // - the alarm's trigger value type must be DURATION. + // - the alarm's trigger value type must be DURATION; + // + // Morever, we don't update the quick table if the property X-WebStatus + // of the trigger is set to "triggered". iCalAlarm *anAlarm; iCalTrigger *aTrigger; NSCalendarDate *relationDate; - NSString *relation; + NSString *relation, *webstatus; NSTimeInterval anInterval; anAlarm = [[self alarms] objectAtIndex: 0]; @@ -222,45 +226,22 @@ anInterval = [[aTrigger value] durationAsTimeInterval]; if ([[anAlarm action] caseInsensitiveCompare: @"DISPLAY"] == NSOrderedSame && - [[aTrigger valueType] caseInsensitiveCompare: @"DURATION"] == NSOrderedSame) + [[aTrigger valueType] caseInsensitiveCompare: @"DURATION"] == NSOrderedSame && + ![self isRecurrent]) { - if ([self isRecurrent]) + webstatus = [aTrigger value: 0 ofAttribute: @"x-webstatus"]; + if (!webstatus || + [webstatus caseInsensitiveCompare: @"TRIGGERED"] != NSOrderedSame) { - if ([self isStillRelevant]) - { - NSArray *occurrences; - NSCalendarDate *now, *later; - NGCalendarDateRange *range; - - // We only compute the next occurrence of the repeating event - // for the next 48 hours - now = [NSCalendarDate calendarDate]; - later = [now addTimeInterval: (60*60*48)]; - range = [NGCalendarDateRange calendarDateRangeWithStartDate: now - endDate: later]; - occurrences = [self recurrenceRangesWithinCalendarDateRange: range]; - if ([occurrences count] > 0) - { - range = [occurrences objectAtIndex: 0]; - if ([relation caseInsensitiveCompare: @"END"] == NSOrderedSame) - relationDate = [range endDate]; - else - relationDate = [range startDate]; - } - } - } - else - { - // Event is not reccurent if ([relation caseInsensitiveCompare: @"END"] == NSOrderedSame) relationDate = endDate; else relationDate = startDate; + + // Compute the next alarm date with respect to the reference date + if ([relationDate isNotNull]) + nextAlarmDate = [relationDate addTimeInterval: anInterval]; } - - // Compute the next alarm date with respect to the reference date - if ([relationDate isNotNull]) - nextAlarmDate = [relationDate addTimeInterval: anInterval]; } } if ([nextAlarmDate isNotNull]) diff --git a/SoObjects/Appointments/iCalToDo+SOGo.m b/SoObjects/Appointments/iCalToDo+SOGo.m index 18df52e53..f54509b3e 100644 --- a/SoObjects/Appointments/iCalToDo+SOGo.m +++ b/SoObjects/Appointments/iCalToDo+SOGo.m @@ -30,7 +30,10 @@ #import #import +#import #import +#import +#import #import "iCalRepeatableEntityObject+SOGo.h" @@ -41,7 +44,7 @@ - (NSMutableDictionary *) quickRecord { NSMutableDictionary *row; - NSCalendarDate *startDate, *dueDate; + NSCalendarDate *startDate, *dueDate, *nextAlarmDate; NSArray *attendees; NSString *uid, *title, *location, *status; NSNumber *sequence; @@ -55,6 +58,7 @@ startDate = [self startDate]; dueDate = [self due]; + nextAlarmDate = nil; uid = [self uid]; title = [self summary]; if (![title isNotNull]) @@ -167,6 +171,46 @@ [row setObject:partstates forKey: @"c_partstates"]; [partstates release]; + if ([self hasAlarms]) + { + // We currently have the following limitations for alarms: + // - the component must not be recurrent; + // - only the first alarm is considered; + // - the alarm's action must be of type DISPLAY; + // - the alarm's trigger value type must be DURATION; + // + // Morever, we don't update the quick table if the property X-WebStatus + // of the trigger is set to "triggered". + + iCalAlarm *anAlarm; + iCalTrigger *aTrigger; + NSString *webstatus; + NSTimeInterval anInterval; + + anAlarm = [[self alarms] objectAtIndex: 0]; + aTrigger = [anAlarm trigger]; + anInterval = [[aTrigger value] durationAsTimeInterval]; + + if ([[anAlarm action] caseInsensitiveCompare: @"DISPLAY"] == NSOrderedSame && + [[aTrigger valueType] caseInsensitiveCompare: @"DURATION"] == NSOrderedSame && + ![self isRecurrent]) + { + webstatus = [aTrigger value: 0 ofAttribute: @"x-webstatus"]; + if (!webstatus || + [webstatus caseInsensitiveCompare: @"TRIGGERED"] != NSOrderedSame) + { + // Compute the next alarm date with respect to the due date + if ([dueDate isNotNull]) + nextAlarmDate = [dueDate addTimeInterval: anInterval]; + } + } + } + if ([nextAlarmDate isNotNull]) + [row setObject: [NSNumber numberWithInt: [nextAlarmDate timeIntervalSince1970]] + forKey: @"c_nextalarm"]; + else + [row setObject: [NSNumber numberWithInt: 0] forKey: @"c_nextalarm"]; + return row; } diff --git a/UI/Common/Dutch.lproj/Localizable.strings b/UI/Common/Dutch.lproj/Localizable.strings index da32d7e46..8d97435b7 100644 --- a/UI/Common/Dutch.lproj/Localizable.strings +++ b/UI/Common/Dutch.lproj/Localizable.strings @@ -41,3 +41,9 @@ "You are not allowed to access this module or this system. Please contact your system administrator." = "U hebt geen toegang tot deze module of dit systeem. Neem contact op met uw systeem beheerder."; "You don't have the required privileges to perform the operation." = "Voor deze actie heeft u niet de benodigde rechten."; + +/* alarms */ +"Reminder:" = "Alarm:"; +"Start:" = "Begin:"; +"Due Date:" = "Verloopdatum:"; +"Location:" = "Plaats:"; diff --git a/UI/Common/English.lproj/Localizable.strings b/UI/Common/English.lproj/Localizable.strings index 4f4f9d657..d7c958422 100644 --- a/UI/Common/English.lproj/Localizable.strings +++ b/UI/Common/English.lproj/Localizable.strings @@ -46,3 +46,9 @@ = "You are not allowed to access this module or this system. Please contact your system administrator."; "You don't have the required privileges to perform the operation." = "You don't have the required privileges to perform the operation."; + +/* alarms */ +"Reminder:" = "Reminder:"; +"Start:" = "Start:"; +"Due Date:" = "Due Date:"; +"Location:" = "Location:"; \ No newline at end of file diff --git a/UI/Common/French.lproj/Localizable.strings b/UI/Common/French.lproj/Localizable.strings index 27bec1b26..f28114db1 100644 --- a/UI/Common/French.lproj/Localizable.strings +++ b/UI/Common/French.lproj/Localizable.strings @@ -43,3 +43,9 @@ = "Vous n'êtes pas autorisé à accéder à ce module ou ce système. Veuillez contacter votre administrateur système."; "You don't have the required privileges to perform the operation." = "Vous n'avez pas les privilèges requis pour compléter l'opération."; + +/* alarms */ +"Reminder:" = "Rappel :"; +"Start:" = "Début :"; +"Due Date:" = "Échéance :"; +"Location:" = "Lieu :"; diff --git a/UI/Common/German.lproj/Localizable.strings b/UI/Common/German.lproj/Localizable.strings index a1223318e..cfb08b136 100644 --- a/UI/Common/German.lproj/Localizable.strings +++ b/UI/Common/German.lproj/Localizable.strings @@ -46,3 +46,9 @@ = "You are not allowed to access this module or this system. Please contact your system administrator."; "You don't have the required privileges to perform the operation." = "Sie haben nicht die benötigte Berechtigung für diesen Befehl."; + +/* alarms */ +"Reminder:" = "Alarm:"; +"Start:" = "Beginn:"; +"Due Date:" = "Fällig:"; +"Location:" = "Ort:"; diff --git a/UI/Common/Italian.lproj/Localizable.strings b/UI/Common/Italian.lproj/Localizable.strings index 23143a2a8..f099f281c 100644 --- a/UI/Common/Italian.lproj/Localizable.strings +++ b/UI/Common/Italian.lproj/Localizable.strings @@ -40,3 +40,9 @@ = "Non sei abilitato ad accedere a questo modulo. Contatta il tuo amministratore di sistema."; "You don't have the required privileges to perform the operation." = "Non disponi dei privilegi richiesti per eseguire questa operazione."; + +/* alarms */ +"Reminder:" = "Promemoria:"; +"Start:" = "Inizio:"; +"Due Date:" = "Scadenza:"; +"Location:" = "Luogo:"; diff --git a/UI/Common/Spanish.lproj/Localizable.strings b/UI/Common/Spanish.lproj/Localizable.strings index eb3f6f921..c1ca2d0ea 100644 --- a/UI/Common/Spanish.lproj/Localizable.strings +++ b/UI/Common/Spanish.lproj/Localizable.strings @@ -50,3 +50,9 @@ = "You are not allowed to access this module or this system. Please contact your system administrator."; "You don't have the required privileges to perform the operation." = "You don't have the required privileges to perform the operation."; + +/* alarms */ +"Reminder:" = "Recordatorio:"; +"Start:" = "Desde:"; +"Due Date:" = "Vencimiento:"; +"Location:" = "Lugar:"; \ No newline at end of file diff --git a/UI/Common/Welsh.lproj/Localizable.strings b/UI/Common/Welsh.lproj/Localizable.strings index 5a95279d8..436e370d6 100644 --- a/UI/Common/Welsh.lproj/Localizable.strings +++ b/UI/Common/Welsh.lproj/Localizable.strings @@ -46,3 +46,9 @@ = "Nid oes gennych caniatad mynediad i'r modiwl hwn na'r system hwn. Cysylltwch a'r Gweinyddwr Systemau os gwelwch yn dda."; "You don't have the required privileges to perform the operation." = "Nid oes gennych y breintiau gofynnol i berfformio'r gweithrediad."; + +/* alarms */ +"Reminder:" = "Atgoffa:"; +"Start:" = "Dechrau:"; +"Due Date:" = "Dyddiad dyledus:"; +"Location:" = "Lleoliad:"; \ No newline at end of file diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index a9bf600e7..8df1550ef 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -34,9 +34,11 @@ #import #import +#import #import #import #import +#import #import #import @@ -394,6 +396,7 @@ SOGoUser *user; SOGoCalendarComponent *co; iCalEvent *master; + BOOL resetAlarm; signed int daylightOffset; [self event]; @@ -406,6 +409,19 @@ [eventDate setTimeZone: timeZone]; co = [self clientObject]; + resetAlarm = [[[context request] formValueForKey: @"resetAlarm"] boolValue]; + if (resetAlarm && [event hasAlarms] && ![event hasRecurrenceRules]) + { + iCalAlarm *anAlarm; + iCalTrigger *aTrigger; + + anAlarm = [[event alarms] objectAtIndex: 0]; + aTrigger = [anAlarm trigger]; + [aTrigger setValue: 0 ofAttribute: @"x-webstatus" to: @"triggered"]; + + [co saveComponent: event]; + } + if ([co isNew] && [co isKindOfClass: [SOGoAppointmentOccurence class]]) { // This is a new exception in a recurrent event -- compute the daylight @@ -421,6 +437,7 @@ } } data = [NSDictionary dictionaryWithObjectsAndKeys: + [event tag], @"component", [dateFormatter formattedDate: eventDate], @"startDate", [dateFormatter formattedTime: eventDate], @"startTime", ([event hasRecurrenceRules]? @"1": @"0"), @"isReccurent", diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index 6d3d2ee7e..fda10b9bf 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -43,6 +43,7 @@ #import #import #import +#import #import @@ -367,7 +368,7 @@ static NSArray *tasksFields = nil; // // We return: // -// {complete Event ID (full path) => Fire date (UTC)} +// [[calendar name (full path), complete Event ID (full path), Fire date (UTC)], ..] // // Called when each module is loaded or whenever a calendar component is created, modified, deleted // or whenever there's a {un}subscribe to a calendar. @@ -375,11 +376,9 @@ static NSArray *tasksFields = nil; // Workflow : // // - for ALL subscribed and ACTIVE calendars -// - returns alarms for which the (event end date > browserTime) OR (browserTime < c_nextalarm) -// - if it's a recurring event and that condition isn't met -// - set date range from X (now) until Y (now+2 days) -// - compute the c_nextalarm and if it is met, store it in c_nextalarm -// +// - returns alarms that will occur in the next 48 hours or the non-triggered alarms +// for non-completed events +// - recurring events are currently ignored // - (WOResponse *) alarmsListAction { @@ -388,9 +387,11 @@ static NSArray *tasksFields = nil; NSMutableArray *allAlarms; NSEnumerator *folders; WOResponse *response; - int browserTime; + unsigned int browserTime, laterTime; + // We look for alarms in the next 48 hours browserTime = [[[context request] formValueForKey: @"browserTime"] intValue]; + laterTime = browserTime + 60*60*48; clientObject = [self clientObject]; allAlarms = [NSMutableArray array]; @@ -399,34 +400,32 @@ static NSArray *tasksFields = nil; { if ([currentFolder isActive]) { - NSDictionary *entry;; + NSDictionary *entry; NSArray *alarms; - int i, v; + BOOL isCycle; + int i; - // Let's compute everything +2 days in case we hit recurring components - alarms = [currentFolder fetchFields: [NSArray arrayWithObjects: @"c_nextalarm", @"c_iscycle", nil] - from: [NSCalendarDate date] - to: [[NSCalendarDate date] dateByAddingYears: 0 months: 0 days: 2 hours: 0 minutes: 0 seconds: 0] - title: nil - component: nil - additionalFilters: nil - includeProtectedInformation: NO]; + alarms = [currentFolder fetchAlarmInfosFrom: [NSNumber numberWithInt: browserTime] + to: [NSNumber numberWithInt: laterTime]]; + for (i = 0; i < [alarms count]; i++) { entry = [alarms objectAtIndex: i]; - v = [[entry objectForKey: @"c_nextalarm"] intValue]; - - if (([[entry objectForKey: @"c_enddate"] intValue] > browserTime) || - browserTime < v) + isCycle = [[entry objectForKey: @"c_iscycle"] boolValue]; + + if (!isCycle) { - [allAlarms addObject: [NSDictionary dictionaryWithObject: [entry objectForKey: @"c_nextalarm"] - forKey: [entry objectForKey: @"c_name"]]]; + [allAlarms addObject: [NSArray arrayWithObjects: + [currentFolder nameInContainer], + [entry objectForKey: @"c_name"], + [entry objectForKey: @"c_nextalarm"], + nil]]; } } } } - - + + response = [self responseWithStatus: 200]; [response appendContentString: [allAlarms jsonRepresentation]]; diff --git a/UI/Scheduler/UIxTaskEditor.m b/UI/Scheduler/UIxTaskEditor.m index 9fc892248..73e002f26 100644 --- a/UI/Scheduler/UIxTaskEditor.m +++ b/UI/Scheduler/UIxTaskEditor.m @@ -20,6 +20,8 @@ * Boston, MA 02111-1307, USA. */ +#import + #import #import #import @@ -28,12 +30,16 @@ #import #import +#import #import -#import #import +#import +#import +#import #import #import +#import #import #import @@ -377,6 +383,59 @@ // return [self jsCloseWithRefreshMethod: @"refreshTasks()"]; // } +- (id ) viewAction +{ + WOResponse *result; + NSDictionary *data; + NSCalendarDate *startDate, *dueDate; + NSTimeZone *timeZone; + SOGoDateFormatter *dateFormatter; + SOGoUser *user; + BOOL resetAlarm; + + [self todo]; + + result = [self responseWithStatus: 200]; + user = [context activeUser]; + timeZone = [user timeZone]; + dateFormatter = [user dateFormatterInContext: context]; + startDate = [todo startDate]; + [startDate setTimeZone: timeZone]; + dueDate = [todo due]; + [dueDate setTimeZone: timeZone]; + + resetAlarm = [[[context request] formValueForKey: @"resetAlarm"] boolValue]; + if (resetAlarm && [todo hasAlarms] && ![todo hasRecurrenceRules]) + { + iCalAlarm *anAlarm; + iCalTrigger *aTrigger; + SOGoCalendarComponent *co; + + anAlarm = [[todo alarms] objectAtIndex: 0]; + aTrigger = [anAlarm trigger]; + [aTrigger setValue: 0 ofAttribute: @"x-webstatus" to: @"triggered"]; + + co = [self clientObject]; + [co saveComponent: todo]; + } + + data = [NSDictionary dictionaryWithObjectsAndKeys: + [todo tag], @"component", + (startDate? [dateFormatter formattedDate: startDate] : @""), @"startDate", + (startDate? [dateFormatter formattedTime: startDate] : @""), @"startTime", + (dueDate? [dateFormatter formattedDate: dueDate] : @""), @"dueDate", + (dueDate? [dateFormatter formattedTime: dueDate] : @""), @"dueTime", + ([todo hasRecurrenceRules]? @"1": @"0"), @"isReccurent", + [todo summary], @"summary", + [todo location], @"location", + [todo comment], @"description", + nil]; + + [result appendContentString: [data jsonRepresentation]]; + + return result; +} + - (BOOL) shouldTakeValuesFromRequest: (WORequest *) request inContext: (WOContext*) context { diff --git a/UI/Scheduler/product.plist b/UI/Scheduler/product.plist index 12426d2c9..51e5afa06 100644 --- a/UI/Scheduler/product.plist +++ b/UI/Scheduler/product.plist @@ -216,6 +216,11 @@ }; }; methods = { + view = { + protectedBy = "ViewAllComponent"; + pageName = "UIxTaskEditor"; + actionName = "view"; + }; edit = { protectedBy = "ViewAllComponent"; pageName = "UIxTaskEditor"; diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index 7a2bbe1ca..0d55952cb 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -1081,7 +1081,7 @@ function _drawMonthCalendarEvents(events, eventsData) { function newMonthEventDIV(eventRep, event) { var eventText; - if (event[7]) + if (event[7]) // all-day event eventText = event[3]; else eventText = eventRep.starthour + " - " + event[3]; @@ -1209,7 +1209,7 @@ function _loadEventHref(href) { document.eventsListAjaxRequest.aborted = true; document.eventsListAjaxRequest.abort(); } - var url = ApplicationBaseURL + "/" + href; + var url = ApplicationBaseURL + href; document.eventsListAjaxRequest = triggerAjaxRequest(url, eventsListCallback, href); @@ -1279,7 +1279,9 @@ function refreshEvents() { titleSearch = "&search=" + escape(value.utf8encode()); else titleSearch = ""; - + + refreshAlarms(); + return _loadEventHref("eventslist?asc=" + sorting["ascending"] + "&sort=" + sorting["attribute"] + "&day=" + currentDay @@ -1288,6 +1290,7 @@ function refreshEvents() { } function refreshTasks() { + refreshAlarms(); return _loadTasksHref("taskslist?show-completed=" + showCompletedTasks); } diff --git a/UI/WebServerResources/generic.js b/UI/WebServerResources/generic.js index 1e8fe63eb..a8cb2549a 100644 --- a/UI/WebServerResources/generic.js +++ b/UI/WebServerResources/generic.js @@ -38,6 +38,10 @@ var lastClickedRow = -1; // logArea = null; var allDocumentElements = null; +// Alarms +var nextAlarm = null; +var Alarms = new Array(); + // Ajax requests counts var activeAjaxRequests = 0; var removeFolderRequestCount = 0; @@ -1215,6 +1219,103 @@ function initTabs() { } } +function reverseSortByAlarmTime(a, b) { + var x = parseInt(a[2]); + var y = parseInt(b[2]); + return (y - x); +} + +function refreshAlarms() { + var url; + var now = new Date(); + var utc = Math.floor(now.getTime()/1000); + + if (document.alarmsListAjaxRequest) { + document.alarmsListAjaxRequest.aborted = true; + document.alarmsListAjaxRequest.abort(); + } + url = UserFolderURL + "Calendar/alarmslist?browserTime=" + utc; + document.alarmsListAjaxRequest + = triggerAjaxRequest(url, refreshAlarmsCallback); + + return true; +} + +function refreshAlarmsCallback(http) { + if (http.readyState == 4 + && http.status == 200) { + document.alarmsListAjaxRequest = null; + + if (http.responseText.length > 0) { + Alarms = http.responseText.evalJSON(true); + Alarms.sort(reverseSortByAlarmTime); + triggerNextAlarm(); + } + } + else + log ("refreshAlarmsCallback Ajax error"); +} + +function triggerNextAlarm() { + if (Alarms.length > 0) { + var next = Alarms.pop(); + var now = new Date(); + var utc = Math.floor(now.getTime()/1000); + var url = next[0] + '/' + next[1]; + var alarmTime = parseInt(next[2]); + var delay = alarmTime; + if (alarmTime > 0) delay -= utc; + var d = new Date(alarmTime*1000); + log ("now = " + now.toUTCString()); + log ("next event " + url + " in " + delay + " seconds (on " + d.toUTCString() + ")"); + showAlarm.delay(delay, url); + } +} + +function showAlarm(url) { + url = UserFolderURL + "Calendar/" + url + "/view?resetAlarm=yes"; + if (document.viewAlarmAjaxRequest) { + document.viewAlarmAjaxRequest.aborted = true; + document.viewAlarmAjaxRequest.abort(); + } + document.viewAlarmAjaxRequest = triggerAjaxRequest(url, showAlarmCallback); +} + +function showAlarmCallback(http) { + if (http.readyState == 4 + && http.status == 200) { + if (http.responseText.length) { + var data = http.responseText.evalJSON(true); + var msg = clabels["Reminder:"] + " " + data["summary"] + "\n"; + if (data["startDate"]) { + msg += clabels["Start:"] + " " + data["startDate"]; + if (parseInt(data["isAllDay"]) == 0) + msg += " - " + data["startTime"]; + msg += "\n"; + } + if (data["dueDate"]) { + msg += clabels["Due Date:"] + " " + data["dueDate"]; + if (data["dueTime"]) + msg += " - " + data["dueTime"]; + msg += "\n"; + } + if (data["location"].length) + msg += "\n" + clabels["Location:"] + " " + data["location"]; + if (data["description"].length) + msg += "\n\n" + data["description"]; + + window.alert(msg); + } + else + log("showAlarmCallback ajax error: no data received"); + } + else { + log("showAlarmCallback ajax error (" + http.status + "): " + http.url); + } + + triggerNextAlarm(); +} + function initMenus() { var menus = getMenus(); if (menus) { @@ -1435,6 +1536,7 @@ function onLoadHandler(event) { queryParameters = parseQueryParameters('' + window.location); if (!$(document.body).hasClassName("popup")) { initLogConsole(); + refreshAlarms(); } initCriteria(); configureSearchField();