mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-05-08 04:55:26 +00:00
(feat) warn when double-booking attendees and offer force save option
This commit is contained in:
@@ -13,6 +13,7 @@ Enhancements
|
||||
- [web] now supports RFC6154 and NoInferiors IMAP flag
|
||||
- [web] improved confirm dialogs for deletions
|
||||
- [web] allow resources to prevent invitations (#3410)
|
||||
- [web] warn when double-booking attendees and offer force save option
|
||||
|
||||
Bug fixes
|
||||
- [web] handle birthday dates before 1970
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2007-2014 Inverse inc.
|
||||
Copyright (C) 2007-2016 Inverse inc.
|
||||
|
||||
This file is part of SOGo
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2007-2015 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2007-2016 Inverse inc.
|
||||
|
||||
This file is part of SOGo
|
||||
|
||||
@@ -55,6 +54,7 @@
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/NSObject+DAV.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoDateFormatter.h>
|
||||
#import <SOGo/SOGoObject.h>
|
||||
#import <SOGo/SOGoPermissions.h>
|
||||
#import <SOGo/SOGoGroup.h>
|
||||
@@ -415,8 +415,8 @@
|
||||
}
|
||||
|
||||
// This method scans the list of attendees.
|
||||
- (NSException *) _handleAttendeeAvailability: (NSArray *) theAttendees
|
||||
forEvent: (iCalEvent *) theEvent
|
||||
- (NSException *) _handleAttendeesAvailability: (NSArray *) theAttendees
|
||||
forEvent: (iCalEvent *) theEvent
|
||||
{
|
||||
iCalPerson *currentAttendee;
|
||||
SOGoUser *user;
|
||||
@@ -496,20 +496,20 @@
|
||||
//
|
||||
// This methods scans the list of attendees. If they are
|
||||
// considered as resource, it checks for conflicting
|
||||
// dates for the event.
|
||||
// dates for the event and potentially auto-accept/decline
|
||||
// the invitation.
|
||||
//
|
||||
// For normal attendees, it'll return an exception with
|
||||
// conflicting dates, unless we force the save.//
|
||||
// We check for between startDate + 1 second and
|
||||
// endDate - 1 second
|
||||
//
|
||||
//
|
||||
// It also CHANGES the participation status of resources
|
||||
// depending on constraints defined on them.
|
||||
//
|
||||
// Note that it doesn't matter if it changes the participation
|
||||
// status since in case of an error, nothing will get saved.
|
||||
//
|
||||
- (NSException *) _handleResourcesConflicts: (NSArray *) theAttendees
|
||||
- (NSException *) _handleAttendeesConflicts: (NSArray *) theAttendees
|
||||
forEvent: (iCalEvent *) theEvent
|
||||
force: (BOOL) forceSave
|
||||
{
|
||||
iCalPerson *currentAttendee;
|
||||
NSMutableArray *attendees;
|
||||
@@ -540,110 +540,115 @@
|
||||
enumerator = [attendees objectEnumerator];
|
||||
while ((currentUID = [enumerator nextObject]))
|
||||
{
|
||||
NSCalendarDate *start, *end, *rangeStartDate, *rangeEndDate;
|
||||
SOGoAppointmentFolder *folder;
|
||||
NGCalendarDateRange *range;
|
||||
NSMutableArray *fbInfo;
|
||||
NSArray *allOccurences;
|
||||
|
||||
BOOL must_delete;
|
||||
int i, j, delta;
|
||||
|
||||
user = [SOGoUser userWithLogin: currentUID];
|
||||
|
||||
if ([user isResource])
|
||||
|
||||
// We get the start/end date for our conflict range. If the event to be added is recurring, we
|
||||
// check for at least a year to start with.
|
||||
start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1];
|
||||
end = [[theEvent endDate] dateByAddingYears: ([theEvent isRecurrent] ? 1 : 0) months: 0 days: 0 hours: 0 minutes: 0 seconds: -1];
|
||||
|
||||
folder = [user personalCalendarFolderInContext: context];
|
||||
|
||||
// Deny access to the resource if the ACLs don't allow the user
|
||||
if (![folder aclSQLListingFilter])
|
||||
{
|
||||
NSCalendarDate *start, *end, *rangeStartDate, *rangeEndDate;
|
||||
SOGoAppointmentFolder *folder;
|
||||
NGCalendarDateRange *range;
|
||||
NSMutableArray *fbInfo;
|
||||
NSArray *allOccurences;
|
||||
|
||||
BOOL must_delete;
|
||||
int i, j, delta;
|
||||
|
||||
// We get the start/end date for our conflict range. If the event to be added is recurring, we
|
||||
// check for at least a year to start with.
|
||||
start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1];
|
||||
end = [[theEvent endDate] dateByAddingYears: ([theEvent isRecurrent] ? 1 : 0) months: 0 days: 0 hours: 0 minutes: 0 seconds: -1];
|
||||
|
||||
folder = [user personalCalendarFolderInContext: context];
|
||||
|
||||
// Deny access to the resource if the ACLs don't allow the user
|
||||
if (![folder aclSQLListingFilter])
|
||||
{
|
||||
NSDictionary *values;
|
||||
NSString *reason;
|
||||
NSDictionary *values;
|
||||
NSString *reason;
|
||||
|
||||
values = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail"];
|
||||
reason = [values keysWithFormat: [self labelForKey: @"Cannot access resource: \"%{Cn} %{SystemEmail}\""]];
|
||||
return [NSException exceptionWithHTTPStatus:403 reason: reason];
|
||||
}
|
||||
values = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail"];
|
||||
reason = [values keysWithFormat: [self labelForKey: @"Cannot access resource: \"%{Cn} %{SystemEmail}\""]];
|
||||
return [NSException exceptionWithHTTPStatus:403 reason: reason];
|
||||
}
|
||||
|
||||
fbInfo = [NSMutableArray arrayWithArray: [folder fetchFreeBusyInfosFrom: start
|
||||
fbInfo = [NSMutableArray arrayWithArray: [folder fetchFreeBusyInfosFrom: start
|
||||
to: end]];
|
||||
|
||||
// We first remove any occurences in the freebusy that corresponds to the
|
||||
// current event. We do this to avoid raising a conflict if we move a 1 hour
|
||||
// meeting from 12:00-13:00 to 12:15-13:15. We would overlap on ourself otherwise.
|
||||
//
|
||||
// We must also check here for repetitive events that don't overlap our event.
|
||||
// We remove all events that don't overlap. The events here are already
|
||||
// decomposed.
|
||||
//
|
||||
if ([theEvent isRecurrent])
|
||||
allOccurences = [theEvent recurrenceRangesWithinCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: start
|
||||
endDate: end]
|
||||
firstInstanceCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: [theEvent startDate]
|
||||
endDate: [theEvent endDate]]];
|
||||
else
|
||||
allOccurences = nil;
|
||||
|
||||
for (i = [fbInfo count]-1; i >= 0; i--)
|
||||
// We first remove any occurences in the freebusy that corresponds to the
|
||||
// current event. We do this to avoid raising a conflict if we move a 1 hour
|
||||
// meeting from 12:00-13:00 to 12:15-13:15. We would overlap on ourself otherwise.
|
||||
//
|
||||
// We must also check here for repetitive events that don't overlap our event.
|
||||
// We remove all events that don't overlap. The events here are already
|
||||
// decomposed.
|
||||
//
|
||||
if ([theEvent isRecurrent])
|
||||
allOccurences = [theEvent recurrenceRangesWithinCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: start
|
||||
endDate: end]
|
||||
firstInstanceCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: [theEvent startDate]
|
||||
endDate: [theEvent endDate]]];
|
||||
else
|
||||
allOccurences = nil;
|
||||
|
||||
for (i = [fbInfo count]-1; i >= 0; i--)
|
||||
{
|
||||
// We MUST use the -uniqueChildWithTag method here because the event has been flattened, so its timezone has been
|
||||
// modified in SOGoAppointmentFolder: -fixupCycleRecord: ....
|
||||
rangeStartDate = [[fbInfo objectAtIndex: i] objectForKey: @"startDate"];
|
||||
delta = [[rangeStartDate timeZoneDetail] timeZoneSecondsFromGMT] - [[[(iCalDateTime *)[theEvent uniqueChildWithTag: @"dtstart"] timeZone] periodForDate: [theEvent startDate]] secondsOffsetFromGMT];
|
||||
rangeStartDate = [rangeStartDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: delta];
|
||||
|
||||
rangeEndDate = [[fbInfo objectAtIndex: i] objectForKey: @"endDate"];
|
||||
delta = [[rangeEndDate timeZoneDetail] timeZoneSecondsFromGMT] - [[[(iCalDateTime *)[theEvent uniqueChildWithTag: @"dtend"] timeZone] periodForDate: [theEvent endDate]] secondsOffsetFromGMT];
|
||||
rangeEndDate = [rangeEndDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: delta];
|
||||
|
||||
range = [NGCalendarDateRange calendarDateRangeWithStartDate: rangeStartDate
|
||||
endDate: rangeEndDate];
|
||||
|
||||
// We remove the freebusy entries corresponding to the actual event being modified
|
||||
if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame)
|
||||
{
|
||||
// We MUST use the -uniqueChildWithTag method here because the event has been flattened, so its timezone has been
|
||||
// modified in SOGoAppointmentFolder: -fixupCycleRecord: ....
|
||||
rangeStartDate = [[fbInfo objectAtIndex: i] objectForKey: @"startDate"];
|
||||
delta = [[rangeStartDate timeZoneDetail] timeZoneSecondsFromGMT] - [[[(iCalDateTime *)[theEvent uniqueChildWithTag: @"dtstart"] timeZone] periodForDate: [theEvent startDate]] secondsOffsetFromGMT];
|
||||
rangeStartDate = [rangeStartDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: delta];
|
||||
|
||||
rangeEndDate = [[fbInfo objectAtIndex: i] objectForKey: @"endDate"];
|
||||
delta = [[rangeEndDate timeZoneDetail] timeZoneSecondsFromGMT] - [[[(iCalDateTime *)[theEvent uniqueChildWithTag: @"dtend"] timeZone] periodForDate: [theEvent endDate]] secondsOffsetFromGMT];
|
||||
rangeEndDate = [rangeEndDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: delta];
|
||||
|
||||
range = [NGCalendarDateRange calendarDateRangeWithStartDate: rangeStartDate
|
||||
endDate: rangeEndDate];
|
||||
[fbInfo removeObjectAtIndex: i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame)
|
||||
// No need to check if the event isn't recurrent here as it's handled correctly
|
||||
// when we compute the "end" date.
|
||||
if ([allOccurences count])
|
||||
{
|
||||
must_delete = YES;
|
||||
|
||||
for (j = 0; j < [allOccurences count]; j++)
|
||||
{
|
||||
[fbInfo removeObjectAtIndex: i];
|
||||
continue;
|
||||
}
|
||||
|
||||
// No need to check if the event isn't recurrent here as it's handled correctly
|
||||
// when we compute the "end" date.
|
||||
if ([allOccurences count])
|
||||
{
|
||||
must_delete = YES;
|
||||
|
||||
for (j = 0; j < [allOccurences count]; j++)
|
||||
if ([range doesIntersectWithDateRange: [allOccurences objectAtIndex: j]])
|
||||
{
|
||||
if ([range doesIntersectWithDateRange: [allOccurences objectAtIndex: j]])
|
||||
{
|
||||
must_delete = NO;
|
||||
break;
|
||||
}
|
||||
must_delete = NO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (must_delete)
|
||||
[fbInfo removeObjectAtIndex: i];
|
||||
}
|
||||
|
||||
if (must_delete)
|
||||
[fbInfo removeObjectAtIndex: i];
|
||||
}
|
||||
}
|
||||
|
||||
// Find the attendee associated to the current UID
|
||||
for (i = 0; i < [theAttendees count]; i++)
|
||||
{
|
||||
currentAttendee = [theAttendees objectAtIndex: i];
|
||||
if ([[currentAttendee uidInContext: context] isEqualToString: currentUID])
|
||||
break;
|
||||
else
|
||||
currentAttendee = nil;
|
||||
}
|
||||
|
||||
if ([fbInfo count])
|
||||
{
|
||||
SOGoDateFormatter *formatter;
|
||||
|
||||
formatter = [[context activeUser] dateFormatterInContext: context];
|
||||
|
||||
// Find the attendee associated to the current UID
|
||||
for (i = 0; i < [theAttendees count]; i++)
|
||||
{
|
||||
currentAttendee = [theAttendees objectAtIndex: i];
|
||||
if ([[currentAttendee uidInContext: context] isEqualToString: currentUID])
|
||||
break;
|
||||
else
|
||||
currentAttendee = nil;
|
||||
}
|
||||
|
||||
if ([fbInfo count])
|
||||
if ([user isResource])
|
||||
{
|
||||
// If we always force the auto-accept if numberOfSimultaneousBookings <= 0 (ie., no limit
|
||||
// is imposed) or if numberOfSimultaneousBookings is greater than the number of
|
||||
@@ -663,35 +668,63 @@
|
||||
NSDictionary *values;
|
||||
NSString *reason;
|
||||
iCalEvent *event;
|
||||
|
||||
|
||||
calendar = [iCalCalendar parseSingleFromSource: [[fbInfo objectAtIndex: 0] objectForKey: @"c_content"]];
|
||||
event = [[calendar events] lastObject];
|
||||
|
||||
|
||||
values = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail",
|
||||
([event summary] ? [event summary] : @""), @"EventTitle",
|
||||
[[fbInfo objectAtIndex: 0] objectForKey: @"startDate"], @"StartDate",
|
||||
nil];
|
||||
|
||||
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail",
|
||||
([event summary] ? [event summary] : @""), @"EventTitle",
|
||||
[formatter formattedDateAndTime: [[fbInfo objectAtIndex: 0] objectForKey: @"startDate"]], @"StartDate",
|
||||
nil];
|
||||
|
||||
reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}."]];
|
||||
|
||||
|
||||
return [NSException exceptionWithHTTPStatus: 403
|
||||
reason: reason];
|
||||
}
|
||||
}
|
||||
else if (currentAttendee)
|
||||
//
|
||||
// We are dealing with a normal attendee. Lets check if we have conflicts, unless
|
||||
// we are being asked to force the save anyway
|
||||
//
|
||||
else if (!forceSave)
|
||||
{
|
||||
// No conflict, we auto-accept. We do this for resources automatically if no
|
||||
// double-booking is observed. If it's not the desired behavior, just don't
|
||||
// set the resource as one!
|
||||
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
NSMutableDictionary *info;
|
||||
NSMutableArray *conflicts;
|
||||
id o;
|
||||
|
||||
info = [NSMutableDictionary dictionary];
|
||||
conflicts = [NSMutableArray array];
|
||||
|
||||
[info setObject: [currentAttendee cn] forKey: @"attendee_name"];
|
||||
[info setObject: [currentAttendee rfc822Email] forKey: @"attendee_email"];
|
||||
|
||||
for (i = 0; i < [fbInfo count]; i++)
|
||||
{
|
||||
o = [fbInfo objectAtIndex: i];
|
||||
[conflicts addObject: [NSDictionary dictionaryWithObjectsAndKeys: [formatter formattedDateAndTime: [o objectForKey: @"startDate"]], @"startDate",
|
||||
[formatter formattedDateAndTime: [o objectForKey: @"endDate"]], @"endDate", nil]];
|
||||
}
|
||||
|
||||
[info setObject: conflicts forKey: @"conflicts"];
|
||||
|
||||
return [NSException exceptionWithHTTPStatus: 403
|
||||
reason: [info jsonRepresentation]];
|
||||
}
|
||||
} // if ([fbInfo count]) ...
|
||||
else if (currentAttendee && [user isResource])
|
||||
{
|
||||
// No conflict, we auto-accept. We do this for resources automatically if no
|
||||
// double-booking is observed. If it's not the desired behavior, just don't
|
||||
// set the resource as one!
|
||||
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
}
|
||||
}
|
||||
|
||||
} // if ([user isResource]) ...
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -700,6 +733,7 @@
|
||||
//
|
||||
- (NSException *) _handleAddedUsers: (NSArray *) attendees
|
||||
fromEvent: (iCalEvent *) newEvent
|
||||
force: (BOOL) forceSave
|
||||
{
|
||||
iCalPerson *currentAttendee;
|
||||
NSEnumerator *enumerator;
|
||||
@@ -707,9 +741,9 @@
|
||||
NSException *e;
|
||||
|
||||
// We check for conflicts
|
||||
if ((e = [self _handleResourcesConflicts: attendees forEvent: newEvent]))
|
||||
if ((e = [self _handleAttendeesConflicts: attendees forEvent: newEvent force: forceSave]))
|
||||
return e;
|
||||
if ((e = [self _handleAttendeeAvailability: attendees forEvent: newEvent]))
|
||||
if ((e = [self _handleAttendeesAvailability: attendees forEvent: newEvent]))
|
||||
return e;
|
||||
|
||||
enumerator = [attendees objectEnumerator];
|
||||
@@ -765,6 +799,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
//
|
||||
- (NSException *) _handleUpdatedEvent: (iCalEvent *) newEvent
|
||||
fromOldEvent: (iCalEvent *) oldEvent
|
||||
force: (BOOL) forceSave
|
||||
{
|
||||
NSArray *addedAttendees, *deletedAttendees, *updatedAttendees;
|
||||
iCalEventChanges *changes;
|
||||
@@ -804,9 +839,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
withType: @"calendar:cancellation"];
|
||||
}
|
||||
|
||||
if ((ex = [self _handleResourcesConflicts: [newEvent attendees] forEvent: newEvent]))
|
||||
if ((ex = [self _handleAttendeesConflicts: [newEvent attendees] forEvent: newEvent force: forceSave]))
|
||||
return ex;
|
||||
if ((ex = [self _handleAttendeeAvailability: [newEvent attendees] forEvent: newEvent]))
|
||||
if ((ex = [self _handleAttendeesAvailability: [newEvent attendees] forEvent: newEvent]))
|
||||
return ex;
|
||||
|
||||
addedAttendees = [changes insertedAttendees];
|
||||
@@ -855,7 +890,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
if ([addedAttendees count])
|
||||
{
|
||||
// Send an invitation to new attendees
|
||||
if ((ex = [self _handleAddedUsers: addedAttendees fromEvent: newEvent]))
|
||||
if ((ex = [self _handleAddedUsers: addedAttendees fromEvent: newEvent force: forceSave]))
|
||||
return ex;
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
@@ -900,6 +935,12 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
//
|
||||
//
|
||||
- (NSException *) saveComponent: (iCalEvent *) newEvent
|
||||
{
|
||||
return [self saveComponent: newEvent force: NO];
|
||||
}
|
||||
|
||||
- (NSException *) saveComponent: (iCalEvent *) newEvent
|
||||
force: (BOOL) forceSave
|
||||
{
|
||||
iCalEvent *oldEvent, *oldMasterEvent;
|
||||
NSCalendarDate *recurrenceId;
|
||||
@@ -924,7 +965,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
|
||||
// We catch conflicts and abort the save process immediately
|
||||
// in case of one with resources
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent]))
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent force: forceSave]))
|
||||
return ex;
|
||||
|
||||
if ([attendees count])
|
||||
@@ -967,7 +1008,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
if (!hasOrganizer || [oldMasterEvent userIsOrganizer: ownerUser])
|
||||
// The owner is the organizer of the event; handle the modifications. We aslo
|
||||
// catch conflicts just like when the events are created
|
||||
if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent]))
|
||||
if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent force: forceSave]))
|
||||
return ex;
|
||||
}
|
||||
|
||||
@@ -1604,7 +1645,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
currentUser = [context activeUser];
|
||||
attendees = [occurence attendeesWithoutUser: currentUser];
|
||||
|
||||
#warning Make sure this is correct ..
|
||||
if (![attendees count] && event != occurence)
|
||||
attendees = [event attendeesWithoutUser: currentUser];
|
||||
|
||||
@@ -2007,7 +2047,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
attendees = [event attendeesWithoutUser: ownerUser];
|
||||
if ([attendees count])
|
||||
{
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: event]))
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: event force: YES]))
|
||||
return ex;
|
||||
else
|
||||
{
|
||||
@@ -2159,7 +2199,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
if (!newEvent && oldEvent)
|
||||
[self prepareDeleteOccurence: oldEvent];
|
||||
// The master event was changed, A RECCURENCE-ID was added or modified
|
||||
else if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent]))
|
||||
else if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent force: YES]))
|
||||
return ex;
|
||||
}
|
||||
//
|
||||
|
||||
@@ -61,7 +61,8 @@
|
||||
- (void) updateComponent: (iCalRepeatableEntityObject *) newObject;
|
||||
- (NSException *) saveCalendar: (iCalCalendar *) newCalendar;
|
||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject;
|
||||
|
||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newEvent
|
||||
force: (BOOL) forceSave;
|
||||
/* mail notifications */
|
||||
- (void) sendEMailUsingTemplateNamed: (NSString *) pageName
|
||||
forObject: (iCalRepeatableEntityObject *) object
|
||||
|
||||
@@ -685,6 +685,12 @@
|
||||
return [self saveCalendar: [newObject parent]];
|
||||
}
|
||||
|
||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newEvent
|
||||
force: (BOOL) forceSave
|
||||
{
|
||||
return [self saveComponent: newEvent];
|
||||
}
|
||||
|
||||
/* raw saving */
|
||||
|
||||
/* EMail Notifications */
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2006-2014 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2014-2016 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
@@ -26,24 +24,6 @@
|
||||
|
||||
#import "SOGoCalendarComponent.h"
|
||||
|
||||
/*
|
||||
SOGoTaskObject
|
||||
|
||||
Represents a single task. This SOPE controller object manages all the
|
||||
attendee storages (that is, it might store into multiple folders for meeting
|
||||
tasks!).
|
||||
|
||||
Note: SOGoTaskObject do not need to exist yet. They can also be "new"
|
||||
tasks with an externally generated unique key.
|
||||
*/
|
||||
|
||||
@class NSArray;
|
||||
@class NSException;
|
||||
@class NSString;
|
||||
|
||||
@class iCalToDo;
|
||||
@class iCalCalendar;
|
||||
|
||||
@interface SOGoTaskObject : SOGoCalendarComponent
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2006-2014 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2014-2016 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*
|
||||
Copyright (C) 2004 SKYRIX Software AG
|
||||
Copyright (C) 2005-2016 Inverse inc.
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo.
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
@@ -48,21 +48,6 @@
|
||||
|
||||
- (NSString *) stringForObjectValue: (id) date;
|
||||
|
||||
// - (void) setFullWeekdayNameAndDetails;
|
||||
|
||||
// - (NSString *) date: (NSCalendarDate *) date
|
||||
// withFormat: (unsigned int) format;
|
||||
// - (NSString *) date: (NSCalendarDate *) date
|
||||
// withNSFormat: (NSNumber *) format;
|
||||
|
||||
|
||||
// - (NSString *) shortDayOfWeek: (int)_day;
|
||||
// - (NSString *) fullDayOfWeek: (int)_day;
|
||||
// - (NSString *) shortMonthOfYear: (int)_month;
|
||||
// - (NSString *) fullMonthOfYear: (int)_month;
|
||||
|
||||
// - (NSString *) fullWeekdayNameAndDetailsForDate: (NSCalendarDate *)_date;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __SOGoDateFormatter_H_ */
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*
|
||||
Copyright (C) 2004 SKYRIX Software AG
|
||||
Copyright (C) 2005-2016 Inverse inc.
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo.
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSUserDefaults.h> /* for NSXXXFormatString, ... */
|
||||
#import <Foundation/NSUserDefaults.h>
|
||||
|
||||
#import "SOGoDateFormatter.h"
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* UIxAppointmentActions.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2010-2016 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,8 +1,6 @@
|
||||
/* UIxAppointmentActions.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011-2014 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2011-2016 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
|
||||
@@ -69,6 +67,7 @@
|
||||
NSException *ex;
|
||||
SOGoAppointmentFolder *targetCalendar, *sourceCalendar;
|
||||
SOGoAppointmentFolders *folders;
|
||||
BOOL forceSave;
|
||||
|
||||
rq = [context request];
|
||||
params = [[rq contentAsString] objectFromJSONString];
|
||||
@@ -77,6 +76,7 @@
|
||||
startDelta = [params objectForKey: @"start"];
|
||||
durationDelta = [params objectForKey: @"duration"];
|
||||
destionationCalendar = [params objectForKey: @"destination"];
|
||||
forceSave = NO;
|
||||
|
||||
if (daysDelta || startDelta || durationDelta)
|
||||
{
|
||||
@@ -120,7 +120,8 @@
|
||||
[event updateRecurrenceRulesUntilDate: end];
|
||||
|
||||
[event setLastModified: [NSCalendarDate calendarDate]];
|
||||
ex = [co saveComponent: event];
|
||||
ex = [co saveComponent: event force: forceSave];
|
||||
|
||||
// This condition will be executed only if the event is moved from a calendar to another. If destionationCalendar == 0; there is no calendar change
|
||||
if ([destionationCalendar length] > 0)
|
||||
{
|
||||
@@ -139,12 +140,21 @@
|
||||
ex = [co moveToFolder: targetCalendar];
|
||||
}
|
||||
}
|
||||
|
||||
if (ex)
|
||||
{
|
||||
unsigned int httpStatus;
|
||||
|
||||
httpStatus = 500;
|
||||
|
||||
if ([ex respondsToSelect: @selector(httpStatus)])
|
||||
httpStatus = [ex httpStatus];
|
||||
|
||||
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[ex reason], @"message",
|
||||
[ex reason], @"message",
|
||||
nil];
|
||||
response = [self responseWithStatus: 403
|
||||
|
||||
response = [self responseWithStatus: httpStatus
|
||||
andJSONRepresentation: jsonResponse];
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* UIxAppointmentEditor.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
|
||||
* 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-2015 Inverse inc.
|
||||
* Copyright (C) 2007-2016 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
|
||||
@@ -467,7 +467,9 @@
|
||||
SOGoAppointmentObject *co;
|
||||
SoSecurityManager *sm;
|
||||
WORequest *request;
|
||||
|
||||
unsigned int httpStatus;
|
||||
BOOL forceSave;
|
||||
|
||||
event = [self event];
|
||||
co = [self clientObject];
|
||||
@@ -488,6 +490,7 @@
|
||||
else
|
||||
{
|
||||
[self setAttributes: params];
|
||||
forceSave = NO;
|
||||
|
||||
if ([event hasRecurrenceRules])
|
||||
[self _adjustRecurrentRules];
|
||||
@@ -511,12 +514,12 @@
|
||||
}
|
||||
|
||||
// Save the event.
|
||||
ex = [co saveComponent: event];
|
||||
ex = [co saveComponent: event force: forceSave];
|
||||
}
|
||||
else
|
||||
{
|
||||
// The event was modified -- save it.
|
||||
ex = [co saveComponent: event];
|
||||
ex = [co saveComponent: event force: forceSave];
|
||||
|
||||
if (componentCalendar
|
||||
&& ![[componentCalendar ocsPath]
|
||||
@@ -539,9 +542,12 @@
|
||||
if (ex)
|
||||
{
|
||||
httpStatus = 500;
|
||||
|
||||
if ([ex respondsToSelect: @selector(httpStatus)])
|
||||
httpStatus = [ex httpStatus];
|
||||
|
||||
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"failure", @"status",
|
||||
[ex reason], @"message",
|
||||
[ex reason], @"message",
|
||||
nil];
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user