Interface AgendaAvailabilityService


public interface AgendaAvailabilityService
Free/busy over the platform's own calendars, under the platform's own calendar ACL and the target user's own sharing choice.

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:

  1. Utils.canAccessCalendar — the same check AgendaEventService.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.
  2. the target user's AvailabilitySharing setting — read from AgendaUserSettingsService.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 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 for
      start - window start, inclusive
      end - window end, exclusive
      userIdentityId - technical identifier of the user asking
      Returns:
      one UserAvailability per 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 users
      IllegalArgumentException - 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 unlike getAvailability(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 — where getAvailability(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 AvailabilityDisclosure exists to prevent. A requested identifier that is not a user (a space, or nothing at all) comes back as AvailabilityDisclosure.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 for
      start - window start, inclusive
      end - window end, exclusive
      userIdentityId - technical identifier of the user asking
      Returns:
      one UserBusyTime per distinct requested user, in the order asked, never null
      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 free
      duration - the length a slot must have
      windowStart - earliest acceptable start
      windowEnd - latest acceptable end
      morningsOnly - restrict candidate starts to before noon
      afternoonsOnly - restrict candidate starts to noon and after
      limit - maximum number of slots to propose
      userIdentityId - technical identifier of the user asking
      Returns:
      the candidate slots, earliest first, at most limit of them
      Throws:
      IllegalAccessException - when the asking user is not allowed to read the availability of any one of the attendees
      IllegalArgumentException - 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. See AvailabilityConflicts.

      Parameters:
      attendeeIdentityIds - technical identifiers of the event's attendees; non-user identities are ignored
      start - proposed window start
      end - proposed window end
      userIdentityId - 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 order
      attendeeIdentityIds - technical identifiers of the event's attendees
      userIdentityId - technical identifier of the user asking
      Returns:
      the options, most-available first, keeping the original order between equally available ones