Class CaldavPendingInvitationService

java.lang.Object
org.exoplatform.caldav.service.CaldavPendingInvitationService

@Service public class CaldavPendingInvitationService extends Object
Puts the meetings a user has not answered yet into their own calendar, with their honest PARTSTAT=NEEDS-ACTION (EXO-89681).

Until this ran, a copy only ever reached the account because the user acted in a browser — and the act that pushed it was accepting, so a meeting the user was still deciding about was exactly the one their phone never showed. This pass runs server-side inside the background sync, so a pending invitation becomes visible on the user's devices while they are deciding — which is the precondition for answering it there at all: the verification pass reads the answer back off the copy.

It seeds; it does not maintain. An event that already has a mapping row is left to the machinery that owns it — the browser-triggered push for edits, the verification pass for drift and answers.

What it seeds, and why that is not only meetings (EXO-89796)

It used to be only meetings: the pass listed the events the user is an attendee of, and refused any event living in a calendar the user owns. Both halves of that were wrong, and together they made the add-on answer two different questions depending on when an event had been created.

An event a user creates in their own calendar with nobody invited has no attendee row at all — agenda writes attendee rows only for the attendees it is given (AgendaEventServiceImpl.createEvent saves them under if (attendees != null && !attendees.isEmpty())) — and agenda's window query joins on those rows (EventDAO.getEventIds does INNER JOIN ev.attendees att ... AND att.identityId IN (:attendeeIds)). So such an event was invisible to this pass whatever response the filter asked for, and the own-calendar refusal would have turned it away even if it had been listed. Created while the account was connected it was copied anyway, by the browser's own push on save; created before, it was copied by nothing, for ever. The same event, copied or not according only to when it came into being.

So the pass now asks two questions — the events the user attends, and the events of the user's own calendars — and stops refusing the second. Nothing is mixed by that: CaldavPushService.pushAgendaEvent(long, String, long) is what routes an event, and it sends an event of the user's own calendar to that calendar's own collection, never to the mirror, answering null when there is no collection to write into.

The one question both paths ask

seedOne(long, String, long) is the whole decision, and it is deliberately the only one: the background pass reaches it through pushUpcomingMeetings(long, String) and the creation listener reaches it through seedMeeting(long, String, long), so an event that would be copied were it created now is the same event this pass backfills. The listing above is candidate selection and nothing more — a cheap way to name events, never a second set of rules. That is why the "not DECLINED" rule lives in seedOne and not only in the query.

  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    int
    pushUpcomingMeetings(long userIdentityId, String username)
    Copies the user's upcoming events — meetings they have not answered yet and events of their own calendars alike — into their connected account, skipping everything a copy already exists for.
    boolean
    seedMeeting(long userIdentityId, String username, long eventId)
    Writes the copy of one named meeting into one user's account, for a caller that already knows which meeting and which user.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • CaldavPendingInvitationService

      public CaldavPendingInvitationService()
  • Method Details

    • pushUpcomingMeetings

      public int pushUpcomingMeetings(long userIdentityId, String username)
      Copies the user's upcoming events — meetings they have not answered yet and events of their own calendars alike — into their connected account, skipping everything a copy already exists for.

      Two questions, because agenda cannot answer them as one: its window query ANDs the calendar-owner predicate with the attendee predicate rather than ORing them (EventDAO.getEventIds), so "events I attend or events in my calendars" is two calls. Their answers are merged and asked about once.

      Parameters:
      userIdentityId - identity of the user whose account receives copies
      username - their eXo login, which the credentials provider maps to their account on the server
      Returns:
      how many events were written this pass
    • seedMeeting

      public boolean seedMeeting(long userIdentityId, String username, long eventId)
      Writes the copy of one named meeting into one user's account, for a caller that already knows which meeting and which user.

      The same decision as the pass above, asked one meeting at a time. It exists because a meeting being created is a fact the platform already tells us (exo.agenda.event.created, EXO-89754), and waiting for the next sweep to rediscover it by listing a 60-day window is both slower and narrower: the window is where a meeting further out, or created in the past, used to fall through and never get a copy at all.

      Every refusal the pass makes is made here too, and that is the point of routing through this service rather than calling the push directly: the account has to be connected with copies enabled, the meeting has to be CONFIRMED, the user must not have declined it, and a meeting that already has a copy is left to the machinery that owns it. That last one is what makes a second trigger on the same creation write nothing.

      Parameters:
      userIdentityId - identity of the user whose account receives the copy
      username - their eXo login, which the credentials provider maps to their account on the server
      eventId - the agenda event — a series master or a single event
      Returns:
      true when a copy was written