diff --git a/application/Espo/Core/Export/Xlsx.php b/application/Espo/Core/Export/Xlsx.php index a2b4a9fa37..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; @@ -304,8 +312,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 +423,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': { 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/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/application/Espo/Core/Mail/Parsers/MailMimeParser.php b/application/Espo/Core/Mail/Parsers/MailMimeParser.php index f802d372e4..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); @@ -174,7 +196,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 +213,7 @@ class MailMimeParser if ($disposition == 'inline') { $attachment->set('role', 'Inline Attachment'); } else { + $disposition = 'attachment'; $attachment->set('role', 'Attachment'); } @@ -201,8 +229,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..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); @@ -186,8 +204,10 @@ class PhpMimeMailParser } 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/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/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/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/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() { 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/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/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..e355f9acb6 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", @@ -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/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/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/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/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"] 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/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" } } } 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/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) { 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) { 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({ 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", 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 { 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(' 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