From 63c60f3e6fe363f1988e52b2e4b118f74e271d51 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 7 Nov 2017 14:03:59 +0200 Subject: [PATCH 01/14] export datetime timezone fix --- application/Espo/Core/Export/Xlsx.php | 29 ++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/application/Espo/Core/Export/Xlsx.php b/application/Espo/Core/Export/Xlsx.php index a2b4a9fa37..6668f3f6a5 100644 --- a/application/Espo/Core/Export/Xlsx.php +++ b/application/Espo/Core/Export/Xlsx.php @@ -304,8 +304,29 @@ class Xlsx extends \Espo\Core\Injectable $sheet->setCellValue("$col$rowNumber", \PHPExcel_Shared_Date::PHPToExcel(strtotime($row[$name]))); } } else if ($type == 'datetime' || $type == 'datetimeOptional') { - if (isset($row[$name])) { - $sheet->setCellValue("$col$rowNumber", \PHPExcel_Shared_Date::PHPToExcel(strtotime($row[$name]))); + $value = null; + if ($type == 'datetimeOptional') { + if (isset($row[$name . 'Date']) && $row[$name . 'Date']) { + $value = $row[$name . 'Date']; + } + } + if (!$value) { + if (isset($row[$name])) { + $value = $row[$name]; + } + } + if ($value && strlen($value) > 11) { + try { + $timeZone = $this->getInjection('config')->get('timeZone'); + $dt = new \DateTime($value); + $dt->setTimezone(new \DateTimeZone($timeZone)); + $value = $dt->format($this->getInjection('dateTime')->getInternalDateTimeFormat()); + } catch (\Exception $e) { + $value = ''; + } + } + if ($value) { + $sheet->setCellValue("$col$rowNumber", \PHPExcel_Shared_Date::PHPToExcel(strtotime($value))); } } else if ($type == 'image') { if (isset($row[$name . 'Id']) && $row[$name . 'Id']) { @@ -394,11 +415,9 @@ class Xlsx extends \Espo\Core\Injectable foreach ($fieldList as $i => $name) { $col = $azRange[$i]; - $type = $typesCache[$name]; - - switch($type) { + switch ($type) { case 'currency': case 'currencyConverted': { From 92b9a40891d78e6a40e94f85a77cdea7ccac1c2b Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 7 Nov 2017 14:19:55 +0200 Subject: [PATCH 02/14] fix excel export: foreign fields formatting --- application/Espo/Core/Export/Xlsx.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/application/Espo/Core/Export/Xlsx.php b/application/Espo/Core/Export/Xlsx.php index 6668f3f6a5..2ea6e4bf0b 100644 --- a/application/Espo/Core/Export/Xlsx.php +++ b/application/Espo/Core/Export/Xlsx.php @@ -249,6 +249,14 @@ class Xlsx extends \Espo\Core\Injectable $type = $this->getInjection('metadata')->get(['entityDefs', $foreignScope, 'fields', $foreignField, 'type'], $type); } } + if ($type === 'foreign') { + $linkName = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'fields', $name, 'link']); + $foreignField = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'fields', $name, 'field']); + $foreignScope = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'links', $linkName, 'entity']); + if ($foreignScope) { + $type = $this->getInjection('metadata')->get(['entityDefs', $foreignScope, 'fields', $foreignField, 'type'], $type); + } + } $typesCache[$name] = $type; $link = null; From e5f88833997710caf33680d1a5d6bb978edb37a0 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 7 Nov 2017 14:33:05 +0200 Subject: [PATCH 03/14] formula: array push function --- .../Formula/Functions/ArrayGroup/PushType.php | 57 +++++++++++++++++++ tests/unit/Espo/Core/Formula/FormulaTest.php | 25 ++++++++ 2 files changed, 82 insertions(+) create mode 100644 application/Espo/Core/Formula/Functions/ArrayGroup/PushType.php diff --git a/application/Espo/Core/Formula/Functions/ArrayGroup/PushType.php b/application/Espo/Core/Formula/Functions/ArrayGroup/PushType.php new file mode 100644 index 0000000000..a9ab3732de --- /dev/null +++ b/application/Espo/Core/Formula/Functions/ArrayGroup/PushType.php @@ -0,0 +1,57 @@ +value)) { + throw new Error('Bad \'Array\\push\' definition.'); + } + if (count($item->value) < 2) { + throw new Error('Bad arguments passed to \'Array\\push\'.'); + } + $list = $this->evaluate($item->value[0]); + if (!is_array($list)) { + return false; + } + + foreach ($item->value as $i => $v) { + if ($i === 0) continue; + $element = $this->evaluate($item->value[$i]); + $list[] = $element; + } + + return $list; + } +} \ No newline at end of file diff --git a/tests/unit/Espo/Core/Formula/FormulaTest.php b/tests/unit/Espo/Core/Formula/FormulaTest.php index a9559cd9a6..1479602222 100644 --- a/tests/unit/Espo/Core/Formula/FormulaTest.php +++ b/tests/unit/Espo/Core/Formula/FormulaTest.php @@ -1718,6 +1718,31 @@ class FormulaTest extends \PHPUnit_Framework_TestCase $this->assertFalse($actual); } + function testArrayPush() + { + $item = json_decode(' + { + "type": "array\\\\push", + "value": [ + { + "type": "value", + "value": ["Test", "Hello"] + }, + { + "type": "value", + "value": "1" + }, + { + "type": "value", + "value": "2" + } + ] + } + '); + $actual = $this->formula->process($item, $this->entity); + $this->assertEquals(['Test', 'Hello', '1', '2'], $actual); + } + function testArrayLength() { $item = json_decode(' From d3cb2584401405d583e4c48aee6ce05833e96a9a Mon Sep 17 00:00:00 2001 From: yuri Date: Thu, 9 Nov 2017 11:20:38 +0200 Subject: [PATCH 04/14] fix email attachment import for emails composed with not proper formatting by bad agents --- application/Espo/Core/Mail/Parsers/MailMimeParser.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/application/Espo/Core/Mail/Parsers/MailMimeParser.php b/application/Espo/Core/Mail/Parsers/MailMimeParser.php index f802d372e4..fb152351a3 100644 --- a/application/Espo/Core/Mail/Parsers/MailMimeParser.php +++ b/application/Espo/Core/Mail/Parsers/MailMimeParser.php @@ -174,7 +174,12 @@ class MailMimeParser $disposition = $attachmentObj->getHeaderValue('Content-Disposition'); $attachment = $this->getEntityManager()->getEntity('Attachment'); - $attachment->set('name', $attachmentObj->getHeaderParameter('Content-Disposition', 'filename', 'unnamed')); + + $filename = $attachmentObj->getHeaderParameter('Content-Disposition', 'filename', null); + if ($filename === null) { + $filename = $attachmentObj->getHeaderParameter('Content-Type', 'name', 'unnamed'); + } + $attachment->set('name', $filename); $attachment->set('type', $attachmentObj->getHeaderValue('Content-Type')); $contentId = $attachmentObj->getHeaderValue('Content-ID'); @@ -186,6 +191,7 @@ class MailMimeParser if ($disposition == 'inline') { $attachment->set('role', 'Inline Attachment'); } else { + $disposition = 'attachment'; $attachment->set('role', 'Attachment'); } From 4ed742fa4a5fc386f6a942385bc935dbd7b269fe Mon Sep 17 00:00:00 2001 From: yuri Date: Thu, 9 Nov 2017 11:44:49 +0200 Subject: [PATCH 05/14] fix email attachment import for weird formatting by apple client --- application/Espo/Core/Mail/Parsers/MailMimeParser.php | 4 +++- application/Espo/Core/Mail/Parsers/PhpMimeMailParser.php | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/application/Espo/Core/Mail/Parsers/MailMimeParser.php b/application/Espo/Core/Mail/Parsers/MailMimeParser.php index fb152351a3..705c3ac3ad 100644 --- a/application/Espo/Core/Mail/Parsers/MailMimeParser.php +++ b/application/Espo/Core/Mail/Parsers/MailMimeParser.php @@ -207,8 +207,10 @@ class MailMimeParser } else if ($disposition == 'inline') { if ($contentId) { $inlineIds[$contentId] = $attachment->id; + $inlineAttachmentList[] = $attachment; + } else { + $email->addLinkMultipleId('attachments', $attachment->id); } - $inlineAttachmentList[] = $attachment; } } diff --git a/application/Espo/Core/Mail/Parsers/PhpMimeMailParser.php b/application/Espo/Core/Mail/Parsers/PhpMimeMailParser.php index 40f076fd62..508c942676 100644 --- a/application/Espo/Core/Mail/Parsers/PhpMimeMailParser.php +++ b/application/Espo/Core/Mail/Parsers/PhpMimeMailParser.php @@ -186,8 +186,10 @@ class PhpMimeMailParser } else if ($disposition == 'inline') { if ($contentId) { $inlineIds[$contentId] = $attachment->id; + $inlineAttachmentList[] = $attachment; + } else { + $email->addLinkMultipleId('attachments', $attachment->id); } - $inlineAttachmentList[] = $attachment; } } From 758120580087b06ad5963d5b33da32db5f1985ab Mon Sep 17 00:00:00 2001 From: Taras Machyshyn Date: Thu, 9 Nov 2017 16:32:25 +0200 Subject: [PATCH 06/14] Portal url fixes --- application/Espo/EntryPoints/Portal.php | 9 +++++++-- portal/index.php | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/application/Espo/EntryPoints/Portal.php b/application/Espo/EntryPoints/Portal.php index 91a188322e..5f5102c47a 100644 --- a/application/Espo/EntryPoints/Portal.php +++ b/application/Espo/EntryPoints/Portal.php @@ -44,9 +44,14 @@ class Portal extends \Espo\Core\EntryPoints\Base } else if (!empty($data['id'])) { $id = $data['id']; } else { - $url = !empty($_SERVER['REDIRECT_URL']) ? $_SERVER['REDIRECT_URL'] : $_SERVER['REQUEST_URI']; - + $url = $_SERVER['REQUEST_URI']; $id = explode('/', $url)[count(explode('/', $_SERVER['SCRIPT_NAME'])) - 1]; + + if (!isset($id)) { + $url = $_SERVER['REDIRECT_URL']; + $id = explode('/', $url)[count(explode('/', $_SERVER['SCRIPT_NAME'])) - 1]; + } + if (!$id) { $id = $this->getConfig()->get('defaultPortalId'); } diff --git a/portal/index.php b/portal/index.php index f3d606aa52..9b3690446e 100644 --- a/portal/index.php +++ b/portal/index.php @@ -34,7 +34,13 @@ if (!$app->isInstalled()) { exit; } -$url = !empty($_SERVER['REDIRECT_URL']) ? $_SERVER['REDIRECT_URL'] : $_SERVER['REQUEST_URI']; +$url = $_SERVER['REQUEST_URI']; +$portalId = explode('/', $url)[count(explode('/', $_SERVER['SCRIPT_NAME'])) - 1]; + +if (!isset($portalId)) { + $url = $_SERVER['REDIRECT_URL']; + $portalId = explode('/', $url)[count(explode('/', $_SERVER['SCRIPT_NAME'])) - 1]; +} $a = explode('?', $url); if (substr($a[0], -1) !== '/') { @@ -46,7 +52,6 @@ if (substr($a[0], -1) !== '/') { exit(); } -$portalId = explode('/', $url)[count(explode('/', $_SERVER['SCRIPT_NAME'])) - 1]; if ($portalId) { $app->setBasePath('../../'); } else { From a581879a8e7a8088c39f223d486960078007d0d5 Mon Sep 17 00:00:00 2001 From: yuri Date: Fri, 10 Nov 2017 12:19:57 +0200 Subject: [PATCH 07/14] email parsing fixes --- .../Espo/Core/Mail/Parsers/MailMimeParser.php | 26 +++++++++++++++++-- .../Core/Mail/Parsers/PhpMimeMailParser.php | 22 ++++++++++++++-- composer.json | 4 +-- composer.lock | 24 ++++++++--------- 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/application/Espo/Core/Mail/Parsers/MailMimeParser.php b/application/Espo/Core/Mail/Parsers/MailMimeParser.php index 705c3ac3ad..c4b745cac7 100644 --- a/application/Espo/Core/Mail/Parsers/MailMimeParser.php +++ b/application/Espo/Core/Mail/Parsers/MailMimeParser.php @@ -147,8 +147,30 @@ class MailMimeParser { $this->loadContent($message); - $bodyPlain = $this->getMessage($message)->getTextContent(); - $bodyHtml = $this->getMessage($message)->getHtmlContent(); + $bodyPlain = ''; + $bodyHtml = ''; + + $htmlPartCount = $this->getMessage($message)->getHtmlPartCount(); + $textPartCount = $this->getMessage($message)->getTextPartCount(); + + if (!$htmlPartCount) { + $bodyHtml = $this->getMessage($message)->getHtmlContent(); + } + if (!$textPartCount) { + $bodyPlain = $this->getMessage($message)->getTextContent(); + } + + for ($i = 0; $i < $htmlPartCount; $i++) { + if ($i) $bodyHtml .= "
"; + $inlinePart = $this->getMessage($message)->getHtmlPart($i); + $bodyHtml .= $inlinePart->getContent(); + } + + for ($i = 0; $i < $textPartCount; $i++) { + if ($i) $bodyPlain .= "\n"; + $inlinePart = $this->getMessage($message)->getTextPart($i); + $bodyPlain .= $inlinePart->getContent(); + } if ($bodyHtml) { $email->set('isHtml', true); diff --git a/application/Espo/Core/Mail/Parsers/PhpMimeMailParser.php b/application/Espo/Core/Mail/Parsers/PhpMimeMailParser.php index 508c942676..3716fe8eda 100644 --- a/application/Espo/Core/Mail/Parsers/PhpMimeMailParser.php +++ b/application/Espo/Core/Mail/Parsers/PhpMimeMailParser.php @@ -137,8 +137,26 @@ class PhpMimeMailParser { $this->loadContent($message); - $bodyPlain = $this->getParser($message)->getMessageBody('text'); - $bodyHtml = $this->getParser($message)->getMessageBody('html'); + $bodyPlain = ''; + $bodyHtml = ''; + + $inlinePartTextList = $this->getParser($message)->getInlineParts('text'); + $inlinePartHtmlList = $this->getParser($message)->getInlineParts('html'); + if (empty($inlinePartTextList)) { + $bodyPlain = $this->getParser($message)->getMessageBody('text'); + } + if (empty($inlinePartHtmlList)) { + $bodyHtml = $this->getParser($message)->getMessageBody('html'); + } + + foreach ($inlinePartTextList as $i => $inlinePart) { + if ($i) $bodyPlain .= "\n"; + $bodyPlain .= $inlinePart; + } + foreach ($inlinePartHtmlList as $i => $inlinePart) { + if ($i) $bodyHtml .= "
"; + $bodyHtml .= $inlinePart; + } if ($bodyHtml) { $email->set('isHtml', true); diff --git a/composer.json b/composer.json index eb6ef69428..78bbcce6b3 100644 --- a/composer.json +++ b/composer.json @@ -12,8 +12,8 @@ "composer/semver": "^1.4", "zendframework/zend-servicemanager": "2.6.0", "tecnickcom/tcpdf": "^6.2", - "php-mime-mail-parser/php-mime-mail-parser": "^2.5", - "zbateson/mail-mime-parser": "0.4.4", + "php-mime-mail-parser/php-mime-mail-parser": "2.9.3", + "zbateson/mail-mime-parser": "0.4.5", "phpoffice/phpexcel": "^1.8" }, "autoload": { diff --git a/composer.lock b/composer.lock index 8672cb2069..bd897dfeb2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "ec99337bafb77d31fccfd7bd47d9ccfe", - "content-hash": "bd159982d9c3db5c6a1f9915652ac173", + "hash": "b7a79ea5683921a46868e4c2ef74febc", + "content-hash": "54fe23c745b7027a9abeeaa5ddac9b43", "packages": [ { "name": "composer/semver", @@ -688,16 +688,16 @@ }, { "name": "php-mime-mail-parser/php-mime-mail-parser", - "version": "2.5.0", + "version": "2.9.3", "source": { "type": "git", "url": "https://github.com/php-mime-mail-parser/php-mime-mail-parser.git", - "reference": "9e4da9b71fa653782fa622ddb4363709aabcb714" + "reference": "c6884c7bc77adbf55979db99841195b232fd30f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-mime-mail-parser/php-mime-mail-parser/zipball/9e4da9b71fa653782fa622ddb4363709aabcb714", - "reference": "9e4da9b71fa653782fa622ddb4363709aabcb714", + "url": "https://api.github.com/repos/php-mime-mail-parser/php-mime-mail-parser/zipball/c6884c7bc77adbf55979db99841195b232fd30f1", + "reference": "c6884c7bc77adbf55979db99841195b232fd30f1", "shasum": "" }, "require": { @@ -764,7 +764,7 @@ "mailparse", "mime" ], - "time": "2016-10-07 16:46:22" + "time": "2017-11-02 05:49:00" }, { "name": "phpoffice/phpexcel", @@ -1022,16 +1022,16 @@ }, { "name": "zbateson/mail-mime-parser", - "version": "0.4.4", + "version": "0.4.5", "source": { "type": "git", "url": "https://github.com/zbateson/MailMimeParser.git", - "reference": "ed9bb727468cff384262046b8e81c744d9090d44" + "reference": "ac1488579b40defa68902b081b576caa8fbff8fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zbateson/MailMimeParser/zipball/ed9bb727468cff384262046b8e81c744d9090d44", - "reference": "ed9bb727468cff384262046b8e81c744d9090d44", + "url": "https://api.github.com/repos/zbateson/MailMimeParser/zipball/ac1488579b40defa68902b081b576caa8fbff8fa", + "reference": "ac1488579b40defa68902b081b576caa8fbff8fa", "shasum": "" }, "require": { @@ -1069,7 +1069,7 @@ "parser", "php-imap" ], - "time": "2017-07-10 01:00:32" + "time": "2017-10-11 06:35:19" }, { "name": "zendframework/zend-crypt", From 95992e918a2e0829a266debf836c53e65c651529 Mon Sep 17 00:00:00 2001 From: yuri Date: Fri, 10 Nov 2017 15:27:15 +0200 Subject: [PATCH 08/14] change formula hook order --- application/Espo/Hooks/Common/Formula.php | 2 +- application/Espo/Hooks/Common/NextNumber.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/Espo/Hooks/Common/Formula.php b/application/Espo/Hooks/Common/Formula.php index de3863d760..b7efff899f 100644 --- a/application/Espo/Hooks/Common/Formula.php +++ b/application/Espo/Hooks/Common/Formula.php @@ -34,7 +34,7 @@ use Espo\Core\Utils\Util; class Formula extends \Espo\Core\Hooks\Base { - public static $order = 5; + public static $order = 11; protected function init() { diff --git a/application/Espo/Hooks/Common/NextNumber.php b/application/Espo/Hooks/Common/NextNumber.php index c9e48e5ceb..13646169b6 100644 --- a/application/Espo/Hooks/Common/NextNumber.php +++ b/application/Espo/Hooks/Common/NextNumber.php @@ -34,7 +34,7 @@ use Espo\Core\Utils\Util; class NextNumber extends \Espo\Core\Hooks\Base { - public static $order = 10; + public static $order = 9; protected function init() { From 495f8bf96874e39b26215dc68531c0fbffc0fe1f Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 13 Nov 2017 11:54:21 +0200 Subject: [PATCH 09/14] import/export fixes --- application/Espo/Services/Import.php | 85 +++++++++++++++------------- application/Espo/Services/Record.php | 20 +++---- client/src/views/import/step2.js | 40 ++++++++----- 3 files changed, 81 insertions(+), 64 deletions(-) diff --git a/application/Espo/Services/Import.php b/application/Espo/Services/Import.php index 4c8b482675..ce1576b557 100644 --- a/application/Espo/Services/Import.php +++ b/application/Espo/Services/Import.php @@ -463,7 +463,6 @@ class Import extends \Espo\Services\Record $isNew = $entity->isNew(); - if (!empty($params['defaultValues'])) { if (is_object($params['defaultValues'])) { $v = get_object_vars($params['defaultValues']); @@ -581,8 +580,6 @@ class Import extends \Espo\Services\Record $result = array(); - $a = $entity->toArray(); - try { if ($isNew) { $isDuplicate = false; @@ -646,7 +643,7 @@ class Import extends \Espo\Services\Record return ['firstName' => $firstName, 'lastName' => $lastName]; } - protected function parseValue(Entity $entity, $field, $value, $params = array()) + protected function parseValue(Entity $entity, $attribute, $value, $params = array()) { $decimalMark = '.'; if (!empty($params['decimalMark'])) { @@ -672,45 +669,55 @@ class Import extends \Espo\Services\Record } } - $fieldDefs = $entity->getFields(); + $type = $entity->getAttributeType($attribute); - if (!empty($fieldDefs[$field])) { - $type = $fieldDefs[$field]['type']; + switch ($type) { + case Entity::DATE: + $dt = \DateTime::createFromFormat($dateFormat, $value); + if ($dt) { + return $dt->format('Y-m-d'); + } + break; + case Entity::DATETIME: + $timezone = new \DateTimeZone(isset($params['timezone']) ? $params['timezone'] : 'UTC'); + $dt = \DateTime::createFromFormat($dateFormat . ' ' . $timeFormat, $value, $timezone); + if ($dt) { + $dt->setTimezone(new \DateTimeZone('UTC')); + return $dt->format('Y-m-d H:i:s'); + } + break; + case Entity::FLOAT: + $currencyAttribute = $attribute . 'Currency'; + if ($entity->hasAttribute($currencyAttribute)) { + if (!$entity->has($currencyAttribute)) { + $entity->set($currencyAttribute, $defaultCurrency); + } + } - switch ($type) { - case Entity::DATE: - $dt = \DateTime::createFromFormat($dateFormat, $value); - if ($dt) { - return $dt->format('Y-m-d'); - } - break; - case Entity::DATETIME: - $timezone = new \DateTimeZone(isset($params['timezone']) ? $params['timezone'] : 'UTC'); - $dt = \DateTime::createFromFormat($dateFormat . ' ' . $timeFormat, $value, $timezone); - if ($dt) { - $dt->setTimezone(new \DateTimeZone('UTC')); - return $dt->format('Y-m-d H:i:s'); - } - break; - case Entity::FLOAT: - $currencyField = $field . 'Currency'; - if ($entity->hasField($currencyField)) { - if (!$entity->has($currencyField)) { - $entity->set($currencyField, $defaultCurrency); - } - } + $a = explode($decimalMark, $value); + $a[0] = preg_replace('/[^A-Za-z0-9\-]/', '', $a[0]); - $a = explode($decimalMark, $value); - $a[0] = preg_replace('/[^A-Za-z0-9\-]/', '', $a[0]); - - if (count($a) > 1) { - return $a[0] . '.' . $a[1]; - } else { - return $a[0]; - } - break; - } + if (count($a) > 1) { + return $a[0] . '.' . $a[1]; + } else { + return $a[0]; + } + break; + case Entity::JSON_OBJECT: + $value = \Espo\Core\Utils\Json::decode($value); + return $value; + case Entity::JSON_ARRAY: + if (!is_string($value)) return; + if (!strlen($value)) return; + if ($value[0] === '[') { + $value = \Espo\Core\Utils\Json::decode($value); + return $value; + } else { + $value = explode(',', $value); + return $value; + } } + return $value; } diff --git a/application/Espo/Services/Record.php b/application/Espo/Services/Record.php index 57e3772696..ac4a8d82af 100644 --- a/application/Espo/Services/Record.php +++ b/application/Espo/Services/Record.php @@ -1387,10 +1387,6 @@ class Record extends \Espo\Core\Services\Base } foreach ($collection as $entity) { - if (is_null($attributeList)) { - - } - $this->loadAdditionalFieldsForExport($entity); if (method_exists($exportObj, 'loadAdditionalFields')) { $exportObj->loadAdditionalFields($entity, $fieldList); @@ -1447,19 +1443,23 @@ class Record extends \Espo\Core\Services\Base throw new Error(); } - protected function getAttributeFromEntityForExport(Entity $entity, $field) + protected function getAttributeFromEntityForExport(Entity $entity, $attribute) { - $methodName = 'getAttribute' . ucfirst($field). 'FromEntityForExport'; + $methodName = 'getAttribute' . ucfirst($attribute). 'FromEntityForExport'; if (method_exists($this, $methodName)) { return $this->$methodName($entity); } $defs = $entity->getAttributes(); - if (!empty($defs[$field]) && !empty($defs[$field]['type'])) { - $type = $defs[$field]['type']; + if (!empty($defs[$attribute]) && !empty($defs[$attribute]['type'])) { + $type = $defs[$attribute]['type']; switch ($type) { + case 'jsonObject': + $value = $entity->get($attribute); + return \Espo\Core\Utils\Json::encode($value); + break; case 'jsonArray': - $value = $entity->get($field); + $value = $entity->get($attribute); if (is_array($value)) { return implode(',', $value); } else { @@ -1471,7 +1471,7 @@ class Record extends \Espo\Core\Services\Base break; } } - return $entity->get($field); + return $entity->get($attribute); } public function prepareEntityForOutput(Entity $entity) diff --git a/client/src/views/import/step2.js b/client/src/views/import/step2.js index 649eb5111d..4ad7b9716d 100644 --- a/client/src/views/import/step2.js +++ b/client/src/views/import/step2.js @@ -134,7 +134,12 @@ Espo.define('views/import/step2', 'view', function (Dep) { $cell = $('').append($select); $row.append($cell); - $cell = $('').html(d.value); + var value = d.value; + if (value.length > 200) { + value = value.substr(0, 200) + '...'; + } + + $cell = $('').html(value); $row.append($cell); if (~['update', 'createAndUpdate'].indexOf(this.formData.action)) { @@ -175,14 +180,14 @@ Espo.define('views/import/step2', 'view', function (Dep) { }, getAttributeList: function () { - var fields = this.getMetadata().get('entityDefs.' + this.scope + '.fields'); + var fields = this.getMetadata().get(['entityDefs', this.scope, 'fields']) || {}; - var fieldList = []; - fieldList.push('id'); + var attributeList = []; + attributeList.push('id'); for (var field in fields) { var d = fields[field]; - if (!~this.allowedFieldList.indexOf(field) && (d.readOnly || d.disabled || d.importDisabled)) { + if (!~this.allowedFieldList.indexOf(field) && (((d.readOnly || d.disabled) && !d.importNotDisabled) || d.importDisabled)) { continue; } @@ -190,14 +195,14 @@ Espo.define('views/import/step2', 'view', function (Dep) { (this.getMetadata().get('entityDefs.' + this.scope + '.fields.' + field + '.typeList' ) || []).map(function (item) { return item.replace(/\s/g, '_'); }, this).forEach(function (item) { - fieldList.push(field + Espo.Utils.upperCaseFirst(item)); + attributeList.push(field + Espo.Utils.upperCaseFirst(item)); }, this); continue; } if (d.type == 'link') { - fieldList.push(field + 'Name'); - fieldList.push(field + 'Id'); + attributeList.push(field + 'Name'); + attributeList.push(field + 'Id'); } if (~['linkMultiple', 'foreign'].indexOf(d.type)) { @@ -205,23 +210,28 @@ Espo.define('views/import/step2', 'view', function (Dep) { } if (d.type == 'personName') { - fieldList.push(field); + attributeList.push(field); } var type = d.type; - var actualFields = this.getFieldManager().getActualAttributes(type, field); - actualFields.forEach(function (f) { - if (fieldList.indexOf(f) === -1) { - fieldList.push(f); + var actualAttributeList = this.getFieldManager().getActualAttributeList(type, field); + + if (!actualAttributeList.length) { + actualAttributeList = [field]; + } + + actualAttributeList.forEach(function (f) { + if (attributeList.indexOf(f) === -1) { + attributeList.push(f); } }, this); } - fieldList = fieldList.sort(function (v1, v2) { + attributeList = attributeList.sort(function (v1, v2) { return this.translate(v1, 'fields', this.scope).localeCompare(this.translate(v2, 'fields', this.scope)); }.bind(this)); - return fieldList + return attributeList }, getFieldDropdown: function (num, name) { From d04035dd3fc62b91ffd5f318e751ec0f095cb3b2 Mon Sep 17 00:00:00 2001 From: Taras Machyshyn Date: Mon, 13 Nov 2017 13:59:30 +0200 Subject: [PATCH 10/14] HookManager improvements --- application/Espo/Core/HookManager.php | 73 +++++--- tests/unit/Espo/Core/HookManagerTest.php | 203 +++++++++++++++++------ 2 files changed, 199 insertions(+), 77 deletions(-) diff --git a/application/Espo/Core/HookManager.php b/application/Espo/Core/HookManager.php index 7470a99bef..8be8f89a1f 100644 --- a/application/Espo/Core/HookManager.php +++ b/application/Espo/Core/HookManager.php @@ -37,6 +37,8 @@ class HookManager private $data; + private $hookListHash = array(); + private $hooks; protected $cacheFile = 'data/cache/application/hooks.php'; @@ -105,18 +107,10 @@ class HookManager $this->loadHooks(); } - $hookList = array(); + $hookList = $this->getHookList($scope, $hookName); - if (isset($this->data['Common'])) { - $hookList = $this->data['Common']; - } - - if (isset($this->data[$scope])) { - $hookList = $this->mergeHooks($hookList, $this->data[$scope]); - } - - if (!empty($hookList[$hookName])) { - foreach ($hookList[$hookName] as $className => $classOrder) { + if (!empty($hookList)) { + foreach ($hookList as $className) { if (empty($this->hooks[$className])) { $this->hooks[$className] = $this->createHookByClassName($className); if (empty($this->hooks[$className])) continue; @@ -174,8 +168,11 @@ class HookManager foreach($hookMethods as $hookType) { $entityHookData = isset($hookData[$normalizedScopeName][$hookType]) ? $hookData[$normalizedScopeName][$hookType] : array(); - if (!$this->isHookExists($className, $entityHookData)) { - $hookData[$normalizedScopeName][$hookType][$className] = $className::$order; + if (!$this->hookExists($className, $entityHookData)) { + $hookData[$normalizedScopeName][$hookType][] = array( + 'className' => $className, + 'order' => $className::$order + ); } } } @@ -198,7 +195,7 @@ class HookManager { foreach ($hooks as $scopeName => &$scopeHooks) { foreach ($scopeHooks as $hookName => &$hookList) { - asort($hookList); + usort($hookList, array($this, 'cmpHooks')); } } @@ -206,21 +203,38 @@ class HookManager } /** - * Merge hooks for two entities + * Get sorted hook list * - * @param array $hookList1 - * @param array $hookList2 + * @param string $scope + * @param string $hookName * * @return array */ - protected function mergeHooks(array $hookList1, array $hookList2) + protected function getHookList($scope, $hookName) { - $mergedHookList = array_merge_recursive($hookList1, $hookList2); - foreach ($mergedHookList as $hookType => &$hookList) { - asort($hookList); + $key = $scope . '_' . $hookName; + + if (!isset($this->hookListHash[$key])) { + $hookList = array(); + + if (isset($this->data['Common'][$hookName])) { + $hookList = $this->data['Common'][$hookName]; + } + + if (isset($this->data[$scope][$hookName])) { + $hookList = array_merge($hookList, $this->data[$scope][$hookName]); + usort($hookList, array($this, 'cmpHooks')); + } + + $normalizedList = array(); + foreach ($hookList as $hookData) { + $normalizedList[] = $hookData['className']; + } + + $this->hookListHash[$key] = $normalizedList; } - return $mergedHookList; + return $this->hookListHash[$key]; } /** @@ -231,16 +245,25 @@ class HookManager * * @return boolean */ - protected function isHookExists($className, array $hookData) + protected function hookExists($className, array $hookData) { $class = preg_replace('/^.*\\\(.*)$/', '$1', $className); - foreach ($hookData as $hookName => $hookOrder) { - if (preg_match('/\\\\'.$class.'$/', $hookName)) { + foreach ($hookData as $hookData) { + if (preg_match('/\\\\'.$class.'$/', $hookData['className'])) { return true; } } return false; } + + protected function cmpHooks($a, $b) + { + if ($a['order'] == $b['order']) { + return 0; + } + + return ($a['order'] < $b['order']) ? -1 : 1; + } } \ No newline at end of file diff --git a/tests/unit/Espo/Core/HookManagerTest.php b/tests/unit/Espo/Core/HookManagerTest.php index e9fb248fe9..d4262b37a1 100644 --- a/tests/unit/Espo/Core/HookManagerTest.php +++ b/tests/unit/Espo/Core/HookManagerTest.php @@ -76,11 +76,26 @@ class HookManagerTest extends \PHPUnit_Framework_TestCase '\\Espo\\Hooks\\Note\\Notifications' => 14, ); - $this->assertTrue( $this->reflection->invokeMethod('isHookExists', array('\\Espo\\Hooks\\Note\\Mentions', $data)) ); - $this->assertTrue( $this->reflection->invokeMethod('isHookExists', array('\\Espo\\Modules\\Crm\\Hooks\\Note\\Mentions', $data)) ); - $this->assertTrue( $this->reflection->invokeMethod('isHookExists', array('\\Espo\\Modules\\Test\\Hooks\\Note\\Mentions', $data)) ); - $this->assertTrue( $this->reflection->invokeMethod('isHookExists', array('\\Espo\\Modules\\Test\\Hooks\\Common\\Stream', $data)) ); - $this->assertFalse( $this->reflection->invokeMethod('isHookExists', array('\\Espo\\Hooks\\Note\\TestHook', $data)) ); + $data = array ( + array ( + 'className' => '\\Espo\\Hooks\\Note\\Stream', + 'order' => 8, + ), + array ( + 'className' => '\\Espo\\Hooks\\Note\\Mentions', + 'order' => 9, + ), + array ( + 'className' => '\\Espo\\Hooks\\Note\\Notifications', + 'order' => 14, + ), + ); + + $this->assertTrue( $this->reflection->invokeMethod('hookExists', array('\\Espo\\Hooks\\Note\\Mentions', $data)) ); + $this->assertTrue( $this->reflection->invokeMethod('hookExists', array('\\Espo\\Modules\\Crm\\Hooks\\Note\\Mentions', $data)) ); + $this->assertTrue( $this->reflection->invokeMethod('hookExists', array('\\Espo\\Modules\\Test\\Hooks\\Note\\Mentions', $data)) ); + $this->assertTrue( $this->reflection->invokeMethod('hookExists', array('\\Espo\\Modules\\Test\\Hooks\\Common\\Stream', $data)) ); + $this->assertFalse( $this->reflection->invokeMethod('hookExists', array('\\Espo\\Hooks\\Note\\TestHook', $data)) ); } public function testSortHooks() @@ -90,26 +105,50 @@ class HookManagerTest extends \PHPUnit_Framework_TestCase array ( 'afterSave' => array ( - '\\Espo\\Hooks\\Common\\AssignmentEmailNotification' => 9, - '\\Espo\\Hooks\\Common\\Notifications' => 10, - '\\Espo\\Hooks\\Common\\Stream' => 9, + array ( + 'className' => '\\Espo\\Hooks\\Common\\AssignmentEmailNotification', + 'order' => 9, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\Notifications', + 'order' => 10, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\Stream', + 'order' => 9, + ), ), 'beforeSave' => array ( - '\\Espo\\Hooks\\Common\\Formula' => 5, - '\\Espo\\Hooks\\Common\\NextNumber' => 10, - '\\Espo\\Hooks\\Common\\CurrencyConverted' => 1, + array ( + 'className' => '\\Espo\\Hooks\\Common\\Formula', + 'order' => 5, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\NextNumber', + 'order' => 10, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\CurrencyConverted', + 'order' => 1, + ), ), ), 'Note' => array ( 'beforeSave' => array ( - '\\Espo\\Hooks\\Note\\Mentions' => 9, + array ( + 'className' => '\\Espo\\Hooks\\Note\\Mentions', + 'order' => 9, + ), ), 'afterSave' => array ( - '\\Espo\\Hooks\\Note\\Notifications' => 14, + array ( + 'className' => '\\Espo\\Hooks\\Note\\Notifications', + 'order' => 14, + ), ), ), ); @@ -119,26 +158,50 @@ class HookManagerTest extends \PHPUnit_Framework_TestCase array ( 'afterSave' => array ( - '\\Espo\\Hooks\\Common\\AssignmentEmailNotification' => 9, - '\\Espo\\Hooks\\Common\\Stream' => 9, - '\\Espo\\Hooks\\Common\\Notifications' => 10, + array ( + 'className' => '\\Espo\\Hooks\\Common\\AssignmentEmailNotification', + 'order' => 9, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\Stream', + 'order' => 9, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\Notifications', + 'order' => 10, + ), ), 'beforeSave' => array ( - '\\Espo\\Hooks\\Common\\CurrencyConverted' => 1, - '\\Espo\\Hooks\\Common\\Formula' => 5, - '\\Espo\\Hooks\\Common\\NextNumber' => 10, + array ( + 'className' => '\\Espo\\Hooks\\Common\\CurrencyConverted', + 'order' => 1, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\Formula', + 'order' => 5, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\NextNumber', + 'order' => 10, + ), ), ), 'Note' => array ( 'beforeSave' => array ( - '\\Espo\\Hooks\\Note\\Mentions' => 9, + array ( + 'className' => '\\Espo\\Hooks\\Note\\Mentions', + 'order' => 9, + ), ), 'afterSave' => array ( - '\\Espo\\Hooks\\Note\\Notifications' => 14, + array ( + 'className' => '\\Espo\\Hooks\\Note\\Notifications', + 'order' => 14, + ), ), ), ); @@ -174,7 +237,10 @@ class HookManagerTest extends \PHPUnit_Framework_TestCase array ( 'beforeSave' => array ( - '\\tests\\unit\\testData\\Hooks\\testCase1\\custom\\Espo\\Custom\\Hooks\\Note\\Mentions' => 7, + array ( + 'className' => '\\tests\\unit\\testData\\Hooks\\testCase1\\custom\\Espo\\Custom\\Hooks\\Note\\Mentions', + 'order' => 7, + ), ), ), ); @@ -210,7 +276,10 @@ class HookManagerTest extends \PHPUnit_Framework_TestCase array ( 'beforeSave' => array ( - '\\tests\\unit\\testData\\Hooks\\testCase2\\application\\Espo\\Modules\\Crm\\Hooks\\Note\\Mentions' => 9, + array ( + 'className' => '\\tests\\unit\\testData\\Hooks\\testCase2\\application\\Espo\\Modules\\Crm\\Hooks\\Note\\Mentions', + 'order' => 9, + ), ), ), ); @@ -246,7 +315,10 @@ class HookManagerTest extends \PHPUnit_Framework_TestCase array ( 'beforeSave' => array ( - '\\tests\\unit\\testData\\Hooks\\testCase2\\application\\Espo\\Modules\\Test\\Hooks\\Note\\Mentions' => 9, + array ( + 'className' => '\\tests\\unit\\testData\\Hooks\\testCase2\\application\\Espo\\Modules\\Test\\Hooks\\Note\\Mentions', + 'order' => 9, + ), ), ), ); @@ -280,7 +352,10 @@ class HookManagerTest extends \PHPUnit_Framework_TestCase array ( 'beforeSave' => array ( - '\\tests\\unit\\testData\\Hooks\\testCase3\\application\\Espo\\Hooks\\Note\\Mentions' => 9, + array ( + 'className' => '\\tests\\unit\\testData\\Hooks\\testCase3\\application\\Espo\\Hooks\\Note\\Mentions', + 'order' => 9, + ), ), ), ); @@ -288,54 +363,78 @@ class HookManagerTest extends \PHPUnit_Framework_TestCase $this->assertEquals($result, $this->reflection->getProperty('data')); } - public function testMergeHooks() + public function testGetHookList() { - $data = array ( + $this->reflection->setProperty('data', array ( 'Common' => array ( 'afterSave' => array ( - '\\Espo\\Hooks\\Common\\AssignmentEmailNotification' => 7, - '\\Espo\\Hooks\\Common\\Stream' => 9, - '\\Espo\\Hooks\\Common\\Notifications' => 10, + array ( + 'className' => '\\Espo\\Hooks\\Common\\AssignmentEmailNotification', + 'order' => 9, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\Stream', + 'order' => 9, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\Notifications', + 'order' => 10, + ), ), 'beforeSave' => array ( - '\\Espo\\Hooks\\Common\\CurrencyConverted' => 1, - '\\Espo\\Hooks\\Common\\Formula' => 5, - '\\Espo\\Hooks\\Common\\NextNumber' => 10, + array ( + 'className' => '\\Espo\\Hooks\\Common\\CurrencyConverted', + 'order' => 1, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\Formula', + 'order' => 5, + ), + array ( + 'className' => '\\Espo\\Hooks\\Common\\NextNumber', + 'order' => 10, + ), ), ), 'Note' => array ( 'beforeSave' => array ( - '\\Espo\\Hooks\\Note\\Mentions' => 9, + array ( + 'className' => '\\Espo\\Hooks\\Note\\Mentions', + 'order' => 9, + ), ), 'afterSave' => array ( - '\\Espo\\Hooks\\Note\\Notifications' => 8, + array ( + 'className' => '\\Espo\\Hooks\\Note\\Btest', + 'order' => 9, + ), + array ( + 'className' => '\\Espo\\Hooks\\Note\\Notifications', + 'order' => 14, + ), ), ), + )); + + $resultBeforeSave = array( + '\\Espo\\Hooks\\Common\\CurrencyConverted', + '\\Espo\\Hooks\\Common\\Formula', + '\\Espo\\Hooks\\Note\\Mentions', + '\\Espo\\Hooks\\Common\\NextNumber', ); - $result = array( - 'afterSave' => - array ( - '\\Espo\\Hooks\\Common\\AssignmentEmailNotification' => 7, - '\\Espo\\Hooks\\Note\\Notifications' => 8, - '\\Espo\\Hooks\\Common\\Stream' => 9, - '\\Espo\\Hooks\\Common\\Notifications' => 10, - ), - 'beforeSave' => - array ( - '\\Espo\\Hooks\\Common\\CurrencyConverted' => 1, - '\\Espo\\Hooks\\Common\\Formula' => 5, - '\\Espo\\Hooks\\Note\\Mentions' => 9, - '\\Espo\\Hooks\\Common\\NextNumber' => 10, - ), + $resultAfterSave = array( + '\\Espo\\Hooks\\Common\\AssignmentEmailNotification', + '\\Espo\\Hooks\\Note\\Btest', + '\\Espo\\Hooks\\Common\\Stream', + '\\Espo\\Hooks\\Common\\Notifications', + '\\Espo\\Hooks\\Note\\Notifications', ); - - $this->assertEquals($result, $this->reflection->invokeMethod('mergeHooks', array($data['Common'], $data['Note']))); } } \ No newline at end of file From 0c993c069d604026b87a22fc052009fb3b238b6b Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 13 Nov 2017 14:43:09 +0200 Subject: [PATCH 11/14] preferences: auto-follow improvements --- application/Espo/Entities/User.php | 10 ++++++++ application/Espo/Hooks/Common/Stream.php | 23 +++++++++++++++++-- application/Espo/Repositories/Preferences.php | 7 +++--- .../Resources/i18n/de_DE/Preferences.json | 3 ++- .../Resources/i18n/en_US/Preferences.json | 15 ++++++++---- .../Espo/Resources/i18n/en_US/Settings.json | 2 +- .../Resources/layouts/Preferences/detail.json | 12 ++++++++++ .../Resources/metadata/app/aclPortal.json | 5 +++- .../metadata/entityDefs/Preferences.json | 13 ++++++++++- client/src/views/preferences/record/edit.js | 10 ++++++++ 10 files changed, 86 insertions(+), 14 deletions(-) diff --git a/application/Espo/Entities/User.php b/application/Espo/Entities/User.php index 5eae30659b..bad4f58ef4 100644 --- a/application/Espo/Entities/User.php +++ b/application/Espo/Entities/User.php @@ -36,11 +36,21 @@ class User extends \Espo\Core\Entities\Person return $this->get('isAdmin'); } + public function isSystem() + { + return $this->id === 'system'; + } + public function isActive() { return $this->get('isActive'); } + public function isPortal() + { + return $this->isPortalUser(); + } + public function isPortalUser() { return $this->get('isPortalUser'); diff --git a/application/Espo/Hooks/Common/Stream.php b/application/Espo/Hooks/Common/Stream.php index 39aed27732..c6dcb862bf 100644 --- a/application/Espo/Hooks/Common/Stream.php +++ b/application/Espo/Hooks/Common/Stream.php @@ -56,6 +56,11 @@ class Stream extends \Espo\Core\Hooks\Base return $this->getInjection('serviceFactory'); } + protected function getPreferences() + { + return $this->getInjection('container')->get('preferences'); + } + protected function checkHasStream(Entity $entity) { $entityType = $entity->getEntityType(); @@ -185,9 +190,23 @@ class Stream extends \Espo\Core\Hooks\Base $createdById = $entity->get('createdById'); if ( - ($this->getConfig()->get('followCreatedEntities') || $this->getUser()->get('isPortalUser')) + !$this->getUser()->isSystem() && - !empty($createdById) + $createdById + && + $createdById === $this->getUser()->id + && + ( + $this->getUser()->isPortalUser() + || + $this->getPreferences()->get('followCreatedEntities') + || + ( + is_array($this->getPreferences()->get('followCreatedEntityTypeList')) + && + in_array($entityType, $this->getPreferences()->get('followCreatedEntityTypeList')) + ) + ) ) { $userIdList[] = $createdById; } diff --git a/application/Espo/Repositories/Preferences.php b/application/Espo/Repositories/Preferences.php index 2a5c0997e0..ff0d1d34f3 100644 --- a/application/Espo/Repositories/Preferences.php +++ b/application/Espo/Repositories/Preferences.php @@ -34,11 +34,12 @@ use Espo\Core\Utils\Json; class Preferences extends \Espo\Core\ORM\Repository { - protected $defaultAttributeListFromSettings = array( + protected $defaultAttributeListFromSettings = [ 'decimalMark', 'thousandSeparator', - 'exportDelimiter' - ); + 'exportDelimiter', + 'followCreatedEntities' + ]; protected $data = array(); diff --git a/application/Espo/Resources/i18n/de_DE/Preferences.json b/application/Espo/Resources/i18n/de_DE/Preferences.json index 2f43c877ad..fd13916561 100644 --- a/application/Espo/Resources/i18n/de_DE/Preferences.json +++ b/application/Espo/Resources/i18n/de_DE/Preferences.json @@ -31,7 +31,8 @@ "emailReplyToAllByDefault": "Standardmäßig Allen antworten", "dashboardLayout": "Dashboard Layout", "emailReplyForceHtml": "E-Mail Antwort als HTML", - "doNotFillAssignedUserIfNotRequired": "Zugwiesenen Benutzer nicht ausfüllen wenn optional" + "doNotFillAssignedUserIfNotRequired": "Zugwiesenen Benutzer nicht ausfüllen wenn optional", + "followCreatedEntities": "Eigene Einträge abonnieren" }, "options": { "weekStart": { diff --git a/application/Espo/Resources/i18n/en_US/Preferences.json b/application/Espo/Resources/i18n/en_US/Preferences.json index d68a28a9c4..7c61201c04 100644 --- a/application/Espo/Resources/i18n/en_US/Preferences.json +++ b/application/Espo/Resources/i18n/en_US/Preferences.json @@ -21,18 +21,20 @@ "receiveAssignmentEmailNotifications": "Email notifications upon assignment", "receiveMentionEmailNotifications": "Email notifications about mentions in posts", "receiveStreamEmailNotifications": "Email notifications about posts and status updates", - "autoFollowEntityTypeList": "Auto-Follow", + "autoFollowEntityTypeList": "Global Auto-Follow", "signature": "Email Signature", "dashboardTabList": "Tab List", "defaultReminders": "Default Reminders", "theme": "Theme", "useCustomTabList": "Custom Tab List", "tabList": "Tab List", - "emailReplyToAllByDefault": "Email Reply to All by Default", + "emailReplyToAllByDefault": "Email Reply to all by default", "dashboardLayout": "Dashboard Layout", "emailReplyForceHtml": "Email Reply in HTML", - "doNotFillAssignedUserIfNotRequired": "Do not fill Assigned User if not required", - "followEntityOnStreamPost": "Auto-follow entity after posting in Stream" + "doNotFillAssignedUserIfNotRequired": "Do not pre-fill assigned user on record creation", + "followEntityOnStreamPost": "Auto-follow record after posting in Stream", + "followCreatedEntities": "Auto-follow created records", + "followCreatedEntityTypeList": "Auto-follow created records of specific entity types" }, "links": { }, @@ -50,6 +52,9 @@ "Locale": "Locale" }, "tooltips": { - "autoFollowEntityTypeList": "User will automatically follow all new records of the selected entity types, will see information in the stream and receive notifications." + "autoFollowEntityTypeList": "Automatically follow ALL new records (created by any user) of the selected entity types. To be able to see information in the stream and receive notifications about all records in the system.", + "doNotFillAssignedUserIfNotRequired": "When create record assigned user won't be filled with own user unless the field is required.", + "followCreatedEntities": "When create new records they will be automatically followed even if assigned to another user.", + "followCreatedEntityTypeList": "When create new records of selected entity types they will be followed automatically even if assigned to another user." } } diff --git a/application/Espo/Resources/i18n/en_US/Settings.json b/application/Espo/Resources/i18n/en_US/Settings.json index 95bbf753e5..7e3335ca3e 100644 --- a/application/Espo/Resources/i18n/en_US/Settings.json +++ b/application/Espo/Resources/i18n/en_US/Settings.json @@ -63,7 +63,7 @@ "streamEmailNotificationsEntityList": "Stream email notifications scopes", "b2cMode": "B2C Mode", "avatarsDisabled": "Disable Avatars", - "followCreatedEntities": "Follow Created Entities", + "followCreatedEntities": "Follow created records", "displayListViewRecordCount": "Display Total Count (on List View)", "theme": "Theme", "userThemesDisabled": "Disable User Themes", diff --git a/application/Espo/Resources/layouts/Preferences/detail.json b/application/Espo/Resources/layouts/Preferences/detail.json index da8e45b3ca..c292fbe0c3 100644 --- a/application/Espo/Resources/layouts/Preferences/detail.json +++ b/application/Espo/Resources/layouts/Preferences/detail.json @@ -59,6 +59,18 @@ { "name": "followEntityOnStreamPost" } + ], + [ + false, + { + "name": "followCreatedEntities" + } + ], + [ + false, + { + "name": "followCreatedEntityTypeList" + } ] ] }, diff --git a/application/Espo/Resources/metadata/app/aclPortal.json b/application/Espo/Resources/metadata/app/aclPortal.json index 08e0edb680..c4319e9bcb 100644 --- a/application/Espo/Resources/metadata/app/aclPortal.json +++ b/application/Espo/Resources/metadata/app/aclPortal.json @@ -60,7 +60,10 @@ "autoFollowEntityTypeList": false, "emailReplyForceHtml": false, "emailReplyToAllByDefault": false, - "signature": false + "signature": false, + "followCreatedEntities": false, + "followEntityOnStreamPost": false, + "doNotFillAssignedUserIfNotRequired": false }, "Call": { "reminders": false diff --git a/application/Espo/Resources/metadata/entityDefs/Preferences.json b/application/Espo/Resources/metadata/entityDefs/Preferences.json index aa5a236542..d1cafa0bfb 100644 --- a/application/Espo/Resources/metadata/entityDefs/Preferences.json +++ b/application/Espo/Resources/metadata/entityDefs/Preferences.json @@ -150,11 +150,22 @@ "notStorable": true }, "doNotFillAssignedUserIfNotRequired": { - "type": "bool" + "type": "bool", + "tooltip": true }, "followEntityOnStreamPost": { "type": "bool", "default": true + }, + "followCreatedEntities": { + "type": "bool", + "tooltip": true + }, + "followCreatedEntityTypeList": { + "type": "multiEnum", + "view": "views/preferences/fields/auto-follow-entity-type-list", + "translation": "Global.scopeNamesPlural", + "tooltip": true } } } diff --git a/client/src/views/preferences/record/edit.js b/client/src/views/preferences/record/edit.js index 028ade0ec8..a34df1126d 100644 --- a/client/src/views/preferences/record/edit.js +++ b/client/src/views/preferences/record/edit.js @@ -100,6 +100,8 @@ Espo.define('views/preferences/record/edit', 'views/record/edit', function (Dep) this.hideField('dashboardLayout'); } + this.controlFollowCreatedEntityListVisibility(); + this.listenTo(this.model, 'change:followCreatedEntities', this.controlFollowCreatedEntityListVisibility); var hideNotificationPanel = true; if (!this.getConfig().get('assignmentEmailNotifications') || this.model.get('isPortalUser')) { @@ -153,6 +155,14 @@ Espo.define('views/preferences/record/edit', 'views/record/edit', function (Dep) }, this); }, + controlFollowCreatedEntityListVisibility: function () { + if (!this.model.get('followCreatedEntities')) { + this.showField('followCreatedEntityTypeList'); + } else { + this.hideField('followCreatedEntityTypeList'); + } + }, + actionReset: function () { this.confirm(this.translate('resetPreferencesConfirmation', 'messages'), function () { $.ajax({ From 5fff3bd09e5501d00f91437714663899f9e97030 Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 13 Nov 2017 15:13:27 +0200 Subject: [PATCH 12/14] dynamic logic: support email address and phone number in conditions --- .../Espo/Resources/metadata/clientDefs/DynamicLogic.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/application/Espo/Resources/metadata/clientDefs/DynamicLogic.json b/application/Espo/Resources/metadata/clientDefs/DynamicLogic.json index 15dfa5a2c3..9334299d35 100644 --- a/application/Espo/Resources/metadata/clientDefs/DynamicLogic.json +++ b/application/Espo/Resources/metadata/clientDefs/DynamicLogic.json @@ -98,6 +98,14 @@ "view": "views/admin/dynamic-logic/conditions/field-types/base", "typeList": ["equals", "notEquals", "isEmpty", "isNotEmpty"] }, + "email": { + "view": "views/admin/dynamic-logic/conditions/field-types/base", + "typeList": ["isEmpty", "isNotEmpty"] + }, + "phone": { + "view": "views/admin/dynamic-logic/conditions/field-types/base", + "typeList": ["isEmpty", "isNotEmpty"] + }, "text": { "view": "views/admin/dynamic-logic/conditions/field-types/base", "typeList": ["isEmpty", "isNotEmpty"] From 3c6fc62a8ae6eae8767545bad724bd13e8c18c15 Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 13 Nov 2017 15:33:35 +0200 Subject: [PATCH 13/14] lead w/o name --- .../Espo/Modules/Crm/Entities/Lead.php | 15 +++++++++++- .../Resources/metadata/clientDefs/Lead.json | 24 ++++++++++++++++++- .../Resources/metadata/entityDefs/Lead.json | 1 - client/src/views/fields/base.js | 8 +++++-- client/src/views/fields/person-name.js | 8 +++---- 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/application/Espo/Modules/Crm/Entities/Lead.php b/application/Espo/Modules/Crm/Entities/Lead.php index 8ed65c1a37..6634a7c947 100644 --- a/application/Espo/Modules/Crm/Entities/Lead.php +++ b/application/Espo/Modules/Crm/Entities/Lead.php @@ -25,12 +25,25 @@ * * 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\Modules\Crm\Entities; class Lead extends \Espo\Core\Entities\Person { + protected function _getName() + { + if (!array_key_exists('name', $this->valuesContainer) || !$this->valuesContainer['name']) { + if ($this->get('accountName')) { + return $this->get('accountName'); + } else if ($this->get('emailAddress')) { + return $this->get('emailAddress'); + } else if ($this->get('phoneNumber')) { + return $this->get('phoneNumber'); + } + } + return $this->valuesContainer['name']; + } } diff --git a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Lead.json b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Lead.json index 7b8614c83a..c57b1e14db 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Lead.json +++ b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Lead.json @@ -122,5 +122,27 @@ "style": "success" } ], - "boolFilterList": ["onlyMy"] + "boolFilterList": ["onlyMy"], + "dynamicLogic": { + "fields": { + "name": { + "required": { + "conditionGroup": [ + { + "type": "isEmpty", + "attribute": "accountName" + }, + { + "type": "isEmpty", + "attribute": "emailAddress" + }, + { + "type": "isEmpty", + "attribute": "phoneNumber" + } + ] + } + } + } + } } diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Lead.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Lead.json index 4ec060913b..115b734c92 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Lead.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Lead.json @@ -15,7 +15,6 @@ "lastName": { "type": "varchar", "maxLength": 100, - "required": true, "default":"" }, "title": { diff --git a/client/src/views/fields/base.js b/client/src/views/fields/base.js index b05ed6d7f9..93d19ca2ed 100644 --- a/client/src/views/fields/base.js +++ b/client/src/views/fields/base.js @@ -262,13 +262,13 @@ Espo.define('views/fields/base', 'view', function (Dep) { this.on('after:render', function () { if (this.mode === 'edit') { - if (this.isRequired()) { + if (this.hasRequiredMarker()) { this.showRequiredSign(); } else { this.hideRequiredSign(); } } else { - if (this.isRequired()) { + if (!this.hasRequiredMarker()) { this.hideRequiredSign(); } } @@ -580,6 +580,10 @@ Espo.define('views/fields/base', 'view', function (Dep) { } }, + hasRequiredMarker: function () { + return this.isRequired(); + }, + fetchToModel: function () { this.model.set(this.fetch(), {silent: true}); }, diff --git a/client/src/views/fields/person-name.js b/client/src/views/fields/person-name.js index 65f8da62d6..17e4f4cf4e 100644 --- a/client/src/views/fields/person-name.js +++ b/client/src/views/fields/person-name.js @@ -82,7 +82,7 @@ Espo.define('views/fields/person-name', 'views/fields/varchar', function (Dep) { }, validateRequired: function () { - var isRequired = this.model.getFieldParam(this.name, 'required'); + var isRequired = this.isRequired(); var validate = function (name) { if (this.model.isRequired(name)) { @@ -109,11 +109,11 @@ Espo.define('views/fields/person-name', 'views/fields/varchar', function (Dep) { return result; }, - isRequired: function () { + hasRequiredMarker: function () { + if (this.isRequired()) return true; return this.model.getFieldParam(this.salutationField, 'required') || this.model.getFieldParam(this.firstField, 'required') || - this.model.getFieldParam(this.lastField, 'required') || - this.model.getFieldParam(this.name, 'required'); + this.model.getFieldParam(this.lastField, 'required'); }, fetch: function (form) { From e2f6e3324a32fb8158b7be1dbc5e1b8962e9f3a1 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 14 Nov 2017 10:57:02 +0200 Subject: [PATCH 14/14] admin notifications param --- .../Espo/Core/Utils/AdminNotificationManager.php | 2 +- application/Espo/Core/defaults/config.php | 1 + .../Espo/Resources/i18n/en_US/Settings.json | 6 ++++-- .../Resources/layouts/Settings/notifications.json | 14 ++++++++++---- .../Resources/metadata/entityDefs/Settings.json | 3 +++ 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/application/Espo/Core/Utils/AdminNotificationManager.php b/application/Espo/Core/Utils/AdminNotificationManager.php index 236ec7dba3..084b258ed8 100644 --- a/application/Espo/Core/Utils/AdminNotificationManager.php +++ b/application/Espo/Core/Utils/AdminNotificationManager.php @@ -65,7 +65,7 @@ class AdminNotificationManager { $notificationList = []; - if ($this->getConfig()->get('adminNotificationsDisabled')) { + if (!$this->getConfig()->get('adminNotifications')) { return []; } diff --git a/application/Espo/Core/defaults/config.php b/application/Espo/Core/defaults/config.php index 3a65bc1573..8e1fb7999b 100644 --- a/application/Espo/Core/defaults/config.php +++ b/application/Espo/Core/defaults/config.php @@ -104,6 +104,7 @@ return array ( 'tabList' => ["Account", "Contact", "Lead", "Opportunity", "Case", "Email", "Calendar", "Meeting", "Call", "Task", "_delimiter_", "Document", "Campaign", "KnowledgeBaseArticle", "Stream", "User"], 'quickCreateList' => ["Account", "Contact", "Lead", "Opportunity", "Meeting", "Call", "Task", "Case", "Email"], 'exportDisabled' => false, + 'adminNotifications' => true, 'assignmentEmailNotifications' => false, 'assignmentEmailNotificationsEntityList' => ['Lead', 'Opportunity', 'Task', 'Case'], 'assignmentNotificationsEntityList' => ['Meeting', 'Call', 'Task', 'Email'], diff --git a/application/Espo/Resources/i18n/en_US/Settings.json b/application/Espo/Resources/i18n/en_US/Settings.json index 7e3335ca3e..e355f9acb6 100644 --- a/application/Espo/Resources/i18n/en_US/Settings.json +++ b/application/Espo/Resources/i18n/en_US/Settings.json @@ -89,7 +89,8 @@ "currencyFormat": "Currency Format", "currencyDecimalPlaces": "Currency Decimal Places", "aclStrictMode": "ACL Strict Mode", - "aclAllowDeleteCreated": "Allow to remove created records" + "aclAllowDeleteCreated": "Allow to remove created records", + "adminNotifications": "System notifications in administration panel" }, "options": { "weekStart": { @@ -148,7 +149,8 @@ "Mass Email": "Mass Email", "Test Connection": "Test Connection", "Connecting": "Connecting...", - "Activities": "Activities" + "Activities": "Activities", + "Admin Notifications": "Admin Notifications" }, "messages": { "ldapTestConnection": "The connection successfully established." diff --git a/application/Espo/Resources/layouts/Settings/notifications.json b/application/Espo/Resources/layouts/Settings/notifications.json index 6df89d7cbf..882fbb6cab 100644 --- a/application/Espo/Resources/layouts/Settings/notifications.json +++ b/application/Espo/Resources/layouts/Settings/notifications.json @@ -2,17 +2,23 @@ { "label": "In-app Notifications", "rows": [ - [{"name": "assignmentNotificationsEntityList"}], - [{"name": "notificationSoundsDisabled"}] + [{"name": "assignmentNotificationsEntityList"}, false], + [{"name": "notificationSoundsDisabled"}, false] ] }, { "label": "Email Notifications", "rows": [ [{"name": "assignmentEmailNotifications"}, {"name": "mentionEmailNotifications"}], - [{"name": "assignmentEmailNotificationsEntityList"}], + [{"name": "assignmentEmailNotificationsEntityList"}, false], [{"name": "streamEmailNotifications"}, {"name": "portalStreamEmailNotifications"}], - [{"name": "streamEmailNotificationsEntityList"}] + [{"name": "streamEmailNotificationsEntityList"}, false] + ] + }, + { + "label": "Admin Notifications", + "rows": [ + [{"name": "adminNotifications"}, false] ] } ] \ No newline at end of file diff --git a/application/Espo/Resources/metadata/entityDefs/Settings.json b/application/Espo/Resources/metadata/entityDefs/Settings.json index 37dfba1d00..004cd89ae2 100644 --- a/application/Espo/Resources/metadata/entityDefs/Settings.json +++ b/application/Espo/Resources/metadata/entityDefs/Settings.json @@ -433,6 +433,9 @@ "max": 200, "default": 20, "required": true + }, + "adminNotifications": { + "type": "bool" } } }