diff --git a/application/Espo/Core/Mail/FiltersMatcher.php b/application/Espo/Core/Mail/FiltersMatcher.php new file mode 100644 index 0000000000..bbe686b534 --- /dev/null +++ b/application/Espo/Core/Mail/FiltersMatcher.php @@ -0,0 +1,94 @@ +get('from')) { + if (strtolower($filter->get('from')) === strtolower($email->get('from'))) { + return true; + } + } + if ($filter->get('to')) { + if ($email->get('to')) { + $toArr = explode(';', $email->get('to')); + foreach ($toArr as $to) { + if (strtolower($to) === strtolower($filter->get('to'))) { + return true; + } + } + } + } + if ($filter->get('subject')) { + if ($this->matchString($filter->get('subject'), $email->get('name'))) { + return true; + } + } + } + return false; + } + + public function matchBody(Email $email, $filterList = []) + { + foreach ($filterList as $filter) { + if ($filter->get('bodyContains')) { + $phraseList = $filter->get('bodyContains'); + $body = $email->get('body'); + $bodyPlain = $email->get('bodyPlain'); + foreach ($phraseList as $phrase) { + if (stripos($bodyPlain, $phrase) !== false) { + return true; + } + if (stripos($body, $phrase) !== false) { + return true; + } + } + } + } + return false; + } + + protected function matchString($pattern, $value) + { + if ($pattern == $value) { + return true; + } + $pattern = preg_quote($pattern, '#'); + $pattern = str_replace('\*', '.*', $pattern).'\z'; + if (preg_match('#^'.$pattern.'#', $value)) { + return true; + } + return false; + } +} diff --git a/application/Espo/Core/Mail/Importer.php b/application/Espo/Core/Mail/Importer.php index b44c1dbd7c..d73545e735 100644 --- a/application/Espo/Core/Mail/Importer.php +++ b/application/Espo/Core/Mail/Importer.php @@ -25,6 +25,7 @@ namespace Espo\Core\Mail; use \Zend\Mime\Mime as Mime; use \Espo\ORM\Entity; +use \Espo\ORM\Email; class Importer { @@ -34,11 +35,14 @@ class Importer private $config; + private $filtersMatcher; + public function __construct($entityManager, $fileManager, $config) { $this->entityManager = $entityManager; $this->fileManager = $fileManager; $this->config = $config; + $this->filtersMatcher = new FiltersMatcher(); } protected function getEntityManager() @@ -55,7 +59,12 @@ class Importer return $this->fileManager; } - public function importMessage($message, $userId, $teamsIds = array()) + protected function getFiltersMatcher() + { + return $this->filtersMatcher; + } + + public function importMessage($message, $userId, $teamsIds = [], $filterList = []) { try { $email = $this->getEntityManager()->getEntity('Email'); @@ -87,6 +96,10 @@ class Importer $email->set('to', implode(';', $toArr)); $email->set('cc', implode(';', $ccArr)); + if ($this->getFiltersMatcher()->match($email, $filterList)) { + return false; + } + if (isset($message->messageId) && !empty($message->messageId)) { $email->set('messageId', $message->messageId); if (isset($message->deliveredTo)) { @@ -149,6 +162,10 @@ class Importer $email->set('body', $body); } + if ($this->getFiltersMatcher()->matchBody($email, $filterList)) { + return false; + } + $parentFound = false; if (isset($message->references) && !empty($message->references)) { diff --git a/application/Espo/Resources/i18n/en_US/EmailFilter.json b/application/Espo/Resources/i18n/en_US/EmailFilter.json index 055ae85456..b801377d9f 100644 --- a/application/Espo/Resources/i18n/en_US/EmailFilter.json +++ b/application/Espo/Resources/i18n/en_US/EmailFilter.json @@ -10,7 +10,7 @@ }, "tooltips": { "name": "Just a name of the filter.", - "subject": "Use wildcard %:\n\ntext% - starts with text,\n%text% - contains text,\n%text - ends with text.", + "subject": "Use wildcard *:\n\ntext* - starts with text,\n*text* - contains text,\n*text - ends with text.", "bodyContains": "Body of email contains any of specified words or phrases.", "from": "Emails being sent from the specified address. Leave empty if not needed.", "to": "Emails being sent to the specified address. Leave empty if not needed.", diff --git a/application/Espo/Services/EmailAccount.php b/application/Espo/Services/EmailAccount.php index edd974362b..c1eff8fc17 100644 --- a/application/Espo/Services/EmailAccount.php +++ b/application/Espo/Services/EmailAccount.php @@ -176,7 +176,6 @@ class EmailAccount extends Record $maxSize = $this->getConfig()->get('emailMessageMaxSize'); $user = $this->getEntityManager()->getEntity('User', $emailAccount->get('assignedUserId')); - if (!$user) { throw new Error(); } @@ -188,6 +187,18 @@ class EmailAccount extends Record $teamIds[] = $teamId; } + $filterCollection = $this->getEntityManager()->getRepository('EmailFilter')->where([ + 'OR' => [ + [ + 'parentType' => $emailAccount->getEntityType(), + 'parentId' => $emailAccount->id + ], + [ + 'parentId' => null + ] + ] + ])->find(); + $fetchData = json_decode($emailAccount->get('fetchData'), true); if (empty($fetchData)) { $fetchData = array(); @@ -263,7 +274,7 @@ class EmailAccount extends Record $flags = $message->getFlags(); } try { - $email = $importer->importMessage($message, $userId, $teamIds); + $email = $importer->importMessage($message, $userId, $teamIds, $filterCollection); } catch (\Exception $e) { $GLOBALS['log']->error('EmailAccount '.$emailAccount->id.' (Import Message): [' . $e->getCode() . '] ' .$e->getMessage()); } diff --git a/application/Espo/Services/InboundEmail.php b/application/Espo/Services/InboundEmail.php index 3bba6f434e..80661c4589 100644 --- a/application/Espo/Services/InboundEmail.php +++ b/application/Espo/Services/InboundEmail.php @@ -142,9 +142,9 @@ class InboundEmail extends \Espo\Services\Record throw new Error(); } - public function fetchFromMailServer(Entity $inboundEmail) + public function fetchFromMailServer(Entity $emailAccount) { - if ($inboundEmail->get('status') != 'Active') { + if ($emailAccount->get('status') != 'Active') { throw new Error(); } @@ -152,17 +152,29 @@ class InboundEmail extends \Espo\Services\Record $maxSize = $this->getConfig()->get('emailMessageMaxSize'); - $teamId = $inboundEmail->get('teamId'); + $teamId = $emailAccount->get('teamId'); $userId = $this->getUser()->id; - if ($inboundEmail->get('assignToUserId')) { - $userId = $inboundEmail->get('assignToUserId'); + if ($emailAccount->get('assignToUserId')) { + $userId = $emailAccount->get('assignToUserId'); } $teamIds = array(); if (!empty($teamId)) { $teamIds[] = $teamId; } - $fetchData = json_decode($inboundEmail->get('fetchData'), true); + $filterCollection = $this->getEntityManager()->getRepository('EmailFilter')->where([ + 'OR' => [ + [ + 'parentType' => $emailAccount->getEntityType(), + 'parentId' => $emailAccount->id + ], + [ + 'parentId' => null + ] + ] + ])->find(); + + $fetchData = json_decode($emailAccount->get('fetchData'), true); if (empty($fetchData)) { $fetchData = array(); } @@ -174,19 +186,19 @@ class InboundEmail extends \Espo\Services\Record } $imapParams = array( - 'host' => $inboundEmail->get('host'), - 'port' => $inboundEmail->get('port'), - 'user' => $inboundEmail->get('username'), - 'password' => $this->getCrypt()->decrypt($inboundEmail->get('password')), + 'host' => $emailAccount->get('host'), + 'port' => $emailAccount->get('port'), + 'user' => $emailAccount->get('username'), + 'password' => $this->getCrypt()->decrypt($emailAccount->get('password')), ); - if ($inboundEmail->get('ssl')) { + if ($emailAccount->get('ssl')) { $imapParams['ssl'] = 'SSL'; } $storage = new \Espo\Core\Mail\Mail\Storage\Imap($imapParams); - $monitoredFolders = $inboundEmail->get('monitoredFolders'); + $monitoredFolders = $emailAccount->get('monitoredFolders'); if (empty($monitoredFolders)) { $monitoredFolders = 'INBOX'; } @@ -198,7 +210,7 @@ class InboundEmail extends \Espo\Services\Record try { $storage->selectFolder($folder); } catch (\Exception $e) { - $GLOBALS['log']->error('InboundEmail '.$inboundEmail->id.' (Select Folder) [' . $e->getCode() . '] ' .$e->getMessage()); + $GLOBALS['log']->error('InboundEmail '.$emailAccount->id.' (Select Folder) [' . $e->getCode() . '] ' .$e->getMessage()); continue; } @@ -246,26 +258,26 @@ class InboundEmail extends \Espo\Services\Record } if (!$toSkip) { try { - $email = $importer->importMessage($message, $userId, $teamIds); + $email = $importer->importMessage($message, $userId, $teamIds, $filterCollection); } catch (\Exception $e) { - $GLOBALS['log']->error('InboundEmail '.$inboundEmail->id.' (Import Message): [' . $e->getCode() . '] ' .$e->getMessage()); + $GLOBALS['log']->error('InboundEmail '.$emailAccount->id.' (Import Message): [' . $e->getCode() . '] ' .$e->getMessage()); } } } catch (\Exception $e) { - $GLOBALS['log']->error('InboundEmail '.$inboundEmail->id.' (Get Message): [' . $e->getCode() . '] ' .$e->getMessage()); + $GLOBALS['log']->error('InboundEmail '.$emailAccount->id.' (Get Message): [' . $e->getCode() . '] ' .$e->getMessage()); } if (!empty($email)) { - if (!$inboundEmail->get('createCase')) { + if (!$emailAccount->get('createCase')) { $this->noteAboutEmail($email); } - if ($inboundEmail->get('createCase')) { - $this->createCase($inboundEmail, $email); + if ($emailAccount->get('createCase')) { + $this->createCase($emailAccount, $email); } else { - if ($inboundEmail->get('reply')) { + if ($emailAccount->get('reply')) { $user = $this->getEntityManager()->getEntity('User', $userId); - $this->autoReply($inboundEmail, $email, $user); + $this->autoReply($emailAccount, $email, $user); } } } @@ -290,8 +302,8 @@ class InboundEmail extends \Espo\Services\Record $fetchData['lastUID'][$folder] = $lastUID; $fetchData['lastDate'][$folder] = $lastDate; - $inboundEmail->set('fetchData', json_encode($fetchData)); - $this->getEntityManager()->saveEntity($inboundEmail, array('silent' => true)); + $emailAccount->set('fetchData', json_encode($fetchData)); + $this->getEntityManager()->saveEntity($emailAccount, array('silent' => true)); } return true; diff --git a/frontend/client/src/acl.js b/frontend/client/src/acl.js index 8e25dd4866..b1d97b9f5b 100644 --- a/frontend/client/src/acl.js +++ b/frontend/client/src/acl.js @@ -135,7 +135,33 @@ Espo.define('acl', [], function () { return model.get('isRemovable'); } } - return this.check(model.name, action, this.user.isOwner(model), this.user.inTeam(model)); + return this.check(model.name, action, this.checkIsOwner(model), this.checkInTeam(model)); + }, + + checkIsOwner: function (model) { + var result = this.user.id === model.get('assignedUserId') || this.user.id === model.get('createdById'); + if (!result) { + if (!model.hasField('assignedUser') && !model.hasField('createdBy')) { + return true; + } + } + return result; + }, + + checkInTeam: function (model) { + var userTeamIds = this.user.getTeamIds(); + + if (model.name == 'Team') { + return (userTeamIds.indexOf(model.id) != -1); + } else { + var teamIds = model.getTeamIds(); + for (var i in userTeamIds) { + if (teamIds.indexOf(i) != -1) { + return true; + } + } + } + return false; }, clear: function () { diff --git a/frontend/client/src/models/user.js b/frontend/client/src/models/user.js index 2b64402778..dc28e41553 100644 --- a/frontend/client/src/models/user.js +++ b/frontend/client/src/models/user.js @@ -26,28 +26,7 @@ Espo.define('models/user', 'model', function (Dep) { isAdmin: function () { return this.get('isAdmin'); - }, - - isOwner: function (model) { - return this.id === model.get('assignedUserId') || this.id === model.get('createdById'); - }, - - inTeam: function (model) { - - var userTeamIds = this.getTeamIds(); - - if (model.name == 'Team') { - return (userTeamIds.indexOf(model.id) != -1); - } else { - var teamIds = model.getTeamIds(); - for (var i in userTeamIds) { - if (teamIds.indexOf(i) != -1) { - return true; - } - } - } - return false; - }, + } }); diff --git a/tests/Espo/Core/Mail/FiltersMatcherTest.php b/tests/Espo/Core/Mail/FiltersMatcherTest.php new file mode 100644 index 0000000000..05969e2802 --- /dev/null +++ b/tests/Espo/Core/Mail/FiltersMatcherTest.php @@ -0,0 +1,131 @@ +object = new \Espo\Core\Mail\FiltersMatcher(); + + $this->emailDefs = array( + 'fields' => array( + 'from' => array( + 'type' => 'varchar' + ), + 'to' => array( + 'type' => 'varchar' + ), + 'name' => array( + 'type' => 'varchar' + ), + 'subject' => array( + 'type' => 'varchar' + ), + 'body' => array( + 'type' => 'text' + ), + 'bodyPlain' => array( + 'type' => 'text' + ) + ) + ); + + $this->filterDefs = array( + 'fields' => array( + 'from' => array( + 'type' => 'varchar' + ), + 'to' => array( + 'type' => 'varchar' + ), + 'subject' => array( + 'type' => 'varchar' + ), + 'bodyContains' => array( + 'type' => 'jsonArray' + ) + ) + ); + } + + protected function tearDown() + { + $this->object = NULL; + } + + function testMatch() + { + $email = new \Espo\Entities\Email($this->emailDefs); + $email->set('from', 'test@tester'); + $filter = new \Espo\Entities\EmailFilter($this->filterDefs); + $filter->set(array( + 'from' => 'test@tester' + )); + $filterList = [$filter]; + $this->assertTrue($this->object->match($email, $filterList)); + + $email->set('from', 'test@tester'); + $email->set('to', 'test@tester;baraka@tester'); + $filter = new \Espo\Entities\EmailFilter($this->filterDefs); + $filter->set(array( + 'to' => 'baraka@tester' + )); + $filterList = [$filter]; + $this->assertTrue($this->object->match($email, $filterList)); + + $email->set('subject', 'test hello man'); + $filter = new \Espo\Entities\EmailFilter($this->filterDefs); + $filter->set(array( + 'subject' => '*hello*' + )); + $filterList = [$filter]; + $this->assertTrue($this->object->match($email, $filterList)); + + $email->set('name', 'test hello man'); + $filter = new \Espo\Entities\EmailFilter($this->filterDefs); + $filter->set(array( + 'subject' => 'hello' + )); + $filterList = [$filter]; + $this->assertFalse($this->object->match($email, $filterList)); + + } + + function testMatchBody() + { + $email = new \Espo\Entities\Email($this->emailDefs); + $email->set('body', 'hello Man tester'); + $filter = new \Espo\Entities\EmailFilter($this->filterDefs); + $filter->set(array( + 'bodyContains' => ['man', 'red'] + )); + $filterList = [$filter]; + $this->assertTrue($this->object->matchBody($email, $filterList)); + } + +}