Skip to content

Commit 7eca726

Browse files
fix(dav): exclude trashed calendar objects from reminder processing
The getRemindersToProcess() query joined calendarobjects without filtering out soft-deleted (trashed) objects. This caused reminders to continue firing for events the user had already deleted, since deletion moves objects to the trashbin rather than removing them from the calendarobjects table. Add a deleted_at IS NULL condition to the calendarobjects join so trashed objects are excluded from reminder processing. Additionally: - Remove unnecessary GROUP BY (joins are on primary keys with no aggregate functions, so no duplicate rows are possible) - Add $stmt->free() to release database cursors after fetching - Clean up redundant docblocks and fix typos - Extract column list for readability Fixes: #53497 Signed-off-by: Josh <josh.t.richards@gmail.com>
1 parent 753e6ee commit 7eca726

File tree

1 file changed

+60
-75
lines changed

1 file changed

+60
-75
lines changed

apps/dav/lib/CalDAV/Reminder/Backend.php

Lines changed: 60 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -3,92 +3,89 @@
33
declare(strict_types=1);
44

55
/**
6-
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-FileCopyrightText: 2019-2026 Nextcloud GmbH and Nextcloud contributors
77
* SPDX-License-Identifier: AGPL-3.0-or-later
88
*/
99
namespace OCA\DAV\CalDAV\Reminder;
1010

1111
use OCP\AppFramework\Utility\ITimeFactory;
1212
use OCP\IDBConnection;
1313

14-
/**
15-
* Class Backend
16-
*
17-
* @package OCA\DAV\CalDAV\Reminder
18-
*/
1914
class Backend {
20-
21-
/**
22-
* Backend constructor.
23-
*
24-
* @param IDBConnection $db
25-
* @param ITimeFactory $timeFactory
26-
*/
2715
public function __construct(
2816
protected IDBConnection $db,
2917
protected ITimeFactory $timeFactory,
3018
) {
3119
}
3220

3321
/**
34-
* Get all reminders with a notification date before now
35-
*
36-
* @return array
37-
* @throws \Exception
22+
* Get all reminders with a notification date before now,
23+
* excluding objects that have been moved to the trashbin.
3824
*/
39-
public function getRemindersToProcess():array {
25+
public function getRemindersToProcess(): array {
4026
$query = $this->db->getQueryBuilder();
41-
$query->select(['cr.id', 'cr.calendar_id','cr.object_id','cr.is_recurring','cr.uid','cr.recurrence_id','cr.is_recurrence_exception','cr.event_hash','cr.alarm_hash','cr.type','cr.is_relative','cr.notification_date','cr.is_repeat_based','co.calendardata', 'c.displayname', 'c.principaluri'])
27+
28+
$columns = [
29+
'cr.id',
30+
'cr.calendar_id',
31+
'cr.object_id',
32+
'cr.is_recurring',
33+
'cr.uid',
34+
'cr.recurrence_id',
35+
'cr.is_recurrence_exception',
36+
'cr.event_hash',
37+
'cr.alarm_hash',
38+
'cr.type',
39+
'cr.is_relative',
40+
'cr.notification_date',
41+
'cr.is_repeat_based',
42+
'co.calendardata',
43+
'c.displayname',
44+
'c.principaluri',
45+
];
46+
47+
$query->select($columns)
4248
->from('calendar_reminders', 'cr')
43-
->where($query->expr()->lte('cr.notification_date', $query->createNamedParameter($this->timeFactory->getTime())))
44-
->join('cr', 'calendarobjects', 'co', $query->expr()->eq('cr.object_id', 'co.id'))
45-
->join('cr', 'calendars', 'c', $query->expr()->eq('cr.calendar_id', 'c.id'))
46-
->groupBy('cr.event_hash', 'cr.notification_date', 'cr.type', 'cr.id', 'cr.calendar_id', 'cr.object_id', 'cr.is_recurring', 'cr.uid', 'cr.recurrence_id', 'cr.is_recurrence_exception', 'cr.alarm_hash', 'cr.is_relative', 'cr.is_repeat_based', 'co.calendardata', 'c.displayname', 'c.principaluri');
49+
->where($query->expr()->lte(
50+
'cr.notification_date',
51+
$query->createNamedParameter($this->timeFactory->getTime())
52+
))
53+
->join('cr', 'calendarobjects', 'co', $query->expr()->andX(
54+
$query->expr()->eq('cr.object_id', 'co.id'),
55+
$query->expr()->isNull('co.deleted_at')
56+
))
57+
->join('cr', 'calendars', 'c', $query->expr()->eq('cr.calendar_id', 'c.id'));
58+
4759
$stmt = $query->executeQuery();
60+
$rows = $stmt->fetchAllAssociative();
61+
$stmt->free();
4862

49-
return array_map(
50-
[$this, 'fixRowTyping'],
51-
$stmt->fetchAllAssociative()
52-
);
63+
return array_map([$this, 'fixRowTyping'], $rows);
5364
}
5465

5566
/**
56-
* Get all scheduled reminders for an event
57-
*
58-
* @param int $objectId
59-
* @return array
67+
* Get all scheduled reminders for an event.
6068
*/
61-
public function getAllScheduledRemindersForEvent(int $objectId):array {
69+
public function getAllScheduledRemindersForEvent(int $objectId): array {
6270
$query = $this->db->getQueryBuilder();
6371
$query->select('*')
6472
->from('calendar_reminders')
6573
->where($query->expr()->eq('object_id', $query->createNamedParameter($objectId)));
74+
6675
$stmt = $query->executeQuery();
76+
$rows = $stmt->fetchAllAssociative();
77+
$stmt->free();
6778

68-
return array_map(
69-
[$this, 'fixRowTyping'],
70-
$stmt->fetchAllAssociative()
71-
);
79+
return array_map([$this, 'fixRowTyping'], $rows);
7280
}
7381

7482
/**
75-
* Insert a new reminder into the database
83+
* Insert a new reminder into the database.
7684
*
77-
* @param int $calendarId
78-
* @param int $objectId
79-
* @param string $uid
80-
* @param bool $isRecurring
81-
* @param int $recurrenceId
82-
* @param bool $isRecurrenceException
83-
* @param string $eventHash
84-
* @param string $alarmHash
85-
* @param string $type
86-
* @param bool $isRelative
87-
* @param int $notificationDate
88-
* @param bool $isRepeatBased
8985
* @return int The insert id
9086
*/
91-
public function insertReminder(int $calendarId,
87+
public function insertReminder(
88+
int $calendarId,
9289
int $objectId,
9390
string $uid,
9491
bool $isRecurring,
@@ -99,7 +96,8 @@ public function insertReminder(int $calendarId,
9996
string $type,
10097
bool $isRelative,
10198
int $notificationDate,
102-
bool $isRepeatBased):int {
99+
bool $isRepeatBased,
100+
): int {
103101
$query = $this->db->getQueryBuilder();
104102
$query->insert('calendar_reminders')
105103
->values([
@@ -122,13 +120,12 @@ public function insertReminder(int $calendarId,
122120
}
123121

124122
/**
125-
* Sets a new notificationDate on an existing reminder
126-
*
127-
* @param int $reminderId
128-
* @param int $newNotificationDate
123+
* Set a new notification date on an existing reminder.
129124
*/
130-
public function updateReminder(int $reminderId,
131-
int $newNotificationDate):void {
125+
public function updateReminder(
126+
int $reminderId,
127+
int $newNotificationDate
128+
): void {
132129
$query = $this->db->getQueryBuilder();
133130
$query->update('calendar_reminders')
134131
->set('notification_date', $query->createNamedParameter($newNotificationDate))
@@ -137,12 +134,9 @@ public function updateReminder(int $reminderId,
137134
}
138135

139136
/**
140-
* Remove a reminder by it's id
141-
*
142-
* @param integer $reminderId
143-
* @return void
137+
* Remove a reminder by its id.
144138
*/
145-
public function removeReminder(int $reminderId):void {
139+
public function removeReminder(int $reminderId): void {
146140
$query = $this->db->getQueryBuilder();
147141

148142
$query->delete('calendar_reminders')
@@ -151,11 +145,9 @@ public function removeReminder(int $reminderId):void {
151145
}
152146

153147
/**
154-
* Cleans reminders in database
155-
*
156-
* @param int $objectId
148+
* Remove all reminders for a calendar object.
157149
*/
158-
public function cleanRemindersForEvent(int $objectId):void {
150+
public function cleanRemindersForEvent(int $objectId): void {
159151
$query = $this->db->getQueryBuilder();
160152

161153
$query->delete('calendar_reminders')
@@ -164,23 +156,16 @@ public function cleanRemindersForEvent(int $objectId):void {
164156
}
165157

166158
/**
167-
* Remove all reminders for a calendar
168-
*
169-
* @param int $calendarId
170-
* @return void
159+
* Remove all reminders for a calendar.
171160
*/
172-
public function cleanRemindersForCalendar(int $calendarId):void {
161+
public function cleanRemindersForCalendar(int $calendarId): void {
173162
$query = $this->db->getQueryBuilder();
174163

175164
$query->delete('calendar_reminders')
176165
->where($query->expr()->eq('calendar_id', $query->createNamedParameter($calendarId)))
177166
->executeStatement();
178167
}
179168

180-
/**
181-
* @param array $row
182-
* @return array
183-
*/
184169
private function fixRowTyping(array $row): array {
185170
$row['id'] = (int)$row['id'];
186171
$row['calendar_id'] = (int)$row['calendar_id'];

0 commit comments

Comments
 (0)