Class CaldavDeletionService
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
FieldsModifier and TypeFieldDescriptionstatic final StringThe account's calendars could not be listed, so whether the calendar named is a share of the user's cannot be told; nothing was hidden.static final StringNo calendar was named to hide.static final StringThe calendar named to hide is not one shared with the user (EXO-90239): their own, in whatever state — and hiding one's own calendar is a different act, done by deleting it and keeping the remote one.static final StringNothing was deleted, on either side. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoiddeleteRemoteCounterpart(long userIdentityId, String username, long calendarId) Deletes a personal calendar in eXo and, when eXo created it, on the server too.org.exoplatform.caldav.model.CalendarDeletionPlandescribeDeletion(long userIdentityId, long calendarId) What deleting this calendar would also do, so the page can say it before the user confirms.voidfreezeOnDisconnect(long userIdentityId, long serverId, String username) Freezes every binding of a user, which is what disconnecting an account does.static StringhiddenShareAnchor(String canonicalHref) The anchor a hidden share's pair records (EXO-90239): a name-based UUID of the collection path, never an agenda calendar's.voidHides a calendar somebody shared with the user (EXO-90239).voidkeepRemoteCounterpart(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, String username) 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.voidShows a hidden calendar again.voidthawOnConnect(long userIdentityId, long serverId) Puts back to work every binding a disconnect froze.
-
Field Details
-
NOTHING_DELETED
Nothing was deleted, on either side.- See Also:
-
NOT_A_SHARE
The calendar named to hide is not one shared with the user (EXO-90239): their own, in whatever state — and hiding one's own calendar is a different act, done by deleting it and keeping the remote one.- See Also:
-
CALENDAR_REQUIRED
No calendar was named to hide.- See Also:
-
ACCOUNT_UNAVAILABLE
The account's calendars could not be listed, so whether the calendar named is a share of the user's cannot be told; nothing was hidden.- See Also:
-
-
Constructor Details
-
CaldavDeletionService
public CaldavDeletionService()
-
-
Method Details
-
deleteRemoteCounterpart
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 userusername- the eXo login the credentials provider resolves the account fromcalendarId- 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_ORPHANEDtombstone so the settings can say that a collection of eXo's making is still out there.- Parameters:
userIdentityId- identity of the usercalendarId- 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 usercalendarId- 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 userserverId- the declared server registration
-
freezeOnDisconnect
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 themCalendarSyncStatus.REMOTE_GONEand they stop receiving events — visibly, rather than by having been destroyed in advance.- Parameters:
userIdentityId- identity of the userserverId- the declared server registrationusername- 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, String username) 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. A hidden share (
CalendarSyncStatus.HIDDEN_SHARE, EXO-90239) is listed beside the tombstones for the same reason, told apart byshared, and named with whoever shared it — the owner the calendar list would name, resolved through the same service, so the drawer says "Shared by Alice" about the very calendar the list said that of.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. For a share that also means the owner withdrew it; the sweep drops its record on its next pass (
CaldavSyncService#forgetRevokedShares), and this listing does not, because a listing is read on every visit to the settings and must not write.- Parameters:
userIdentityId- identity of the userusername- the eXo login the credentials provider resolves the account from- 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.
For a hidden share (EXO-90239) dropping the pair is all there is: a share is never materialised, so no synchronisation would bring anything back, and the collection is back under the remote calendars on the very next listing because it is unbound again. Synchronising here would spend a full pass of the account to change nothing.
- Parameters:
userIdentityId- identity of the userpairId- the hidden calendar to lift — a tombstone or a hidden shareusername- 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 hidden calendar is not this user'sorg.exoplatform.commons.exception.ObjectNotFoundException- when there is no such hidden calendar, tombstone or hidden share
-
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 calendarsusername- the user's login, which agenda's ACL needs to read their calendars- Returns:
- the states worth showing, empty when everything is well
-