From d8cd0d31db007a0d35a3edab099a0bef744315f8 Mon Sep 17 00:00:00 2001 From: yuri Date: Thu, 25 Aug 2016 10:43:06 +0300 Subject: [PATCH] email template foreign fields and acl --- application/Espo/Services/EmailTemplate.php | 36 +++++++++- client/src/acl-manager.js | 32 +++++++++ .../email-template/fields/insert-field.js | 67 ++++++++++++++++++- client/src/views/template/fields/variables.js | 13 ++++ 4 files changed, 143 insertions(+), 5 deletions(-) diff --git a/application/Espo/Services/EmailTemplate.php b/application/Espo/Services/EmailTemplate.php index 58be8b929e..4325ec0900 100644 --- a/application/Espo/Services/EmailTemplate.php +++ b/application/Espo/Services/EmailTemplate.php @@ -165,10 +165,15 @@ class EmailTemplate extends Record return $this->parseTemplate($emailTemplate, $params, $copyAttachments); } - protected function parseText($type, Entity $entity, $text) + protected function parseText($type, Entity $entity, $text, $skipLinks = false, $prefixLink = null) { - $fieldList = array_keys($entity->getFields()); + $fieldList = array_keys($entity->getAttributes()); + + $forbidenAttributeList = $this->getAcl()->getScopeForbiddenAttributeList($entity->getEntityType(), 'read'); + foreach ($fieldList as $field) { + if (in_array($field, $forbidenAttributeList)) continue; + $value = $entity->get($field); if (is_object($value)) { continue; @@ -195,9 +200,34 @@ class EmailTemplate extends Record } } if (is_string($value) || $value === null || is_scalar($value) || is_callable([$value, '__toString'])) { - $text = str_replace('{' . $type . '.' . $field . '}', $value, $text); + $variableName = $field; + if (!is_null($prefixLink)) { + $variableName = $prefixLink . '.' . $field; + } + $text = str_replace('{' . $type . '.' . $variableName . '}', $value, $text); } } + + if (!$skipLinks) { + $relationDefs = $entity->getRelations(); + foreach ($entity->getRelationList() as $relation) { + if ( + !empty($relationDefs[$relation]['type']) + && + ($entity->getRelationType($relation) === 'belongsTo' || $entity->getRelationType($relation) === 'belongsToParent') + ) { + $relatedEntity = $entity->get($relation); + if (!$relatedEntity) continue; + if ($this->getAcl()) { + if (!$this->getAcl()->check($relatedEntity, 'read')) continue; + } + + $text = $this->parseText($type, $relatedEntity, $text, true, $relation); + } + } + } + + return $text; } } diff --git a/client/src/acl-manager.js b/client/src/acl-manager.js index 62b2e5a523..cd27ee10aa 100644 --- a/client/src/acl-manager.js +++ b/client/src/acl-manager.js @@ -61,6 +61,7 @@ Espo.define('acl-manager', ['acl'], function (Acl) { this.implementationHash = {}; this.forbiddenFieldsCache = {}; this.implementationClassMap = {}; + this.forbiddenAttributesCache = {}; }, getImplementation: function (scope) { @@ -220,6 +221,37 @@ Espo.define('acl-manager', ['acl'], function (Acl) { this.forbiddenFieldsCache[key] = fieldList; return fieldList; + }, + + getScopeForbiddenAttributeList: function (scope, action, thresholdLevel) { + action = action || 'read'; + thresholdLevel = thresholdLevel || 'no'; + + var key = scope + '_' + action + '_' + thresholdLevel; + if (key in this.forbiddenAttributesCache) { + return this.forbiddenAttributesCache[key]; + } + + var levelList = this.fieldLevelList.slice(this.fieldLevelList.indexOf(thresholdLevel)); + + var fieldTableQuickAccess = this.data.fieldTableQuickAccess || {}; + var scopeData = fieldTableQuickAccess[scope] || {}; + + var attributesData = scopeData.attributes || {}; + var actionData = attributesData[action] || {}; + + var attributeList = []; + levelList.forEach(function (level) { + var list = actionData[level] || []; + list.forEach(function (attribute) { + if (~attributeList.indexOf(attribute)) return; + attributeList.push(attribute); + }, this); + }, this); + + this.forbiddenAttributesCache[key] = attributeList; + + return attributeList; } }); diff --git a/client/src/views/email-template/fields/insert-field.js b/client/src/views/email-template/fields/insert-field.js index 966891b5e1..bf4120e3b7 100644 --- a/client/src/views/email-template/fields/insert-field.js +++ b/client/src/views/email-template/fields/insert-field.js @@ -56,19 +56,74 @@ Espo.define('views/email-template/fields/insert-field', 'views/fields/base', fun return (defs[scope].entity && (defs[scope].tab || defs[scope].object)); }); + this.translatedOptions = {}; + var entityFields = {}; entityList.forEach(function (scope) { + this.translatedOptions[scope] = {}; + var list = this.getFieldManager().getEntityAttributes(scope) || []; + + var forbiddenList = this.getAcl().getScopeForbiddenAttributeList(scope); + list = list.filter(function (item) { + if (~forbiddenList.indexOf(item)) return; + return true; + }, this); + list.push('id'); if (this.getMetadata().get('entityDefs.' + scope + '.fields.name.type') == 'personName') { list.unshift('name'); + this.translatedOptions[scope]['name'] = this.translate('name', 'fields', scope); }; entityFields[scope] = list.sort(function (v1, v2) { return this.translate(v1, 'fields', scope).localeCompare(this.translate(v2, 'fields', scope)); }.bind(this)); + + entityFields[scope].forEach(function (item) { + this.translatedOptions[scope][item] = this.translate(item, 'fields', scope); + }, this); + + var links = this.getMetadata().get('entityDefs.' + scope + '.links') || {}; + + var linkList = Object.keys(links).sort(function (v1, v2) { + return this.translate(v1, 'links', scope).localeCompare(this.translate(v2, 'links', scope)); + }.bind(this)); + + linkList.forEach(function (link) { + var type = links[link].type + if (type != 'belongsTo') return; + var foreignScope = links[link].entity; + if (!foreignScope) return; + + var attributeList = this.getFieldManager().getEntityAttributes(foreignScope) || []; + + var forbiddenList = this.getAcl().getScopeForbiddenAttributeList(scope); + attributeList = attributeList.filter(function (item) { + if (~forbiddenList.indexOf(item)) return; + return true; + }, this); + + attributeList.push('id'); + if (this.getMetadata().get('entityDefs.' + foreignScope + '.fields.name.type') == 'personName') { + attributeList.unshift('name'); + }; + + attributeList.sort(function (v1, v2) { + return this.translate(v1, 'fields', foreignScope).localeCompare(this.translate(v2, 'fields', foreignScope)); + }.bind(this)); + + attributeList.forEach(function (item) { + entityFields[scope].push(link + '.' + item); + + this.translatedOptions[scope][link + '.' + item] = + this.translate(link, 'links', scope) + '.' + this.translate(item, 'fields', foreignScope); + }, this); + }, this); + }, this); entityFields['Person'] = ['name', 'firstName', 'lastName', 'salutationName', 'emailAddress', 'assignedUserName']; + this.translatedOptions['Person'] = {}; this.entityList = entityList; this.entityFields = entityFields; @@ -109,13 +164,21 @@ Espo.define('views/email-template/fields/insert-field', 'views/fields/base', fun var entityType = this.$entityType.val(); var fieldList = this.entityFields[entityType]; - this.$field.empty(); + this.$field.html(''); fieldList.forEach(function (field) { - this.$field.append(''); + this.$field.append(''); }, this); }, + translateItem: function (entityType, item) { + if (this.translatedOptions[entityType][item]) { + return this.translatedOptions[entityType][item]; + } else { + return this.translate(item, 'fields'); + } + }, + insert: function (entityType, field) { this.trigger('insert-field', { entityType: entityType, diff --git a/client/src/views/template/fields/variables.js b/client/src/views/template/fields/variables.js index 381fd58e22..71c88deca7 100644 --- a/client/src/views/template/fields/variables.js +++ b/client/src/views/template/fields/variables.js @@ -69,6 +69,13 @@ Espo.define('views/template/fields/variables', 'views/fields/base', function (De var entityType = this.model.get('entityType'); var attributeList = this.getFieldManager().getEntityAttributes(entityType) || []; + + var forbiddenList = this.getAcl().getScopeForbiddenAttributeList(entityType); + attributeList = attributeList.filter(function (item) { + if (~forbiddenList.indexOf(item)) return; + return true; + }, this); + attributeList.push('id'); if (this.getMetadata().get('entityDefs.' + entityType + '.fields.name.type') == 'personName') { attributeList.unshift('name'); @@ -100,6 +107,12 @@ Espo.define('views/template/fields/variables', 'views/fields/base', function (De var attributeList = this.getFieldManager().getEntityAttributes(scope) || []; + var forbiddenList = this.getAcl().getScopeForbiddenAttributeList(scope); + attributeList = attributeList.filter(function (item) { + if (~forbiddenList.indexOf(item)) return; + return true; + }, this); + attributeList.push('id'); if (this.getMetadata().get('entityDefs.' + scope + '.fields.name.type') == 'personName') { attributeList.unshift('name');