See ChangeLog
Monotone-Parent: be9e28d5d42ed05605b27d2127cf29b07678495b Monotone-Revision: 5de6a9584cf27a2c1dad8d1ab8b84fc9ddab2720 Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2011-04-25T10:31:08 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
326d376e6d
commit
12856abe4d
|
@ -1,3 +1,10 @@
|
|||
2011-04-25 Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
|
||||
* Added the concept of "resources" in SOGo in order
|
||||
to avoid double-bookings (if not more) and also, handle
|
||||
auto-accepts. This works for the web interface and
|
||||
over DAV - generating 403 errors in case of a conflict.
|
||||
|
||||
2011-04-21 Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
|
||||
* Added the possibility of translating IMAP namespaces
|
||||
|
|
|
@ -60,3 +60,6 @@ vtodo_class2 = "(Confidential task)";
|
|||
= "%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}has not yet decided upon your event invitation.";
|
||||
|
||||
/* Resources */
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\".";
|
|
@ -60,3 +60,6 @@ vtodo_class2 = "(Tâche confidentielle)";
|
|||
= "%{Attendee} %{SentByText}a délégué votre invitation à %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}choisit de reporter sa décision par rapport à votre invitation.";
|
||||
|
||||
/* Resources */
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Le nombre maximum (%{NumberOfSimultaneousBookings}) de réservation(s) simultanée(s) a été atteint pour la ressource \"%{Cn} %{SystemEmail}\".";
|
|
@ -2,14 +2,14 @@
|
|||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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.
|
||||
|
@ -623,6 +623,9 @@ static NSNumber *sharedYes = nil;
|
|||
return record;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSArray *) fixupRecords: (NSArray *) theRecords
|
||||
{
|
||||
// TODO: is the result supposed to be sorted by date?
|
||||
|
@ -802,6 +805,9 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
|||
[self _fixExceptionRecord: newRecord fromRow: row];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) _appendCycleExceptionsFromRow: (NSDictionary *) row
|
||||
firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
forRange: (NGCalendarDateRange *) dateRange
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2007-2010 Inverse inc.
|
||||
Copyright (C) 2007-2011 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.
|
||||
|
@ -25,17 +24,6 @@
|
|||
|
||||
#import <SOGo/SOGoContentObject.h>
|
||||
|
||||
/*
|
||||
SOGoAppointmentObject
|
||||
|
||||
Represents a single appointment. This SOPE controller object manages all the
|
||||
attendee storages (that is, it might store into multiple folders for meeting
|
||||
appointments!).
|
||||
|
||||
Note: SOGoAppointmentObject do not need to exist yet. They can also be "new"
|
||||
appointments with an externally generated unique key.
|
||||
*/
|
||||
|
||||
@class NSArray;
|
||||
@class NSException;
|
||||
@class NSString;
|
||||
|
@ -61,13 +49,6 @@
|
|||
- (NSArray *) postCalDAVEventReplyTo: (NSArray *) recipients from: (NSString *) originator;
|
||||
- (NSArray *) postCalDAVEventCancelTo: (NSArray *) recipients from: (NSString *) originator;
|
||||
|
||||
/* "iCal multifolder saves" */
|
||||
|
||||
// - (NSException *) saveContentString: (NSString *) _iCal
|
||||
// baseSequence: (int) _v;
|
||||
// - (NSException *) deleteWithBaseSequence: (int) _v;
|
||||
// - (NSException *) saveContentString: (NSString *) _iCalString;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __Appointments_SOGoAppointmentObject_H__ */
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo
|
||||
|
||||
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
|
||||
|
@ -42,6 +42,7 @@
|
|||
|
||||
#import <SOGo/SOGoUserManager.h>
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/NSObject+DAV.h>
|
||||
#import <SOGo/SOGoObject.h>
|
||||
#import <SOGo/SOGoPermissions.h>
|
||||
|
@ -198,7 +199,6 @@
|
|||
// We check if the attendee that was added to a single occurence is
|
||||
// present in the master component. If not, we add it with a participation
|
||||
// status set to "DECLINED".
|
||||
|
||||
user = [SOGoUser userWithLogin: theUID];
|
||||
person = [iCalPerson elementWithTag: @"attendee"];
|
||||
[person setCn: [user cn]];
|
||||
|
@ -425,14 +425,117 @@
|
|||
}
|
||||
|
||||
//
|
||||
// This methods scans the list of attendees. If they are
|
||||
// considered as resource, it checks for conflicting
|
||||
// dates for the event.
|
||||
//
|
||||
// We check for between startDate + 1 second and
|
||||
// endDate - 1 second
|
||||
//
|
||||
//
|
||||
- (void) _handleAddedUsers: (NSArray *) attendees
|
||||
fromEvent: (iCalEvent *) newEvent
|
||||
// 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
|
||||
forEvent: (iCalEvent *) theEvent
|
||||
{
|
||||
NSEnumerator *enumerator;
|
||||
iCalPerson *currentAttendee;
|
||||
NSEnumerator *enumerator;
|
||||
NSString *currentUID;
|
||||
SOGoUser *user;
|
||||
|
||||
enumerator = [theAttendees objectEnumerator];
|
||||
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
|
||||
if (currentUID)
|
||||
{
|
||||
user = [SOGoUser userWithLogin: currentUID];
|
||||
|
||||
if ([user isResource])
|
||||
{
|
||||
SOGoAppointmentFolder *folder;
|
||||
NSCalendarDate *start, *end;
|
||||
NSMutableArray *fbInfo;
|
||||
int i;
|
||||
|
||||
start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1];
|
||||
end = [[theEvent endDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -1];
|
||||
|
||||
folder = [[SOGoUser userWithLogin: currentUID]
|
||||
personalCalendarFolderInContext: context];
|
||||
|
||||
|
||||
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.
|
||||
for (i = [fbInfo count]-1; i >= 0; i--)
|
||||
{
|
||||
if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame)
|
||||
[fbInfo removeObjectAtIndex: i];
|
||||
}
|
||||
|
||||
if ([fbInfo count])
|
||||
{
|
||||
// If we always force the auto-accept if numberOfSimultaneousBookings == 0 (ie., no limit
|
||||
// is imposed) or if numberOfSimultaneousBookings is greater than the number of
|
||||
// overlapping events
|
||||
if ([user numberOfSimultaneousBookings] == 0 ||
|
||||
[user numberOfSimultaneousBookings] > [fbInfo count])
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
else
|
||||
{
|
||||
NSDictionary *values;
|
||||
NSString *reason;
|
||||
|
||||
values = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail",
|
||||
nil];
|
||||
|
||||
reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"."]];
|
||||
|
||||
return [NSException exceptionWithHTTPStatus:403
|
||||
reason: reason];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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 setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSException *) _handleAddedUsers: (NSArray *) attendees
|
||||
fromEvent: (iCalEvent *) newEvent
|
||||
{
|
||||
iCalPerson *currentAttendee;
|
||||
NSEnumerator *enumerator;
|
||||
NSString *currentUID;
|
||||
NSException *e;
|
||||
|
||||
// We check for conflicts
|
||||
if ((e = [self _handleResourcesConflicts: attendees forEvent: newEvent]))
|
||||
return e;
|
||||
|
||||
enumerator = [attendees objectEnumerator];
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
|
@ -443,25 +546,31 @@
|
|||
forUID: currentUID
|
||||
owner: owner];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) _handleUpdatedEvent: (iCalEvent *) newEvent
|
||||
fromOldEvent: (iCalEvent *) oldEvent
|
||||
- (NSException *) _handleUpdatedEvent: (iCalEvent *) newEvent
|
||||
fromOldEvent: (iCalEvent *) oldEvent
|
||||
{
|
||||
NSArray *attendees;
|
||||
iCalEventChanges *changes;
|
||||
NSArray *attendees;
|
||||
NSException *ex;
|
||||
|
||||
changes = [newEvent getChangesRelativeToEvent: oldEvent];
|
||||
if ([changes sequenceShouldBeIncreased])
|
||||
{
|
||||
// Set new attendees status to "needs action" and recompute changes when
|
||||
// the list of attendees has changed.
|
||||
// the list of attendees has changed. The list might have changed since
|
||||
// by changing a major property of the event, we remove all the delegation
|
||||
// chains to "other" attendees
|
||||
if ([self _requireResponseFromAttendees: newEvent])
|
||||
changes = [newEvent getChangesRelativeToEvent: oldEvent];
|
||||
}
|
||||
|
||||
attendees = [changes deletedAttendees];
|
||||
if ([attendees count])
|
||||
{
|
||||
|
@ -475,6 +584,10 @@
|
|||
forObject: newEvent to: attendees];
|
||||
}
|
||||
|
||||
if ((ex = [self _handleResourcesConflicts: [newEvent attendees]
|
||||
forEvent: newEvent]))
|
||||
return ex;
|
||||
|
||||
attendees = [changes insertedAttendees];
|
||||
if ([changes sequenceShouldBeIncreased])
|
||||
{
|
||||
|
@ -516,7 +629,9 @@
|
|||
if ([attendees count])
|
||||
{
|
||||
// Send an invitation to new attendees
|
||||
[self _handleAddedUsers: attendees fromEvent: newEvent];
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent]))
|
||||
return ex;
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
forObject: [newEvent itipEntryWithMethod: @"request"]
|
||||
previousObject: oldEvent
|
||||
|
@ -524,6 +639,8 @@
|
|||
[self sendReceiptEmailUsingTemplateNamed: @"Invitation"
|
||||
forObject: newEvent to: attendees];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -551,13 +668,14 @@
|
|||
// _updateAttendee:withDelegate:ownerUser:forEventUID:withRecurrenceId:withSequence:forUID:shouldAddSentBy:
|
||||
//
|
||||
//
|
||||
- (void) saveComponent: (iCalEvent *) newEvent
|
||||
- (NSException *) saveComponent: (iCalEvent *) newEvent
|
||||
{
|
||||
iCalEvent *oldEvent, *oldMasterEvent;
|
||||
NSArray *attendees;
|
||||
NSCalendarDate *recurrenceId;
|
||||
NSString *recurrenceTime;
|
||||
SOGoUser *ownerUser;
|
||||
NSArray *attendees;
|
||||
NSException *ex;
|
||||
|
||||
[[newEvent parent] setMethod: @""];
|
||||
ownerUser = [SOGoUser userWithLogin: owner];
|
||||
|
@ -574,7 +692,11 @@
|
|||
attendees = [newEvent attendeesWithoutUser: ownerUser];
|
||||
if ([attendees count])
|
||||
{
|
||||
[self _handleAddedUsers: attendees fromEvent: newEvent];
|
||||
// We catch conflicts and abort the save process immediately
|
||||
// in case of one with resources
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent]))
|
||||
return ex;
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
forObject: [newEvent itipEntryWithMethod: @"request"]
|
||||
previousObject: nil
|
||||
|
@ -586,6 +708,7 @@
|
|||
else
|
||||
{
|
||||
BOOL hasOrganizer;
|
||||
|
||||
// Event is modified -- sent update status to all attendees
|
||||
// and modify their calendars.
|
||||
recurrenceId = [newEvent recurrenceId];
|
||||
|
@ -606,8 +729,10 @@
|
|||
hasOrganizer = [[[oldMasterEvent organizer] email] length];
|
||||
|
||||
if (!hasOrganizer || [oldMasterEvent userIsOrganizer: ownerUser])
|
||||
// The owner is the organizer of the event; handle the modifications
|
||||
[self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent];
|
||||
// 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]))
|
||||
return ex;
|
||||
}
|
||||
|
||||
[super saveComponent: newEvent];
|
||||
|
@ -618,6 +743,8 @@
|
|||
safeCalendar = nil;
|
||||
[originalCalendar release];
|
||||
originalCalendar = nil;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -1479,6 +1606,7 @@
|
|||
//
|
||||
- (id) PUTAction: (WOContext *) _ctx
|
||||
{
|
||||
NSException *ex;
|
||||
NSArray *roles;
|
||||
WORequest *rq;
|
||||
id response;
|
||||
|
@ -1540,7 +1668,9 @@
|
|||
attendees = [event attendeesWithoutUser: ownerUser];
|
||||
if ([attendees count])
|
||||
{
|
||||
[self _handleAddedUsers: attendees fromEvent: event];
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: event]))
|
||||
return ex;
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
forObject: [event itipEntryWithMethod: @"request"]
|
||||
previousObject: nil
|
||||
|
@ -1680,7 +1810,8 @@
|
|||
|
||||
if ([uid caseInsensitiveCompare: owner] == NSOrderedSame)
|
||||
{
|
||||
[self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent];
|
||||
if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent]))
|
||||
return ex;
|
||||
|
||||
// A RECURRENCE-ID was removed so there has to be a change in the master event
|
||||
// We could also have an EXDATE added in the master component of the attendees
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
toFolder: (SOGoGCSFolder *) newFolder;
|
||||
|
||||
- (void) updateComponent: (iCalRepeatableEntityObject *) newObject;
|
||||
- (void) saveComponent: (iCalRepeatableEntityObject *) newObject;
|
||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject;
|
||||
|
||||
/* mail notifications */
|
||||
- (void) sendEMailUsingTemplateNamed: (NSString *) pageName
|
||||
|
|
|
@ -655,13 +655,15 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence,
|
|||
}
|
||||
}
|
||||
|
||||
- (void) saveComponent: (iCalRepeatableEntityObject *) newObject
|
||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject
|
||||
{
|
||||
NSString *newiCalString;
|
||||
|
||||
newiCalString = [[newObject parent] versitString];
|
||||
|
||||
[self saveContentString: newiCalString];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* raw saving */
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
/*
|
||||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2000-2004 SKYRIX Software AG
|
||||
|
||||
This file is part of OGo
|
||||
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.
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
/*
|
||||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2000-2004 SKYRIX Software AG
|
||||
|
||||
This file is part of OGo
|
||||
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.
|
||||
|
|
|
@ -68,6 +68,10 @@
|
|||
BOOL passwordPolicy;
|
||||
|
||||
NSMutableDictionary *_dnCache;
|
||||
|
||||
/* resources handling */
|
||||
NSString *kindField;
|
||||
NSString *multipleBookingsField;
|
||||
}
|
||||
|
||||
- (void) setBindDN: (NSString *) newBindDN
|
||||
|
@ -85,7 +89,9 @@
|
|||
searchFields: (NSArray *) newSearchFields
|
||||
IMAPHostField: (NSString *) newIMAPHostField
|
||||
IMAPLoginField: (NSString *) newIMAPLoginField
|
||||
andBindFields: (id) newBindFields;
|
||||
bindFields: (id) newBindFields
|
||||
kindField: (NSString *) newKindField
|
||||
andMultipleBookingsField: (NSString *) newMultipleBookingsField;
|
||||
|
||||
- (NGLdapEntry *) lookupGroupEntryByUID: (NSString *) theUID;
|
||||
- (NGLdapEntry *) lookupGroupEntryByEmail: (NSString *) theEmail;
|
||||
|
|
|
@ -170,6 +170,9 @@ static NSArray *commonSearchFields;
|
|||
searchAttributes = nil;
|
||||
passwordPolicy = NO;
|
||||
|
||||
kindField = nil;
|
||||
multipleBookingsField = nil;
|
||||
|
||||
_dnCache = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
|
@ -198,6 +201,8 @@ static NSArray *commonSearchFields;
|
|||
[searchAttributes release];
|
||||
[domain release];
|
||||
[_dnCache release];
|
||||
[kindField release];
|
||||
[multipleBookingsField release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
@ -226,7 +231,9 @@ static NSArray *commonSearchFields;
|
|||
searchFields: [udSource objectForKey: @"SearchFieldNames"]
|
||||
IMAPHostField: [udSource objectForKey: @"IMAPHostFieldName"]
|
||||
IMAPLoginField: [udSource objectForKey: @"IMAPLoginFieldName"]
|
||||
andBindFields: [udSource objectForKey: @"bindFields"]];
|
||||
bindFields: [udSource objectForKey: @"bindFields"]
|
||||
kindField: [udSource objectForKey: @"KindFieldName"]
|
||||
andMultipleBookingsField: [udSource objectForKey: @"MultipleBookingsFieldName"]];
|
||||
|
||||
if ([sourceDomain length])
|
||||
{
|
||||
|
@ -309,7 +316,9 @@ static NSArray *commonSearchFields;
|
|||
searchFields: (NSArray *) newSearchFields
|
||||
IMAPHostField: (NSString *) newIMAPHostField
|
||||
IMAPLoginField: (NSString *) newIMAPLoginField
|
||||
andBindFields: (id) newBindFields
|
||||
bindFields: (id) newBindFields
|
||||
kindField: (NSString *) newKindField
|
||||
andMultipleBookingsField: (NSString *) newMultipleBookingsField
|
||||
{
|
||||
ASSIGN (baseDN, [newBaseDN lowercaseString]);
|
||||
if (newIDField)
|
||||
|
@ -349,6 +358,10 @@ static NSArray *commonSearchFields;
|
|||
ASSIGN(bindFields, [newBindFields componentsSeparatedByString: @","]);
|
||||
}
|
||||
}
|
||||
if (newKindField)
|
||||
ASSIGN(kindField, newKindField);
|
||||
if (newMultipleBookingsField)
|
||||
ASSIGN(multipleBookingsField, newMultipleBookingsField);
|
||||
}
|
||||
|
||||
- (BOOL) _setupEncryption: (NGLdapConnection *) encryptedConn
|
||||
|
@ -704,6 +717,13 @@ static NSArray *commonSearchFields;
|
|||
// Add IMAP login from user defaults
|
||||
if ([IMAPLoginField length])
|
||||
[searchAttributes addObjectUniquely: IMAPLoginField];
|
||||
|
||||
// Add the resources handling attributes
|
||||
if ([kindField length])
|
||||
[searchAttributes addObjectUniquely: kindField];
|
||||
|
||||
if ([multipleBookingsField length])
|
||||
[searchAttributes addObjectUniquely: multipleBookingsField];
|
||||
}
|
||||
|
||||
return searchAttributes;
|
||||
|
@ -855,6 +875,14 @@ static NSArray *commonSearchFields;
|
|||
[contactEntry setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"isGroup"];
|
||||
}
|
||||
// We check if our entry is a resource. We also support
|
||||
// determining resources based on the KindFieldName attribute
|
||||
// value - see below.
|
||||
else if ([classes containsObject: @"calendarresource"])
|
||||
{
|
||||
[contactEntry setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"isResource"];
|
||||
}
|
||||
}
|
||||
|
||||
while ((currentAttribute = [attributes nextObject]))
|
||||
|
@ -864,7 +892,37 @@ static NSArray *commonSearchFields;
|
|||
|
||||
// It's important here to set our attributes' key in lowercase.
|
||||
if (value)
|
||||
[contactEntry setObject: value forKey: [currentAttribute lowercaseString]];
|
||||
{
|
||||
currentAttribute = [currentAttribute lowercaseString];
|
||||
[contactEntry setObject: value forKey: currentAttribute];
|
||||
|
||||
// We check if that entry corresponds to a resource. For this,
|
||||
// kindField must be defined and it must hold one of those values
|
||||
//
|
||||
// location
|
||||
// thing
|
||||
// group
|
||||
//
|
||||
if (kindField &&
|
||||
[kindField caseInsensitiveCompare: currentAttribute] == NSOrderedSame)
|
||||
{
|
||||
if ([value caseInsensitiveCompare: @"location"] == NSOrderedSame ||
|
||||
[value caseInsensitiveCompare: @"thing"] == NSOrderedSame ||
|
||||
[value caseInsensitiveCompare: @"group"] == NSOrderedSame)
|
||||
{
|
||||
[contactEntry setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"isResource"];
|
||||
}
|
||||
}
|
||||
// We check for the number of simultanous bookings that is allowed.
|
||||
// A value of 0 means that there's no limit.
|
||||
if (multipleBookingsField &&
|
||||
[multipleBookingsField caseInsensitiveCompare: currentAttribute] == NSOrderedSame)
|
||||
{
|
||||
[contactEntry setObject: [NSNumber numberWithInt: [value intValue]]
|
||||
forKey: @"numberOfSimultaneousBookings"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = [[ldapEntry attributeWithName: IDField] stringValueAtIndex: 0];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SOGoGroup.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009-2010 Inverse inc.
|
||||
* Copyright (C) 2009-2011 Inverse inc.
|
||||
*
|
||||
* Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2006-2010 Inverse inc.
|
||||
Copyright (C) 2006-2011 Inverse inc.
|
||||
Copyright (C) 2005 SKYRIX Software AG
|
||||
|
||||
This file is part of SOGo.
|
||||
|
@ -114,16 +114,17 @@
|
|||
- (BOOL) isSuperUser;
|
||||
- (BOOL) canAuthenticate;
|
||||
|
||||
/* resource */
|
||||
- (BOOL) isResource;
|
||||
- (int) numberOfSimultaneousBookings;
|
||||
|
||||
/* module access */
|
||||
- (BOOL) canAccessModule: (NSString *) module;
|
||||
|
||||
/* folders */
|
||||
|
||||
- (SOGoUserFolder *) homeFolderInContext: (id) context;
|
||||
|
||||
- (SOGoAppointmentFolders *) calendarsFolderInContext: (WOContext *) context;
|
||||
- (SOGoAppointmentFolder *)
|
||||
personalCalendarFolderInContext: (WOContext *) context;
|
||||
- (SOGoAppointmentFolder *) personalCalendarFolderInContext: (WOContext *) context;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -766,6 +766,28 @@
|
|||
return [authValue boolValue];
|
||||
}
|
||||
|
||||
/* resource */
|
||||
- (BOOL) isResource
|
||||
{
|
||||
NSNumber *v;
|
||||
|
||||
v = [self _fetchFieldForUser: @"isResource"];
|
||||
|
||||
return (v && [v intValue]);
|
||||
}
|
||||
|
||||
- (int) numberOfSimultaneousBookings
|
||||
{
|
||||
NSNumber *v;
|
||||
|
||||
v = [self _fetchFieldForUser: @"numberOfSimultaneousBookings"];
|
||||
|
||||
if (v)
|
||||
return [v intValue];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* module access */
|
||||
- (BOOL) canAccessModule: (NSString *) module
|
||||
{
|
||||
|
|
|
@ -548,6 +548,9 @@
|
|||
[contact setObject: [emails objectAtIndex: 0] forKey: @"c_email"];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) _fillContactInfosForUser: (NSMutableDictionary *) currentUser
|
||||
withUIDorEmail: (NSString *) uid
|
||||
{
|
||||
|
@ -600,6 +603,15 @@
|
|||
if (!access)
|
||||
[currentUser setObject: [NSNumber numberWithBool: NO]
|
||||
forKey: @"MailAccess"];
|
||||
|
||||
// We also fill the resource attributes, if any
|
||||
if ([userEntry objectForKey: @"isResource"])
|
||||
[currentUser setObject: [userEntry objectForKey: @"isResource"]
|
||||
forKey: @"isResource"];
|
||||
if ([userEntry objectForKey: @"numberOfSimultaneousBookings"])
|
||||
[currentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"]
|
||||
forKey: @"numberOfSimultaneousBookings"];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -614,6 +626,7 @@
|
|||
[currentUser setObject: c_imaphostname forKey: @"c_imaphostname"];
|
||||
if (c_imaplogin)
|
||||
[currentUser setObject: c_imaplogin forKey: @"c_imaplogin"];
|
||||
|
||||
[currentUser setObject: emails forKey: @"emails"];
|
||||
[currentUser setObject: cn forKey: @"cn"];
|
||||
[currentUser setObject: c_uid forKey: @"c_uid"];
|
||||
|
|
|
@ -165,8 +165,8 @@
|
|||
"InboxFolderName" = "Posteingang";
|
||||
"DraftsFolderName" = "Entwürfe";
|
||||
"SieveFolderName" = "Filter";
|
||||
"OtherUsersFolderName" = "Other Users";
|
||||
"SharedFoldersName" = "Shared Folders";
|
||||
"Other Users" = "Andere Benutzer";
|
||||
"Shared Folders" = "Gemeinsame Ordner";
|
||||
"Folders" = "Ordner"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
/*
|
||||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2004 SKYRIX Software AG
|
||||
|
||||
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.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* UIxJSClose.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006 Inverse inc.
|
||||
* Copyright (C) 2006-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* UIxJSClose.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006 Inverse inc.
|
||||
* Copyright (C) 2006-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
|
|
|
@ -378,6 +378,7 @@
|
|||
{
|
||||
SOGoAppointmentFolder *previousCalendar;
|
||||
SOGoAppointmentObject *co;
|
||||
NSString *jsonResponse;
|
||||
SoSecurityManager *sm;
|
||||
NSException *ex;
|
||||
|
||||
|
@ -386,6 +387,7 @@
|
|||
co = [co container];
|
||||
previousCalendar = [co container];
|
||||
sm = [SoSecurityManager sharedSecurityManager];
|
||||
ex = nil;
|
||||
|
||||
if ([event hasRecurrenceRules])
|
||||
[self _adjustRecurrentRules];
|
||||
|
@ -409,12 +411,12 @@
|
|||
}
|
||||
|
||||
// Save the event.
|
||||
[co saveComponent: event];
|
||||
ex = [co saveComponent: event];
|
||||
}
|
||||
else
|
||||
{
|
||||
// The event was modified -- save it.
|
||||
[co saveComponent: event];
|
||||
ex = [co saveComponent: event];
|
||||
|
||||
if (componentCalendar
|
||||
&& ![[componentCalendar ocsPath]
|
||||
|
@ -433,7 +435,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
return [self jsCloseWithRefreshMethod: @"refreshEventsAndDisplay()"];
|
||||
if (ex)
|
||||
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"failure", @"status",
|
||||
[ex reason],
|
||||
@"message",
|
||||
nil];
|
||||
else
|
||||
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"success", @"status", nil];
|
||||
|
||||
return [self responseWithStatus: 200
|
||||
andString: [jsonResponse jsonRepresentation]];
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) viewAction
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<form var:href="saveURL" name="editform" onsubmit="return validateAptEditor();">
|
||||
<form var:href="saveURL" name="editform">
|
||||
<div id="eventView">
|
||||
<label><var:string label:value="Title:" /><span class="content"
|
||||
><input type="text" name="summary" id="summary"
|
||||
|
|
|
@ -96,6 +96,8 @@ function validateAptEditor() {
|
|||
}
|
||||
}
|
||||
|
||||
AIM.submit($(document.editform), {'onComplete' : onEventPostComplete});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -176,6 +178,21 @@ function addContact(tag, fullContactName, contactId, contactName, contactEmail)
|
|||
return false;
|
||||
}
|
||||
|
||||
function onEventPostComplete(response) {
|
||||
if (response && response.length > 0) {
|
||||
var jsonResponse = response.evalJSON();
|
||||
if (jsonResponse["status"] == "success") {
|
||||
if (window.opener)
|
||||
window.opener.refreshEventsAndDisplay();
|
||||
window.close();
|
||||
}
|
||||
else {
|
||||
var message = jsonResponse["message"];
|
||||
alert(jsonResponse["message"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function saveEvent(sender) {
|
||||
if (validateAptEditor()) {
|
||||
document.forms['editform'].attendees.value = Object.toJSON($(attendees));
|
||||
|
|
Loading…
Reference in New Issue