diff --git a/client/src/search-manager.js b/client/src/search-manager.js
index 08037d8528..d22c9b61f2 100644
--- a/client/src/search-manager.js
+++ b/client/src/search-manager.js
@@ -122,7 +122,7 @@ Espo.define('search-manager', [], function () {
},
getWherePart: function (name, defs) {
- var field = name;
+ var attribute = name;
if ('where' in defs) {
return defs.where;
@@ -140,13 +140,16 @@ Espo.define('search-manager', [], function () {
value: a
};
}
- if ('field' in defs) {
- field = defs.field;
+ if ('field' in defs) { // for backward compatibility
+ attribute = defs.field;
+ }
+ if ('attribute' in defs) {
+ attribute = defs.attribute;
}
if (defs.dateTime) {
return {
type: type,
- field: field,
+ attribute: attribute,
value: defs.value,
dateTime: true,
timeZone: this.dateTime.timeZone || 'UTC'
@@ -155,8 +158,8 @@ Espo.define('search-manager', [], function () {
value = defs.value;
return {
type: type,
- field: field,
- value: value,
+ attribute: attribute,
+ value: value
};
}
}
diff --git a/client/src/view-helper.js b/client/src/view-helper.js
index de138f491d..6d8441f6c3 100644
--- a/client/src/view-helper.js
+++ b/client/src/view-helper.js
@@ -29,11 +29,11 @@
Espo.define('view-helper', [], function () {
var ViewHelper = function (options) {
- this.urlRegex = /(^|[^\[])(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
+ this.urlRegex = /(^|[^\(])(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
this._registerHandlebarsHelpers();
this.mdSearch = [
- /\("?(.*?)"?\)\[(.*?)\]/g,
+ /\["?(.*?)"?\]\((.*?)\)/g,
/\&\#x60;(([\s\S]*?)\&\#x60;)/g,
/(\*\*)(.*?)\1/g,
/(\*)(.*?)\1/g,
@@ -199,7 +199,7 @@ Espo.define('view-helper', [], function () {
Handlebars.registerHelper('complexText', function (text) {
text = Handlebars.Utils.escapeExpression(text || '');
- text = text.replace(self.urlRegex, '$1($2)[$2]');
+ text = text.replace(self.urlRegex, '$1[$2]($2)');
self.mdSearch.forEach(function (re, i) {
text = text.replace(re, self.mdReplace[i]);
diff --git a/client/src/views/email-folder/list-side.js b/client/src/views/email-folder/list-side.js
index ca4ca6b3ba..84f34f9869 100644
--- a/client/src/views/email-folder/list-side.js
+++ b/client/src/views/email-folder/list-side.js
@@ -88,7 +88,7 @@ Espo.define('views/email-folder/list-side', 'view', function (Dep) {
this.manageModelRemoving(model);
}, this);
- this.listenTo(this.emailCollection, 'retrieving-to-trash', function (id) {
+ this.listenTo(this.emailCollection, 'retrieving-from-trash', function (id) {
var model = this.emailCollection.get(id);
if (!model) return;
if (this.countsIsBeingLoaded) return;
diff --git a/client/src/views/email/detail.js b/client/src/views/email/detail.js
index b767b7943b..acb74e9746 100644
--- a/client/src/views/email/detail.js
+++ b/client/src/views/email/detail.js
@@ -189,7 +189,7 @@ Espo.define('views/email/detail', ['views/detail', 'email-helper'], function (De
attributes.parentName = this.model.get('parentName');
attributes.parentType = this.model.get('parentType');
- attributes.description = '(' + this.model.get('name') + ')[#Email/view/' + this.model.id + ']';
+ attributes.description = '[' + this.model.get('name') + '](#Email/view/' + this.model.id + ')';
attributes.name = this.translate('Email', 'scopeNames') + ': ' + this.model.get('name');
diff --git a/client/src/views/email/record/list.js b/client/src/views/email/record/list.js
index b91611af9a..134bff7b59 100644
--- a/client/src/views/email/record/list.js
+++ b/client/src/views/email/record/list.js
@@ -52,6 +52,7 @@ Espo.define('views/email/record/list', 'views/record/list', function (Dep) {
this.massActionList.push('markAsImportant');
this.massActionList.push('markAsNotImportant');
this.massActionList.push('moveToFolder');
+ this.massActionList.push('retrieveFromTrash');
},
massActionMarkAsRead: function () {
@@ -141,13 +142,16 @@ Espo.define('views/email/record/list', 'views/record/list', function (Dep) {
for (var i in this.checkedList) {
ids.push(this.checkedList[i]);
}
- $.ajax({
- url: 'Email/action/moveToTrash',
- type: 'POST',
- data: JSON.stringify({
- ids: ids
- })
- });
+
+ this.ajaxPostRequest('Email/action/moveToTrash', {
+ ids: ids
+ }).then(function () {
+ Espo.Ui.success(this.translate('Done'));
+ }.bind(this));
+
+ if (this.collection.data.folderId === 'trash') {
+ return;
+ }
ids.forEach(function (id) {
this.collection.trigger('moving-to-trash', id);
@@ -155,6 +159,28 @@ Espo.define('views/email/record/list', 'views/record/list', function (Dep) {
}, this);
},
+ massActionRetrieveFromTrash: function () {
+ var ids = [];
+ for (var i in this.checkedList) {
+ ids.push(this.checkedList[i]);
+ }
+
+ this.ajaxPostRequest('Email/action/retrieveFromTrash', {
+ ids: ids
+ }).then(function () {
+ Espo.Ui.success(this.translate('Done'));
+ }.bind(this));
+
+ if (this.collection.data.folderId !== 'trash') {
+ return;
+ }
+
+ ids.forEach(function (id) {
+ this.collection.trigger('retrieving-from-trash', id);
+ this.removeRecordFromList(id);
+ }, this);
+ },
+
massActionMoveToFolder: function () {
var ids = [];
for (var i in this.checkedList) {
@@ -242,7 +268,7 @@ Espo.define('views/email/record/list', 'views/record/list', function (Dep) {
id: id
}).then(function () {
Espo.Ui.warning(this.translate('Retrieved from Trash', 'labels', 'Email'));
- this.collection.trigger('retrieving-to-trash', id);
+ this.collection.trigger('retrieving-from-trash', id);
this.removeRecordFromList(id);
}.bind(this));
},
diff --git a/client/src/views/fields/base.js b/client/src/views/fields/base.js
index c8ebf447f3..0571b29c5a 100644
--- a/client/src/views/fields/base.js
+++ b/client/src/views/fields/base.js
@@ -157,6 +157,9 @@ Espo.define('views/fields/base', 'view', function (Dep) {
if (this.mode === 'search') {
data.searchParams = this.searchParams;
data.searchData = this.searchData;
+ data.searchValues = this.getSearchValues();
+ data.searchType = this.getSearchType();
+ data.searchTypeList = this.getSearchTypeList();
}
return data;
},
@@ -298,6 +301,22 @@ Espo.define('views/fields/base', 'view', function (Dep) {
}
},
+ getSearchParamsData: function () {
+ return this.searchParams.data || {};
+ },
+
+ getSearchValues: function () {
+ return this.getSearchParamsData().values || {};
+ },
+
+ getSearchType: function () {
+ return this.getSearchParamsData().type || this.searchParams.type;
+ },
+
+ getSearchTypeList: function () {
+ return this.searchTypeList;
+ },
+
initInlineEdit: function () {
var $cell = this.getCellElement();
var $editLink = $('
');
diff --git a/client/src/views/fields/currency-converted.js b/client/src/views/fields/currency-converted.js
index ace2893abf..7fb73545b1 100644
--- a/client/src/views/fields/currency-converted.js
+++ b/client/src/views/fields/currency-converted.js
@@ -26,13 +26,13 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
-Espo.define('Views.Fields.CurrencyConverted', 'Views.Fields.Float', function (Dep) {
+Espo.define('views/fields/currency-converted', 'views/fields/float', function (Dep) {
return Dep.extend({
- detailTemplate: 'fields.currency.detail',
+ detailTemplate: 'fields/currency/detail',
- listTemplate: 'fields.currency.detail',
+ listTemplate: 'fields/currency/detail',
data: function () {
return _.extend({
diff --git a/client/src/views/fields/date.js b/client/src/views/fields/date.js
index 7008a05ecf..1dd02ed79e 100644
--- a/client/src/views/fields/date.js
+++ b/client/src/views/fields/date.js
@@ -38,22 +38,22 @@ Espo.define('views/fields/date', 'views/fields/base', function (Dep) {
validations: ['required', 'date', 'after', 'before'],
- searchTypeOptions: ['lastSevenDays', 'ever', 'currentMonth', 'lastMonth', 'currentQuarter', 'lastQuarter', 'currentYear', 'lastYear', 'today', 'past', 'future', 'lastXDays', 'nextXDays', 'on', 'after', 'before', 'between'],
+ searchTypeList: ['lastSevenDays', 'ever', 'isEmpty', 'currentMonth', 'lastMonth', 'currentQuarter', 'lastQuarter', 'currentYear', 'lastYear', 'today', 'past', 'future', 'lastXDays', 'nextXDays', 'on', 'after', 'before', 'between'],
setup: function () {
Dep.prototype.setup.call(this);
},
data: function () {
+ var data = Dep.prototype.data.call(this);
if (this.mode === 'search') {
this.searchData.dateValue = this.getDateTime().toDisplayDate(this.searchParams.dateValue);
this.searchData.dateValueTo = this.getDateTime().toDisplayDate(this.searchParams.dateValueTo);
}
- return Dep.prototype.data.call(this);
+ return data;
},
setupSearch: function () {
- this.searchData.typeOptions = this.searchTypeOptions;
this.events = _.extend({
'change select.search-type': function (e) {
var type = $(e.currentTarget).val();
@@ -246,6 +246,13 @@ Espo.define('views/fields/date', 'views/fields/base', function (Dep) {
value: value,
dateValue: value
};
+ } else if (type === 'isEmpty') {
+ data = {
+ type: 'isNull',
+ data: {
+ type: type
+ }
+ }
} else {
data = {
type: type
@@ -254,6 +261,10 @@ Espo.define('views/fields/date', 'views/fields/base', function (Dep) {
return data;
},
+ getSearchType: function () {
+ return this.getSearchParamsData().type || this.searchParams.typeFront || this.searchParams.type;
+ },
+
validateRequired: function () {
if (this.isRequired()) {
if (this.model.get(this.name) === null) {
diff --git a/client/src/views/fields/datetime.js b/client/src/views/fields/datetime.js
index 8280a8cfef..60aa771f8b 100644
--- a/client/src/views/fields/datetime.js
+++ b/client/src/views/fields/datetime.js
@@ -36,7 +36,7 @@ Espo.define('views/fields/datetime', 'views/fields/date', function (Dep) {
validations: ['required', 'datetime', 'after', 'before'],
- searchTypeOptions: ['lastSevenDays', 'ever', 'currentMonth', 'lastMonth', 'currentQuarter', 'lastQuarter', 'currentYear', 'lastYear', 'today', 'past', 'future', 'lastXDays', 'nextXDays', 'on', 'after', 'before', 'between'],
+ searchTypeList: ['lastSevenDays', 'ever', 'isEmpty', 'currentMonth', 'lastMonth', 'currentQuarter', 'lastQuarter', 'currentYear', 'lastYear', 'today', 'past', 'future', 'lastXDays', 'nextXDays', 'on', 'after', 'before', 'between'],
timeFormatMap: {
'HH:mm': 'H:i',
diff --git a/client/src/views/fields/int.js b/client/src/views/fields/int.js
index 776704b170..f4703c8e5f 100644
--- a/client/src/views/fields/int.js
+++ b/client/src/views/fields/int.js
@@ -42,6 +42,8 @@ Espo.define('views/fields/int', 'views/fields/base', function (Dep) {
thousandSeparator: ',',
+ searchTypeList: ['isNotEmpty', 'isEmpty', 'equals', 'notEquals', 'greaterThan', 'lessThan', 'greaterThanOrEquals', 'lessThanOrEquals', 'between'],
+
setup: function () {
Dep.prototype.setup.call(this);
this.defineMaxLength();
@@ -59,6 +61,15 @@ Espo.define('views/fields/int', 'views/fields/base', function (Dep) {
}
},
+ afterRender: function () {
+ Dep.prototype.afterRender.call(this);
+
+ if (this.mode == 'search') {
+ var $searchType = this.$el.find('select.search-type');
+ this.handleSearchType($searchType.val());
+ }
+ },
+
data: function () {
var data = Dep.prototype.data.call(this);
@@ -86,19 +97,29 @@ Espo.define('views/fields/int', 'views/fields/base', function (Dep) {
},
setupSearch: function () {
- this.searchData.typeOptions = ['equals', 'notEquals', 'greaterThan', 'lessThan', 'greaterThanOrEquals', 'lessThanOrEquals', 'between'];
this.events = _.extend({
'change select.search-type': function (e) {
- var additional = this.$el.find('input.additional');
- if ($(e.currentTarget).val() == 'between') {
- additional.removeClass('hide');
- } else {
- additional.addClass('hide');
- }
+ this.handleSearchType($(e.currentTarget).val());
},
}, this.events || {});
},
+ handleSearchType: function (type) {
+ var $additionalInput = this.$el.find('input.additional');
+ var $input = this.$el.find('input[name="'+this.name+'"]');
+
+ if (type === 'between') {
+ $additionalInput.removeClass('hidden');
+ $input.removeClass('hidden');
+ } else if (~['isEmpty', 'isNotEmpty'].indexOf(type)) {
+ $additionalInput.addClass('hidden');
+ $input.addClass('hidden');
+ } else {
+ $additionalInput.addClass('hidden');
+ $input.removeClass('hidden');
+ }
+ },
+
defineMaxLength: function () {
var maxValue = this.model.getFieldParam(this.name, 'max');
if (maxValue) {
@@ -193,13 +214,7 @@ Espo.define('views/fields/int', 'views/fields/base', function (Dep) {
return false;
}
- if (type != 'between') {
- data = {
- type: type,
- value: value,
- value1: value
- };
- } else {
+ if (type === 'between') {
var valueTo = this.parse(this.$el.find('[name="' + this.name + '-additional"]').val());
if (isNaN(valueTo)) {
return false;
@@ -210,10 +225,30 @@ Espo.define('views/fields/int', 'views/fields/base', function (Dep) {
value1: value,
value2: valueTo
};
+ } else if (type == 'isEmpty') {
+ data = {
+ type: 'isNull',
+ typeFront: 'isEmpty'
+ };
+ } else if (type == 'isNotEmpty') {
+ data = {
+ type: 'isNotNull',
+ typeFront: 'isNotEmpty'
+ };
+ } else {
+ data = {
+ type: type,
+ value: value,
+ value1: value
+ };
}
return data;
},
+ getSearchType: function () {
+ return this.searchParams.typeFront || this.searchParams.type;
+ }
+
});
});
diff --git a/client/src/views/fields/link-parent.js b/client/src/views/fields/link-parent.js
index 61c28b890d..2abb72cde3 100644
--- a/client/src/views/fields/link-parent.js
+++ b/client/src/views/fields/link-parent.js
@@ -54,6 +54,8 @@ Espo.define('views/fields/link-parent', 'views/fields/base', function (Dep) {
createDisabled: false,
+ searchTypeList: ['is', 'isEmpty', 'isNotEmpty'],
+
data: function () {
return _.extend({
idName: this.idName,
@@ -142,7 +144,6 @@ Espo.define('views/fields/link-parent', 'views/fields/base', function (Dep) {
},
setupSearch: function () {
- this.searchData.typeOptions = ['is', 'isEmpty', 'isNotEmpty'];
this.events = _.extend({
'change select.search-type': function (e) {
@@ -301,15 +302,19 @@ Espo.define('views/fields/link-parent', 'views/fields/base', function (Dep) {
if (type == 'isEmpty') {
var data = {
type: 'isNull',
- typeFront: type,
- field: this.idName
+ field: this.idName,
+ data: {
+ type: type
+ }
};
return data;
} else if (type == 'isNotEmpty') {
var data = {
type: 'isNotNull',
- typeFront: type,
- field: this.idName
+ field: this.idName,
+ data: {
+ type: type
+ }
};
return data;
}
@@ -326,7 +331,6 @@ Espo.define('views/fields/link-parent', 'views/fields/base', function (Dep) {
var data;
if (entityId) {
data = {
- frontType: 'is',
type: 'and',
field: this.idName,
@@ -345,10 +349,12 @@ Espo.define('views/fields/link-parent', 'views/fields/base', function (Dep) {
valueId: entityId,
valueName: entityName,
valueType: entityType,
+ data: {
+ type: 'is'
+ }
};
} else {
data = {
- frontType: 'is',
type: 'and',
field: this.idName,
value: [
@@ -362,10 +368,17 @@ Espo.define('views/fields/link-parent', 'views/fields/base', function (Dep) {
value: entityType,
}
],
- valueType: entityType
+ valueType: entityType,
+ data: {
+ type: 'is'
+ }
};
}
return data;
+ },
+
+ getSearchType: function () {
+ return this.getSearchParamsData().type || this.searchParams.typeFront;
}
});
});
diff --git a/client/src/views/fields/link.js b/client/src/views/fields/link.js
index cfe5507641..cdb1392ee6 100644
--- a/client/src/views/fields/link.js
+++ b/client/src/views/fields/link.js
@@ -54,6 +54,8 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) {
createDisabled: false,
+ searchTypeList: ['is', 'isEmpty', 'isNotEmpty', 'isOneOf'],
+
data: function () {
return _.extend({
idName: this.idName,
@@ -161,7 +163,6 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) {
},
setupSearch: function () {
- this.searchData.typeOptions = ['is', 'isEmpty', 'isNotEmpty', 'isOneOf'];
this.searchData.oneOfIdList = this.searchParams.oneOfIdList || [];
this.searchData.oneOfNameHash = this.searchParams.oneOfNameHash || {};
@@ -395,25 +396,31 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) {
if (type == 'isEmpty') {
var data = {
type: 'isNull',
- typeFront: type,
- field: this.idName
+ field: this.idName,
+ data: {
+ type: type
+ }
};
return data;
} else if (type == 'isNotEmpty') {
var data = {
type: 'isNotNull',
- typeFront: type,
- field: this.idName
+ field: this.idName,
+ data: {
+ type: type
+ }
};
return data;
} else if (type == 'isOneOf') {
var data = {
type: 'in',
- typeFront: type,
field: this.idName,
value: this.searchData.oneOfIdList,
oneOfIdList: this.searchData.oneOfIdList,
- oneOfNameHash: this.searchData.oneOfNameHash
+ oneOfNameHash: this.searchData.oneOfNameHash,
+ data: {
+ type: type
+ }
};
return data;
@@ -423,14 +430,21 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) {
}
var data = {
type: 'equals',
- typeFront: type,
field: this.idName,
value: value,
valueName: this.$el.find('[name="' + this.nameName + '"]').val(),
+ data: {
+ type: type
+ }
};
return data;
}
+ },
+
+ getSearchType: function () {
+ return this.getSearchParamsData().type || this.searchParams.typeFront || this.searchParams.frontType;
}
+
});
});
diff --git a/client/src/views/fields/user.js b/client/src/views/fields/user.js
index 1c86c2ef86..fe84e397e5 100644
--- a/client/src/views/fields/user.js
+++ b/client/src/views/fields/user.js
@@ -35,7 +35,9 @@ Espo.define('views/fields/user', 'views/fields/link', function (Dep) {
setupSearch: function () {
Dep.prototype.setupSearch.call(this);
- this.searchData.typeOptions.push('isFromTeams');
+ this.searchTypeList = Espo.Utils.clone(this.searchTypeList);
+ this.searchTypeList.push('isFromTeams');
+
this.searchData.teamIdList = this.searchParams.teamIdList || [];
this.searchData.teamNameHash = this.searchParams.teamNameHash || {};
@@ -177,11 +179,13 @@ Espo.define('views/fields/user', 'views/fields/link', function (Dep) {
if (type == 'isFromTeams') {
var data = {
type: 'isUserFromTeams',
- typeFront: type,
field: this.name,
value: this.searchData.teamIdList,
teamIdList: this.searchData.teamIdList,
- teamNameHash: this.searchData.teamNameHash
+ teamNameHash: this.searchData.teamNameHash,
+ data: {
+ type: type
+ }
};
return data;
}
diff --git a/client/src/views/user/list.js b/client/src/views/user/list.js
index f7623c2b7c..9cf14a4a48 100644
--- a/client/src/views/user/list.js
+++ b/client/src/views/user/list.js
@@ -57,7 +57,7 @@ Espo.define('views/user/list', 'views/list', function (Dep) {
attributes.contactId = model.id;
attributes.contactName = model.get('name');
- if (model.has('accountId')) {
+ if (model.get('accountId')) {
var names = {};
names[model.get('accountId')] = model.get('accountName');