parent
b3dc645282
commit
a6424680cc
1
NEWS
1
NEWS
|
@ -31,6 +31,7 @@ Bug fixes
|
||||||
- fixed handling of the '=' character in cards/events/tasks (#2505)
|
- fixed handling of the '=' character in cards/events/tasks (#2505)
|
||||||
- simplify searches in the address book (#2187)
|
- simplify searches in the address book (#2187)
|
||||||
- warn user when dnd failed because of a resource conflict (#1613)
|
- warn user when dnd failed because of a resource conflict (#1613)
|
||||||
|
- respect the maximum number of bookings when viewing the freebusy information of a resource (#2560)
|
||||||
|
|
||||||
2.1.1b (2013-12-04)
|
2.1.1b (2013-12-04)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
|
@ -91,67 +91,90 @@
|
||||||
NSDictionary *record;
|
NSDictionary *record;
|
||||||
SOGoUser *user;
|
SOGoUser *user;
|
||||||
|
|
||||||
int recordCount, recordMax, count, startInterval, endInterval, i, type;
|
int recordCount, recordMax, count, startInterval, endInterval, i, type, maxBookings, isResource;
|
||||||
|
|
||||||
recordMax = [records count];
|
recordMax = [records count];
|
||||||
user = [SOGoUser userWithLogin: [[self clientObject] ownerInContext: context]
|
user = [SOGoUser userWithLogin: [[self clientObject] ownerInContext: context] roles: nil];
|
||||||
roles: nil];
|
maxBookings = [user numberOfSimultaneousBookings];
|
||||||
|
isResource = [user isResource];
|
||||||
|
|
||||||
for (recordCount = 0; recordCount < recordMax; recordCount++)
|
// Don't fetch freebusy information if the user is of type 'resource' and has unlimited bookings
|
||||||
|
if (!isResource || maxBookings > 0)
|
||||||
{
|
{
|
||||||
record = [records objectAtIndex: recordCount];
|
for (recordCount = 0; recordCount < recordMax; recordCount++)
|
||||||
if ([[record objectForKey: @"c_isopaque"] boolValue])
|
{
|
||||||
{
|
record = [records objectAtIndex: recordCount];
|
||||||
type = 0;
|
if ([[record objectForKey: @"c_isopaque"] boolValue])
|
||||||
|
{
|
||||||
|
type = 0;
|
||||||
|
|
||||||
// If the event has NO organizer (which means it's the user that has created it) OR
|
// If the event has NO organizer (which means it's the user that has created it) OR
|
||||||
// If we are the organizer of the event THEN we are automatically busy
|
// If we are the organizer of the event THEN we are automatically busy
|
||||||
if ([[record objectForKey: @"c_orgmail"] length] == 0 ||
|
if ([[record objectForKey: @"c_orgmail"] length] == 0 ||
|
||||||
[user hasEmail: [record objectForKey: @"c_orgmail"]])
|
[user hasEmail: [record objectForKey: @"c_orgmail"]])
|
||||||
{
|
{
|
||||||
type = 1;
|
type = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// We check if the user has accepted/declined or needs action
|
// We check if the user has accepted/declined or needs action
|
||||||
// on the current event.
|
// on the current event.
|
||||||
emails = [[record objectForKey: @"c_partmails"] componentsSeparatedByString: @"\n"];
|
emails = [[record objectForKey: @"c_partmails"] componentsSeparatedByString: @"\n"];
|
||||||
|
|
||||||
for (i = 0; i < [emails count]; i++)
|
for (i = 0; i < [emails count]; i++)
|
||||||
{
|
{
|
||||||
if ([user hasEmail: [emails objectAtIndex: i]])
|
if ([user hasEmail: [emails objectAtIndex: i]])
|
||||||
{
|
{
|
||||||
// We now fetch the c_partstates array and get the participation
|
// We now fetch the c_partstates array and get the participation
|
||||||
// status of the user for the event
|
// status of the user for the event
|
||||||
partstates = [[record objectForKey: @"c_partstates"] componentsSeparatedByString: @"\n"];
|
partstates = [[record objectForKey: @"c_partstates"] componentsSeparatedByString: @"\n"];
|
||||||
|
|
||||||
if (i < [partstates count])
|
if (i < [partstates count])
|
||||||
{
|
{
|
||||||
type = ([[partstates objectAtIndex: i] intValue] < 2 ? 1 : 0);
|
type = ([[partstates objectAtIndex: i] intValue] < 2 ? 1 : 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDate = [record objectForKey: @"startDate"];
|
if (type == 1)
|
||||||
if ([currentDate earlierDate: startDate] == currentDate)
|
{
|
||||||
startInterval = 0;
|
// User is busy for this event; update items bit string
|
||||||
else
|
currentDate = [record objectForKey: @"startDate"];
|
||||||
startInterval = ([currentDate timeIntervalSinceDate: startDate]
|
if ([currentDate earlierDate: startDate] == currentDate)
|
||||||
/ intervalSeconds);
|
startInterval = 0;
|
||||||
|
else
|
||||||
|
startInterval = ([currentDate timeIntervalSinceDate: startDate]
|
||||||
|
/ intervalSeconds);
|
||||||
|
|
||||||
currentDate = [record objectForKey: @"endDate"];
|
currentDate = [record objectForKey: @"endDate"];
|
||||||
if ([currentDate earlierDate: endDate] == endDate)
|
if ([currentDate earlierDate: endDate] == endDate)
|
||||||
endInterval = itemCount - 1;
|
endInterval = itemCount - 1;
|
||||||
else
|
else
|
||||||
endInterval = ([currentDate timeIntervalSinceDate: startDate]
|
endInterval = ([currentDate timeIntervalSinceDate: startDate]
|
||||||
/ intervalSeconds);
|
/ intervalSeconds);
|
||||||
|
|
||||||
if (type == 1)
|
// Update bit string representation
|
||||||
for (count = startInterval; count < endInterval; count++)
|
// If the user is a resource, keep the sum of overlapping events
|
||||||
*(items + count) = 1;
|
for (count = startInterval; count < endInterval; count++)
|
||||||
}
|
{
|
||||||
|
*(items + count) = isResource ? *(items + count) + 1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxBookings > 0)
|
||||||
|
{
|
||||||
|
// Reset the freebusy for the periods that are bellow the maximum number of bookings
|
||||||
|
for (count = 0; count < itemCount; count++)
|
||||||
|
{
|
||||||
|
if (*(items + count) < maxBookings)
|
||||||
|
*(items + count) = 0;
|
||||||
|
else
|
||||||
|
*(items + count) = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,17 +191,23 @@
|
||||||
interval = [endDate timeIntervalSinceDate: startDate] + 60;
|
interval = [endDate timeIntervalSinceDate: startDate] + 60;
|
||||||
intervals = interval / intervalSeconds; /* slices of 15 minutes */
|
intervals = interval / intervalSeconds; /* slices of 15 minutes */
|
||||||
|
|
||||||
|
// Build a bit string representation of the freebusy data for the period
|
||||||
freeBusyItems = NSZoneCalloc (NULL, intervals, sizeof (int));
|
freeBusyItems = NSZoneCalloc (NULL, intervals, sizeof (int));
|
||||||
[self _fillFreeBusyItems: freeBusyItems count: intervals
|
[self _fillFreeBusyItems: freeBusyItems
|
||||||
|
count: intervals
|
||||||
withRecords: [fb fetchFreeBusyInfosFrom: startDate to: endDate forContact: uid]
|
withRecords: [fb fetchFreeBusyInfosFrom: startDate to: endDate forContact: uid]
|
||||||
fromStartDate: startDate toEndDate: endDate];
|
fromStartDate: startDate
|
||||||
|
toEndDate: endDate];
|
||||||
|
|
||||||
|
// Convert bit string to a NSArray
|
||||||
freeBusy = [NSMutableArray arrayWithCapacity: intervals];
|
freeBusy = [NSMutableArray arrayWithCapacity: intervals];
|
||||||
for (count = 0; count < intervals; count++)
|
for (count = 0; count < intervals; count++)
|
||||||
[freeBusy
|
{
|
||||||
addObject: [NSString stringWithFormat: @"%d", *(freeBusyItems + count)]];
|
[freeBusy addObject: [NSString stringWithFormat: @"%d", *(freeBusyItems + count)]];
|
||||||
|
}
|
||||||
NSZoneFree (NULL, freeBusyItems);
|
NSZoneFree (NULL, freeBusyItems);
|
||||||
|
|
||||||
|
// Return a NSString representation
|
||||||
return [freeBusy componentsJoinedByString: @","];
|
return [freeBusy componentsJoinedByString: @","];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue