From 9c5a8c63ceed51e74220c29bade9dc87897f785b Mon Sep 17 00:00:00 2001 From: yuri Date: Fri, 19 Jan 2018 14:44:32 +0200 Subject: [PATCH 01/92] portal preferences layout fix --- .../Preferences/{portal/detail.json => detailPortal.json} | 0 client/src/views/preferences/record/edit.js | 4 ++++ 2 files changed, 4 insertions(+) rename application/Espo/Resources/layouts/Preferences/{portal/detail.json => detailPortal.json} (100%) diff --git a/application/Espo/Resources/layouts/Preferences/portal/detail.json b/application/Espo/Resources/layouts/Preferences/detailPortal.json similarity index 100% rename from application/Espo/Resources/layouts/Preferences/portal/detail.json rename to application/Espo/Resources/layouts/Preferences/detailPortal.json diff --git a/client/src/views/preferences/record/edit.js b/client/src/views/preferences/record/edit.js index fd345c61ec..a4debfd01b 100644 --- a/client/src/views/preferences/record/edit.js +++ b/client/src/views/preferences/record/edit.js @@ -87,6 +87,10 @@ Espo.define('views/preferences/record/edit', 'views/record/edit', function (Dep) setup: function () { Dep.prototype.setup.call(this); + if (this.model.get('isPortalUser')) { + this.layoutName = 'detailPortal'; + } + if (this.model.id == this.getUser().id) { this.on('after:save', function () { var data = this.model.toJSON(); From b06736351b5fa60e4502c8acb9db70974310fc5e Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 22 Jan 2018 12:46:25 +0200 Subject: [PATCH 02/92] fix lang --- application/Espo/Resources/i18n/en_US/Settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/Espo/Resources/i18n/en_US/Settings.json b/application/Espo/Resources/i18n/en_US/Settings.json index 4f21e4b241..d0fe49a51c 100644 --- a/application/Espo/Resources/i18n/en_US/Settings.json +++ b/application/Espo/Resources/i18n/en_US/Settings.json @@ -68,10 +68,10 @@ "theme": "Theme", "userThemesDisabled": "Disable User Themes", "emailMessageMaxSize": "Email Max Size (Mb)", - "massEmailMaxPerHourCount": "Max count of emails sent per hour", + "massEmailMaxPerHourCount": "Max number of emails sent per hour", "personalEmailMaxPortionSize": "Max email portion size for personal account fetching", "inboundEmailMaxPortionSize": "Max email portion size for group account fetching", - "maxEmailAccountCount": "Max count of personal email accounts per user", + "maxEmailAccountCount": "Max number of personal email accounts per user", "authTokenLifetime": "Auth Token Lifetime (hours)", "authTokenMaxIdleTime": "Auth Token Max Idle Time (hours)", "dashboardLayout": "Dashboard Layout (default)", From 7c4e1393fe7ae211db576d189ff80cca8a30c7f8 Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 22 Jan 2018 12:49:18 +0200 Subject: [PATCH 03/92] lang --- application/Espo/Core/Templates/i18n/lt_LT/BasePlus.json | 2 +- application/Espo/Core/Templates/i18n/lt_LT/Company.json | 4 ++-- application/Espo/Core/Templates/i18n/lt_LT/Person.json | 2 +- .../Espo/Modules/Crm/Resources/i18n/lt_LT/Account.json | 2 +- .../Espo/Modules/Crm/Resources/i18n/lt_LT/Campaign.json | 1 + .../Espo/Modules/Crm/Resources/i18n/lt_LT/Case.json | 2 +- .../Espo/Modules/Crm/Resources/i18n/lt_LT/Global.json | 6 +++--- .../Espo/Modules/Crm/Resources/i18n/lt_LT/Lead.json | 2 +- application/Espo/Resources/i18n/lt_LT/EntityManager.json | 3 +++ application/Espo/Resources/i18n/lt_LT/Global.json | 9 +++++---- application/Espo/Resources/i18n/lt_LT/Import.json | 2 ++ application/Espo/Resources/i18n/lt_LT/Integration.json | 1 + application/Espo/Resources/i18n/lt_LT/Settings.json | 3 +-- 13 files changed, 23 insertions(+), 16 deletions(-) diff --git a/application/Espo/Core/Templates/i18n/lt_LT/BasePlus.json b/application/Espo/Core/Templates/i18n/lt_LT/BasePlus.json index cce10944a6..7dbb435806 100644 --- a/application/Espo/Core/Templates/i18n/lt_LT/BasePlus.json +++ b/application/Espo/Core/Templates/i18n/lt_LT/BasePlus.json @@ -1,6 +1,6 @@ { "links": { - "meetings": "Susirinkimai", + "meetings": "Susitikimai", "calls": "Skambučiai", "tasks": "Užduotys" }, diff --git a/application/Espo/Core/Templates/i18n/lt_LT/Company.json b/application/Espo/Core/Templates/i18n/lt_LT/Company.json index 123ff22618..1a7cd5896f 100644 --- a/application/Espo/Core/Templates/i18n/lt_LT/Company.json +++ b/application/Espo/Core/Templates/i18n/lt_LT/Company.json @@ -2,10 +2,10 @@ "fields": { "billingAddress": "Mokėtojo adresas", "shippingAddress": "Siuntimo adresas", - "website": "Interntinė Svetainė" + "website": "Internetinė Svetainė" }, "links": { - "meetings": "Susirinkimai", + "meetings": "Susitikimai", "calls": "Skambučiai", "tasks": "Užduotys" }, diff --git a/application/Espo/Core/Templates/i18n/lt_LT/Person.json b/application/Espo/Core/Templates/i18n/lt_LT/Person.json index 3754d136cd..cafb590f15 100644 --- a/application/Espo/Core/Templates/i18n/lt_LT/Person.json +++ b/application/Espo/Core/Templates/i18n/lt_LT/Person.json @@ -3,7 +3,7 @@ "address": "Adresas" }, "links": { - "meetings": "Susirinkimai", + "meetings": "Susitikimai", "calls": "Skambučiai", "tasks": "Užduotys" }, diff --git a/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Account.json b/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Account.json index be6ab7f65f..e5db561d6d 100644 --- a/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Account.json +++ b/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Account.json @@ -2,7 +2,7 @@ "fields": { "name": "Vardas", "emailAddress": "El. paštas", - "website": "Interntinė Svetainė", + "website": "Internetinė Svetainė", "phoneNumber": "Telefonas", "billingAddress": "Mokėtojo adresas", "shippingAddress": "Siuntimo adresas", diff --git a/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Campaign.json b/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Campaign.json index c14b11b5b1..eb492dfd1e 100644 --- a/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Campaign.json +++ b/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Campaign.json @@ -12,6 +12,7 @@ "openedCount": "Atverta", "clickedCount": "Paspausta", "optedOutCount": "Pasirinkta", + "bouncedCount": "Atmesta", "leadCreatedCount": "Potencialūs klientai sukurti", "revenue": "Pajamos", "revenueConverted": "Pajamos (konvertuotos)", diff --git a/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Case.json b/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Case.json index b8bcf7f1d6..a5a46645f9 100644 --- a/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Case.json +++ b/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Case.json @@ -18,7 +18,7 @@ "account": "Įmonė", "contact": "Kontaktas (pirminis)", "Contacts": "Kontaktai", - "meetings": "Susirinkimai", + "meetings": "Susitikimai", "calls": "Skambučiai", "tasks": "Užduotys", "emails": "El. laiška", diff --git a/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Global.json b/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Global.json index f0370b09e4..247a82cccd 100644 --- a/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Global.json +++ b/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Global.json @@ -4,7 +4,7 @@ "contacts": "Kontaktai", "opportunities": "Galimybės", "leads": "Potencialūs klientai", - "meetings": "Susirinkimai", + "meetings": "Susitikimai", "calls": "Skambučiai", "tasks": "Užduotys", "emails": "El. laiška", @@ -21,7 +21,7 @@ "Lead": "Potencialus klientas", "Target": "Adresatas", "Opportunity": "Galimybė", - "Meeting": "Susirinkimas", + "Meeting": "Susitikimas", "Calendar": "Kalendorius", "Call": "Skambutis", "Task": "Užduotis", @@ -42,7 +42,7 @@ "Lead": "Potencialūs klientai", "Target": "Adresatai", "Opportunity": "Galimybės", - "Meeting": "Susirinkimai", + "Meeting": "Susitikimai", "Calendar": "Kalendorius", "Call": "Skambučiai", "Task": "Užduotys", diff --git a/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Lead.json b/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Lead.json index ad65407d1c..de411c1f40 100644 --- a/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Lead.json +++ b/application/Espo/Modules/Crm/Resources/i18n/lt_LT/Lead.json @@ -9,7 +9,7 @@ "name": "Vardas", "emailAddress": "El. paštas", "title": "Pavadinimas", - "website": "Interntinė Svetainė", + "website": "Internetinė Svetainė", "phoneNumber": "Telefonas", "accountName": "Įmonės pavadinimas", "doNotCall": "Neskambinti", diff --git a/application/Espo/Resources/i18n/lt_LT/EntityManager.json b/application/Espo/Resources/i18n/lt_LT/EntityManager.json index 1d1f33f2df..64a6a59951 100644 --- a/application/Espo/Resources/i18n/lt_LT/EntityManager.json +++ b/application/Espo/Resources/i18n/lt_LT/EntityManager.json @@ -46,6 +46,9 @@ "desc": "Mažėjančia tvarka" } }, + "messages": { + "linkAlreadyExists": "Sąsajos pavadinimo konfliktas." + }, "tooltips": { "statusField": "Šio lauko atnaujinimai įrašomi sraute.", "textFilterFields": "Laukeliai, naudojami teksto paieškoje.", diff --git a/application/Espo/Resources/i18n/lt_LT/Global.json b/application/Espo/Resources/i18n/lt_LT/Global.json index d35fa9468c..f4c18d00fd 100644 --- a/application/Espo/Resources/i18n/lt_LT/Global.json +++ b/application/Espo/Resources/i18n/lt_LT/Global.json @@ -42,7 +42,7 @@ "PhoneNumber": "Telefono numeris" }, "scopeNamesPlural": { - "Email": "El. laiška", + "Email": "El. laiškai", "User": "Vartotojai", "Team": "Komandos", "Role": "Rolės", @@ -54,7 +54,7 @@ "ExternalAccount": "Išorinės paskyros", "Extension": "Plėtiniai", "Dashboard": "Prietaisų skydelis", - "InboundEmail": "Grupės el. pašto paskyros ", + "InboundEmail": "Grupės el. pašto paskyros", "Stream": "Srautas", "Import": "Importuoti rezultatus", "Template": "Šablonai", @@ -196,6 +196,7 @@ "List View": "Rodyti kaip sąrašą\n", "Tree View": "Rodyti kaip medį", "Unlink All": "Atsieti visus", + "Total": "Iš viso", "Print to PDF": "Spausdinti į PDF", "Default": "Numatytas", "Number": "Numeris", @@ -291,7 +292,7 @@ "firstName": "Vardas", "lastName": "Pavardė", "salutationName": "Sveikinimas", - "assignedUser": "Priskirta vartotojas", + "assignedUser": "Priskirtas darbuotojas", "assignedUsers": "Priskirti vartotojai", "emailAddress": "El. paštas", "assignedUserName": "Priskirto vartotojo vardas", @@ -313,7 +314,7 @@ "children": "Antrinis" }, "links": { - "assignedUser": "Priskirta vartotojas", + "assignedUser": "Priskirtas darbuotojas", "createdBy": "Sukurta", "modifiedBy": "Koreguota", "team": "Komanda", diff --git a/application/Espo/Resources/i18n/lt_LT/Import.json b/application/Espo/Resources/i18n/lt_LT/Import.json index 0f294c6ffe..039b809670 100644 --- a/application/Espo/Resources/i18n/lt_LT/Import.json +++ b/application/Espo/Resources/i18n/lt_LT/Import.json @@ -26,6 +26,7 @@ "Header Row Value": "Antraštinės eilutės vertė", "Field": "Laukelis", "What to Import?": "Ką importuoti?", + "Entity Type": "Vedinio tipas", "What to do?": "Ką daryti?", "Properties": "Savybės", "Header Row": "Antraštinė eilutė", @@ -56,6 +57,7 @@ }, "fields": { "file": "Failas", + "entityType": "Vedinio tipas", "imported": "Importuoti įrašai", "duplicates": "Dublikuoti įrašai", "updated": "Atnaujinti įrašai", diff --git a/application/Espo/Resources/i18n/lt_LT/Integration.json b/application/Espo/Resources/i18n/lt_LT/Integration.json index 7a6486ee36..6dde9dda66 100644 --- a/application/Espo/Resources/i18n/lt_LT/Integration.json +++ b/application/Espo/Resources/i18n/lt_LT/Integration.json @@ -1,5 +1,6 @@ { "fields": { + "enabled": "Įjungta", "clientId": "Kliento ID", "redirectUri": "Nukreipti URL", "apiKey": "API raktas" diff --git a/application/Espo/Resources/i18n/lt_LT/Settings.json b/application/Espo/Resources/i18n/lt_LT/Settings.json index 52544e82a2..96c7cd7dfa 100644 --- a/application/Espo/Resources/i18n/lt_LT/Settings.json +++ b/application/Espo/Resources/i18n/lt_LT/Settings.json @@ -41,13 +41,12 @@ "exportDisabled": "Išjungti eksportavimą (leidžiama tik admionistratoriui)", "b2cMode": "B2C režimas", "avatarsDisabled": "Išjungti piktogramas", + "displayListViewRecordCount": "Rodyti visus (sąrašo rodmenyje)", "theme": "Tema", "userThemesDisabled": "Išjungti naudotojo temas", "emailMessageMaxSize": "Maksimalus el. laiško dydis (Mb)", - "massEmailMaxPerHourCount": "Maksimalus per valandą siunčiamų el. laiškų skaičius", "personalEmailMaxPortionSize": "Maksimalus el. pašto siuntimo dydis asmeninei paskyrai", "inboundEmailMaxPortionSize": "Maksimalus el. laiškų kiekis grupės paskyrai laikomas", - "maxEmailAccountCount": "Maksimalus el. pašto paskyrų kiekis vienam vartotojui", "authTokenLifetime": "Autentikavimo žymės gyvavimas (valandomis)", "authTokenMaxIdleTime": "Autentikavimo žymės maksimalus laikas (valandomis)", "dashboardLayout": "Prietaisų skydelio išdėstymas (numatytas)", From e3e25785c7639b9517c7541223ce3249f24df6af Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 22 Jan 2018 14:43:57 +0200 Subject: [PATCH 04/92] excel export link multiple fields --- application/Espo/Core/Export/Xlsx.php | 19 +++++++++++++++++++ application/Espo/Services/Record.php | 8 +++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/application/Espo/Core/Export/Xlsx.php b/application/Espo/Core/Export/Xlsx.php index 6adef723b4..9294587d6d 100644 --- a/application/Espo/Core/Export/Xlsx.php +++ b/application/Espo/Core/Export/Xlsx.php @@ -78,6 +78,13 @@ class Xlsx extends \Espo\Core\Injectable } } } + foreach ($fieldList as $field) { + if ($this->getMetadata()->get(['entityDefs', $entity->getEntityType(), 'fields', $field, 'type']) === 'linkMultiple') { + if (!$entity->has($field . 'Ids')) { + $entity->loadLinkMultipleField($field); + } + } + } } public function addAdditionalAttributes($entityType, &$attributeList, $fieldList) @@ -367,6 +374,18 @@ class Xlsx extends \Espo\Core\Injectable } $sheet->setCellValue("$col$rowNumber", $value); } + } else if ($type == 'linkMultiple') { + if (array_key_exists($name . 'Ids', $row) && array_key_exists($name . 'Names', $row)) { + $nameList = []; + foreach ($row[$name . 'Ids'] as $relatedId) { + $relatedName = $relatedId; + if (property_exists($row[$name . 'Names'], $relatedId)) { + $relatedName = $row[$name . 'Names']->$relatedId; + } + $nameList[] = $relatedName; + } + $sheet->setCellValue("$col$rowNumber", implode(', ', $nameList)); + } } else { if (array_key_exists($name, $row)) { $sheet->setCellValue("$col$rowNumber", $row[$name]); diff --git a/application/Espo/Services/Record.php b/application/Espo/Services/Record.php index d77482e4fd..cca2257bae 100644 --- a/application/Espo/Services/Record.php +++ b/application/Espo/Services/Record.php @@ -1306,14 +1306,16 @@ class Record extends \Espo\Core\Services\Base return false; } - public function checkAttributeIsAllowedForExport($entity, $attribute) + public function checkAttributeIsAllowedForExport($entity, $attribute, $isExportAllFields = false) { $entity = $this->getEntityManager()->getEntity($this->getEntityType()); if (in_array($attribute, $this->internalAttributeList)) { return false; } - + if (!$isExportAllFields) { + return true; + } $isNotStorable = $entity->getAttributeParam($attribute, 'notStorable'); if (!$isNotStorable) { return true; @@ -1441,7 +1443,7 @@ class Record extends \Espo\Core\Services\Base if (in_array($attribute, $attributeListToSkip)) { continue; } - if ($this->checkAttributeIsAllowedForExport($seed, $attribute)) { + if ($this->checkAttributeIsAllowedForExport($seed, $attribute, true)) { $attributeList[] = $attribute; } } From 2900e8afb79e91f68762727651dfa21773b1922e Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 22 Jan 2018 15:12:40 +0200 Subject: [PATCH 05/92] csv export link multiple --- application/Espo/Core/Export/Csv.php | 37 ++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/application/Espo/Core/Export/Csv.php b/application/Espo/Core/Export/Csv.php index 4230a6774d..925428e050 100644 --- a/application/Espo/Core/Export/Csv.php +++ b/application/Espo/Core/Export/Csv.php @@ -29,21 +29,37 @@ namespace Espo\Core\Export; -use \Espo\Core\Exceptions\Error; +use \Espo\Exceptions\Error; + +use \Espo\Core\ORM\Entity; class Csv extends \Espo\Core\Injectable { protected $dependencyList = [ 'config', - 'preferences' + 'preferences', + 'metadata' ]; + public function loadAdditionalFields(Entity $entity, $fieldList) + { + foreach ($fieldList as $field) { + if ($this->getInjection('metadata')->get(['entityDefs', $entity->getEntityType(), 'fields', $field, 'type']) === 'linkMultiple') { + if (!$entity->has($field . 'Ids')) { + $entity->loadLinkMultipleField($field); + } + } + } + } + public function process($entityType, $params, $dataList) { if (!is_array($params['attributeList'])) { throw new Error(); } + $dataList = $this->prepareDataList($dataList); + $attributeList = $params['attributeList']; $delimiter = $this->getInjection('preferences')->get('exportDelimiter'); @@ -62,4 +78,21 @@ class Csv extends \Espo\Core\Injectable return $csv; } + + protected function prepareDataList($dataList) + { + $prepareDataList = []; + foreach ($dataList as $row) { + $preparedRow = []; + foreach ($row as $item) { + if (is_array($item) || is_object($item)) { + $item = \Espo\Core\Utils\Json::encode($item); + } + $preparedRow[] = $item; + } + $prepareDataList[] = $preparedRow; + } + + return $prepareDataList; + } } \ No newline at end of file From 0a23a469e2aec25b0f37bb3dd70ec224038e150b Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 22 Jan 2018 15:28:15 +0200 Subject: [PATCH 06/92] loadLinkMultipleField improvement --- application/Espo/Core/ORM/Entity.php | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/application/Espo/Core/ORM/Entity.php b/application/Espo/Core/ORM/Entity.php index 099b5e8497..b9d92dacff 100644 --- a/application/Espo/Core/ORM/Entity.php +++ b/application/Espo/Core/ORM/Entity.php @@ -69,12 +69,20 @@ class Entity extends \Espo\ORM\Entity } } + $defs['select'] = ['id', 'name']; + + $hasType = false; + if ($this->hasField($field . 'Types')) { + $hasType = true; + $defs['select'][] = 'type'; + } + $collection = $this->get($field, $defs); - $ids = array(); - $names = new \stdClass(); - $types = new \stdClass(); + $ids = []; + $names = (object) []; + $types = (object) []; if (!empty($columns)) { - $columnsData = new \stdClass(); + $columnsData = (object) []; } if ($collection) { @@ -82,7 +90,9 @@ class Entity extends \Espo\ORM\Entity $id = $e->id; $ids[] = $id; $names->$id = $e->get('name'); - $types->$id = $e->get('type'); + if ($hasType) { + $types->$id = $e->get('type'); + } if (!empty($columns)) { $columnsData->$id = new \stdClass(); foreach ($columns as $column => $f) { @@ -94,7 +104,9 @@ class Entity extends \Espo\ORM\Entity $this->set($field . 'Ids', $ids); $this->set($field . 'Names', $names); - $this->set($field . 'Types', $types); + if ($hasType) { + $this->set($field . 'Types', $types); + } if (!empty($columns)) { $this->set($field . 'Columns', $columnsData); } From b0438991bf3b1102564a15e19f47be028ab49d33 Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 22 Jan 2018 15:52:13 +0200 Subject: [PATCH 07/92] entity manager refactoring --- application/Espo/Core/ORM/Entity.php | 18 ++++++++++++++++++ application/Espo/ORM/EntityManager.php | 13 +++++++++---- application/Espo/ORM/Metadata.php | 8 ++++++++ application/Espo/Services/Record.php | 11 ++++------- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/application/Espo/Core/ORM/Entity.php b/application/Espo/Core/ORM/Entity.php index b9d92dacff..f9b289912f 100644 --- a/application/Espo/Core/ORM/Entity.php +++ b/application/Espo/Core/ORM/Entity.php @@ -41,6 +41,24 @@ class Entity extends \Espo\ORM\Entity return $this->hasAttribute($field . 'Id'); } + public function loadParentNameField($field) + { + if (!$this->hasAttribute($field. 'Id') || !$this->hasAttribute($field . 'Type')) return; + + $parentId = $this->get($field . 'Id'); + $parentType = $this->get($field . 'Type'); + + if ($parentId && $parentType) { + if (!$this->entityManager->hasRepository($parentType)) return; + $repository = $this->entityManager->getRepository($parentType); + + $foreignEntity = $repository->select(['id', 'name'])->where(['id' => $parentId])->findOne(); + if ($foreignEntity) { + $this->set($field . 'Name', $foreignEntity->get('name')); + } + } + } + public function loadLinkMultipleField($field, $columns = null) { if (!$this->hasRelation($field) || !$this->hasAttribute($field . 'Ids')) return; diff --git a/application/Espo/ORM/EntityManager.php b/application/Espo/ORM/EntityManager.php index 16f05afe12..d894f73fef 100644 --- a/application/Espo/ORM/EntityManager.php +++ b/application/Espo/ORM/EntityManager.php @@ -163,14 +163,14 @@ class EntityManager public function saveEntity(Entity $entity, array $options = array()) { - $entityName = $entity->getEntityName(); - return $this->getRepository($entityName)->save($entity, $options); + $entityType = $entity->getEntityType(); + return $this->getRepository($entityType)->save($entity, $options); } public function removeEntity(Entity $entity, array $options = array()) { - $entityName = $entity->getEntityName(); - return $this->getRepository($entityName)->remove($entity, $options); + $entityType = $entity->getEntityType(); + return $this->getRepository($entityType)->remove($entity, $options); } public function getRepository($name) @@ -186,6 +186,11 @@ class EntityManager $this->metadata->setData($data); } + public function hasRepository($name) + { + return $this->getMetadata()->has($name); + } + public function getMetadata() { return $this->metadata; diff --git a/application/Espo/ORM/Metadata.php b/application/Espo/ORM/Metadata.php index a370f1bd68..81d62e8f31 100644 --- a/application/Espo/ORM/Metadata.php +++ b/application/Espo/ORM/Metadata.php @@ -45,4 +45,12 @@ class Metadata } return $this->data[$entityType]; } + + public function has($entityType) + { + if (!array_key_exists($entityType, $this->data)) { + return null; + } + return true; + } } diff --git a/application/Espo/Services/Record.php b/application/Espo/Services/Record.php index cca2257bae..98d061c92d 100644 --- a/application/Espo/Services/Record.php +++ b/application/Espo/Services/Record.php @@ -282,13 +282,10 @@ class Record extends \Espo\Core\Services\Base $fieldDefs = $this->getMetadata()->get('entityDefs.' . $entity->getEntityType() . '.fields', array()); foreach ($fieldDefs as $field => $defs) { if (isset($defs['type']) && $defs['type'] == 'linkParent') { - $id = $entity->get($field . 'Id'); - $scope = $entity->get($field . 'Type'); - - if ($scope) { - if ($foreignEntity = $this->getEntityManager()->getEntity($scope, $id)) { - $entity->set($field . 'Name', $foreignEntity->get('name')); - } + $parentId = $entity->get($field . 'Id'); + $parentType = $entity->get($field . 'Type'); + if ($parentId && $parentType) { + $entity->loadParentNameField($field); } } } From d44f8e7ab4c098a3e5b241f2a45d91e82775e1c2 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 23 Jan 2018 10:40:16 +0200 Subject: [PATCH 08/92] fix export exception --- application/Espo/Core/Export/Csv.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/Espo/Core/Export/Csv.php b/application/Espo/Core/Export/Csv.php index 925428e050..1d0c14b249 100644 --- a/application/Espo/Core/Export/Csv.php +++ b/application/Espo/Core/Export/Csv.php @@ -29,7 +29,7 @@ namespace Espo\Core\Export; -use \Espo\Exceptions\Error; +use \Espo\Core\Exceptions\Error; use \Espo\Core\ORM\Entity; From f4d9e3fbc6ce6ee0df27391cc70236f1a04ac4c8 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 23 Jan 2018 11:01:09 +0200 Subject: [PATCH 09/92] fix navbar --- client/src/views/site/navbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/views/site/navbar.js b/client/src/views/site/navbar.js index bccb7091d0..7bbe75a9fd 100644 --- a/client/src/views/site/navbar.js +++ b/client/src/views/site/navbar.js @@ -198,7 +198,7 @@ Espo.define('views/site/navbar', 'view', function (Dep) { $moreDd = $('#nav-more-tabs-dropdown'); $moreLi = $moreDd.closest('li'); - var navbarBaseWidth = this.getThemeManager().getParam('navbarBaseWidth') || 512; + var navbarBaseWidth = this.getThemeManager().getParam('navbarBaseWidth') || 516; var updateWidth = function () { var windowWidth = $(window.document).width(); From f2fb3fc077fe4c140c8f60ac547cccd173b9c8e7 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 23 Jan 2018 11:30:28 +0200 Subject: [PATCH 10/92] css fix --- client/res/templates/admin/integrations/edit.tpl | 2 +- client/res/templates/admin/integrations/oauth2.tpl | 2 +- frontend/less/espo/custom.less | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/res/templates/admin/integrations/edit.tpl b/client/res/templates/admin/integrations/edit.tpl index 1440ab6750..958c0050da 100644 --- a/client/res/templates/admin/integrations/edit.tpl +++ b/client/res/templates/admin/integrations/edit.tpl @@ -6,7 +6,7 @@
-
+
{{{enabled}}}
diff --git a/client/res/templates/admin/integrations/oauth2.tpl b/client/res/templates/admin/integrations/oauth2.tpl index 5f84be33f1..a827988adb 100644 --- a/client/res/templates/admin/integrations/oauth2.tpl +++ b/client/res/templates/admin/integrations/oauth2.tpl @@ -6,7 +6,7 @@
-
+
{{{enabled}}}
diff --git a/frontend/less/espo/custom.less b/frontend/less/espo/custom.less index 6024581a84..bbe83fe44d 100644 --- a/frontend/less/espo/custom.less +++ b/frontend/less/espo/custom.less @@ -518,6 +518,10 @@ select[multiple].input-sm { margin-bottom: 0; } +.well > p:last-child { + margin-bottom: 0; +} + .panel-body > div > .list-expanded { margin-left: -@panel-padding; margin-right: -@panel-padding; From 315e35965cd5709eb3ba010fafc73de0babb323c Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 23 Jan 2018 12:47:56 +0200 Subject: [PATCH 11/92] acl gender default --- application/Espo/Resources/metadata/app/acl.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/application/Espo/Resources/metadata/app/acl.json b/application/Espo/Resources/metadata/app/acl.json index ebe3ae0243..6d19d31c4a 100644 --- a/application/Espo/Resources/metadata/app/acl.json +++ b/application/Espo/Resources/metadata/app/acl.json @@ -64,9 +64,6 @@ "Attachment": { "parent": false }, - "User": { - "gender": false - }, "EmailFolder": { "assignedUser": false }, @@ -80,6 +77,11 @@ "scopeLevel": { }, "fieldLevel": { + }, + "scopeFieldLevel": { + "User": { + "gender": false + } } }, "valuePermissionList": [ From d72884a0e4941880d785a66ea2c9e6cede2567b3 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 23 Jan 2018 13:25:04 +0200 Subject: [PATCH 12/92] lang --- application/Espo/Resources/i18n/en_US/Email.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/application/Espo/Resources/i18n/en_US/Email.json b/application/Espo/Resources/i18n/en_US/Email.json index eba2acf6bc..1fb52fd252 100644 --- a/application/Espo/Resources/i18n/en_US/Email.json +++ b/application/Espo/Resources/i18n/en_US/Email.json @@ -37,7 +37,15 @@ "emailAccounts": "Personal Accounts", "hasAttachment": "Has Attachment", "assignedUsers": "Assigned Users", - "sentBy": "Sent By" + "sentBy": "Sent By", + "ccEmailAddresses": "CC Email Addresses", + "messageId": "Message Id", + "messageIdInternal": "Message Id (Internal)", + "folderId": "Folder Id", + "fromName": "From Name", + "fromString": "From String", + "isSystem": "Is System" + }, "links": { "replied": "Replied", From cd2e3eec08b0df2ce891145e27100022712eb128 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 23 Jan 2018 13:40:39 +0200 Subject: [PATCH 13/92] acl fix --- application/Espo/Resources/metadata/app/aclPortal.json | 8 ++++++-- client/src/views/role/modals/add-field.js | 4 +++- client/src/views/role/record/table.js | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/application/Espo/Resources/metadata/app/aclPortal.json b/application/Espo/Resources/metadata/app/aclPortal.json index 52fc51e720..83ee1b3c94 100644 --- a/application/Espo/Resources/metadata/app/aclPortal.json +++ b/application/Espo/Resources/metadata/app/aclPortal.json @@ -80,8 +80,9 @@ "isInternal": false, "isGlobal": false }, - "User": { - "gender": false + "Email": { + "inboundEmails": false, + "emailAccounts": false } } }, @@ -113,6 +114,9 @@ "edit": "no" }, "leads": false + }, + "User": { + "gender": false } } }, diff --git a/client/src/views/role/modals/add-field.js b/client/src/views/role/modals/add-field.js index a847568e3c..e5733edc41 100644 --- a/client/src/views/role/modals/add-field.js +++ b/client/src/views/role/modals/add-field.js @@ -67,7 +67,9 @@ Espo.define('views/role/modals/add-field', 'views/modal', function (Dep) { var d = fields[field]; if (field in this.options.ignoreFieldList) return; if (d.disabled) return; - + if (this.getMetadata().get(['app', this.options.type, 'mandatory', 'scopeFieldLevel', this.scope, field]) !== null) { + return; + } fieldList.push(field); }, this); diff --git a/client/src/views/role/record/table.js b/client/src/views/role/record/table.js index d1d962c616..e11ddd4a0b 100644 --- a/client/src/views/role/record/table.js +++ b/client/src/views/role/record/table.js @@ -512,7 +512,8 @@ Espo.define('views/role/record/table', 'view', function (Dep) { this.createView('addField', 'views/role/modals/add-field', { scope: scope, - ignoreFieldList: ignoreFieldList + ignoreFieldList: ignoreFieldList, + type: this.type }, function (view) { view.render(); From 75eb49ced3bc3f9daf454e3cde0756677c103f10 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 23 Jan 2018 14:51:28 +0200 Subject: [PATCH 14/92] acl changes --- application/Espo/Core/Acl/Table.php | 19 +++++++++++++++---- application/Espo/Core/AclPortal/Table.php | 2 ++ .../Espo/Resources/metadata/app/acl.json | 17 +++++++++++++++++ .../Resources/metadata/app/aclPortal.json | 12 ++++-------- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/application/Espo/Core/Acl/Table.php b/application/Espo/Core/Acl/Table.php index d1c4e9ee00..04513c56e7 100644 --- a/application/Espo/Core/Acl/Table.php +++ b/application/Espo/Core/Acl/Table.php @@ -73,6 +73,8 @@ class Table protected $forbiddenFieldsCache = array(); + protected $isStrictModeForced = false; + protected $isStrictMode = false; public function __construct(User $user, Config $config = null, FileManager $fileManager = null, Metadata $metadata = null, FieldManagerUtil $fieldManager = null) @@ -83,7 +85,11 @@ class Table 'fieldTableQuickAccess' => (object) [], ]; - $this->isStrictMode = $config->get('aclStrictMode', false); + if ($this->isStrictModeForced) { + $this->isStrictMode = true; + } else { + $this->isStrictMode = $config->get('aclStrictMode', false); + } $this->user = $user; @@ -405,7 +411,12 @@ class Table return; } - $data = $this->metadata->get('app.'.$this->type.'.default.scopeLevel', array()); + $defaultsGroupName = 'default'; + if ($this->isStrictMode) { + $defaultsGroupName = 'strictDefault'; + } + + $data = $this->metadata->get(['app', $this->type, $defaultsGroupName, 'scopeLevel'], []); foreach ($data as $scope => $item) { if (isset($table->$scope)) continue; @@ -416,7 +427,7 @@ class Table $table->$scope = $value; } - $defaultFieldData = $this->metadata->get('app.'.$this->type.'.default.fieldLevel', array()); + $defaultFieldData = $this->metadata->get(['app', $this->type, $defaultsGroupName, 'fieldLevel'], []); foreach ($this->getScopeList() as $scope) { if (isset($table->$scope) && $table->$scope === false) continue; @@ -424,7 +435,7 @@ class Table $fieldList = array_keys($this->getMetadata()->get("entityDefs.{$scope}.fields", [])); - $defaultScopeFieldData = $this->metadata->get('app.'.$this->type.'.default.scopeFieldLevel.' . $scope, array()); + $defaultScopeFieldData = $this->metadata->get('app.'.$this->type.'.'.$defaultsGroupName.'.scopeFieldLevel.' . $scope, []); foreach (array_merge($defaultFieldData, $defaultScopeFieldData) as $field => $f) { if (!in_array($field, $fieldList)) continue; diff --git a/application/Espo/Core/AclPortal/Table.php b/application/Espo/Core/AclPortal/Table.php index 44735bda4a..c2835e5187 100644 --- a/application/Espo/Core/AclPortal/Table.php +++ b/application/Espo/Core/AclPortal/Table.php @@ -50,6 +50,8 @@ class Table extends \Espo\Core\Acl\Table protected $levelList = ['yes', 'all', 'account', 'contact', 'own', 'no']; + protected $isStrictModeForced = true; + public function __construct(User $user, Portal $portal, Config $config = null, FileManager $fileManager = null, Metadata $metadata = null, FieldManagerUtil $fieldManager = null) { if (empty($portal)) { diff --git a/application/Espo/Resources/metadata/app/acl.json b/application/Espo/Resources/metadata/app/acl.json index 6d19d31c4a..ba87b4375f 100644 --- a/application/Espo/Resources/metadata/app/acl.json +++ b/application/Espo/Resources/metadata/app/acl.json @@ -75,6 +75,23 @@ }, "default": { "scopeLevel": { + "User": { + "read": "all" + } + }, + "fieldLevel": { + }, + "scopeFieldLevel": { + "User": { + "gender": false + } + } + }, + "strictDefault": { + "scopeLevel": { + "User": { + "read": "own" + } }, "fieldLevel": { }, diff --git a/application/Espo/Resources/metadata/app/aclPortal.json b/application/Espo/Resources/metadata/app/aclPortal.json index 83ee1b3c94..095aa0b669 100644 --- a/application/Espo/Resources/metadata/app/aclPortal.json +++ b/application/Espo/Resources/metadata/app/aclPortal.json @@ -86,7 +86,7 @@ } } }, - "default": { + "strictDefault": { "scopeLevel": { }, "fieldLevel": { @@ -115,6 +115,9 @@ }, "leads": false }, + "KnowledgeBaseArticle": { + "assignedUser": false + }, "User": { "gender": false } @@ -123,16 +126,9 @@ "valuePermissionList": [ "exportPermission" ], - "permissionsDefaults": { - "exportPermission": "no" - }, "permissionsStrictDefaults": { "exportPermission": "no" }, - "scopeLevelTypesDefaults": { - "boolean": false, - "record": false - }, "scopeLevelTypesStrictDefaults": { "boolean": false, "record": false From bc097db09f06f8bec6c7566faecdd373bcc92afb Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 23 Jan 2018 15:55:40 +0200 Subject: [PATCH 15/92] record grid layout improvement --- .../Espo/Resources/layouts/Role/detail.json | 6 +-- client/res/layout-types/record.tpl | 51 ++++++++++++++++++- client/src/views/record/detail.js | 9 +++- client/src/views/role/record/detail.js | 2 + client/src/views/role/record/edit.js | 2 + 5 files changed, 62 insertions(+), 8 deletions(-) diff --git a/application/Espo/Resources/layouts/Role/detail.json b/application/Espo/Resources/layouts/Role/detail.json index 460e0b0896..6080bb67cb 100644 --- a/application/Espo/Resources/layouts/Role/detail.json +++ b/application/Espo/Resources/layouts/Role/detail.json @@ -3,13 +3,11 @@ "rows": [ [ {"name": "name"}, - {"name": "exportPermission"} - ], - [ - {"name": "assignmentPermission"}, + {"name": "exportPermission"}, {"name": "userPermission"} ], [ + {"name": "assignmentPermission"}, {"name": "portalPermission"}, {"name": "groupEmailAccountPermission"} ] diff --git a/client/res/layout-types/record.tpl b/client/res/layout-types/record.tpl index e3311977d9..a5fef71fe7 100644 --- a/client/res/layout-types/record.tpl +++ b/client/res/layout-types/record.tpl @@ -19,8 +19,55 @@ <% _.each(panel.rows, function (row, rowNumber) { %>
<% _.each(row, function (cell, cellNumber) { %> + + <% + var spanClassBase; + if (columnCount === 1) { + spanClassBase = 'col-sm-12'; + } else if (columnCount === 2) { + spanClassBase = 'col-sm-6'; + } else if (columnCount === 3) { + spanClassBase = 'col-sm-4'; + } else if (columnCount === 4) { + spanClassBase = 'col-md-3 col-sm-6'; + } else { + spanClass = 'col-sm-12'; + } + %> <% if (cell != false) { %> -
+ <% + var spanClass; + if (columnCount === 1 || cell.fullWidth) { + spanClass = 'col-sm-12'; + } else if (columnCount === 2) { + if (cell.span === 2) { + spanClass = 'col-sm-12'; + } else { + spanClass = 'col-sm-6'; + } + } else if (columnCount === 3) { + if (cell.span === 2) { + spanClass = 'col-sm-8'; + } else if (cell.span === 3) { + spanClass = 'col-sm-12'; + } else { + spanClass = 'col-sm-4'; + } + } else if (columnCount === 4) { + if (cell.span === 2) { + spanClass = 'col-sm-6'; + } else if (cell.span === 3) { + spanClass = 'col-sm-9'; + } else if (cell.span === 4) { + spanClass = 'col-sm-12'; + } else { + spanClass = 'col-md-3 col-sm-6'; + } + } else { + spanClass = 'col-sm-12'; + } + %> +
<% if (!cell.noLabel) { %>
<% } else { %> -
+
<% } %> <% }); %>
diff --git a/client/src/views/record/detail.js b/client/src/views/record/detail.js index 7444e2ea7b..98116e5c0e 100644 --- a/client/src/views/record/detail.js +++ b/client/src/views/record/detail.js @@ -1106,6 +1106,9 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'], if ('noLabel' in cellDefs) { cell.noLabel = cellDefs.noLabel; } + if ('span' in cellDefs) { + cell.span = cellDefs.span; + } row.push(cell); } @@ -1123,9 +1126,11 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'], return; } + var gridLayoutType = this.gridLayoutType || 'record'; + if (this.detailLayout) { this.gridLayout = { - type: 'record', + type: gridLayoutType, layout: this.convertDetailLayout(this.detailLayout) }; callback(this.gridLayout); @@ -1134,7 +1139,7 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'], this._helper.layoutManager.get(this.model.name, this.layoutName, function (simpleLayout) { this.gridLayout = { - type: 'record', + type: gridLayoutType, layout: this.convertDetailLayout(simpleLayout) }; callback(this.gridLayout); diff --git a/client/src/views/role/record/detail.js b/client/src/views/role/record/detail.js index 70f558f195..340cc69f16 100644 --- a/client/src/views/role/record/detail.js +++ b/client/src/views/role/record/detail.js @@ -38,6 +38,8 @@ Espo.define('views/role/record/detail', 'views/record/detail', function (Dep) { editModeDisabled: true, + columnCount: 3, + setup: function () { Dep.prototype.setup.call(this); this.createView('extra', this.tableView, { diff --git a/client/src/views/role/record/edit.js b/client/src/views/role/record/edit.js index 73d99e42b3..a0d1c2a808 100644 --- a/client/src/views/role/record/edit.js +++ b/client/src/views/role/record/edit.js @@ -36,6 +36,8 @@ Espo.define('views/role/record/edit', 'views/record/edit', function (Dep) { isWide: true, + columnCount: 3, + events: _.extend({ }, Dep.prototype.events), From 2a35b9912c117cea5746132dc23a3fc0e9a7d5d9 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 23 Jan 2018 15:59:37 +0200 Subject: [PATCH 16/92] target lists exportable --- .../Espo/Modules/Crm/Resources/metadata/entityDefs/Account.json | 1 - .../Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json | 1 - 2 files changed, 2 deletions(-) diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Account.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Account.json index 112f8cf6ef..1d520d6e25 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Account.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Account.json @@ -195,7 +195,6 @@ "layoutListDisabled": true, "layoutMassUpdateDisabled": true, "layoutFiltersDisabled": true, - "exportDisabled": true, "entity": "TargetList" }, "originalLead": { diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json index 99689b868c..c6cede1479 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json @@ -253,7 +253,6 @@ "layoutListDisabled": true, "layoutMassUpdateDisabled": true, "importDisabled": true, - "exportDisabled": true, "noLoad": true }, "targetList": { From d2dfd7ddb0904db7dc41cd40fb4807bf4c3609e6 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 23 Jan 2018 16:29:54 +0200 Subject: [PATCH 17/92] excel export: supporting address --- application/Espo/Core/Export/Xlsx.php | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/application/Espo/Core/Export/Xlsx.php b/application/Espo/Core/Export/Xlsx.php index 9294587d6d..26be562e0c 100644 --- a/application/Espo/Core/Export/Xlsx.php +++ b/application/Espo/Core/Export/Xlsx.php @@ -386,6 +386,40 @@ class Xlsx extends \Espo\Core\Injectable } $sheet->setCellValue("$col$rowNumber", implode(', ', $nameList)); } + } else if ($type == 'address') { + $value = ''; + if (!empty($row[$name . 'Street'])) { + $value = $value .= $row[$name.'Street']; + } + if (!empty($row[$name.'City']) || !empty($row[$name.'State']) || !empty($row[$name.'PostalCode'])) { + if ($value) { + $value .= "\n"; + } + if (!empty($row[$name.'City'])) { + $value .= $row[$name.'City']; + if ( + !empty($row[$name.'State']) || !empty($row[$name.'PostalCode']) + ) { + $value .= ', '; + } + } + if (!empty($row[$name.'State'])) { + $value .= $row[$name.'State']; + if (!empty($row[$name.'PostalCode'])) { + $value .= ' '; + } + } + if (!empty($row[$name.'PostalCode'])) { + $value .= $row[$name.'PostalCode']; + } + } + if (!empty($row[$name.'Country'])) { + if ($value) { + $value .= "\n"; + } + $value .= $row[$name.'Country']; + } + $sheet->setCellValue("$col$rowNumber", $value); } else { if (array_key_exists($name, $row)) { $sheet->setCellValue("$col$rowNumber", $row[$name]); From a30c17ed912cf57647ada4aa7c03ad4bf399ef08 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 23 Jan 2018 18:06:04 +0200 Subject: [PATCH 18/92] chart fixes --- .../crm/src/views/dashlets/abstract/chart.js | 18 +++++++++++++----- .../dashlets/opportunities-by-lead-source.js | 16 +++++++++++++--- .../views/dashlets/opportunities-by-stage.js | 8 +++++--- .../crm/src/views/dashlets/sales-by-month.js | 10 +++++++--- .../crm/src/views/dashlets/sales-pipeline.js | 4 ++-- 5 files changed, 40 insertions(+), 16 deletions(-) diff --git a/client/modules/crm/src/views/dashlets/abstract/chart.js b/client/modules/crm/src/views/dashlets/abstract/chart.js index 6018db8abb..932d9960f9 100644 --- a/client/modules/crm/src/views/dashlets/abstract/chart.js +++ b/client/modules/crm/src/views/dashlets/abstract/chart.js @@ -85,18 +85,26 @@ Espo.define('crm:views/dashlets/abstract/chart', ['views/dashlets/abstract/base' return ''; }, + getLegentHeight: function () { + if (this.chartData.length > 5) { + legendHeight = this.getThemeManager().getParam('dashletChartLegent2RowHeight') || 52; + } else { + legendHeight = this.getThemeManager().getParam('dashletChartLegent1RowHeight') || 22; + } + return legendHeight; + }, + afterRender: function () { this.fetch(function (data) { this.chartData = this.prepareData(data); var $container = this.$container = this.$el.find('.chart-container'); - var height = 'calc(100% - 22px)'; - if (this.chartData.length > 5) { - height = 'calc(100% - 42px)'; - } + var legendHeight = this.getLegentHeight(); - $container.css('height', height); + var heightCss = 'calc(100% - '+legendHeight.toString()+'px)'; + + $container.css('height', heightCss); setTimeout(function () { if (!$container.size() || !$container.is(":visible")) return; diff --git a/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js b/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js index 27100cc2af..211e89e82e 100644 --- a/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js +++ b/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js @@ -55,7 +55,7 @@ Espo.define('crm:views/dashlets/opportunities-by-lead-source', 'crm:views/dashle setup: function () { this.currency = this.getConfig().get('defaultCurrency'); - this.currencySymbol = ''; + this.currencySymbol = this.getMetadata().get(['app', 'currency', 'symbolMap', this.currency]) || ''; }, drow: function () { @@ -69,6 +69,11 @@ Espo.define('crm:views/dashlets/opportunities-by-lead-source', 'crm:views/dashle lineWidth: 1, fillOpacity: 1, sizeRatio: 0.8, + labelFormatter: function (total, value) { + var percentage = (100 * value / total).toFixed(2); + if (percentage < 5) return ''; + return ''+ percentage.toString() +'%' + ''; + } }, grid: { horizontalLines: false, @@ -89,8 +94,13 @@ Espo.define('crm:views/dashlets/opportunities-by-lead-source', 'crm:views/dashle track: true, relative: true, trackFormatter: function (obj) { - return self.formatNumber(obj.y) + ' ' + self.currency; - }, + var value = self.currencySymbol + self.formatNumber(obj.y); + + var fraction = obj.fraction || 0; + var percentage = (100 * fraction).toFixed(2).toString(); + + return (obj.series.label || self.translate('None')) + ':
' + value + ' / ' + percentage + '%'; + } }, legend: { show: true, diff --git a/client/modules/crm/src/views/dashlets/opportunities-by-stage.js b/client/modules/crm/src/views/dashlets/opportunities-by-stage.js index 3641b427c0..36a90261b9 100644 --- a/client/modules/crm/src/views/dashlets/opportunities-by-stage.js +++ b/client/modules/crm/src/views/dashlets/opportunities-by-stage.js @@ -73,7 +73,7 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs setup: function () { this.currency = this.getConfig().get('defaultCurrency'); - this.currencySymbol = ''; + this.currencySymbol = this.getMetadata().get(['app', 'currency', 'symbolMap', this.currency]) || ''; }, drow: function () { @@ -102,7 +102,7 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs min: 0, tickFormatter: function (value) { if (value != 0) { - return self.formatNumber(value) + ' ' + self.currency; + return self.currencySymbol + self.formatNumber(value); } return ''; }, @@ -112,7 +112,9 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs relative: true, position: 's', trackFormatter: function (obj) { - return self.formatNumber(obj.x) + ' ' + self.currency; + var label = (obj.series.label || self.translate('None')); + var value = label + ':
' + self.currencySymbol + self.formatNumber(obj.x); + return value; }, }, legend: { diff --git a/client/modules/crm/src/views/dashlets/sales-by-month.js b/client/modules/crm/src/views/dashlets/sales-by-month.js index cc28fb8072..5690c880d3 100644 --- a/client/modules/crm/src/views/dashlets/sales-by-month.js +++ b/client/modules/crm/src/views/dashlets/sales-by-month.js @@ -41,6 +41,10 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch return 'Opportunity/action/reportSalesByMonth?dateFrom=' + this.getOption('dateFrom') + '&dateTo=' + this.getOption('dateTo'); }, + getLegentHeight: function () { + return 0; + }, + prepareData: function (response) { var months = this.months = Object.keys(response).sort(); @@ -71,7 +75,7 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch setup: function () { this.currency = this.getConfig().get('defaultCurrency'); - this.currencySymbol = ''; + this.currencySymbol = this.getMetadata().get(['app', 'currency', 'symbolMap', this.currency]) || ''; this.colorBad = this.successColor; }, @@ -101,7 +105,7 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch if (value == 0) { return ''; } - return self.formatNumber(value) + ' ' + self.currency; + return self.currencySymbol + self.formatNumber(value); }, }, xaxis: { @@ -120,7 +124,7 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch track: true, relative: true, trackFormatter: function (obj) { - return self.formatNumber(obj.y) + ' ' + self.currency; + return self.currencySymbol + self.formatNumber(obj.y); }, } }); diff --git a/client/modules/crm/src/views/dashlets/sales-pipeline.js b/client/modules/crm/src/views/dashlets/sales-pipeline.js index bbc96c0975..bafdbacbad 100644 --- a/client/modules/crm/src/views/dashlets/sales-pipeline.js +++ b/client/modules/crm/src/views/dashlets/sales-pipeline.js @@ -80,7 +80,7 @@ Espo.define('crm:views/dashlets/sales-pipeline', 'crm:views/dashlets/abstract/ch setup: function () { this.currency = this.getConfig().get('defaultCurrency'); - this.currencySymbol = ''; + this.currencySymbol = this.getMetadata().get(['app', 'currency', 'symbolMap', this.currency]) || ''; var data = [ { @@ -181,7 +181,7 @@ Espo.define('crm:views/dashlets/sales-pipeline', 'crm:views/dashlets/abstract/ch if (obj.x >= self.chartData.length) { return null; } - return self.formatNumber(obj.y) + ' ' + self.currency; + return self.currencySymbol + self.formatNumber(obj.y); }, }, legend: { From 02edc35d3a340d8cdeaee50278cc36c1da0ef944 Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 11:20:10 +0200 Subject: [PATCH 19/92] user mass update fixes --- application/Espo/Acl/User.php | 18 ++++++++++++++- application/Espo/Services/Record.php | 9 ++++++-- application/Espo/Services/User.php | 34 +++++++++++++++++++++++----- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/application/Espo/Acl/User.php b/application/Espo/Acl/User.php index ff24bdc312..95d15b1d6a 100644 --- a/application/Espo/Acl/User.php +++ b/application/Espo/Acl/User.php @@ -30,6 +30,7 @@ namespace Espo\Acl; use \Espo\ORM\Entity; +use \Espo\Entities\User as EntityUser; class User extends \Espo\Core\Acl\Base { @@ -37,5 +38,20 @@ class User extends \Espo\Core\Acl\Base { return $user->id === $entity->id; } -} + public function checkEntityDelete(EntityUser $user, Entity $entity, $data) + { + if ($entity->id === 'system') { + return false; + } + return parent::checkEntityDelete($user, $entity, $data); + } + + public function checkEntityEdit(EntityUser $user, Entity $entity, $data) + { + if ($entity->id === 'system') { + return false; + } + return $this->checkEntity($user, $entity, $data, 'edit'); + } +} diff --git a/application/Espo/Services/Record.php b/application/Espo/Services/Record.php index 98d061c92d..7e09fbfd4b 100644 --- a/application/Espo/Services/Record.php +++ b/application/Espo/Services/Record.php @@ -1062,7 +1062,7 @@ class Record extends \Espo\Core\Services\Base $ids = $params['ids']; foreach ($ids as $id) { $entity = $this->getEntity($id); - if ($this->getAcl()->check($entity, 'edit')) { + if ($this->getAcl()->check($entity, 'edit') && $this->checkEntityForMassUpdate($entity, $data)) { $entity->set($data); if ($this->checkAssignment($entity)) { if ($repository->save($entity)) { @@ -1092,7 +1092,7 @@ class Record extends \Espo\Core\Services\Base $collection = $repository->find($selectParams); foreach ($collection as $entity) { - if ($this->getAcl()->check($entity, 'edit')) { + if ($this->getAcl()->check($entity, 'edit') && $this->checkEntityForMassUpdate($entity, $data)) { $entity->set($data); if ($this->checkAssignment($entity)) { if ($repository->save($entity)) { @@ -1125,6 +1125,11 @@ class Record extends \Espo\Core\Services\Base return true; } + protected function checkEntityForMassUpdate(Entity $entity, $data) + { + return true; + } + public function massRemove(array $params) { $idsRemoved = array(); diff --git a/application/Espo/Services/User.php b/application/Espo/Services/User.php index 80afe2b1ca..6c780ce7f2 100644 --- a/application/Espo/Services/User.php +++ b/application/Espo/Services/User.php @@ -212,6 +212,15 @@ class User extends Record return $passwordHash->hash($password); } + protected function filterInput($data) + { + parent::filterInput($data); + + if (!$this->getUser()->get('isSuperAdmin')) { + unset($data->isSuperAdmin); + } + } + public function createEntity($data) { $newPassword = null; @@ -219,9 +228,6 @@ class User extends Record $newPassword = $data->password; $data->password = $this->hashPassword($data->password); } - if (!$this->getUser()->get('isSuperAdmin')) { - unset($data->isSuperAdmin); - } $user = parent::createEntity($data); @@ -251,9 +257,6 @@ class User extends Record unset($data->isActive); unset($data->isPortalUser); } - if (!$this->getUser()->get('isSuperAdmin')) { - unset($data->isSuperAdmin); - } $user = parent::updateEntity($id, $data); @@ -470,6 +473,25 @@ class User extends Record return true; } + protected function checkEntityForMassUpdate(Entity $entity, $data) + { + if ($entity->id == 'system') { + return false; + } + if ($entity->id == $this->getUser()->id) { + if (property_exists($data, 'isActive')) { + return false; + } + if (property_exists($data, 'isPortalUser')) { + return false; + } + if (property_exists($data, 'isSuperAdmin')) { + return false; + } + } + return true; + } + public function afterUpdate(Entity $entity, array $data = array()) { parent::afterUpdate($entity, $data); From 5cb82cca42903274047aa6846824409d83546ac6 Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 11:40:52 +0200 Subject: [PATCH 20/92] email store sent fix --- application/Espo/Services/Email.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/application/Espo/Services/Email.php b/application/Espo/Services/Email.php index 8e10bc3475..44d4ead58c 100644 --- a/application/Espo/Services/Email.php +++ b/application/Espo/Services/Email.php @@ -119,9 +119,11 @@ class Email extends Record } } } + + $emailAccountService = $this->getServiceFactory()->create('EmailAccount'); + $emailAccount = $emailAccountService->findAccountForUser($this->getUser(), $fromAddress); + if (!$smtpParams) { - $emailAccountService = $this->getServiceFactory()->create('EmailAccount'); - $emailAccount = $emailAccountService->findAccountForUser($this->getUser(), $fromAddress); if ($emailAccount) { $smtpParams = $emailAccountService->getSmtpParamsFromAccount($emailAccount); if ($smtpParams) { From 7e9ddf0c141ab3b85551534d3760309bfa7f3242 Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 11:48:06 +0200 Subject: [PATCH 21/92] user access table: hide mandatory --- client/src/views/user/modals/access.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/src/views/user/modals/access.js b/client/src/views/user/modals/access.js index c03d4eff0a..9c727c9edc 100644 --- a/client/src/views/user/modals/access.js +++ b/client/src/views/user/modals/access.js @@ -65,10 +65,20 @@ Espo.define('views/user/modals/access', 'views/modal', function (Dep) { } ]; + var fieldTable = Espo.Utils.cloneDeep(this.options.aclData.fieldTable || {}); + for (var scope in fieldTable) { + var scopeData = fieldTable[scope] || {}; + for (var field in scopeData) { + if (this.getMetadata().get(['app', 'acl', 'mandatory', 'scopeFieldLevel', scope, field]) !== null) { + delete scopeData[field]; + } + } + } + this.createView('table', 'views/role/record/table', { acl: { data: this.options.aclData.table, - fieldData: this.options.aclData.fieldTable, + fieldData: fieldTable, }, final: true }); From a60a2885e11739193efe2dff3dd93727c5059536 Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 13:12:34 +0200 Subject: [PATCH 22/92] fix php mime mail parser --- application/Espo/Core/Mail/Parsers/PhpMimeMailParser/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/Espo/Core/Mail/Parsers/PhpMimeMailParser/Parser.php b/application/Espo/Core/Mail/Parsers/PhpMimeMailParser/Parser.php index 256604cd06..23aa6e3ea0 100644 --- a/application/Espo/Core/Mail/Parsers/PhpMimeMailParser/Parser.php +++ b/application/Espo/Core/Mail/Parsers/PhpMimeMailParser/Parser.php @@ -33,7 +33,7 @@ use \PhpMimeMailParser\Attachment; class Parser extends \PhpMimeMailParser\Parser { - public function getAttachments() + public function getAttachments($include_inline = true) { $attachments = []; $dispositions = ['attachment', 'inline']; From 5ff0e6739228e59bfa2920d7adbfb9d464bf4851 Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 13:16:33 +0200 Subject: [PATCH 23/92] skipHooks param --- application/Espo/Core/ORM/Repositories/RDB.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/application/Espo/Core/ORM/Repositories/RDB.php b/application/Espo/Core/ORM/Repositories/RDB.php index 9059671737..8aecd4fe28 100644 --- a/application/Espo/Core/ORM/Repositories/RDB.php +++ b/application/Espo/Core/ORM/Repositories/RDB.php @@ -176,7 +176,7 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable protected function beforeRemove(Entity $entity, array $options = array()) { parent::beforeRemove($entity, $options); - if (!$this->hooksDisabled) { + if (!$this->hooksDisabled && empty($options['skipHooks'])) { $this->getEntityManager()->getHookManager()->process($this->entityType, 'beforeRemove', $entity, $options); } @@ -192,14 +192,14 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable protected function afterRemove(Entity $entity, array $options = array()) { parent::afterRemove($entity, $options); - if (!$this->hooksDisabled) { + if (!$this->hooksDisabled && empty($options['skipHooks'])) { $this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRemove', $entity, $options); } } protected function afterMassRelate(Entity $entity, $relationName, array $params = array(), array $options = array()) { - if (!$this->hooksDisabled) { + if (!$this->hooksDisabled && empty($options['skipHooks'])) { $hookData = array( 'relationName' => $relationName, 'relationParams' => $params @@ -220,7 +220,7 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable if ($foreign instanceof Entity) { $foreignEntity = $foreign; - if (!$this->hooksDisabled) { + if (!$this->hooksDisabled && empty($options['skipHooks'])) { $hookData = array( 'relationName' => $relationName, 'relationData' => $data, @@ -237,7 +237,7 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable if ($foreign instanceof Entity) { $foreignEntity = $foreign; - if (!$this->hooksDisabled) { + if (!$this->hooksDisabled && empty($options['skipHooks'])) { $hookData = array( 'relationName' => $relationName, 'foreignEntity' => $foreignEntity @@ -251,7 +251,7 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable { parent::beforeSave($entity, $options); - if (!$this->hooksDisabled) { + if (!$this->hooksDisabled && empty($options['skipHooks'])) { $this->getEntityManager()->getHookManager()->process($this->entityType, 'beforeSave', $entity, $options); } } @@ -271,7 +271,7 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable $this->processFileFieldsSave($entity); } - if (!$this->hooksDisabled) { + if (!$this->hooksDisabled && empty($options['skipHooks'])) { $this->getEntityManager()->getHookManager()->process($this->entityType, 'afterSave', $entity, $options); } } From 9ddd1dac3a5abe9bfaa028847a81d3fc9d9072aa Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 14:03:06 +0200 Subject: [PATCH 24/92] fix typo in email --- application/Espo/Repositories/Email.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/Espo/Repositories/Email.php b/application/Espo/Repositories/Email.php index c6feca9618..acd411caab 100644 --- a/application/Espo/Repositories/Email.php +++ b/application/Espo/Repositories/Email.php @@ -322,8 +322,8 @@ class Email extends \Espo\Core\ORM\Repositories\RDB } } - if ($entity->get('isBeingImportered')) { - $entity->set('isBeingImportered', false); + if ($entity->get('isBeingImported')) { + $entity->set('isBeingImported', false); } } From f95cbe4ed2b9ffcb644eaf7976a90179f6f7dd38 Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 15:05:17 +0200 Subject: [PATCH 25/92] fix email replate to email account --- application/Espo/Services/EmailAccount.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/Espo/Services/EmailAccount.php b/application/Espo/Services/EmailAccount.php index 5a3c322558..475f510a39 100644 --- a/application/Espo/Services/EmailAccount.php +++ b/application/Espo/Services/EmailAccount.php @@ -320,8 +320,8 @@ class EmailAccount extends Record } if (!empty($email)) { + $this->getEntityManager()->getRepository('EmailAccount')->relate($emailAccount, 'emails', $email); if (!$email->isFetched()) { - $this->getEntityManager()->getRepository('EmailAccount')->relate($emailAccount, 'emails', $email); $this->noteAboutEmail($email); } } From 63e17e91bd8717313a0fdb07dd315a5b818cf71e Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 15:05:52 +0200 Subject: [PATCH 26/92] email import concurrency issue fix --- application/Espo/Core/Mail/Importer.php | 60 ++++++++++++++----------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/application/Espo/Core/Mail/Importer.php b/application/Espo/Core/Mail/Importer.php index 3eb9f35738..f1d6374957 100644 --- a/application/Espo/Core/Mail/Importer.php +++ b/application/Espo/Core/Mail/Importer.php @@ -154,31 +154,7 @@ class Importer } if ($duplicate = $this->findDuplicate($email)) { - if ($assignedUserId) { - $duplicate->addLinkMultipleId('users', $assignedUserId); - $duplicate->addLinkMultipleId('assignedUsers', $assignedUserId); - } - if (!empty($userIdList)) { - foreach ($userIdList as $uId) { - $duplicate->addLinkMultipleId('users', $uId); - } - } - - if ($folderData) { - foreach ($folderData as $uId => $folderId) { - $email->setLinkMultipleColumn('users', 'folderId', $uId, $folderId); - } - } - - $duplicate->set('isBeingImported', true); - - $this->getEntityManager()->saveEntity($duplicate); - - if (!empty($teamsIdList)) { - foreach ($teamsIdList as $teamId) { - $this->getEntityManager()->getRepository('Email')->relate($duplicate, 'teams', $teamId); - } - } + $this->processDuplicate($duplicate, $assignedUserId, $userIdList, $folderData, $teamsIdList); return $duplicate; } @@ -299,6 +275,11 @@ class Importer } } + if ($duplicate = $this->findDuplicate($email)) { + $this->processDuplicate($duplicate, $assignedUserId, $userIdList, $folderData, $teamsIdList); + return $duplicate; + } + $this->getEntityManager()->saveEntity($email); foreach ($inlineAttachmentList as $attachment) { @@ -360,4 +341,33 @@ class Importer } } } + + protected function processDuplicate(Entity $duplicate, $assignedUserId, $userIdList, $folderData, $teamsIdList) + { + if ($assignedUserId) { + $duplicate->addLinkMultipleId('users', $assignedUserId); + $duplicate->addLinkMultipleId('assignedUsers', $assignedUserId); + } + if (!empty($userIdList)) { + foreach ($userIdList as $uId) { + $duplicate->addLinkMultipleId('users', $uId); + } + } + + if ($folderData) { + foreach ($folderData as $uId => $folderId) { + $duplicate->setLinkMultipleColumn('users', 'folderId', $uId, $folderId); + } + } + + $duplicate->set('isBeingImported', true); + + $this->getEntityManager()->saveEntity($duplicate); + + if (!empty($teamsIdList)) { + foreach ($teamsIdList as $teamId) { + $this->getEntityManager()->getRepository('Email')->relate($duplicate, 'teams', $teamId); + } + } + } } From 8964e98873a5828813e7be1994082b5c48c28d69 Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 15:10:51 +0200 Subject: [PATCH 27/92] version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 29a350211e..92207e0e30 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "espocrm", - "version": "5.0.3", + "version": "5.0.4", "description": "", "main": "index.php", "repository": { From f83827886e8878cd1ec9a94a7ad01b6c5f39d7ec Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 16:07:46 +0200 Subject: [PATCH 28/92] chart sales by month fix --- .../Espo/Modules/Crm/Services/Opportunity.php | 44 +++++++++++++++++-- .../crm/src/views/dashlets/sales-by-month.js | 23 ++++++---- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/application/Espo/Modules/Crm/Services/Opportunity.php b/application/Espo/Modules/Crm/Services/Opportunity.php index ed420137d0..fd403be09a 100644 --- a/application/Espo/Modules/Crm/Services/Opportunity.php +++ b/application/Espo/Modules/Crm/Services/Opportunity.php @@ -159,8 +159,46 @@ class Opportunity extends \Espo\Services\Record $result[$row['month']] = floatval($row['amount']); } - return $result; + $dt = new \DateTime($dateFrom); + $dtTo = new \DateTime($dateTo); + $dtTo->setDate($dtTo->format('Y'), $dtTo->format('m'), 1); + if ($dt && $dtTo) { + $interval = new \DateInterval('P1M'); + while ($dt->getTimestamp() <= $dtTo->getTimestamp()) { + $month = $dt->format('Y-m'); + if (!array_key_exists($month, $result)) { + $result[$month] = 0; + } + $dt->add($interval); + } + } + + + $keyList = array_keys($result); + sort($keyList); + + $today = new \DateTime(); + + $endPosition = count($keyList) - 1; + for ($i = count($keyList) - 1; $i >= 0; $i--) { + $key = $keyList[$i]; + $dt = new \DateTime($key . '-01'); + + if ($dt->getTimestamp() < $today->getTimestamp()) { + break; + } + if (empty($result[$key])) { + $endPosition = $i; + } else { + break; + } + } + + $keyList = array_slice($keyList, 0, $endPosition); + + return (object) [ + 'keyList' => $keyList, + 'dataMap' => $result + ]; } - } - diff --git a/client/modules/crm/src/views/dashlets/sales-by-month.js b/client/modules/crm/src/views/dashlets/sales-by-month.js index 5690c880d3..099e88b994 100644 --- a/client/modules/crm/src/views/dashlets/sales-by-month.js +++ b/client/modules/crm/src/views/dashlets/sales-by-month.js @@ -46,13 +46,15 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch }, prepareData: function (response) { - var months = this.months = Object.keys(response).sort(); + var monthList = this.monthList = response.keyList; + + var dataMap = response.dataMap || {}; var values = []; - for (var month in response) { - values.push(response[month]); - } + monthList.forEach(function (month) { + values.push(dataMap[month]); + }, this); this.chartData = []; @@ -113,8 +115,8 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch tickFormatter: function (value) { if (value % 1 == 0) { var i = parseInt(value); - if (i in self.months) { - return moment(self.months[i] + '-01').format('MMM YYYY'); + if (i in self.monthList) { + return moment(self.monthList[i] + '-01').format('MMM YYYY'); } } return ''; @@ -124,10 +126,15 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch track: true, relative: true, trackFormatter: function (obj) { - return self.currencySymbol + self.formatNumber(obj.y); + var i = parseInt(obj.x); + var value = ''; + if (i in self.monthList) { + value += moment(self.monthList[i] + '-01').format('MMM YYYY') + ':
'; + } + return value + self.currencySymbol + self.formatNumber(obj.y); }, } - }); + }) }, }); }); From 9a17dbbd688ea50ae45062ddd87a7121cf2464ed Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 16:26:33 +0200 Subject: [PATCH 29/92] chart overflow fix --- client/modules/crm/src/views/dashlets/abstract/chart.js | 4 ++++ frontend/less/espo/custom.less | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/client/modules/crm/src/views/dashlets/abstract/chart.js b/client/modules/crm/src/views/dashlets/abstract/chart.js index 932d9960f9..fab47423eb 100644 --- a/client/modules/crm/src/views/dashlets/abstract/chart.js +++ b/client/modules/crm/src/views/dashlets/abstract/chart.js @@ -95,6 +95,10 @@ Espo.define('crm:views/dashlets/abstract/chart', ['views/dashlets/abstract/base' }, afterRender: function () { + this.$el.closest('.panel-body').css({ + 'overflow-y': 'visible', + 'overflow-x': 'visible' + }); this.fetch(function (data) { this.chartData = this.prepareData(data); diff --git a/frontend/less/espo/custom.less b/frontend/less/espo/custom.less index bbe83fe44d..2674821961 100644 --- a/frontend/less/espo/custom.less +++ b/frontend/less/espo/custom.less @@ -1206,6 +1206,11 @@ stream-head-text-container .span { font-size: @font-size-base; } +.dashlet-body .legend-container { + overflow-x: hidden; + overflow-y: hidden; +} + .legend-container table td { padding: 2px 2px; line-height: 1em; From 85d8c9795a34e59b3327ffd225eca85507e50a25 Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 24 Jan 2018 16:41:19 +0200 Subject: [PATCH 30/92] fix calendar warning --- client/modules/crm/src/views/calendar/calendar.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/modules/crm/src/views/calendar/calendar.js b/client/modules/crm/src/views/calendar/calendar.js index b796ffbe1d..15a3492984 100644 --- a/client/modules/crm/src/views/calendar/calendar.js +++ b/client/modules/crm/src/views/calendar/calendar.js @@ -158,6 +158,10 @@ Espo.define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], functi if (Object.prototype.toString.call(this.enabledScopeList) !== '[object Array]') { this.enabledScopeList = []; } + + this.once('remove', function () { + this.isRemoved = true; + }, this); }, toggleScopeFilter: function (name) { @@ -376,6 +380,7 @@ Espo.define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], functi }, adjustSize: function () { + if (this.isRemoved) return; var height = this.getCalculatedHeight(); this.$calendar.fullCalendar('option', 'contentHeight', height); }, From a08fa65fae735ca84ff3013212611aefff0feb12 Mon Sep 17 00:00:00 2001 From: yuri Date: Thu, 25 Jan 2018 15:47:47 +0200 Subject: [PATCH 31/92] chart improvements --- .../Espo/Resources/metadata/themes/Espo.json | 8 +- .../Resources/metadata/themes/EspoRtl.json | 8 +- .../metadata/themes/EspoVertical.json | 8 +- .../Resources/metadata/themes/Hazyblue.json | 8 +- .../metadata/themes/HazyblueVertical.json | 8 +- .../Resources/metadata/themes/Sakura.json | 8 +- .../metadata/themes/SakuraVertical.json | 8 +- .../Resources/metadata/themes/Violet.json | 8 +- .../metadata/themes/VioletVertical.json | 8 +- .../crm/src/views/dashlets/abstract/chart.js | 46 +++++++--- .../dashlets/opportunities-by-lead-source.js | 25 +++--- .../views/dashlets/opportunities-by-stage.js | 32 ++++--- .../crm/src/views/dashlets/sales-by-month.js | 10 ++- .../crm/src/views/dashlets/sales-pipeline.js | 87 +++++-------------- frontend/less/espo/custom.less | 17 ++++ 15 files changed, 175 insertions(+), 114 deletions(-) diff --git a/application/Espo/Resources/metadata/themes/Espo.json b/application/Espo/Resources/metadata/themes/Espo.json index 68e3ef6163..2bccf23894 100644 --- a/application/Espo/Resources/metadata/themes/Espo.json +++ b/application/Espo/Resources/metadata/themes/Espo.json @@ -5,5 +5,11 @@ "dashboardCellMargin": 19, "navbarHeight": 44, "modalFooterAtTheTop": true, - "modalFullHeight": true + "modalFullHeight": true, + "textColor": "#333", + "chartGridColor": "#333", + "chartSuccessColor": "#87C956", + "chartTickColor": "#ddd", + "chartColorList": ["#6FA8D6", "#4E6CAD", "#EDC555", "#ED8F42", "#DE6666", "#7CC4A4", "#8A7CC2", "#D4729B"], + "chartColorAlternativeList": ["#6FA8D6", "#EDC555", "#ED8F42", "#7CC4A4", "#D4729B"] } \ No newline at end of file diff --git a/application/Espo/Resources/metadata/themes/EspoRtl.json b/application/Espo/Resources/metadata/themes/EspoRtl.json index 5bf66f9f5b..0052bd47aa 100644 --- a/application/Espo/Resources/metadata/themes/EspoRtl.json +++ b/application/Espo/Resources/metadata/themes/EspoRtl.json @@ -5,5 +5,11 @@ "dashboardCellMargin": 19, "navbarHeight": 44, "modalFooterAtTheTop": true, - "modalFullHeight": true + "modalFullHeight": true, + "textColor": "#333", + "chartGridColor": "#333", + "chartTickColor": "#ddd", + "chartSuccessColor": "#87C956", + "chartColorList": ["#6FA8D6", "#4E6CAD", "#EDC555", "#ED8F42", "#DE6666", "#7CC4A4", "#8A7CC2", "#D4729B"], + "chartColorAlternativeList": ["#6FA8D6", "#EDC555", "#ED8F42", "#7CC4A4", "#D4729B"] } \ No newline at end of file diff --git a/application/Espo/Resources/metadata/themes/EspoVertical.json b/application/Espo/Resources/metadata/themes/EspoVertical.json index b7ad366217..142ac48f77 100644 --- a/application/Espo/Resources/metadata/themes/EspoVertical.json +++ b/application/Espo/Resources/metadata/themes/EspoVertical.json @@ -8,5 +8,11 @@ "dashboardCellHeight": 155, "dashboardCellMargin": 19, "modalFooterAtTheTop": true, - "modalFullHeight": true + "modalFullHeight": true, + "textColor": "#333", + "chartGridColor": "#333", + "chartTickColor": "#ddd", + "chartSuccessColor": "#87C956", + "chartColorList": ["#6FA8D6", "#4E6CAD", "#EDC555", "#ED8F42", "#DE6666", "#7CC4A4", "#8A7CC2", "#D4729B"], + "chartColorAlternativeList": ["#6FA8D6", "#EDC555", "#ED8F42", "#7CC4A4", "#D4729B"] } \ No newline at end of file diff --git a/application/Espo/Resources/metadata/themes/Hazyblue.json b/application/Espo/Resources/metadata/themes/Hazyblue.json index f4b601f94f..5746ebbba4 100644 --- a/application/Espo/Resources/metadata/themes/Hazyblue.json +++ b/application/Espo/Resources/metadata/themes/Hazyblue.json @@ -5,5 +5,11 @@ "dashboardCellMargin": 19, "navbarHeight": 44, "modalFooterAtTheTop": true, - "modalFullHeight": true + "modalFullHeight": true, + "textColor": "#333", + "chartGridColor": "#333", + "chartTickColor": "#ddd", + "chartSuccessColor": "#85b75f", + "chartColorList": ["#6FA8D6", "#4E6CAD", "#EDC555", "#ED8F42", "#DE6666", "#7CC4A4", "#8A7CC2", "#D4729B"], + "chartColorAlternativeList": ["#6FA8D6", "#EDC555", "#ED8F42", "#7CC4A4", "#D4729B"] } \ No newline at end of file diff --git a/application/Espo/Resources/metadata/themes/HazyblueVertical.json b/application/Espo/Resources/metadata/themes/HazyblueVertical.json index a4d1a20b59..774543400c 100644 --- a/application/Espo/Resources/metadata/themes/HazyblueVertical.json +++ b/application/Espo/Resources/metadata/themes/HazyblueVertical.json @@ -8,5 +8,11 @@ "dashboardCellHeight": 155, "dashboardCellMargin": 19, "modalFooterAtTheTop": true, - "modalFullHeight": true + "modalFullHeight": true, + "textColor": "#333", + "chartGridColor": "#333", + "chartTickColor": "#ddd", + "chartSuccessColor": "#85b75f", + "chartColorList": ["#6FA8D6", "#4E6CAD", "#EDC555", "#ED8F42", "#DE6666", "#7CC4A4", "#8A7CC2", "#D4729B"], + "chartColorAlternativeList": ["#6FA8D6", "#EDC555", "#ED8F42", "#7CC4A4", "#D4729B"] } \ No newline at end of file diff --git a/application/Espo/Resources/metadata/themes/Sakura.json b/application/Espo/Resources/metadata/themes/Sakura.json index c499cd81eb..af2e37e201 100644 --- a/application/Espo/Resources/metadata/themes/Sakura.json +++ b/application/Espo/Resources/metadata/themes/Sakura.json @@ -5,5 +5,11 @@ "dashboardCellMargin": 19, "navbarHeight": 44, "modalFooterAtTheTop": true, - "modalFullHeight": true + "modalFullHeight": true, + "textColor": "#333", + "chartGridColor": "#333", + "chartTickColor": "#ddd", + "chartSuccessColor": "#83CD77", + "chartColorList": ["#6FA8D6", "#4E6CAD", "#EDC555", "#ED8F42", "#DE6666", "#7CC4A4", "#8A7CC2", "#D4729B"], + "chartColorAlternativeList": ["#6FA8D6", "#EDC555", "#ED8F42", "#7CC4A4", "#D4729B"] } \ No newline at end of file diff --git a/application/Espo/Resources/metadata/themes/SakuraVertical.json b/application/Espo/Resources/metadata/themes/SakuraVertical.json index 68bd61cb8c..1005ef315c 100644 --- a/application/Espo/Resources/metadata/themes/SakuraVertical.json +++ b/application/Espo/Resources/metadata/themes/SakuraVertical.json @@ -8,5 +8,11 @@ "dashboardCellHeight": 155, "dashboardCellMargin": 19, "modalFooterAtTheTop": true, - "modalFullHeight": true + "modalFullHeight": true, + "textColor": "#333", + "chartGridColor": "#333", + "chartTickColor": "#ddd", + "chartSuccessColor": "#83CD77", + "chartColorList": ["#6FA8D6", "#4E6CAD", "#EDC555", "#ED8F42", "#DE6666", "#7CC4A4", "#8A7CC2", "#D4729B"], + "chartColorAlternativeList": ["#6FA8D6", "#EDC555", "#ED8F42", "#7CC4A4", "#D4729B"] } \ No newline at end of file diff --git a/application/Espo/Resources/metadata/themes/Violet.json b/application/Espo/Resources/metadata/themes/Violet.json index abb43296f5..6f6d340bac 100644 --- a/application/Espo/Resources/metadata/themes/Violet.json +++ b/application/Espo/Resources/metadata/themes/Violet.json @@ -5,5 +5,11 @@ "dashboardCellMargin": 19, "navbarHeight": 44, "modalFooterAtTheTop": true, - "modalFullHeight": true + "modalFullHeight": true, + "textColor": "#333", + "chartGridColor": "#333", + "chartTickColor": "#ddd", + "chartSuccessColor": "#7BC169", + "chartColorList": ["#6FA8D6", "#4E6CAD", "#EDC555", "#ED8F42", "#DE6666", "#7CC4A4", "#8A7CC2", "#D4729B"], + "chartColorAlternativeList": ["#6FA8D6", "#EDC555", "#ED8F42", "#7CC4A4", "#D4729B"] } \ No newline at end of file diff --git a/application/Espo/Resources/metadata/themes/VioletVertical.json b/application/Espo/Resources/metadata/themes/VioletVertical.json index b88b822757..0da777f73e 100644 --- a/application/Espo/Resources/metadata/themes/VioletVertical.json +++ b/application/Espo/Resources/metadata/themes/VioletVertical.json @@ -8,5 +8,11 @@ "dashboardCellHeight": 155, "dashboardCellMargin": 19, "modalFooterAtTheTop": true, - "modalFullHeight": true + "modalFullHeight": true, + "textColor": "#333", + "chartGridColor": "#333", + "chartTickColor": "#ddd", + "chartSuccessColor": "#7BC169", + "chartColorList": ["#6FA8D6", "#4E6CAD", "#EDC555", "#ED8F42", "#DE6666", "#7CC4A4", "#8A7CC2", "#D4729B"], + "chartColorAlternativeList": ["#6FA8D6", "#EDC555", "#ED8F42", "#7CC4A4", "#D4729B"] } \ No newline at end of file diff --git a/client/modules/crm/src/views/dashlets/abstract/chart.js b/client/modules/crm/src/views/dashlets/abstract/chart.js index fab47423eb..190aedda86 100644 --- a/client/modules/crm/src/views/dashlets/abstract/chart.js +++ b/client/modules/crm/src/views/dashlets/abstract/chart.js @@ -36,17 +36,35 @@ Espo.define('crm:views/dashlets/abstract/chart', ['views/dashlets/abstract/base' thousandSeparator: ',', - colors: ['#6FA8D6', '#4E6CAD', '#EDC555', '#ED8F42', '#DE6666', '#7CC4A4', '#8A7CC2', '#D4729B'], + defaultColorList: ['#6FA8D6', '#4E6CAD', '#EDC555', '#ED8F42', '#DE6666', '#7CC4A4', '#8A7CC2', '#D4729B'], successColor: '#85b75f', - outlineColor: '#333', + tickColor: '#ddd', + + textColor: '#333', + + hoverColor: '#FF3F19', + + legendColumnWidth: 90, + + legendColumnNumber: 6, + + labelFormatter: function (v) { + return '' + v + ''; + }, init: function () { Dep.prototype.init.call(this); this.flotr = Flotr; + this.successColor = this.getThemeManager().getParam('chartSuccessColor') || this.successColor; + this.colorList = this.getThemeManager().getParam('chartColorList') || this.defaultColorList; + this.tickColor = this.getThemeManager().getParam('chartTickColor') || this.tickColor; + this.textColor = this.getThemeManager().getParam('textColor') || this.textColor; + this.hoverColor = this.getThemeManager().getParam('hoverColor') || this.hoverColor; + if (this.getPreferences().has('decimalMark')) { this.decimalMark = this.getPreferences().get('decimalMark') } else { @@ -64,7 +82,7 @@ Espo.define('crm:views/dashlets/abstract/chart', ['views/dashlets/abstract/base' this.once('after:render', function () { $(window).on('resize.chart' + this.name, function () { - this.drow(); + this.draw(); }.bind(this)); }, this); @@ -85,11 +103,19 @@ Espo.define('crm:views/dashlets/abstract/chart', ['views/dashlets/abstract/base' return ''; }, + getLegentColumnNumber: function () { + var width = this.$el.closest('.panel-body').width(); + var legendColumnNumber = Math.floor(width / this.legendColumnWidth); + return legendColumnNumber || this.legendColumnNumber; + }, + getLegentHeight: function () { - if (this.chartData.length > 5) { - legendHeight = this.getThemeManager().getParam('dashletChartLegent2RowHeight') || 52; - } else { - legendHeight = this.getThemeManager().getParam('dashletChartLegent1RowHeight') || 22; + var lineNumber = Math.ceil(this.chartData.length / this.getLegentColumnNumber()); + var legendHeight = 0; + + var lineHeight = this.getThemeManager().getParam('dashletChartLegentRowHeight') || 22; + if (lineNumber > 0) { + legendHeight = lineHeight * lineNumber; } return legendHeight; }, @@ -103,16 +129,13 @@ Espo.define('crm:views/dashlets/abstract/chart', ['views/dashlets/abstract/base' this.chartData = this.prepareData(data); var $container = this.$container = this.$el.find('.chart-container'); - var legendHeight = this.getLegentHeight(); - var heightCss = 'calc(100% - '+legendHeight.toString()+'px)'; - $container.css('height', heightCss); setTimeout(function () { if (!$container.size() || !$container.is(":visible")) return; - this.drow(); + this.draw(); }.bind(this), 1); }); }, @@ -135,4 +158,3 @@ Espo.define('crm:views/dashlets/abstract/chart', ['views/dashlets/abstract/base' }); }); - diff --git a/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js b/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js index 211e89e82e..fb20ecf3b7 100644 --- a/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js +++ b/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js @@ -58,10 +58,10 @@ Espo.define('crm:views/dashlets/opportunities-by-lead-source', 'crm:views/dashle this.currencySymbol = this.getMetadata().get(['app', 'currency', 'symbolMap', this.currency]) || ''; }, - drow: function () { + draw: function () { var self = this; this.flotr.draw(this.$container.get(0), this.chartData, { - colors: this.colors, + colors: this.colorList, shadowSize: false, pie: { show: true, @@ -72,20 +72,22 @@ Espo.define('crm:views/dashlets/opportunities-by-lead-source', 'crm:views/dashle labelFormatter: function (total, value) { var percentage = (100 * value / total).toFixed(2); if (percentage < 5) return ''; - return ''+ percentage.toString() +'%' + ''; + return ''+ percentage.toString() +'%' + ''; } }, grid: { horizontalLines: false, verticalLines: false, outline: '', - color: this.outlineColor + tickColor: this.tickColor }, yaxis: { showLabels: false, + color: this.textColor }, xaxis: { showLabels: false, + color: this.textColor }, legend: { show: false, @@ -93,6 +95,7 @@ Espo.define('crm:views/dashlets/opportunities-by-lead-source', 'crm:views/dashle mouse: { track: true, relative: true, + lineColor: this.hoverColor, trackFormatter: function (obj) { var value = self.currencySymbol + self.formatNumber(obj.y); @@ -104,14 +107,14 @@ Espo.define('crm:views/dashlets/opportunities-by-lead-source', 'crm:views/dashle }, legend: { show: true, - noColumns: 8, + noColumns: this.getLegentColumnNumber(), container: this.$el.find('.legend-container'), - labelBoxMargin: 0 - }, + labelBoxMargin: 0, + labelFormatter: self.labelFormatter.bind(self), + labelBoxBorderColor: 'transparent', + backgroundOpacity: 0 + } }); - }, - + } }); }); - - diff --git a/client/modules/crm/src/views/dashlets/opportunities-by-stage.js b/client/modules/crm/src/views/dashlets/opportunities-by-stage.js index 36a90261b9..f00eb12ed4 100644 --- a/client/modules/crm/src/views/dashlets/opportunities-by-stage.js +++ b/client/modules/crm/src/views/dashlets/opportunities-by-stage.js @@ -60,7 +60,7 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs data: [[item.value, d.length - i]], label: this.getLanguage().translateOption(item.stage, 'stage', 'Opportunity'), } - if (item.stage == 'Closed Won') { + if (item.stagsuccessColore == 'Closed Won') { o.color = this.successColor; } data.push(o); @@ -76,10 +76,10 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs this.currencySymbol = this.getMetadata().get(['app', 'currency', 'symbolMap', this.currency]) || ''; }, - drow: function () { + draw: function () { var self = this; this.flotr.draw(this.$container.get(0), this.chartData, { - colors: this.colors, + colors: this.colorList, shadowSize: false, bars: { show: true, @@ -87,46 +87,50 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs shadowSize: 0, lineWidth: 1, fillOpacity: 1, - barWidth: 0.5, + barWidth: 0.5 }, grid: { horizontalLines: false, outline: 'sw', - color: this.outlineColor + color: this.tickColor, + tickColor: this.tickColor }, yaxis: { min: 0, showLabels: false, + color: this.textColor }, xaxis: { min: 0, + color: this.textColor, tickFormatter: function (value) { if (value != 0) { return self.currencySymbol + self.formatNumber(value); } return ''; - }, + } }, mouse: { track: true, relative: true, position: 's', + lineColor: this.hoverColor, trackFormatter: function (obj) { var label = (obj.series.label || self.translate('None')); var value = label + ':
' + self.currencySymbol + self.formatNumber(obj.x); return value; - }, + } }, legend: { show: true, - noColumns: 5, + noColumns: this.getLegentColumnNumber(), container: this.$el.find('.legend-container'), - labelBoxMargin: 0 - }, + labelBoxMargin: 0, + labelFormatter: self.labelFormatter.bind(self), + labelBoxBorderColor: 'transparent', + backgroundOpacity: 0 + } }); - }, - + } }); }); - - diff --git a/client/modules/crm/src/views/dashlets/sales-by-month.js b/client/modules/crm/src/views/dashlets/sales-by-month.js index 099e88b994..987281049a 100644 --- a/client/modules/crm/src/views/dashlets/sales-by-month.js +++ b/client/modules/crm/src/views/dashlets/sales-by-month.js @@ -82,7 +82,7 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch this.colorBad = this.successColor; }, - drow: function () { + draw: function () { var self = this; this.flotr.draw(this.$container.get(0), this.chartData, { shadowSize: false, @@ -98,11 +98,13 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch horizontalLines: true, verticalLines: false, outline: 'sw', - color: this.outlineColor + color: this.tickColor, + tickColor: this.tickColor }, yaxis: { min: 0, showLabels: true, + color: this.textColor, tickFormatter: function (value) { if (value == 0) { return ''; @@ -112,6 +114,7 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch }, xaxis: { min: 0, + color: this.textColor, tickFormatter: function (value) { if (value % 1 == 0) { var i = parseInt(value); @@ -125,6 +128,7 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch mouse: { track: true, relative: true, + lineColor: this.hoverColor, trackFormatter: function (obj) { var i = parseInt(obj.x); var value = ''; @@ -132,7 +136,7 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch value += moment(self.monthList[i] + '-01').format('MMM YYYY') + ':
'; } return value + self.currencySymbol + self.formatNumber(obj.y); - }, + } } }) }, diff --git a/client/modules/crm/src/views/dashlets/sales-pipeline.js b/client/modules/crm/src/views/dashlets/sales-pipeline.js index bafdbacbad..cad9106258 100644 --- a/client/modules/crm/src/views/dashlets/sales-pipeline.js +++ b/client/modules/crm/src/views/dashlets/sales-pipeline.js @@ -82,61 +82,13 @@ Espo.define('crm:views/dashlets/sales-pipeline', 'crm:views/dashlets/abstract/ch this.currency = this.getConfig().get('defaultCurrency'); this.currencySymbol = this.getMetadata().get(['app', 'currency', 'symbolMap', this.currency]) || ''; - var data = [ - { - value: 12000, - stage: 'Prospecting' - }, - { - value: 5050, - stage: 'Qualification' - }, - { - value: 4050, - stage: 'Needs Analysis' - }, - { - value: 3230, - stage: 'Value Proposition' - }, - { - value: 2000, - stage: 'Proposal/Price Quote' - }, - { - value: 1200.5, - stage: 'Negotiation/Review' - }, - { - value: 700, - stage: 'Closed Won' - }, - ]; - this.chartData = []; - - for (var i = 0; i < data.length; i++) { - var item = data[i]; - var value = item.value; - var nextValue = ((i + 1) < data.length) ? data[i + 1].value : value; - var o = { - data: [[i, value], [i + 1, nextValue]], - label: item.stage - }; - - this.chartData.push(o); - } - - this.maxY = 1000; - if (data.length) { - this.maxY = data[0].value + (data[0].value / 20); - } }, - drow: function () { + draw: function () { var self = this; - var colors = Espo.Utils.clone(this.colors); + var colors = Espo.Utils.clone(this.colorList); this.chartData.forEach(function (item, i) { if (i + 1 > colors.length) { @@ -154,46 +106,51 @@ Espo.define('crm:views/dashlets/sales-pipeline', 'crm:views/dashlets/abstract/ch lines: { show: true, fill: true, - fillOpacity: 1, + fillOpacity: 1 }, points: { - show: true, + show: true }, grid: { - horizontalLines: false, - outline: 'sw', - color: this.outlineColor + color: this.tickColor, + verticalLines: false, + outline: 'ew', + tickColor: this.tickColor }, yaxis: { min: 0, max: this.maxY, - showLabels: false, + showLabels: false }, xaxis: { min: 0, - showLabels: false, + showLabels: false }, mouse: { track: true, relative: true, position: 'ne', + lineColor: this.hoverColor, trackFormatter: function (obj) { if (obj.x >= self.chartData.length) { return null; } - return self.currencySymbol + self.formatNumber(obj.y); - }, + var label = self.chartData[parseInt(obj.x)].label; + var label = (label || self.translate('None')); + return label + ':
' + self.currencySymbol + self.formatNumber(obj.y); + } }, legend: { show: true, - noColumns: 5, + noColumns: this.getLegentColumnNumber(), container: this.$el.find('.legend-container'), - labelBoxMargin: 0 - }, + labelBoxMargin: 0, + labelFormatter: self.labelFormatter.bind(self), + labelBoxBorderColor: 'transparent', + backgroundOpacity: 0 + } }); - }, + } }); }); - - diff --git a/frontend/less/espo/custom.less b/frontend/less/espo/custom.less index 2674821961..a9def50a29 100644 --- a/frontend/less/espo/custom.less +++ b/frontend/less/espo/custom.less @@ -1216,6 +1216,23 @@ stream-head-text-container .span { line-height: 1em; } +.dashlet-body .legend-container td.flotr-legend-label > span { + max-height: 2em; + display: inline-block; + overflow: hidden; +} + +.legend-container .flotr-legend-color-box > div { + position: relative; + top: 1px; +} + +.legend-container .flotr-legend-color-box > div > div > div { + position: relative; + top: -2px; + left: -2px; +} + .textcomplete-item a { cursor: default; } From 65c3bb3291a81b94b4f6a08b75c3c0898f63ef90 Mon Sep 17 00:00:00 2001 From: yuri Date: Thu, 25 Jan 2018 16:08:56 +0200 Subject: [PATCH 32/92] chart fixes --- .../crm/src/views/dashlets/opportunities-by-lead-source.js | 4 ++-- frontend/less/espo/custom.less | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js b/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js index fb20ecf3b7..53b0dbf20c 100644 --- a/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js +++ b/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js @@ -71,8 +71,8 @@ Espo.define('crm:views/dashlets/opportunities-by-lead-source', 'crm:views/dashle sizeRatio: 0.8, labelFormatter: function (total, value) { var percentage = (100 * value / total).toFixed(2); - if (percentage < 5) return ''; - return ''+ percentage.toString() +'%' + ''; + if (percentage < 7) return ''; + return ''+ percentage.toString() +'%' + ''; } }, grid: { diff --git a/frontend/less/espo/custom.less b/frontend/less/espo/custom.less index a9def50a29..f8023da356 100644 --- a/frontend/less/espo/custom.less +++ b/frontend/less/espo/custom.less @@ -1216,6 +1216,10 @@ stream-head-text-container .span { line-height: 1em; } +.flotr-mouse-value { + cursor: default; +} + .dashlet-body .legend-container td.flotr-legend-label > span { max-height: 2em; display: inline-block; From 7bcacdbd27f64da4e325abe87a8a9eb5f023f6b1 Mon Sep 17 00:00:00 2001 From: yuri Date: Thu, 25 Jan 2018 16:25:37 +0200 Subject: [PATCH 33/92] css fix --- frontend/less/espo/custom.less | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/less/espo/custom.less b/frontend/less/espo/custom.less index f8023da356..e90a0f26f1 100644 --- a/frontend/less/espo/custom.less +++ b/frontend/less/espo/custom.less @@ -1218,6 +1218,7 @@ stream-head-text-container .span { .flotr-mouse-value { cursor: default; + font-size: 0.9em; } .dashlet-body .legend-container td.flotr-legend-label > span { From 8d0c1348b1576e8827262e359eb6466043181c9d Mon Sep 17 00:00:00 2001 From: yuri Date: Thu, 25 Jan 2018 17:31:22 +0200 Subject: [PATCH 34/92] chart fixes --- .../crm/src/views/dashlets/abstract/chart.js | 43 +++++++++++++++++-- .../dashlets/opportunities-by-lead-source.js | 2 +- .../views/dashlets/opportunities-by-stage.js | 9 ++-- .../crm/src/views/dashlets/sales-by-month.js | 7 ++- .../crm/src/views/dashlets/sales-pipeline.js | 2 +- 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/client/modules/crm/src/views/dashlets/abstract/chart.js b/client/modules/crm/src/views/dashlets/abstract/chart.js index 190aedda86..fbfd885306 100644 --- a/client/modules/crm/src/views/dashlets/abstract/chart.js +++ b/client/modules/crm/src/views/dashlets/abstract/chart.js @@ -91,14 +91,49 @@ Espo.define('crm:views/dashlets/abstract/chart', ['views/dashlets/abstract/base' }, this); }, - formatNumber: function (value) { + formatNumber: function (value, isCurrency) { if (value !== null) { + var maxDecimalPlaces = 2; + var currencyDecimalPlaces = this.getConfig().get('currencyDecimalPlaces'); + + if (isCurrency) { + if (currencyDecimalPlaces === 0) { + value = Math.round(value); + } else if (currencyDecimalPlaces) { + value = Math.round(value * Math.pow(10, currencyDecimalPlaces)) / (Math.pow(10, currencyDecimalPlaces)); + } else { + value = Math.round(value * Math.pow(10, maxDecimalPlaces)) / (Math.pow(10, maxDecimalPlaces)); + } + } else { + var maxDecimalPlaces = 4; + value = Math.round(value * Math.pow(10, maxDecimalPlaces)) / (Math.pow(10, maxDecimalPlaces)); + } + var parts = value.toString().split("."); parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, this.thousandSeparator); - if (parts[1] == 0) { - parts.splice(1, 1); + + if (isCurrency) { + if (currencyDecimalPlaces === 0) { + delete parts[1]; + } else if (currencyDecimalPlaces) { + var decimalPartLength = 0; + if (parts.length > 1) { + decimalPartLength = parts[1].length; + } else { + parts[1] = ''; + } + + if (currencyDecimalPlaces && decimalPartLength < currencyDecimalPlaces) { + var limit = currencyDecimalPlaces - decimalPartLength; + for (var i = 0; i < limit; i++) { + parts[1] += '0'; + } + } + } } - return parts.join(this.decimalMark); + + var value = parts.join(this.decimalMark); + return value; } return ''; }, diff --git a/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js b/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js index 53b0dbf20c..3af2cc66df 100644 --- a/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js +++ b/client/modules/crm/src/views/dashlets/opportunities-by-lead-source.js @@ -97,7 +97,7 @@ Espo.define('crm:views/dashlets/opportunities-by-lead-source', 'crm:views/dashle relative: true, lineColor: this.hoverColor, trackFormatter: function (obj) { - var value = self.currencySymbol + self.formatNumber(obj.y); + var value = self.currencySymbol + self.formatNumber(obj.y, true); var fraction = obj.fraction || 0; var percentage = (100 * fraction).toFixed(2).toString(); diff --git a/client/modules/crm/src/views/dashlets/opportunities-by-stage.js b/client/modules/crm/src/views/dashlets/opportunities-by-stage.js index f00eb12ed4..0599550a2f 100644 --- a/client/modules/crm/src/views/dashlets/opportunities-by-stage.js +++ b/client/modules/crm/src/views/dashlets/opportunities-by-stage.js @@ -104,8 +104,11 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs min: 0, color: this.textColor, tickFormatter: function (value) { - if (value != 0) { - return self.currencySymbol + self.formatNumber(value); + if (value == 0) { + return ''; + } + if (value % 1 == 0) { + return self.currencySymbol + self.formatNumber(Math.floor(value)).toString(); } return ''; } @@ -117,7 +120,7 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs lineColor: this.hoverColor, trackFormatter: function (obj) { var label = (obj.series.label || self.translate('None')); - var value = label + ':
' + self.currencySymbol + self.formatNumber(obj.x); + var value = label + ':
' + self.currencySymbol + self.formatNumber(obj.x, true); return value; } }, diff --git a/client/modules/crm/src/views/dashlets/sales-by-month.js b/client/modules/crm/src/views/dashlets/sales-by-month.js index 987281049a..857ec3dfee 100644 --- a/client/modules/crm/src/views/dashlets/sales-by-month.js +++ b/client/modules/crm/src/views/dashlets/sales-by-month.js @@ -109,7 +109,10 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch if (value == 0) { return ''; } - return self.currencySymbol + self.formatNumber(value); + if (value % 1 == 0) { + return self.currencySymbol + self.formatNumber(Math.floor(value)).toString(); + } + return ''; }, }, xaxis: { @@ -135,7 +138,7 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch if (i in self.monthList) { value += moment(self.monthList[i] + '-01').format('MMM YYYY') + ':
'; } - return value + self.currencySymbol + self.formatNumber(obj.y); + return value + self.currencySymbol + self.formatNumber(obj.y, true); } } }) diff --git a/client/modules/crm/src/views/dashlets/sales-pipeline.js b/client/modules/crm/src/views/dashlets/sales-pipeline.js index cad9106258..25607f0b81 100644 --- a/client/modules/crm/src/views/dashlets/sales-pipeline.js +++ b/client/modules/crm/src/views/dashlets/sales-pipeline.js @@ -137,7 +137,7 @@ Espo.define('crm:views/dashlets/sales-pipeline', 'crm:views/dashlets/abstract/ch } var label = self.chartData[parseInt(obj.x)].label; var label = (label || self.translate('None')); - return label + ':
' + self.currencySymbol + self.formatNumber(obj.y); + return label + ':
' + self.currencySymbol + self.formatNumber(obj.y, true); } }, legend: { From 07128d04b06e995b00837e2486c1740a1bfb6c07 Mon Sep 17 00:00:00 2001 From: yuri Date: Thu, 25 Jan 2018 17:42:22 +0200 Subject: [PATCH 35/92] dashboard side menu issue fix --- client/src/views/site/navbar.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/client/src/views/site/navbar.js b/client/src/views/site/navbar.js index 7bbe75a9fd..63a2b2b74e 100644 --- a/client/src/views/site/navbar.js +++ b/client/src/views/site/navbar.js @@ -61,14 +61,7 @@ Espo.define('views/site/navbar', 'view', function (Dep) { this.quickCreate(scope); }, 'click a.minimizer': function () { - var $body = $('body'); - if ($body.hasClass('minimized')) { - $body.removeClass('minimized'); - this.getStorage().clear('state', 'layoutMinimized'); - } else { - $body.addClass('minimized'); - this.getStorage().set('state', 'layoutMinimized', true); - } + this.switchMinimizer(); }, 'click a.action': function (e) { var $el = $(e.currentTarget); @@ -83,6 +76,22 @@ Espo.define('views/site/navbar', 'view', function (Dep) { } }, + switchMinimizer: function () { + var $body = $('body'); + if ($body.hasClass('minimized')) { + $body.removeClass('minimized'); + this.getStorage().clear('state', 'layoutMinimized'); + } else { + $body.addClass('minimized'); + this.getStorage().set('state', 'layoutMinimized', true); + } + if (window.Event) { + try { + window.dispatchEvent(new Event('resize')); + } catch (e) {} + } + }, + getLogoSrc: function () { var companyLogoId = this.getConfig().get('companyLogoId'); if (!companyLogoId) { From 1e32df5a26d5a92881f8ee0fe9d71732059fee30 Mon Sep 17 00:00:00 2001 From: yuri Date: Thu, 25 Jan 2018 17:56:04 +0200 Subject: [PATCH 36/92] fix layout manager panel name loss --- client/res/templates/admin/layouts/grid-panel.tpl | 2 +- client/res/templates/admin/layouts/grid.tpl | 2 +- client/src/views/admin/layouts/grid.js | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/res/templates/admin/layouts/grid-panel.tpl b/client/res/templates/admin/layouts/grid-panel.tpl index 2a32e6f214..e87182313b 100644 --- a/client/res/templates/admin/layouts/grid-panel.tpl +++ b/client/res/templates/admin/layouts/grid-panel.tpl @@ -1,4 +1,4 @@ -
+
  diff --git a/client/res/templates/admin/layouts/grid.tpl b/client/res/templates/admin/layouts/grid.tpl index 5ab8f6a478..d24c78dc23 100644 --- a/client/res/templates/admin/layouts/grid.tpl +++ b/client/res/templates/admin/layouts/grid.tpl @@ -142,7 +142,7 @@