1<?php 2 3declare(strict_types=1); 4 5/** 6 * @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net> 7 * 8 * @author Julius Härtl <jus@bitgrid.net> 9 * 10 * @license GNU AGPL version 3 or any later version 11 * 12 * This program is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU Affero General Public License as 14 * published by the Free Software Foundation, either version 3 of the 15 * License, or (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU Affero General Public License for more details. 21 * 22 * You should have received a copy of the GNU Affero General Public License 23 * along with this program. If not, see <http://www.gnu.org/licenses/>. 24 * 25 */ 26 27namespace OCA\Deck\Notification; 28 29use DateTime; 30use Exception; 31use OCA\Deck\AppInfo\Application; 32use OCA\Deck\Db\Acl; 33use OCA\Deck\Db\AssignmentMapper; 34use OCA\Deck\Db\Board; 35use OCA\Deck\Db\BoardMapper; 36use OCA\Deck\Db\Card; 37use OCA\Deck\Db\CardMapper; 38use OCA\Deck\Db\User; 39use OCA\Deck\Service\ConfigService; 40use OCA\Deck\Service\PermissionService; 41use OCP\AppFramework\Db\DoesNotExistException; 42use OCP\AppFramework\Db\MultipleObjectsReturnedException; 43use OCP\Comments\IComment; 44use OCP\IConfig; 45use OCP\IGroupManager; 46use OCP\Notification\IManager; 47use OCP\Notification\INotification; 48 49class NotificationHelper { 50 51 /** @var CardMapper */ 52 protected $cardMapper; 53 /** @var BoardMapper */ 54 protected $boardMapper; 55 /** @var AssignmentMapper */ 56 protected $assignmentMapper; 57 /** @var PermissionService */ 58 protected $permissionService; 59 /** @var IConfig */ 60 protected $config; 61 /** @var IManager */ 62 protected $notificationManager; 63 /** @var IGroupManager */ 64 protected $groupManager; 65 /** @var string */ 66 protected $currentUser; 67 /** @var array */ 68 private $boards = []; 69 70 public function __construct( 71 CardMapper $cardMapper, 72 BoardMapper $boardMapper, 73 AssignmentMapper $assignmentMapper, 74 PermissionService $permissionService, 75 IConfig $config, 76 IManager $notificationManager, 77 IGroupManager $groupManager, 78 $userId 79 ) { 80 $this->cardMapper = $cardMapper; 81 $this->boardMapper = $boardMapper; 82 $this->assignmentMapper = $assignmentMapper; 83 $this->permissionService = $permissionService; 84 $this->config = $config; 85 $this->notificationManager = $notificationManager; 86 $this->groupManager = $groupManager; 87 $this->currentUser = $userId; 88 } 89 90 /** 91 * @throws DoesNotExistException 92 * @throws Exception thrown on invalid due date 93 */ 94 public function sendCardDuedate(Card $card): void { 95 // check if notification has already been sent 96 // ideally notifications should not be deleted once seen by the user so we can 97 // also deliver due date notifications for users who have been added later to a board 98 // this should maybe be addressed in nextcloud/server 99 if ($card->getNotified()) { 100 return; 101 } 102 103 $boardId = $this->cardMapper->findBoardId($card->getId()); 104 $board = $this->getBoard($boardId, false, true); 105 /** @var User $user */ 106 foreach ($this->permissionService->findUsers($boardId) as $user) { 107 $notificationSetting = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'board:' . $boardId . ':notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_DEFAULT); 108 109 if ($notificationSetting === ConfigService::SETTING_BOARD_NOTIFICATION_DUE_OFF) { 110 continue; 111 } 112 113 $shouldNotify = $notificationSetting === ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ALL; 114 115 if ($user->getUID() === $board->getOwner() && count($board->getAcl()) === 0) { 116 // Notify if all or assigned is configured for unshared boards 117 $shouldNotify = true; 118 } elseif ($notificationSetting === ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED && $this->assignmentMapper->isUserAssigned($card->getId(), $user->getUID())) { 119 // Notify if the user is assigned and has the assigned setting selected 120 $shouldNotify = true; 121 } 122 123 if ($shouldNotify) { 124 $notification = $this->notificationManager->createNotification(); 125 $notification 126 ->setApp('deck') 127 ->setUser((string)$user->getUID()) 128 ->setObject('card', (string)$card->getId()) 129 ->setSubject('card-overdue', [ 130 $card->getTitle(), $board->getTitle() 131 ]) 132 ->setDateTime(new DateTime($card->getDuedate())); 133 $this->notificationManager->notify($notification); 134 } 135 } 136 $this->cardMapper->markNotified($card); 137 } 138 139 public function markDuedateAsRead(Card $card): void { 140 $notification = $this->notificationManager->createNotification(); 141 $notification 142 ->setApp('deck') 143 ->setObject('card', (string)$card->getId()) 144 ->setSubject('card-overdue', []); 145 $this->notificationManager->markProcessed($notification); 146 } 147 148 public function sendCardAssigned(Card $card, string $userId): void { 149 $boardId = $this->cardMapper->findBoardId($card->getId()); 150 try { 151 $board = $this->getBoard($boardId); 152 } catch (Exception $e) { 153 return; 154 } 155 156 $notification = $this->notificationManager->createNotification(); 157 $notification 158 ->setApp('deck') 159 ->setUser($userId) 160 ->setDateTime(new DateTime()) 161 ->setObject('card', (string)$card->getId()) 162 ->setSubject('card-assigned', [ 163 $card->getTitle(), 164 $board->getTitle(), 165 $this->currentUser 166 ]); 167 $this->notificationManager->notify($notification); 168 } 169 170 public function markCardAssignedAsRead(Card $card, string $userId): void { 171 $notification = $this->notificationManager->createNotification(); 172 $notification 173 ->setApp('deck') 174 ->setUser($userId) 175 ->setObject('card', (string)$card->getId()) 176 ->setSubject('card-assigned', []); 177 $this->notificationManager->markProcessed($notification); 178 } 179 180 /** 181 * Send notifications that a board was shared with a user/group 182 */ 183 public function sendBoardShared(int $boardId, Acl $acl, bool $markAsRead = false): void { 184 try { 185 $board = $this->getBoard($boardId); 186 } catch (Exception $e) { 187 return; 188 } 189 190 if ($acl->getType() === Acl::PERMISSION_TYPE_USER) { 191 $notification = $this->generateBoardShared($board, $acl->getParticipant()); 192 if ($markAsRead) { 193 $this->notificationManager->markProcessed($notification); 194 } else { 195 $notification->setDateTime(new DateTime()); 196 $this->notificationManager->notify($notification); 197 } 198 } 199 if ($acl->getType() === Acl::PERMISSION_TYPE_GROUP) { 200 $group = $this->groupManager->get($acl->getParticipant()); 201 if ($group === null) { 202 return; 203 } 204 foreach ($group->getUsers() as $user) { 205 if ($user->getUID() === $this->currentUser) { 206 continue; 207 } 208 $notification = $this->generateBoardShared($board, $user->getUID()); 209 if ($markAsRead) { 210 $this->notificationManager->markProcessed($notification); 211 } else { 212 $notification->setDateTime(new DateTime()); 213 $this->notificationManager->notify($notification); 214 } 215 } 216 } 217 } 218 219 public function sendMention(IComment $comment): void { 220 foreach ($comment->getMentions() as $mention) { 221 $card = $this->cardMapper->find($comment->getObjectId()); 222 $boardId = $this->cardMapper->findBoardId($card->getId()); 223 $notification = $this->notificationManager->createNotification(); 224 $notification 225 ->setApp('deck') 226 ->setUser((string) $mention['id']) 227 ->setDateTime(new DateTime()) 228 ->setObject('card', (string) $card->getId()) 229 ->setSubject('card-comment-mentioned', [$card->getTitle(), $boardId, $this->currentUser]) 230 ->setMessage('{message}', ['message' => $comment->getMessage()]); 231 $this->notificationManager->notify($notification); 232 } 233 } 234 235 /** 236 * @throws DoesNotExistException 237 * @throws MultipleObjectsReturnedException 238 */ 239 private function getBoard(int $boardId, bool $withLabels = false, bool $withAcl = false): Board { 240 if (!array_key_exists($boardId, $this->boards)) { 241 $this->boards[$boardId] = $this->boardMapper->find($boardId, $withLabels, $withAcl); 242 } 243 return $this->boards[$boardId]; 244 } 245 246 private function generateBoardShared(Board $board, string $userId): INotification { 247 $notification = $this->notificationManager->createNotification(); 248 $notification 249 ->setApp('deck') 250 ->setUser($userId) 251 ->setObject('board', (string)$board->getId()) 252 ->setSubject('board-shared', [$board->getTitle(), $this->currentUser]); 253 return $notification; 254 } 255} 256