Merge branch 'fix'

This commit is contained in:
Yuri Kuznetsov
2025-11-14 15:26:30 +02:00
19 changed files with 100 additions and 36 deletions
@@ -1,5 +1,5 @@
[ [
{"name":"name","link":true, "width": "40"}, {"name":"name", "link":true, "width": 40},
{"name":"account"}, {"name":"account"},
{"name":"emailAddress", "notSortable": true} {"name":"emailAddress", "notSortable": true}
] ]
@@ -1,6 +1,6 @@
[ [
{"name":"action", "width": "18", "notSortable": true}, {"name":"action", "width": 18, "notSortable": true},
{"name":"targetType", "width": "20", "notSortable": true}, {"name":"targetType", "width": 20, "notSortable": true},
{"name":"target", "notSortable": true}, {"name":"target", "notSortable": true},
{"name":"createdAt", "width": "18", "align": "right", "notSortable": true} {"name":"createdAt", "width": 18, "align": "right", "notSortable": true}
] ]
@@ -1,7 +1,7 @@
[ [
{"name":"user"}, {"name":"user"},
{"name":"isActive", "widthPx": "100"}, {"name":"isActive", "widthPx": 100},
{"name":"ipAddress", "width": "17"}, {"name":"ipAddress", "width": 17},
{"name":"createdAt", "width": "19"}, {"name":"createdAt", "width": 19},
{"name":"lastAccess", "width": "19"} {"name":"lastAccess", "width": 19}
] ]
@@ -1,7 +1,7 @@
[ [
{"name":"user"}, {"name":"user"},
{"name":"isActive", "widthPx": "100"}, {"name":"isActive", "widthPx": 100},
{"name":"ipAddress", "width": "17"}, {"name":"ipAddress", "width": 17},
{"name":"createdAt", "width": "19"}, {"name":"createdAt", "width": 19},
{"name":"lastAccess", "width": "19"} {"name":"lastAccess", "width": 19}
] ]
@@ -1,5 +1,5 @@
[ [
{"name":"target", "notSortable": true}, {"name":"target", "notSortable": true},
{"name":"isCreated", "width": "25", "notSortable": true}, {"name":"isCreated", "width": 25, "notSortable": true},
{"name":"createdAt", "width": "30", "notSortable": true} {"name":"createdAt", "width": 30, "notSortable": true}
] ]
@@ -389,7 +389,7 @@ class UserRecordService
[ [
'entityType:' => 'parentType', 'entityType:' => 'parentType',
'entityId:' => 'parentId', 'entityId:' => 'parentId',
'subscription.userId' => $user->getId(), 'subscriptionExclude.userId' => $user->getId(),
] ]
) )
->where([ ->where([
+1 -1
View File
@@ -34,7 +34,7 @@
data-action="selectPreset" data-action="selectPreset"
> >
<div class="{{#if style}}text-{{style}}{{/if}}"> <div class="{{#if style}}text-{{style}}{{/if}}">
{{#if label}}{{label}}{{else}}{{translate name category='presetFilters' scope=../entityType}}{{/if}} {{~#if label}}{{label}}{{else}}{{translate name category='presetFilters' scope=../entityType}}{{/if~}}
</div> </div>
</a> </a>
</li> </li>
+1 -1
View File
@@ -174,7 +174,7 @@ class Collection {
parentModel parentModel
/** /**
* @param {Model[]|null} [models] Models. * @param {Model[]|Record<string, *>[]|null} [models] Models.
* @param {{ * @param {{
* entityType?: string, * entityType?: string,
* model?: Model.prototype, * model?: Model.prototype,
@@ -420,7 +420,38 @@ class EmailAddressVarcharFieldView extends BaseFieldView {
return true; return true;
} }
/**
*
* @param {string} address
* @param {string} name
* @param {string} [type]
* @param {string} [id]
*/
addAddress(address, name, type, id) { addAddress(address, name, type, id) {
if (name === '') {
const nameHash = this.model.attributes.nameHash ?? {};
if (address in nameHash) {
name = nameHash[address];
}
}
if (type === undefined) {
const typeHash = this.model.attributes.typeHash ?? {};
if (address in typeHash) {
type = typeHash[address];
}
}
if (id === undefined) {
const idHash = this.model.attributes.idHash ?? {};
if (address in idHash) {
id = idHash[address];
}
}
if (this.justAddedAddress) { if (this.justAddedAddress) {
this.deleteAddress(this.justAddedAddress); this.deleteAddress(this.justAddedAddress);
} }
@@ -482,9 +513,9 @@ class EmailAddressVarcharFieldView extends BaseFieldView {
$text.append( $text.append(
$('<span>').text(name), $('<span>').text(name),
' ', '<span class="no-select"> </span>',
$('<span>').addClass('text-muted middle-dot'), $('<span>').addClass('text-muted middle-dot'),
' ' '<span class="no-select"> </span>'
); );
} }
+4
View File
@@ -256,6 +256,10 @@ class ArrayFieldView extends BaseFieldView {
this.allowCustomOptions = true; this.allowCustomOptions = true;
} }
if (this.params.allowCustomOptions === false) {
this.allowCustomOptions = false;
}
if (this.type === 'array') { if (this.type === 'array') {
this.validations.push('noInputValue') this.validations.push('noInputValue')
} }
+28 -9
View File
@@ -217,6 +217,13 @@ class LinkFieldView extends BaseFieldView {
*/ */
linkClass linkClass
/**
* @protected
* @type {string}
* @since 9.2.5
*/
foreignNameAttribute
/** @inheritDoc */ /** @inheritDoc */
events = { events = {
/** @this LinkFieldView */ /** @this LinkFieldView */
@@ -364,6 +371,10 @@ class LinkFieldView extends BaseFieldView {
this.model.getFieldParam(this.name, 'entity') || this.model.getFieldParam(this.name, 'entity') ||
this.model.getLinkParam(this.name, 'entity'); this.model.getLinkParam(this.name, 'entity');
this.foreignNameAttribute = this.model.getLinkParam(this.name, 'foreignName') ??
this.getMetadata().get(`clientDefs.${this.foreignScope}.nameAttribute`) ??
'name';
if ('createDisabled' in this.options) { if ('createDisabled' in this.options) {
this.createDisabled = this.options.createDisabled; this.createDisabled = this.options.createDisabled;
} }
@@ -407,12 +418,12 @@ class LinkFieldView extends BaseFieldView {
* @return {Promise|void} * @return {Promise|void}
*/ */
select(model) { select(model) {
this.$elementName.val(model.get('name') || model.id); this.$elementName.val(model.get(this.foreignNameAttribute) || model.id);
this.$elementId.val(model.get('id')); this.$elementId.val(model.id);
if (this.mode === this.MODE_SEARCH) { if (this.mode === this.MODE_SEARCH) {
this.searchData.idValue = model.get('id'); this.searchData.idValue = model.id;
this.searchData.nameValue = model.get('name') || model.id; this.searchData.nameValue = model.get(this.foreignNameAttribute) || model.id;
} }
this.trigger('change'); this.trigger('change');
@@ -611,7 +622,7 @@ class LinkFieldView extends BaseFieldView {
if (!this.forceSelectAllAttributes) { if (!this.forceSelectAllAttributes) {
const mandatorySelectAttributeList = this.getMandatorySelectAttributeList(); const mandatorySelectAttributeList = this.getMandatorySelectAttributeList();
let select = ['id', 'name']; let select = ['id', this.foreignNameAttribute];
if (mandatorySelectAttributeList) { if (mandatorySelectAttributeList) {
select = select.concat(mandatorySelectAttributeList); select = select.concat(mandatorySelectAttributeList);
@@ -824,12 +835,20 @@ class LinkFieldView extends BaseFieldView {
const list = []; const list = [];
response.list.forEach(item => { response.list.forEach(item => {
const name = item[this.foreignNameAttribute] || item.name || item.id;
const attributes = item;
if (this.foreignNameAttribute !== 'name') {
attributes[this.foreignNameAttribute] = name;
}
list.push({ list.push({
id: item.id, id: item.id,
name: item.name || item.id, name: name,
data: item.id, data: item.id,
value: item.name || item.id, value: name,
attributes: item, attributes: attributes,
}); });
}); });
@@ -1365,7 +1384,7 @@ class LinkFieldView extends BaseFieldView {
*/ */
selectOneOf(models) { selectOneOf(models) {
models.forEach(model => { models.forEach(model => {
this.addLinkOneOf(model.id, model.get('name')); this.addLinkOneOf(model.id, model.get(this.foreignNameAttribute));
}); });
} }
} }
+5 -1
View File
@@ -148,7 +148,11 @@ class EditRecordView extends DetailRecordView {
this.wait(promise); this.wait(promise);
// @todo Revise. Possible race condition issues. // @todo Revise. Possible race condition issues.
promise.then(() => super.setupBeforeFinal()); promise.then(() => {
super.setupBeforeFinal();
this.processDynamicLogic();
});
} }
if (this.model.isNew()) { if (this.model.isNew()) {
+1 -1
View File
@@ -938,7 +938,7 @@ class SearchView extends View {
this.$el this.$el
.find('ul.filter-menu a.preset[data-name="'+presetName+'"]') .find('ul.filter-menu a.preset[data-name="'+presetName+'"]')
.prepend('<span class="fas fa-check pull-right"></span>'); .prepend('<span class="fas fa-check check-icon pull-right"></span>');
} }
manageBoolFilters() { manageBoolFilters() {
+1 -1
View File
@@ -112,7 +112,7 @@ img.image-preview {
.preset { .preset {
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden !important; //overflow: hidden !important;
} }
.preset > div{ .preset > div{
@@ -8,6 +8,8 @@
padding: var(--5px) 0; padding: var(--5px) 0;
user-select: none; user-select: none;
min-width: calc(var(--50px) + var(--10px));
.autocomplete-suggestion.autocomplete-selected { .autocomplete-suggestion.autocomplete-selected {
background-color: var(--dropdown-link-hover-bg) !important; background-color: var(--dropdown-link-hover-bg) !important;
color: var(--dropdown-link-hover-color) !important; color: var(--dropdown-link-hover-color) !important;
+4
View File
@@ -213,6 +213,10 @@ small,
top: var(--minus-1px); top: var(--minus-1px);
} }
.no-select {
user-select: none;
}
a[role="button"], a[role="button"],
a[href="javascript:"] { a[href="javascript:"] {
&:not(.selectable) { &:not(.selectable) {
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "espocrm", "name": "espocrm",
"version": "9.2.4", "version": "9.2.5",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "espocrm", "name": "espocrm",
"version": "9.2.4", "version": "9.2.5",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "espocrm", "name": "espocrm",
"version": "9.2.4", "version": "9.2.5",
"description": "Open-source CRM.", "description": "Open-source CRM.",
"repository": { "repository": {
"type": "git", "type": "git",
+1 -1
View File
@@ -19,7 +19,7 @@
}, },
"accessCheckCommand": { "accessCheckCommand": {
"type": "string", "type": "string",
"description": "A console command that will be used to check whether a topic is allowed for a user when they attempting to subscribe. Patameter placeholders starts with the colon :. The userId parameter contains a user ID. If access checking is not needed, omit this parameter." "description": "A console command that will be used to check whether a topic is allowed for a user when they attempting to subscribe. Parameter placeholders starts with the colon :. The userId parameter contains a user ID. If access checking is not needed, omit this parameter."
} }
} }
} }