Skip to content

Write access over CalDAV#7655

Open
Jaggob wants to merge 36 commits intonextcloud:mainfrom
Jaggob:feature/cal-dav-write
Open

Write access over CalDAV#7655
Jaggob wants to merge 36 commits intonextcloud:mainfrom
Jaggob:feature/cal-dav-write

Conversation

@Jaggob
Copy link

@Jaggob Jaggob commented Feb 18, 2026

Summary

This PR adds CalDAV write support for Deck, enabling CalDAV clients to create, update, complete/uncomplete, and delete Deck cards.

It also introduces per-user CalDAV list mapping modes in Deck settings, so each user can choose how Deck lists are represented in CalDAV clients:

  • Default (existing behavior): list hierarchy via VTODO parent/child relationships (RELATED-TO)
  • One VCALENDAR per list
  • Single board calendar with list name in CATEGORIES
  • Single board calendar with list position mapped to PRIORITY

Additional changes include DAV hardening and compatibility improvements (notably for Thunderbird and Apple clients), plus backend cleanup/performance improvements in the CalDAV handling path.

Done in this PR

  • Added CalDAV write support for Deck cards (create/update/delete/complete)
  • Added per-user CalDAV list mapping modes in Deck settings
  • Implemented category sync (CATEGORIES) including Thunderbird interoperability
  • Added DAV hardening and compatibility fixes (including stale href handling)

Future TODO

  • Improve move semantics further for clients that still use create+delete behavior instead of true move semantics. At the current implementation moving cards with e. g. Thunderbird leads to Nextcloud saying cards have been deleted and created instead of moved (but aside from activity logs moving works fine)
  • Add CalDAV mapping for assignees
  • Add CalDAV mapping for file attachments
  • Improve Apple tag interoperability where Deck tags are currently not recognized as native Apple tags

Checklist

  • Code is properly formatted
  • Sign-off message is added to all commits
  • Tests (unit, integration, api and/or acceptance) are included
  • Documentation (manuals or wiki) has been updated or is not required

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36747a6fd3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +305 to +307
$currentBoardId = $this->getBoardIdForCard($currentCard);
if ($currentBoardId !== $expectedBoardId) {
// Ignore trailing delete from source calendar after a cross-board move.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Skip delete when card already moved within same board

In per-list-calendar mode, clients often move tasks as PUT to the destination calendar followed by DELETE on the source href. This guard only suppresses the trailing delete when the board changes, so a move between stacks in the same board falls through to cardService->delete() and removes the task that was just moved. This is a data-loss path for any client that preserves Deck UIDs during cross-calendar moves.

Useful? React with 👍 / 👎.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 584dcf5.

deleteCalendarObject() now also guards against trailing source deletes when the card was moved to a different stack within the same board (not only cross-board moves).
So create+delete move flows in per-list-calendar mode no longer delete the just-moved task.

still needs a better move handling separately to model MOVE semantics more explicitly, since activity can still show create+delete instead of a single move event in some client flows.

Comment on lines 151 to 155
$fallbackItem = $this->backend->findCalendarObjectByName(
$name,
$this->board->getId(),
$this->stack?->getId()
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid resolving deleted cards in createFile fallback lookup

createFile() uses getChildNode(..., false) before calling createCalendarObject, and this fallback lookup can return soft-deleted cards. That routes a PUT for card-<id>.ics into the update path instead of the create/restore path; updateCardFromCalendar() then calls cardService->find() (non-deleted) and fails. In practice, recreating/restoring a deleted card via CalDAV with the same href can return an error instead of restoring the task.

Useful? React with 👍 / 👎.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 584dcf5.

This is a rather unlikely edge case for most clients, but: createFile() now resolves existing children with deleted fallback disabled, so soft-deleted cards are no longer routed into the regular update path by getChildNode(...).
This restores correct create/restore behavior for PUT requests on the same card-<id>.ics href.

Jacob Bühler and others added 25 commits February 18, 2026 15:42
Implement first working write path for Deck CalDAV objects.

- allow DAV write privilege on Deck calendars

- handle PUT on existing card/stack ICS objects and map VTODO fields to Deck services

- add NC32 compatibility fixes in BoardMapper for empty orX() usage

- add unit tests for calendar update mapping

Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Follow-up fixes after real-world macOS Reminders tests.

- convert COMPLETED timestamps to DateTime expected by Deck entities

- provide calendar object owner/group to avoid DELETE scheduling crashes

- add backend tests for delete path and COMPLETED-without-STATUS mapping

Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
…bility

Stabilize bidirectional task sync for Apple Reminders and Thunderbird.

- implement createFile support with stack resolution and alias normalization

- support delete and robust completed mapping (STATUS/COMPLETED/PERCENT-COMPLETE)

- add UID/resource-name upsert logic to avoid duplicates on move-back

- handle board-to-board moves by updating existing deck-card IDs

- export richer VTODO metadata (DTSTAMP/CREATED/LAST-MODIFIED/PERCENT-COMPLETE)

- add extensive unit tests for update/create/delete and fallback paths

Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
…Labels"

This reverts commit 3d0af5c.

Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
- fix Deck settings CalDAV mode selector rendering

- make ETag/last-modified depend on selected list mapping mode

- map list priority as left=9, right=1 for Thunderbird/Apple

Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
…ateFile

Signed-off-by: Jaggob <37583151+Jaggob@users.noreply.github.com>
@Jaggob Jaggob force-pushed the feature/cal-dav-write branch from 584dcf5 to 8f1393f Compare February 18, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Write access over CalDAV

1 participant

Comments