diff --git a/NEWS b/NEWS index 4bc22406a..1ba21e5cb 100644 --- a/NEWS +++ b/NEWS @@ -31,6 +31,7 @@ Bug fixes - fixed handling of the '=' character in cards/events/tasks (#2505) - simplify searches in the address book (#2187) - 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) ------------------- diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index 623bbf135..1e2e361ae 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -91,67 +91,90 @@ NSDictionary *record; SOGoUser *user; - int recordCount, recordMax, count, startInterval, endInterval, i, type; + int recordCount, recordMax, count, startInterval, endInterval, i, type, maxBookings, isResource; recordMax = [records count]; - user = [SOGoUser userWithLogin: [[self clientObject] ownerInContext: context] - roles: nil]; + user = [SOGoUser userWithLogin: [[self clientObject] ownerInContext: context] 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]; - if ([[record objectForKey: @"c_isopaque"] boolValue]) - { - type = 0; + for (recordCount = 0; recordCount < recordMax; recordCount++) + { + record = [records objectAtIndex: recordCount]; + 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 we are the organizer of the event THEN we are automatically busy - if ([[record objectForKey: @"c_orgmail"] length] == 0 || - [user hasEmail: [record objectForKey: @"c_orgmail"]]) - { - type = 1; - } - else - { - // We check if the user has accepted/declined or needs action - // on the current event. - emails = [[record objectForKey: @"c_partmails"] componentsSeparatedByString: @"\n"]; + // 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 ([[record objectForKey: @"c_orgmail"] length] == 0 || + [user hasEmail: [record objectForKey: @"c_orgmail"]]) + { + type = 1; + } + else + { + // We check if the user has accepted/declined or needs action + // on the current event. + emails = [[record objectForKey: @"c_partmails"] componentsSeparatedByString: @"\n"]; - for (i = 0; i < [emails count]; i++) - { - if ([user hasEmail: [emails objectAtIndex: i]]) - { - // We now fetch the c_partstates array and get the participation - // status of the user for the event - partstates = [[record objectForKey: @"c_partstates"] componentsSeparatedByString: @"\n"]; + for (i = 0; i < [emails count]; i++) + { + if ([user hasEmail: [emails objectAtIndex: i]]) + { + // We now fetch the c_partstates array and get the participation + // status of the user for the event + partstates = [[record objectForKey: @"c_partstates"] componentsSeparatedByString: @"\n"]; - if (i < [partstates count]) - { - type = ([[partstates objectAtIndex: i] intValue] < 2 ? 1 : 0); - } - break; - } - } - } + if (i < [partstates count]) + { + type = ([[partstates objectAtIndex: i] intValue] < 2 ? 1 : 0); + } + break; + } + } + } - currentDate = [record objectForKey: @"startDate"]; - if ([currentDate earlierDate: startDate] == currentDate) - startInterval = 0; - else - startInterval = ([currentDate timeIntervalSinceDate: startDate] - / intervalSeconds); + if (type == 1) + { + // User is busy for this event; update items bit string + currentDate = [record objectForKey: @"startDate"]; + if ([currentDate earlierDate: startDate] == currentDate) + startInterval = 0; + else + startInterval = ([currentDate timeIntervalSinceDate: startDate] + / intervalSeconds); - currentDate = [record objectForKey: @"endDate"]; - if ([currentDate earlierDate: endDate] == endDate) - endInterval = itemCount - 1; - else - endInterval = ([currentDate timeIntervalSinceDate: startDate] - / intervalSeconds); + currentDate = [record objectForKey: @"endDate"]; + if ([currentDate earlierDate: endDate] == endDate) + endInterval = itemCount - 1; + else + endInterval = ([currentDate timeIntervalSinceDate: startDate] + / intervalSeconds); - if (type == 1) - for (count = startInterval; count < endInterval; count++) - *(items + count) = 1; - } + // Update bit string representation + // If the user is a resource, keep the sum of overlapping events + 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; 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)); - [self _fillFreeBusyItems: freeBusyItems count: intervals + [self _fillFreeBusyItems: freeBusyItems + count: intervals 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]; for (count = 0; count < intervals; count++) - [freeBusy - addObject: [NSString stringWithFormat: @"%d", *(freeBusyItems + count)]]; + { + [freeBusy addObject: [NSString stringWithFormat: @"%d", *(freeBusyItems + count)]]; + } NSZoneFree (NULL, freeBusyItems); + // Return a NSString representation return [freeBusy componentsJoinedByString: @","]; }