Skip to content

Commit 96a12e7

Browse files
Merge pull request #51736 from nextcloud/backport/50244/stable31
2 parents e694f65 + 338da85 commit 96a12e7

File tree

7 files changed

+153
-52
lines changed

7 files changed

+153
-52
lines changed

apps/files_reminders/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
'OCA\\FilesReminders\\Db\\Reminder' => $baseDir . '/../lib/Db/Reminder.php',
1717
'OCA\\FilesReminders\\Db\\ReminderMapper' => $baseDir . '/../lib/Db/ReminderMapper.php',
1818
'OCA\\FilesReminders\\Exception\\NodeNotFoundException' => $baseDir . '/../lib/Exception/NodeNotFoundException.php',
19+
'OCA\\FilesReminders\\Exception\\ReminderNotFoundException' => $baseDir . '/../lib/Exception/ReminderNotFoundException.php',
1920
'OCA\\FilesReminders\\Exception\\UserNotFoundException' => $baseDir . '/../lib/Exception/UserNotFoundException.php',
2021
'OCA\\FilesReminders\\Listener\\LoadAdditionalScriptsListener' => $baseDir . '/../lib/Listener/LoadAdditionalScriptsListener.php',
2122
'OCA\\FilesReminders\\Listener\\NodeDeletedListener' => $baseDir . '/../lib/Listener/NodeDeletedListener.php',

apps/files_reminders/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class ComposerStaticInitFilesReminders
3131
'OCA\\FilesReminders\\Db\\Reminder' => __DIR__ . '/..' . '/../lib/Db/Reminder.php',
3232
'OCA\\FilesReminders\\Db\\ReminderMapper' => __DIR__ . '/..' . '/../lib/Db/ReminderMapper.php',
3333
'OCA\\FilesReminders\\Exception\\NodeNotFoundException' => __DIR__ . '/..' . '/../lib/Exception/NodeNotFoundException.php',
34+
'OCA\\FilesReminders\\Exception\\ReminderNotFoundException' => __DIR__ . '/..' . '/../lib/Exception/ReminderNotFoundException.php',
3435
'OCA\\FilesReminders\\Exception\\UserNotFoundException' => __DIR__ . '/..' . '/../lib/Exception/UserNotFoundException.php',
3536
'OCA\\FilesReminders\\Listener\\LoadAdditionalScriptsListener' => __DIR__ . '/..' . '/../lib/Listener/LoadAdditionalScriptsListener.php',
3637
'OCA\\FilesReminders\\Listener\\NodeDeletedListener' => __DIR__ . '/..' . '/../lib/Listener/NodeDeletedListener.php',

apps/files_reminders/lib/Controller/ApiController.php

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
use DateTimeZone;
1515
use Exception;
1616
use OCA\FilesReminders\Exception\NodeNotFoundException;
17+
use OCA\FilesReminders\Exception\ReminderNotFoundException;
1718
use OCA\FilesReminders\Service\ReminderService;
18-
use OCP\AppFramework\Db\DoesNotExistException;
1919
use OCP\AppFramework\Http;
2020
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
2121
use OCP\AppFramework\Http\DataResponse;
@@ -53,15 +53,14 @@ public function get(int $fileId): DataResponse {
5353

5454
try {
5555
$reminder = $this->reminderService->getDueForUser($user, $fileId);
56-
$reminderData = [
56+
if ($reminder === null) {
57+
return new DataResponse(['dueDate' => null], Http::STATUS_OK);
58+
}
59+
return new DataResponse([
5760
'dueDate' => $reminder->getDueDate()->format(DateTimeInterface::ATOM), // ISO 8601
58-
];
59-
return new DataResponse($reminderData, Http::STATUS_OK);
60-
} catch (DoesNotExistException $e) {
61-
$reminderData = [
62-
'dueDate' => null,
63-
];
64-
return new DataResponse($reminderData, Http::STATUS_OK);
61+
], Http::STATUS_OK);
62+
} catch (NodeNotFoundException $e) {
63+
return new DataResponse(['dueDate' => null], Http::STATUS_OK);
6564
}
6665
}
6766

@@ -125,7 +124,7 @@ public function remove(int $fileId): DataResponse {
125124
try {
126125
$this->reminderService->remove($user, $fileId);
127126
return new DataResponse([], Http::STATUS_OK);
128-
} catch (DoesNotExistException $e) {
127+
} catch (NodeNotFoundException|ReminderNotFoundException $e) {
129128
return new DataResponse([], Http::STATUS_NOT_FOUND);
130129
}
131130
}

apps/files_reminders/lib/Dav/PropFindPlugin.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
namespace OCA\FilesReminders\Dav;
1111

1212
use DateTimeInterface;
13+
use OCA\DAV\Connector\Sabre\Directory;
1314
use OCA\DAV\Connector\Sabre\Node;
1415
use OCA\FilesReminders\Service\ReminderService;
15-
use OCP\AppFramework\Db\DoesNotExistException;
16+
use OCP\Files\Folder;
1617
use OCP\IUser;
1718
use OCP\IUserSession;
1819
use Sabre\DAV\INode;
@@ -43,6 +44,15 @@ public function propFind(PropFind $propFind, INode $node) {
4344
return;
4445
}
4546

47+
if (
48+
$node instanceof Directory
49+
&& $propFind->getDepth() > 0
50+
&& $propFind->getStatus(static::REMINDER_DUE_DATE_PROPERTY) !== null
51+
) {
52+
$folder = $node->getNode();
53+
$this->cacheFolder($folder);
54+
}
55+
4656
$propFind->handle(
4757
static::REMINDER_DUE_DATE_PROPERTY,
4858
function () use ($node) {
@@ -52,14 +62,21 @@ function () use ($node) {
5262
}
5363

5464
$fileId = $node->getId();
55-
try {
56-
$reminder = $this->reminderService->getDueForUser($user, $fileId);
57-
} catch (DoesNotExistException $e) {
65+
$reminder = $this->reminderService->getDueForUser($user, $fileId);
66+
if ($reminder === null) {
5867
return '';
5968
}
6069

6170
return $reminder->getDueDate()->format(DateTimeInterface::ATOM); // ISO 8601
6271
},
6372
);
6473
}
74+
75+
private function cacheFolder(Folder $folder): void {
76+
$user = $this->userSession->getUser();
77+
if (!($user instanceof IUser)) {
78+
return;
79+
}
80+
$this->reminderService->cacheFolder($user, $folder);
81+
}
6582
}

apps/files_reminders/lib/Db/ReminderMapper.php

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OCP\AppFramework\Db\DoesNotExistException;
1414
use OCP\AppFramework\Db\QBMapper;
1515
use OCP\DB\QueryBuilder\IQueryBuilder;
16+
use OCP\Files\Folder;
1617
use OCP\Files\Node;
1718
use OCP\Files\NotFoundException;
1819
use OCP\IDBConnection;
@@ -39,16 +40,6 @@ public function markNotified(Reminder $reminder): Reminder {
3940
return $this->update($reminderUpdate);
4041
}
4142

42-
public function find(int $id): Reminder {
43-
$qb = $this->db->getQueryBuilder();
44-
45-
$qb->select('id', 'user_id', 'file_id', 'due_date', 'updated_at', 'created_at', 'notified')
46-
->from($this->getTableName())
47-
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
48-
49-
return $this->findEntity($qb);
50-
}
51-
5243
/**
5344
* @throws DoesNotExistException
5445
*/
@@ -141,4 +132,30 @@ public function findNotified(DateTime $buffer, ?int $limit = null) {
141132

142133
return $this->findEntities($qb);
143134
}
135+
136+
/**
137+
* @return Reminder[]
138+
*/
139+
public function findAllInFolder(IUser $user, Folder $folder) {
140+
$fileIds = array_values(array_filter(array_map(
141+
function (Node $node) {
142+
try {
143+
return $node->getId();
144+
} catch (NotFoundException $e) {
145+
return null;
146+
}
147+
},
148+
$folder->getDirectoryListing(),
149+
)));
150+
151+
$qb = $this->db->getQueryBuilder();
152+
153+
$qb->select('id', 'user_id', 'file_id', 'due_date', 'updated_at', 'created_at', 'notified')
154+
->from($this->getTableName())
155+
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID(), IQueryBuilder::PARAM_STR)))
156+
->andWhere($qb->expr()->in('file_id', $qb->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)))
157+
->orderBy('due_date', 'ASC');
158+
159+
return $this->findEntities($qb);
160+
}
144161
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\FilesReminders\Exception;
11+
12+
use Exception;
13+
14+
class ReminderNotFoundException extends Exception {
15+
}

apps/files_reminders/lib/Service/ReminderService.php

Lines changed: 79 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@
1515
use OCA\FilesReminders\Db\Reminder;
1616
use OCA\FilesReminders\Db\ReminderMapper;
1717
use OCA\FilesReminders\Exception\NodeNotFoundException;
18+
use OCA\FilesReminders\Exception\ReminderNotFoundException;
1819
use OCA\FilesReminders\Exception\UserNotFoundException;
1920
use OCA\FilesReminders\Model\RichReminder;
2021
use OCP\AppFramework\Db\DoesNotExistException;
22+
use OCP\Files\Folder;
2123
use OCP\Files\IRootFolder;
2224
use OCP\Files\Node;
25+
use OCP\ICache;
26+
use OCP\ICacheFactory;
2327
use OCP\IURLGenerator;
2428
use OCP\IUser;
2529
use OCP\IUserManager;
@@ -28,30 +32,57 @@
2832
use Throwable;
2933

3034
class ReminderService {
35+
36+
private ICache $cache;
37+
3138
public function __construct(
3239
protected IUserManager $userManager,
3340
protected IURLGenerator $urlGenerator,
3441
protected INotificationManager $notificationManager,
3542
protected ReminderMapper $reminderMapper,
3643
protected IRootFolder $root,
3744
protected LoggerInterface $logger,
45+
protected ICacheFactory $cacheFactory,
3846
) {
47+
$this->cache = $this->cacheFactory->createInMemory();
3948
}
4049

41-
/**
42-
* @throws DoesNotExistException
43-
*/
44-
public function get(int $id): RichReminder {
45-
$reminder = $this->reminderMapper->find($id);
46-
return new RichReminder($reminder, $this->root);
50+
public function cacheFolder(IUser $user, Folder $folder): void {
51+
$reminders = $this->reminderMapper->findAllInFolder($user, $folder);
52+
$reminderMap = [];
53+
foreach ($reminders as $reminder) {
54+
$reminderMap[$reminder->getFileId()] = $reminder;
55+
}
56+
57+
$nodes = $folder->getDirectoryListing();
58+
foreach ($nodes as $node) {
59+
$reminder = $reminderMap[$node->getId()] ?? false;
60+
$this->cache->set("{$user->getUID()}-{$node->getId()}", $reminder);
61+
}
4762
}
4863

4964
/**
50-
* @throws DoesNotExistException
65+
* @throws NodeNotFoundException
5166
*/
52-
public function getDueForUser(IUser $user, int $fileId): RichReminder {
53-
$reminder = $this->reminderMapper->findDueForUser($user, $fileId);
54-
return new RichReminder($reminder, $this->root);
67+
public function getDueForUser(IUser $user, int $fileId): ?RichReminder {
68+
$this->checkNode($user, $fileId);
69+
/** @var null|false|Reminder $cachedReminder */
70+
$cachedReminder = $this->cache->get("{$user->getUID()}-$fileId");
71+
if ($cachedReminder === false) {
72+
return null;
73+
}
74+
if ($cachedReminder instanceof Reminder) {
75+
return new RichReminder($cachedReminder, $this->root);
76+
}
77+
78+
try {
79+
$reminder = $this->reminderMapper->findDueForUser($user, $fileId);
80+
$this->cache->set("{$user->getUID()}-$fileId", $reminder);
81+
return new RichReminder($reminder, $this->root);
82+
} catch (DoesNotExistException $e) {
83+
$this->cache->set("{$user->getUID()}-$fileId", false);
84+
return null;
85+
}
5586
}
5687

5788
/**
@@ -74,48 +105,50 @@ public function getAll(?IUser $user = null) {
74105
*/
75106
public function createOrUpdate(IUser $user, int $fileId, DateTime $dueDate): bool {
76107
$now = new DateTime('now', new DateTimeZone('UTC'));
77-
try {
78-
$reminder = $this->reminderMapper->findDueForUser($user, $fileId);
79-
$reminder->setDueDate($dueDate);
80-
$reminder->setUpdatedAt($now);
81-
$this->reminderMapper->update($reminder);
82-
return false;
83-
} catch (DoesNotExistException $e) {
84-
$node = $this->root->getUserFolder($user->getUID())->getFirstNodeById($fileId);
85-
if (!$node) {
86-
throw new NodeNotFoundException();
87-
}
88-
// Create new reminder if no reminder is found
108+
$this->checkNode($user, $fileId);
109+
$reminder = $this->getDueForUser($user, $fileId);
110+
if ($reminder === null) {
89111
$reminder = new Reminder();
90112
$reminder->setUserId($user->getUID());
91113
$reminder->setFileId($fileId);
92114
$reminder->setDueDate($dueDate);
93115
$reminder->setUpdatedAt($now);
94116
$reminder->setCreatedAt($now);
95117
$this->reminderMapper->insert($reminder);
118+
$this->cache->set("{$user->getUID()}-$fileId", $reminder);
96119
return true;
97120
}
121+
$reminder->setDueDate($dueDate);
122+
$reminder->setUpdatedAt($now);
123+
$this->reminderMapper->update($reminder);
124+
$this->cache->set("{$user->getUID()}-$fileId", $reminder);
125+
return false;
98126
}
99127

100128
/**
101-
* @throws DoesNotExistException
129+
* @throws NodeNotFoundException
130+
* @throws ReminderNotFoundException
102131
*/
103132
public function remove(IUser $user, int $fileId): void {
104-
$reminder = $this->reminderMapper->findDueForUser($user, $fileId);
105-
$this->reminderMapper->delete($reminder);
133+
$this->checkNode($user, $fileId);
134+
$reminder = $this->getDueForUser($user, $fileId);
135+
if ($reminder === null) {
136+
throw new ReminderNotFoundException();
137+
}
138+
$this->deleteReminder($reminder);
106139
}
107140

108141
public function removeAllForNode(Node $node): void {
109142
$reminders = $this->reminderMapper->findAllForNode($node);
110143
foreach ($reminders as $reminder) {
111-
$this->reminderMapper->delete($reminder);
144+
$this->deleteReminder($reminder);
112145
}
113146
}
114147

115148
public function removeAllForUser(IUser $user): void {
116149
$reminders = $this->reminderMapper->findAllForUser($user);
117150
foreach ($reminders as $reminder) {
118-
$this->reminderMapper->delete($reminder);
151+
$this->deleteReminder($reminder);
119152
}
120153
}
121154

@@ -147,6 +180,7 @@ public function send(Reminder $reminder): void {
147180
try {
148181
$this->notificationManager->notify($notification);
149182
$this->reminderMapper->markNotified($reminder);
183+
$this->cache->set("{$user->getUID()}-{$reminder->getFileId()}", $reminder);
150184
} catch (Throwable $th) {
151185
$this->logger->error($th->getMessage(), $th->getTrace());
152186
}
@@ -158,7 +192,24 @@ public function cleanUp(?int $limit = null): void {
158192
->modify('-1 day');
159193
$reminders = $this->reminderMapper->findNotified($buffer, $limit);
160194
foreach ($reminders as $reminder) {
161-
$this->reminderMapper->delete($reminder);
195+
$this->deleteReminder($reminder);
196+
}
197+
}
198+
199+
private function deleteReminder(Reminder $reminder): void {
200+
$this->reminderMapper->delete($reminder);
201+
$this->cache->set("{$reminder->getUserId()}-{$reminder->getFileId()}", false);
202+
}
203+
204+
205+
/**
206+
* @throws NodeNotFoundException
207+
*/
208+
private function checkNode(IUser $user, int $fileId): void {
209+
$userFolder = $this->root->getUserFolder($user->getUID());
210+
$node = $userFolder->getFirstNodeById($fileId);
211+
if ($node === null) {
212+
throw new NodeNotFoundException();
162213
}
163214
}
164215
}

0 commit comments

Comments
 (0)