Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend
This commit is contained in:
@@ -100,10 +100,10 @@ class Htmlizer
|
||||
$forbidenAttributeList = $this->getAcl()->getScopeForbiddenAttributeList($entity->getEntityType(), 'read');
|
||||
}
|
||||
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
if (in_array($field, $forbidenAttributeList)) continue;
|
||||
|
||||
|
||||
$type = $entity->getAttributeType($field);
|
||||
|
||||
if ($type == Entity::DATETIME) {
|
||||
@@ -219,6 +219,14 @@ class Htmlizer
|
||||
return number_format($number, $decimals, $decimalPoint, $thousandsSeparator);
|
||||
}
|
||||
return '';
|
||||
},
|
||||
'var' => function ($context, $options) {
|
||||
if ($context && isset($context[0]) && isset($context[1])) {
|
||||
if (isset($context[1][$context[0]])) {
|
||||
return $context[1][$context[0]];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
],
|
||||
'hbhelpers' => [
|
||||
|
||||
@@ -205,6 +205,10 @@ class Importer
|
||||
))->findOne();
|
||||
if ($replied) {
|
||||
$email->set('repliedId', $replied->id);
|
||||
$repliedTeamIdList = $replied->getLinkMultipleIdList('teams');
|
||||
foreach ($repliedTeamIdList as $repliedTeamId) {
|
||||
$email->addLinkMultipleId('teams', $repliedTeamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1504,7 +1504,7 @@ class Base
|
||||
);
|
||||
}
|
||||
|
||||
public function getFullTextSearchDataForTextFilter($textFilter, $stripWildcard = false)
|
||||
public function getFullTextSearchDataForTextFilter($textFilter, $isAuxiliaryUse = false)
|
||||
{
|
||||
if (array_key_exists($textFilter, $this->fullTextSearchDataCacheHash)) {
|
||||
return $this->fullTextSearchDataCacheHash[$textFilter];
|
||||
@@ -1518,7 +1518,7 @@ class Base
|
||||
|
||||
$fieldList = $this->getTextFilterFieldList();
|
||||
|
||||
if ($stripWildcard) {
|
||||
if ($isAuxiliaryUse) {
|
||||
$textFilter = str_replace('%', '', $textFilter);
|
||||
}
|
||||
|
||||
@@ -1562,6 +1562,8 @@ class Base
|
||||
|
||||
if ($useFullTextSearch) {
|
||||
if (
|
||||
$isAuxiliaryUse
|
||||
||
|
||||
mb_strpos($textFilter, ' ') === false
|
||||
&&
|
||||
mb_strpos($textFilter, '+') === false
|
||||
@@ -1644,14 +1646,21 @@ class Base
|
||||
$attributeType = $fieldDefs[$field]['type'];
|
||||
}
|
||||
|
||||
if ($attributeType === 'int') {
|
||||
if (is_numeric($textFilter)) {
|
||||
$group[$field] = intval($textFilter);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$skipWidlcards) {
|
||||
if (
|
||||
strlen($textFilter) >= $textFilterContainsMinLength
|
||||
mb_strlen($textFilter) >= $textFilterContainsMinLength
|
||||
&&
|
||||
(
|
||||
$attributeType == 'text'
|
||||
||
|
||||
!empty($this->textFilterUseContainsAttributeList[$field])
|
||||
in_array($field, $this->textFilterUseContainsAttributeList)
|
||||
||
|
||||
$attributeType == 'varchar' && $this->getConfig()->get('textFilterUseContainsForVarchar')
|
||||
)
|
||||
@@ -1679,6 +1688,10 @@ class Base
|
||||
$group[$field . '*'] = $expression;
|
||||
}
|
||||
|
||||
if (!$forceFullTextSearch) {
|
||||
$this->applyAdditionalToTextFilterGroup($textFilter, $group);
|
||||
}
|
||||
|
||||
if (!empty($fullTextGroup)) {
|
||||
$group['AND'] = $fullTextGroup;
|
||||
}
|
||||
@@ -1694,6 +1707,10 @@ class Base
|
||||
];
|
||||
}
|
||||
|
||||
protected function applyAdditionalToTextFilterGroup($textFilter, &$group)
|
||||
{
|
||||
}
|
||||
|
||||
public function applyAccess(&$result)
|
||||
{
|
||||
$this->prepareResult($result);
|
||||
|
||||
@@ -43,11 +43,11 @@ class HasMany extends Base
|
||||
$entityName => array (
|
||||
'fields' => array(
|
||||
$linkName.'Ids' => array(
|
||||
'type' => 'varchar',
|
||||
'type' => 'jsonArray',
|
||||
'notStorable' => true,
|
||||
),
|
||||
$linkName.'Names' => array(
|
||||
'type' => 'varchar',
|
||||
'type' => 'jsonObject',
|
||||
'notStorable' => true,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -50,11 +50,11 @@ class ManyMany extends Base
|
||||
$entityName => array(
|
||||
'fields' => array(
|
||||
$linkName.'Ids' => array(
|
||||
'type' => 'varchar',
|
||||
'type' => 'jsonArray',
|
||||
'notStorable' => true,
|
||||
),
|
||||
$linkName.'Names' => array(
|
||||
'type' => 'varchar',
|
||||
'type' => 'jsonObject',
|
||||
'notStorable' => true,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -170,7 +170,7 @@ return array (
|
||||
'inlineAttachmentUploadMaxSize' => 20,
|
||||
'textFilterUseContainsForVarchar' => false,
|
||||
'tabColorsDisabled' => false,
|
||||
'massPrintPdfMaxCount' => 100,
|
||||
'massPrintPdfMaxCount' => 50,
|
||||
'isInstalled' => false
|
||||
);
|
||||
|
||||
|
||||
@@ -166,7 +166,8 @@
|
||||
},
|
||||
"collection": {
|
||||
"sortBy": "number",
|
||||
"asc": false
|
||||
"asc": false,
|
||||
"textFilterFields": ["name", "number"]
|
||||
},
|
||||
"indexes": {
|
||||
"status": {
|
||||
|
||||
@@ -336,7 +336,7 @@ abstract class Base
|
||||
}
|
||||
|
||||
$columns = substr($rest, 0, $delimiterPosition);
|
||||
$query = substr($rest, $delimiterPosition + 1);
|
||||
$query = mb_substr($rest, $delimiterPosition + 1);
|
||||
|
||||
$columnList = explode(',', $columns);
|
||||
|
||||
@@ -837,6 +837,7 @@ abstract class Base
|
||||
if (empty($isComplex)) {
|
||||
|
||||
if (!isset($entity->fields[$field])) {
|
||||
$whereParts[] = '0';
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -340,6 +340,7 @@
|
||||
"children": "Children",
|
||||
"id": "ID",
|
||||
"ids": "IDs",
|
||||
"type": "Type",
|
||||
"names": "Names",
|
||||
"targetListIsOptedOut": "Is Opted Out (Target List)"
|
||||
},
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
"layoutListDisabled": true,
|
||||
"provider": "Google",
|
||||
"height": 300,
|
||||
"exportDisabled": true
|
||||
"exportDisabled": true,
|
||||
"importDisabled": true
|
||||
}
|
||||
},
|
||||
"notMergeable":true,
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
"autoincrement":true,
|
||||
"unique":true
|
||||
},
|
||||
"textFilter": true,
|
||||
"readOnly": true
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
},
|
||||
"converted":{
|
||||
"type":"currencyConverted",
|
||||
"readOnly": true
|
||||
"readOnly": true,
|
||||
"importDisabled": true
|
||||
}
|
||||
},
|
||||
"filter": true,
|
||||
|
||||
@@ -27,5 +27,6 @@
|
||||
"fieldDefs":{
|
||||
"notStorable":true
|
||||
},
|
||||
"textFilter": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -31,5 +31,6 @@
|
||||
}
|
||||
],
|
||||
"filter": true,
|
||||
"textFilter": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -29,5 +29,6 @@
|
||||
"unique": false
|
||||
},
|
||||
"hookClassName": "\\Espo\\Core\\Utils\\FieldManager\\Hooks\\NumberType",
|
||||
"textFilter": true,
|
||||
"readOnly": true
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"filter":true,
|
||||
"skipOrmDefs": true,
|
||||
"personalData": true,
|
||||
"textFilter": true,
|
||||
"fullTextSearch": true,
|
||||
"fullTextSearchColumnList": [
|
||||
"first",
|
||||
|
||||
@@ -26,5 +26,6 @@
|
||||
"notStorable":true
|
||||
},
|
||||
"translatedOptions": true,
|
||||
"textFilter": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -38,5 +38,6 @@
|
||||
],
|
||||
"filter": true,
|
||||
"personalData": true,
|
||||
"textFilter": true,
|
||||
"fullTextSearch": true
|
||||
}
|
||||
|
||||
@@ -29,5 +29,6 @@
|
||||
],
|
||||
"filter": true,
|
||||
"personalData": true,
|
||||
"textFilter": true,
|
||||
"fullTextSearch": true
|
||||
}
|
||||
|
||||
@@ -36,5 +36,6 @@
|
||||
"type":"text"
|
||||
},
|
||||
"personalData": true,
|
||||
"textFilter": true,
|
||||
"fullTextSearch": true
|
||||
}
|
||||
|
||||
@@ -270,28 +270,16 @@ class Email extends \Espo\Core\SelectManagers\Base
|
||||
);
|
||||
}
|
||||
|
||||
protected function textFilter($textFilter, &$result)
|
||||
protected function applyAdditionalToTextFilterGroup($textFilter, &$group)
|
||||
{
|
||||
$d = array();
|
||||
|
||||
$d['name*'] = '%' . $textFilter . '%';
|
||||
|
||||
if (strlen($textFilter) >= self::MIN_LENGTH_FOR_CONTENT_SEARCH) {
|
||||
$d['bodyPlain*'] = '%' . $textFilter . '%';
|
||||
$d['body*'] = '%' . $textFilter . '%';
|
||||
|
||||
$emailAddressId = $this->getEmailAddressIdByValue($textFilter);
|
||||
if ($emailAddressId) {
|
||||
$this->leftJoinEmailAddress($result);
|
||||
$d['fromEmailAddressId'] = $emailAddressId;
|
||||
$d['emailEmailAddress.emailAddressId'] = $emailAddressId;
|
||||
$group['fromEmailAddressId'] = $emailAddressId;
|
||||
$group['emailEmailAddress.emailAddressId'] = $emailAddressId;
|
||||
}
|
||||
}
|
||||
|
||||
$result['whereClause'][] = array(
|
||||
'OR' => $d
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
protected function getEmailAddressIdByValue($value)
|
||||
|
||||
@@ -2168,12 +2168,19 @@ class Record extends \Espo\Core\Services\Base
|
||||
if (!empty($params['sortBy'])) {
|
||||
$sortByField = $params['sortBy'];
|
||||
$sortByFieldType = $this->getMetadata()->get(['entityDefs', $this->getEntityType(), 'fields', $sortByField, 'type']);
|
||||
|
||||
if ($sortByFieldType === 'currency') {
|
||||
if (!in_array($sortByField . 'Converted', $attributeList)) {
|
||||
$attributeList[] = $sortByField . 'Converted';
|
||||
}
|
||||
}
|
||||
|
||||
$sortByAttributeList = $this->getFieldManagerUtil()->getAttributeList($this->getEntityType(), $sortByField);
|
||||
foreach ($sortByAttributeList as $attribute) {
|
||||
if (!in_array($attribute, $attributeList) && $seed->hasAttribute($attribute)) {
|
||||
$attributeList[] = $attribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->mandatorySelectAttributeList as $attribute) {
|
||||
|
||||
@@ -36,7 +36,8 @@ Espo.define('crm:views/record/row-actions/activities', 'views/record/row-actions
|
||||
label: 'View',
|
||||
data: {
|
||||
id: this.model.id
|
||||
}
|
||||
},
|
||||
link: '#' + this.model.name + '/view/' + this.model.id
|
||||
}];
|
||||
if (this.options.acl.edit) {
|
||||
list.push({
|
||||
@@ -44,7 +45,8 @@ Espo.define('crm:views/record/row-actions/activities', 'views/record/row-actions
|
||||
label: 'Edit',
|
||||
data: {
|
||||
id: this.model.id
|
||||
}
|
||||
},
|
||||
link: '#' + this.model.name + '/edit/' + this.model.id
|
||||
});
|
||||
if (this.model.name == 'Meeting' || this.model.name == 'Call') {
|
||||
list.push({
|
||||
|
||||
@@ -36,7 +36,8 @@ Espo.define('crm:views/record/row-actions/history', 'views/record/row-actions/re
|
||||
label: 'View',
|
||||
data: {
|
||||
id: this.model.id
|
||||
}
|
||||
},
|
||||
link: '#' + this.model.name + '/view/' + this.model.id
|
||||
}];
|
||||
if (this.model.name == 'Email') {
|
||||
list.push({
|
||||
@@ -54,7 +55,8 @@ Espo.define('crm:views/record/row-actions/history', 'views/record/row-actions/re
|
||||
label: 'Edit',
|
||||
data: {
|
||||
id: this.model.id
|
||||
}
|
||||
},
|
||||
link: '#' + this.model.name + '/edit/' + this.model.id
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ Espo.define('crm:views/record/row-actions/tasks', 'views/record/row-actions/rela
|
||||
label: 'View',
|
||||
data: {
|
||||
id: this.model.id
|
||||
}
|
||||
},
|
||||
link: '#' + this.model.name + '/view/' + this.model.id
|
||||
}];
|
||||
if (this.options.acl.edit) {
|
||||
list.push({
|
||||
@@ -44,7 +45,8 @@ Espo.define('crm:views/record/row-actions/tasks', 'views/record/row-actions/rela
|
||||
label: 'Edit',
|
||||
data: {
|
||||
id: this.model.id
|
||||
}
|
||||
},
|
||||
link: '#' + this.model.name + '/edit/' + this.model.id
|
||||
});
|
||||
|
||||
if (!~['Completed', 'Canceled'].indexOf(this.model.get('status'))) {
|
||||
|
||||
@@ -161,6 +161,16 @@ Espo.define('email-helper', [], function () {
|
||||
attributes['parentType'] = model.get('parentType');
|
||||
}
|
||||
|
||||
if (model.get('teamsIds') && model.get('teamsIds').length) {
|
||||
attributes.teamsIds = Espo.Utils.clone(model.get('teamsIds'));
|
||||
attributes.teamsNames = Espo.Utils.clone(model.get('teamsNames') || {});
|
||||
|
||||
if (this.user.get('defaultTeamId')) {
|
||||
attributes.teamsIds.push(this.user.get('defaultTeamId'));
|
||||
attributes.teamsNames[this.user.get('defaultTeamId')] = this.user.get('defaultTeamName');
|
||||
}
|
||||
}
|
||||
|
||||
attributes.nameHash = nameHash;
|
||||
|
||||
attributes.repliedId = model.id;
|
||||
|
||||
@@ -280,9 +280,8 @@ Espo.define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'mo
|
||||
});
|
||||
|
||||
var optionList = Object.keys(fieldDefs).filter(function (item) {
|
||||
if (!~['varchar', 'wysiwyg', 'text', 'phone', 'email', 'personName', 'number'].indexOf(this.getMetadata().get(['entityDefs', scope, 'fields', item, 'type']))) {
|
||||
return false;
|
||||
}
|
||||
var fieldType = fieldDefs[item].type;
|
||||
if (!this.getMetadata().get(['fields', fieldType, 'textFilter'])) return false
|
||||
if (this.getMetadata().get(['entityDefs', scope, 'fields', item, 'disabled'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ Espo.define('views/import/step2', 'view', function (Dep) {
|
||||
for (var field in defs) {
|
||||
var d = defs[field];
|
||||
|
||||
if (!~this.allowedFieldList.indexOf(field) && (d.readOnly || d.disabled || d.importDisabled)) {
|
||||
if (!~this.allowedFieldList.indexOf(field) && (d.disabled || d.importDisabled)) {
|
||||
continue;
|
||||
}
|
||||
fieldList.push(field);
|
||||
@@ -187,7 +187,7 @@ Espo.define('views/import/step2', 'view', function (Dep) {
|
||||
|
||||
for (var field in fields) {
|
||||
var d = fields[field];
|
||||
if (!~this.allowedFieldList.indexOf(field) && (((d.readOnly || d.disabled) && !d.importNotDisabled) || d.importDisabled)) {
|
||||
if (!~this.allowedFieldList.indexOf(field) && (((d.disabled) && !d.importNotDisabled) || d.importDisabled)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -260,6 +260,11 @@ Espo.define('views/import/step2', 'view', function (Dep) {
|
||||
if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
|
||||
label = this.translate(baseField, 'fields', scope) + ' (' + this.translate('name', 'fields') + ')';
|
||||
}
|
||||
} else if (field.indexOf('Type') === field.length - 4) {
|
||||
var baseField = field.substr(0, field.length - 4);
|
||||
if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
|
||||
label = this.translate(baseField, 'fields', scope) + ' (' + this.translate('type', 'fields') + ')';
|
||||
}
|
||||
} else if (field.indexOf('phoneNumber') === 0) {
|
||||
var phoneNumberType = field.substr(11);
|
||||
var phoneNumberTypeLabel = this.getLanguage().translateOption(phoneNumberType, 'phoneNumber', scope);
|
||||
@@ -300,7 +305,13 @@ Espo.define('views/import/step2', 'view', function (Dep) {
|
||||
$('#default-values-container').append(html);
|
||||
|
||||
var type = Espo.Utils.upperCaseFirst(this.model.getFieldParam(name, 'type'));
|
||||
this.createView(name, this.getFieldManager().getViewName(type), {
|
||||
|
||||
var viewName =
|
||||
this.getMetadata().get(['entityDefs', this.scope, 'fields', name, 'view'])
|
||||
||
|
||||
this.getFieldManager().getViewName(type);
|
||||
|
||||
this.createView(name, viewName, {
|
||||
model: this.model,
|
||||
el: this.getSelector() + ' .field[data-name="' + name + '"]',
|
||||
defs: {
|
||||
|
||||
@@ -143,26 +143,7 @@ Espo.define('views/record/list', 'view', function (Dep) {
|
||||
}
|
||||
},
|
||||
'click .select-all': function (e) {
|
||||
this.checkedList = [];
|
||||
|
||||
if (e.currentTarget.checked) {
|
||||
this.$el.find('input.record-checkbox').prop('checked', true);
|
||||
this.$el.find('.actions-button').removeAttr('disabled');
|
||||
this.collection.models.forEach(function (model) {
|
||||
this.checkedList.push(model.id);
|
||||
}, this);
|
||||
|
||||
this.$el.find('.list > table tbody tr').addClass('active');
|
||||
} else {
|
||||
if (this.allResultIsChecked) {
|
||||
this.unselectAllResult();
|
||||
}
|
||||
this.$el.find('input.record-checkbox').prop('checked', false);
|
||||
this.$el.find('.actions-button').attr('disabled', true);
|
||||
this.$el.find('.list > table tbody tr').removeClass('active');
|
||||
}
|
||||
|
||||
this.trigger('check');
|
||||
this.selectAllHandler(e.currentTarget.checked);
|
||||
},
|
||||
'click .action': function (e) {
|
||||
var $el = $(e.currentTarget);
|
||||
@@ -190,6 +171,30 @@ Espo.define('views/record/list', 'view', function (Dep) {
|
||||
}
|
||||
},
|
||||
|
||||
selectAllHandler: function (isChecked) {
|
||||
this.checkedList = [];
|
||||
|
||||
var $actionsButton = this.$el.find('.actions-button');
|
||||
|
||||
if (isChecked) {
|
||||
this.$el.find('input.record-checkbox').prop('checked', true);
|
||||
$actionsButton.removeAttr('disabled');
|
||||
this.collection.models.forEach(function (model) {
|
||||
this.checkedList.push(model.id);
|
||||
}, this);
|
||||
this.$el.find('.list > table tbody tr').addClass('active');
|
||||
} else {
|
||||
if (this.allResultIsChecked) {
|
||||
this.unselectAllResult();
|
||||
}
|
||||
this.$el.find('input.record-checkbox').prop('checked', false);
|
||||
$actionsButton.attr('disabled', true);
|
||||
this.$el.find('.list > table tbody tr').removeClass('active');
|
||||
}
|
||||
|
||||
this.trigger('check');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} or {bool} ['both', 'top', 'bottom', false, true] Where to display paginations.
|
||||
*/
|
||||
@@ -304,7 +309,10 @@ Espo.define('views/record/list', 'view', function (Dep) {
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.$el.find('.actions-button').removeAttr('disabled');
|
||||
if (this.checkAllResultMassActionList.length) {
|
||||
this.$el.find('.actions-button').removeAttr('disabled');
|
||||
}
|
||||
|
||||
this.$el.find('.list > table tbody tr').removeClass('active');
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user