From 62eb54d853fdcdfdd606871bd249c44cab503e52 Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 9 Sep 2015 17:03:37 +0300 Subject: [PATCH] dev --- application/Espo/Core/Mail/Sender.php | 6 +- .../Modules/Crm/Entities/EmailQueueItem.php | 28 +++ .../Modules/Crm/Jobs/ProcessMassEmail.php | 59 +++++ .../Crm/Resources/i18n/en_US/MassEmail.json | 3 +- .../Resources/i18n/en_US/ScheduledJob.json | 7 + .../metadata/entityDefs/EmailQueueItem.json | 2 +- .../Espo/Modules/Crm/Services/MassEmail.php | 207 ++++++++++++++++++ application/Espo/ORM/EntityManager.php | 2 +- application/Espo/Services/EmailTemplate.php | 58 +++-- install/core/afterInstall/records.php | 6 + 10 files changed, 351 insertions(+), 27 deletions(-) create mode 100644 application/Espo/Modules/Crm/Entities/EmailQueueItem.php create mode 100644 application/Espo/Modules/Crm/Jobs/ProcessMassEmail.php create mode 100644 application/Espo/Modules/Crm/Resources/i18n/en_US/ScheduledJob.json diff --git a/application/Espo/Core/Mail/Sender.php b/application/Espo/Core/Mail/Sender.php index 520e3d13bb..45a56d6f45 100644 --- a/application/Espo/Core/Mail/Sender.php +++ b/application/Espo/Core/Mail/Sender.php @@ -131,7 +131,7 @@ class Sender return $this; } - public function send(Email $email, $params = array(), &$message = null) + public function send(Email $email, $params = array(), &$message = null, $attachmetList = []) { $message = new Message(); $config = $this->config; @@ -220,6 +220,10 @@ class Sender $attachmentCollection = $email->get('attachments'); $attachmentInlineCollection = $email->getInlineAttachments(); + foreach ($attachmetList as $attachment) { + $attachmentCollection[] = $attachment; + } + if (!empty($attachmentCollection)) { foreach ($attachmentCollection as $a) { $fileName = 'data/upload/' . $a->id; diff --git a/application/Espo/Modules/Crm/Entities/EmailQueueItem.php b/application/Espo/Modules/Crm/Entities/EmailQueueItem.php new file mode 100644 index 0000000000..b5cb97d379 --- /dev/null +++ b/application/Espo/Modules/Crm/Entities/EmailQueueItem.php @@ -0,0 +1,28 @@ +getServiceFactory()->create('MassEmail'); + + $massEmailList = $this->getEntityManager()->getRepository('MassEmail')->where(array( + 'status' => 'Pending', + 'startAt<=' => date('Y-m-d H:i:s') + ))->find(); + foreach ($massEmailList as $massEmail) { + try { + $service->createQueue($massEmail); + } catch (\Exception $e) { + $GLOBALS['log']->error('Job ProcessMassEmail#createQueue '.$massEmail->id.': [' . $e->getCode() . '] ' .$e->getMessage()); + } + } + + $massEmailList = $this->getEntityManager()->getRepository('MassEmail')->where(array( + 'status' => 'In Process' + ))->find(); + foreach ($massEmailList as $massEmail) { + try { + $service->processSending($massEmail); + } catch (\Exception $e) { + $GLOBALS['log']->error('Job ProcessMassEmail#processSending '.$massEmail->id.': [' . $e->getCode() . '] ' .$e->getMessage()); + } + } + + return true; + } +} + diff --git a/application/Espo/Modules/Crm/Resources/i18n/en_US/MassEmail.json b/application/Espo/Modules/Crm/Resources/i18n/en_US/MassEmail.json index 5e25c55f69..d31bbed654 100644 --- a/application/Espo/Modules/Crm/Resources/i18n/en_US/MassEmail.json +++ b/application/Espo/Modules/Crm/Resources/i18n/en_US/MassEmail.json @@ -26,7 +26,8 @@ "Pending": "Pending", "In Process": "In Process", "Complete": "Complete", - "Canceled": "Canceled" + "Canceled": "Canceled", + "Failed": "Failed" } }, "labels": { diff --git a/application/Espo/Modules/Crm/Resources/i18n/en_US/ScheduledJob.json b/application/Espo/Modules/Crm/Resources/i18n/en_US/ScheduledJob.json new file mode 100644 index 0000000000..4a71f96bb5 --- /dev/null +++ b/application/Espo/Modules/Crm/Resources/i18n/en_US/ScheduledJob.json @@ -0,0 +1,7 @@ +{ + "options": { + "job": { + "ProcessMassEmail": "Send Mass Emails" + } + } +} diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/EmailQueueItem.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/EmailQueueItem.json index eea1498e33..9fe9d78dc6 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/EmailQueueItem.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/EmailQueueItem.json @@ -15,7 +15,7 @@ "default": 0 }, "target": { - "type": "linkTarget", + "type": "linkParent", "readOnly": true }, "sentAt": { diff --git a/application/Espo/Modules/Crm/Services/MassEmail.php b/application/Espo/Modules/Crm/Services/MassEmail.php index 9e7aae2c9f..4130336e09 100644 --- a/application/Espo/Modules/Crm/Services/MassEmail.php +++ b/application/Espo/Modules/Crm/Services/MassEmail.php @@ -28,6 +28,18 @@ use \Espo\ORM\Entity; class MassEmail extends \Espo\Services\Record { + const MAX_ATTEMPT_COUNT = 3; + + protected function init() + { + $this->dependencies[] = 'container'; + } + + protected function getMailSender() + { + return $this->getInjection('container')->get('mailSender'); + } + protected function beforeCreate(Entity $entity, array $data = array()) { parent::beforeCreate($entity, $data); @@ -35,5 +47,200 @@ class MassEmail extends \Espo\Services\Record throw new Forbidden(); } } + + public function createQueue(Entity $massEmail) + { + $existingQueueItemList = $this->getEntityManager()->getRepository('EmailQueueItem')->where(array( + 'status' => ['Pending', 'Failed'], + 'massEmailId' => $massEmail->id + ))->find(); + foreach ($existingQueueItemList as $existingQueueItem) { + $this->getEntityManager()->getMapper('RDB')->deleteFromDb('EmailQueueItem', $existingQueueItem->id); + } + + $targetHash = array(); + $entityList = []; + + $targetListCollection = $massEmail->get('targetLists'); + foreach ($targetListCollection as $targetList) { + $accountList = $targetList->get('accounts'); + foreach ($accountList as $account) { + $hashId = $account->getEntityType() . '-'. $account->id; + if (!empty($targetHash[$hashId])) { + continue; + } + $entityList[] = $account; + $targetHash[$hashId] = true; + } + $contactList = $targetList->get('contacts'); + foreach ($contactList as $contact) { + $hashId = $contact->getEntityType() . '-'. $contact->id; + if (!empty($targetHash[$hashId])) { + continue; + } + $entityList[] = $contact; + $targetHash[$hashId] = true; + } + $leadList = $targetList->get('leads'); + foreach ($leadList as $lead) { + $hashId = $lead->getEntityType() . '-'. $lead->id; + if (!empty($targetHash[$hashId])) { + continue; + } + $entityList[] = $lead; + $targetHash[$hashId] = true; + } + $userList = $targetList->get('users'); + foreach ($userList as $user) { + $hashId = $user->getEntityType() . '-'. $user->id; + if (!empty($targetHash[$hashId])) { + continue; + } + $entityList[] = $user; + $targetHash[$hashId] = true; + } + } + + foreach ($entityList as $target) { + $queueItem = $this->getEntityManager()->getEntity('EmailQueueItem'); + $queueItem->set(array( + 'massEmailId' => $massEmail->id, + 'status' => 'Pending', + 'targetId' => $target->id, + 'targetType' => $target->getEntityType() + )); + $this->getEntityManager()->saveEntity($queueItem); + } + + $massEmail->set('status', 'In Process'); + + if (empty($entityList)) { + $massEmail->set('status', 'Complete'); + } + + $this->getEntityManager()->saveEntity($massEmail); + } + + protected function setFailed(Entity $massEmail) + { + $massEmail->set('status', 'Failed'); + $this->getEntityManager()->saveEntity($massEmail); + + $queueItemList = $this->getEntityManager()->getRepository('EmailQueueItem')->where(array( + 'status' => 'Pending', + 'massEmailId' => $massEmail->id + ))->find(); + foreach ($queueItemList as $queueItem) { + $queueItem->set('status', 'Failed'); + $this->getEntityManager()->saveEntity($queueItem); + } + } + + public function processSending(Entity $massEmail) + { + $queueItemList = $this->getEntityManager()->getRepository('EmailQueueItem')->where(array( + 'status' => 'Pending', + 'massEmailId' => $massEmail->id + ))->find(); + + $templateId = $massEmail->get('emailTemplateId'); + if (!$templateId) { + $this->setFailed($massEmail); + return; + } + + $emailTemplate = $this->getEntityManager()->getEntity('EmailTemplate', $templateId); + if (!$emailTemplate) { + $this->setFailed($massEmail); + return; + } + $attachmetList = $emailTemplate->get('attachmets'); + + foreach ($queueItemList as $queueItem) { + $this->sendQueueItem($queueItem, $massEmail, $emailTemplate, $attachmetList); + } + + $countLeft = $this->getEntityManager()->getRepository('EmailQueueItem')->where(array( + 'status' => 'Pending', + 'massEmailId' => $massEmail->id + ))->count(); + if ($countLeft == 0) { + $massEmail->set('status', 'Complete'); + $this->getEntityManager()->saveEntity($massEmail); + } + } + + protected function sendQueueItem(Entity $queueItem, Entity $massEmail, Entity $emailTemplate, $attachmetList = []) + { + $target = $this->getEntityManager()->getEntity($queueItem->get('targetType'), $queueItem->get('targetId')); + if (!$target || !$target->id || !$target->get('emailAddress')) { + $queueItem->set('status', 'Failed'); + $this->getEntityManager()->saveEntity($queueItem); + return; + } + + $templateParams = array( + 'parent' => $target + ); + + $emailData = $this->getEmailTemplateService()->parseTemplate($emailTemplate, $templateParams); + + print_r($emailData); + return; + + $email = $this->getEntityManager()->getEntity('Email'); + $email->set($emailData); + $emailAddress = $target->get('emailAddress'); + + if (empty($emailAddress)) { + return false; + } + + $email->set('to', $emailAddress); + + $params = array(); + if ($massEmail->get('fromAddress')) { + $email->set('from', $massEmail->get('fromAddress')); + } + if ($massEmail->get('replyToAddress')) { + $email->set('replyToAddress', $massEmail->get('replyToAddress')); + } + if ($massEmail->get('fromName')) { + $params['fromName'] = $massEmail->get('fromName'); + } + if ($massEmail->get('replyToName')) { + $params['replyToName'] = $massEmail->get('replyToName'); + } + + try { + $attemptCount = $queueItem->get('attemptCount'); + $attemptCount++; + $queueItem->set('attemptCount', $attemptCount); + + $message = false; + $this->getMailSender()->useGlobal()->send($email, $params, $message, $attachmetList); + + $queueItem->set('status', 'Sent'); + $queueItem->set('sentAt', date('Y-m-d H:i:s')); + $this->getEntityManager()->saveEntity($queueItem); + } catch (\Exception $e) { + if ($queueItem->get('attemptCount') >= self::MAX_ATTEMPT_COUNT) { + $queueItem->set('status', 'Failed'); + } + $this->getEntityManager()->saveEntity($queueItem); + $GLOBALS['log']->error('MassEmail#sendQueueItem: [' . $e->getCode() . '] ' .$e->getMessage()); + return false; + } + + return true; + } + + protected function getEmailTemplateService() + { + if (!$this->emailTemplateService) { + $this->emailTemplateService = $this->getServiceFactory()->create('EmailTemplate'); + } + return $this->emailTemplateService; + } } diff --git a/application/Espo/ORM/EntityManager.php b/application/Espo/ORM/EntityManager.php index ac059384eb..7cd74e14fb 100644 --- a/application/Espo/ORM/EntityManager.php +++ b/application/Espo/ORM/EntityManager.php @@ -95,7 +95,7 @@ class EntityManager protected function getMapperClassName($name) { $className = null; - + switch ($name) { case 'RDB': $platform = $this->params['platform']; diff --git a/application/Espo/Services/EmailTemplate.php b/application/Espo/Services/EmailTemplate.php index ec1293cdbb..16c71301f0 100644 --- a/application/Espo/Services/EmailTemplate.php +++ b/application/Espo/Services/EmailTemplate.php @@ -48,20 +48,15 @@ class EmailTemplate extends Record return $this->injections['dateTime']; } - public function parse($id, array $params = array(), $copyAttachments = false) + public function parseTemplate(Entity $emailTemplate, array $params = array(), $copyAttachments = false) { - $emailTemplate = $this->getEntity($id); - if (empty($emailTemplate)) { - throw new NotFound(); - } - - $entityList = array(); + $entityHash = array(); if (!empty($params['entityHash']) && is_array($params['entityHash'])) { - $entityList = $params['entityHash']; + $entityHash = $params['entityHash']; } - if (!isset($entityList['User'])) { - $entityList['User'] = $this->getUser(); + if (!isset($entityHash['User'])) { + $entityHash['User'] = $this->getUser(); } if (!empty($params['emailAddress'])) { @@ -73,33 +68,40 @@ class EmailTemplate extends Record if ($entity) { if ($entity instanceof Person) { - $entityList['Person'] = $entity; + $entityHash['Person'] = $entity; } - if (empty($entityList[$entity->getEntityType()])) { - $entityList[$entity->getEntityType()] = $entity; + if (empty($entityHash[$entity->getEntityType()])) { + $entityHash[$entity->getEntityType()] = $entity; } } } - if (!empty($params['parentId']) && !empty($params['parentType'])) { - $parent = $this->getEntityManager()->getEntity($params['parentType'], $params['parentId']); - if (!empty($parent)) { - $entityList[$params['parentType']] = $parent; - $entityList['Parent'] = $parent; - - if (empty($entityList['Person']) && ($parent instanceof Person)) { - $entityList['Person'] = $parent; + if (empty($params['parent'])) { + if (!empty($params['parentId']) && !empty($params['parentType'])) { + $parent = $this->getEntityManager()->getEntity($params['parentType'], $params['parentId']); + if ($parent) { + $params['parent'] = $parent; } } } + if (!empty($params['parent'])) { + $parent = $params['parent']; + $entityHash[$parent->getEntityType()] = $parent; + $entityHash['Parent'] = $parent; + + if (empty($entityHash['Person']) && ($parent instanceof Person)) { + $entityHash['Person'] = $parent; + } + } + $subject = $emailTemplate->get('subject'); $body = $emailTemplate->get('body'); - foreach ($entityList as $type => $entity) { + foreach ($entityHash as $type => $entity) { $subject = $this->parseText($type, $entity, $subject); } - foreach ($entityList as $type => $entity) { + foreach ($entityHash as $type => $entity) { $body = $this->parseText($type, $entity, $body); } @@ -139,6 +141,16 @@ class EmailTemplate extends Record ); } + public function parse($id, array $params = array(), $copyAttachments = false) + { + $emailTemplate = $this->getEntity($id); + if (empty($emailTemplate)) { + throw new NotFound(); + } + + return $this->parseTemplate($emailTemplate, $params, $copyAttachments); + } + protected function parseText($type, Entity $entity, $text) { $fieldList = array_keys($entity->getFields()); diff --git a/install/core/afterInstall/records.php b/install/core/afterInstall/records.php index 682a814d2c..46cc549860 100644 --- a/install/core/afterInstall/records.php +++ b/install/core/afterInstall/records.php @@ -54,6 +54,12 @@ return array( 'status' => 'Active', 'scheduling' => '1 1 * * 0', ), + 4 => array( + 'name' => 'Send Mass Emails', + 'job' => 'ProcessMassEmail', + 'status' => 'Active', + 'scheduling' => '0 2-5 * * *', + ), ), ); \ No newline at end of file