Interface CalendarShareChannelPlugin


public interface CalendarShareChannelPlugin
A channel that can also carry a calendar share outside eXo — the CalDAV add-on granting the colleague's principal read access to the collection that mirrors the calendar (EXO-90357).

Agenda owns the share; a channel delivers it

Sharing a calendar with a colleague is an eXo fact, recorded by agenda and enforced by agenda's own ACL, for every owner — including one who never connected a CalDAV account. A channel is asked, after the record is written, to carry the same share where it can; its answer never decides whether the share exists. A channel that fails leaves the record standing, undelivered: agenda logs the failure at WARN and tells the owner nothing, and nothing is retried on its own. A channel that says the share is none of its business leaves the record eXo-only. A grant a channel holds for a colleague of this deployment that agenda has no record of is recorded silently, as an adopted share, the moment the owner lists their shares.

How it is registered

As a Spring @Service bean, non-final, in the contributing add-on's own context: agenda collects every bean of this type at the moment it asks, so an add-on whose WAR boots after agenda's is found just the same, and a deployment without the add-on simply has none. Same idiom as RemoteEventCopyPlugin.

No business logic here

An implementation transfers the call to its own service layer and answers; it decides nothing about who may share what. It may throw: agenda reads an exception as a ChannelDelivery.Status.FAILED delivery, or as a withdrawal that did not happen, and carries on with the eXo record.

  • Method Details

    • id

      String id()
      The channel's identifier — caldav. Stable across restarts. A delivery may qualify it with the server that holds the grant, caldav:<serverId>, on the record's deliveredTo: agenda resolves the channel by that prefix when it withdraws the share.
      Returns:
      the identifier, never blank
    • deliver

      ChannelDelivery deliver(CalendarShare share, String ownerUsername)
      Makes this channel's grant match a share record: carries a share the owner just recorded, shared again while it was still undelivered, or levelled (EXO-90378).

      Idempotent, and reconciling. The record's CalendarShare.getLevel() is what the channel must end up holding: EDIT over an existing read grant widens it, VIEW over an edit grant narrows it, and either over a grant that already matches writes nothing. A channel that can carry the share but not at that level answers ChannelDelivery.delivered(String, String, org.exoplatform.agenda.constant.CalendarShareLevel) naming the level it does hold, rather than failing: the share still stands in eXo at the level the owner chose, and that is what decides every right inside eXo.

      Parameters:
      share - the eXo record, never null, carrying the level to match
      ownerUsername - the owner of the calendar
      Returns:
      what happened, never null
    • withdraw

      boolean withdraw(CalendarShare share, String ownerUsername)
      Withdraws a share this channel carried, before agenda deletes the record. Called only for a record whose deliveredTo is this channel's id.
      Parameters:
      share - the eXo record, never null
      ownerUsername - the owner of the calendar
      Returns:
      true when the grant is gone on the channel's side, false when it could not be removed — the record is deleted all the same, and the grant then shows up under the external shares
    • listExternalShares

      List<ExternalShare> listExternalShares(long calendarId, String ownerUsername, List<Long> recordedShareeIds)
      The shares of a calendar that exist on this channel's server without an eXo record, read live. The channel leaves out every grantee agenda already holds a record for — recordedShareeIds — so a delivered share is never listed twice. A grant to a user of this deployment whose shape eXo itself writes — ExternalShare.access VIEW or EDIT (EXO-90378) — carries its deliveryRef: agenda records it as an adopted share at that level, silently and without touching the server, the moment the owner lists their shares. Every other grant is listed to the owner as access held outside eXo.
      Parameters:
      calendarId - technical identifier of the calendar
      ownerUsername - the owner of the calendar
      recordedShareeIds - identity identifiers of the colleagues agenda already holds a record for, never null
      Returns:
      the external shares, empty when the calendar is not on this channel or the owner has no account there
    • removeExternalShare

      boolean removeExternalShare(long calendarId, String externalId, String ownerUsername)
      Removes an external share on the server, at the owner's request.
      Parameters:
      calendarId - technical identifier of the calendar
      externalId - the channel's identifier of the grant
      ownerUsername - the owner of the calendar
      Returns:
      true when removed
    • holdsMeetingCopies

      default boolean holdsMeetingCopies(long calendarId, String ownerUsername)
      Whether this channel writes copies of the owner's eXo meetings into the calendar — the mirror calendar of a connected account. Sharing such a calendar exposes the titles, descriptions and spaces of every meeting the owner attends, private spaces included, so the drawer asks once before sharing it (EXO-90345). A channel that copies nothing answers false, the default.
      Parameters:
      calendarId - technical identifier of the calendar
      ownerUsername - the owner of the calendar
      Returns:
      true when the calendar receives meeting copies from this channel
    • listShares

      default ChannelShares listShares(long calendarId, String ownerUsername, List<Long> recordedShareeIds)
      Everything this channel has to say about a calendar when the owner opens the Share drawer (EXO-90385): listExternalShares(long, java.lang.String, java.util.List<java.lang.Long>) and holdsMeetingCopies(long, java.lang.String), in one ask.

      Why agenda asks this one rather than the two. The two answers come out of the same conversation with the channel's server, and a channel asked for them separately holds that conversation twice — resolving the same collection, asking the same account. Against a remote CalDAV server that is seconds of pure latency on every opening of the drawer. A channel that can answer both from one read overrides this method and does so; the default asks the two methods it stands for, so a channel that overrides neither needs nothing more.

      The default asks the two independently, each under its own guard: an unreadable access list must not turn the warning off, which is the expensive direction per the tolerance below. A failure of either question is logged here, at WARN for the list and at DEBUG for the flag, because the caller sees only the answer.

      Same contract as the two methods it stands for, including their tolerances: the external shares are those the channel's server holds that agenda has no record of, recordedShareeIds left out, and the flag is what holdsMeetingCopies(long, java.lang.String) means. A channel that cannot read the server answers an empty list, and still answers the flag as best it can — a missed warning exposes the owner's meetings while a false one costs a click.

      Parameters:
      calendarId - technical identifier of the calendar
      ownerUsername - the owner of the calendar
      recordedShareeIds - identity identifiers of the colleagues agenda already holds a record for, never null
      Returns:
      the external shares and the meeting-copies flag, never null