Class CaldavDeletionService

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

@Service public class CaldavDeletionService extends Object
Deleting a personal calendar, on both sides.

The only irreversible outward-facing action in this design. Removing a collection removes every event in it — including events the user added from their own phone, which eXo never authored and cannot restore. Everything here is arranged around that fact.

  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final String
    Nothing was deleted, on either side.
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    deleteRemoteCounterpart(long userIdentityId, long calendarId)
    Deletes a personal calendar in eXo and, when eXo created it, on the server too.
    org.exoplatform.caldav.model.CalendarDeletionPlan
    describeDeletion(long userIdentityId, long calendarId)
    What deleting this calendar would also do, so the page can say it before the user confirms.
    void
    freezeOnDisconnect(long userIdentityId, long serverId, String username)
    Freezes every binding of a user, which is what disconnecting an account does.
    void
    keepRemoteCounterpart(long userIdentityId, long calendarId)
    Deletes the calendar in eXo and leaves the remote collection standing, because the user asked for exactly that.
    List<org.exoplatform.caldav.model.HiddenCalendar>
    listHidden(long userIdentityId)
    The calendars this user has hidden on this account.
    List<org.exoplatform.caldav.model.CalendarSyncState>
    listSyncStates(long userIdentityId, String username)
    What each of this user's calendars is doing, for the ones worth telling them about.
    void
    showAgain(long userIdentityId, long pairId, String username)
    Shows a hidden calendar again.
    void
    thawOnConnect(long userIdentityId, long serverId)
    Puts back to work every binding a disconnect froze.

    Methods inherited from class java.lang.Object

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

  • Constructor Details

    • CaldavDeletionService

      public CaldavDeletionService()
  • Method Details

    • deleteRemoteCounterpart

      public void deleteRemoteCounterpart(long userIdentityId, long calendarId)
      Deletes a personal calendar in eXo and, when eXo created it, on the server too.

      Remote first, and the order is the whole design. The failable step runs while nothing has happened yet, so a server that refuses or cannot be reached leaves both sides exactly as they were. The reverse order can strand a collection on the server after the record that knew about it is gone — an orphan nothing will ever find again, holding the user's events.

      Success is confirmed by the collection's absence from a fresh listing, never by a status code. This migration has twice met a server that answers success while doing nothing, and a deletion that reports done while the collection still stands would take the local calendar with it.

      The local half is not done here. Agenda owns the calendar and deletes it itself, once this returns without throwing — which is exactly what makes the ordering work: the failable step runs first, and a refusal here stops agenda before it has touched anything.

      Parameters:
      userIdentityId - identity of the user
      calendarId - the eXo calendar being deleted
      Throws:
      CaldavPushException - when the remote deletion could not be carried out; nothing has been deleted on either side
    • keepRemoteCounterpart

      public void keepRemoteCounterpart(long userIdentityId, long calendarId)
      Deletes the calendar in eXo and leaves the remote collection standing, because the user asked for exactly that.

      The escape hatch from the atomic rule, and it exists so that divergence between the two sides is only ever chosen, named and recorded — never a side effect of a failed deletion. The pair is kept as an CalendarSyncStatus.EXO_ORPHANED tombstone so the settings can say that a collection of eXo's making is still out there.

      Parameters:
      userIdentityId - identity of the user
      calendarId - the eXo calendar being deleted
    • describeDeletion

      public org.exoplatform.caldav.model.CalendarDeletionPlan describeDeletion(long userIdentityId, long calendarId)
      What deleting this calendar would also do, so the page can say it before the user confirms.

      The page cannot work this out for itself: it does not know whether eXo created the remote collection, nor which server holds it. Both decide what the confirmation must warn about — and whether it must warn at all.

      Parameters:
      userIdentityId - identity of the user
      calendarId - the eXo calendar in question
      Returns:
      what is bound to it, or a plan claiming nothing
    • thawOnConnect

      public void thawOnConnect(long userIdentityId, long serverId)
      Puts back to work every binding a disconnect froze.

      Disconnecting pauses the bindings of the calendars eXo pushed out, so that reconnecting the same account finds its collections again instead of creating a second set beside them. Reconnecting is the other half of that bargain: the account is usable again, so its bindings are too.

      Left paused they are repaired only incidentally — by the next sweep re-ensuring each collection, a second or so later. In the meantime the account is connected and the user's own calendars report themselves as failing, which a UI reading the states has every reason to believe and show.

      A pause set because credentials were refused is thawed here as well, and deliberately: someone who has just entered a password is asking for precisely that retry.

      Parameters:
      userIdentityId - identity of the user
      serverId - the declared server registration
    • freezeOnDisconnect

      public void freezeOnDisconnect(long userIdentityId, long serverId, String username)
      Freezes every binding of a user, which is what disconnecting an account does.

      Nothing on the server is ever touched. A user unlinking their account is saying "stop syncing", not "destroy what is on my server".

      Nothing in eXo is destroyed either, and that is the change (EXO-89800). A calendar eXo materialised from a collection of the account used to go with the account: its eXo calendar was deleted, and with it every event in it, its object mappings and its binding. Reconnecting the same account then found no binding for the collection, materialised it a second time, and imported every event again under new identifiers — observed live as the same BlueMind collection materialised at 23:28 and again at 08:37 the next morning, as two different eXo calendars.

      That deletion could not be undone and was not the user's to lose. A materialised calendar is an ordinary agenda calendar: the user adds events to it, and the connector pushes those back to the account, so it holds work of theirs that exists nowhere else. Deleting it on "stop syncing" also broke every reference to the events it held — an activity, a notification, a link — since the re-imported copies carry new identifiers.

      So every binding is now treated the way a pushed calendar already was, and for the reason its own comment gave: reconnecting the same account must find its collection again instead of creating a second one beside it. The binding is paused, the calendar stays, and thawOnConnect(long, long) puts it back to work.

      A paused binding stops importing, which is what "no longer updating" means; listSyncStates(long, String) reports the state so the settings can say so. And reconnecting a different account on the same server does not resurrect the old collections: their hrefs are absent from the new account's listing, so the first pass marks them CalendarSyncStatus.REMOTE_GONE and they stop receiving events — visibly, rather than by having been destroyed in advance.

      Parameters:
      userIdentityId - identity of the user
      serverId - the declared server registration
      username - the user's login. No longer needed to delete anything on their behalf, and kept because it is still what tells the caller the account can be identified at all — CaldavConnectorServiceImpl.deleteCaldavSetting(long, String) only freezes when it has one — and because a support trace of a disconnect is worth the login it happened under
    • listHidden

      public List<org.exoplatform.caldav.model.HiddenCalendar> listHidden(long userIdentityId)
      The calendars this user has hidden on this account.

      A tombstone is what makes a deletion stick: it keeps a collection from being materialised again, and since the shim stopped serving bound collections it keeps it off the screen entirely. That is what the user asked for — and it leaves them with no way back, which is why this exists.

      The name comes from the server, read now rather than stored: a calendar renamed in the user's own client since they hid it should be offered back under the name they would recognise today. A collection the server no longer has is left out — offering to show something that is gone would be a promise nothing can keep.

      Parameters:
      userIdentityId - identity of the user
      Returns:
      what can be shown again, empty when nothing is hidden
    • showAgain

      public void showAgain(long userIdentityId, long pairId, String username) throws IllegalAccessException, org.exoplatform.commons.exception.ObjectNotFoundException
      Shows a hidden calendar again.

      Dropping the tombstone is all it takes: the next sync finds a collection with no binding and materialises it afresh. Deliberately a new calendar rather than a resurrection — the one the user deleted is gone, and pretending otherwise would promise back events that agenda moved to their default calendar at deletion time.

      Parameters:
      userIdentityId - identity of the user
      pairId - the tombstone to lift
      username - the user's login, which the synchronisation run here needs because agenda's ACL reads it rather than the identity id
      Throws:
      IllegalAccessException - when the tombstone is not this user's
      org.exoplatform.commons.exception.ObjectNotFoundException - when there is no such tombstone
    • listSyncStates

      public List<org.exoplatform.caldav.model.CalendarSyncState> listSyncStates(long userIdentityId, String username)
      What each of this user's calendars is doing, for the ones worth telling them about.

      The engine has always known this per binding and kept it in the database and the logs. A user seeing their agenda has no way to tell a calendar that is synchronising from one whose server refused it — both simply sit there — so this surfaces the states where something they might do would change the outcome, and nothing else.

      Names come from the eXo calendar for a binding that has one, and from the server for the ones that do not — a collection eXo was refused permission to create has no eXo calendar to name it by.

      Parameters:
      userIdentityId - whose calendars
      username - the user's login, which agenda's ACL needs to read their calendars
      Returns:
      the states worth showing, empty when everything is well