mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-08-19 21:53:19 +00:00
Support for repetitive alarms and tasks
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/* UIxAppointmentEditor.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2013 Inverse inc.
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* UIxAppointmentEditor.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2013 Inverse inc.
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
*
|
||||
* 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
|
||||
@@ -23,6 +23,7 @@
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGObjWeb/SoObject.h>
|
||||
#import <NGObjWeb/SoPermissions.h>
|
||||
@@ -31,6 +32,7 @@
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||
#import <NGExtensions/NGCalendarDateRange.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import <NGCards/iCalAlarm.h>
|
||||
@@ -49,8 +51,10 @@
|
||||
#import <SOGo/SOGoPermissions.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
#import <Appointments/iCalCalendar+SOGo.h>
|
||||
#import <Appointments/iCalEntityObject+SOGo.h>
|
||||
#import <Appointments/iCalPerson+SOGo.h>
|
||||
#import <Appointments/iCalRepeatableEntityObject+SOGo.h>
|
||||
#import <Appointments/SOGoAppointmentFolder.h>
|
||||
#import <Appointments/SOGoAppointmentObject.h>
|
||||
#import <Appointments/SOGoAppointmentOccurence.h>
|
||||
@@ -470,6 +474,7 @@
|
||||
SOGoUserDefaults *ud;
|
||||
SOGoCalendarComponent *co;
|
||||
NSString *created_by;
|
||||
iCalAlarm *anAlarm;
|
||||
|
||||
BOOL resetAlarm;
|
||||
unsigned int snoozeAlarm;
|
||||
@@ -492,12 +497,26 @@
|
||||
componentCalendar = [componentCalendar container];
|
||||
[componentCalendar retain];
|
||||
}
|
||||
|
||||
created_by = [event createdBy];
|
||||
|
||||
if ([event hasAlarms] && ![event hasRecurrenceRules])
|
||||
// resetAlarm=yes is set only when we are about to show the alarm popup in the Web
|
||||
// interface of SOGo. See generic.js for details. snoozeAlarm=X is called when the
|
||||
// user clicks on "Snooze for" X minutes, when the popup is being displayed.
|
||||
// If either is set to yes, we must find the right alarm.
|
||||
resetAlarm = [[[context request] formValueForKey: @"resetAlarm"] boolValue];
|
||||
snoozeAlarm = [[[context request] formValueForKey: @"snoozeAlarm"] intValue];
|
||||
|
||||
if (resetAlarm || snoozeAlarm)
|
||||
{
|
||||
iCalAlarm *anAlarm;
|
||||
resetAlarm = [[[context request] formValueForKey: @"resetAlarm"] boolValue];
|
||||
snoozeAlarm = [[[context request] formValueForKey: @"snoozeAlarm"] intValue];
|
||||
iCalEvent *master;
|
||||
|
||||
master = event;
|
||||
[componentCalendar findEntityForClosestAlarm: &event
|
||||
timezone: timeZone
|
||||
startDate: &eventStartDate
|
||||
endDate: &eventEndDate];
|
||||
|
||||
if (resetAlarm)
|
||||
{
|
||||
iCalTrigger *aTrigger;
|
||||
@@ -506,7 +525,7 @@
|
||||
aTrigger = [anAlarm trigger];
|
||||
[aTrigger setValue: 0 ofAttribute: @"x-webstatus" to: @"triggered"];
|
||||
|
||||
[co saveComponent: event];
|
||||
[co saveComponent: master];
|
||||
}
|
||||
else if (snoozeAlarm)
|
||||
{
|
||||
@@ -516,8 +535,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
created_by = [event createdBy];
|
||||
|
||||
data = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[[componentCalendar displayName] stringByEscapingHTMLString], @"calendar",
|
||||
[event tag], @"component",
|
||||
@@ -525,7 +542,6 @@
|
||||
[dateFormatter formattedTime: eventStartDate], @"startTime",
|
||||
[dateFormatter formattedDate: eventEndDate], @"endDate",
|
||||
[dateFormatter formattedTime: eventEndDate], @"endTime",
|
||||
//([event hasRecurrenceRules] ? @"1": @"0"), @"isRecurring",
|
||||
([event isAllDay] ? @"1": @"0"), @"isAllDay",
|
||||
[[event summary] stringByEscapingHTMLString], @"summary",
|
||||
[[event location] stringByEscapingHTMLString], @"location",
|
||||
|
||||
+155
-153
@@ -99,7 +99,7 @@ static NSArray *tasksFields = nil;
|
||||
@"c_status", @"c_title", @"c_enddate",
|
||||
@"c_classification", @"c_location", @"c_category",
|
||||
@"editable", @"erasable",
|
||||
@"c_priority", @"c_owner", nil];
|
||||
@"c_priority", @"c_owner", @"c_recurrence_id", @"isException", nil];
|
||||
[tasksFields retain];
|
||||
}
|
||||
}
|
||||
@@ -331,150 +331,158 @@ static NSArray *tasksFields = nil;
|
||||
|
||||
folders = [[clientObject subFolders] objectEnumerator];
|
||||
while ((currentFolder = [folders nextObject]))
|
||||
{
|
||||
if ([currentFolder isActive])
|
||||
{
|
||||
folderIsRemote = [currentFolder isKindOfClass: [SOGoWebAppointmentFolder class]];
|
||||
if ([currentFolder isActive])
|
||||
{
|
||||
folderIsRemote = [currentFolder isKindOfClass: [SOGoWebAppointmentFolder class]];
|
||||
|
||||
if ([criteria isEqualToString:@"title_Category_Location"])
|
||||
currentInfos = [[currentFolder fetchCoreInfosFrom: startDate
|
||||
to: endDate
|
||||
title: value
|
||||
component: component
|
||||
additionalFilters: criteria] objectEnumerator];
|
||||
|
||||
else if ([criteria isEqualToString:@"entireContent"])
|
||||
{
|
||||
// First research : Through the quick table inside the location, category and title columns
|
||||
quickInfos = [currentFolder fetchCoreInfosFrom: startDate
|
||||
to: endDate
|
||||
title: value
|
||||
component: component
|
||||
additionalFilters: criteria];
|
||||
|
||||
// Save the c_name in another array to compare with
|
||||
if ([quickInfos count] > 0)
|
||||
{
|
||||
quickInfosFlag = YES;
|
||||
quickInfosName = [NSMutableArray arrayWithCapacity:[quickInfos count]];
|
||||
for (i = 0; i < [quickInfos count]; i++)
|
||||
[quickInfosName addObject:[[quickInfos objectAtIndex:i] objectForKey:@"c_name"]];
|
||||
}
|
||||
|
||||
// Second research : Every objects except for those already in the quickInfos array
|
||||
allInfos = [currentFolder fetchCoreInfosFrom: startDate
|
||||
to: endDate
|
||||
title: nil
|
||||
component: component];
|
||||
if (quickInfosFlag == YES)
|
||||
{
|
||||
for (i = ([allInfos count] - 1); i >= 0 ; i--) {
|
||||
if([quickInfosName containsObject:[[allInfos objectAtIndex:i] objectForKey:@"c_name"]])
|
||||
[allInfos removeObjectAtIndex:i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < [allInfos count]; i++)
|
||||
{
|
||||
iCalString = [[allInfos objectAtIndex:i] objectForKey:@"c_content"];
|
||||
calendar = [iCalCalendar parseSingleFromSource: iCalString];
|
||||
master = [calendar firstChildWithTag:component];
|
||||
if (master) {
|
||||
if ([[master comment] length] > 0)
|
||||
//
|
||||
// criteria can have the following values: "title", "title_Category_Location" and "entireContent"
|
||||
//
|
||||
if ([criteria isEqualToString:@"title_Category_Location"])
|
||||
{
|
||||
match = [[master comment] rangeOfString:value options:NSCaseInsensitiveSearch];
|
||||
if (match.length > 0) {
|
||||
[quickInfos addObject:[allInfos objectAtIndex:i]];
|
||||
}
|
||||
currentInfos = [[currentFolder fetchCoreInfosFrom: startDate
|
||||
to: endDate
|
||||
title: value
|
||||
component: component
|
||||
additionalFilters: criteria] objectEnumerator];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ([criteria isEqualToString:@"entireContent"])
|
||||
{
|
||||
// First search : Through the quick table inside the location, category and title columns
|
||||
quickInfos = [currentFolder fetchCoreInfosFrom: startDate
|
||||
to: endDate
|
||||
title: value
|
||||
component: component
|
||||
additionalFilters: criteria];
|
||||
|
||||
currentInfos = [quickInfos objectEnumerator];
|
||||
}
|
||||
|
||||
// Save the c_name in another array to compare with
|
||||
if ([quickInfos count] > 0)
|
||||
{
|
||||
quickInfosFlag = YES;
|
||||
quickInfosName = [NSMutableArray arrayWithCapacity:[quickInfos count]];
|
||||
for (i = 0; i < [quickInfos count]; i++)
|
||||
[quickInfosName addObject:[[quickInfos objectAtIndex:i] objectForKey:@"c_name"]];
|
||||
}
|
||||
|
||||
// Second research : Every objects except for those already in the quickInfos array
|
||||
allInfos = [currentFolder fetchCoreInfosFrom: startDate
|
||||
to: endDate
|
||||
title: nil
|
||||
component: component];
|
||||
if (quickInfosFlag == YES)
|
||||
{
|
||||
for (i = ([allInfos count] - 1); i >= 0 ; i--) {
|
||||
if([quickInfosName containsObject:[[allInfos objectAtIndex:i] objectForKey:@"c_name"]])
|
||||
[allInfos removeObjectAtIndex:i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < [allInfos count]; i++)
|
||||
{
|
||||
iCalString = [[allInfos objectAtIndex:i] objectForKey:@"c_content"];
|
||||
calendar = [iCalCalendar parseSingleFromSource: iCalString];
|
||||
master = [calendar firstChildWithTag:component];
|
||||
if (master) {
|
||||
if ([[master comment] length] > 0)
|
||||
{
|
||||
match = [[master comment] rangeOfString:value options:NSCaseInsensitiveSearch];
|
||||
if (match.length > 0) {
|
||||
[quickInfos addObject:[allInfos objectAtIndex:i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentInfos = [quickInfos objectEnumerator];
|
||||
}
|
||||
else
|
||||
{
|
||||
id foo;
|
||||
|
||||
else
|
||||
currentInfos = [[currentFolder fetchCoreInfosFrom: startDate
|
||||
to: endDate
|
||||
title: value
|
||||
component: component] objectEnumerator];
|
||||
foo = [currentFolder fetchCoreInfosFrom: startDate
|
||||
to: endDate
|
||||
title: value
|
||||
component: component];
|
||||
currentInfos = [foo objectEnumerator];
|
||||
}
|
||||
|
||||
owner = [currentFolder ownerInContext: context];
|
||||
ownerUser = [SOGoUser userWithLogin: owner];
|
||||
isErasable = ([owner isEqualToString: userLogin] || [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]);
|
||||
owner = [currentFolder ownerInContext: context];
|
||||
ownerUser = [SOGoUser userWithLogin: owner];
|
||||
isErasable = ([owner isEqualToString: userLogin] || [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]);
|
||||
|
||||
while ((newInfo = [currentInfos nextObject]))
|
||||
{
|
||||
if ([fields containsObject: @"editable"])
|
||||
{
|
||||
if (folderIsRemote)
|
||||
// .ics subscriptions are not editable
|
||||
[newInfo setObject: [NSNumber numberWithInt: 0]
|
||||
forKey: @"editable"];
|
||||
else
|
||||
{
|
||||
// Identifies whether the active user can edit the event.
|
||||
role = [currentFolder roleForComponentsWithAccessClass:[[newInfo objectForKey: @"c_classification"] intValue]
|
||||
forUser: userLogin];
|
||||
if ([role isEqualToString: @"ComponentModifier"] || [role length] == 0)
|
||||
[newInfo setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"editable"];
|
||||
else
|
||||
[newInfo setObject: [NSNumber numberWithInt: 0]
|
||||
forKey: @"editable"];
|
||||
}
|
||||
}
|
||||
while ((newInfo = [currentInfos nextObject]))
|
||||
{
|
||||
if ([fields containsObject: @"editable"])
|
||||
{
|
||||
if (folderIsRemote)
|
||||
// .ics subscriptions are not editable
|
||||
[newInfo setObject: [NSNumber numberWithInt: 0]
|
||||
forKey: @"editable"];
|
||||
else
|
||||
{
|
||||
// Identifies whether the active user can edit the event.
|
||||
role = [currentFolder roleForComponentsWithAccessClass:[[newInfo objectForKey: @"c_classification"] intValue]
|
||||
forUser: userLogin];
|
||||
if ([role isEqualToString: @"ComponentModifier"] || [role length] == 0)
|
||||
[newInfo setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"editable"];
|
||||
else
|
||||
[newInfo setObject: [NSNumber numberWithInt: 0]
|
||||
forKey: @"editable"];
|
||||
}
|
||||
}
|
||||
if ([fields containsObject: @"ownerIsOrganizer"])
|
||||
{
|
||||
// Identifies whether the active user is the organizer
|
||||
// of this event.
|
||||
NSString *c_orgmail;
|
||||
c_orgmail = [newInfo objectForKey: @"c_orgmail"];
|
||||
{
|
||||
// Identifies whether the active user is the organizer
|
||||
// of this event.
|
||||
NSString *c_orgmail;
|
||||
c_orgmail = [newInfo objectForKey: @"c_orgmail"];
|
||||
|
||||
if ([c_orgmail isKindOfClass: [NSString class]] && [ownerUser hasEmail: c_orgmail])
|
||||
[newInfo setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"ownerIsOrganizer"];
|
||||
else
|
||||
[newInfo setObject: [NSNumber numberWithInt: 0]
|
||||
forKey: @"ownerIsOrganizer"];
|
||||
}
|
||||
if ([c_orgmail isKindOfClass: [NSString class]] && [ownerUser hasEmail: c_orgmail])
|
||||
[newInfo setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"ownerIsOrganizer"];
|
||||
else
|
||||
[newInfo setObject: [NSNumber numberWithInt: 0]
|
||||
forKey: @"ownerIsOrganizer"];
|
||||
}
|
||||
if (isErasable)
|
||||
[newInfo setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"erasable"];
|
||||
[newInfo setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"erasable"];
|
||||
else
|
||||
[newInfo setObject: [NSNumber numberWithInt: 0]
|
||||
forKey: @"erasable"];
|
||||
[newInfo setObject: [NSNumber numberWithInt: 0]
|
||||
forKey: @"erasable"];
|
||||
|
||||
[newInfo setObject: [currentFolder nameInContainer]
|
||||
forKey: @"c_folder"];
|
||||
[newInfo setObject: [currentFolder ownerInContext: context]
|
||||
forKey: @"c_owner"];
|
||||
calendarName = [currentFolder displayName];
|
||||
if (calendarName == nil)
|
||||
calendarName = @"";
|
||||
[newInfo setObject: calendarName
|
||||
forKey: @"calendarName"];
|
||||
if (![[newInfo objectForKey: @"c_title"] length])
|
||||
[self _fixComponentTitle: newInfo withType: component];
|
||||
forKey: @"c_folder"];
|
||||
[newInfo setObject: [currentFolder ownerInContext: context]
|
||||
forKey: @"c_owner"];
|
||||
calendarName = [currentFolder displayName];
|
||||
if (calendarName == nil)
|
||||
calendarName = @"";
|
||||
[newInfo setObject: calendarName
|
||||
forKey: @"calendarName"];
|
||||
if (![[newInfo objectForKey: @"c_title"] length])
|
||||
[self _fixComponentTitle: newInfo withType: component];
|
||||
|
||||
// Possible improvement: only call _fixDates if event is recurrent
|
||||
// or the view range span a daylight saving time change
|
||||
[self _fixDates: newInfo];
|
||||
newInfoForComponent = [NSMutableArray arrayWithArray: [newInfo objectsForKeys: fields
|
||||
notFoundMarker: marker]];
|
||||
// Escape HTML
|
||||
count = [newInfoForComponent count];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
currentInfo = [newInfoForComponent objectAtIndex: i];
|
||||
if ([currentInfo respondsToSelector: @selector (stringByEscapingHTMLString)])
|
||||
[newInfoForComponent replaceObjectAtIndex: i withObject: [currentInfo stringByEscapingHTMLString]];
|
||||
[self _fixDates: newInfo];
|
||||
newInfoForComponent = [NSMutableArray arrayWithArray: [newInfo objectsForKeys: fields
|
||||
notFoundMarker: marker]];
|
||||
// Escape HTML
|
||||
count = [newInfoForComponent count];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
currentInfo = [newInfoForComponent objectAtIndex: i];
|
||||
if ([currentInfo respondsToSelector: @selector (stringByEscapingHTMLString)])
|
||||
[newInfoForComponent replaceObjectAtIndex: i withObject: [currentInfo stringByEscapingHTMLString]];
|
||||
}
|
||||
[infos addObject: newInfoForComponent];
|
||||
}
|
||||
}
|
||||
[infos addObject: newInfoForComponent];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return infos;
|
||||
}
|
||||
@@ -520,7 +528,6 @@ static NSArray *tasksFields = nil;
|
||||
// - for ALL subscribed and ACTIVE calendars
|
||||
// - 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
|
||||
{
|
||||
@@ -539,33 +546,28 @@ static NSArray *tasksFields = nil;
|
||||
|
||||
folders = [[clientObject subFolders] objectEnumerator];
|
||||
while ((currentFolder = [folders nextObject]))
|
||||
{
|
||||
if ([currentFolder isActive] && [currentFolder showCalendarAlarms])
|
||||
{
|
||||
NSDictionary *entry;
|
||||
NSArray *alarms;
|
||||
BOOL isCycle;
|
||||
int i;
|
||||
|
||||
alarms = [currentFolder fetchAlarmInfosFrom: [NSNumber numberWithInt: browserTime]
|
||||
to: [NSNumber numberWithInt: laterTime]];
|
||||
|
||||
for (i = 0; i < [alarms count]; i++)
|
||||
{
|
||||
entry = [alarms objectAtIndex: i];
|
||||
isCycle = [[entry objectForKey: @"c_iscycle"] boolValue];
|
||||
|
||||
if (!isCycle)
|
||||
if ([currentFolder isActive] && [currentFolder showCalendarAlarms])
|
||||
{
|
||||
[allAlarms addObject: [NSArray arrayWithObjects:
|
||||
[currentFolder nameInContainer],
|
||||
[entry objectForKey: @"c_name"],
|
||||
[entry objectForKey: @"c_nextalarm"],
|
||||
nil]];
|
||||
NSDictionary *entry;
|
||||
NSArray *alarms;
|
||||
int i;
|
||||
|
||||
alarms = [currentFolder fetchAlarmInfosFrom: [NSNumber numberWithInt: browserTime]
|
||||
to: [NSNumber numberWithInt: laterTime]];
|
||||
|
||||
for (i = 0; i < [alarms count]; i++)
|
||||
{
|
||||
entry = [alarms objectAtIndex: i];
|
||||
|
||||
[allAlarms addObject: [NSArray arrayWithObjects:
|
||||
[currentFolder nameInContainer],
|
||||
[entry objectForKey: @"c_name"],
|
||||
[entry objectForKey: @"c_nextalarm"],
|
||||
nil]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
response = [self responseWithStatus: 200];
|
||||
@@ -1242,9 +1244,9 @@ _computeBlocksPosition (NSArray *blocks)
|
||||
[filteredTasks addObject: filteredTask];
|
||||
else if ([tasksView isEqualToString:@"view_all"])
|
||||
[filteredTasks addObject: filteredTask];
|
||||
else if (([tasksView isEqualToString:@"view_overdue"]) && ([[filteredTask objectAtIndex:12] isEqualToString:@"overdue"]))
|
||||
else if (([tasksView isEqualToString:@"view_overdue"]) && ([[filteredTask objectAtIndex:15] isEqualToString:@"overdue"]))
|
||||
[filteredTasks addObject: filteredTask];
|
||||
else if ([tasksView isEqualToString:@"view_incomplete"] && (![[filteredTask objectAtIndex:12] isEqualToString:@"completed"]))
|
||||
else if ([tasksView isEqualToString:@"view_incomplete"] && (![[filteredTask objectAtIndex:15] isEqualToString:@"completed"]))
|
||||
[filteredTasks addObject: filteredTask];
|
||||
else if ([tasksView isEqualToString:@"view_not_started"] && ([[[filteredTask objectAtIndex:3] stringValue] isEqualToString:@"0"]))
|
||||
[filteredTasks addObject: filteredTask];
|
||||
|
||||
@@ -176,7 +176,6 @@
|
||||
////////////////////////////////// JUNK ////////////////////////////////////////
|
||||
|
||||
/* access */
|
||||
- (BOOL) isMyComponent;
|
||||
- (BOOL) canEditComponent;
|
||||
- (unsigned int) firstDayOfWeek;
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
/* UIxComponentEditor.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006-2013 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Francis Lachapelle <flachapelle@inverse.ca>
|
||||
* Copyright (C) 2006-2014 Inverse inc.
|
||||
*
|
||||
* 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
|
||||
@@ -33,10 +30,15 @@
|
||||
|
||||
#import <NGCards/iCalAlarm.h>
|
||||
#import <NGCards/iCalByDayMask.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalEvent.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalRepeatableEntityObject.h>
|
||||
#import <NGCards/iCalRecurrenceRule.h>
|
||||
#import <NGCards/iCalToDo.h>
|
||||
#import <NGCards/iCalTrigger.h>
|
||||
|
||||
|
||||
#import <NGCards/NSString+NGCards.h>
|
||||
#import <NGCards/NSCalendarDate+NGCards.h>
|
||||
#import <NGObjWeb/SoSecurityManager.h>
|
||||
@@ -46,6 +48,7 @@
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import <Appointments/iCalEntityObject+SOGo.h>
|
||||
@@ -1717,14 +1720,9 @@ RANGE(2);
|
||||
|
||||
/* access */
|
||||
|
||||
- (BOOL) isMyComponent
|
||||
{
|
||||
return ([[context activeUser] hasEmail: [organizer rfc822Email]]);
|
||||
}
|
||||
|
||||
- (BOOL) canEditComponent
|
||||
{
|
||||
return [self isMyComponent];
|
||||
return ([[context activeUser] hasEmail: [organizer rfc822Email]]);
|
||||
}
|
||||
|
||||
/* response generation */
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* UIxOccurenceDialog.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2008 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2008-2014 Inverse inc.
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
{
|
||||
if (!todo)
|
||||
{
|
||||
todo = (iCalToDo *) [[self clientObject] component: YES secure: YES];
|
||||
todo = (iCalToDo *) [[self clientObject] occurence];
|
||||
[[todo parent] retain];
|
||||
}
|
||||
|
||||
@@ -424,12 +424,17 @@
|
||||
WOResponse *result;
|
||||
NSDictionary *data;
|
||||
NSCalendarDate *startDate, *dueDate;
|
||||
SOGoCalendarComponent *co;
|
||||
NSTimeZone *timeZone;
|
||||
SOGoUserDefaults *ud;
|
||||
iCalAlarm *anAlarm;
|
||||
BOOL resetAlarm;
|
||||
BOOL snoozeAlarm;
|
||||
|
||||
[self todo];
|
||||
|
||||
co = [self clientObject];
|
||||
|
||||
result = [self responseWithStatus: 200];
|
||||
ud = [[context activeUser] userDefaults];
|
||||
timeZone = [ud timeZone];
|
||||
@@ -437,21 +442,41 @@
|
||||
[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];
|
||||
// resetAlarm=yes is set only when we are about to show the alarm popup in the Web
|
||||
// interface of SOGo. See generic.js for details. snoozeAlarm=X is called when the
|
||||
// user clicks on "Snooze for" X minutes, when the popup is being displayed.
|
||||
// If either is set to yes, we must find the right alarm.
|
||||
resetAlarm = [[[context request] formValueForKey: @"resetAlarm"] boolValue];
|
||||
snoozeAlarm = [[[context request] formValueForKey: @"snoozeAlarm"] intValue];
|
||||
|
||||
if (resetAlarm || snoozeAlarm)
|
||||
{
|
||||
iCalToDo *master;
|
||||
|
||||
master = todo;
|
||||
[[co container] findEntityForClosestAlarm: &todo
|
||||
timezone: timeZone
|
||||
startDate: &startDate
|
||||
endDate: &dueDate];
|
||||
if (resetAlarm)
|
||||
{
|
||||
iCalTrigger *aTrigger;
|
||||
|
||||
anAlarm = [[todo alarms] objectAtIndex: 0];
|
||||
aTrigger = [anAlarm trigger];
|
||||
[aTrigger setValue: 0 ofAttribute: @"x-webstatus" to: @"triggered"];
|
||||
|
||||
[co saveComponent: master];
|
||||
}
|
||||
else if (snoozeAlarm)
|
||||
{
|
||||
anAlarm = [[todo alarms] objectAtIndex: 0];
|
||||
if ([[anAlarm action] caseInsensitiveCompare: @"DISPLAY"] == NSOrderedSame)
|
||||
[co snoozeAlarm: snoozeAlarm];
|
||||
}
|
||||
}
|
||||
resetAlarm = [[[context request] formValueForKey: @"resetAlarm"] boolValue];
|
||||
|
||||
data = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[todo tag], @"component",
|
||||
@@ -538,26 +563,6 @@
|
||||
|
||||
}
|
||||
|
||||
// TODO: add tentatively
|
||||
|
||||
// - (id) acceptOrDeclineAction: (BOOL) _accept
|
||||
// {
|
||||
// [[self clientObject] changeParticipationStatus:
|
||||
// _accept ? @"ACCEPTED" : @"DECLINED"];
|
||||
|
||||
// return self;
|
||||
// }
|
||||
|
||||
// - (id) acceptAction
|
||||
// {
|
||||
// return [self acceptOrDeclineAction: YES];
|
||||
// }
|
||||
|
||||
// - (id) declineAction
|
||||
// {
|
||||
// return [self acceptOrDeclineAction: NO];
|
||||
// }
|
||||
|
||||
- (id) changeStatusAction
|
||||
{
|
||||
NSString *newStatus;
|
||||
|
||||
@@ -399,6 +399,11 @@
|
||||
pageName = "UIxTaskEditor";
|
||||
actionName = "save";
|
||||
};
|
||||
delete = {
|
||||
protectedBy = "Delete Object";
|
||||
pageName = "UIxOccurenceDialog";
|
||||
actionName = "delete";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user