diff --git a/application/Espo/Core/Notification/UserEnabledChecker.php b/application/Espo/Core/Notification/UserEnabledChecker.php index 3752f7994f..834142bad0 100644 --- a/application/Espo/Core/Notification/UserEnabledChecker.php +++ b/application/Espo/Core/Notification/UserEnabledChecker.php @@ -30,21 +30,29 @@ namespace Espo\Core\Notification; use Espo\Core\ORM\EntityManager; +use Espo\Core\Utils\Config; +use Espo\Entities\Preferences; class UserEnabledChecker { /** @var array */ private $assignmentCache = []; - public function __construct(private EntityManager $entityManager) - {} + public function __construct( + private EntityManager $entityManager, + private Config $config, + ) {} public function checkAssignment(string $entityType, string $userId): bool { + if (!in_array($entityType, $this->config->get('assignmentNotificationsEntityList', []))) { + return false; + } + $key = $entityType . '_' . $userId; if (!array_key_exists($key, $this->assignmentCache)) { - $preferences = $this->entityManager->getEntity('Preferences', $userId); + $preferences = $this->entityManager->getEntityById(Preferences::ENTITY_TYPE, $userId); $isEnabled = false; diff --git a/application/Espo/Entities/Notification.php b/application/Espo/Entities/Notification.php index ae37d94e1c..e1a42b5e76 100644 --- a/application/Espo/Entities/Notification.php +++ b/application/Espo/Entities/Notification.php @@ -70,7 +70,10 @@ class Notification extends Entity return $this->get('data'); } - public function setData(stdClass $data): self + /** + * @param stdClass|array $data + */ + public function setData(stdClass|array $data): self { $this->set('data', $data); diff --git a/application/Espo/Resources/i18n/en_US/Global.json b/application/Espo/Resources/i18n/en_US/Global.json index c7db875f72..1cdf74215d 100644 --- a/application/Espo/Resources/i18n/en_US/Global.json +++ b/application/Espo/Resources/i18n/en_US/Global.json @@ -509,7 +509,8 @@ "notificationMessages": { "assign": "{entityType} {entity} has been assigned to you", "emailReceived": "Email received from {from}", - "entityRemoved": "{user} removed {entityType} {entity}" + "entityRemoved": "{user} removed {entityType} {entity}", + "emailInbox": "{user} added email {entity} to your inbox" }, "streamMessages": { "post": "{user} posted on {entityType} {entity}", diff --git a/application/Espo/Resources/metadata/clientDefs/Notification.json b/application/Espo/Resources/metadata/clientDefs/Notification.json index 4f4995904d..144fee03a4 100644 --- a/application/Espo/Resources/metadata/clientDefs/Notification.json +++ b/application/Espo/Resources/metadata/clientDefs/Notification.json @@ -4,6 +4,7 @@ "aclPortal": "acl-portal/notification", "collection": "collections/note", "itemViews": { - "System": "views/notification/items/system" + "System": "views/notification/items/system", + "EmailInbox": "views/notification/items/email-inbox" } } diff --git a/application/Espo/Tools/Email/Api/PostUsers.php b/application/Espo/Tools/Email/Api/PostUsers.php index 8d5b90f11e..fdf62c1de4 100644 --- a/application/Espo/Tools/Email/Api/PostUsers.php +++ b/application/Espo/Tools/Email/Api/PostUsers.php @@ -37,8 +37,11 @@ use Espo\Core\Api\ResponseComposer; use Espo\Core\Exceptions\BadRequest; use Espo\Core\Exceptions\Forbidden; use Espo\Core\Exceptions\NotFound; +use Espo\Core\Field\LinkParent; +use Espo\Core\Notification\UserEnabledChecker; use Espo\Core\Record\EntityProvider; use Espo\Entities\Email; +use Espo\Entities\Notification; use Espo\Entities\User; use Espo\ORM\EntityManager; use RuntimeException; @@ -52,6 +55,8 @@ class PostUsers implements Action private EntityProvider $entityProvider, private Acl $acl, private EntityManager $entityManager, + private UserEnabledChecker $userEnabledChecker, + private User $user, ) {} public function process(Request $request): Response @@ -87,6 +92,8 @@ class PostUsers implements Action } $relation->relate($user); + + $this->processNotify($email, $user); } return ResponseComposer::json(true); @@ -137,4 +144,25 @@ class PostUsers implements Action return $users; } + + private function processNotify(Email $email, User $user): void + { + if (!$this->userEnabledChecker->checkAssignment(Email::ENTITY_TYPE, $user->getId())) { + return; + } + + $notification = $this->entityManager->getRDBRepositoryByClass(Notification::class)->getNew(); + + $notification + ->setType('EmailInbox') + ->setRelated(LinkParent::createFromEntity($email)) + ->setUserId($user->getId()) + ->setData([ + 'emailName' => $email->getSubject(), + 'userId' => $this->user->getId(), + 'userName' => $this->user->getName(), + ]); + + $this->entityManager->saveEntity($notification); + } } diff --git a/application/Espo/Tools/Notification/HookProcessor.php b/application/Espo/Tools/Notification/HookProcessor.php index e696f91448..65c67c9463 100644 --- a/application/Espo/Tools/Notification/HookProcessor.php +++ b/application/Espo/Tools/Notification/HookProcessor.php @@ -96,6 +96,7 @@ class HookProcessor $notificator = $this->getNotificator($entityType); if (!$notificator instanceof AssignmentNotificator) { + // @todo Remove in v9.0. // For backward compatibility. $notificator->process($entity, $options); diff --git a/client/src/views/notification/items/email-inbox.js b/client/src/views/notification/items/email-inbox.js new file mode 100644 index 0000000000..673630cabb --- /dev/null +++ b/client/src/views/notification/items/email-inbox.js @@ -0,0 +1,77 @@ +/************************************************************************ + * This file is part of EspoCRM. + * + * EspoCRM – Open Source CRM application. + * Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko + * Website: https://www.espocrm.com + * + * 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 . + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU Affero General Public License version 3. + * + * In accordance with Section 7(b) of the GNU Affero General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +import BaseNotificationItemView from 'views/notification/items/base'; + +class EmailInboxNotificationItemView extends BaseNotificationItemView { + + template = 'notification/items/system' + + messageName = 'emailInbox' + + // language=Handlebars + templateContent = ` +
+
{{{avatar}}}
+
+ {{{message}}} +
+
+
+ {{{createdAt}}} +
+ ` + + setup() { + /** @type {{userId: string, userName: string, emailName: string}} */ + const data = this.model.attributes.data || {}; + + this.userId = data.userId; + + this.messageData['entityType'] = this.translateEntityType('Email'); + + const entity = document.createElement('a'); + entity.href = `#Email/view/${this.model.attributes.relatedId}`; + entity.dataset.id = this.model.attributes.relatedId; + entity.dataset.scope = 'Email'; + entity.innerText = data.emailName; + + const user = document.createElement('a'); + user.href = `#User/view/${data.userId}`; + user.dataset.id = data.userId; + user.dataset.scope = 'User'; + user.innerText = data.userName; + + this.messageData['entity'] = entity; + this.messageData['user'] = user; + + this.createMessage(); + } +} + +export default EmailInboxNotificationItemView;