Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
3c80c48
feat(deck-dav): enable CalDAV write updates for existing Deck items
Feb 16, 2026
42e62e8
fix(deck-dav): stabilize completed and delete sync from CalDAV
Feb 16, 2026
dc2f646
feat(deck-dav): harden CalDAV move/create flows and client interopera…
Feb 16, 2026
0252e11
feat(caldav): map VTODO CATEGORIES to Deck labels
Feb 17, 2026
ea25188
fix(caldav): sync CATEGORIES with Deck labels for Thunderbird
Feb 17, 2026
06ae791
fix(deck): accept null offset in CardMapper findAll for stacks API
Feb 17, 2026
990261c
feat(caldav): add Apple Reminders tag mapping via X-APPLE-TAGS
Feb 17, 2026
c698ae6
feat(caldav): add Apple tag fallback in DESCRIPTION via Deck-Labels
Feb 17, 2026
ccfe941
Revert "feat(caldav): add Apple tag fallback in DESCRIPTION via Deck-…
Feb 17, 2026
2fffcfa
feat(caldav): add per-user list mapping modes for CalDAV
Feb 17, 2026
c3279d6
fix(caldav): refresh mode-based fields and invert list priority mapping
Feb 17, 2026
ce4ccb9
Refactor CalDAV update handling and remove experimental move reattach
Feb 17, 2026
8ae48c1
Pass board context on delete and expose stable deck card id
Feb 17, 2026
873c543
Trigger CI
Feb 17, 2026
e9a2989
Stabilize CalDAV backend after review and CI feedback
Feb 17, 2026
a7aca1b
Harden CalDAV ACL and deleted-card lookup
Feb 17, 2026
5745dc4
Touch source board on cross-board card moves
Feb 17, 2026
bd17705
Restore permissive CalDAV ACL for client move compatibility
Feb 17, 2026
595223a
Fix CalDAV object name normalization for Thunderbird PUT/MULTIGET
Feb 17, 2026
3073dd7
Avoid PROPFIND failure on deck calendar children listing
Feb 17, 2026
038919b
Accept both card and deck-card names in CalDAV child lookup
Feb 17, 2026
5a7ee87
Fix CalDAV 412 on Thunderbird cross-board moves
Feb 17, 2026
04431c1
Harden CalDAV backend ACL and type handling
Jaggob Feb 17, 2026
274c2b1
Decorate list VTODOs with category and priority
Jaggob Feb 17, 2026
bbb4ecf
Harden CalDAV fallback handling and lightweight card lookups
Jaggob Feb 17, 2026
3324a80
perf(dav): reduce redundant lookups and serialization work
Jaggob Feb 17, 2026
567a76d
fix(dav): avoid Thunderbird 404 on stale deck card hrefs
Jaggob Feb 17, 2026
cc23f90
fix(dav): restrict placeholder resolution for write paths
Jaggob Feb 17, 2026
13888d3
fix(psalm): suppress unresolved Sabre symbols in DAV backend
Jaggob Feb 17, 2026
3b7344d
fix(frontend): guard optional OC.Files client bootstrap
Jaggob Feb 17, 2026
9c4e209
test(dav): cover app-generated calendar URI normalization
Jaggob Feb 17, 2026
782d355
test(dav): cover in-progress status and Apple tags label sync
Jaggob Feb 17, 2026
29b946d
style(test): fix import ordering in dav backend tests
Jaggob Feb 17, 2026
13a901d
ci: use ubuntu-latest for fork pull-request workflows
Jaggob Feb 18, 2026
1a9472e
chore(ci): drop workflow runner changes for upstream PR
Jaggob Feb 18, 2026
8449215
fix(dav): prevent trailing deletes on stack moves and restore via cre…
Jaggob Feb 18, 2026
ff34287
chore: align small review cleanups with main
Jaggob Mar 13, 2026
331adf8
fix(dav): persist client hrefs for created cards
Jaggob Mar 14, 2026
28d81f9
fix(dav): prefer target list on Thunderbird stack moves
Jaggob Mar 14, 2026
72753b7
refactor(db): drop non-essential mapper refactors
Jaggob Mar 14, 2026
3516982
fix(dav): return 404 for direct reads on stale moved hrefs
Jaggob Mar 14, 2026
1ff52bc
refactor(service): consolidate deleted-aware card lookup
Jaggob Mar 14, 2026
ce41e62
fix(dav): resolve href collisions within a board
Jaggob Mar 14, 2026
12fc5e0
fix(dav): align stack calendar rename and acl handling
Jaggob Mar 14, 2026
28e7c22
fix(dav): allow shared calendar property writes again
Jaggob Mar 14, 2026
52350d6
fix(ci): cast activity object ids for psalm
Jaggob Mar 14, 2026
5127e1d
fix(dav): narrow stack object acl without blocking cards
Jaggob Mar 14, 2026
a0611c7
style(dav): format calendar prop patch callback
Jaggob Mar 14, 2026
726f29c
fix(dav): centralize calendar plugin resolution
Jaggob Mar 14, 2026
7ed31bd
chore(dav): clean up calendar placeholder handling
Jaggob Mar 14, 2026
7f2d27e
fix(dav): limit auto-created labels from categories
Jaggob Mar 14, 2026
458a463
fix(dav): sync category changes outside card no-op updates
Jaggob Mar 14, 2026
c679e81
perf(dav): avoid repeated stack lookups for object etags
Jaggob Mar 14, 2026
4e1c644
refactor(dav): inject request and logger services
Jaggob Mar 14, 2026
8fc6434
refactor(dav): inject l10n for placeholder titles
Jaggob Mar 14, 2026
84e26f1
fix(dav): avoid dropping labels on unresolved category sync
Jaggob Mar 14, 2026
a2dce36
fix(tests): align phpunit mocks with dav and activity changes
Jaggob Mar 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 33 additions & 21 deletions lib/Activity/ActivityManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,12 @@ private function createEvent($objectType, $entity, $subject, $additionalParams =
case self::SUBJECT_LABEL_UNASSING:
case self::SUBJECT_CARD_USER_ASSIGN:
case self::SUBJECT_CARD_USER_UNASSIGN:
$subjectParams = $this->findDetailsForCard($entity->getId(), $subject);
$subjectParams = $this->findDetailsForCard(
$entity instanceof Card ? $entity : $entity->getId(),
$subject,
($additionalParams['stack'] ?? null) instanceof Stack ? $additionalParams['stack'] : null,
isset($additionalParams['board']['id'], $additionalParams['board']['title']) && is_array($additionalParams['board']) ? $additionalParams['board'] : null,
);

if (isset($additionalParams['after']) && $additionalParams['after'] instanceof \DateTimeInterface) {
$additionalParams['after'] = $additionalParams['after']->format('c');
Expand Down Expand Up @@ -446,15 +451,17 @@ private function createEvent($objectType, $entity, $subject, $additionalParams =
* @param IEvent $event
*/
private function sendToUsers(IEvent $event) {
switch ($event->getObjectType()) {
case self::DECK_OBJECT_BOARD:
$mapper = $this->boardMapper;
break;
case self::DECK_OBJECT_CARD:
$mapper = $this->cardMapper;
break;
$subjectParams = $event->getSubjectParameters();
$objectType = $event->getObjectType();
if ($objectType === self::DECK_OBJECT_BOARD) {
$boardId = (int)$event->getObjectId();
} elseif (isset($subjectParams['board']['id'])) {
$boardId = (int)$subjectParams['board']['id'];
} elseif ($objectType === self::DECK_OBJECT_CARD) {
$boardId = $this->cardMapper->findBoardId((int)$event->getObjectId());
} else {
return;
}
$boardId = $mapper->findBoardId($event->getObjectId());
/** @var IUser $user */
foreach ($this->permissionService->findUsers($boardId) as $user) {
$event->setAffectedUser($user->getUID());
Expand All @@ -479,8 +486,7 @@ private function findObjectForEntity($objectType, $entity) {
if ($objectType === self::DECK_OBJECT_CARD) {
switch ($className) {
case Card::class:
$objectId = $entity->getId();
break;
return $entity;
case Attachment::class:
case Label::class:
case Assignment::class:
Expand All @@ -497,8 +503,7 @@ private function findObjectForEntity($objectType, $entity) {
if ($objectType === self::DECK_OBJECT_BOARD) {
switch ($className) {
case Board::class:
$objectId = $entity->getId();
break;
return $entity;
case Label::class:
case Stack::class:
case Acl::class:
Expand All @@ -521,16 +526,23 @@ private function findDetailsForStack($stackId) {
];
}

private function findDetailsForCard(int $cardId, ?string $subject = null): array {
$card = $this->cardMapper->find($cardId);
$stack = $this->stackMapper->find($card->getStackId());
$board = $this->boardMapper->find($stack->getBoardId());
private function findDetailsForCard($cardOrId, ?string $subject = null, ?Stack $knownStack = null, ?array $knownBoard = null): array {
$card = $cardOrId instanceof Card ? $cardOrId : $this->cardMapper->find((int)$cardOrId);
$stack = $knownStack ?? $this->stackMapper->find($card->getStackId());

// Reduce the data size in activity table
$boardArray = [
'id' => $board->getId(),
'title' => $board->getTitle(),
];
if ($knownBoard !== null) {
$boardArray = [
'id' => (int)$knownBoard['id'],
'title' => (string)$knownBoard['title'],
];
} else {
$board = $this->boardMapper->find($stack->getBoardId());
$boardArray = [
'id' => $board->getId(),
'title' => $board->getTitle(),
];
}

if ($subject !== self::SUBJECT_CARD_UPDATE_DESCRIPTION) {
$card = [
Expand Down
Loading