diff --git a/application/Espo/Resources/i18n/en_US/LayoutManager.json b/application/Espo/Resources/i18n/en_US/LayoutManager.json
index 54da5fcab7..cd481d4e47 100644
--- a/application/Espo/Resources/i18n/en_US/LayoutManager.json
+++ b/application/Espo/Resources/i18n/en_US/LayoutManager.json
@@ -5,7 +5,8 @@
"notSortable": "Not Sortable",
"align": "Align",
"panelName": "Panel Name",
- "style": "Style"
+ "style": "Style",
+ "sticked": "Sticked"
},
"options": {
"align": {
diff --git a/client/res/templates/admin/layouts/rows.tpl b/client/res/templates/admin/layouts/rows.tpl
index 2c442a8235..ba81f8c032 100644
--- a/client/res/templates/admin/layouts/rows.tpl
+++ b/client/res/templates/admin/layouts/rows.tpl
@@ -12,7 +12,7 @@
list-style-type: none;
margin: 0;
}
-
+
#layout ul li {
list-style: none;
border: 1px solid #CCC;
@@ -20,45 +20,43 @@
padding: 5px;
height: 32px;
}
-
+
#layout header {
font-weight: bold;
}
-
+
#layout ul > li .left {
float: left;
}
-
+
#layout ul > li {
background-color: #FFF;
}
-
+
#layout ul.enabled > li .right {
float: right;
}
-
+
#layout ul.disabled > li .right {
display: none;
}
-
-
#layout ul > li .width {
font-size: small;
}
-
+
#layout ul.disabled > li .width {
display: none;
}
-
+
#layout label {
font-weight: normal;
}
-
+
.enabled li a.edit-field {
display: none;
}
-
+
.enabled li:hover a.edit-field {
display: block;
}
@@ -70,7 +68,7 @@
{{translate 'Enabled' scope='Admin'}}
{{#each layout}}
- -
+
-
diff --git a/client/src/views/admin/layouts/base.js b/client/src/views/admin/layouts/base.js
index d11341d9a3..8d0a3e1016 100644
--- a/client/src/views/admin/layouts/base.js
+++ b/client/src/views/admin/layouts/base.js
@@ -108,7 +108,7 @@ Espo.define('views/admin/layouts/base', 'view', function (Dep) {
fetch: function () {},
setup: function () {
- this.dataAttributes = _.clone(this.dataAttributes);
+ this.dataAttributeList = _.clone(this.dataAttributeList);
this.buttonList = _.clone(this.buttonList);
this.events = _.clone(this.events);
this.scope = this.options.scope;
@@ -136,12 +136,13 @@ Espo.define('views/admin/layouts/base', 'view', function (Dep) {
openEditDialog: function (attributes) {
var name = attributes.name;
- this.createView('editModal', 'Admin.Layouts.Modals.EditAttributes', {
+ this.createView('editModal', 'views/admin/layouts/modals/edit-attributes', {
name: attributes.name,
scope: this.scope,
- attributeList: this.dataAttributes,
+ attributeList: this.dataAttributeList,
attributeDefs: this.dataAttributesDefs,
- attributes: attributes
+ attributes: attributes,
+ languageCategory: this.languageCategory
}, function (view) {
view.render();
this.listenToOnce(view, 'after:save', function (attributes) {
diff --git a/client/src/views/admin/layouts/detail.js b/client/src/views/admin/layouts/detail.js
index ae0541510f..732408d36b 100644
--- a/client/src/views/admin/layouts/detail.js
+++ b/client/src/views/admin/layouts/detail.js
@@ -30,7 +30,7 @@ Espo.define('views/admin/layouts/detail', 'views/admin/layouts/grid', function (
return Dep.extend({
- dataAttributes: ['name', 'fullWidth'],
+ dataAttributeList: ['name', 'fullWidth'],
dataAttributesDefs: {
fullWidth: {
diff --git a/client/src/views/admin/layouts/filters.js b/client/src/views/admin/layouts/filters.js
index c55c8acf74..3fc0e035a9 100644
--- a/client/src/views/admin/layouts/filters.js
+++ b/client/src/views/admin/layouts/filters.js
@@ -30,7 +30,7 @@ Espo.define('views/admin/layouts/filters', 'views/admin/layouts/rows', function
return Dep.extend({
- dataAttributes: ['name'],
+ dataAttributeList: ['name'],
editable: false,
diff --git a/client/src/views/admin/layouts/grid.js b/client/src/views/admin/layouts/grid.js
index 9029aa4008..152138855f 100644
--- a/client/src/views/admin/layouts/grid.js
+++ b/client/src/views/admin/layouts/grid.js
@@ -32,7 +32,7 @@ Espo.define('views/admin/layouts/grid', 'views/admin/layouts/base', function (De
template: 'admin/layouts/grid',
- dataAttributes: null,
+ dataAttributeList: null,
panels: null,
@@ -282,7 +282,7 @@ Espo.define('views/admin/layouts/grid', 'views/admin/layouts/base', function (De
var cell = false;
if (!$(li).hasClass('empty')) {
cell = {};
- self.dataAttributes.forEach(function (attr) {
+ self.dataAttributeList.forEach(function (attr) {
var value = $(li).data(Espo.Utils.toDom(attr)) || null;
if (value) {
cell[attr] = value;
diff --git a/client/src/views/admin/layouts/list.js b/client/src/views/admin/layouts/list.js
index 2458042f5c..d78a1586a8 100644
--- a/client/src/views/admin/layouts/list.js
+++ b/client/src/views/admin/layouts/list.js
@@ -30,7 +30,7 @@ Espo.define('views/admin/layouts/list', 'views/admin/layouts/rows', function (De
return Dep.extend({
- dataAttributes: ['name', 'width', 'link', 'notSortable', 'align'],
+ dataAttributeList: ['name', 'width', 'link', 'notSortable', 'align'],
dataAttributesDefs: {
link: {type: 'bool'},
@@ -44,6 +44,8 @@ Espo.define('views/admin/layouts/list', 'views/admin/layouts/rows', function (De
editable: true,
+ languageCategory: 'fields',
+
ignoreList: [],
ignoreTypeList: [],
@@ -110,17 +112,6 @@ Espo.define('views/admin/layouts/list', 'views/admin/layouts/rows', function (De
}
},
- parseDataAttributes: function (dialog) {
- var width = parseFloat(dialog.$el.find("[name='width']").val());
- if (isNaN(width) || width > 100 || width < 0) {
- width = '';
- }
- return {
- width: width,
- link: dialog.$el.find("[name='link']").val()
- };
- },
-
checkFieldType: function (type) {
if (['linkMultiple'].indexOf(type) != -1) {
return false;
diff --git a/client/src/views/admin/layouts/mass-update.js b/client/src/views/admin/layouts/mass-update.js
index ea4fd72b4b..68288127f5 100644
--- a/client/src/views/admin/layouts/mass-update.js
+++ b/client/src/views/admin/layouts/mass-update.js
@@ -30,7 +30,7 @@ Espo.define('views/admin/layouts/mass-update', 'views/admin/layouts/rows', funct
return Dep.extend({
- dataAttributes: ['name'],
+ dataAttributeList: ['name'],
editable: false,
diff --git a/client/src/views/admin/layouts/modals/edit-attributes.js b/client/src/views/admin/layouts/modals/edit-attributes.js
index a5d9d48dac..3f9c512abb 100644
--- a/client/src/views/admin/layouts/modals/edit-attributes.js
+++ b/client/src/views/admin/layouts/modals/edit-attributes.js
@@ -50,7 +50,11 @@ Espo.define('views/admin/layouts/modals/edit-attributes', ['views/modal', 'model
model.name = 'LayoutManager';
model.set(this.options.attributes || {});
- this.header = this.translate(this.options.name, 'fields', this.options.scope);
+ if (this.options.languageCategory) {
+ this.header = this.translate(this.options.name, this.options.languageCategory || 'fields', this.options.scope);
+ } else {
+ this.header = false;
+ }
var attributeList = Espo.Utils.clone(this.options.attributeList || []);
var index = attributeList.indexOf('name');
diff --git a/client/src/views/admin/layouts/relationships.js b/client/src/views/admin/layouts/relationships.js
index 94e8159808..e60d0e2d45 100644
--- a/client/src/views/admin/layouts/relationships.js
+++ b/client/src/views/admin/layouts/relationships.js
@@ -30,9 +30,19 @@ Espo.define('views/admin/layouts/relationships', 'views/admin/layouts/rows', fun
return Dep.extend({
- dataAttributes: ['name'],
+ dataAttributeList: ['name', 'style'],
- editable: false,
+ editable: true,
+
+ dataAttributesDefs: {
+ style: {
+ type: 'enum',
+ options: ['default', 'success', 'danger', 'primary', 'info', 'warning'],
+ translation: 'LayoutManager.options.style'
+ }
+ },
+
+ languageCategory: 'links',
setup: function () {
Dep.prototype.setup.call(this);
@@ -64,11 +74,28 @@ Espo.define('views/admin/layouts/relationships', 'views/admin/layouts/rows', fun
this.enabledFields = [];
this.disabledFields = [];
for (var i in layout) {
- this.enabledFields.push({
- name: layout[i],
- label: this.getLanguage().translate(layout[i], 'links', this.scope)
- });
- this.enabledFieldsList.push(layout[i]);
+ var item = layout[i];
+ var o;
+ if (typeof item == 'string' || item instanceof String) {
+ o = {
+ name: item,
+ label: this.getLanguage().translate(item, 'links', this.scope)
+ };
+ } else {
+ o = item;
+ o.label = this.getLanguage().translate(o.name, 'links', this.scope);
+ }
+ this.dataAttributeList.forEach(function (attribute) {
+ if (attribute === 'name') return;
+ if (attribute in o) return;
+
+ var value = this.getMetadata().get(['clientDefs', this.scope, 'relationshipPanels', o.name, attribute]);
+ if (value === null) return;
+ o[attribute] = value;
+ }, this);
+
+ this.enabledFields.push(o);
+ this.enabledFieldsList.push(o.name);
}
for (var i in allFields) {
@@ -90,14 +117,6 @@ Espo.define('views/admin/layouts/relationships', 'views/admin/layouts/rows', fun
}.bind(this));
},
- fetch: function () {
- var layout = [];
- $("#layout ul.enabled > li").each(function (i, el) {
- layout.push($(el).data('name'));
- }.bind(this));
- return layout;
- },
-
validate: function () {
return true;
},
diff --git a/client/src/views/admin/layouts/rows.js b/client/src/views/admin/layouts/rows.js
index 0a848990d9..a2a38b038e 100644
--- a/client/src/views/admin/layouts/rows.js
+++ b/client/src/views/admin/layouts/rows.js
@@ -32,18 +32,17 @@ Espo.define('views/admin/layouts/rows', 'views/admin/layouts/base', function (De
template: 'admin/layouts/rows',
-
events: _.extend({
'click #layout a[data-action="editField"]': function (e) {
var data = {};
- this.dataAttributes.forEach(function (attr) {
+ this.dataAttributeList.forEach(function (attr) {
data[attr] = $(e.target).closest('li').data(Espo.Utils.toDom(attr))
});
this.openEditDialog(data);
},
}, Dep.prototype.events),
- dataAttributes: null,
+ dataAttributeList: null,
dataAttributesDefs: {},
@@ -57,7 +56,7 @@ Espo.define('views/admin/layouts/rows', 'views/admin/layouts/base', function (De
enabledFields: this.enabledFields,
disabledFields: this.disabledFields,
layout: this.rowLayout,
- dataAttributes: this.dataAttributes,
+ dataAttributeList: this.dataAttributeList,
dataAttributesDefs: this.dataAttributesDefs,
editable: this.editable,
};
@@ -73,7 +72,7 @@ Espo.define('views/admin/layouts/rows', 'views/admin/layouts/base', function (De
var layout = [];
$("#layout ul.enabled > li").each(function (i, el) {
var o = {};
- this.dataAttributes.forEach(function (attr) {
+ this.dataAttributeList.forEach(function (attr) {
var value = $(el).data(Espo.Utils.toDom(attr)) || null;
if (value) {
o[attr] = value;
diff --git a/client/src/views/admin/layouts/side-panels-detail.js b/client/src/views/admin/layouts/side-panels-detail.js
index 527ba0c38f..0e650b48d4 100644
--- a/client/src/views/admin/layouts/side-panels-detail.js
+++ b/client/src/views/admin/layouts/side-panels-detail.js
@@ -30,12 +30,17 @@ Espo.define('views/admin/layouts/side-panels-detail', 'views/admin/layouts/rows'
return Dep.extend({
- dataAttributes: ['name'],
+ dataAttributeList: ['name', 'style'],
dataAttributesDefs: {
+ style: {
+ type: 'enum',
+ options: ['default', 'success', 'danger', 'primary', 'info', 'warning'],
+ translation: 'LayoutManager.options.style'
+ }
},
- editable: false,
+ editable: true,
ignoreList: [],
@@ -64,12 +69,14 @@ Espo.define('views/admin/layouts/side-panels-detail', 'views/admin/layouts/rows'
readDataFromLayout: function (layout) {
var panelListAll = [];
var labels = {};
+ var params = {};
(this.getMetadata().get(['clientDefs', this.scope, 'sidePanels', this.viewType]) || []).forEach(function (item) {
if (!item.name) return;
panelListAll.push(item.name);
if (item.label) {
labels[item.name] = item.label;
}
+ params[item.name] = item;
}, this);
this.disabledFields = [];
@@ -80,7 +87,8 @@ Espo.define('views/admin/layouts/side-panels-detail', 'views/admin/layouts/rows'
panelListAll.forEach(function (item) {
var disabled = false;
- if ((layout[item] || {}).disabled) {
+ var itemData = layout[item] || {};
+ if (itemData.disabled) {
disabled = true;
}
var labelText;
@@ -96,25 +104,52 @@ Espo.define('views/admin/layouts/side-panels-detail', 'views/admin/layouts/rows'
label: labelText
});
} else {
- this.rowLayout.push({
+ var o = {
name: item,
label: labelText
- });
+ };
+ if (o.name in params) {
+ this.dataAttributeList.forEach(function (attribute) {
+ if (attribute === 'name') return;
+ var itemParams = params[o.name] || {};
+ if (attribute in itemParams) {
+ o[attribute] = itemParams[attribute];
+ }
+ }, this);
+ }
+ for (var i in itemData) {
+ o[i] = itemData[i];
+ }
+ this.rowLayout.push(o);
}
}, this);
-
},
fetch: function () {
var layout = {};
$("#layout ul.disabled > li").each(function (i, el) {
- var o = {};
var name = $(el).attr('data-name');
layout[name] = {
disabled: true
};
}.bind(this));
+
+ $("#layout ul.enabled > li").each(function (i, el) {
+ var $el = $(el);
+ var o = {};
+ var name = $el.attr('data-name');
+
+ this.dataAttributeList.forEach(function (attribute) {
+ if (attribute === 'name') return;
+
+ var value = $el.data(Espo.Utils.toDom(attribute)) || null;
+ o[attribute] = value;
+ });
+
+ layout[name] = o;
+ }.bind(this))
+
return layout;
},
diff --git a/client/src/views/record/detail-bottom.js b/client/src/views/record/detail-bottom.js
index dbac205019..793037a62b 100644
--- a/client/src/views/record/detail-bottom.js
+++ b/client/src/views/record/detail-bottom.js
@@ -265,7 +265,7 @@ Espo.define('views/record/detail-bottom', 'view', function (Dep) {
if (typeof item == 'string' || item instanceof String) {
p = {name: item};
} else {
- p = item || {};
+ p = Espo.Utils.clone(item || {});
}
if (!p.name) {
return;
@@ -286,12 +286,17 @@ Espo.define('views/record/detail-bottom', 'view', function (Dep) {
return;
}
- p.view = 'views/record/panels/relationship';
-
var defs = this.getMetadata().get('clientDefs.' + scope + '.relationshipPanels.' + name) || {};
defs = Espo.Utils.clone(defs);
- p = _.extend(p, defs);
+ for (var i in defs) {
+ if (i in p) continue;
+ p[i] = defs[i];
+ }
+
+ if (!p.view) {
+ p.view = 'views/record/panels/relationship';
+ }
if (this.recordHelper.getPanelStateParam(p.name, 'hidden') !== null) {
p.hidden = this.recordHelper.getPanelStateParam(p.name, 'hidden');
diff --git a/client/src/views/record/detail-side.js b/client/src/views/record/detail-side.js
index 3c0185183d..d2e472e25b 100644
--- a/client/src/views/record/detail-side.js
+++ b/client/src/views/record/detail-side.js
@@ -144,20 +144,24 @@ Espo.define('views/record/detail-side', 'view', function (Dep) {
this.wait(true);
this.getHelper().layoutManager.get(this.scope, 'sidePanels' + Espo.Utils.upperCaseFirst(this.mode), function (layoutData) {
if (layoutData) {
- this.filterPanels(layoutData);
+ this.alterPanels(layoutData);
}
this.setupPanelViews();
this.wait(false);
}.bind(this));
},
- filterPanels: function (layoutData) {
+ alterPanels: function (layoutData) {
layoutData = layoutData || {};
var newList = [];
this.panelList.forEach(function (item) {
- if (item.name && (layoutData[item.name] || {}).disabled) {
- return;
+ if (item.name) {
+ var itemData = layoutData[item.name] || {};
+ if (itemData.disabled) return;
+ for (var i in itemData) {
+ item[i] = itemData[i];
+ }
}
newList.push(item);
}, this);
diff --git a/frontend/less/espo/custom.less b/frontend/less/espo/custom.less
index 006d4e56d4..c6168b2bba 100644
--- a/frontend/less/espo/custom.less
+++ b/frontend/less/espo/custom.less
@@ -7,8 +7,8 @@
}
.panel-body {
- padding: 14px;
- .clearfix();
+ padding: @panel-padding;
+ .clearfix();
}
blockquote {
@@ -543,7 +543,7 @@ table.no-margin {
.show-more > .btn {
border-radius: 0;
-
+ border-width: 0;
border-color: #e0e0e0;
}
@@ -879,6 +879,7 @@ body > footer > p a:visited {
.record .middle > .panel:first-child {
margin-top: 0;
+ border-top-width: 2px;
}
.record .middle > .panel {
diff --git a/frontend/less/espo/variables.less b/frontend/less/espo/variables.less
index 332915f61c..65daa2a775 100644
--- a/frontend/less/espo/variables.less
+++ b/frontend/less/espo/variables.less
@@ -17,7 +17,7 @@
@grid-gutter-width: 16px;
@container-lg: @container-large-desktop;
-@panel-padding: 15px;
+@panel-padding: 14px;
@container-padding: 15px;