From 3e76a51c4ba2a141d3a0ef148e4b039ae91faf17 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Sat, 4 Apr 2020 11:42:15 +0300 Subject: [PATCH 1/4] css less border shadow --- frontend/less/hazyblue-vertical/custom.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/less/hazyblue-vertical/custom.less b/frontend/less/hazyblue-vertical/custom.less index 504bff9e42..cf4ecfc0ae 100644 --- a/frontend/less/hazyblue-vertical/custom.less +++ b/frontend/less/hazyblue-vertical/custom.less @@ -94,7 +94,7 @@ table.table { .list-kanban > div > table > thead, .list-group-panel, .popup-notification { - .box-shadow(2px 2px 4px rgba(0,0,0,0.13)); + .box-shadow(2px 2px 4px rgba(0,0,0,0.09)); } .stick-sub.button-container { From af1fc53e10ca4d7a87f5304cd54233739c257922 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Mon, 6 Apr 2020 10:36:01 +0300 Subject: [PATCH 2/4] generate keys ids improvements --- application/Espo/Core/DataManager.php | 2 +- application/Espo/Core/Utils/Crypt.php | 2 +- application/Espo/Core/Utils/Util.php | 25 ++++++++++++++-------- application/Espo/Entities/UniqueId.php | 6 ++++-- application/Espo/Repositories/UniqueId.php | 2 +- application/Espo/Repositories/Webhook.php | 2 +- application/Espo/Services/User.php | 8 +++---- 7 files changed, 28 insertions(+), 19 deletions(-) diff --git a/application/Espo/Core/DataManager.php b/application/Espo/Core/DataManager.php index cf482f8d7b..cad516b815 100644 --- a/application/Espo/Core/DataManager.php +++ b/application/Espo/Core/DataManager.php @@ -219,7 +219,7 @@ class DataManager $cryptKey = $config->get('cryptKey'); if (!$cryptKey) { - $cryptKey = \Espo\Core\Utils\Util::generateKey(); + $cryptKey = \Espo\Core\Utils\Util::generateSecretKey(); $config->set('cryptKey', $cryptKey); } diff --git a/application/Espo/Core/Utils/Crypt.php b/application/Espo/Core/Utils/Crypt.php index 1f3cfd7bfd..2e92e91d7c 100644 --- a/application/Espo/Core/Utils/Crypt.php +++ b/application/Espo/Core/Utils/Crypt.php @@ -97,6 +97,6 @@ class Crypt public function generateKey() { - return \Espo\Core\Utils\Util::generateKey(); + return \Espo\Core\Utils\Util::generateSecretKey(); } } diff --git a/application/Espo/Core/Utils/Util.php b/application/Espo/Core/Utils/Util.php index 32061eb603..151999afca 100644 --- a/application/Espo/Core/Utils/Util.php +++ b/application/Espo/Core/Utils/Util.php @@ -555,28 +555,35 @@ class Util return true; } - public static function generateId() + public static function generateId() : string { return uniqid() . substr(md5(rand()), 0, 4); } - public static function generateApiKey() + public static function generateMoreEntropyId() : string + { + return substr(md5(uniqid(rand(), true)), 0, 16) . substr(md5(rand()), 0, 4); + } + + public static function generateCryptId() : string { if (!function_exists('random_bytes')) { - return self::generateId(); + return self::generateMoreEntropyId(); } return bin2hex(random_bytes(16)); } - public static function generateSecretKey() + public static function generateApiKey() : string { - if (!function_exists('random_bytes')) { - return self::generateId(); - } - return bin2hex(random_bytes(16)); + return self::generateCryptId(); } - public static function generateKey() + public static function generateSecretKey() : string + { + return self::generateCryptId(); + } + + public static function generateKey() : string { return md5(uniqid(rand(), true)); } diff --git a/application/Espo/Entities/UniqueId.php b/application/Espo/Entities/UniqueId.php index 2d88bacf38..7ed1ee8e7b 100644 --- a/application/Espo/Entities/UniqueId.php +++ b/application/Espo/Entities/UniqueId.php @@ -31,6 +31,8 @@ namespace Espo\Entities; class UniqueId extends \Espo\Core\ORM\Entity { - + public function getIdValue() : ?string + { + return $this->get('name'); + } } - diff --git a/application/Espo/Repositories/UniqueId.php b/application/Espo/Repositories/UniqueId.php index 8735b7e1f6..1892683ebc 100644 --- a/application/Espo/Repositories/UniqueId.php +++ b/application/Espo/Repositories/UniqueId.php @@ -44,7 +44,7 @@ class UniqueId extends \Espo\Core\ORM\Repositories\RDB public function getNew() : ?Entity { $entity = parent::getNew(); - $entity->set('name', \Espo\Core\Utils\Util::generateId()); + $entity->set('name', \Espo\Core\Utils\Util::generateMoreEntropyId()); return $entity; } } diff --git a/application/Espo/Repositories/Webhook.php b/application/Espo/Repositories/Webhook.php index 83dc7349b7..b93bb7eb8a 100644 --- a/application/Espo/Repositories/Webhook.php +++ b/application/Espo/Repositories/Webhook.php @@ -52,7 +52,7 @@ class Webhook extends \Espo\Core\ORM\Repositories\RDB protected function fillSecretKey(Entity $entity) { - $secretKey = \Espo\Core\Utils\Util::generateKey(); + $secretKey = \Espo\Core\Utils\Util::generateSecretKey(); $entity->set('secretKey', $secretKey); } diff --git a/application/Espo/Services/User.php b/application/Espo/Services/User.php index 0c9b20a0ba..c9d9993c0e 100644 --- a/application/Espo/Services/User.php +++ b/application/Espo/Services/User.php @@ -245,7 +245,7 @@ class User extends Record throw new Forbidden(json_encode(['reason' => 'Already-Sent'])); } - $requestId = Util::generateId() . Util::generateKey(); + $requestId = Util::generateCryptId(); $passwordChangeRequest = $this->getEntityManager()->getEntity('PasswordChangeRequest'); $passwordChangeRequest->set([ @@ -411,7 +411,7 @@ class User extends Record $entity->set('apiKey', $apiKey); if ($entity->get('authMethod') === 'Hmac') { - $secretKey = \Espo\Core\Utils\Util::generateKey(); + $secretKey = \Espo\Core\Utils\Util::generateSecretKey(); $entity->set('secretKey', $secretKey); } @@ -508,7 +508,7 @@ class User extends Record $entity->set('apiKey', $apiKey); if ($entity->get('authMethod') === 'Hmac') { - $secretKey = \Espo\Core\Utils\Util::generateKey(); + $secretKey = \Espo\Core\Utils\Util::generateSecretKey(); $entity->set('secretKey', $secretKey); } } @@ -559,7 +559,7 @@ class User extends Record if ($entity->isApi()) { if ($entity->isAttributeChanged('authMethod') && $entity->get('authMethod') === 'Hmac') { - $secretKey = \Espo\Core\Utils\Util::generateKey(); + $secretKey = \Espo\Core\Utils\Util::generateSecretKey(); $entity->set('secretKey', $secretKey); } } From b19f05af1947c34b6390a360263c07f3fa6aad35 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Mon, 6 Apr 2020 11:52:24 +0300 Subject: [PATCH 3/4] orm mapper massDeleteFromDb --- application/Espo/ORM/DB/Mapper.php | 27 +++++++++++++++++++ tests/integration/Espo/ORM/MapperTest.php | 33 +++++++++++++++++++++++ tests/unit/Espo/ORM/DB/MapperTest.php | 28 +++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/application/Espo/ORM/DB/Mapper.php b/application/Espo/ORM/DB/Mapper.php index 0f4ba0488f..05e8fbe055 100644 --- a/application/Espo/ORM/DB/Mapper.php +++ b/application/Espo/ORM/DB/Mapper.php @@ -1061,6 +1061,33 @@ abstract class Mapper implements IMapper return false; } + /** + * Mass delete from database by specified whereClause. + * @return Number of deleted records or null if failure. + */ + public function massDeleteFromDb(string $entityType, array $whereClause) : ?int + { + $table = $this->toDb($entityType); + + $sql = "DELETE FROM `{$table}`"; + + $entity = $this->entityFactory->create($entityType); + if (!$entity) return null; + + $wherePart = $this->query->getWhere($entity, $whereClause); + if ($wherePart) { + $sql .= ' WHERE ' . $wherePart; + } + + $sth = $this->pdo->prepare($sql); + + if ($sth->execute()) { + return $sth->rowCount(); + } + + return null; + } + public function restoreDeleted(string $entityType, $id) { if (empty($entityType) || empty($id)) return false; diff --git a/tests/integration/Espo/ORM/MapperTest.php b/tests/integration/Espo/ORM/MapperTest.php index 7f0631cbb6..5776ecc5ae 100644 --- a/tests/integration/Espo/ORM/MapperTest.php +++ b/tests/integration/Espo/ORM/MapperTest.php @@ -302,4 +302,37 @@ class MapperTest extends \tests\integration\Core\BaseTestCase $isRelated = $em->getRepository('Lead')->isRelated($l1, 'createdAccount', $a1); $this->assertFalse($isRelated); } + + public function testMassDeleteFromDb1() + { + $app = $this->createApplication(); + $em = $app->getContainer()->get('entityManager'); + $mapper = $em->getMapper('RDB'); + + $a1 = $em->createEntity('Account', [ + 'name' => '1', + ]); + $a2 = $em->createEntity('Account', [ + 'name' => '2', + ]); + + $count = $mapper->massDeleteFromDb('Account', [ + 'name' => '1', + ]); + + $this->assertEquals(1, $count); + + $a1 = $em->getEntity('Account', $a1->id); + $this->assertNull($a1); + + $a2 = $em->getEntity('Account', $a2->id); + $this->assertNotNull($a2); + + + $count = $mapper->massDeleteFromDb('Account', [ + 'name' => '3', + ]); + + $this->assertEquals(0, $count); + } } diff --git a/tests/unit/Espo/ORM/DB/MapperTest.php b/tests/unit/Espo/ORM/DB/MapperTest.php index 9deef2a34c..64d7951897 100644 --- a/tests/unit/Espo/ORM/DB/MapperTest.php +++ b/tests/unit/Espo/ORM/DB/MapperTest.php @@ -490,4 +490,32 @@ class DBMapperTest extends \PHPUnit\Framework\TestCase )); } + public function testMassDeleteFromDb() + { + $query = "DELETE FROM `post` WHERE post.name = 'test' AND post.deleted = '1'"; + + $sth = $this->getMockBuilder('\\PDOStatement')->disableOriginalConstructor()->getMock(); + + $this->pdo + ->expects($this->once()) + ->method('prepare') + ->with($query) + ->will($this->returnValue($sth)); + + $sth + ->expects($this->once()) + ->method('execute') + ->will($this->returnValue(true)); + + $sth + ->expects($this->once()) + ->method('rowCount') + ->will($this->returnValue(1)); + + + $this->db->massDeleteFromDb('Post', [ + 'name' => 'test', + 'deleted' => true, + ]); + } } From 82f93514142a3462049299a17c2e4639c184a465 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Mon, 6 Apr 2020 11:52:33 +0300 Subject: [PATCH 4/4] mass email faster cleanup --- application/Espo/Modules/Crm/Services/MassEmail.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/application/Espo/Modules/Crm/Services/MassEmail.php b/application/Espo/Modules/Crm/Services/MassEmail.php index cdbac55159..d04eb09be4 100644 --- a/application/Espo/Modules/Crm/Services/MassEmail.php +++ b/application/Espo/Modules/Crm/Services/MassEmail.php @@ -83,18 +83,17 @@ class MassEmail extends \Espo\Services\Record { parent::afterDeleteEntity($massEmail); - $this->cleanupQueueItems($massEmail); + $this->getEntityManager()->getMapper('RDB')->massDeleteFromDb('EmailQueueItem', [ + 'massEmailId' => $massEmail->id, + ]); } protected function cleanupQueueItems(Entity $massEmail) { - $existingQueueItemList = $this->getEntityManager()->getRepository('EmailQueueItem')->select(['id'])->where([ - 'status' => ['Pending', 'Failed'], + $this->getEntityManager()->getMapper('RDB')->massDeleteFromDb('EmailQueueItem', [ 'massEmailId' => $massEmail->id, - ])->find(); - foreach ($existingQueueItemList as $existingQueueItem) { - $this->getEntityManager()->getMapper('RDB')->deleteFromDb('EmailQueueItem', $existingQueueItem->id); - } + 'status' => ['Pending', 'Failed'], + ]); } public function createQueue(Entity $massEmail, $isTest = false, $additionalTargetList = [])