summaryrefslogtreecommitdiffstats
path: root/apps/files_reminders/lib
diff options
context:
space:
mode:
authorChristopher Ng <chrng8@gmail.com>2023-07-31 12:10:50 -0700
committerChristopher Ng <chrng8@gmail.com>2023-08-08 16:29:11 -0700
commit782a462cadd19fbb2008649c07b1c8a16556e74a (patch)
tree21c815d5818f91d151c897779168d936e66a0d98 /apps/files_reminders/lib
parentb195fe73139d5b5dc5de55265b41d85bb0ae3712 (diff)
downloadnextcloud-server-782a462cadd19fbb2008649c07b1c8a16556e74a.tar.gz
nextcloud-server-782a462cadd19fbb2008649c07b1c8a16556e74a.zip
feat(files_reminders): add service and notifier
Signed-off-by: Christopher Ng <chrng8@gmail.com> (cherry picked from commit ea5e128fefba6d87c400cdaafa87cd566746c109)
Diffstat (limited to 'apps/files_reminders/lib')
-rw-r--r--apps/files_reminders/lib/Db/ReminderMapper.php18
-rw-r--r--apps/files_reminders/lib/Exception/NodeNotFoundException.php32
-rw-r--r--apps/files_reminders/lib/Exception/UserNotFoundException.php32
-rw-r--r--apps/files_reminders/lib/Notification/Notifier.php142
-rw-r--r--apps/files_reminders/lib/Service/ReminderService.php87
5 files changed, 310 insertions, 1 deletions
diff --git a/apps/files_reminders/lib/Db/ReminderMapper.php b/apps/files_reminders/lib/Db/ReminderMapper.php
index 974416245fa..f383ee66511 100644
--- a/apps/files_reminders/lib/Db/ReminderMapper.php
+++ b/apps/files_reminders/lib/Db/ReminderMapper.php
@@ -60,7 +60,23 @@ class ReminderMapper extends QBMapper {
$qb->select('user_id', 'file_id', 'remind_at')
->from($this->getTableName())
->where($qb->expr()->lt('remind_at', $qb->createFunction('NOW()')))
- ->andWhere($qb->expr()->eq('notified', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)));
+ ->andWhere($qb->expr()->eq('notified', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
+ ->orderBy('remind_at', 'ASC');
+
+ return $this->findEntities($qb);
+ }
+
+ /**
+ * @return Reminder[]
+ */
+ public function findToDelete(?int $limit = null) {
+ $qb = $this->db->getQueryBuilder();
+
+ $qb->select('user_id', 'file_id', 'remind_at')
+ ->from($this->getTableName())
+ ->where($qb->expr()->eq('notified', $qb->createNamedParameter(true, IQueryBuilder::PARAM_BOOL)))
+ ->orderBy('remind_at', 'ASC')
+ ->setMaxResults($limit);
return $this->findEntities($qb);
}
diff --git a/apps/files_reminders/lib/Exception/NodeNotFoundException.php b/apps/files_reminders/lib/Exception/NodeNotFoundException.php
new file mode 100644
index 00000000000..5dfe784f5fc
--- /dev/null
+++ b/apps/files_reminders/lib/Exception/NodeNotFoundException.php
@@ -0,0 +1,32 @@
+<?php
+
+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/>.
+ *
+ */
+
+namespace OCA\FilesReminders\Exception;
+
+use Exception;
+
+class NodeNotFoundException extends Exception {
+}
diff --git a/apps/files_reminders/lib/Exception/UserNotFoundException.php b/apps/files_reminders/lib/Exception/UserNotFoundException.php
new file mode 100644
index 00000000000..3c57ea475e9
--- /dev/null
+++ b/apps/files_reminders/lib/Exception/UserNotFoundException.php
@@ -0,0 +1,32 @@
+<?php
+
+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/>.
+ *
+ */
+
+namespace OCA\FilesReminders\Exception;
+
+use Exception;
+
+class UserNotFoundException extends Exception {
+}
diff --git a/apps/files_reminders/lib/Notification/Notifier.php b/apps/files_reminders/lib/Notification/Notifier.php
new file mode 100644
index 00000000000..b3c1a98484a
--- /dev/null
+++ b/apps/files_reminders/lib/Notification/Notifier.php
@@ -0,0 +1,142 @@
+<?php
+
+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/>.
+ *
+ */
+
+namespace OCA\FilesReminders\Notification;
+
+use InvalidArgumentException;
+use OCA\FilesReminders\AppInfo\Application;
+use OCA\FilesReminders\Exception\NodeNotFoundException;
+use OCP\Files\FileInfo;
+use OCP\Files\IRootFolder;
+use OCP\Files\Node;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory;
+use OCP\Notification\IAction;
+use OCP\Notification\INotification;
+use OCP\Notification\INotifier;
+
+class Notifier implements INotifier {
+ public function __construct(
+ protected IFactory $l10nFactory,
+ protected IURLGenerator $urlGenerator,
+ protected IRootFolder $root,
+ ) {}
+
+ public function getID(): string {
+ return Application::APP_ID;
+ }
+
+ public function getName(): string {
+ return $this->l10nFactory->get(Application::APP_ID)->t('File reminders');
+ }
+
+ /**
+ * @throws InvalidArgumentException
+ * @throws NodeNotFoundException
+ */
+ 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();
+ }
+
+ switch ($notification->getSubject()) {
+ case 'reminder-due':
+ $fileId = $notification->getSubjectParameters()['fileId'];
+ $node = $this->getNode($fileId);
+
+ $path = rtrim($node->getPath(), '/');
+ if (strpos($path, '/' . $notification->getUser() . '/files/') === 0) {
+ // Remove /user/files/...
+ $fullPath = $path;
+ [,,, $path] = explode('/', $fullPath, 4);
+ }
+
+ $link = $this->urlGenerator->linkToRouteAbsolute(
+ 'files.viewcontroller.showFile',
+ ['fileid' => $node->getId()],
+ );
+
+ $subject = $l->t('Reminder for {filename}');
+ $notification
+ ->setRichSubject(
+ $subject,
+ [
+ 'filename' => [
+ 'type' => 'file',
+ 'id' => $node->getId(),
+ 'name' => $node->getName(),
+ 'path' => $path,
+ 'link' => $link,
+ ],
+ ],
+ )
+ ->setParsedSubject(str_replace(
+ ['{filename}'],
+ [$node->getName()],
+ $subject,
+ ))
+ ->setLink($link);
+
+ $label = match ($node->getType()) {
+ FileInfo::TYPE_FILE => $l->t('View file'),
+ FileInfo::TYPE_FOLDER => $l->t('View folder'),
+ };
+
+ $this->addActionButton($notification, $label);
+ break;
+ default:
+ throw new InvalidArgumentException();
+ break;
+ }
+
+ return $notification;
+ }
+
+ protected function addActionButton(INotification $notification, string $label): void {
+ $action = $notification->createAction();
+
+ $action->setLabel($label)
+ ->setParsedLabel($label)
+ ->setLink($notification->getLink(), IAction::TYPE_WEB)
+ ->setPrimary(true);
+
+ $notification->addParsedAction($action);
+ }
+
+ /**
+ * @throws NodeNotFoundException
+ */
+ protected function getNode(int $fileId): Node {
+ $nodes = $this->root->getById($fileId);
+ if (empty($nodes)) {
+ throw new NodeNotFoundException();
+ }
+ $node = reset($nodes);
+ return $node;
+ }
+}
diff --git a/apps/files_reminders/lib/Service/ReminderService.php b/apps/files_reminders/lib/Service/ReminderService.php
new file mode 100644
index 00000000000..69cef367856
--- /dev/null
+++ b/apps/files_reminders/lib/Service/ReminderService.php
@@ -0,0 +1,87 @@
+<?php
+
+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/>.
+ *
+ */
+
+namespace OCA\FilesReminders\Service;
+
+use DateTime;
+use InvalidArgumentException;
+use OCA\FilesReminders\AppInfo\Application;
+use OCA\FilesReminders\Db\Reminder;
+use OCA\FilesReminders\Db\ReminderMapper;
+use OCA\FilesReminders\Exception\UserNotFoundException;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\IURLGenerator;
+use OCP\IUserManager;
+use OCP\Notification\IManager as INotificationManager;
+use Psr\Log\LoggerInterface;
+
+class ReminderService {
+ public function __construct(
+ protected IUserManager $userManager,
+ protected IURLGenerator $urlGenerator,
+ protected INotificationManager $notificationManager,
+ protected ReminderMapper $reminderMapper,
+ protected LoggerInterface $logger,
+ ) {}
+
+ /**
+ * @throws DoesNotExistException
+ * @throws UserNotFoundException
+ */
+ public function send(Reminder $reminder): void {
+ if ($reminder->getNotified()) {
+ return;
+ }
+
+ $user = $this->userManager->get($reminder->getUserId());
+ if ($user === null) {
+ throw new UserNotFoundException();
+ }
+
+ $notification = $this->notificationManager->createNotification();
+ $notification
+ ->setApp(Application::APP_ID)
+ ->setIcon($this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('files', 'folder.svg')))
+ ->setUser($user->getUID())
+ ->setObject('reminder', (string)$reminder->getId())
+ ->setSubject('reminder-due', ['fileId' => $reminder->getFileId()])
+ ->setDateTime(DateTime::createFromFormat('U', (string)$reminder->getRemindAt()));
+
+ try {
+ $this->notificationManager->notify($notification);
+ $this->reminderMapper->markNotified($reminder);
+ } catch (InvalidArgumentException $e) {
+ $this->logger->error('Failed to send reminder notification', $e->getTrace());
+ }
+ }
+
+ public function cleanUp(?int $limit = null): void {
+ $reminders = $this->reminderMapper->findToDelete($limit);
+ foreach ($reminders as $reminder) {
+ $this->reminderMapper->delete($reminder);
+ }
+ }
+}