Interface AgendaAvailabilityService
Every method takes the asking user's identity, and every method enforces it. Availability is calendar content: knowing when someone is busy is knowing that they have a meeting, and over a long enough window it maps their working life.
Two rules decide, in this order, and there is exactly one place in the implementation where they are applied:
Utils.canAccessCalendar— the same checkAgendaEventService.getEvents(org.exoplatform.agenda.model.EventFilter, java.time.ZoneId, long)applies to a calendar owner. For a personal calendar it resolves to "its owner, and no one else"; for a space calendar, to whoever may view the space. This is the floor: whatever it permits is always permitted.- the target user's
AvailabilitySharingsetting — read fromAgendaUserSettingsService.getAvailabilitySharing(long), which owns it — and which can only ever widen that floor, by exactly one step and only for a target that is a user: the people that user shares a space with. A space's availability is never disclosed by anyone's personal setting, no setting lets a viewer read a calendar some other rule keeps from them, and nobody outside all of the target's spaces is ever admitted.
What is disclosed does not change with the setting: time ranges, busy and free. Never a title, a location, an attendee or a calendar name. That includes busy time derived from a calendar the user connected to the platform, whose events are materialised as ordinary eXo events — the fact that they are busy is disclosed, what they are doing is not.
A user who discloses nothing is unknown, never available.
The methods that must answer refuse (IllegalAccessException); the
methods that enrich a write the user may already perform degrade and name
whom they could not check, so that "no clash was found" is never read as
"everyone is free".
-
Method Summary
Modifier and TypeMethodDescriptiongetAvailability(List<Long> targetIdentityIds, ZonedDateTime start, ZonedDateTime end, long userIdentityId) Computes the busy and free blocks of each given user over a window.getBusyTime(List<Long> targetIdentityIds, ZonedDateTime start, ZonedDateTime end, long userIdentityId) Reports, for each given user, their busy time over a window and what happened when it was asked for.getConflicts(List<Long> attendeeIdentityIds, ZonedDateTime start, ZonedDateTime end, long userIdentityId) Reports which of the given attendees clash with a proposed window.rankDateOptionsByAvailability(List<EventDateOption> dateOptions, List<Long> attendeeIdentityIds, long userIdentityId) Orders date-poll options so that the ones suiting the most attendees come first.suggestMeetingTime(List<Long> attendeeIdentityIds, Duration duration, ZonedDateTime windowStart, ZonedDateTime windowEnd, boolean morningsOnly, boolean afternoonsOnly, int limit, long userIdentityId) Proposes the slots of the given length, inside the given window, over which every one of the given attendees is free.
-
Method Details
-
getAvailability
List<UserAvailability> getAvailability(List<Long> targetIdentityIds, ZonedDateTime start, ZonedDateTime end, long userIdentityId) throws IllegalAccessException Computes the busy and free blocks of each given user over a window.- Parameters:
targetIdentityIds- technical identifiers of the users whose availability is asked forstart- window start, inclusiveend- window end, exclusiveuserIdentityId- technical identifier of the user asking- Returns:
- one
UserAvailabilityper requested user, in the order asked - Throws:
IllegalAccessException- when the asking user is not allowed to read the availability of any one of the requested usersIllegalArgumentException- when the window or the user list is missing or empty, or when the window ends before it starts
-
getBusyTime
List<UserBusyTime> getBusyTime(List<Long> targetIdentityIds, ZonedDateTime start, ZonedDateTime end, long userIdentityId) Reports, for each given user, their busy time over a window and what happened when it was asked for.Like
getConflicts(java.util.List<java.lang.Long>, java.time.ZonedDateTime, java.time.ZonedDateTime, long)and unlikegetAvailability(java.util.List<java.lang.Long>, java.time.ZonedDateTime, java.time.ZonedDateTime, long), this method does not refuse: it answers for every user it was asked about, saying per user whether a calendar was actually read. It is the shape a screen needs — one that draws what it knows and has to name what it does not — wheregetAvailability(java.util.List<java.lang.Long>, java.time.ZonedDateTime, java.time.ZonedDateTime, long)'s all-or-nothing refusal would blank a whole grid because one participant shares nothing.It adds no gate. Every user in the answer went through the same single gate as every other read in this service; this method only reports its verdict per user instead of throwing on the first refusal.
The returned list has one entry per distinct requested identifier, in the order asked, and never omits one — an absent entry would be an unstated status, which is the very thing
AvailabilityDisclosureexists to prevent. A requested identifier that is not a user (a space, or nothing at all) comes back asAvailabilityDisclosure.NOT_DISCLOSED, which is literally true of it: no busy time was disclosed for it.- Parameters:
targetIdentityIds- technical identifiers of the users whose busy time is asked forstart- window start, inclusiveend- window end, exclusiveuserIdentityId- technical identifier of the user asking- Returns:
- one
UserBusyTimeper distinct requested user, in the order asked, nevernull - Throws:
IllegalArgumentException- when the window or the user list is missing or empty, or when the window ends before it starts
-
suggestMeetingTime
List<TimeBlock> suggestMeetingTime(List<Long> attendeeIdentityIds, Duration duration, ZonedDateTime windowStart, ZonedDateTime windowEnd, boolean morningsOnly, boolean afternoonsOnly, int limit, long userIdentityId) throws IllegalAccessException Proposes the slots of the given length, inside the given window, over which every one of the given attendees is free.The proposal is only worth as much as the calendars behind it, so this method refuses rather than guesses: if the asking user cannot read one attendee's availability, no slot is proposed at all. Silently proposing slots computed from a subset of the attendees would return a confident answer that is not backed by the data.
- Parameters:
attendeeIdentityIds- technical identifiers of the users who must all be freeduration- the length a slot must havewindowStart- earliest acceptable startwindowEnd- latest acceptable endmorningsOnly- restrict candidate starts to before noonafternoonsOnly- restrict candidate starts to noon and afterlimit- maximum number of slots to proposeuserIdentityId- technical identifier of the user asking- Returns:
- the candidate slots, earliest first, at most
limitof them - Throws:
IllegalAccessException- when the asking user is not allowed to read the availability of any one of the attendeesIllegalArgumentException- when the attendee list, the duration or the window is missing or invalid
-
getConflicts
AvailabilityConflicts getConflicts(List<Long> attendeeIdentityIds, ZonedDateTime start, ZonedDateTime end, long userIdentityId) Reports which of the given attendees clash with a proposed window.Unlike
getAvailability(java.util.List<java.lang.Long>, java.time.ZonedDateTime, java.time.ZonedDateTime, long)this method does not refuse: it is an enrichment of an operation the user is already allowed to perform — creating or updating an event — and failing it would deny a legitimate write over information the user never asked for. It reports on the attendees it may read and names the ones it may not, so the answer is partial but never misleading. SeeAvailabilityConflicts.- Parameters:
attendeeIdentityIds- technical identifiers of the event's attendees; non-user identities are ignoredstart- proposed window startend- proposed window enduserIdentityId- technical identifier of the user asking- Returns:
- the clash report, never
null
-
rankDateOptionsByAvailability
List<EventDateOption> rankDateOptionsByAvailability(List<EventDateOption> dateOptions, List<Long> attendeeIdentityIds, long userIdentityId) Orders date-poll options so that the ones suiting the most attendees come first.Only the attendees the asking user may read are counted; an attendee whose availability is not readable counts for no option, so they shift no option relative to another. Like
getConflicts(java.util.List<java.lang.Long>, java.time.ZonedDateTime, java.time.ZonedDateTime, long)this degrades rather than refusing — an unreadable attendee costs ordering quality, not the poll.- Parameters:
dateOptions- the options to orderattendeeIdentityIds- technical identifiers of the event's attendeesuserIdentityId- technical identifier of the user asking- Returns:
- the options, most-available first, keeping the original order between equally available ones
-