Feat/valarm suppression shared calendars#8006
Open
howudodat wants to merge 4 commits intonextcloud:mainfrom
Open
Feat/valarm suppression shared calendars#8006howudodat wants to merge 4 commits intonextcloud:mainfrom
howudodat wants to merge 4 commits intonextcloud:mainfrom
Conversation
added 4 commits
February 23, 2026 20:21
Add migration, entity, and mapper for the calendar_share_alarms table. This stores per-share preferences for whether VALARM components should be stripped from CalDAV responses for shared calendars. Ref: nextcloud#7498
Register a SabreDAV plugin via SabrePluginAddEvent that intercepts CalDAV REPORT and GET responses. When alarm suppression is enabled for a share, VALARM components are stripped from the ICS data before it reaches the sharee's client. Hooks into propFind (priority 600) for REPORT responses and afterMethod:GET for direct .ics fetches. Uses an in-memory cache to avoid repeated DB queries within a single request. Ref: nextcloud#7498
Add ShareAlarmController with GET and POST endpoints at /v1/share-alarm for reading and toggling per-share alarm suppression. Resolves calendar DAV URLs to internal IDs via CalDavBackend and verifies calendar ownership. Ref: nextcloud#7498
Add "suppress alarms" checkbox to ShareItem in the EditCalendarModal. The owner can toggle alarm suppression per sharee. Settings are loaded when the modal opens and persisted via the share-alarm API. Also fixes a pre-existing Vue 3 migration bug where the isWriteable watcher fired on mount and toggled permissions unintentionally. Both checkboxes now use @update:modelValue instead of @update:checked. Ref: nextcloud#7498
Member
|
TL;DR: doing things properly is much harder This is not a bad idea, but:
|
Contributor
|
Hi, I would agree with @tcitworld. This should be fixed in the dav app which is the calendaring backend, but this will require some thought on implementation. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Owner-Controlled VALARM Suppression for Shared Calendars
Problem
When a Nextcloud calendar is shared with read-write access, VALARM (alarm/reminder) components from the owner's events are transmitted to the sharee via CalDAV. Android calendar apps synced via DAVx5 have no per-calendar notification suppression, causing unwanted alerts on the sharee's device.
Existing behavior: Nextcloud server's
CalendarObject::get()(inapps/dav) already strips VALARM for read-only shared calendars. For read-write shares, VALARM is preserved.Reference: Issue #7498
Solution
Register a SabreDAV plugin from the calendar app via the
SabrePluginAddEventmechanism (NC 28+; this app targets NC 32+). The plugin intercepts CalDAV responses and strips VALARM when the owner has enabled suppression for that share. A new database table stores the per-share preference, and the sharing UI exposes the toggle.The owner controls the setting: in the calendar edit/share modal, each sharee row has a "suppress alarms" checkbox alongside the existing "can edit" checkbox.
Plan
Architecture
Data Flow
POST /v1/share-alarm→ upserts record incalendar_share_alarmsStripAlarmsPluginintercepts, checks DB (cached), strips VALARM from ICS dataFiles Created
lib/Migration/Version5050Date20250701000005.phpcalendar_share_alarmstable withcalendar_id,principal_uri,suppress_alarmslib/Db/ShareAlarmSetting.phplib/Db/ShareAlarmSettingMapper.phpisSuppressed(),findAllByCalendarId(), and cleanup methodslib/Dav/StripAlarmsPlugin.phppropFind(priority 600) +afterMethod:GEThooks, in-memory cache, VALARM stripping viaSabre\VObject\Readerlib/Listener/SabrePluginAddListener.phpStripAlarmsPluginviaSabrePluginAddEventlib/Controller/ShareAlarmController.phpGET /v1/share-alarmandPOST /v1/share-alarm, ownership verification, calendar URL→ID resolution viaCalDavBackendsrc/services/shareAlarmService.js@nextcloud/axiosFiles Modified
lib/AppInfo/Application.phpSabrePluginAddListenerforSabrePluginAddEventappinfo/routes.phpGETandPOST /v1/share-alarmroutessrc/models/calendarShare.jssuppressAlarms: falseto default share objectsrc/store/calendars.jsloadShareAlarmSettingsandtoggleShareAlarmSuppressionactionssrc/components/AppNavigation/EditCalendarModal/ShareItem.vueNcCheckboxRadioSwitch, watcher, andupdateAlarmSuppression()methodsrc/components/AppNavigation/EditCalendarModal.vueDatabase Schema
API Endpoints
GET /apps/calendar/v1/share-alarm?calendarUrl=...Returns suppression state for all shares of a calendar.
{ "status": "success", "data": { "principals/users/alice": true, "principals/users/bob": false } }POST /apps/calendar/v1/share-alarmToggles suppression for one share.
{ "calendarUrl": "/remote.php/dav/calendars/owner/calname/", "principalUri": "principals/users/alice", "suppressAlarms": true }SabreDAV Plugin Details
StripAlarmsPluginhooks into two interception points:propFind(priority 600) — For REPORT requests (calendar-multiget, calendar-query). Runs after the CalDAV plugin (priority 150-550) has populated{urn:ietf:params:xml:ns:caldav}calendar-data. CallspropFind->get()to read the ICS, strips VALARM, callspropFind->set()to replace.afterMethod:GET— For direct GET on.icsfiles. Readsresponse->getBodyAsString(), strips VALARM, callsresponse->setBody().VALARM stripping follows the same pattern as the server's
CalendarObject::removeVAlarms():Performance: An in-memory
$suppressionCachearray (keyed bycalendarId:principalUri) avoids repeated DB lookups for objects in the same calendar during a single REPORT response.CalendarInfo access: Uses reflection as fallback to access the protected
calendarInfoproperty on Sabre'sCalendarObject, with a last-resort parent node lookup via the server tree.Known Caveats
calendarInfoaccess: The plugin uses reflection to access the protectedcalendarInfoproperty onCalendarObject. This should be tested against the actual Nextcloud server version. IfgetCalendarInfo()becomes public in a future NC version, the reflection fallback becomes unnecessary.Calendar ID resolution: The controller resolves the calendar URL to an internal ID via
CalDavBackend::getCalendarsForUser(). This adds a dependency on the DAV app's backend class (OCA\DAV\CalDAV\CalDavBackend).Principal URI format mismatch: Frontend uses
principal:principals/users/alice(cdav-library format), backend usesprincipals/users/alice. The store actions strip theprincipal:prefix before API calls.propFind->set()after lazy eval: ThePropFind::set()behavior afterget()triggers lazy evaluation needs verification against the SabreDAV version bundled with NC 32+.Verification
Unit Tests
ShareAlarmSettingMapper: CRUD operations,isSuppressed()returns false for missing recordsStripAlarmsPlugin: MockCalendarObjectnode withcalendarInfo, verify VALARM stripping when enabled and no-op when disabledManual End-to-End