diff --git a/application/Espo/Jobs/SendEmailNotifications.php b/application/Espo/Jobs/SendEmailNotifications.php new file mode 100644 index 0000000000..595faab4d0 --- /dev/null +++ b/application/Espo/Jobs/SendEmailNotifications.php @@ -0,0 +1,42 @@ +getServiceFactory()->create('EmailNotification'); + $service->process(); + } +} + diff --git a/application/Espo/ORM/DB/Query/Base.php b/application/Espo/ORM/DB/Query/Base.php index ada3121fa3..e0bb0bf531 100644 --- a/application/Espo/ORM/DB/Query/Base.php +++ b/application/Espo/ORM/DB/Query/Base.php @@ -407,6 +407,9 @@ abstract class Base } if (!is_null($order)) { + if (is_bool($order)) { + $order = $order ? 'DESC' : 'ASC'; + } $order = strtoupper($order); if (!in_array($order, ['ASC', 'DESC'])) { $order = 'ASC'; diff --git a/application/Espo/Resources/i18n/en_US/Preferences.json b/application/Espo/Resources/i18n/en_US/Preferences.json index 316ab28b40..e9022fd825 100644 --- a/application/Espo/Resources/i18n/en_US/Preferences.json +++ b/application/Espo/Resources/i18n/en_US/Preferences.json @@ -18,7 +18,8 @@ "smtpPassword": "Password", "smtpEmailAddress": "Email Address", "exportDelimiter": "Export Delimiter", - "receiveAssignmentEmailNotifications": "Receive Email Notifications upon Assignment", + "receiveAssignmentEmailNotifications": "Receive email notifications upon assignment", + "receiveMentionEmailNotifications": "Receive email notifications about mentions in posts", "autoFollowEntityTypeList": "Auto-Follow", "signature": "Email Signature", "dashboardTabList": "Tab List", diff --git a/application/Espo/Resources/i18n/en_US/ScheduledJob.json b/application/Espo/Resources/i18n/en_US/ScheduledJob.json index 99e6322efe..5d8c8f1bbc 100644 --- a/application/Espo/Resources/i18n/en_US/ScheduledJob.json +++ b/application/Espo/Resources/i18n/en_US/ScheduledJob.json @@ -17,7 +17,8 @@ "CheckInboundEmails": "Check Group Email Accounts", "CheckEmailAccounts": "Check Personal Email Accounts", "SendEmailReminders": "Send Email Reminders", - "AuthTokenControl": "Auth Token Control" + "AuthTokenControl": "Auth Token Control", + "SendEmailNotifications": "Send Email Notifications" }, "cronSetup": { "linux": "Note: Add this line to the crontab file to run Espo Scheduled Jobs:", diff --git a/application/Espo/Resources/i18n/en_US/Settings.json b/application/Espo/Resources/i18n/en_US/Settings.json index de50d0c49d..f09882df3c 100644 --- a/application/Espo/Resources/i18n/en_US/Settings.json +++ b/application/Espo/Resources/i18n/en_US/Settings.json @@ -46,9 +46,9 @@ "ldapAccountDomainNameShort": "Account Domain Name Short", "ldapOptReferrals": "Opt Referrals", "exportDisabled": "Disable Export (only admin is allowed)", - "assignmentNotificationsEntityList": "Entities to Notify about upon Assignment", - "assignmentEmailNotifications": "Send Email Notifications upon Assignment", - "assignmentEmailNotificationsEntityList": "Entities to Notify about with Email upon Assignment", + "assignmentNotificationsEntityList": "Entities to notify about upon assignment", + "assignmentEmailNotifications": "Send email notifications upon assignment", + "assignmentEmailNotificationsEntityList": "Entities to notify about with email upon assignment", "b2cMode": "B2C Mode", "avatarsDisabled": "Disable Avatars", "followCreatedEntities": "Follow Created Entities", @@ -68,7 +68,8 @@ "addressFormat": "Address Format", "notificationSoundsDisabled": "Disable Notification Sounds", "applicationName": "Application Name", - "calendarEntityList": "Calendar Entity List" + "calendarEntityList": "Calendar Entity List", + "mentionEmailNotifications": "Send email notifications about mentions in posts" }, "options": { "weekStart": { diff --git a/application/Espo/Resources/layouts/Preferences/detail.json b/application/Espo/Resources/layouts/Preferences/detail.json index ecfd9eeaa3..91668ca49a 100644 --- a/application/Espo/Resources/layouts/Preferences/detail.json +++ b/application/Espo/Resources/layouts/Preferences/detail.json @@ -71,7 +71,7 @@ { "label": "Notifications", "rows": [ - [{"name": "receiveAssignmentEmailNotifications"}, false] + [{"name": "receiveAssignmentEmailNotifications"}, {"name": "receiveMentionEmailNotifications"}] ] } ] diff --git a/application/Espo/Resources/layouts/Settings/notifications.json b/application/Espo/Resources/layouts/Settings/notifications.json index d48ebe4b10..7e8e7cb515 100644 --- a/application/Espo/Resources/layouts/Settings/notifications.json +++ b/application/Espo/Resources/layouts/Settings/notifications.json @@ -9,7 +9,7 @@ { "label": "Email Notifications", "rows": [ - [{"name": "assignmentEmailNotifications"}], + [{"name": "assignmentEmailNotifications"}, {"name": "mentionEmailNotifications"}], [{"name": "assignmentEmailNotificationsEntityList"}] ] } diff --git a/application/Espo/Resources/metadata/entityDefs/Notification.json b/application/Espo/Resources/metadata/entityDefs/Notification.json index a68fc6e33c..f45659bb45 100644 --- a/application/Espo/Resources/metadata/entityDefs/Notification.json +++ b/application/Espo/Resources/metadata/entityDefs/Notification.json @@ -17,6 +17,9 @@ "read": { "type": "bool" }, + "emailIsProcessed": { + "type": "bool" + }, "user": { "type": "link" }, diff --git a/application/Espo/Resources/metadata/entityDefs/Preferences.json b/application/Espo/Resources/metadata/entityDefs/Preferences.json index 5cafec1cc2..29bb6afb99 100644 --- a/application/Espo/Resources/metadata/entityDefs/Preferences.json +++ b/application/Espo/Resources/metadata/entityDefs/Preferences.json @@ -98,6 +98,10 @@ "type": "bool", "default": true }, + "receiveMentionEmailNotifications": { + "type": "bool", + "default": true + }, "autoFollowEntityTypeList": { "type": "multiEnum", "view": "views/preferences/fields/auto-follow-entity-type-list", diff --git a/application/Espo/Resources/metadata/entityDefs/ScheduledJob.json b/application/Espo/Resources/metadata/entityDefs/ScheduledJob.json index d2987dc603..5eee405940 100644 --- a/application/Espo/Resources/metadata/entityDefs/ScheduledJob.json +++ b/application/Espo/Resources/metadata/entityDefs/ScheduledJob.json @@ -64,6 +64,7 @@ "CheckEmailAccounts": "*/5 * * * *", "SendEmailReminders": "/2 * * * *", "Cleanup": "1 1 * * 0", - "AuthTokenControl": "*/6 * * * *" + "AuthTokenControl": "*/6 * * * *", + "SendEmailNotifications": "/2 * * * *" } } diff --git a/application/Espo/Resources/metadata/entityDefs/Settings.json b/application/Espo/Resources/metadata/entityDefs/Settings.json index 9268044f5f..416804d04b 100644 --- a/application/Espo/Resources/metadata/entityDefs/Settings.json +++ b/application/Espo/Resources/metadata/entityDefs/Settings.json @@ -223,6 +223,10 @@ "translation": "Global.scopeNamesPlural", "view": "views/settings/fields/assignment-notifications-entity-list" }, + "mentionEmailNotifications": { + "type": "bool", + "default": false + }, "b2cMode": { "type": "bool", "default": false diff --git a/application/Espo/Resources/templates/mention/en_US/body.tpl b/application/Espo/Resources/templates/mention/en_US/body.tpl new file mode 100644 index 0000000000..6826ed23ee --- /dev/null +++ b/application/Espo/Resources/templates/mention/en_US/body.tpl @@ -0,0 +1,6 @@ +

You were mentioned in post by {{userName}}.

+{{#if parentName}} +

Related to: {{parentName}}

+{{/if}} +

{{{post}}}

+

View

diff --git a/application/Espo/Resources/templates/mention/en_US/subject.tpl b/application/Espo/Resources/templates/mention/en_US/subject.tpl new file mode 100644 index 0000000000..5b7901b6b1 --- /dev/null +++ b/application/Espo/Resources/templates/mention/en_US/subject.tpl @@ -0,0 +1 @@ +You were mentioned \ No newline at end of file diff --git a/application/Espo/Services/EmailNotification.php b/application/Espo/Services/EmailNotification.php index d5b75c67bb..eee40421fc 100644 --- a/application/Espo/Services/EmailNotification.php +++ b/application/Espo/Services/EmailNotification.php @@ -36,6 +36,8 @@ use Espo\ORM\Entity; class EmailNotification extends \Espo\Core\Services\Base { + const HOURS_THERSHOLD = 5; + protected function init() { $this->addDependencyList([ @@ -186,4 +188,132 @@ class EmailNotification extends \Espo\Core\Services\Base $fileName = "application/Espo/Resources/templates/{$type}/{$language}/{$name}.tpl"; return $fileName; } + + protected function getMentionTemplate($name) + { + $fileName = $this->getMentionTemplateFileName($name); + return file_get_contents($fileName); + } + + protected function getMentionTemplateFileName($name) + { + $language = $this->getConfig()->get('language'); + $type = 'mention'; + + $fileName = "custom/Espo/Custom/Resources/templates/{$type}/{$language}/{$name}.tpl"; + if (file_exists($fileName)) return $fileName; + + $fileName = "application/Espo/Resources/templates/{$type}/{$language}/{$name}.tpl"; + if (file_exists($fileName)) return $fileName; + + $language = 'en_US'; + + $fileName = "custom/Espo/Custom/Resources/templates/{$type}/{$language}/{$name}.tpl"; + if (file_exists($fileName)) return $fileName; + + $fileName = "application/Espo/Resources/templates/{$type}/{$language}/{$name}.tpl"; + return $fileName; + } + + public function process() + { + $dateTime = new \DateTime(); + $dateTime->modify('-' . self::HOURS_THERSHOLD . ' hours'); + + $mentionEmailNotifications = $this->getConfig()->get('mentionEmailNotifications'); + + $typeList = []; + if ($mentionEmailNotifications) { + $typeList[] = 'MentionInPost'; + } + + if (!$mentionEmailNotifications) return; + + $where = array( + 'createdAt' > $dateTime, + 'read' => false, + 'emailIsProcessed' => false + ); + + $where['type'] = $typeList; + + $notificationList = $this->getEntityManager()->getRepository('Notification')->where($where)->order('createdAt')->find(); + + 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); + } + } + + public function processNotificationMentionInPost(Entity $notification) + { + $userId = $notification->get('userId'); + + $user = $this->getEntityManager()->getEntity('User', $userId); + + $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; + + $post = $note->get('post'); + $parentId = $note->get('parentId'); + $parentType = $note->get('parentType'); + + $data = array(); + + if ($parentId && $parentType) { + $parent = $this->getEntityManager()->getEntity($parentType, $parentId); + if (!$parent) return; + + $data['url'] = rtrim($this->getConfig()->get('siteUrl'), '/') . '/#' . $parentType . '/' . $parentId; + $data['parentName'] = $parent->get('name'); + $data['parentType'] = $parentType; + $data['parentId'] = $parentId; + } else { + $data['url'] = rtrim($this->getConfig()->get('siteUrl'), '/') . '/#Notification'; + } + + $data['userName'] = $note->get('createdByName'); + + $data['post'] = $note->get('post'); + + $subjectTpl = $this->getMentionTemplate('subject'); + $bodyTpl = $this->getMentionTemplate('body'); + $subjectTpl = str_replace(array("\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(array( + 'subject' => $subject, + 'body' => $body, + 'isHtml' => true, + 'to' => $emailAddress, + 'isSystem' => true + )); + try { + $this->getMailSender()->send($email); + } catch (\Exception $e) { + $GLOBALS['log']->error('EmailNotification: [' . $e->getCode() . '] ' .$e->getMessage()); + } + } } \ No newline at end of file diff --git a/install/core/afterInstall/records.php b/install/core/afterInstall/records.php index 1d42d50b2a..7cbd991f38 100644 --- a/install/core/afterInstall/records.php +++ b/install/core/afterInstall/records.php @@ -55,6 +55,12 @@ return array( 'status' => 'Active', 'scheduling' => '*/2 * * * *', ), + array( + 'name' => 'Send Email Notifications', + 'job' => 'SendEmailNotifications', + 'status' => 'Active', + 'scheduling' => '*/2 * * * *', + ), array( 'name' => 'Clean-up', 'job' => 'Cleanup',