diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml
index 74188ba28f..859c5652b2 100644
--- a/.idea/jsonSchemas.xml
+++ b/.idea/jsonSchemas.xml
@@ -341,6 +341,25 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 603397e5ac..1db639ab7d 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -184,6 +184,12 @@
],
"url": "./schema/metadata/app/client.json"
},
+ {
+ "fileMatch": [
+ "*/Resources/metadata/app/clientRecord.json"
+ ],
+ "url": "./schema/metadata/app/clientRecord.json"
+ },
{
"fileMatch": [
"*/Resources/metadata/app/clientRoutes.json"
diff --git a/application/Espo/Core/Templates/Metadata/BasePlus/clientDefs.json b/application/Espo/Core/Templates/Metadata/BasePlus/clientDefs.json
index 6d02b78b4a..1b34cc159c 100644
--- a/application/Espo/Core/Templates/Metadata/BasePlus/clientDefs.json
+++ b/application/Espo/Core/Templates/Metadata/BasePlus/clientDefs.json
@@ -5,21 +5,15 @@
"detail": [
{
"name": "activities",
- "label": "Activities",
- "view": "crm:views/record/panels/activities",
- "aclScope": "Activities"
+ "reference": "activities"
},
{
"name": "history",
- "label": "History",
- "view": "crm:views/record/panels/history",
- "aclScope": "Activities"
+ "reference": "history"
},
{
"name": "tasks",
- "label": "Tasks",
- "view": "crm:views/record/panels/tasks",
- "aclScope": "Task"
+ "reference": "tasks"
}
]
}
diff --git a/application/Espo/Core/Templates/Metadata/Company/clientDefs.json b/application/Espo/Core/Templates/Metadata/Company/clientDefs.json
index 6d02b78b4a..1b34cc159c 100644
--- a/application/Espo/Core/Templates/Metadata/Company/clientDefs.json
+++ b/application/Espo/Core/Templates/Metadata/Company/clientDefs.json
@@ -5,21 +5,15 @@
"detail": [
{
"name": "activities",
- "label": "Activities",
- "view": "crm:views/record/panels/activities",
- "aclScope": "Activities"
+ "reference": "activities"
},
{
"name": "history",
- "label": "History",
- "view": "crm:views/record/panels/history",
- "aclScope": "Activities"
+ "reference": "history"
},
{
"name": "tasks",
- "label": "Tasks",
- "view": "crm:views/record/panels/tasks",
- "aclScope": "Task"
+ "reference": "tasks"
}
]
}
diff --git a/application/Espo/Core/Templates/Metadata/Person/clientDefs.json b/application/Espo/Core/Templates/Metadata/Person/clientDefs.json
index 6d02b78b4a..1b34cc159c 100644
--- a/application/Espo/Core/Templates/Metadata/Person/clientDefs.json
+++ b/application/Espo/Core/Templates/Metadata/Person/clientDefs.json
@@ -5,21 +5,15 @@
"detail": [
{
"name": "activities",
- "label": "Activities",
- "view": "crm:views/record/panels/activities",
- "aclScope": "Activities"
+ "reference": "activities"
},
{
"name": "history",
- "label": "History",
- "view": "crm:views/record/panels/history",
- "aclScope": "Activities"
+ "reference": "history"
},
{
"name": "tasks",
- "label": "Tasks",
- "view": "crm:views/record/panels/tasks",
- "aclScope": "Task"
+ "reference": "tasks"
}
]
}
diff --git a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Account.json b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Account.json
index e0bf3c9b06..07c2886b20 100644
--- a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Account.json
+++ b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Account.json
@@ -8,41 +8,29 @@
"detail": [
{
"name": "activities",
- "label": "Activities",
- "view": "crm:views/record/panels/activities",
- "aclScope": "Activities"
+ "reference": "activities"
},
{
"name": "history",
- "label": "History",
- "view": "crm:views/record/panels/history",
- "aclScope": "Activities"
+ "reference": "history"
},
{
"name": "tasks",
- "label": "Tasks",
- "view": "crm:views/record/panels/tasks",
- "aclScope": "Task"
+ "reference": "tasks"
}
],
"detailSmall": [
{
"name": "activities",
- "label": "Activities",
- "view": "crm:views/record/panels/activities",
- "aclScope": "Activities"
+ "reference": "activities"
},
{
"name": "history",
- "label": "History",
- "view": "crm:views/record/panels/history",
- "aclScope": "Activities"
+ "reference": "history"
},
{
"name": "tasks",
- "label": "Tasks",
- "view": "crm:views/record/panels/tasks",
- "aclScope": "Task"
+ "reference": "tasks"
}
]
},
diff --git a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Case.json b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Case.json
index f703ddd449..e28f862b95 100644
--- a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Case.json
+++ b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Case.json
@@ -19,46 +19,36 @@
"checkVisibilityFunction": "isRejectAvailable"
}
],
- "sidePanels":{
- "detail":[
- {
- "name":"activities",
- "label":"Activities",
- "view":"crm:views/case/record/panels/activities",
- "aclScope": "Activities"
- },
- {
- "name":"history",
- "label":"History",
- "view":"crm:views/record/panels/history",
- "aclScope": "Activities"
- },
- {
- "name":"tasks",
- "label":"Tasks",
- "view":"crm:views/record/panels/tasks",
- "aclScope": "Task"
- }
+ "sidePanels": {
+ "detail": [
+ {
+ "name": "activities",
+ "reference": "activities",
+ "view": "crm:views/case/record/panels/activities"
+ },
+ {
+ "name": "history",
+ "reference": "history"
+ },
+ {
+ "name": "tasks",
+ "reference": "tasks"
+ }
],
- "detailSmall":[
- {
- "name":"activities",
- "label":"Activities",
- "view":"crm:views/case/record/panels/activities",
- "aclScope": "Activities"
- },
- {
- "name":"history",
- "label":"History",
- "view":"crm:views/record/panels/history",
- "aclScope": "Activities"
- },
- {
- "name":"tasks",
- "label":"Tasks",
- "view":"crm:views/record/panels/tasks",
- "aclScope": "Task"
- }
+ "detailSmall": [
+ {
+ "name": "activities",
+ "reference": "activities",
+ "view": "crm:views/case/record/panels/activities"
+ },
+ {
+ "name": "history",
+ "reference": "history"
+ },
+ {
+ "name": "tasks",
+ "reference": "tasks"
+ }
]
},
"filterList": [
diff --git a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Contact.json b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Contact.json
index 5cd1641368..7e24631184 100644
--- a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Contact.json
+++ b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Contact.json
@@ -12,41 +12,29 @@
"detail": [
{
"name": "activities",
- "label": "Activities",
- "view": "crm:views/record/panels/activities",
- "aclScope": "Activities"
+ "reference": "activities"
},
{
"name": "history",
- "label": "History",
- "view": "crm:views/record/panels/history",
- "aclScope": "Activities"
+ "reference": "history"
},
{
"name": "tasks",
- "label": "Tasks",
- "view": "crm:views/record/panels/tasks",
- "aclScope": "Task"
+ "reference": "tasks"
}
],
"detailSmall": [
{
"name": "activities",
- "label": "Activities",
- "view": "crm:views/record/panels/activities",
- "aclScope": "Activities"
+ "reference": "activities"
},
{
"name": "history",
- "label": "History",
- "view": "crm:views/record/panels/history",
- "aclScope": "Activities"
+ "reference": "history"
},
{
"name": "tasks",
- "label": "Tasks",
- "view": "crm:views/record/panels/tasks",
- "aclScope": "Task"
+ "reference": "tasks"
}
]
},
diff --git a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Lead.json b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Lead.json
index 1b0f3235e7..fc90d6f6fa 100644
--- a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Lead.json
+++ b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Lead.json
@@ -6,8 +6,8 @@
"recordViews":{
"detail":"crm:views/lead/record/detail"
},
- "sidePanels":{
- "detail":[
+ "sidePanels": {
+ "detail": [
{
"name":"convertedTo",
"label":"Converted To",
@@ -18,22 +18,16 @@
"isForm": true
},
{
- "name":"activities",
- "label":"Activities",
- "view":"crm:views/record/panels/activities",
- "aclScope":"Activities"
+ "name": "activities",
+ "reference": "activities"
},
{
- "name":"history",
- "label":"History",
- "view":"crm:views/record/panels/history",
- "aclScope":"Activities"
+ "name": "history",
+ "reference": "history"
},
{
- "name":"tasks",
- "label":"Tasks",
- "view":"crm:views/record/panels/tasks",
- "aclScope":"Task"
+ "name": "tasks",
+ "reference": "tasks"
}
],
"edit":[
@@ -47,7 +41,7 @@
"isForm": true
}
],
- "detailSmall":[
+ "detailSmall": [
{
"name":"convertedTo",
"label":"Converted To",
@@ -58,22 +52,16 @@
"isForm": true
},
{
- "name":"activities",
- "label":"Activities",
- "view":"crm:views/record/panels/activities",
- "aclScope":"Activities"
+ "name": "activities",
+ "reference": "activities"
},
{
- "name":"history",
- "label":"History",
- "view":"crm:views/record/panels/history",
- "aclScope":"Activities"
+ "name": "history",
+ "reference": "history"
},
{
- "name":"tasks",
- "label":"Tasks",
- "view":"crm:views/record/panels/tasks",
- "aclScope":"Task"
+ "name": "tasks",
+ "reference": "tasks"
}
],
"editSmall":[
diff --git a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Opportunity.json b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Opportunity.json
index 00b65a8707..5352d4b54c 100644
--- a/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Opportunity.json
+++ b/application/Espo/Modules/Crm/Resources/metadata/clientDefs/Opportunity.json
@@ -10,46 +10,36 @@
"list":"crm:views/opportunity/record/list",
"kanban": "crm:views/opportunity/record/kanban"
},
- "sidePanels":{
- "detail":[
- {
- "name":"activities",
- "label":"Activities",
- "view":"crm:views/opportunity/record/panels/activities",
- "aclScope": "Activities"
- },
- {
- "name":"history",
- "label":"History",
- "view":"crm:views/record/panels/history",
- "aclScope": "Activities"
- },
- {
- "name":"tasks",
- "label":"Tasks",
- "view":"crm:views/record/panels/tasks",
- "aclScope": "Task"
- }
+ "sidePanels": {
+ "detail": [
+ {
+ "name": "activities",
+ "reference": "activities",
+ "view": "crm:views/opportunity/record/panels/activities"
+ },
+ {
+ "name": "history",
+ "reference": "history"
+ },
+ {
+ "name": "tasks",
+ "reference": "tasks"
+ }
],
- "detailSmall":[
- {
- "name":"activities",
- "label":"Activities",
- "view":"crm:views/record/panels/activities",
- "aclScope": "Activities"
- },
- {
- "name":"history",
- "label":"History",
- "view":"crm:views/record/panels/history",
- "aclScope": "Activities"
- },
- {
- "name":"tasks",
- "label":"Tasks",
- "view":"crm:views/record/panels/tasks",
- "aclScope": "Task"
- }
+ "detailSmall": [
+ {
+ "name": "activities",
+ "reference": "activities",
+ "view": "crm:views/opportunity/record/panels/activities"
+ },
+ {
+ "name": "history",
+ "reference": "history"
+ },
+ {
+ "name": "tasks",
+ "reference": "tasks"
+ }
]
},
"filterList": [
diff --git a/application/Espo/Resources/metadata/app/clientRecord.json b/application/Espo/Resources/metadata/app/clientRecord.json
new file mode 100644
index 0000000000..72d3aa9bf3
--- /dev/null
+++ b/application/Espo/Resources/metadata/app/clientRecord.json
@@ -0,0 +1,22 @@
+{
+ "panels": {
+ "activities": {
+ "name": "activities",
+ "label": "Activities",
+ "view": "crm:views/record/panels/activities",
+ "aclScope": "Activities"
+ },
+ "history": {
+ "name": "history",
+ "label": "History",
+ "view": "crm:views/record/panels/history",
+ "aclScope": "Activities"
+ },
+ "tasks": {
+ "name": "tasks",
+ "label": "Tasks",
+ "view": "crm:views/record/panels/tasks",
+ "aclScope": "Task"
+ }
+ }
+}
diff --git a/application/Espo/Resources/metadata/clientDefs/PortalUser.json b/application/Espo/Resources/metadata/clientDefs/PortalUser.json
index 539a2cd587..3d25d24cb8 100644
--- a/application/Espo/Resources/metadata/clientDefs/PortalUser.json
+++ b/application/Espo/Resources/metadata/clientDefs/PortalUser.json
@@ -32,29 +32,21 @@
"detail": [
{
"name": "activities",
- "label": "Activities",
- "view": "crm:views/record/panels/activities",
- "aclScope": "Activities"
+ "reference": "activities"
},
{
"name": "history",
- "label": "History",
- "view": "crm:views/record/panels/history",
- "aclScope": "Activities"
+ "reference": "history"
}
],
"detailSmall": [
{
"name": "activities",
- "label": "Activities",
- "view": "crm:views/record/panels/activities",
- "aclScope": "Activities"
+ "reference": "activities"
},
{
"name": "history",
- "label": "History",
- "view": "crm:views/record/panels/history",
- "aclScope": "Activities"
+ "reference": "history"
}
]
},
@@ -66,4 +58,4 @@
"filter": "activePortal"
},
"iconClass": "far fa-user-circle"
-}
\ No newline at end of file
+}
diff --git a/application/Espo/Resources/metadata/clientDefs/User.json b/application/Espo/Resources/metadata/clientDefs/User.json
index 312bf11f97..8ecbbfe6a5 100644
--- a/application/Espo/Resources/metadata/clientDefs/User.json
+++ b/application/Espo/Resources/metadata/clientDefs/User.json
@@ -73,41 +73,31 @@
"detail": [
{
"name": "activities",
- "label": "Activities",
- "view": "crm:views/record/panels/activities",
- "aclScope": "Activities"
+ "reference": "activities"
},
{
"name": "history",
- "label": "History",
- "view": "crm:views/record/panels/history",
- "aclScope": "Activities"
+ "reference": "history"
},
{
"name": "tasks",
- "label": "Tasks",
- "view": "crm:views/user/record/panels/tasks",
- "aclScope": "Task"
+ "reference": "tasks",
+ "view": "crm:views/user/record/panels/tasks"
}
],
"detailSmall": [
{
"name": "activities",
- "label": "Activities",
- "view": "crm:views/record/panels/activities",
- "aclScope": "Activities"
+ "reference": "activities"
},
{
"name": "history",
- "label": "History",
- "view": "crm:views/record/panels/history",
- "aclScope": "Activities"
+ "reference": "history"
},
{
"name": "tasks",
- "label": "Tasks",
- "view": "crm:views/user/record/panels/tasks",
- "aclScope": "Task"
+ "reference": "tasks",
+ "view": "crm:views/user/record/panels/tasks"
}
]
},
diff --git a/client/src/views/admin/layouts/bottom-panels-detail.js b/client/src/views/admin/layouts/bottom-panels-detail.js
index 80ed90c746..3ad007175e 100644
--- a/client/src/views/admin/layouts/bottom-panels-detail.js
+++ b/client/src/views/admin/layouts/bottom-panels-detail.js
@@ -82,7 +82,14 @@ class LayoutBottomPanelsDetail extends LayoutSidePanelsDetailView {
(this.getMetadata()
.get(['clientDefs', this.scope, 'bottomPanels', this.viewType]) || []
- ).forEach(item => {
+ ).forEach(/** Record */item => {
+ if (item.reference) {
+ item = {
+ ...this.getMetadata().get(`app.clientRecord.panels.${item.reference}`),
+ ...item,
+ };
+ }
+
if (!item.name) {
return;
}
diff --git a/client/src/views/admin/layouts/side-panels-detail.js b/client/src/views/admin/layouts/side-panels-detail.js
index 95cebfdc41..772fc5c648 100644
--- a/client/src/views/admin/layouts/side-panels-detail.js
+++ b/client/src/views/admin/layouts/side-panels-detail.js
@@ -150,7 +150,14 @@ class LayoutSidePanelsDetailView extends LayoutRowsView {
}
(this.getMetadata().get(['clientDefs', this.scope, 'sidePanels', this.viewType]) || [])
- .forEach(item => {
+ .forEach(/** Record */item => {
+ if (item.reference) {
+ item = {
+ ...this.getMetadata().get(`app.clientRecord.panels.${item.reference}`),
+ ...item,
+ };
+ }
+
if (!item.name) {
return;
}
diff --git a/client/src/views/record/detail-bottom.js b/client/src/views/record/detail-bottom.js
index 8d361420ed..227686eaa5 100644
--- a/client/src/views/record/detail-bottom.js
+++ b/client/src/views/record/detail-bottom.js
@@ -50,9 +50,23 @@ class DetailBottomRecordView extends PanelsContainerRecordView {
setupPanels() {
const scope = this.scope;
- this.panelList = Espo.Utils.clone(
- this.getMetadata()
- .get(['clientDefs', scope, 'bottomPanels', this.type]) || this.panelList || []);
+ this.panelList = this.getMetadata().get(['clientDefs', scope, 'bottomPanels', this.type]) ||
+ this.panelList || [];
+
+ this.panelList = [...this.panelList].map(item => {
+ if ('reference' in item && item.reference) {
+ // noinspection UnnecessaryLocalVariableJS
+ /** @type {module:views/record/panels-container~panel} */
+ const newItem = {
+ ...this.getMetadata().get(`app.clientRecord.panels.${item.reference}`),
+ ...item,
+ };
+
+ return newItem;
+ }
+
+ return item;
+ });
this.panelList.forEach(item => {
if ('index' in item) {
diff --git a/client/src/views/record/detail-side.js b/client/src/views/record/detail-side.js
index 951206f286..73c91832bd 100644
--- a/client/src/views/record/detail-side.js
+++ b/client/src/views/record/detail-side.js
@@ -111,6 +111,21 @@ class DetailSideRecordView extends PanelsContainerRecordView {
});
}
+ this.panelList = this.panelList.map(item => {
+ if ('reference' in item && item.reference) {
+ // noinspection UnnecessaryLocalVariableJS
+ /** @type {module:views/record/panels-container~panel} */
+ const newItem = {
+ ...this.getMetadata().get(`app.clientRecord.panels.${item.reference}`),
+ ...item,
+ };
+
+ return newItem;
+ }
+
+ return item;
+ });
+
this.panelList = this.panelList.filter((p) => {
if (p.aclScope) {
if (!this.getAcl().checkScope(p.aclScope)) {
diff --git a/schema/metadata/app/clientRecord.json b/schema/metadata/app/clientRecord.json
new file mode 100644
index 0000000000..1bf5f0ab1e
--- /dev/null
+++ b/schema/metadata/app/clientRecord.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://www.espocrm.com/schema/metadata/app/clientRecord.json",
+ "title": "app/navbar",
+ "description": "Client record definitions.",
+ "type": "object",
+ "properties": {
+ "panels": {
+ "description": "Detail view panels. Can be references from clientDefs > sidePanels/bottomPanels. As of v9.0.",
+ "type": "object",
+ "additionalProperties": {
+ "description": "A panel.",
+ "type": "object",
+ "$ref": "../clientDefs.json#/definitions/panel"
+ }
+ }
+ }
+}
diff --git a/schema/metadata/clientDefs.json b/schema/metadata/clientDefs.json
index 4edb76f8c0..8c91752e51 100644
--- a/schema/metadata/clientDefs.json
+++ b/schema/metadata/clientDefs.json
@@ -330,59 +330,7 @@
"anyOf": [
{"const": "__APPEND__"},
{
- "type": "object",
- "properties": {
- "name": {
- "description": "A name.",
- "type": "string"
- },
- "label": {
- "description": "A translatable label.",
- "type": "string"
- },
- "view": {
- "description": "A view class.",
- "type": "string"
- },
- "options": {
- "description": "Options to be passed to the view.",
- "type": "object"
- },
- "isForm": {
- "description": "Whether the panel should display a form.",
- "type": "boolean"
- },
- "notRefreshable": {
- "description": "Disable the ability to refresh the panel.",
- "type": "boolean"
- },
- "style": {
- "description": "A style.",
- "enum": ["default", "success", "warning", "danger"]
- },
- "hidden": {
- "description": "Hidden by default.",
- "type": "boolean"
- },
- "aclScope": {
- "description": "A scope access to which is required for the panel being visible.",
- "type": "string"
- },
- "acl": {
- "description": "An ACL action access to which is required for the panel being visible.",
- "type": "string",
- "enum": [
- "create",
- "read",
- "edit",
- "delete",
- "stream"
- ]
- },
- "accessDataList": {
- "$ref": "#/definitions/accessDataList"
- }
- }
+ "$ref": "#/definitions/panel"
}
]
}
@@ -1247,6 +1195,65 @@
}
}
}
+ },
+ "panel": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "description": "A name.",
+ "type": "string"
+ },
+ "label": {
+ "description": "A translatable label.",
+ "type": "string"
+ },
+ "view": {
+ "description": "A view class.",
+ "type": "string"
+ },
+ "options": {
+ "description": "Options to be passed to the view.",
+ "type": "object"
+ },
+ "isForm": {
+ "description": "Whether the panel should display a form.",
+ "type": "boolean"
+ },
+ "notRefreshable": {
+ "description": "Disable the ability to refresh the panel.",
+ "type": "boolean"
+ },
+ "style": {
+ "description": "A style.",
+ "enum": ["default", "success", "warning", "danger"]
+ },
+ "hidden": {
+ "description": "Hidden by default.",
+ "type": "boolean"
+ },
+ "aclScope": {
+ "description": "A scope access to which is required for the panel being visible.",
+ "type": "string"
+ },
+ "acl": {
+ "description": "An ACL action access to which is required for the panel being visible.",
+ "type": "string",
+ "enum": [
+ "create",
+ "read",
+ "edit",
+ "delete",
+ "stream"
+ ]
+ },
+ "accessDataList": {
+ "$ref": "#/definitions/accessDataList"
+ },
+ "reference": {
+ "type": "string",
+ "description": "A panel reference name. Panels are defined in app > clientRecord > panels. As of v9.0."
+ }
+ }
}
}
}