diff options
Diffstat (limited to 'apps/files_reminders/lib')
20 files changed, 346 insertions, 378 deletions
diff --git a/apps/files_reminders/lib/AppInfo/Application.php b/apps/files_reminders/lib/AppInfo/Application.php index 56b0c97a7a2..2776e9db0b1 100644 --- a/apps/files_reminders/lib/AppInfo/Application.php +++ b/apps/files_reminders/lib/AppInfo/Application.php @@ -3,34 +3,20 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\AppInfo; +use OCA\DAV\Events\SabrePluginAddEvent; use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCA\FilesReminders\Listener\LoadAdditionalScriptsListener; use OCA\FilesReminders\Listener\NodeDeletedListener; +use OCA\FilesReminders\Listener\SabrePluginAddListener; use OCA\FilesReminders\Listener\UserDeletedListener; use OCA\FilesReminders\Notification\Notifier; +use OCA\FilesReminders\SetupChecks\NeedNotificationsApp; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; @@ -51,9 +37,13 @@ class Application extends App implements IBootstrap { public function register(IRegistrationContext $context): void { $context->registerNotifierService(Notifier::class); + $context->registerEventListener(SabrePluginAddEvent::class, SabrePluginAddListener::class); + $context->registerEventListener(NodeDeletedEvent::class, NodeDeletedListener::class); $context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class); $context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalScriptsListener::class); + + $context->registerSetupCheck(NeedNotificationsApp::class); } } diff --git a/apps/files_reminders/lib/BackgroundJob/CleanUpReminders.php b/apps/files_reminders/lib/BackgroundJob/CleanUpReminders.php index afa8c514a0a..35b72b190e8 100644 --- a/apps/files_reminders/lib/BackgroundJob/CleanUpReminders.php +++ b/apps/files_reminders/lib/BackgroundJob/CleanUpReminders.php @@ -3,32 +3,14 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\BackgroundJob; use OCA\FilesReminders\Service\ReminderService; use OCP\AppFramework\Utility\ITimeFactory; -use OCP\BackgroundJob\IJob; use OCP\BackgroundJob\TimedJob; class CleanUpReminders extends TimedJob { @@ -39,7 +21,7 @@ class CleanUpReminders extends TimedJob { parent::__construct($time); $this->setInterval(24 * 60 * 60); // 1 day - $this->setTimeSensitivity(IJob::TIME_INSENSITIVE); + $this->setTimeSensitivity(self::TIME_INSENSITIVE); } /** diff --git a/apps/files_reminders/lib/BackgroundJob/ScheduledNotifications.php b/apps/files_reminders/lib/BackgroundJob/ScheduledNotifications.php index 15ae56f0698..ab8c762d674 100644 --- a/apps/files_reminders/lib/BackgroundJob/ScheduledNotifications.php +++ b/apps/files_reminders/lib/BackgroundJob/ScheduledNotifications.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\BackgroundJob; @@ -30,10 +13,10 @@ use OCA\FilesReminders\Db\ReminderMapper; use OCA\FilesReminders\Service\ReminderService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; -use OCP\BackgroundJob\Job; +use OCP\BackgroundJob\TimedJob; use Psr\Log\LoggerInterface; -class ScheduledNotifications extends Job { +class ScheduledNotifications extends TimedJob { public function __construct( ITimeFactory $time, protected ReminderMapper $reminderMapper, @@ -41,6 +24,8 @@ class ScheduledNotifications extends Job { protected LoggerInterface $logger, ) { parent::__construct($time); + + $this->setInterval(60); } /** diff --git a/apps/files_reminders/lib/Command/ListCommand.php b/apps/files_reminders/lib/Command/ListCommand.php index f9113e15605..118d00c45d3 100644 --- a/apps/files_reminders/lib/Command/ListCommand.php +++ b/apps/files_reminders/lib/Command/ListCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Command; diff --git a/apps/files_reminders/lib/Controller/ApiController.php b/apps/files_reminders/lib/Controller/ApiController.php index 91b7e51eb6f..c95a74a04f4 100644 --- a/apps/files_reminders/lib/Controller/ApiController.php +++ b/apps/files_reminders/lib/Controller/ApiController.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Controller; @@ -31,8 +14,8 @@ use DateTimeInterface; use DateTimeZone; use Exception; use OCA\FilesReminders\Exception\NodeNotFoundException; +use OCA\FilesReminders\Exception\ReminderNotFoundException; use OCA\FilesReminders\Service\ReminderService; -use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; @@ -56,10 +39,10 @@ class ApiController extends OCSController { * Get a reminder * * @param int $fileId ID of the file - * @return DataResponse<Http::STATUS_OK, array{dueDate: ?string}, array{}>|DataResponse<Http::STATUS_UNAUTHORIZED, array<empty>, array{}> + * @return DataResponse<Http::STATUS_OK, array{dueDate: ?string}, array{}>|DataResponse<Http::STATUS_UNAUTHORIZED, list<empty>, array{}> * * 200: Reminder returned - * 401: User not found + * 401: Account not found */ #[NoAdminRequired] public function get(int $fileId): DataResponse { @@ -70,15 +53,14 @@ class ApiController extends OCSController { try { $reminder = $this->reminderService->getDueForUser($user, $fileId); - $reminderData = [ + if ($reminder === null) { + return new DataResponse(['dueDate' => null], Http::STATUS_OK); + } + return new DataResponse([ 'dueDate' => $reminder->getDueDate()->format(DateTimeInterface::ATOM), // ISO 8601 - ]; - return new DataResponse($reminderData, Http::STATUS_OK); - } catch (DoesNotExistException $e) { - $reminderData = [ - 'dueDate' => null, - ]; - return new DataResponse($reminderData, Http::STATUS_OK); + ], Http::STATUS_OK); + } catch (NodeNotFoundException $e) { + return new DataResponse(['dueDate' => null], Http::STATUS_OK); } } @@ -88,12 +70,12 @@ class ApiController extends OCSController { * @param int $fileId ID of the file * @param string $dueDate ISO 8601 formatted date time string * - * @return DataResponse<Http::STATUS_OK|Http::STATUS_CREATED|Http::STATUS_BAD_REQUEST|Http::STATUS_UNAUTHORIZED|Http::STATUS_NOT_FOUND, array<empty>, array{}> + * @return DataResponse<Http::STATUS_OK|Http::STATUS_CREATED|Http::STATUS_BAD_REQUEST|Http::STATUS_UNAUTHORIZED|Http::STATUS_NOT_FOUND, list<empty>, array{}> * * 200: Reminder updated * 201: Reminder created successfully * 400: Creating reminder is not possible - * 401: User not found + * 401: Account not found * 404: File not found */ #[NoAdminRequired] @@ -126,10 +108,10 @@ class ApiController extends OCSController { * * @param int $fileId ID of the file * - * @return DataResponse<Http::STATUS_OK|Http::STATUS_UNAUTHORIZED|Http::STATUS_NOT_FOUND, array<empty>, array{}> + * @return DataResponse<Http::STATUS_OK|Http::STATUS_UNAUTHORIZED|Http::STATUS_NOT_FOUND, list<empty>, array{}> * * 200: Reminder deleted successfully - * 401: User not found + * 401: Account not found * 404: Reminder not found */ #[NoAdminRequired] @@ -142,7 +124,7 @@ class ApiController extends OCSController { try { $this->reminderService->remove($user, $fileId); return new DataResponse([], Http::STATUS_OK); - } catch (DoesNotExistException $e) { + } catch (NodeNotFoundException|ReminderNotFoundException $e) { return new DataResponse([], Http::STATUS_NOT_FOUND); } } diff --git a/apps/files_reminders/lib/Dav/PropFindPlugin.php b/apps/files_reminders/lib/Dav/PropFindPlugin.php new file mode 100644 index 00000000000..014e636eb2d --- /dev/null +++ b/apps/files_reminders/lib/Dav/PropFindPlugin.php @@ -0,0 +1,82 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\FilesReminders\Dav; + +use DateTimeInterface; +use OCA\DAV\Connector\Sabre\Directory; +use OCA\DAV\Connector\Sabre\Node; +use OCA\FilesReminders\Service\ReminderService; +use OCP\Files\Folder; +use OCP\IUser; +use OCP\IUserSession; +use Sabre\DAV\INode; +use Sabre\DAV\PropFind; +use Sabre\DAV\Server; +use Sabre\DAV\ServerPlugin; + +class PropFindPlugin extends ServerPlugin { + + public const REMINDER_DUE_DATE_PROPERTY = '{http://nextcloud.org/ns}reminder-due-date'; + + public function __construct( + private ReminderService $reminderService, + private IUserSession $userSession, + ) { + } + + public function initialize(Server $server): void { + $server->on('propFind', [$this, 'propFind']); + } + + public function propFind(PropFind $propFind, INode $node) { + if (!in_array(static::REMINDER_DUE_DATE_PROPERTY, $propFind->getRequestedProperties())) { + return; + } + + if (!($node instanceof Node)) { + return; + } + + if ( + $node instanceof Directory + && $propFind->getDepth() > 0 + && $propFind->getStatus(static::REMINDER_DUE_DATE_PROPERTY) !== null + ) { + $folder = $node->getNode(); + $this->cacheFolder($folder); + } + + $propFind->handle( + static::REMINDER_DUE_DATE_PROPERTY, + function () use ($node) { + $user = $this->userSession->getUser(); + if (!($user instanceof IUser)) { + return ''; + } + + $fileId = $node->getId(); + $reminder = $this->reminderService->getDueForUser($user, $fileId, false); + if ($reminder === null) { + return ''; + } + + return $reminder->getDueDate()->format(DateTimeInterface::ATOM); // ISO 8601 + }, + ); + } + + private function cacheFolder(Folder $folder): void { + $user = $this->userSession->getUser(); + if (!($user instanceof IUser)) { + return; + } + $this->reminderService->cacheFolder($user, $folder); + } +} diff --git a/apps/files_reminders/lib/Db/Reminder.php b/apps/files_reminders/lib/Db/Reminder.php index 36f967f1434..1a8ba15063e 100644 --- a/apps/files_reminders/lib/Db/Reminder.php +++ b/apps/files_reminders/lib/Db/Reminder.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Db; diff --git a/apps/files_reminders/lib/Db/ReminderMapper.php b/apps/files_reminders/lib/Db/ReminderMapper.php index 3f7fc04b2f0..63cba437d07 100644 --- a/apps/files_reminders/lib/Db/ReminderMapper.php +++ b/apps/files_reminders/lib/Db/ReminderMapper.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Db; @@ -30,6 +13,7 @@ use DateTime; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\Folder; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\IDBConnection; @@ -56,16 +40,6 @@ class ReminderMapper extends QBMapper { return $this->update($reminderUpdate); } - public function find(int $id): Reminder { - $qb = $this->db->getQueryBuilder(); - - $qb->select('id', 'user_id', 'file_id', 'due_date', 'updated_at', 'created_at', 'notified') - ->from($this->getTableName()) - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))); - - return $this->findEntity($qb); - } - /** * @throws DoesNotExistException */ @@ -152,10 +126,26 @@ class ReminderMapper extends QBMapper { $qb->select('id', 'user_id', 'file_id', 'due_date', 'updated_at', 'created_at', 'notified') ->from($this->getTableName()) ->where($qb->expr()->eq('notified', $qb->createNamedParameter(true, IQueryBuilder::PARAM_BOOL))) - ->andWhere($qb->expr()->lt('due_date', $qb->createNamedParameter($buffer, IQueryBuilder::PARAM_DATE))) + ->andWhere($qb->expr()->lt('due_date', $qb->createNamedParameter($buffer, IQueryBuilder::PARAM_DATETIME_MUTABLE))) ->orderBy('due_date', 'ASC') ->setMaxResults($limit); return $this->findEntities($qb); } + + /** + * @return Reminder[] + */ + public function findAllInFolder(IUser $user, Folder $folder) { + $qb = $this->db->getQueryBuilder(); + + $qb->select('r.id', 'r.user_id', 'r.file_id', 'r.due_date', 'r.updated_at', 'r.created_at', 'r.notified') + ->from($this->getTableName(), 'r') + ->innerJoin('r', 'filecache', 'f', $qb->expr()->eq('r.file_id', 'f.fileid')) + ->where($qb->expr()->eq('r.user_id', $qb->createNamedParameter($user->getUID(), IQueryBuilder::PARAM_STR))) + ->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($folder->getId(), IQueryBuilder::PARAM_INT))) + ->orderBy('r.due_date', 'ASC'); + + return $this->findEntities($qb); + } } diff --git a/apps/files_reminders/lib/Exception/NodeNotFoundException.php b/apps/files_reminders/lib/Exception/NodeNotFoundException.php index 5dfe784f5fc..65e1b28fe1e 100644 --- a/apps/files_reminders/lib/Exception/NodeNotFoundException.php +++ b/apps/files_reminders/lib/Exception/NodeNotFoundException.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Exception; diff --git a/apps/files_reminders/lib/Exception/ReminderNotFoundException.php b/apps/files_reminders/lib/Exception/ReminderNotFoundException.php new file mode 100644 index 00000000000..fd7031a834f --- /dev/null +++ b/apps/files_reminders/lib/Exception/ReminderNotFoundException.php @@ -0,0 +1,15 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\FilesReminders\Exception; + +use Exception; + +class ReminderNotFoundException extends Exception { +} diff --git a/apps/files_reminders/lib/Exception/UserNotFoundException.php b/apps/files_reminders/lib/Exception/UserNotFoundException.php index 3c57ea475e9..d1ddf9148cb 100644 --- a/apps/files_reminders/lib/Exception/UserNotFoundException.php +++ b/apps/files_reminders/lib/Exception/UserNotFoundException.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Exception; diff --git a/apps/files_reminders/lib/Listener/LoadAdditionalScriptsListener.php b/apps/files_reminders/lib/Listener/LoadAdditionalScriptsListener.php index f95b3da3d61..765bf1e3ce2 100644 --- a/apps/files_reminders/lib/Listener/LoadAdditionalScriptsListener.php +++ b/apps/files_reminders/lib/Listener/LoadAdditionalScriptsListener.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Listener; @@ -32,10 +15,13 @@ use OCP\App\IAppManager; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Util; +use Psr\Log\LoggerInterface; +/** @template-implements IEventListener<LoadAdditionalScriptsEvent> */ class LoadAdditionalScriptsListener implements IEventListener { public function __construct( private IAppManager $appManager, + private LoggerInterface $logger, ) { } @@ -44,7 +30,9 @@ class LoadAdditionalScriptsListener implements IEventListener { return; } - if (!$this->appManager->isEnabledForUser('notifications')) { + if (!$this->appManager->isEnabledForUser(Application::APP_ID) + || !$this->appManager->isEnabledForUser('notifications') + ) { return; } diff --git a/apps/files_reminders/lib/Listener/NodeDeletedListener.php b/apps/files_reminders/lib/Listener/NodeDeletedListener.php index 944ce2cbe36..06a4733e6cd 100644 --- a/apps/files_reminders/lib/Listener/NodeDeletedListener.php +++ b/apps/files_reminders/lib/Listener/NodeDeletedListener.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Listener; @@ -31,6 +14,7 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Files\Events\Node\NodeDeletedEvent; +/** @template-implements IEventListener<NodeDeletedEvent> */ class NodeDeletedListener implements IEventListener { public function __construct( private ReminderService $reminderService, diff --git a/apps/files_reminders/lib/Listener/SabrePluginAddListener.php b/apps/files_reminders/lib/Listener/SabrePluginAddListener.php new file mode 100644 index 00000000000..b2c4501f9af --- /dev/null +++ b/apps/files_reminders/lib/Listener/SabrePluginAddListener.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\FilesReminders\Listener; + +use OCA\DAV\Events\SabrePluginAddEvent; +use OCA\FilesReminders\Dav\PropFindPlugin; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use Psr\Container\ContainerInterface; + +/** @template-implements IEventListener<SabrePluginAddEvent> */ +class SabrePluginAddListener implements IEventListener { + public function __construct( + private ContainerInterface $container, + ) { + } + + public function handle(Event $event): void { + if (!($event instanceof SabrePluginAddEvent)) { + return; + } + + $server = $event->getServer(); + $plugin = $this->container->get(PropFindPlugin::class); + $server->addPlugin($plugin); + } +} diff --git a/apps/files_reminders/lib/Listener/UserDeletedListener.php b/apps/files_reminders/lib/Listener/UserDeletedListener.php index 4e54a6d9946..366a5e60420 100644 --- a/apps/files_reminders/lib/Listener/UserDeletedListener.php +++ b/apps/files_reminders/lib/Listener/UserDeletedListener.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Listener; @@ -31,6 +14,7 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\User\Events\UserDeletedEvent; +/** @template-implements IEventListener<UserDeletedEvent> */ class UserDeletedListener implements IEventListener { public function __construct( private ReminderService $reminderService, diff --git a/apps/files_reminders/lib/Migration/Version10000Date20230725162149.php b/apps/files_reminders/lib/Migration/Version10000Date20230725162149.php index 76999329d64..74614c6515e 100644 --- a/apps/files_reminders/lib/Migration/Version10000Date20230725162149.php +++ b/apps/files_reminders/lib/Migration/Version10000Date20230725162149.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Migration; diff --git a/apps/files_reminders/lib/Model/RichReminder.php b/apps/files_reminders/lib/Model/RichReminder.php index 10dc89799fe..4f221252717 100644 --- a/apps/files_reminders/lib/Model/RichReminder.php +++ b/apps/files_reminders/lib/Model/RichReminder.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Model; @@ -45,11 +28,10 @@ class RichReminder extends Reminder implements JsonSerializable { * @throws NodeNotFoundException */ public function getNode(): Node { - $nodes = $this->root->getUserFolder($this->getUserId())->getById($this->getFileId()); - if (empty($nodes)) { + $node = $this->root->getUserFolder($this->getUserId())->getFirstNodeById($this->getFileId()); + if (!$node) { throw new NodeNotFoundException(); } - $node = reset($nodes); return $node; } diff --git a/apps/files_reminders/lib/Notification/Notifier.php b/apps/files_reminders/lib/Notification/Notifier.php index f7ffa0b4437..337ef04c814 100644 --- a/apps/files_reminders/lib/Notification/Notifier.php +++ b/apps/files_reminders/lib/Notification/Notifier.php @@ -3,30 +3,12 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Notification; -use InvalidArgumentException; use OCA\FilesReminders\AppInfo\Application; use OCP\Files\FileInfo; use OCP\Files\IRootFolder; @@ -36,6 +18,7 @@ use OCP\Notification\AlreadyProcessedException; use OCP\Notification\IAction; use OCP\Notification\INotification; use OCP\Notification\INotifier; +use OCP\Notification\UnknownNotificationException; class Notifier implements INotifier { public function __construct( @@ -54,14 +37,13 @@ class Notifier implements INotifier { } /** - * @throws InvalidArgumentException - * @throws AlreadyProcessedException + * @throws UnknownNotificationException */ public function prepare(INotification $notification, string $languageCode): INotification { $l = $this->l10nFactory->get(Application::APP_ID, $languageCode); if ($notification->getApp() !== Application::APP_ID) { - throw new InvalidArgumentException(); + throw new UnknownNotificationException(); } switch ($notification->getSubject()) { @@ -69,11 +51,10 @@ class Notifier implements INotifier { $params = $notification->getSubjectParameters(); $fileId = $params['fileId']; - $nodes = $this->root->getUserFolder($notification->getUser())->getById($fileId); - if (empty($nodes)) { - throw new InvalidArgumentException(); + $node = $this->root->getUserFolder($notification->getUser())->getFirstNodeById($fileId); + if ($node === null) { + throw new AlreadyProcessedException(); } - $node = reset($nodes); $path = rtrim($node->getPath(), '/'); if (strpos($path, '/' . $notification->getUser() . '/files/') === 0) { @@ -95,7 +76,7 @@ class Notifier implements INotifier { [ 'name' => [ 'type' => 'highlight', - 'id' => $node->getId(), + 'id' => (string)$node->getId(), 'name' => $node->getName(), ], ], @@ -110,8 +91,7 @@ class Notifier implements INotifier { $this->addActionButton($notification, $label); break; default: - throw new InvalidArgumentException(); - break; + throw new UnknownNotificationException(); } return $notification; diff --git a/apps/files_reminders/lib/Service/ReminderService.php b/apps/files_reminders/lib/Service/ReminderService.php index fabca65c45a..6ee39562076 100644 --- a/apps/files_reminders/lib/Service/ReminderService.php +++ b/apps/files_reminders/lib/Service/ReminderService.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2023 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FilesReminders\Service; @@ -32,11 +15,15 @@ use OCA\FilesReminders\AppInfo\Application; use OCA\FilesReminders\Db\Reminder; use OCA\FilesReminders\Db\ReminderMapper; use OCA\FilesReminders\Exception\NodeNotFoundException; +use OCA\FilesReminders\Exception\ReminderNotFoundException; use OCA\FilesReminders\Exception\UserNotFoundException; use OCA\FilesReminders\Model\RichReminder; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; +use OCP\ICache; +use OCP\ICacheFactory; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; @@ -45,6 +32,9 @@ use Psr\Log\LoggerInterface; use Throwable; class ReminderService { + + private ICache $cache; + public function __construct( protected IUserManager $userManager, protected IURLGenerator $urlGenerator, @@ -52,23 +42,49 @@ class ReminderService { protected ReminderMapper $reminderMapper, protected IRootFolder $root, protected LoggerInterface $logger, + protected ICacheFactory $cacheFactory, ) { + $this->cache = $this->cacheFactory->createInMemory(); } - /** - * @throws DoesNotExistException - */ - public function get(int $id): RichReminder { - $reminder = $this->reminderMapper->find($id); - return new RichReminder($reminder, $this->root); + public function cacheFolder(IUser $user, Folder $folder): void { + $reminders = $this->reminderMapper->findAllInFolder($user, $folder); + $reminderMap = []; + foreach ($reminders as $reminder) { + $reminderMap[$reminder->getFileId()] = $reminder; + } + + $nodes = $folder->getDirectoryListing(); + foreach ($nodes as $node) { + $reminder = $reminderMap[$node->getId()] ?? false; + $this->cache->set("{$user->getUID()}-{$node->getId()}", $reminder); + } } /** - * @throws DoesNotExistException + * @throws NodeNotFoundException */ - public function getDueForUser(IUser $user, int $fileId): RichReminder { - $reminder = $this->reminderMapper->findDueForUser($user, $fileId); - return new RichReminder($reminder, $this->root); + public function getDueForUser(IUser $user, int $fileId, bool $checkNode = true): ?RichReminder { + if ($checkNode) { + $this->checkNode($user, $fileId); + } + /** @var null|false|Reminder $cachedReminder */ + $cachedReminder = $this->cache->get("{$user->getUID()}-$fileId"); + if ($cachedReminder === false) { + return null; + } + if ($cachedReminder instanceof Reminder) { + return new RichReminder($cachedReminder, $this->root); + } + + try { + $reminder = $this->reminderMapper->findDueForUser($user, $fileId); + $this->cache->set("{$user->getUID()}-$fileId", $reminder); + return new RichReminder($reminder, $this->root); + } catch (DoesNotExistException $e) { + $this->cache->set("{$user->getUID()}-$fileId", false); + return null; + } } /** @@ -91,18 +107,9 @@ class ReminderService { */ public function createOrUpdate(IUser $user, int $fileId, DateTime $dueDate): bool { $now = new DateTime('now', new DateTimeZone('UTC')); - try { - $reminder = $this->reminderMapper->findDueForUser($user, $fileId); - $reminder->setDueDate($dueDate); - $reminder->setUpdatedAt($now); - $this->reminderMapper->update($reminder); - return false; - } catch (DoesNotExistException $e) { - $nodes = $this->root->getUserFolder($user->getUID())->getById($fileId); - if (empty($nodes)) { - throw new NodeNotFoundException(); - } - // Create new reminder if no reminder is found + $this->checkNode($user, $fileId); + $reminder = $this->getDueForUser($user, $fileId); + if ($reminder === null) { $reminder = new Reminder(); $reminder->setUserId($user->getUID()); $reminder->setFileId($fileId); @@ -110,29 +117,40 @@ class ReminderService { $reminder->setUpdatedAt($now); $reminder->setCreatedAt($now); $this->reminderMapper->insert($reminder); + $this->cache->set("{$user->getUID()}-$fileId", $reminder); return true; } + $reminder->setDueDate($dueDate); + $reminder->setUpdatedAt($now); + $this->reminderMapper->update($reminder); + $this->cache->set("{$user->getUID()}-$fileId", $reminder); + return false; } /** - * @throws DoesNotExistException + * @throws NodeNotFoundException + * @throws ReminderNotFoundException */ public function remove(IUser $user, int $fileId): void { - $reminder = $this->reminderMapper->findDueForUser($user, $fileId); - $this->reminderMapper->delete($reminder); + $this->checkNode($user, $fileId); + $reminder = $this->getDueForUser($user, $fileId); + if ($reminder === null) { + throw new ReminderNotFoundException(); + } + $this->deleteReminder($reminder); } public function removeAllForNode(Node $node): void { $reminders = $this->reminderMapper->findAllForNode($node); foreach ($reminders as $reminder) { - $this->reminderMapper->delete($reminder); + $this->deleteReminder($reminder); } } public function removeAllForUser(IUser $user): void { $reminders = $this->reminderMapper->findAllForUser($user); foreach ($reminders as $reminder) { - $this->reminderMapper->delete($reminder); + $this->deleteReminder($reminder); } } @@ -164,6 +182,7 @@ class ReminderService { try { $this->notificationManager->notify($notification); $this->reminderMapper->markNotified($reminder); + $this->cache->set("{$user->getUID()}-{$reminder->getFileId()}", $reminder); } catch (Throwable $th) { $this->logger->error($th->getMessage(), $th->getTrace()); } @@ -175,7 +194,24 @@ class ReminderService { ->modify('-1 day'); $reminders = $this->reminderMapper->findNotified($buffer, $limit); foreach ($reminders as $reminder) { - $this->reminderMapper->delete($reminder); + $this->deleteReminder($reminder); + } + } + + private function deleteReminder(Reminder $reminder): void { + $this->reminderMapper->delete($reminder); + $this->cache->set("{$reminder->getUserId()}-{$reminder->getFileId()}", false); + } + + + /** + * @throws NodeNotFoundException + */ + private function checkNode(IUser $user, int $fileId): void { + $userFolder = $this->root->getUserFolder($user->getUID()); + $node = $userFolder->getFirstNodeById($fileId); + if ($node === null) { + throw new NodeNotFoundException(); } } } diff --git a/apps/files_reminders/lib/SetupChecks/NeedNotificationsApp.php b/apps/files_reminders/lib/SetupChecks/NeedNotificationsApp.php new file mode 100644 index 00000000000..e5890567181 --- /dev/null +++ b/apps/files_reminders/lib/SetupChecks/NeedNotificationsApp.php @@ -0,0 +1,39 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\FilesReminders\SetupChecks; + +use OCP\App\IAppManager; +use OCP\IL10N; +use OCP\SetupCheck\ISetupCheck; +use OCP\SetupCheck\SetupResult; + +class NeedNotificationsApp implements ISetupCheck { + public function __construct( + private IAppManager $appManager, + private IL10N $l10n, + ) { + } + + public function getName(): string { + return $this->l10n->t('Files reminder'); + } + + public function getCategory(): string { + return 'system'; + } + + public function run(): SetupResult { + if ($this->appManager->isEnabledForAnyone('notifications')) { + return SetupResult::success($this->l10n->t('The "files_reminders" app can work properly.')); + } else { + return SetupResult::warning($this->l10n->t('The "files_reminders" app needs the notification app to work properly. You should either enable notifications or disable files_reminder.')); + } + } +} |