From 068eff9c94c0d678fbdb05d4e5224fbfce0b3df5 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Mon, 30 Mar 2020 16:15:22 +0300 Subject: [PATCH] email sending changes --- application/Espo/Core/Loaders/MailSender.php | 3 +- application/Espo/Core/Mail/Sender.php | 117 ++++++++++++------ .../Espo/Resources/i18n/en_US/Settings.json | 2 + .../layouts/Settings/outboundEmails.json | 14 +-- .../metadata/entityAcl/EmailAccount.json | 6 + .../metadata/entityDefs/EmailAccount.json | 8 ++ .../metadata/entityDefs/Settings.json | 6 +- application/Espo/Services/Email.php | 45 +++---- application/Espo/Services/EmailAccount.php | 22 +++- application/Espo/Services/InboundEmail.php | 22 +++- .../views/email-account/fields/test-send.js | 8 +- 11 files changed, 172 insertions(+), 81 deletions(-) diff --git a/application/Espo/Core/Loaders/MailSender.php b/application/Espo/Core/Loaders/MailSender.php index 09501b9166..f5b09f65a8 100644 --- a/application/Espo/Core/Loaders/MailSender.php +++ b/application/Espo/Core/Loaders/MailSender.php @@ -35,7 +35,8 @@ class MailSender extends Base { return new \Espo\Core\Mail\Sender( $this->getContainer()->get('config'), - $this->getContainer()->get('entityManager') + $this->getContainer()->get('entityManager'), + $this->getContainer()->get('serviceFactory') ); } } diff --git a/application/Espo/Core/Mail/Sender.php b/application/Espo/Core/Mail/Sender.php index f57e85be74..dbccfbe7d3 100644 --- a/application/Espo/Core/Mail/Sender.php +++ b/application/Espo/Core/Mail/Sender.php @@ -48,16 +48,26 @@ class Sender protected $entityManager; + protected $serviceFactory; + protected $transport; protected $isGlobal = false; protected $params = []; - public function __construct($config, $entityManager) + private $systemInboundEmail = null; + + private $inboundEmailService = null; + + private $systemInboundEmailIsCached = false; + + public function __construct($config, $entityManager, $serviceFactory = null) { $this->config = $config; $this->entityManager = $entityManager; + $this->serviceFactory = $serviceFactory; + $this->useGlobal(); } @@ -86,6 +96,19 @@ class Sender public function useSmtp(array $params = []) : self { $this->isGlobal = false; + $this->applySmtp($params); + return $this; + } + + public function useGlobal() + { + $this->params = []; + $this->isGlobal = true; + return $this; + } + + protected function applySmtp(array $params = []) + { $this->params = $params; $this->transport = new SmtpTransport(); @@ -106,11 +129,12 @@ class Sender $options['connectionConfig'][$key] = $value; } - if ($params['auth']) { - if (!empty($params['smtpAuthMechanism'])) { - $smtpAuthMechanism = preg_replace("([\.]{2,})", '', $params['smtpAuthMechanism']); - if (in_array($smtpAuthMechanism, ['login', 'crammd5', 'plain'])) { - $options['connectionClass'] = $smtpAuthMechanism; + if ($params['auth'] ?? false) { + $authMechanism = $params['authMechanism'] ?? $params['smtpAuthMechanism'] ?? null; + if ($authMechanism) { + $authMechanism = preg_replace("([\.]{2,})", '', $params['authMechanism']); + if (in_array($authMechanism, ['login', 'crammd5', 'plain'])) { + $options['connectionClass'] = $authMechanism; } else { $options['connectionClass'] = 'login'; } @@ -121,11 +145,12 @@ class Sender $options['connectionConfig']['password'] = $params['password']; } - if (!empty($params['smtpAuthClassName'])) { - $options['connectionClass'] = $params['smtpAuthClassName']; + $authClassName = $params['authClassName'] ?? $params['smtpAuthClassName'] ?? null; + if ($authClassName) { + $options['connectionClass'] = $authClassName; } - if ($params['security']) { + if ($params['security'] ?? null) { $options['connectionConfig']['ssl'] = strtolower($params['security']); } @@ -138,48 +163,68 @@ class Sender $smtpOptions = new SmtpOptions($options); $this->transport->setOptions($smtpOptions); - - return $this; } - public function useGlobal() + protected function applyGlobal() { - $this->params = []; - if ($this->isGlobal) { - return $this; - } - - $this->transport = new SmtpTransport(); - $config = $this->config; - $localHostName = $config->get('smtpLocalHostName', gethostname()); + if (!$config->get('smtpServer') && $config->get('outboundEmailFromAddress')) { + $inboundEmail = $this->getSystemInboundEmail(); + if ($inboundEmail) { + $service = $this->getInboundEmailService(); + if ($service) { + $params = $service->getSmtpParamsFromAccount($inboundEmail); + $this->applySmtp($params); + return; + } + } + } - $options = [ - 'name' => $localHostName, - 'host' => $config->get('smtpServer'), + $this->applySmtp([ + 'server' => $config->get('smtpServer'), 'port' => $config->get('smtpPort'), - 'connection_config' => [], - ]; - if ($config->get('smtpAuth')) { - $options['connection_class'] = $config->get('smtpAuthMechanism', 'login'); - $options['connection_config']['username'] = $config->get('smtpUsername'); - $options['connection_config']['password'] = $config->get('smtpPassword'); - } - if ($config->get('smtpSecurity')) { - $options['connection_config']['ssl'] = strtolower($config->get('smtpSecurity')); + 'auth' => $config->get('smtpAuth'), + 'authMechanism' => $config->get('smtpAuthMechanism', 'login'), + 'username' => $config->get('smtpUsername'), + 'password' => $config->get('smtpPassword'), + 'security' => $config->get('smtpSecurity'), + ]); + } + + protected function getSystemInboundEmail() + { + $address = $this->config->get('outboundEmailFromAddress'); + + if (!$this->systemInboundEmailIsCached && $address) { + $this->systemInboundEmail = $this->getEntityManager()->getRepository('InboundEmail')->where([ + 'status' => 'Active', + 'useSmtp' => true, + 'emailAddress' => $address, + ])->findOne(); + $this->systemInboundEmailIsCached = true; } - $smtpOptions = new SmtpOptions($options); - $this->transport->setOptions($smtpOptions); + return $this->systemInboundEmail; + } - $this->isGlobal = true; + protected function getInboundEmailService() + { + if (!$this->serviceFactory) return null; - return $this; + if (!$this->inboundEmailService) { + $this->inboundEmailService = $this->serviceFactory->create('InboundEmail'); + } + + return $this->inboundEmailService; } public function send(Email $email, ?array $params = [], $message = null, $attachmentList = []) { + if ($this->isGlobal) { + $this->applyGlobal(); + } + if (!$message) { $message = new Message(); } diff --git a/application/Espo/Resources/i18n/en_US/Settings.json b/application/Espo/Resources/i18n/en_US/Settings.json index b98006b9ba..dd42811d9d 100644 --- a/application/Espo/Resources/i18n/en_US/Settings.json +++ b/application/Espo/Resources/i18n/en_US/Settings.json @@ -159,6 +159,8 @@ } }, "tooltips": { + "outboundEmailFromAddress": "The system email address.", + "smtpServer": "If empty, then Group Email Account with the corresponding email address will be used.", "busyRangesEntityList": "What will be taken into account when showing busy time ranges in scheduler & timeline.", "massEmailVerp": "Variable envelope return path. For better handling of bounced messages. Make sure that your SMTP provider supports it.", "recordsPerPage": "Number of records initially displayed in list views.", diff --git a/application/Espo/Resources/layouts/Settings/outboundEmails.json b/application/Espo/Resources/layouts/Settings/outboundEmails.json index e1c847aa68..5e23ddc57d 100644 --- a/application/Espo/Resources/layouts/Settings/outboundEmails.json +++ b/application/Espo/Resources/layouts/Settings/outboundEmails.json @@ -1,4 +1,11 @@ [ + { + "label": "Configuration", + "rows": [ + [{"name": "outboundEmailFromAddress"}, {"name": "outboundEmailIsShared"}], + [{"name": "outboundEmailFromName"}, {"name": "outboundEmailBccAddress"}] + ] + }, { "label": "SMTP", "rows": [ @@ -8,13 +15,6 @@ [{"name": "smtpPassword"}, false] ] }, - { - "label": "Configuration", - "rows": [ - [{"name": "outboundEmailFromAddress"}, {"name": "outboundEmailIsShared"}], - [{"name": "outboundEmailFromName"}, {"name": "outboundEmailBccAddress"}] - ] - }, { "label": "Mass Email", "rows": [ diff --git a/application/Espo/Resources/metadata/entityAcl/EmailAccount.json b/application/Espo/Resources/metadata/entityAcl/EmailAccount.json index 7084a5bafe..1cbd68b94e 100644 --- a/application/Espo/Resources/metadata/entityAcl/EmailAccount.json +++ b/application/Espo/Resources/metadata/entityAcl/EmailAccount.json @@ -6,6 +6,12 @@ "smtpPassword": { "internal": true }, + "imapHandler": { + "internal": true + }, + "smtpHandler": { + "internal": true + }, "fetchData": { "readOnly": true } diff --git a/application/Espo/Resources/metadata/entityDefs/EmailAccount.json b/application/Espo/Resources/metadata/entityDefs/EmailAccount.json index 675daabe25..6eea6a548a 100644 --- a/application/Espo/Resources/metadata/entityDefs/EmailAccount.json +++ b/application/Espo/Resources/metadata/entityDefs/EmailAccount.json @@ -119,6 +119,14 @@ "options": ["login", "crammd5", "plain"], "default": "login" }, + "imapHandler": { + "type": "varchar", + "readOnly": true + }, + "smtpHandler": { + "type": "varchar", + "readOnly": true + }, "createdBy": { "type": "link", "readOnly": true diff --git a/application/Espo/Resources/metadata/entityDefs/Settings.json b/application/Espo/Resources/metadata/entityDefs/Settings.json index 38706b2c6c..9da3db4a62 100644 --- a/application/Espo/Resources/metadata/entityDefs/Settings.json +++ b/application/Espo/Resources/metadata/entityDefs/Settings.json @@ -92,10 +92,12 @@ "outboundEmailFromAddress": { "type": "varchar", "default": "crm@example.com", - "trim": true + "trim": true, + "tooltip": true }, "smtpServer": { - "type": "varchar" + "type": "varchar", + "tooltip": true }, "smtpPort": { "type": "int", diff --git a/application/Espo/Services/Email.php b/application/Espo/Services/Email.php index db199b08af..3f77003dce 100644 --- a/application/Espo/Services/Email.php +++ b/application/Espo/Services/Email.php @@ -209,10 +209,6 @@ class Email extends Record $inboundEmail = $inboundEmailService->findSharedAccountForUser($this->getUser(), $originalFromAddress); if ($inboundEmail) { $smtpParams = $inboundEmailService->getSmtpParamsFromAccount($inboundEmail); - - if ($inboundEmail->get('smtpHandler')) { - $this->applyGroupSmtpHandler($inboundEmail, $smtpParams); - } } if ($smtpParams) { $emailSender->useSmtp($smtpParams); @@ -316,29 +312,13 @@ class Email extends Record } if (method_exists($handler, 'applyParams')) { $handler->applyParams($userId, $emailAddress, $params); + return; } } } } } - protected function applyGroupSmtpHandler(\Espo\Entities\InboundEmail $inboundEmail, array &$params) - { - $handlerClassName = $inboundEmail->get('smtpHandler'); - if (!$handlerClassName) return; - - try { - $handler = $this->getInjection('injectableFactory')->createByClassName($handlerClassName); - } catch (\Throwable $e) { - $GLOBALS['log']->error( - "Send Email: Could not create Smtp Handler for inbound email {$inboundEmail->id}. Error: " . $e->getMessage() - ); - } - if (method_exists($handler, 'applyParams')) { - $handler->applyParams($inboundEmail->id, $params); - } - } - public function validateEmailAddresses(\Espo\Entities\Email $entity) { $from = $entity->get('from'); @@ -865,7 +845,7 @@ class Email extends Record if (empty($smtpParams['auth'])) { unset($smtpParams['username']); unset($smtpParams['password']); - unset($smtpParams['smtpAuthMechanism']); + unset($smtpParams['authMechanism']); } $userId = $data['userId'] ?? null; @@ -885,19 +865,26 @@ class Email extends Record 'to' => $data['emailAddress'], ]); - if ($userId) { - if ($fromAddress) { - $this->applySmtpHandler($userId, $fromAddress, $smtpParams); - } - } - $type = $data['type'] ?? null; $id = $data['id'] ?? null; + if ($type === 'emailAccount' && $id) { + $emailAccount = $this->getEntityManager()->getEntity('EmailAccount', $id); + if ($emailAccount && $emailAccount->get('smtpHandler')) { + $this->getServiceFactory()->create('EmailAccount')->applySmtpHandler($emailAccount, $smtpParams); + } + } + if ($type === 'inboundEmail' && $id) { $inboundEmail = $this->getEntityManager()->getEntity('InboundEmail', $id); if ($inboundEmail && $inboundEmail->get('smtpHandler')) { - $this->applyGroupSmtpHandler($inboundEmail, $smtpParams); + $this->getServiceFactory()->create('InboundEmail')->applySmtpHandler($inboundEmail, $smtpParams); + } + } + + if ($userId) { + if ($fromAddress) { + $this->applySmtpHandler($userId, $fromAddress, $smtpParams); } } diff --git a/application/Espo/Services/EmailAccount.php b/application/Espo/Services/EmailAccount.php index 846534f89b..d8716dab2f 100644 --- a/application/Espo/Services/EmailAccount.php +++ b/application/Espo/Services/EmailAccount.php @@ -496,13 +496,33 @@ class EmailAccount extends Record if ($emailAccount->get('smtpAuth')) { $smtpParams['username'] = $emailAccount->get('smtpUsername'); $smtpParams['password'] = $emailAccount->get('smtpPassword'); - $smtpParams['smtpAuthMechanism'] = $emailAccount->get('smtpAuthMechanism'); + $smtpParams['authMechanism'] = $emailAccount->get('smtpAuthMechanism'); } if (array_key_exists('password', $smtpParams)) { $smtpParams['password'] = $this->getCrypt()->decrypt($smtpParams['password']); } + + $this->applySmtpHandler($emailAccount, $smtpParams); + return $smtpParams; } return; } + + public function applySmtpHandler(\Espo\Entities\EmailAccount $emailAccount, array &$params) + { + $handlerClassName = $emailAccount->get('smtpHandler'); + if (!$handlerClassName) return; + + try { + $handler = $this->getInjection('injectableFactory')->createByClassName($handlerClassName); + } catch (\Throwable $e) { + $GLOBALS['log']->error( + "EmailAccount: Could not create Smtp Handler for account {$emailAccount->id}. Error: " . $e->getMessage() + ); + } + if (method_exists($handler, 'applyParams')) { + $handler->applyParams($emailAccount->id, $params); + } + } } diff --git a/application/Espo/Services/InboundEmail.php b/application/Espo/Services/InboundEmail.php index 347b093c44..6ce5d6d6ff 100644 --- a/application/Espo/Services/InboundEmail.php +++ b/application/Espo/Services/InboundEmail.php @@ -962,7 +962,7 @@ class InboundEmail extends \Espo\Services\Record $smtpParams['password'] = $emailAccount->get('smtpPassword'); if ($emailAccount->get('smtpAuth')) { - $smtpParams['smtpAuthMechanism'] = $emailAccount->get('smtpAuthMechanism'); + $smtpParams['authMechanism'] = $emailAccount->get('smtpAuthMechanism'); } if ($emailAccount->get('fromName')) { @@ -974,8 +974,28 @@ class InboundEmail extends \Espo\Services\Record if (array_key_exists('password', $smtpParams) && is_string($smtpParams['password'])) { $smtpParams['password'] = $this->getCrypt()->decrypt($smtpParams['password']); } + + $this->applySmtpHandler($emailAccount, $smtpParams); + return $smtpParams; } return; } + + public function applySmtpHandler(\Espo\Entities\InboundEmail $emailAccount, array &$params) + { + $handlerClassName = $emailAccount->get('smtpHandler'); + if (!$handlerClassName) return; + + try { + $handler = $this->getInjection('injectableFactory')->createByClassName($handlerClassName); + } catch (\Throwable $e) { + $GLOBALS['log']->error( + "InboundEmail: Could not create Smtp Handler for account {$emailAccount->id}. Error: " . $e->getMessage() + ); + } + if (method_exists($handler, 'applyParams')) { + $handler->applyParams($emailAccount->id, $params); + } + } } diff --git a/client/src/views/email-account/fields/test-send.js b/client/src/views/email-account/fields/test-send.js index 4ab6f29d7f..34526669be 100644 --- a/client/src/views/email-account/fields/test-send.js +++ b/client/src/views/email-account/fields/test-send.js @@ -26,7 +26,7 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -Espo.define('views/email-account/fields/test-send', 'views/outbound-email/fields/test-send', function (Dep) { +define('views/email-account/fields/test-send', 'views/outbound-email/fields/test-send', function (Dep) { return Dep.extend({ @@ -55,7 +55,7 @@ Espo.define('views/email-account/fields/test-send', 'views/outbound-email/fields 'security': this.model.get('smtpSecurity'), 'username': this.model.get('smtpUsername'), 'password': this.model.get('smtpPassword') || null, - 'smtpAuthMechanism': this.model.get('smtpAuthMechanism'), + 'authMechanism': this.model.get('smtpAuthMechanism'), 'fromName': this.getUser().get('name'), 'fromAddress': this.model.get('emailAddress'), 'type': 'emailAccount', @@ -63,7 +63,7 @@ Espo.define('views/email-account/fields/test-send', 'views/outbound-email/fields 'userId': this.model.get('assignedUserId'), }; return data; - } - }); + }, + }); });