email import fix 3

This commit is contained in:
yuri
2018-11-16 11:19:18 +02:00
parent d62a83ba0c
commit 603019116b
10 changed files with 340 additions and 197 deletions

View File

@@ -32,7 +32,7 @@ namespace Espo\Core;
class Container
{
private $data = array();
private $data = [];
/**
@@ -222,6 +222,13 @@ class Container
);
}
protected function loadNotificatorFactory()
{
return new \Espo\Core\NotificatorFactory(
$this
);
}
protected function loadMetadata()
{
return new \Espo\Core\Utils\Metadata(

View File

@@ -31,9 +31,9 @@ namespace Espo\Core;
abstract class Injectable implements \Espo\Core\Interfaces\Injectable
{
protected $dependencyList = array();
protected $dependencyList = [];
protected $injections = array();
protected $injections = [];
public function inject($name, $object)
{

View File

@@ -55,4 +55,14 @@ class InjectableFactory
}
throw new Error("Class '$className' does not exist");
}
protected function getMetadata()
{
return $this->getContainer()->get('metadata');
}
protected function getContainer()
{
return $this->container;
}
}

View File

@@ -42,11 +42,14 @@ class Importer
private $filtersMatcher;
public function __construct($entityManager, $config)
private $notificator = null;
public function __construct($entityManager, $config, $notificator = null)
{
$this->entityManager = $entityManager;
$this->config = $config;
$this->filtersMatcher = new FiltersMatcher();
$this->notificator = $notificator;
}
protected function getEntityManager()
@@ -64,6 +67,11 @@ class Importer
return $this->filtersMatcher;
}
protected function getNotificator()
{
return $this->notificator;
}
public function importMessage($parserType = 'ZendMail', $message, $assignedUserId = null, $teamsIdList = [], $userIdList = [], $filterList = [], $fetchOnlyHeader = false, $folderData = null)
{
$parser = $message->getParser();
@@ -153,10 +161,14 @@ class Importer
}
}
$duplicate = null;
if ($duplicate = $this->findDuplicate($email)) {
$duplicate = $this->getEntityManager()->getEntity('Email', $duplicate->id);
$this->processDuplicate($duplicate, $assignedUserId, $userIdList, $folderData, $teamsIdList);
return $duplicate;
if ($duplicate->get('status') != 'Being Imported') {
$duplicate = $this->getEntityManager()->getEntity('Email', $duplicate->id);
$this->processDuplicate($duplicate, $assignedUserId, $userIdList, $folderData, $teamsIdList);
return $duplicate;
}
}
if ($parser->checkMessageAttribute($message, 'date')) {
@@ -280,11 +292,34 @@ class Importer
}
}
$this->getEntityManager()->getPdo()->query('LOCK TABLES `email` WRITE');
if (!$duplicate) {
$this->lockEmailTable();
if ($duplicate = $this->findDuplicate($email)) {
$this->unlockTables();
if ($duplicate->get('status') != 'Being Imported') {
$duplicate = $this->getEntityManager()->getEntity('Email', $duplicate->id);
$this->processDuplicate($duplicate, $assignedUserId, $userIdList, $folderData, $teamsIdList);
return $duplicate;
}
}
}
if ($duplicate) {
$duplicate->set([
'from' => $email->get('from'),
'to' => $email->get('to'),
'cc' => $email->get('cc'),
'bcc' => $email->get('bcc'),
'replyTo' => $email->get('replyTo'),
'name' => $email->get('name'),
'dateSent' => $email->get('dateSent'),
'body' => $email->get('body'),
'bodyPlain' => $email->get('name'),
'parentType' => $email->get('parentType'),
'parentId' => $email->get('parentId')
]);
$this->getEntityManager()->getRepository('Email')->fillAccount($duplicate);
if ($duplicate = $this->findDuplicate($email)) {
$this->getEntityManager()->getPdo()->query('UNLOCK TABLES');
$duplicate = $this->getEntityManager()->getEntity('Email', $duplicate->id);
$this->processDuplicate($duplicate, $assignedUserId, $userIdList, $folderData, $teamsIdList);
return $duplicate;
}
@@ -292,13 +327,14 @@ class Importer
if (!$email->get('messageId')) {
$email->setDummyMessageId();
}
$email->set('status', 'Being Imported');
$this->getEntityManager()->saveEntity($email, [
'skipAll' => true,
'keepNew' => true
]);
$this->getEntityManager()->getPdo()->query('UNLOCK TABLES');
$this->unlockTables();
if ($parentFound) {
$parentType = $email->get('parentType');
@@ -317,6 +353,8 @@ class Importer
}
}
$email->set('status', 'Archived');
$this->getEntityManager()->saveEntity($email, [
'isBeingImported' => true
]);
@@ -332,6 +370,16 @@ class Importer
return $email;
}
protected function lockEmailTable()
{
$this->getEntityManager()->getPdo()->query('LOCK TABLES `email` WRITE');
}
protected function unlockTables()
{
$this->getEntityManager()->getPdo()->query('UNLOCK TABLES');
}
protected function findParent(Entity $email, $emailAddress)
{
$contact = $this->getEntityManager()->getRepository('Contact')->where(array(
@@ -415,13 +463,24 @@ class Importer
$duplicate->set('isBeingImported', true);
$this->getEntityManager()->saveEntity($duplicate, [
$this->getEntityManager()->getRepository('Email')->applyUsersFilters($duplicate);
$this->getEntityManager()->getRepository('Email')->processLinkMultipleFieldSave($duplicate, 'users', [
'skipLinkMultipleRemove' => true,
'skipLinkMultipleUpdate' => true,
'isBeingImported' => true,
'isDuplicate' => true
'skipLinkMultipleUpdate' => true
]);
$this->getEntityManager()->getRepository('Email')->processLinkMultipleFieldSave($duplicate, 'assignedUsers', [
'skipLinkMultipleRemove' => true,
'skipLinkMultipleUpdate' => true
]);
if ($notificator = $this->getNotificator()) {
$notificator->process($duplicate, [
'isBeingImported' => true
]);
}
if (!empty($teamsIdList)) {
foreach ($teamsIdList as $teamId) {
$this->getEntityManager()->getRepository('Email')->relate($duplicate, 'teams', $teamId);

View File

@@ -0,0 +1,58 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core;
use \Espo\Core\Exceptions\Error;
use \Espo\Core\Utils\Util;
use \Espo\Core\InjectableFactory;
class NotificatorFactory extends InjectableFactory
{
public function create($entityType)
{
$normalizedName = Util::normilizeClassName($entityType);
$className = '\\Espo\\Custom\\Notificators\\' . $normalizedName;
if (!class_exists($className)) {
$moduleName = $this->getMetadata()->getScopeModuleName($entityType);
if ($moduleName) {
$className = '\\Espo\\Modules\\' . $moduleName . '\\Notificators\\' . $normalizedName;
} else {
$className = '\\Espo\\Notificators\\' . $normalizedName;
}
if (!class_exists($className)) {
$className = '\\Espo\\Core\\Notificators\\Base';
}
}
return $this->createByClassName($className);
}
}

View File

@@ -454,6 +454,134 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
}
}
public function processLinkMultipleFieldSave(Entity $entity, $link, array $options = [])
{
$name = $link;
$idListAttribute = $link . 'Ids';
$columnsAttribute = $link . 'Columns';
if ($this->getMetadata()->get("entityDefs." . $entity->getEntityType() . ".fields.{$name}.noSave")) {
return;
}
$skipCreate = false;
$skipRemove = false;
$skipUpdate = false;
if (!empty($options['skipLinkMultipleCreate'])) $skipCreate = true;
if (!empty($options['skipLinkMultipleRemove'])) $skipRemove = true;
if (!empty($options['skipLinkMultipleUpdate'])) $skipUpdate = true;
if ($entity->isNew()) {
$skipRemove = true;
}
if ($entity->has($idListAttribute)) {
$specifiedIdList = $entity->get($idListAttribute);
} else if ($entity->has($columnsAttribute)) {
$skipRemove = true;
$specifiedIdList = [];
foreach ($entity->get($columnsAttribute) as $id => $d) {
$specifiedIdList[] = $id;
}
} else {
return;
}
if (!is_array($specifiedIdList)) return;
$toRemoveIdList = [];
$existingIdList = [];
$toUpdateIdList = [];
$toCreateIdList = [];
$existingColumnsData = (object)[];
$defs = [];
$columns = $this->getMetadata()->get("entityDefs." . $entity->getEntityType() . ".fields.{$name}.columns");
if (!empty($columns)) {
$columnData = $entity->get($columnsAttribute);
$defs['additionalColumns'] = $columns;
}
$foreignEntityList = $entity->get($name, $defs);
if ($foreignEntityList) {
foreach ($foreignEntityList as $foreignEntity) {
$existingIdList[] = $foreignEntity->id;
if (!empty($columns)) {
$data = (object)[];
foreach ($columns as $columnName => $columnField) {
$foreignId = $foreignEntity->id;
$data->$columnName = $foreignEntity->get($columnField);
}
$existingColumnsData->$foreignId = $data;
if (!$entity->isNew()) {
$entity->setFetched($columnsAttribute, $existingColumnsData);
}
}
}
}
if (!$entity->isNew()) {
if ($entity->has($idListAttribute) && !$entity->hasFetched($idListAttribute)) {
$entity->setFetched($idListAttribute, $existingIdList);
}
if ($entity->has($columnsAttribute) && !empty($columns)) {
$entity->setFetched($columnsAttribute, $existingColumnsData);
}
}
foreach ($existingIdList as $id) {
if (!in_array($id, $specifiedIdList)) {
if (!$skipRemove) {
$toRemoveIdList[] = $id;
}
} else {
if (!$skipUpdate && !empty($columns)) {
foreach ($columns as $columnName => $columnField) {
if (isset($columnData->$id) && is_object($columnData->$id)) {
if (
property_exists($columnData->$id, $columnName)
&&
(
!property_exists($existingColumnsData->$id, $columnName)
||
$columnData->$id->$columnName !== $existingColumnsData->$id->$columnName
)
) {
$toUpdateIdList[] = $id;
}
}
}
}
}
}
if (!$skipCreate) {
foreach ($specifiedIdList as $id) {
if (!in_array($id, $existingIdList)) {
$toCreateIdList[] = $id;
}
}
}
foreach ($toCreateIdList as $id) {
$data = null;
if (!empty($columns) && isset($columnData->$id)) {
$data = $columnData->$id;
}
$this->relate($entity, $name, $id, $data);
}
foreach ($toRemoveIdList as $id) {
$this->unrelate($entity, $name, $id);
}
foreach ($toUpdateIdList as $id) {
$data = $columnData->$id;
$this->updateRelation($entity, $name, $id, $data);
}
}
protected function processSpecifiedRelationsSave(Entity $entity, array $options = [])
{
$relationTypeList = [$entity::HAS_MANY, $entity::MANY_MANY, $entity::HAS_CHILDREN];
@@ -461,128 +589,8 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
if (in_array($defs['type'], $relationTypeList)) {
$idListAttribute = $name . 'Ids';
$columnsAttribute = $name . 'Columns';
if ($entity->has($idListAttribute) || $entity->has($columnsAttribute)) {
if ($this->getMetadata()->get("entityDefs." . $entity->getEntityType() . ".fields.{$name}.noSave")) {
continue;
}
$skipCreate = false;
$skipRemove = false;
$skipUpdate = false;
if (!empty($options['skipLinkMultipleCreate'])) $skipCreate = true;
if (!empty($options['skipLinkMultipleRemove'])) $skipRemove = true;
if (!empty($options['skipLinkMultipleUpdate'])) $skipUpdate = true;
if ($entity->isNew()) {
$skipRemove = true;
}
if ($entity->has($idListAttribute)) {
$specifiedIdList = $entity->get($idListAttribute);
} else if ($entity->has($columnsAttribute)) {
$skipRemove = true;
$specifiedIdList = [];
foreach ($entity->get($columnsAttribute) as $id => $d) {
$specifiedIdList[] = $id;
}
} else {
continue;
}
if (!is_array($specifiedIdList)) continue;
$toRemoveIdList = [];
$existingIdList = [];
$toUpdateIdList = [];
$toCreateIdList = [];
$existingColumnsData = (object)[];
$defs = [];
$columns = $this->getMetadata()->get("entityDefs." . $entity->getEntityType() . ".fields.{$name}.columns");
if (!empty($columns)) {
$columnData = $entity->get($columnsAttribute);
$defs['additionalColumns'] = $columns;
}
$foreignEntityList = $entity->get($name, $defs);
if ($foreignEntityList) {
foreach ($foreignEntityList as $foreignEntity) {
$existingIdList[] = $foreignEntity->id;
if (!empty($columns)) {
$data = (object)[];
foreach ($columns as $columnName => $columnField) {
$foreignId = $foreignEntity->id;
$data->$columnName = $foreignEntity->get($columnField);
}
$existingColumnsData->$foreignId = $data;
if (!$entity->isNew()) {
$entity->setFetched($columnsAttribute, $existingColumnsData);
}
}
}
}
if (!$entity->isNew()) {
if ($entity->has($idListAttribute) && !$entity->hasFetched($idListAttribute)) {
$entity->setFetched($idListAttribute, $existingIdList);
}
if ($entity->has($columnsAttribute) && !empty($columns)) {
$entity->setFetched($columnsAttribute, $existingColumnsData);
}
}
foreach ($existingIdList as $id) {
if (!in_array($id, $specifiedIdList)) {
if (!$skipRemove) {
$toRemoveIdList[] = $id;
}
} else {
if (!$skipUpdate && !empty($columns)) {
foreach ($columns as $columnName => $columnField) {
if (isset($columnData->$id) && is_object($columnData->$id)) {
if (
property_exists($columnData->$id, $columnName)
&&
(
!property_exists($existingColumnsData->$id, $columnName)
||
$columnData->$id->$columnName !== $existingColumnsData->$id->$columnName
)
) {
$toUpdateIdList[] = $id;
}
}
}
}
}
}
if (!$skipCreate) {
foreach ($specifiedIdList as $id) {
if (!in_array($id, $existingIdList)) {
$toCreateIdList[] = $id;
}
}
}
foreach ($toCreateIdList as $id) {
$data = null;
if (!empty($columns) && isset($columnData->$id)) {
$data = $columnData->$id;
}
$this->relate($entity, $name, $id, $data);
}
foreach ($toRemoveIdList as $id) {
$this->unrelate($entity, $name, $id);
}
foreach ($toUpdateIdList as $id) {
$data = $columnData->$id;
$this->updateRelation($entity, $name, $id, $data);
}
$this->processLinkMultipleFieldSave($entity, $name, $options);
}
} else if ($defs['type'] === $entity::HAS_ONE) {
if (empty($defs['entity']) || empty($defs['foreignKey'])) continue;

View File

@@ -47,6 +47,11 @@ class Notifications extends \Espo\Core\Hooks\Base
return $this->getContainer()->get('serviceFactory');
}
protected function getNotificatorFactory()
{
return $this->getContainer()->get('notificatorFactory');
}
protected function checkHasStream($entityType)
{
if (!array_key_exists($entityType, $this->hasStreamCache)) {
@@ -58,27 +63,7 @@ class Notifications extends \Espo\Core\Hooks\Base
protected function getNotificator($entityType)
{
if (empty($this->notifatorsHash[$entityType])) {
$normalizedName = Util::normilizeClassName($entityType);
$className = '\\Espo\\Custom\\Notificators\\' . $normalizedName;
if (!class_exists($className)) {
$moduleName = $this->getMetadata()->getScopeModuleName($entityType);
if ($moduleName) {
$className = '\\Espo\\Modules\\' . $moduleName . '\\Notificators\\' . $normalizedName;
} else {
$className = '\\Espo\\Notificators\\' . $normalizedName;
}
if (!class_exists($className)) {
$className = '\\Espo\\Core\\Notificators\\Base';
}
}
$notificator = new $className();
$dependencies = $notificator->getDependencyList();
foreach ($dependencies as $name) {
$notificator->inject($name, $this->getContainer()->get($name));
}
$notificator = $this->getNotificatorFactory()->create($entityType);
$this->notifatorsHash[$entityType] = $notificator;
}
return $this->notifatorsHash[$entityType];
@@ -150,6 +135,4 @@ class Notifications extends \Espo\Core\Hooks\Base
}
return $this->streamService;
}
}

View File

@@ -275,30 +275,8 @@ class Email extends \Espo\Core\ORM\Repositories\RDB
$entity->setLinkMultipleColumn('users', 'isRead', $entity->get('createdById'), true);
}
if (!$entity->isNew() && $entity->isAttributeChanged('parentId')) {
$entity->set('accountId', null);
}
$parentId = $entity->get('parentId');
$parentType = $entity->get('parentType');
if ($parentId && $parentType) {
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
if ($parent) {
$accountId = null;
if ($parent->getEntityType() == 'Account') {
$accountId = $parent->id;
}
if (!$accountId && $parent->get('accountId') && $parent->getRelationParam('account', 'entity') == 'Account') {
$accountId = $parent->get('accountId');
}
if ($accountId) {
$account = $this->getEntityManager()->getEntity('Account', $accountId);
if ($account) {
$entity->set('accountId', $accountId);
$entity->set('accountName', $account->get('name'));
}
}
}
if ($entity->isNew() || $entity->isAttributeChanged('parentId')) {
$this->fillAccount($entity);
}
}
@@ -311,17 +289,51 @@ class Email extends \Espo\Core\ORM\Repositories\RDB
$this->loadToField($entity);
}
}
foreach ($entity->getLinkMultipleIdList('users') as $userId) {
$filter = $this->getEmailFilterManager()->getMatchingFilter($entity, $userId);
if ($filter) {
$action = $filter->get('action');
if ($action === 'Skip') {
$entity->setLinkMultipleColumn('users', 'inTrash', $userId, true);
} else if ($action === 'Move to Folder') {
$folderId = $filter->get('emailFolderId');
if ($folderId) {
$entity->setLinkMultipleColumn('users', 'folderId', $userId, $folderId);
}
$this->applyUsersFilters($entity);
}
}
public function fillAccount(Entity $entity)
{
if (!$entity->isNew()) {
$entity->set('accountId', null);
}
$parentId = $entity->get('parentId');
$parentType = $entity->get('parentType');
if ($parentId && $parentType) {
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
if ($parent) {
$accountId = null;
if ($parent->getEntityType() == 'Account') {
$accountId = $parent->id;
}
if (!$accountId && $parent->get('accountId') && $parent->getRelationParam('account', 'entity') == 'Account') {
$accountId = $parent->get('accountId');
}
if ($accountId) {
$account = $this->getEntityManager()->getEntity('Account', $accountId);
if ($account) {
$entity->set('accountId', $accountId);
$entity->set('accountName', $account->get('name'));
}
}
}
}
}
public function applyUsersFilters(Entity $entity)
{
foreach ($entity->getLinkMultipleIdList('users') as $userId) {
$filter = $this->getEmailFilterManager()->getMatchingFilter($entity, $userId);
if ($filter) {
$action = $filter->get('action');
if ($action === 'Skip') {
$entity->setLinkMultipleColumn('users', 'inTrash', $userId, true);
} else if ($action === 'Move to Folder') {
$folderId = $filter->get('emailFolderId');
if ($folderId) {
$entity->setLinkMultipleColumn('users', 'folderId', $userId, $folderId);
}
}
}

View File

@@ -47,6 +47,7 @@ class EmailAccount extends Record
{
parent::init();
$this->addDependency('crypt');
$this->addDependency('notificatorFactory');
}
protected function getCrypt()
@@ -176,7 +177,9 @@ class EmailAccount extends Record
throw new Error("Email Account {$emailAccount->id} is not active.");
}
$importer = new \Espo\Core\Mail\Importer($this->getEntityManager(), $this->getConfig());
$notificator = $this->getInjection('notificatorFactory')->create('Email');
$importer = new \Espo\Core\Mail\Importer($this->getEntityManager(), $this->getConfig(), $notificator);
$maxSize = $this->getConfig()->get('emailMessageMaxSize');

View File

@@ -76,6 +76,7 @@ class InboundEmail extends \Espo\Services\Record
$this->addDependency('mailSender');
$this->addDependency('crypt');
$this->addDependency('notificatorFactory');
}
protected function getMailSender()
@@ -159,7 +160,9 @@ class InboundEmail extends \Espo\Services\Record
throw new Error("Group Email Account {$emailAccount->id} is not active.");
}
$importer = new \Espo\Core\Mail\Importer($this->getEntityManager(), $this->getConfig());
$notificator = $this->getInjection('notificatorFactory')->create('Email');
$importer = new \Espo\Core\Mail\Importer($this->getEntityManager(), $this->getConfig(), $notificator);
$maxSize = $this->getConfig()->get('emailMessageMaxSize');