From c8a32a70cd7564e739b7c60196f804d50a954b8a Mon Sep 17 00:00:00 2001 From: Christopher Ng Date: Mon, 31 Jul 2023 12:10:50 -0700 Subject: [PATCH] feat(files_reminders): add list command Signed-off-by: Christopher Ng --- .../lib/Command/ListCommand.php | 92 +++++++++++++++++++ apps/files_reminders/lib/Db/Reminder.php | 8 +- .../files_reminders/lib/Db/ReminderMapper.php | 42 ++++++++- .../lib/Model/RichReminder.php | 62 +++++++++++++ .../lib/Notification/Notifier.php | 28 ++---- .../lib/Service/ReminderService.php | 24 ++++- 6 files changed, 229 insertions(+), 27 deletions(-) create mode 100644 apps/files_reminders/lib/Command/ListCommand.php create mode 100644 apps/files_reminders/lib/Model/RichReminder.php diff --git a/apps/files_reminders/lib/Command/ListCommand.php b/apps/files_reminders/lib/Command/ListCommand.php new file mode 100644 index 00000000000..35a778e87d1 --- /dev/null +++ b/apps/files_reminders/lib/Command/ListCommand.php @@ -0,0 +1,92 @@ + + * + * @author Christopher Ng + * + * @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 . + * + */ + +namespace OCA\FilesReminders\Command; + +use DateTime; +use DateTimeInterface; +use OC\Core\Command\Base; +use OCA\FilesReminders\Model\RichReminder; +use OCA\FilesReminders\Service\ReminderService; +use OCP\IUserManager; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class ListCommand extends Base { + public function __construct( + private ReminderService $reminderService, + private IUserManager $userManager, + ) { + parent::__construct(); + } + + protected function configure(): void { + $this + ->setName('files:reminders') + ->setDescription('List file reminders') + ->addArgument( + 'user', + InputArgument::OPTIONAL, + 'list reminders for user', + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $io = new SymfonyStyle($input, $output); + + $uid = $input->getArgument('user'); + if ($uid !== null) { + /** @var string $uid */ + $user = $this->userManager->get($uid); + if ($user === null) { + $io->error("Unknown user <$uid>"); + return 1; + } + } + + $reminders = $this->reminderService->getAll($user ?? null); + if (empty($reminders)) { + $io->text('No reminders'); + return 0; + } + + $io->table( + ['UserId', 'Path', 'RemindAt', 'Notified'], + array_map( + fn (RichReminder $reminder) => [ + $reminder->getUserId(), + $reminder->getNode()->getPath(), + DateTime::createFromFormat('U', (string)$reminder->getRemindAt())->format(DateTimeInterface::ATOM), // ISO 8601 + $reminder->getNotified() ? 'true' : 'false', + ], + $reminders, + ) + ); + return 0; + } +} diff --git a/apps/files_reminders/lib/Db/Reminder.php b/apps/files_reminders/lib/Db/Reminder.php index 98e2cc5838f..99cd9b59092 100644 --- a/apps/files_reminders/lib/Db/Reminder.php +++ b/apps/files_reminders/lib/Db/Reminder.php @@ -42,10 +42,10 @@ use OCP\AppFramework\Db\Entity; * @method bool getNotified() */ class Reminder extends Entity { - protected string $userId; - protected int $fileId; - protected int $remindAt; - protected bool $notified = false; + protected $userId; + protected $fileId; + protected $remindAt; + protected $notified = false; public function __construct() { $this->addType('userId', 'string'); diff --git a/apps/files_reminders/lib/Db/ReminderMapper.php b/apps/files_reminders/lib/Db/ReminderMapper.php index f383ee66511..6b2e3a1f2b8 100644 --- a/apps/files_reminders/lib/Db/ReminderMapper.php +++ b/apps/files_reminders/lib/Db/ReminderMapper.php @@ -29,6 +29,7 @@ namespace OCA\FilesReminders\Db; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +use OCP\IUser; /** * @template-extends QBMapper @@ -51,13 +52,50 @@ class ReminderMapper extends QBMapper { return parent::update($reminderUpdate); } + public function find(int $id): Reminder { + $qb = $this->db->getQueryBuilder(); + + $qb->select('user_id', 'file_id', 'remind_at', 'notified') + ->from($this->getTableName()) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))); + + return $this->findEntity($qb); + } + + /** + * @return Reminder[] + */ + public function findAll() { + $qb = $this->db->getQueryBuilder(); + + $qb->select('user_id', 'file_id', 'remind_at', 'notified') + ->from($this->getTableName()) + ->orderBy('remind_at', 'ASC'); + + return $this->findEntities($qb); + } + + /** + * @return Reminder[] + */ + public function findAllForUser(IUser $user) { + $qb = $this->db->getQueryBuilder(); + + $qb->select('user_id', 'file_id', 'remind_at', 'notified') + ->from($this->getTableName()) + ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID(), IQueryBuilder::PARAM_STR))) + ->orderBy('remind_at', 'ASC'); + + return $this->findEntities($qb); + } + /** * @return Reminder[] */ public function findToRemind() { $qb = $this->db->getQueryBuilder(); - $qb->select('user_id', 'file_id', 'remind_at') + $qb->select('user_id', 'file_id', 'remind_at', 'notified') ->from($this->getTableName()) ->where($qb->expr()->lt('remind_at', $qb->createFunction('NOW()'))) ->andWhere($qb->expr()->eq('notified', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL))) @@ -72,7 +110,7 @@ class ReminderMapper extends QBMapper { public function findToDelete(?int $limit = null) { $qb = $this->db->getQueryBuilder(); - $qb->select('user_id', 'file_id', 'remind_at') + $qb->select('user_id', 'file_id', 'remind_at', 'notified') ->from($this->getTableName()) ->where($qb->expr()->eq('notified', $qb->createNamedParameter(true, IQueryBuilder::PARAM_BOOL))) ->orderBy('remind_at', 'ASC') diff --git a/apps/files_reminders/lib/Model/RichReminder.php b/apps/files_reminders/lib/Model/RichReminder.php new file mode 100644 index 00000000000..21b42d9c2e7 --- /dev/null +++ b/apps/files_reminders/lib/Model/RichReminder.php @@ -0,0 +1,62 @@ + + * + * @author Christopher Ng + * + * @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 . + * + */ + +namespace OCA\FilesReminders\Model; + +use OCA\FilesReminders\Db\Reminder; +use OCA\FilesReminders\Exception\NodeNotFoundException; +use OCP\Files\IRootFolder; +use OCP\Files\Node; + +class RichReminder extends Reminder { + public function __construct( + private Reminder $reminder, + private IRootFolder $root, + ) { + parent::__construct(); + } + + /** + * @throws NodeNotFoundException + */ + public function getNode(): Node { + $userFolder = $this->root->getUserFolder($this->getUserId()); + $nodes = $userFolder->getById($this->getFileId()); + if (empty($nodes)) { + throw new NodeNotFoundException(); + } + $node = reset($nodes); + return $node; + } + + protected function getter(string $name): mixed { + return $this->reminder->getter($name); + } + + public function __call(string $methodName, array $args) { + return $this->reminder->__call($methodName, $args); + } +} diff --git a/apps/files_reminders/lib/Notification/Notifier.php b/apps/files_reminders/lib/Notification/Notifier.php index b3c1a98484a..fb1d3264dbd 100644 --- a/apps/files_reminders/lib/Notification/Notifier.php +++ b/apps/files_reminders/lib/Notification/Notifier.php @@ -29,9 +29,8 @@ namespace OCA\FilesReminders\Notification; use InvalidArgumentException; use OCA\FilesReminders\AppInfo\Application; use OCA\FilesReminders\Exception\NodeNotFoundException; +use OCA\FilesReminders\Service\ReminderService; use OCP\Files\FileInfo; -use OCP\Files\IRootFolder; -use OCP\Files\Node; use OCP\IURLGenerator; use OCP\L10N\IFactory; use OCP\Notification\IAction; @@ -42,7 +41,7 @@ class Notifier implements INotifier { public function __construct( protected IFactory $l10nFactory, protected IURLGenerator $urlGenerator, - protected IRootFolder $root, + protected ReminderService $reminderService, ) {} public function getID(): string { @@ -66,8 +65,8 @@ class Notifier implements INotifier { switch ($notification->getSubject()) { case 'reminder-due': - $fileId = $notification->getSubjectParameters()['fileId']; - $node = $this->getNode($fileId); + $reminderId = (int)$notification->getObjectId(); + $node = $this->reminderService->get($reminderId)->getNode(); $path = rtrim($node->getPath(), '/'); if (strpos($path, '/' . $notification->getUser() . '/files/') === 0) { @@ -81,12 +80,13 @@ class Notifier implements INotifier { ['fileid' => $node->getId()], ); - $subject = $l->t('Reminder for {filename}'); + // TRANSLATORS The name placeholder is for a file or folder name + $subject = $l->t('Reminder for {name}'); $notification ->setRichSubject( $subject, [ - 'filename' => [ + 'name' => [ 'type' => 'file', 'id' => $node->getId(), 'name' => $node->getName(), @@ -96,7 +96,7 @@ class Notifier implements INotifier { ], ) ->setParsedSubject(str_replace( - ['{filename}'], + ['{name}'], [$node->getName()], $subject, )) @@ -127,16 +127,4 @@ class Notifier implements INotifier { $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 index 69cef367856..e65cdbfb867 100644 --- a/apps/files_reminders/lib/Service/ReminderService.php +++ b/apps/files_reminders/lib/Service/ReminderService.php @@ -32,8 +32,11 @@ use OCA\FilesReminders\AppInfo\Application; use OCA\FilesReminders\Db\Reminder; use OCA\FilesReminders\Db\ReminderMapper; use OCA\FilesReminders\Exception\UserNotFoundException; +use OCA\FilesReminders\Model\RichReminder; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\Files\IRootFolder; use OCP\IURLGenerator; +use OCP\IUser; use OCP\IUserManager; use OCP\Notification\IManager as INotificationManager; use Psr\Log\LoggerInterface; @@ -44,9 +47,28 @@ class ReminderService { protected IURLGenerator $urlGenerator, protected INotificationManager $notificationManager, protected ReminderMapper $reminderMapper, + protected IRootFolder $root, protected LoggerInterface $logger, ) {} + public function get(int $id): RichReminder { + $reminder = $this->reminderMapper->find($id); + return new RichReminder($reminder, $this->root); + } + + /** + * @return RichReminder[] + */ + public function getAll(?IUser $user = null) { + $reminders = ($user !== null) + ? $this->reminderMapper->findAllForUser($user) + : $this->reminderMapper->findAll(); + return array_map( + fn (Reminder $reminder) => new RichReminder($reminder, $this->root), + $reminders, + ); + } + /** * @throws DoesNotExistException * @throws UserNotFoundException @@ -67,7 +89,7 @@ class ReminderService { ->setIcon($this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('files', 'folder.svg'))) ->setUser($user->getUID()) ->setObject('reminder', (string)$reminder->getId()) - ->setSubject('reminder-due', ['fileId' => $reminder->getFileId()]) + ->setSubject('reminder-due') ->setDateTime(DateTime::createFromFormat('U', (string)$reminder->getRemindAt())); try { -- 2.39.5