addDependencyList([ 'metadata', 'mailSender', 'language', 'dateTime', 'number', 'fileManager', 'selectManagerFactory', 'templateFileManager', 'injectableFactory', 'config' ]); } protected $emailNotificationEntityHandlerHash = []; protected function getMailSender() { return $this->getInjection('mailSender'); } protected function getMetadata() { return $this->getInjection('metadata'); } protected function getLanguage() { return $this->getInjection('language'); } protected function getDateTime() { return $this->getInjection('dateTime'); } protected function getConfig() { return $this->getInjection('config'); } protected function getTemplateFileManager() { return $this->getInjection('templateFileManager'); } protected function getHtmlizer() { if (empty($this->htmlizer)) { $this->htmlizer = new \Espo\Core\Htmlizer\Htmlizer($this->getInjection('fileManager'), $this->getInjection('dateTime'), $this->getInjection('number'), null); } return $this->htmlizer; } protected $userIdPortalCacheMap = []; public function notifyAboutAssignmentJob($data) { if (empty($data->userId)) return; if (empty($data->assignerUserId)) return; if (empty($data->entityId)) return; if (empty($data->entityType)) return; $userId = $data->userId; $assignerUserId = $data->assignerUserId; $entityId = $data->entityId; $entityType = $data->entityType; $user = $this->getEntityManager()->getEntity('User', $userId); if (!$user) return; if ($user->isPortal()) return; $preferences = $this->getEntityManager()->getEntity('Preferences', $userId); if (!$preferences) return; if (!$preferences->get('receiveAssignmentEmailNotifications')) return; $ignoreList = $preferences->get('assignmentEmailNotificationsIgnoreEntityTypeList') ?? []; if (in_array($entityType, $ignoreList)) return; $assignerUser = $this->getEntityManager()->getEntity('User', $assignerUserId); $entity = $this->getEntityManager()->getEntity($entityType, $entityId); if (!$entity) return true; if (!$assignerUser) return true; $this->loadParentNameFields($entity); if (!$entity->hasLinkMultipleField('assignedUsers')) { if ($entity->get('assignedUserId') !== $userId) return true; } $emailAddress = $user->get('emailAddress'); if (!empty($emailAddress)) { $email = $this->getEntityManager()->getEntity('Email'); $subjectTpl = $this->getTemplateFileManager()->getTemplate('assignment', 'subject', $entity->getEntityType()); $bodyTpl = $this->getTemplateFileManager()->getTemplate('assignment', 'body', $entity->getEntityType()); $subjectTpl = str_replace(["\n", "\r"], '', $subjectTpl); $recordUrl = rtrim($this->getConfig()->get('siteUrl'), '/') . '/#' . $entity->getEntityType() . '/view/' . $entity->id; $data = [ 'userName' => $user->get('name'), 'assignerUserName' => $assignerUser->get('name'), 'recordUrl' => $recordUrl, 'entityType' => $this->getLanguage()->translate($entity->getEntityType(), 'scopeNames') ]; $data['entityTypeLowerFirst'] = Util::mbLowerCaseFirst($data['entityType']); $subject = $this->getHtmlizer()->render($entity, $subjectTpl, 'assignment-email-subject-' . $entity->getEntityType(), $data, true); $body = $this->getHtmlizer()->render($entity, $bodyTpl, 'assignment-email-body-' . $entity->getEntityType(), $data, true); $email->set([ 'subject' => $subject, 'body' => $body, 'isHtml' => true, 'to' => $emailAddress, 'isSystem' => true, 'parentId' => $entity->id, 'parentType' => $entity->getEntityType() ]); try { $this->getMailSender()->send($email); } catch (\Exception $e) { $GLOBALS['log']->error('EmailNotification: [' . $e->getCode() . '] ' .$e->getMessage()); } } return true; } public function process() { $mentionEmailNotifications = $this->getConfig()->get('mentionEmailNotifications'); $streamEmailNotifications = $this->getConfig()->get('streamEmailNotifications'); $portalStreamEmailNotifications = $this->getConfig()->get('portalStreamEmailNotifications'); $typeList = []; if ($mentionEmailNotifications) { $typeList[] = 'MentionInPost'; } if ($streamEmailNotifications || $portalStreamEmailNotifications) { $typeList[] = 'Note'; } if (empty($typeList)) return; $fromDt = new \DateTime(); $fromDt->modify('-' . self::HOURS_THERSHOLD . ' hours'); $where = [ 'createdAt>' => $fromDt->format('Y-m-d H:i:s'), 'read' => false, 'emailIsProcessed' => false, ]; $delay = $this->getConfig()->get('emailNotificationsDelay'); if ($delay) { $delayDt = new \DateTime(); $delayDt->modify('-' . $delay . ' seconds'); $where[] = ['createdAt<' => $delayDt->format('Y-m-d H:i:s')]; } $sqlArr = []; foreach ($typeList as $type) { $methodName = 'getNotificationSelectParams' . $type; $selectParams = $this->$methodName(); $selectParams['whereClause'][] = $where; $sqlArr[] = $this->getEntityManager()->getQuery()->createSelectQuery('Notification', $selectParams); } $maxCount = intval(self::PROCESS_MAX_COUNT); $sql = '' . implode(' UNION ', $sqlArr) . " ORDER BY number LIMIT 0, {$maxCount}"; $notificationList = $this->getEntityManager()->getRepository('Notification')->findByQuery($sql); foreach ($notificationList as $notification) { $notification->set('emailIsProcessed', true); $type = $notification->get('type'); $methodName = 'processNotification' . ucfirst($type); if (method_exists($this, $methodName)) { $this->$methodName($notification); } $this->getEntityManager()->saveEntity($notification); } } protected function getNotificationSelectParamsMentionInPost() { $selectManager = $this->getInjection('selectManagerFactory')->create('Notification'); $selectParams = $selectManager->getEmptySelectParams(); $selectParams['whereClause']['type'] = 'MentionInPost'; return $selectParams; } protected function getNotificationSelectParamsNote() { $noteNotificationTypeList = $this->getConfig()->get('streamEmailNotificationsTypeList', []); $selectManager = $this->getInjection('selectManagerFactory')->create('Notification'); $selectParams = $selectManager->getEmptySelectParams(); $selectParams['whereClause']['type'] = 'Note'; $selectParams['whereClause']['relatedType'] = 'Note'; $selectParams['customJoin'] .= ' JOIN note ON notification.related_id = note.id'; $selectParams['whereClause']['note.type'] = $noteNotificationTypeList; $entityList = $this->getConfig()->get('streamEmailNotificationsEntityList'); if (empty($entityList)) { $selectParams['whereClause']['relatedParentType'] = null; } else { $selectParams['whereClause'][] = [ 'OR' => [ [ 'relatedParentType' => $entityList ], [ 'relatedParentType' => null ] ] ]; } $forInternal = $this->getConfig()->get('streamEmailNotifications'); $forPortal = $this->getConfig()->get('portalStreamEmailNotifications'); if ($forInternal && !$forPortal) { $selectParams['whereClause']['user.type!='] = 'portal'; } else if (!$forInternal && $forPortal) { $selectParams['whereClause']['user.type'] = 'portal'; } return $selectParams; } protected function processNotificationMentionInPost(Entity $notification) { if (!$notification->get('userId')) return; $userId = $notification->get('userId'); $user = $this->getEntityManager()->getEntity('User', $userId); if (!$user) return; $emailAddress = $user->get('emailAddress'); if (!$emailAddress) return; $preferences = $this->getEntityManager()->getEntity('Preferences', $userId); if (!$preferences) return; if (!$preferences->get('receiveMentionEmailNotifications')) return; if ($notification->get('relatedType') !== 'Note' || !$notification->get('relatedId')) return; $note = $this->getEntityManager()->getEntity('Note', $notification->get('relatedId')); if (!$note) return; $parentId = $note->get('parentId'); $parentType = $note->get('parentType'); $data = []; if ($parentId && $parentType) { $parent = $this->getEntityManager()->getEntity($parentType, $parentId); if (!$parent) return; $data['url'] = $this->getSiteUrl($user) . '/#' . $parentType . '/view/' . $parentId; $data['parentName'] = $parent->get('name'); $data['parentType'] = $parentType; $data['parentId'] = $parentId; } else { $data['url'] = $this->getSiteUrl($user) . '/#Notification'; } $data['userName'] = $note->get('createdByName'); $post = $note->get('post') ?? ''; $post = \Michelf\Markdown::defaultTransform($post); $data['post'] = $post; $subjectTpl = $this->getTemplateFileManager()->getTemplate('mention', 'subject'); $bodyTpl = $this->getTemplateFileManager()->getTemplate('mention', 'body'); $subjectTpl = str_replace(["\n", "\r"], '', $subjectTpl); $subject = $this->getHtmlizer()->render($note, $subjectTpl, 'mention-email-subject', $data, true); $body = $this->getHtmlizer()->render($note, $bodyTpl, 'mention-email-body', $data, true); $email = $this->getEntityManager()->getEntity('Email'); $email->set([ 'subject' => $subject, 'body' => $body, 'isHtml' => true, 'to' => $emailAddress, 'isSystem' => true ]); if ($parentId && $parentType) { $email->set([ 'parentId' => $parentId, 'parentType' => $parentType ]); } try { $this->getMailSender()->send($email); } catch (\Exception $e) { $GLOBALS['log']->error('EmailNotification: [' . $e->getCode() . '] ' .$e->getMessage()); } } protected function processNotificationNote(Entity $notification) { if ($notification->get('relatedType') !== 'Note') return; if (!$notification->get('relatedId')) return; $note = $this->getEntityManager()->getEntity('Note', $notification->get('relatedId')); if (!$note) return; $noteNotificationTypeList = $this->getConfig()->get('streamEmailNotificationsTypeList', []); if (!in_array($note->get('type'), $noteNotificationTypeList)) return; if (!$notification->get('userId')) return; $userId = $notification->get('userId'); $user = $this->getEntityManager()->getEntity('User', $userId); if (!$user) return; $emailAddress = $user->get('emailAddress'); if (!$emailAddress) return; $preferences = $this->getEntityManager()->getEntity('Preferences', $userId); if (!$preferences) return; if (!$preferences->get('receiveStreamEmailNotifications')) return; $methodName = 'processNotificationNote' . $note->get('type'); if (!method_exists($this, $methodName)) return; $this->$methodName($note, $user); } protected function getEmailNotificationEntityHandler($entityType) { if (!array_key_exists($entityType, $this->emailNotificationEntityHandlerHash)) { $this->emailNotificationEntityHandlerHash[$entityType] = null; $className = $this->getMetadata()->get(['app', 'emailNotifications', 'handlerClassNameMap', $entityType]); if ($className && class_exists($className)) { $handler = $this->getInjection('injectableFactory')->createByClassName($className); if ($handler) { $this->emailNotificationEntityHandlerHash[$entityType] = $handler; } } } return $this->emailNotificationEntityHandlerHash[$entityType]; } protected function processNotificationNotePost($note, $user) { $parentId = $note->get('parentId'); $parentType = $note->get('parentType'); $emailAddress = $user->get('emailAddress'); if (!$emailAddress) return; $data = []; $data['userName'] = $note->get('createdByName'); $post = $note->get('post') ?? ''; $post = \Michelf\Markdown::defaultTransform($post); $data['post'] = $post; if ($parentId && $parentType) { $parent = $this->getEntityManager()->getEntity($parentType, $parentId); if (!$parent) return; $data['url'] = $this->getSiteUrl($user) . '/#' . $parentType . '/view/' . $parentId; $data['parentName'] = $parent->get('name'); $data['parentType'] = $parentType; $data['parentId'] = $parentId; $data['name'] = $data['parentName']; $data['entityType'] = $this->getLanguage()->translate($data['parentType'], 'scopeNames'); $data['entityTypeLowerFirst'] = Util::mbLowerCaseFirst($data['entityType']); $subjectTpl = $this->getTemplateFileManager()->getTemplate('notePost', 'subject', $parentType); $bodyTpl = $this->getTemplateFileManager()->getTemplate('notePost', 'body', $parentType); $subjectTpl = str_replace(["\n", "\r"], '', $subjectTpl); $subject = $this->getHtmlizer()->render($note, $subjectTpl, 'note-post-email-subject-' . $parentType, $data, true); $body = $this->getHtmlizer()->render($note, $bodyTpl, 'note-post-email-body-' . $parentType, $data, true); } else { $data['url'] = $this->getSiteUrl($user) . '/#Notification'; $subjectTpl = $this->getTemplateFileManager()->getTemplate('notePostNoParent', 'subject'); $bodyTpl = $this->getTemplateFileManager()->getTemplate('notePostNoParent', 'body'); $subjectTpl = str_replace(["\n", "\r"], '', $subjectTpl); $subject = $this->getHtmlizer()->render($note, $subjectTpl, 'note-post-email-subject', $data, true); $body = $this->getHtmlizer()->render($note, $bodyTpl, 'note-post-email-body', $data, true); } $email = $this->getEntityManager()->getEntity('Email'); $email->set([ 'subject' => $subject, 'body' => $body, 'isHtml' => true, 'to' => $emailAddress, 'isSystem' => true ]); if ($parentId && $parentType) { $email->set([ 'parentId' => $parentId, 'parentType' => $parentType ]); } $smtpParams = null; if ($parentId && $parentType && !empty($parent)) { $handler = $this->getEmailNotificationEntityHandler($parentType); if ($handler) { $prepareEmailMethodName = 'prepareEmail'; if (method_exists($handler, $prepareEmailMethodName)) { $handler->$prepareEmailMethodName('notePost', $parent, $email, $user); } $getSmtpParamsMethodName = 'getSmtpParams'; if (method_exists($handler, $getSmtpParamsMethodName)) { $smtpParams = $handler->$getSmtpParamsMethodName('notePost', $parent, $user); } } } try { if ($smtpParams) { $this->getMailSender()->setParams($smtpParams); } $this->getMailSender()->send($email); } catch (\Exception $e) { $GLOBALS['log']->error('EmailNotification: [' . $e->getCode() . '] ' .$e->getMessage()); } } protected function getSiteUrl(\Espo\Entities\User $user) { if ($user->isPortal()) { if (!array_key_exists($user->id, $this->userIdPortalCacheMap)) { $this->userIdPortalCacheMap[$user->id] = null; $portalIdList = $user->getLinkMultipleIdList('portals'); $defaultPortalId = $this->getConfig()->get('defaultPortalId'); $portalId = null; if (in_array($defaultPortalId, $portalIdList)) { $portalId = $defaultPortalId; } else if (count($portalIdList)) { $portalId = $portalIdList[0]; } if ($portalId) { $portal = $this->getEntityManager()->getEntity('Portal', $portalId); $this->getEntityManager()->getRepository('Portal')->loadUrlField($portal); $this->userIdPortalCacheMap[$user->id] = $portal; } } else { $portal = $this->userIdPortalCacheMap[$user->id]; } if ($portal) { $url = $portal->get('url'); $url = rtrim($url, '/'); return $url; } } return $this->getConfig()->getSiteUrl(); } protected function processNotificationNoteStatus($note, $user) { $parentId = $note->get('parentId'); $parentType = $note->get('parentType'); $emailAddress = $user->get('emailAddress'); if (!$emailAddress) return; $data = []; if (!$parentId || !$parentType) return; $parent = $this->getEntityManager()->getEntity($parentType, $parentId); if (!$parent) return; $data['url'] = $this->getSiteUrl($user) . '/#' . $parentType . '/view/' . $parentId; $data['parentName'] = $parent->get('name'); $data['parentType'] = $parentType; $data['parentId'] = $parentId; $data['name'] = $data['parentName']; $data['entityType'] = $this->getLanguage()->translate($data['parentType'], 'scopeNames'); $data['entityTypeLowerFirst'] = Util::mbLowerCaseFirst($data['entityType']); $noteData = $note->get('data'); if (empty($noteData)) return; $data['value'] = $noteData->value; $data['field'] = $noteData->field; $data['valueTranslated'] = $this->getLanguage()->translateOption($data['value'], $data['field'], $parentType); $data['fieldTranslated'] = $this->getLanguage()->translate($data['field'], 'fields', $parentType); $data['fieldTranslatedLowerCase'] = Util::mbLowerCaseFirst($data['fieldTranslated']); $data['userName'] = $note->get('createdByName'); $subjectTpl = $this->getTemplateFileManager()->getTemplate('noteStatus', 'subject', $parentType); $bodyTpl = $this->getTemplateFileManager()->getTemplate('noteStatus', 'body', $parentType); $subjectTpl = str_replace(["\n", "\r"], '', $subjectTpl); $subject = $this->getHtmlizer()->render($note, $subjectTpl, 'note-status-email-subject', $data, true); $body = $this->getHtmlizer()->render($note, $bodyTpl, 'note-status-email-body', $data, true); $email = $this->getEntityManager()->getEntity('Email'); $email->set([ 'subject' => $subject, 'body' => $body, 'isHtml' => true, 'to' => $emailAddress, 'isSystem' => true, 'parentId' => $parentId, 'parentType' => $parentType ]); try { $this->getMailSender()->send($email); } catch (\Exception $e) { $GLOBALS['log']->error('EmailNotification: [' . $e->getCode() . '] ' .$e->getMessage()); } } protected function processNotificationNoteEmailReceived($note, $user) { $parentId = $note->get('parentId'); $parentType = $note->get('parentType'); $allowedEntityTypeList = $this->getConfig()->get('streamEmailNotificationsEmailReceivedEntityTypeList'); if ( is_array($allowedEntityTypeList) && !in_array($parentType, $allowedEntityTypeList) ) return; $emailAddress = $user->get('emailAddress'); if (!$emailAddress) return; $noteData = $note->get('data'); if (!($noteData instanceof \StdClass)) return; if (!isset($noteData->emailId)) return; $email = $this->getEntityManager()->getEntity('Email', $noteData->emailId); if (!$email) return; $emailRepository = $this->getEntityManager()->getRepository('Email'); $eaList = $user->get('emailAddresses'); foreach ($eaList as $ea) { if ( $emailRepository->isRelated($email, 'toEmailAddresses', $ea) || $emailRepository->isRelated($email, 'ccEmailAddresses', $ea) ) return; } $data = []; $data['fromName'] = ''; if (isset($noteData->personEntityName)) { $data['fromName'] = $noteData->personEntityName; } else if (isset($noteData->fromString)) { $data['fromName'] = $noteData->fromString; } $data['subject'] = ''; if (isset($noteData->emailName)) { $data['subject'] = $noteData->emailName; } $data['post'] = nl2br($note->get('post')); if (!$parentId || !$parentType) return; $parent = $this->getEntityManager()->getEntity($parentType, $parentId); if (!$parent) return; $data['url'] = $this->getSiteUrl($user) . '/#' . $parentType . '/view/' . $parentId; $data['parentName'] = $parent->get('name'); $data['parentType'] = $parentType; $data['parentId'] = $parentId; $data['name'] = $data['parentName']; $data['entityType'] = $this->getLanguage()->translate($data['parentType'], 'scopeNames'); $data['entityTypeLowerFirst'] = Util::mbLowerCaseFirst($data['entityType']); $subjectTpl = $this->getTemplateFileManager()->getTemplate('noteEmailRecieved', 'subject', $parentType); $bodyTpl = $this->getTemplateFileManager()->getTemplate('noteEmailRecieved', 'body', $parentType); $subjectTpl = str_replace(["\n", "\r"], '', $subjectTpl); $subject = $this->getHtmlizer()->render($note, $subjectTpl, 'note-email-recieved-email-subject-' . $parentType, $data, true); $body = $this->getHtmlizer()->render($note, $bodyTpl, 'note-email-recieved-email-body-' . $parentType, $data, true); $email = $this->getEntityManager()->getEntity('Email'); $email->set([ 'subject' => $subject, 'body' => $body, 'isHtml' => true, 'to' => $emailAddress, 'isSystem' => true ]); $email->set([ 'parentId' => $parentId, 'parentType' => $parentType ]); try { $this->getMailSender()->send($email); } catch (\Exception $e) { $GLOBALS['log']->error('EmailNotification: [' . $e->getCode() . '] ' .$e->getMessage()); } } protected function loadParentNameFields(Entity $entity) { $fieldDefs = $this->getMetadata()->get(['entityDefs', $entity->getEntityType(), 'fields'], []); foreach ($fieldDefs as $field => $defs) { if (isset($defs['type']) && $defs['type'] == 'linkParent') { $entity->loadParentNameField($field); } } } }