diff --git a/domismp-tests/domismp-tests-api/groovy/oracle-4.1_integration_test_data.sql b/domismp-tests/domismp-tests-api/groovy/oracle-4.1_integration_test_data.sql
index 8d0e7f054b29de275c7523e98c15d069e7f95ccf..941441d2d7ed27665aaa7c94db616e0178588779 100644
--- a/domismp-tests/domismp-tests-api/groovy/oracle-4.1_integration_test_data.sql
+++ b/domismp-tests/domismp-tests-api/groovy/oracle-4.1_integration_test_data.sql
@@ -19,6 +19,8 @@ DELETE FROM SMP_SUBRESOURCE;
 DELETE FROM SMP_SUBRESOURCE_AUD;
 DELETE FROM SMP_RESOURCE;
 DELETE FROM SMP_RESOURCE_AUD;
+DELETE FROM SMP_DOCUMENT_PROPERTY;
+DELETE FROM SMP_DOCUMENT_PROPERTY_AUD;
 DELETE FROM SMP_DOCUMENT_VERSION;
 DELETE FROM SMP_DOCUMENT_VERSION_AUD;
 DELETE FROM SMP_DOCUMENT;
diff --git a/smp-angular/package-lock.json b/smp-angular/package-lock.json
index eb0db505958e31d36b78244ced33b36c178056ad..9fd3da8f8fe4a48170d3ae7466fb5e1be26fbee2 100644
--- a/smp-angular/package-lock.json
+++ b/smp-angular/package-lock.json
@@ -24,14 +24,14 @@
         "@angular/platform-browser-dynamic": "^16.2.12",
         "@angular/platform-server": "^16.2.12",
         "@angular/router": "^16.2.12",
-        "@codemirror/commands": "^6.5.0",
-        "@codemirror/language": "^6.10.1",
+        "@codemirror/commands": "^6.6.0",
+        "@codemirror/language": "^6.10.2",
         "@codemirror/language-data": "^6.5.1",
-        "@codemirror/merge": "^6.6.2",
+        "@codemirror/merge": "^6.6.3",
+        "@codemirror/search": "^6.5.6",
         "@codemirror/theme-one-dark": "^6.1.2",
-        "@codemirror/view": "^6.26.3",
+        "@codemirror/view": "^6.28.1",
         "@swimlane/ngx-datatable": "^20.1.0",
-        "codemirror": "^6.0.1",
         "file-saver": "^2.0.5",
         "rxjs": "^7.8.1",
         "ts-helpers": "^1.1.2",
@@ -2792,13 +2792,13 @@
       }
     },
     "node_modules/@codemirror/commands": {
-      "version": "6.5.0",
-      "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.5.0.tgz",
-      "integrity": "sha512-rK+sj4fCAN/QfcY9BEzYMgp4wwL/q5aj/VfNSoH1RWPF9XS/dUwBkvlL3hpWgEjOqlpdN1uLC9UkjJ4tmyjJYg==",
+      "version": "6.6.0",
+      "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz",
+      "integrity": "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==",
       "dependencies": {
         "@codemirror/language": "^6.0.0",
         "@codemirror/state": "^6.4.0",
-        "@codemirror/view": "^6.0.0",
+        "@codemirror/view": "^6.27.0",
         "@lezer/common": "^1.1.0"
       }
     },
@@ -3043,9 +3043,9 @@
       }
     },
     "node_modules/@codemirror/language": {
-      "version": "6.10.1",
-      "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz",
-      "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
+      "version": "6.10.2",
+      "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz",
+      "integrity": "sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==",
       "dependencies": {
         "@codemirror/state": "^6.0.0",
         "@codemirror/view": "^6.23.0",
@@ -3103,9 +3103,9 @@
       }
     },
     "node_modules/@codemirror/merge": {
-      "version": "6.6.2",
-      "resolved": "https://registry.npmjs.org/@codemirror/merge/-/merge-6.6.2.tgz",
-      "integrity": "sha512-g6ltz4OLYbudvZfgLUp30yQngL+jMXrb9EHKXKT0lkWm6NjWxmjG4I2gcPqMlyb43UCPgEVlylKRBbj9AJ7H9A==",
+      "version": "6.6.3",
+      "resolved": "https://registry.npmjs.org/@codemirror/merge/-/merge-6.6.3.tgz",
+      "integrity": "sha512-sui2Y/qKyvpPs1VJysAC4Q4w9Oe6MQM8LPOLphaQzhP6wfY28Flq/xuaBBUGDRP1Rtu7Ksf6zwdHPLbMn3zuBw==",
       "dependencies": {
         "@codemirror/language": "^6.0.0",
         "@codemirror/state": "^6.0.0",
@@ -3141,9 +3141,9 @@
       }
     },
     "node_modules/@codemirror/view": {
-      "version": "6.26.3",
-      "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz",
-      "integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==",
+      "version": "6.28.1",
+      "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.1.tgz",
+      "integrity": "sha512-BUWr+zCJpMkA/u69HlJmR+YkV4yPpM81HeMkOMZuwFa8iM5uJdEPKAs1icIRZKkKmy0Ub1x9/G3PQLTXdpBxrQ==",
       "dependencies": {
         "@codemirror/state": "^6.4.0",
         "style-mod": "^4.1.0",
@@ -5392,9 +5392,9 @@
       "dev": true
     },
     "node_modules/@types/cors": {
-      "version": "2.8.13",
-      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
-      "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
+      "version": "2.8.17",
+      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
+      "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
       "dev": true,
       "dependencies": {
         "@types/node": "*"
@@ -6565,12 +6565,12 @@
       }
     },
     "node_modules/braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
       "dev": true,
       "dependencies": {
-        "fill-range": "^7.0.1"
+        "fill-range": "^7.1.1"
       },
       "engines": {
         "node": ">=8"
@@ -6979,20 +6979,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/codemirror": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
-      "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
-      "dependencies": {
-        "@codemirror/autocomplete": "^6.0.0",
-        "@codemirror/commands": "^6.0.0",
-        "@codemirror/language": "^6.0.0",
-        "@codemirror/lint": "^6.0.0",
-        "@codemirror/search": "^6.0.0",
-        "@codemirror/state": "^6.0.0",
-        "@codemirror/view": "^6.0.0"
-      }
-    },
     "node_modules/color-convert": {
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -8026,9 +8012,9 @@
       }
     },
     "node_modules/engine.io": {
-      "version": "6.4.2",
-      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz",
-      "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==",
+      "version": "6.5.5",
+      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
+      "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
       "dev": true,
       "dependencies": {
         "@types/cookie": "^0.4.1",
@@ -8039,17 +8025,17 @@
         "cookie": "~0.4.1",
         "cors": "~2.8.5",
         "debug": "~4.3.1",
-        "engine.io-parser": "~5.0.3",
-        "ws": "~8.11.0"
+        "engine.io-parser": "~5.2.1",
+        "ws": "~8.17.1"
       },
       "engines": {
-        "node": ">=10.0.0"
+        "node": ">=10.2.0"
       }
     },
     "node_modules/engine.io-parser": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz",
-      "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==",
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
+      "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
       "dev": true,
       "engines": {
         "node": ">=10.0.0"
@@ -8913,9 +8899,9 @@
       "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
     },
     "node_modules/fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
       "dev": true,
       "dependencies": {
         "to-regex-range": "^5.0.1"
@@ -10789,9 +10775,9 @@
       }
     },
     "node_modules/jsdom/node_modules/ws": {
-      "version": "7.5.9",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
-      "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+      "version": "7.5.10",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+      "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
       "dev": true,
       "engines": {
         "node": ">=8.3.0"
@@ -16454,29 +16440,31 @@
       }
     },
     "node_modules/socket.io": {
-      "version": "4.6.1",
-      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz",
-      "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==",
+      "version": "4.7.5",
+      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
+      "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
       "dev": true,
       "dependencies": {
         "accepts": "~1.3.4",
         "base64id": "~2.0.0",
+        "cors": "~2.8.5",
         "debug": "~4.3.2",
-        "engine.io": "~6.4.1",
+        "engine.io": "~6.5.2",
         "socket.io-adapter": "~2.5.2",
-        "socket.io-parser": "~4.2.1"
+        "socket.io-parser": "~4.2.4"
       },
       "engines": {
-        "node": ">=10.0.0"
+        "node": ">=10.2.0"
       }
     },
     "node_modules/socket.io-adapter": {
-      "version": "2.5.2",
-      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
-      "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
+      "version": "2.5.5",
+      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
+      "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
       "dev": true,
       "dependencies": {
-        "ws": "~8.11.0"
+        "debug": "~4.3.4",
+        "ws": "~8.17.1"
       }
     },
     "node_modules/socket.io-parser": {
@@ -18191,27 +18179,6 @@
         "webpack": "^4.0.0 || ^5.0.0"
       }
     },
-    "node_modules/webpack-dev-server/node_modules/ws": {
-      "version": "8.17.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",
-      "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==",
-      "dev": true,
-      "engines": {
-        "node": ">=10.0.0"
-      },
-      "peerDependencies": {
-        "bufferutil": "^4.0.1",
-        "utf-8-validate": ">=5.0.2"
-      },
-      "peerDependenciesMeta": {
-        "bufferutil": {
-          "optional": true
-        },
-        "utf-8-validate": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/webpack-merge": {
       "version": "5.9.0",
       "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz",
@@ -18539,16 +18506,16 @@
       "dev": true
     },
     "node_modules/ws": {
-      "version": "8.11.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
-      "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+      "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
       "dev": true,
       "engines": {
         "node": ">=10.0.0"
       },
       "peerDependencies": {
         "bufferutil": "^4.0.1",
-        "utf-8-validate": "^5.0.2"
+        "utf-8-validate": ">=5.0.2"
       },
       "peerDependenciesMeta": {
         "bufferutil": {
@@ -20530,13 +20497,13 @@
       }
     },
     "@codemirror/commands": {
-      "version": "6.5.0",
-      "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.5.0.tgz",
-      "integrity": "sha512-rK+sj4fCAN/QfcY9BEzYMgp4wwL/q5aj/VfNSoH1RWPF9XS/dUwBkvlL3hpWgEjOqlpdN1uLC9UkjJ4tmyjJYg==",
+      "version": "6.6.0",
+      "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz",
+      "integrity": "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==",
       "requires": {
         "@codemirror/language": "^6.0.0",
         "@codemirror/state": "^6.4.0",
-        "@codemirror/view": "^6.0.0",
+        "@codemirror/view": "^6.27.0",
         "@lezer/common": "^1.1.0"
       }
     },
@@ -20781,9 +20748,9 @@
       }
     },
     "@codemirror/language": {
-      "version": "6.10.1",
-      "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz",
-      "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
+      "version": "6.10.2",
+      "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz",
+      "integrity": "sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==",
       "requires": {
         "@codemirror/state": "^6.0.0",
         "@codemirror/view": "^6.23.0",
@@ -20841,9 +20808,9 @@
       }
     },
     "@codemirror/merge": {
-      "version": "6.6.2",
-      "resolved": "https://registry.npmjs.org/@codemirror/merge/-/merge-6.6.2.tgz",
-      "integrity": "sha512-g6ltz4OLYbudvZfgLUp30yQngL+jMXrb9EHKXKT0lkWm6NjWxmjG4I2gcPqMlyb43UCPgEVlylKRBbj9AJ7H9A==",
+      "version": "6.6.3",
+      "resolved": "https://registry.npmjs.org/@codemirror/merge/-/merge-6.6.3.tgz",
+      "integrity": "sha512-sui2Y/qKyvpPs1VJysAC4Q4w9Oe6MQM8LPOLphaQzhP6wfY28Flq/xuaBBUGDRP1Rtu7Ksf6zwdHPLbMn3zuBw==",
       "requires": {
         "@codemirror/language": "^6.0.0",
         "@codemirror/state": "^6.0.0",
@@ -20879,9 +20846,9 @@
       }
     },
     "@codemirror/view": {
-      "version": "6.26.3",
-      "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz",
-      "integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==",
+      "version": "6.28.1",
+      "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.1.tgz",
+      "integrity": "sha512-BUWr+zCJpMkA/u69HlJmR+YkV4yPpM81HeMkOMZuwFa8iM5uJdEPKAs1icIRZKkKmy0Ub1x9/G3PQLTXdpBxrQ==",
       "requires": {
         "@codemirror/state": "^6.4.0",
         "style-mod": "^4.1.0",
@@ -22678,9 +22645,9 @@
       "dev": true
     },
     "@types/cors": {
-      "version": "2.8.13",
-      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
-      "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
+      "version": "2.8.17",
+      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
+      "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
       "dev": true,
       "requires": {
         "@types/node": "*"
@@ -23652,12 +23619,12 @@
       }
     },
     "braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
       "dev": true,
       "requires": {
-        "fill-range": "^7.0.1"
+        "fill-range": "^7.1.1"
       }
     },
     "browser-process-hrtime": {
@@ -23939,20 +23906,6 @@
         "shallow-clone": "^3.0.0"
       }
     },
-    "codemirror": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
-      "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
-      "requires": {
-        "@codemirror/autocomplete": "^6.0.0",
-        "@codemirror/commands": "^6.0.0",
-        "@codemirror/language": "^6.0.0",
-        "@codemirror/lint": "^6.0.0",
-        "@codemirror/search": "^6.0.0",
-        "@codemirror/state": "^6.0.0",
-        "@codemirror/view": "^6.0.0"
-      }
-    },
     "color-convert": {
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -24769,9 +24722,9 @@
       }
     },
     "engine.io": {
-      "version": "6.4.2",
-      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz",
-      "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==",
+      "version": "6.5.5",
+      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
+      "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
       "dev": true,
       "requires": {
         "@types/cookie": "^0.4.1",
@@ -24782,14 +24735,14 @@
         "cookie": "~0.4.1",
         "cors": "~2.8.5",
         "debug": "~4.3.1",
-        "engine.io-parser": "~5.0.3",
-        "ws": "~8.11.0"
+        "engine.io-parser": "~5.2.1",
+        "ws": "~8.17.1"
       }
     },
     "engine.io-parser": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz",
-      "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==",
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
+      "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
       "dev": true
     },
     "enhanced-resolve": {
@@ -25449,9 +25402,9 @@
       "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
     },
     "fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
       "dev": true,
       "requires": {
         "to-regex-range": "^5.0.1"
@@ -26877,9 +26830,9 @@
           "dev": true
         },
         "ws": {
-          "version": "7.5.9",
-          "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
-          "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+          "version": "7.5.10",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+          "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
           "dev": true,
           "requires": {}
         }
@@ -31141,26 +31094,28 @@
       "dev": true
     },
     "socket.io": {
-      "version": "4.6.1",
-      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz",
-      "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==",
+      "version": "4.7.5",
+      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
+      "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
       "dev": true,
       "requires": {
         "accepts": "~1.3.4",
         "base64id": "~2.0.0",
+        "cors": "~2.8.5",
         "debug": "~4.3.2",
-        "engine.io": "~6.4.1",
+        "engine.io": "~6.5.2",
         "socket.io-adapter": "~2.5.2",
-        "socket.io-parser": "~4.2.1"
+        "socket.io-parser": "~4.2.4"
       }
     },
     "socket.io-adapter": {
-      "version": "2.5.2",
-      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
-      "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
+      "version": "2.5.5",
+      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
+      "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
       "dev": true,
       "requires": {
-        "ws": "~8.11.0"
+        "debug": "~4.3.4",
+        "ws": "~8.17.1"
       }
     },
     "socket.io-parser": {
@@ -32483,13 +32438,6 @@
             "range-parser": "^1.2.1",
             "schema-utils": "^4.0.0"
           }
-        },
-        "ws": {
-          "version": "8.17.0",
-          "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",
-          "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==",
-          "dev": true,
-          "requires": {}
         }
       }
     },
@@ -32680,9 +32628,9 @@
       "dev": true
     },
     "ws": {
-      "version": "8.11.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
-      "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+      "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
       "dev": true,
       "requires": {}
     },
diff --git a/smp-angular/package.json b/smp-angular/package.json
index 8fe7ce8d5e8dd3b0c6f9a7fd80613dddc04b3dbb..ce9ed65b29789115c65bdef4da0590bd30d4cd41 100644
--- a/smp-angular/package.json
+++ b/smp-angular/package.json
@@ -31,13 +31,13 @@
     "@swimlane/ngx-datatable": "^20.1.0",
     "@angular-material-components/datetime-picker": "^16.0.1",
     "@angular-material-components/moment-adapter": "^16.0.1",
-    "codemirror": "^6.0.1",
-    "@codemirror/view": "^6.26.3",
-    "@codemirror/commands": "^6.5.0",
-    "@codemirror/language": "^6.10.1",
+    "@codemirror/view": "^6.28.1",
+    "@codemirror/search": "^6.5.6",
+    "@codemirror/commands": "^6.6.0",
+    "@codemirror/language": "^6.10.2",
     "@codemirror/language-data": "^6.5.1",
     "@codemirror/theme-one-dark": "^6.1.2",
-    "@codemirror/merge": "^6.6.2",
+    "@codemirror/merge": "^6.6.3",
     "file-saver": "^2.0.5",
     "rxjs": "^7.8.1",
     "ts-helpers": "^1.1.2",
diff --git a/smp-angular/src/_smp-all-themes.scss b/smp-angular/src/_smp-all-themes.scss
index 5e919e9fde8faa3451e898e738839a3d098bbc42..6deaa632784c6d80eaf1380dd85d63dc64e3e6ba 100644
--- a/smp-angular/src/_smp-all-themes.scss
+++ b/smp-angular/src/_smp-all-themes.scss
@@ -38,9 +38,9 @@
   }
 
   .mat-mdc-table .mat-mdc-header-cell {
-    padding: 5px !important;
+    padding:2px 5px !important;
     box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.24);
-    background-color: smp.get-theme-color($theme, primary, 800, 0.1) !important;
+    background-color: smp.get-theme-color($theme, primary, 200, 1) !important;
     text-align: center;
   }
 
diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts
index d649da40fd94addffeddbdb6883f6165a1d69ac1..d72cf927d8e4b2bc120b68ae604fdca3c2c5ff06 100644
--- a/smp-angular/src/app/app.module.ts
+++ b/smp-angular/src/app/app.module.ts
@@ -121,7 +121,7 @@ import {ResourceDetailsPanelComponent} from "./edit/edit-resources/resource-deta
 import {ResourceDocumentPanelComponent} from "./edit/edit-resources/resource-document-panel/resource-document-panel.component";
 import {DocumentWizardDialogComponent} from "./edit/edit-resources/document-wizard-dialog/document-wizard-dialog.component";
 import {SubresourcePanelComponent} from "./edit/edit-resources/subresource-panel/subresource-panel.component";
-import {SubresourceDialogComponent} from "./edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component";
+import {SubresourceDialogComponent} from "./edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component";
 import {SubresourceDocumentPanelComponent} from "./edit/edit-resources/subresource-document-panel/subresource-document-panel.component";
 import {SubresourceDocumentWizardComponent} from "./edit/edit-resources/subresource-document-wizard-dialog/subresource-document-wizard.component";
 import {SmpWarningPanelComponent} from "./common/components/smp-warning-panel/smp-warning-panel.component";
@@ -158,6 +158,12 @@ import {HttpSessionInterceptor} from "./http/http-session-interceptor";
 import {
   SessionExpirationDialogComponent
 } from "./common/dialogs/session-expiration-dialog/session-expiration-dialog.component";
+import {
+  DocumentPropertiesPanelComponent
+} from "./common/panels/document-properties-panel/document-properties-panel.component";
+import {
+  DocumentPropertyDialogComponent
+} from "./common/dialogs/document-property-dialog/document-property-dialog.component";
 
 
 @NgModule({
@@ -190,6 +196,8 @@ import {
     DnsToolsComponent,
     DnsQueryPanelComponent,
     DocumentWizardDialogComponent,
+    DocumentPropertiesPanelComponent,
+    DocumentPropertyDialogComponent,
     DomainGroupComponent,
     DomainPanelComponent,
     DomainResourceTypePanelComponent,
diff --git a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.css b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.css
index 40f877779fd4d6868d776a3d5096024c43410169..ab91721a1c58e910fb232b3d30fd3a74ce6f0061 100644
--- a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.css
+++ b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.css
@@ -1,3 +1,9 @@
-.cm-gutters {
-  padding-top: 1.4em;
+
+.smp-document-editor {
+  display:block;
+  overflow: auto;
+  flex: 1;
+  align-self: stretch;
+  flex-direction: column;
+  border: ridge 3px #b0bec5
 }
diff --git a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.html b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.html
index b11a0efcc28880bce42a12170fbb6f64dd0de7bc..2349abfd26064ddbe3471b1026bd9e859b0a1fbe 100644
--- a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.html
+++ b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.html
@@ -1,2 +1,3 @@
+<!-- The nested root element -->
 <div #editorRoot id="editorRoot">
 </div>
diff --git a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts
index c3228be08f0383d62e9e88bf7ff2cc77fa37d404..a3238c96351aa1d8a0579851f92ca28aa5c086bc 100644
--- a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts
+++ b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts
@@ -8,16 +8,38 @@ import {
   ViewChild,
 } from '@angular/core';
 import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
-import {EditorView} from "@codemirror/view";
-import {basicSetup} from "codemirror";
+import {
+  EditorView,
+  lineNumbers,
+  keymap,
+  highlightActiveLineGutter,
+  highlightSpecialChars,
+  drawSelection,
+  dropCursor,
+  rectangularSelection, crosshairCursor, highlightActiveLine
+} from "@codemirror/view";
 import {Compartment, EditorState, Extension} from "@codemirror/state"
-import {LanguageSupport} from "@codemirror/language"
+import {
+  bracketMatching,
+  defaultHighlightStyle,
+  foldGutter, foldKeymap, indentOnInput,
+  LanguageSupport,
+  syntaxHighlighting
+} from "@codemirror/language"
 import {javascript} from "@codemirror/lang-javascript"
 import {xml} from "@codemirror/lang-xml"
 import {html} from "@codemirror/lang-html"
 import {json} from "@codemirror/lang-json"
 import {ThemeService} from "../../theme-service/theme.service";
 import {oneDark} from "@codemirror/theme-one-dark";
+import {defaultKeymap, history, historyKeymap} from "@codemirror/commands";
+import {
+  autocompletion,
+  closeBrackets,
+  closeBracketsKeymap, completionKeymap
+} from "@codemirror/autocomplete";
+import {highlightSelectionMatches, searchKeymap} from "@codemirror/search";
+import {lintKeymap} from "@codemirror/lint";
 
 function normalizeLineEndings(str: string): string {
   if (!str) {
@@ -46,7 +68,7 @@ export class SmpEditorComponent
   @Input() name = 'smpEditor';
   @ViewChild('editorRoot') ref!: ElementRef<HTMLDivElement>;
   value = '';
-  _readOnly:boolean = false;
+  _readOnly: boolean = false;
   _mimeType: string = "text/xml";
 
   codeMirror: EditorView;
@@ -54,7 +76,7 @@ export class SmpEditorComponent
   documentLanguage = new Compartment;
   themeConfig = new Compartment;
 
-  constructor( private  themeService: ThemeService) {
+  constructor(private themeService: ThemeService) {
   }
 
   ngAfterViewInit() {
@@ -65,16 +87,41 @@ export class SmpEditorComponent
       }
     });
     // configure the default extensions
-    let initExtensions: Extension[] =  [
-        basicSetup,
-        EditorView.lineWrapping,
-        updateListenerExtension,
-        this.documentLanguage.of(xml()),
-        this.readOnlyDocument.of(EditorState.readOnly.of(false))
-      ];
+    let initExtensions: Extension[] = [
+      lineNumbers(),
+      highlightActiveLineGutter(),
+      highlightSpecialChars(),
+      history(),
+      foldGutter(),
+      drawSelection(),
+      dropCursor(),
+      EditorState.allowMultipleSelections.of(true),
+      indentOnInput(),
+      syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+      bracketMatching(),
+      closeBrackets(),
+      autocompletion(),
+      rectangularSelection(),
+      crosshairCursor(),
+      highlightActiveLine(),
+      highlightSelectionMatches(),
+      keymap.of([
+        ...closeBracketsKeymap,
+        ...defaultKeymap,
+        ...searchKeymap,
+        ...historyKeymap,
+        ...foldKeymap,
+        ...completionKeymap,
+        ...lintKeymap
+      ]),
+      EditorView.lineWrapping,
+      updateListenerExtension,
+      this.documentLanguage.of(xml()),
+      this.readOnlyDocument.of(EditorState.readOnly.of(false))
+    ];
     // add the dark theme extension
     if (this.themeService.currentTheme === "pink_blue-grey_theme"
-      || this.themeService.currentTheme === "purple_green_theme" ) {
+      || this.themeService.currentTheme === "purple_green_theme") {
       initExtensions.push(this.themeConfig.of(oneDark));
     }
     this.codeMirror = new EditorView({
@@ -126,7 +173,6 @@ export class SmpEditorComponent
     this.codeMirror.dispatch({selection: {anchor: length, head: length}})
   }
 
-
   ngOnDestroy() {
     // is there a lighter-weight way to remove the cm instance?
     if (this.codeMirror) {
diff --git a/smp-angular/src/app/common/components/spacer/spacer.component.css b/smp-angular/src/app/common/components/spacer/spacer.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..4f8f11b1a480af7b0ba1734429440f6e66588881
--- /dev/null
+++ b/smp-angular/src/app/common/components/spacer/spacer.component.css
@@ -0,0 +1,13 @@
+.vertical-spacer {
+  margin-left:5px;
+  width: 10px;
+  border-left: 2px dotted gray
+}
+
+.horizontal-spacer {
+  margin-top:5px;
+  height: 10px;
+  width: 100%;
+  display: block;
+  border-top: 2px dotted gray;
+}
diff --git a/smp-angular/src/app/common/components/spacer/spacer.component.ts b/smp-angular/src/app/common/components/spacer/spacer.component.ts
index 321bbf8d57090bb41a8b87ea3798533b7c56632f..658ba9fcffc9c9d8971808dbfcc48954d6b7f13f 100644
--- a/smp-angular/src/app/common/components/spacer/spacer.component.ts
+++ b/smp-angular/src/app/common/components/spacer/spacer.component.ts
@@ -2,9 +2,11 @@ import { Component, Input } from '@angular/core';
 
 @Component({
   selector: 'tool-button-spacer',
+  styleUrls: ['./spacer.component.css'],
   template:
-    `<span style="margin-left:5px; width: 10px;border-left: solid black">&nbsp;</span>
+    `<span [ngClass]="vertical?'vertical-spacer':'horizontal-spacer'">&nbsp;</span>
     `
 })
 export class SpacerComponent {
+  @Input() vertical: boolean=true;
 }
diff --git a/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.css b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..e60b7f9d448fca96e25305f31f33adc60c7c8b9d
--- /dev/null
+++ b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.css
@@ -0,0 +1,9 @@
+.mat-form-field {
+  width: 100%;
+}
+
+.property-title {
+  font-size: 1em;
+  word-wrap: break-word;
+  font-weight: bold
+}
diff --git a/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.html b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..f3e078aeb884067fb128b2755702998ba8076bcf
--- /dev/null
+++ b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.html
@@ -0,0 +1,66 @@
+<h2 mat-dialog-title>{{ formTitle }}</h2>
+<mat-dialog-content style="min-height:100px;width:780px">
+  <form [formGroup]="propertyForm">
+    <mat-form-field appearance="fill"
+                    style="width: 100%"
+    >
+      <mat-label>Property name:</mat-label>
+      <input style="width: 100%;padding: 5px"
+             matInput
+             formControlName="property"/>
+      <div
+        *ngIf="propertyForm.controls['property'].hasError('notInList')"
+        style="color:red; font-size: 70%">
+        The property name already exists!
+      </div>
+    </mat-form-field>
+    <mat-form-field
+      *ngIf="propertyForm.controls['type'].value !== PropertyValueTypeEnum.BOOLEAN"
+      appearance="fill" style="width: 100%">
+      <mat-label>Property value:</mat-label>
+      <input style="width: 100%;padding: 5px"
+             matInput [type]="getInputType(propertyForm.controls['type'].value)"
+             formControlName="value"
+      />
+    </mat-form-field>
+    <mat-checkbox
+      *ngIf="propertyForm.controls['type'].value === PropertyValueTypeEnum.BOOLEAN"
+      formControlName="value"
+      style="width: 100%">
+      {{ propertyForm.controls['property'].value }}
+    </mat-checkbox>
+    <mat-form-field appearance="fill" style="width: 100%">
+      <mat-label>Property Description:</mat-label>
+      <input style="width: 100%;padding: 5px"
+             matInput
+             formControlName="desc"
+      />
+
+    </mat-form-field>
+    <mat-form-field appearance="fill" style="width: 100%">
+      <mat-label>Property Type:</mat-label>
+      <mat-select formControlName="type" style="width: 100%">
+        <mat-option *ngFor="let type of propertyTypes" [value]="type">
+          {{ propertyValueTypeDescription(type) }}
+        </mat-option>
+      </mat-select>
+    </mat-form-field>
+  </form>
+</mat-dialog-content>
+
+<mat-dialog-actions>
+  <button id="updatePropertyButton"
+          *ngIf="!isReadOnly"
+          mat-raised-button color="primary" (click)="submitForm()"
+          [disabled]="!isDirty || !propertyForm.valid">
+    <mat-icon>check_circle</mat-icon>
+    <span>OK</span>
+  </button>
+  <button mat-raised-button color="primary" mat-dialog-close>
+    <mat-icon>cancel</mat-icon>
+    <span *ngIf="isReadOnly; else notReadOnly">Close</span>
+    <ng-template #notReadOnly>
+      <span>Cancel</span>
+    </ng-template>
+  </button>
+</mat-dialog-actions>
diff --git a/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.ts b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..67799a5bb4f0dca4139b85a98487cd2beddbd738
--- /dev/null
+++ b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.ts
@@ -0,0 +1,218 @@
+import {Component, Inject} from '@angular/core';
+import {
+  MAT_DIALOG_DATA,
+  MatDialog,
+  MatDialogRef
+} from '@angular/material/dialog';
+import {
+  AbstractControl,
+  UntypedFormBuilder,
+  UntypedFormControl,
+  UntypedFormGroup
+} from "@angular/forms";
+import {
+  AlertMessageService
+} from "../../../common/alert-message/alert-message.service";
+import {EntityStatus} from "../../../common/enums/entity-status.enum";
+import {HttpClient} from "@angular/common/http";
+import {DocumentPropertyRo} from "../../model/document-property-ro.model";
+import {PropertyValueTypeEnum} from "../../enums/property-value-type.enum";
+import {
+  PropertyValueTypeEnumUtil
+} from "../../enums/utils/PropertyValueTypeEnumUtil";
+
+@Component({
+  selector: 'document-property-dialog',
+  templateUrl: './document-property-dialog.component.html',
+  styleUrls: ['./document-property-dialog.component.css']
+})
+export class DocumentPropertyDialogComponent {
+
+  static readonly NEW_MODE: string = 'Create Document Property';
+  static readonly EDIT_MODE: string = 'Edit Document Property';
+  public propertyTypes: string[] = Object.keys(PropertyValueTypeEnum)
+  protected readonly PropertyValueTypeEnum = PropertyValueTypeEnum;
+  formTitle: string;
+  current: DocumentPropertyRo;
+  propertyForm: UntypedFormGroup;
+  disabled: true;
+  private allPropertyNames: string[] = [];
+
+  notInList(list: string[], exception: string) {
+    if (!list || !exception) {
+      return (c: AbstractControl): { [key: string]: any } => {
+        return null;
+      }
+    }
+
+    return (c: AbstractControl): { [key: string]: any } => {
+      if (c.value && c.value !== exception && list.includes(c.value))
+        return {'notInList': {valid: false}};
+      return null;
+    }
+  }
+
+
+  constructor(
+    public dialog: MatDialog,
+    protected http: HttpClient,
+    private dialogRef: MatDialogRef<DocumentPropertyDialogComponent>,
+    private alertService: AlertMessageService,
+    @Inject(MAT_DIALOG_DATA) public data: any,
+    private fb: UntypedFormBuilder) {
+
+    this.current = {...data.row};
+    this.allPropertyNames = data.allPropertyNames;
+
+    this.updateTitle();
+
+    this.propertyForm = fb.group({
+      'property': new UntypedFormControl({value: '', readonly: true}, [
+        this.notInList(this.allPropertyNames, this.current.property)]),
+      'desc': new UntypedFormControl({value: '', readonly: true}, null),
+      'type': new UntypedFormControl({value: '', readonly: true}, null),
+      'value': new UntypedFormControl({value: ''}),
+      'valuePattern': new UntypedFormControl({value: ''}),
+      'errorMessage': new UntypedFormControl({value: ''}),
+    });
+
+    this.propertyForm.controls['errorMessage'].setValue('');
+    this.updateValueState();
+  }
+
+  get isNewItem(): boolean {
+    return this.current?.status === EntityStatus.NEW;
+  }
+
+  get isReadOnly(): boolean {
+    return this.current?.readonly;
+  }
+
+  private updateTitle(): void {
+    this.formTitle = this.isNewItem ? DocumentPropertyDialogComponent.NEW_MODE : DocumentPropertyDialogComponent.EDIT_MODE;
+  }
+
+  /**
+   * Methods validates the property with server validator. If property value is ok
+   * it closes the dialog else writes the errorMessage.
+   */
+  async submitForm() {
+    this.checkValidity(this.propertyForm)
+    this.dialogRef.close(this.getCurrent());
+  }
+
+  checkValidity(g: UntypedFormGroup) {
+    Object.keys(g.controls).forEach(key => {
+      g.get(key).markAsDirty();
+    });
+    Object.keys(g.controls).forEach(key => {
+      g.get(key).markAsTouched();
+    });
+    //!!! updateValueAndValidity - else some filed did no update current / on blur never happened
+    Object.keys(g.controls).forEach(key => {
+      g.get(key).updateValueAndValidity();
+    });
+  }
+
+  /**
+   * Method casts string value to correct property type for dialog component used for editing.
+   * @param value
+   * @param propertyType
+   */
+  public valueFromPropertyStringValue(value: string, propertyType: PropertyValueTypeEnum) {
+    if (propertyType === PropertyValueTypeEnum.BOOLEAN) {
+      return value === 'true';
+    } else {
+      return value;
+    }
+  }
+
+  public valueToPropertyStringValue(value: any, propertyType: PropertyValueTypeEnum) {
+    if (propertyType === PropertyValueTypeEnum.BOOLEAN) {
+      return value ? 'true' : 'false';
+    } else {
+      return value;
+    }
+  }
+
+  getInputType(propertyType: PropertyValueTypeEnum) {
+    console.log("Get input type for row " + PropertyValueTypeEnumUtil.getKeyName(this.current.type))
+    switch (propertyType) {
+      case PropertyValueTypeEnum.STRING:
+      case PropertyValueTypeEnum.LIST_STRING:
+      case PropertyValueTypeEnum.MAP_STRING:
+      case PropertyValueTypeEnum.FILENAME:
+      case PropertyValueTypeEnum.PATH:
+        return 'text';
+      case PropertyValueTypeEnum.INTEGER:
+        return 'text';
+      case PropertyValueTypeEnum.BOOLEAN:
+        return 'checkbox';
+      case PropertyValueTypeEnum.REGEXP:
+        return 'text';
+      case PropertyValueTypeEnum.EMAIL:
+        return 'email';
+      case PropertyValueTypeEnum.URL:
+        return 'url';
+      default:
+        return 'text';
+    }
+  }
+
+  public getCurrent(): DocumentPropertyRo {
+    if (!this.propertyForm.dirty) {
+      return this.current;
+    }
+    if (this.current.status === EntityStatus.NEW) {
+      this.current.property = this.propertyForm.value['property'];
+    } else if (this.current.status === EntityStatus.PERSISTED) {
+      this.current.status = EntityStatus.UPDATED;
+    }
+    this.current.desc = this.propertyForm.value['desc'];
+    this.current.type = this.propertyForm.value['type'];
+    this.current.value = this.valueToPropertyStringValue(this.propertyForm.value['value'], this.current.type);
+    return this.current;
+  }
+
+  closeDialog() {
+    // just close the dialog without any result
+    this.dialogRef.close(null);
+  }
+
+  /**
+   * Method updates the state of the value field based on the system default checkbox.
+   */
+  updateValueState(): void {
+
+    if (!this.isNewItem || this.isReadOnly) {
+      this.propertyForm.controls['property'].disable();
+    } else {
+      this.propertyForm.controls['property'].enable();
+      this.propertyForm.markAsDirty();
+    }
+
+    if (this.isReadOnly) {
+      this.propertyForm.controls['value'].disable();
+      this.propertyForm.controls['desc'].disable();
+      this.propertyForm.controls['type'].disable();
+    } else {
+      this.propertyForm.controls['value'].enable();
+      this.propertyForm.controls['desc'].enable();
+      this.propertyForm.controls['type'].enable();
+    }
+    // update values
+    this.propertyForm.controls['property'].setValue(this.current.property);
+    this.propertyForm.controls['desc'].setValue(this.current.desc);
+    this.propertyForm.controls['type'].setValue(this.current.type);
+    this.propertyForm.controls['value'].setValue(
+      this.valueFromPropertyStringValue(this.current.value, this.current.type));
+  }
+
+  get isDirty(): boolean {
+    return this.propertyForm.dirty;
+  }
+
+  propertyValueTypeDescription(name: string): string {
+    return PropertyValueTypeEnumUtil.getDescription(PropertyValueTypeEnum[name]);
+  }
+}
diff --git a/smp-angular/src/app/common/dialogs/property-details-dialog/property-details-dialog.component.ts b/smp-angular/src/app/common/dialogs/property-details-dialog/property-details-dialog.component.ts
index c71953731331e8d61bf9f3aa32e57a56991fd240..5c64b025cd350695b1395ad019cddf8f0747ee87 100644
--- a/smp-angular/src/app/common/dialogs/property-details-dialog/property-details-dialog.component.ts
+++ b/smp-angular/src/app/common/dialogs/property-details-dialog/property-details-dialog.component.ts
@@ -24,7 +24,7 @@ import {
 import {
   PropertyValidationRo
 } from "../../../system-settings/admin-properties/property-validate-ro.model";
-import {PropertyTypeEnum} from "../../enums/property-type.enum";
+import {PropertySourceEnum} from "../../enums/property-source.enum";
 import {firstValueFrom} from "rxjs";
 
 @Component({
@@ -44,7 +44,7 @@ export class PropertyDetailsDialogComponent implements OnInit {
   propertyForm: UntypedFormGroup;
   disabled: true;
   showSpinner: boolean = false;
-  propertyType: PropertyTypeEnum = PropertyTypeEnum.SYSTEM;
+  propertyType: PropertySourceEnum = PropertySourceEnum.SYSTEM;
 
 
   constructor(
@@ -58,7 +58,7 @@ export class PropertyDetailsDialogComponent implements OnInit {
 
     this.editMode = data.edit;
     this.propertyType = data.propertyType;
-    this.propertyType = !data.propertyType ? PropertyTypeEnum.SYSTEM : data.propertyType;
+    this.propertyType = !data.propertyType ? PropertySourceEnum.SYSTEM : data.propertyType;
     this.formTitle = (this.editMode ? PropertyDetailsDialogComponent.EDIT_MODE : PropertyDetailsDialogComponent.NEW_MODE)
       .replace('{TYPE}', this.capitalize(this.propertyType));
 
@@ -111,7 +111,7 @@ export class PropertyDetailsDialogComponent implements OnInit {
     let request: PropertyRo = this.getCurrent();
 
     // if domain property we do not need to validate
-    if (this.propertyType == PropertyTypeEnum.DOMAIN) {
+    if (this.propertyType == PropertySourceEnum.DOMAIN) {
       this.propertyForm.controls['errorMessage'].setValue("");
       // we can close the dialog
       this.closeDialog();
@@ -258,7 +258,7 @@ export class PropertyDetailsDialogComponent implements OnInit {
   }
 
   get isDomainProperty(): boolean {
-    return this.propertyType == PropertyTypeEnum.DOMAIN;
+    return this.propertyType == PropertySourceEnum.DOMAIN;
   }
 
   capitalize<T extends string>(str: T): string {
diff --git a/smp-angular/src/app/common/enums/property-type.enum.ts b/smp-angular/src/app/common/enums/property-source.enum.ts
similarity index 76%
rename from smp-angular/src/app/common/enums/property-type.enum.ts
rename to smp-angular/src/app/common/enums/property-source.enum.ts
index 95bfa7a7c0d9ccc4b7eeb86a37980d34f43d6f62..6843539905d0c09a4f03793dcd915f62f66e2013 100644
--- a/smp-angular/src/app/common/enums/property-type.enum.ts
+++ b/smp-angular/src/app/common/enums/property-source.enum.ts
@@ -1,5 +1,5 @@
 
-export enum PropertyTypeEnum {
+export enum PropertySourceEnum {
   /**
    * Resource, group of domain is marked as PUBLIC.
    */
diff --git a/smp-angular/src/app/common/enums/property-value-type.enum.ts b/smp-angular/src/app/common/enums/property-value-type.enum.ts
new file mode 100644
index 0000000000000000000000000000000000000000..426c870da61877e2e2916ec83381b86f5e53031a
--- /dev/null
+++ b/smp-angular/src/app/common/enums/property-value-type.enum.ts
@@ -0,0 +1,12 @@
+export enum PropertyValueTypeEnum {
+  STRING = 'STRING',
+  LIST_STRING = 'LIST_STRING',
+  MAP_STRING = 'MAP_STRING',
+  FILENAME = 'FILENAME',
+  PATH = 'PATH',
+  INTEGER = 'INTEGER',
+  BOOLEAN = 'BOOLEAN',
+  REGEXP = 'REGEXP',
+  EMAIL = 'EMAIL',
+  URL = 'URL',
+}
diff --git a/smp-angular/src/app/common/enums/utils/PropertyValueTypeEnumUtil.ts b/smp-angular/src/app/common/enums/utils/PropertyValueTypeEnumUtil.ts
new file mode 100644
index 0000000000000000000000000000000000000000..56065cde7adebbd72496bb2f10ded129ff6aa201
--- /dev/null
+++ b/smp-angular/src/app/common/enums/utils/PropertyValueTypeEnumUtil.ts
@@ -0,0 +1,99 @@
+import {PropertyValueTypeEnum} from "../property-value-type.enum";
+
+/**
+ *  Utility class for PropertyValueTypeEnum to return description, regular expression
+ *  and input type for each enum value.
+ *
+ *  @since 5.1
+ *  @author Joze Rihtarsic
+ */
+export class PropertyValueTypeEnumUtil {
+
+
+  static getKeyNames(): Array<string> {
+    return Object.keys(PropertyValueTypeEnum).filter(k => typeof PropertyValueTypeEnum[k as any] === "number");
+  }
+
+  static getKeyName(enumItem: PropertyValueTypeEnum): string {
+    return PropertyValueTypeEnum[enumItem];
+  }
+
+  static getDescription(enumItem: PropertyValueTypeEnum): string {
+    console.log("Get description for row " + enumItem)
+    switch (enumItem) {
+      case PropertyValueTypeEnum.STRING:
+        return 'String';
+      case PropertyValueTypeEnum.LIST_STRING:
+        return 'List of strings';
+      case PropertyValueTypeEnum.MAP_STRING:
+        return 'Map of strings';
+      case PropertyValueTypeEnum.FILENAME:
+        return 'Filename';
+      case PropertyValueTypeEnum.PATH:
+        return 'Path';
+      case PropertyValueTypeEnum.INTEGER:
+        return 'Integer';
+      case PropertyValueTypeEnum.BOOLEAN:
+        return 'Boolean';
+      case PropertyValueTypeEnum.REGEXP:
+        return 'Regular expression';
+      case PropertyValueTypeEnum.EMAIL:
+        return 'Email';
+      case PropertyValueTypeEnum.URL:
+        return 'URL';
+      default:
+        return '';
+    }
+  }
+
+  static getInputType(propertyType: PropertyValueTypeEnum): string {
+    console.log("Get input type for row " + propertyType)
+    switch (propertyType) {
+      case PropertyValueTypeEnum.STRING:
+      case PropertyValueTypeEnum.LIST_STRING:
+      case PropertyValueTypeEnum.MAP_STRING:
+      case PropertyValueTypeEnum.FILENAME:
+      case PropertyValueTypeEnum.PATH:
+      case PropertyValueTypeEnum.REGEXP:
+        return 'text';
+      case PropertyValueTypeEnum.INTEGER:
+        return 'number';
+      case PropertyValueTypeEnum.BOOLEAN:
+        return 'checkbox';
+      case PropertyValueTypeEnum.EMAIL:
+        return 'email';
+      case PropertyValueTypeEnum.URL:
+        return 'url';
+      default:
+        return 'text';
+    }
+  }
+
+  static getRegExp(enumItem: PropertyValueTypeEnum): RegExp {
+    console.log("Get input pattern for row " + enumItem)
+    switch (enumItem) {
+      case PropertyValueTypeEnum.STRING:
+      case PropertyValueTypeEnum.LIST_STRING:
+      case PropertyValueTypeEnum.MAP_STRING:
+      case PropertyValueTypeEnum.FILENAME:
+        return /.*/
+      case PropertyValueTypeEnum.PATH:
+        return /^(.+)\/([^\/]+)$/;
+      case PropertyValueTypeEnum.INTEGER:
+        return /^-?\d+$/;
+      case PropertyValueTypeEnum.BOOLEAN:
+        return /^(true|false)$/i;
+      case PropertyValueTypeEnum.REGEXP:
+        // This regular expression checks if a string could be a valid RegExp
+        return /^\/(.*)\/([gimuy]*)$/;
+      case PropertyValueTypeEnum.EMAIL:
+        return /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/;
+      case PropertyValueTypeEnum.URL:
+        return /^(http|https):\/\/[^ "]+$/;
+      default:
+        return null;
+    }
+  }
+
+
+}
diff --git a/smp-angular/src/app/common/model/document-property-ro.model.ts b/smp-angular/src/app/common/model/document-property-ro.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e07792de6a9493eb96a30999dd140c3a84ae7156
--- /dev/null
+++ b/smp-angular/src/app/common/model/document-property-ro.model.ts
@@ -0,0 +1,33 @@
+/*-
+ * #START_LICENSE#
+ * smp-webapp
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+import {SearchTableEntity} from '../../common/search-table/search-table-entity.model';
+import {PropertyValueTypeEnum} from "../enums/property-value-type.enum";
+
+/**
+ * Document property read only model.
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+export interface DocumentPropertyRo extends SearchTableEntity {
+  property: string;
+  value: string;
+  type?: PropertyValueTypeEnum;
+  desc: string;
+  readonly?: boolean;
+}
diff --git a/smp-angular/src/app/common/model/document-ro.model.ts b/smp-angular/src/app/common/model/document-ro.model.ts
index 737e367c0cbfbe757a73b763487ab8f2311afeae..cf0a058179bbb01d17fe30b2a2c6864f25314888 100644
--- a/smp-angular/src/app/common/model/document-ro.model.ts
+++ b/smp-angular/src/app/common/model/document-ro.model.ts
@@ -1,4 +1,8 @@
-export interface DocumentRo {
+import {DocumentPropertyRo} from "./document-property-ro.model";
+import {SearchTableEntity} from "../search-table/search-table-entity.model";
+import {EntityStatus} from "../enums/entity-status.enum";
+
+export interface DocumentRo extends SearchTableEntity  {
   mimeType?: string;
   name?: string;
   documentId?: string;
@@ -7,5 +11,7 @@ export interface DocumentRo {
   payloadVersion?:number;
   payloadCreatedOn?: Date;
   payload?:string;
+  payloadStatus: EntityStatus;
+  properties?: DocumentPropertyRo[];
 }
 
diff --git a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.html b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..f1668a1f42a1104e9200ff53864ac609ec6fde8e
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.html
@@ -0,0 +1,79 @@
+<div style="display: flex;flex-direction: row;height: 100%">
+  <div *ngIf="showPropertyPanel" id="document-properties-panel">
+    <div style="display:flex; flex-grow: 1; flex-direction: column">
+      <mat-form-field style="width: 100%">
+        <mat-label>Filter</mat-label>
+        <input matInput (keyup)="applyFilter($event)" #input>
+      </mat-form-field>
+      <div id="property-table-container">
+        <table #DocumentPropertyTable mat-table class="mat-elevation-z1"
+               style="max-height: 100%;flex-grow: 1;"
+               id="document-property-table"
+               [dataSource]="propertyDataSource" matSort>
+
+          <ng-container matColumnDef="property">
+            <th mat-header-cell *matHeaderCellDef mat-sort-header>Property</th>
+            <td mat-cell *matCellDef="let row"
+                [matTooltip]="row.desc">{{ row.property }}
+            </td>
+          </ng-container>
+          <ng-container matColumnDef="value">
+            <th mat-header-cell *matHeaderCellDef mat-sort-header>Value</th>
+            <td mat-cell *matCellDef="let row">{{ getTableRowValue(row) }}</td>
+          </ng-container>
+          <tr mat-header-row
+              *matHeaderRowDef="displayedColumns; sticky:true"></tr>
+          <tr mat-row
+              *matRowDef="let odd = odd; let row; columns: displayedColumns;"
+              (click)="propertySelected(row)"
+              (dblclick)="onEditSelectedProperty()"
+              [ngClass]="getRowClass(row, odd)"
+          ></tr>
+          <tr class="mat-row" *matNoDataRow>
+            <td class="mat-cell" colspan="2">No Document properties found</td>
+          </tr>
+        </table>
+      </div>
+      <mat-paginator [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons
+                     [pageSize]="10" [length]="propertyDataSource.data?.length"
+                     aria-label="Select page of properties"></mat-paginator>
+
+    </div>
+  </div>
+  <!-- table Toolbar -->
+  <div style="width: 42px">
+    <button mat-mini-fab aria-label="Expand/Collapse"
+            matTooltip="Expand/Collapse property panel"
+            (click)="onToggleButtonClicked()">
+      <mat-icon *ngIf="showPropertyPanel">chevron_left</mat-icon>
+      <mat-icon *ngIf="!showPropertyPanel">chevron_right</mat-icon>
+    </button>
+    <tool-button-spacer [vertical]="false"></tool-button-spacer>
+    <button id="createButton" mat-mini-fab aria-label="Create"
+            matTooltip="Create new property"
+            (click)="onCreateProperty()">
+      <mat-icon>add</mat-icon>
+    </button>
+    <button id="editButton" mat-mini-fab aria-label="Edit"
+            matTooltip="Edit property"
+            (click)="onEditSelectedProperty()"
+            [disabled]="editButtonDisabled">
+      <mat-icon>edit</mat-icon>
+    </button>
+    <button id="deleteButton" mat-mini-fab aria-label="Delete/Remove"
+            matTooltip="Delete selected property"
+            (click)="onDeleteSelectedProperty()"
+            [disabled]="deleteButtonDisabled"
+    >
+      <mat-icon>remove</mat-icon>
+    </button>
+    <tool-button-spacer [vertical]="false"></tool-button-spacer>
+    <button id="resetButton" mat-mini-fab aria-label="Reset changes"
+            matTooltip="Reset changes"
+            (click)="onResetButtonClicked()"
+            color="primary"
+            [disabled]="!cancelButtonEnabled">
+      <mat-icon>refresh</mat-icon>
+    </button>
+  </div>
+</div>
diff --git a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.scss b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..9f1ad60fe59831908890ef23303b065d3471e78d
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.scss
@@ -0,0 +1,39 @@
+#document-properties-panel {
+  display: flex;
+  flex-direction: column;
+  flex-grow: 1;
+  padding: 0 1em;
+  min-width: 600px;
+}
+
+#property-table-container {
+  position: relative;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  verflow-y: scroll;
+}
+
+#property-table-container table {
+  width: 100%;
+}
+
+
+.deleted {
+  text-decoration: line-through !important;
+  font-weight: bold;
+}
+
+.table-row-new {
+  color: darkgreen !important;
+  font-weight: bold;
+}
+
+.table-row-updated {
+  font-weight: bold;
+}
+
+.table-row {
+  font-weight: normal;
+}
diff --git a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.ts b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3e067286b9a3ad539e3ebf02ba679208dea2c416
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.ts
@@ -0,0 +1,318 @@
+/*-
+ * #START_LICENSE#
+ * smp-webapp
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+import {
+  AfterViewInit,
+  Component,
+  forwardRef,
+  Input,
+  ViewChild,
+} from '@angular/core';
+import {
+  BeforeLeaveGuard
+} from "../../../window/sidenav/navigation-on-leave-guard";
+import {DomainPropertyRo} from "../../../common/model/domain-property-ro.model";
+import {MatTable, MatTableDataSource} from "@angular/material/table";
+import {EntityStatus} from "../../../common/enums/entity-status.enum";
+import {
+  ControlContainer,
+  ControlValueAccessor,
+  FormControl,
+  FormControlDirective,
+  NG_VALUE_ACCESSOR
+} from "@angular/forms";
+import {DocumentPropertyRo} from "../../model/document-property-ro.model";
+import {MatSort} from "@angular/material/sort";
+import {MatDialog, MatDialogRef} from "@angular/material/dialog";
+import {
+  DocumentPropertyDialogComponent
+} from "../../dialogs/document-property-dialog/document-property-dialog.component";
+import {PropertyValueTypeEnum} from "../../enums/property-value-type.enum";
+import {MatPaginator} from "@angular/material/paginator";
+
+/**
+ * Component to display the properties of a document in a table. The properties can be edited and saved.
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+@Component({
+  selector: 'document-properties-panel',
+  templateUrl: './document-properties-panel.component.html',
+  styleUrls: ['./document-properties-panel.component.scss'],
+  providers: [
+    {
+      provide: NG_VALUE_ACCESSOR,
+      useExisting: forwardRef(() => DocumentPropertiesPanelComponent),
+      multi: true
+    }
+  ]
+})
+export class DocumentPropertiesPanelComponent implements AfterViewInit, BeforeLeaveGuard, ControlValueAccessor {
+  private readonly NEW_PROPERTY_NAME_TEMPLATE: string = 'document.property.v';
+  displayedColumns: string[] = ['property', 'value'];
+  private onChangeCallback: (_: any) => void = () => {
+  };
+  selected?: DocumentPropertyRo = null;
+  dataChanged: boolean = false
+  showPropertyPanel: boolean = true;
+  initPropertyList: DocumentPropertyRo[] = [];
+  propertyDataSource: MatTableDataSource<DocumentPropertyRo> = new MatTableDataSource();
+
+  @ViewChild("DocumentPropertyTable") table: MatTable<DocumentPropertyRo>;
+  @ViewChild(MatPaginator) paginator: MatPaginator
+  @ViewChild(MatSort) sort: MatSort;
+
+  constructor(public dialog: MatDialog, private controlContainer: ControlContainer) {
+
+  }
+
+  @ViewChild(FormControlDirective, {static: true})
+  formControlDirective: FormControlDirective;
+  @Input()
+  formControl: FormControl;
+
+  @Input()
+  formControlName: string;  /* get hold of FormControl instance no matter formControl or    formControlName is given. If formControlName is given, then this.controlContainer.control is the parent FormGroup (or FormArray) instance. */
+  get control() {
+    return this.formControl || this.controlContainer.control.get(this.formControlName);
+  }
+
+
+  ngAfterViewInit() {
+    this.propertyDataSource.paginator = this.paginator;
+    this.propertyDataSource.sort = this.sort;
+  }
+
+  applyFilter(event: Event) {
+    const filterValue: string = (event.target as HTMLInputElement).value;
+    this.propertyDataSource.filter = filterValue?.trim().toLowerCase();
+
+    if (this.propertyDataSource.paginator) {
+      this.propertyDataSource.paginator.firstPage();
+    }
+  }
+
+  get saveButtonEnabled(): boolean {
+    return this.dataChanged;
+  }
+
+  get cancelButtonEnabled(): boolean {
+    return this.dataChanged;
+  }
+
+  public onToggleButtonClicked(): void {
+    this.showPropertyPanel = !this.showPropertyPanel;
+  }
+
+  /**
+   * Method created a new property and opens the dialog to edit it.
+   * After the dialog is closed with 'Save' button, the new property is added
+   * to the list of properties.
+   */
+  public onCreateProperty(): void {
+    const newProperty: DocumentPropertyRo = {
+      property: this.uniquePropertyName,
+      value: "",
+      type: PropertyValueTypeEnum.STRING,
+      desc: "",
+      status: EntityStatus.NEW,
+      readonly: false
+    };
+    this.editSelectedProperty(newProperty).afterClosed()
+      .subscribe((result): void => {
+        if (result) {
+          this.addNewProperty(result);
+        }
+      });
+  }
+
+  /**
+   * Method returns unique property name. The method checks all properties in the list and
+   * creates a new name with index if the name is already used.
+   */
+  get uniquePropertyName(): string {
+    let propertyNames: string[] = this.allPropertyNames;
+    // create index and check "my.property.[index]" until we find unique name
+    // and format index with 3 digits
+    let index: number = 1;
+    let propertyName: string = this.NEW_PROPERTY_NAME_TEMPLATE + index.toString().padStart(3, '0');
+
+    while (propertyNames.includes(propertyName)) {
+      index++;
+      propertyName = this.NEW_PROPERTY_NAME_TEMPLATE + index.toString().padStart(3, '0');
+    }
+    return propertyName
+  }
+
+  private addNewProperty(newProperty: DocumentPropertyRo) {
+    this.propertyDataSource.data.push(newProperty);
+
+    // to trigger refresh . render rows does not work on angular 16
+    this.propertyDataSource.data = [...this.propertyDataSource.data];
+    if (this.propertyDataSource.paginator) {
+      this.propertyDataSource.paginator.lastPage();
+    }
+    this.selected = newProperty;
+    this.dataChanged = true;
+    this.onChangeCallback(this.propertyDataSource.data);
+  }
+
+  public onEditSelectedProperty(): void {
+    if (!this.selected) {
+      return;
+    }
+
+    this.editSelectedProperty(this.selected).afterClosed()
+      .subscribe((result): void => {
+        if (result) {
+          let indexToUpdate = this.propertyDataSource.data
+            .findIndex(item => item.property === result.property);
+
+          let property: DocumentPropertyRo = this.equals(this.initPropertyList[indexToUpdate], result) ?
+            this.initPropertyList[indexToUpdate] : result;
+
+
+          this.propertyDataSource.data[indexToUpdate] = property;
+          // trigger reload
+          this.propertyDataSource.data = [...this.propertyDataSource.data];
+          this.selected = property;
+          this.dataChanged = true;
+          this.onChangeCallback(this.propertyDataSource.data);
+        }
+      });
+  }
+
+  /**
+   * Method compares two DocumentPropertyRo objects and returns true if they are equal. The method
+   * validates property name, value, type and description.
+   * @param value1
+   * @param value2
+   */
+  private equals(value1: DocumentPropertyRo, value2: DocumentPropertyRo): boolean {
+    return value1.property === value2.property
+      && value1.value === value2.value
+      && value1.desc === value2.desc
+      && value1.type === value2.type;
+  }
+
+  public onDeleteSelectedProperty(): void {
+    if (!this.selected) {
+      return;
+    }
+    let properties = this.propertyDataSource.data;
+
+    if (this.selected.status === EntityStatus.NEW) {
+      const index: number = properties.indexOf(this.selected);
+      if (index !== -1) {
+        properties.splice(index, 1);
+      }
+    } else {
+      this.selected.status = EntityStatus.REMOVED;
+      this.selected.deleted = true;
+    }
+    // to trigger refresh
+    this.propertyDataSource.data = [...properties];
+
+    this.onChangeCallback(this.propertyDataSource.data);
+  }
+
+
+  public onSaveButtonClicked() {
+    // submit list of properties to the backend
+
+  }
+
+  /*
+    * reset/reload properties from the server
+   */
+  public onResetButtonClicked(): void {
+    this.control.setValue(this.initPropertyList);
+    this.control.markAsPristine();
+  }
+
+  /**
+   * return the value to be displayed in the table row. If the row is updated and not system default,
+   * then display the new value else display the old value
+   * @param row
+   */
+  getTableRowValue(row: DomainPropertyRo) {
+    return row.systemDefault ? row.systemDefaultValue : row.value;
+  }
+
+  isDirty(): boolean {
+    return this.dataChanged;
+  }
+
+  propertySelected(property: DocumentPropertyRo) {
+    this.selected = property;
+  }
+
+  get editButtonDisabled(): boolean {
+    return !this.selected;
+  }
+
+  get deleteButtonDisabled(): boolean {
+    return !this.selected || this.selected.readonly;
+  }
+
+  getRowClass(row, oddRow: boolean) {
+    return {
+      'datatable-row-selected': row === this.selected,
+      'table-row-new': (row.status === EntityStatus.NEW),
+      'table-row-updated': (row.status === EntityStatus.UPDATED),
+      'deleted': (row.status === EntityStatus.REMOVED),
+      'datatable-row-odd': oddRow
+    };
+  }
+
+  registerOnChange(fn: any): void {
+    this.onChangeCallback = fn;
+
+  }
+
+
+  registerOnTouched(fn: any): void {
+  }
+
+  setDisabledState(isDisabled: boolean): void {
+  }
+
+  /**
+   * Implementation of the ControlValueAccessor method to  write value to the component.
+   * @param propertyList
+   */
+  writeValue(propertyList: DocumentPropertyRo[]): void {
+    this.initPropertyList = propertyList;
+    this.propertyDataSource.data = !propertyList?.length ? [] : [...propertyList];
+    this.dataChanged = false;
+  }
+
+  public editSelectedProperty(row: DocumentPropertyRo): MatDialogRef<any> {
+    return this.dialog.open(DocumentPropertyDialogComponent, {
+      data: {
+        editMode: row.status,
+        row: row,
+        allPropertyNames: this.allPropertyNames
+      }
+    });
+  }
+
+  get allPropertyNames(): string[] {
+    return this.propertyDataSource.data.map(item => item.property);
+  }
+}
diff --git a/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts b/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts
index 716901eb6914eb853e068be65c2dda343076329e..a6f65dc6942e8d0b8fe1f467cd600546993c01b2 100644
--- a/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts
+++ b/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts
@@ -12,7 +12,6 @@ import {MatTabGroup} from "@angular/material/tabs";
 import {MemberTypeEnum} from "../../common/enums/member-type.enum";
 import {firstValueFrom} from "rxjs";
 
-
 @Component({
   templateUrl: './edit-domain.component.html',
   styleUrls: ['./edit-domain.component.css']
diff --git a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html
index 938b1184cf17a13a29997a365a4c2bbe13a7ca00..bef883a2d4bd931640c501a670838f0c26e476e9 100644
--- a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html
+++ b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html
@@ -1,7 +1,8 @@
 <div id="resource-document-panel">
 
   <mat-toolbar class="mat-elevation-z2" style="min-height: 50px !important;">
-    <mat-toolbar-row class="smp-toolbar-row" style="justify-content: space-between;min-height: 50px !important;">
+    <mat-toolbar-row class="smp-toolbar-row"
+                     style="justify-content: space-between;min-height: 50px !important;">
 
       <button id="validateResource_id" mat-raised-button
               color="primary"
@@ -35,7 +36,8 @@
       <div [formGroup]="documentForm"
            style="float: right;  vertical-align:middle;display: flex;align-items: center;justify-content: center; gap:0.4em;padding-right: 10px">
         <span style="font-size: 0.8em">Show version:</span>
-        <select matNativeControl style="width: 100px; border-bottom: grey solid 1px"
+        <select matNativeControl
+                style="width: 100px; border-bottom: grey solid 1px"
                 placeholder="All document version"
                 matTooltip="Select version to display."
                 formControlName="payloadVersion"
@@ -45,7 +47,7 @@
           <option *ngFor="let version of getDocumentVersions"
                   [value]="version"
           >
-            {{version}}
+            {{ version }}
           </option>
         </select>
 
@@ -56,62 +58,52 @@
                readonly>
         <mat-datepicker-toggle matSuffix [for]="payloadCreatedOnPicker"
                                style="visibility: hidden"></mat-datepicker-toggle>
-        <ngx-mat-datetime-picker #payloadCreatedOnPicker [showSpinners]="true" [showSeconds]="false"
+        <ngx-mat-datetime-picker #payloadCreatedOnPicker [showSpinners]="true"
+                                 [showSeconds]="false"
                                  [hideTime]="false"></ngx-mat-datetime-picker>
 
       </div>
     </mat-toolbar-row>
   </mat-toolbar>
-  <div class="panel">
 
-    <div style="display: flex;flex-direction: row;width: 100%">
-      <smp-titled-label style="flex-grow: 1" title="Resource identifier:"
-                        value="{{resource?.identifierValue}}"></smp-titled-label>
-      <smp-titled-label style="flex-grow: 1" title="Resource scheme:"
-                        value="{{resource?.identifierScheme}}"></smp-titled-label>
-    </div>
-    <div style="display: flex;flex-direction: row;width: 100%">
-      <smp-titled-label style="flex-grow: 1" title="Document name:" value="{{_document?.name}}"></smp-titled-label>
-      <smp-titled-label style="flex-grow: 1" title="Document mimeType:"
-                        value="{{_document?.mimeType}}"></smp-titled-label>
-      <smp-titled-label style="flex-grow: 1" title="Current document version:"
-                        value="{{_document?.currentResourceVersion}}"></smp-titled-label>
-    </div>
-  </div>
+  <div class="panel" [formGroup]="documentForm"
+       style="display: flex;flex-direction: row;flex-grow: 1; ">
 
-  <div [formGroup]="documentForm" style="width: 100%; display: flex; flex-direction: column; flex:1; gap:0.5em ">
     <div
       style="display:block; overflow: auto;flex: 2;align-self: stretch; flex-direction: column;border: ridge 3px #b0bec5"
       (click)="onEditPanelClick()"
     >
-      <smp-editor #smpDocumentEditor
-                  formControlName="payload"
-                  ngDefaultControl
-      ></smp-editor>
+      <smp-editor #smpDocumentEditor formControlName="payload"
+                  ngDefaultControl></smp-editor>
     </div>
-    <mat-toolbar class="mat-elevation-z2" style="flex-grow: 0">
-      <mat-toolbar-row class="smp-toolbar-row">
-        <button id="back_id" mat-raised-button color="primary"
-                (click)="onBackButtonClicked()">
-          <mat-icon>arrow_circle_left</mat-icon>
-          <span>Back</span>
-        </button>
-        <button id="cancel_id" mat-raised-button color="primary"
-                [disabled]="cancelButtonDisabled"
-                (click)="onDocumentResetButtonClicked()">
-          <mat-icon>cancel</mat-icon>
-          <span>Cancel</span>
-        </button>
-        <button id="saveResource_id" mat-raised-button
-                color="primary"
-                matTooltip="Validate resource"
-                [disabled]="saveButtonDisabled"
-                (click)="onSaveButtonClicked()"
-        >
-          <mat-icon>save</mat-icon>
-          <span>Save</span>
-        </button>
-      </mat-toolbar-row>
-    </mat-toolbar>
+    <document-properties-panel style="min-height: 0; overflow: auto"
+                               formControlName="properties"
+                               ngDefaultControl></document-properties-panel>
   </div>
+
+  <mat-toolbar class="mat-elevation-z2"
+               style="flex-grow: 0;">
+    <mat-toolbar-row class="smp-toolbar-row">
+      <button id="back_id" mat-raised-button color="primary"
+              (click)="onBackButtonClicked()">
+        <mat-icon>arrow_circle_left</mat-icon>
+        <span>Back</span>
+      </button>
+      <button id="cancel_id" mat-raised-button color="primary"
+              [disabled]="cancelButtonDisabled"
+              (click)="onDocumentResetButtonClicked()">
+        <mat-icon>cancel</mat-icon>
+        <span>Cancel</span>
+      </button>
+      <button id="saveResource_id" mat-raised-button
+              color="primary"
+              matTooltip="Validate resource"
+              [disabled]="saveButtonDisabled"
+              (click)="onSaveButtonClicked()"
+      >
+        <mat-icon>save</mat-icon>
+        <span>Save</span>
+      </button>
+    </mat-toolbar-row>
+  </mat-toolbar>
 </div>
diff --git a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts
index 1b03dd752868ef015d278cf9736519bce601a6a8..f40e7e9e5debdd2307d01269e762b7f53bcd27b1 100644
--- a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts
+++ b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts
@@ -1,18 +1,33 @@
-import {AfterViewInit, Component, Input, ViewChild, ViewEncapsulation,} from '@angular/core';
+import {Component, Input, ViewChild, ViewEncapsulation,} from '@angular/core';
 import {MatDialog, MatDialogRef} from "@angular/material/dialog";
-import {BeforeLeaveGuard} from "../../../window/sidenav/navigation-on-leave-guard";
+import {
+  BeforeLeaveGuard
+} from "../../../window/sidenav/navigation-on-leave-guard";
 import {GroupRo} from "../../../common/model/group-ro.model";
 import {ResourceRo} from "../../../common/model/resource-ro.model";
-import {AlertMessageService} from "../../../common/alert-message/alert-message.service";
+import {
+  AlertMessageService
+} from "../../../common/alert-message/alert-message.service";
 import {DomainRo} from "../../../common/model/domain-ro.model";
-import {ResourceDefinitionRo} from "../../../system-settings/admin-extension/resource-definition-ro.model";
+import {
+  ResourceDefinitionRo
+} from "../../../system-settings/admin-extension/resource-definition-ro.model";
 import {EditResourceService} from "../edit-resource.service";
 import {FormBuilder, FormControl, FormGroup} from "@angular/forms";
 import {DocumentRo} from "../../../common/model/document-ro.model";
-import {NavigationService} from "../../../window/sidenav/navigation-model.service";
-import {DocumentWizardDialogComponent} from "../document-wizard-dialog/document-wizard-dialog.component";
-import {ConfirmationDialogComponent} from "../../../common/dialogs/confirmation-dialog/confirmation-dialog.component";
-import {SmpEditorComponent} from "../../../common/components/smp-editor/smp-editor.component";
+import {
+  NavigationService
+} from "../../../window/sidenav/navigation-model.service";
+import {
+  DocumentWizardDialogComponent
+} from "../document-wizard-dialog/document-wizard-dialog.component";
+import {
+  ConfirmationDialogComponent
+} from "../../../common/dialogs/confirmation-dialog/confirmation-dialog.component";
+import {
+  SmpEditorComponent
+} from "../../../common/components/smp-editor/smp-editor.component";
+import {EntityStatus} from "../../../common/enums/entity-status.enum";
 
 @Component({
   templateUrl: './resource-document-panel.component.html',
@@ -52,6 +67,7 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard {
       'payloadCreatedOn': new FormControl({value: null}),
       'payloadVersion': new FormControl({value: null}),
       'payload': new FormControl({value: null}),
+      'properties': new FormControl({value: null}),
     });
     this.resource = editResourceService.selectedResource
 
@@ -94,7 +110,6 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard {
     this._document = value;
     this.documentForm.disable();
     if (!!value) {
-      console.log("Document: " + value.payload)
       this.documentEditor.mimeType = value.mimeType;
       this.documentForm.controls['mimeType'].setValue(value.mimeType);
       this.documentForm.controls['name'].setValue(value.name);
@@ -103,6 +118,7 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard {
       this.documentForm.controls['payloadCreatedOn'].setValue(value.payloadCreatedOn);
       this.documentForm.controls['payload'].setValue(value.payload);
       this.documentForm.controls['payload'].enable();
+      this.documentForm.controls['properties'].setValue(value.properties);
       // the method documentVersionsExists already uses the current value to check if versions exists
       if (!this.documentVersionsExists) {
         this.documentForm.controls['payloadVersion'].disable();
@@ -116,13 +132,18 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard {
       this.documentForm.controls['payloadVersion'].setValue("");
       this.documentForm.controls['payloadCreatedOn'].setValue("");
       this.documentForm.controls['payload'].setValue("");
+      this.documentForm.controls['properties'].setValue([]);
     }
     this.documentForm.markAsPristine();
   }
 
   get document(): DocumentRo {
     let doc: DocumentRo = {...this._document};
-    doc.payload = this.documentForm.controls['payload'].value;
+    if(this.documentForm.controls['payload'].dirty){
+      doc.payload = this.documentForm.controls['payload'].value;
+      doc.payloadStatus = EntityStatus.UPDATED;
+    }
+    doc.properties = this.documentForm.controls['properties'].value;
     return doc;
   }
 
@@ -156,6 +177,7 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard {
 
 
   onSaveButtonClicked(): void {
+
     this.editResourceService.saveDocumentObservable(this._resource, this.document).subscribe((value: DocumentRo) => {
       if (value) {
         this.alertService.success("Document is saved with current version [" + value.currentResourceVersion + "].")
@@ -265,9 +287,3 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard {
     return this._resource?.resourceTypeIdentifier === 'edelivery-oasis-smp-1.0-servicegroup';
   }
 }
-
-
-
-
-
-
diff --git a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html
index 3dcbe7efcc2a1e413c4001488ef23537c4f8885f..6a02eb7d2ff642e567db13de5a8d27ab24020fb5 100644
--- a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html
+++ b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html
@@ -58,62 +58,44 @@
       </div>
     </mat-toolbar-row>
   </mat-toolbar>
-  <div class="panel">
+  <div class="panel" [formGroup]="documentForm"
+       style="display: flex;flex-direction: row;flex-grow: 1; ">
 
-    <div style="display: flex;flex-direction: row;width: 100%">
-      <smp-titled-label style="flex-grow: 1" title="Resource identifier:"
-                        value="{{resource?.identifierValue}}"></smp-titled-label>
-      <smp-titled-label style="flex-grow: 1" title="Resource scheme:"
-                        value="{{resource?.identifierScheme}}"></smp-titled-label>
-    </div>
-    <div style="display: flex;flex-direction: row;width: 100%">
-      <smp-titled-label style="flex-grow: 1" title="Subresource identifier:"
-                        value="{{subresource?.identifierValue}}"></smp-titled-label>
-      <smp-titled-label style="flex-grow: 1" title="Subresource scheme:"
-                        value="{{subresource?.identifierScheme}}"></smp-titled-label>
-    </div>
-    <div style="display: flex;flex-direction: row;width: 100%">
-      <smp-titled-label style="flex-grow: 1" title="Document name:" value="{{_document?.name}}"></smp-titled-label>
-      <smp-titled-label style="flex-grow: 1" title="Document mimeType:"
-                        value="{{_document?.mimeType}}"></smp-titled-label>
-      <smp-titled-label style="flex-grow: 1" title="Current document version:"
-                        value="{{_document?.currentResourceVersion}}"></smp-titled-label>
-    </div>
-  </div>
-
-  <div [formGroup]="documentForm" style="width: 100%; display: flex; flex-direction: column; flex:1; gap:0.5em ">
     <div
-      style="display:block; overflow: auto;flex: 2;align-self: stretch; flex-direction: column;background-color: #FCFCFCBB; border: ridge 3px #b0bec5"
+      style="display:block; overflow: auto;flex: 2;align-self: stretch; flex-direction: column;border: ridge 3px #b0bec5"
       (click)="onEditPanelClick()"
     >
-      <smp-editor #smpDocumentEditor
-                  formControlName="payload"
-                  ngDefaultControl
-      ></smp-editor>
+      <smp-editor #smpDocumentEditor formControlName="payload"
+                  ngDefaultControl></smp-editor>
     </div>
-    <mat-toolbar class="mat-elevation-z2" style="flex-grow: 0">
-      <mat-toolbar-row class="smp-toolbar-row">
-        <button id="back_id" mat-raised-button color="primary"
-                (click)="onBackButtonClicked()">
-          <mat-icon>arrow_circle_left</mat-icon>
-          <span>Back</span>
-        </button>
-        <button id="cancel_id" mat-raised-button color="primary"
-                [disabled]="cancelButtonDisabled"
-                (click)="onDocumentResetButtonClicked()">
-          <mat-icon>cancel</mat-icon>
-          <span>Cancel</span>
-        </button>
-        <button id="saveResource_id" mat-raised-button
-                color="primary"
-                matTooltip="Validate resource"
-                [disabled]="saveButtonDisabled"
-                (click)="onSaveButtonClicked()"
-        >
-          <mat-icon>save</mat-icon>
-          <span>Save</span>
-        </button>
-      </mat-toolbar-row>
-    </mat-toolbar>
+    <document-properties-panel style="min-height: 0; overflow: auto"
+                               formControlName="properties"
+                               ngDefaultControl></document-properties-panel>
   </div>
+
+  <mat-toolbar class="mat-elevation-z2"
+               style="flex-grow: 0;">
+    <mat-toolbar-row class="smp-toolbar-row">
+      <button id="back_id" mat-raised-button color="primary"
+              (click)="onBackButtonClicked()">
+        <mat-icon>arrow_circle_left</mat-icon>
+        <span>Back</span>
+      </button>
+      <button id="cancel_id" mat-raised-button color="primary"
+              [disabled]="cancelButtonDisabled"
+              (click)="onDocumentResetButtonClicked()">
+        <mat-icon>cancel</mat-icon>
+        <span>Cancel</span>
+      </button>
+      <button id="saveResource_id" mat-raised-button
+              color="primary"
+              matTooltip="Validate resource"
+              [disabled]="saveButtonDisabled"
+              (click)="onSaveButtonClicked()"
+      >
+        <mat-icon>save</mat-icon>
+        <span>Save</span>
+      </button>
+    </mat-toolbar-row>
+  </mat-toolbar>
 </div>
diff --git a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts
index 37990409b684fa6c1fe9a8c112f6d720ad2cf2fd..e063630daf2b90df08eb78fbf9a116bb0f93d2aa 100644
--- a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts
+++ b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts
@@ -19,6 +19,7 @@ import {
 } from "../subresource-document-wizard-dialog/subresource-wizard-edit-ro.model";
 import {ConfirmationDialogComponent} from "../../../common/dialogs/confirmation-dialog/confirmation-dialog.component";
 import {SmpEditorComponent} from "../../../common/components/smp-editor/smp-editor.component";
+import {EntityStatus} from "../../../common/enums/entity-status.enum";
 
 @Component({
   templateUrl: './subresource-document-panel.component.html',
@@ -66,6 +67,7 @@ export class SubresourceDocumentPanelComponent implements AfterViewInit, BeforeL
       'payloadVersion': new FormControl({value: null}),
       'payloadCreatedOn': new FormControl({value: null}),
       'payload': new FormControl({value: null}),
+      'properties': new FormControl({value: null}),
     });
     this.documentForm.controls['payload'].setValue("")
 
@@ -133,7 +135,8 @@ export class SubresourceDocumentPanelComponent implements AfterViewInit, BeforeL
   @Input() set document(value: DocumentRo) {
     this._document = value;
     this.documentForm.disable();
-    if (!!value) {
+    if (!!value){
+      this.documentEditor.mimeType = value.mimeType;
       this.documentForm.controls['mimeType'].setValue(value.mimeType);
       this.documentForm.controls['name'].setValue(value.name);
       this.documentForm.controls['currentResourceVersion'].setValue(value.currentResourceVersion);
@@ -141,6 +144,7 @@ export class SubresourceDocumentPanelComponent implements AfterViewInit, BeforeL
       this.documentForm.controls['payloadCreatedOn'].setValue(value.payloadCreatedOn);
       this.documentForm.controls['payload'].setValue(!value.payload ? "" : value.payload);
       this.documentForm.controls['payload'].enable();
+      this.documentForm.controls['properties'].setValue(value.properties);
 
       if (!this.documentVersionsExists) {
         this.documentForm.controls['payloadVersion'].disable();
@@ -155,13 +159,18 @@ export class SubresourceDocumentPanelComponent implements AfterViewInit, BeforeL
       this.documentForm.controls['payloadVersion'].setValue("");
       this.documentForm.controls['payloadCreatedOn'].setValue("");
       this.documentForm.controls['payload'].setValue("");
+      this.documentForm.controls['properties'].setValue([]);
     }
     this.documentForm.markAsPristine();
   }
 
   get document(): DocumentRo {
     let doc: DocumentRo = {...this._document};
-    doc.payload = this.documentForm.controls['payload'].value;
+    if(this.documentForm.controls['payload'].dirty){
+      doc.payload = this.documentForm.controls['payload'].value;
+      doc.payloadStatus = EntityStatus.UPDATED;
+    }
+    doc.properties = this.documentForm.controls['properties'].value;
     return doc;
   }
 
@@ -288,7 +297,6 @@ export class SubresourceDocumentPanelComponent implements AfterViewInit, BeforeL
   }
 
   onDocumentResetButtonClicked(): void {
-
     this.dialog.open(ConfirmationDialogComponent, {
       data: {
         title: "Cancel changes",
diff --git a/smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.css b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.css
similarity index 100%
rename from smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.css
rename to smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.css
diff --git a/smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.html b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.html
similarity index 100%
rename from smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.html
rename to smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.html
diff --git a/smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.ts b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.ts
similarity index 91%
rename from smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.ts
rename to smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.ts
index 75f730993171097a813d53e680daa8070d440b1f..a2430fab995528469161128cebf4bc6c1a5206bc 100644
--- a/smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.ts
+++ b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.ts
@@ -1,15 +1,17 @@
 import {Component, ElementRef, Inject, Input, ViewChild} from '@angular/core';
 import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
 import {FormBuilder, FormControl, FormGroup} from "@angular/forms";
-import {AlertMessageService} from "../../../../common/alert-message/alert-message.service";
-import {VisibilityEnum} from "../../../../common/enums/visibility.enum";
-import {GroupRo} from "../../../../common/model/group-ro.model";
+import {
+  AlertMessageService
+} from "../../../../common/alert-message/alert-message.service";
 import {ResourceRo} from "../../../../common/model/resource-ro.model";
 import {DomainRo} from "../../../../common/model/domain-ro.model";
-import {ResourceDefinitionRo} from "../../../../system-settings/admin-extension/resource-definition-ro.model";
-import {EditGroupService} from "../../../edit-group/edit-group.service";
+import {
+  ResourceDefinitionRo
+} from "../../../../system-settings/admin-extension/resource-definition-ro.model";
 import {SubresourceRo} from "../../../../common/model/subresource-ro.model";
 import {EditResourceService} from "../../edit-resource.service";
+
 @Component({
   templateUrl: './subresource-dialog.component.html',
   styleUrls: ['./subresource-dialog.component.css']
diff --git a/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-panel.component.ts b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-panel.component.ts
index e230d34614de642526ba2e376eb8fb6422c14b76..0a3da6f7b74f5e2afd7f949063dec8fcfe4d3cc8 100644
--- a/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-panel.component.ts
+++ b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-panel.component.ts
@@ -12,7 +12,7 @@ import {EditResourceService} from "../edit-resource.service";
 import {SubresourceRo} from "../../../common/model/subresource-ro.model";
 import {MatTableDataSource} from "@angular/material/table";
 import {ConfirmationDialogComponent} from "../../../common/dialogs/confirmation-dialog/confirmation-dialog.component";
-import {SubresourceDialogComponent} from "./resource-dialog/subresource-dialog.component";
+import {SubresourceDialogComponent} from "./subresource-dialog/subresource-dialog.component";
 import {SubresourceDefinitionRo} from "../../../system-settings/admin-extension/subresource-definition-ro.model";
 import {NavigationNode, NavigationService} from "../../../window/sidenav/navigation-model.service";
 
@@ -22,7 +22,7 @@ import {NavigationNode, NavigationService} from "../../../window/sidenav/navigat
   templateUrl: './subresource-panel.component.html',
   styleUrls: ['./subresource-panel.component.scss']
 })
-export class SubresourcePanelComponent implements AfterViewInit, OnInit, BeforeLeaveGuard {
+export class SubresourcePanelComponent implements AfterViewInit, BeforeLeaveGuard {
 
 
   title: string = "Subresources";
@@ -44,9 +44,6 @@ export class SubresourcePanelComponent implements AfterViewInit, OnInit, BeforeL
               private dialog: MatDialog) {
   }
 
-  ngOnInit(): void {
-  }
-
   ngAfterViewInit() {
 
     this.dataSource.paginator = this.paginator;
diff --git a/smp-angular/src/app/system-settings/admin-domain/domain-properties-panel/domain-properties-panel.component.ts b/smp-angular/src/app/system-settings/admin-domain/domain-properties-panel/domain-properties-panel.component.ts
index d9593e3dde887d80820a8124b2e848b61712dfa4..06b7b6799d95b34c034d6d5fdcbf23ff2921cdd9 100644
--- a/smp-angular/src/app/system-settings/admin-domain/domain-properties-panel/domain-properties-panel.component.ts
+++ b/smp-angular/src/app/system-settings/admin-domain/domain-properties-panel/domain-properties-panel.component.ts
@@ -17,7 +17,7 @@ import {MatTableDataSource} from "@angular/material/table";
 import {Subscription} from "rxjs";
 import {PropertyController} from "../../admin-properties/property-controller";
 import {EntityStatus} from "../../../common/enums/entity-status.enum";
-import {PropertyTypeEnum} from "../../../common/enums/property-type.enum";
+import {PropertySourceEnum} from "../../../common/enums/property-source.enum";
 import {EditDomainService} from "../../../edit/edit-domain/edit-domain.service";
 
 @Component({
@@ -145,7 +145,7 @@ export class DomainPropertiesPanelComponent implements OnInit, OnDestroy, Before
     const dialogRef: MatDialogRef<any> =  this.propertyController.edit({
       data: {
         edit: this.selected?.status != EntityStatus.NEW,
-        propertyType: PropertyTypeEnum.DOMAIN,
+        propertyType: PropertySourceEnum.DOMAIN,
         row: this.selected
       }
     })
diff --git a/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.ts b/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.ts
index bc296a6022806678b940c977c6d6ae2613278977..173f6d09d71739cfc203ceb528f74c5cbe67bbda 100644
--- a/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.ts
+++ b/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.ts
@@ -28,7 +28,6 @@ export class UserAlertsComponent implements OnInit, OnDestroy, BeforeLeaveGuard
     private securityEventService: SecurityEventService,
     public dialog: MatDialog) {
 
-
     this.securityEventServiceSub = this.securityEventService.onLoginSuccessEvent().subscribe(() => {
         this.updateUserData(this.securityService.getCurrentUser())
       }
diff --git a/smp-angular/src/styles.css b/smp-angular/src/styles.css
index 406c091b98bf47ff29561204a87f051ab74f5c34..48c456a492e3046dd930df20fb72baaef94a63ba 100644
--- a/smp-angular/src/styles.css
+++ b/smp-angular/src/styles.css
@@ -57,24 +57,6 @@ td , th {
   word-wrap: break-word !important;
 }
 
-div:before,
-table:before,
-tr:before,
-td:before,
-form:before,
-tbody:before,
-mat-card:before,
-mat-card-content:before,
-.panel:before,
-.selectionCriteria:before,
-table.buttonsRow:before,
-table.buttonsMoveRow,
-mat-input-container:before,
-ngx-datatable span:before {
-  content: " " !important;
-}
-
-
 .ngx-datatable span {
   word-wrap: break-word;
 }
@@ -282,26 +264,8 @@ mat-card {
   margin-top: 0 !important;
 }
 
-/* --- INPUT ---*/
-.panel mat-select, .panel mat-datepicker {
-  padding-top: 10px;
-  padding-bottom: 10px;
-}
-
 .panel mat-select, .panel mat-form-field, .panel mat-datepicker {
-  margin-right: 2%;
-}
-
-@media (max-width: 991px) {
-  .panel mat-form-field, .panel mat-select, .panel mat-datepicker {
-    width: 46%;
-  }
-}
-
-@media (max-width: 767px) {
-  .panel mat-form-field, .panel mat-select, .panel mat-datepicker {
-    width: 100%;
-  }
+  margin-right: 5px;
 }
 
 .mat-select-menu-container,
@@ -353,6 +317,13 @@ mat-card {
   border-top: 1px solid transparent;
 }
 
+.mat-mdc-table .mdc-data-table__header-row {
+  height: 2.0em;
+}
+.mat-mdc-table .mdc-data-table__row {
+  height: 1.8em;
+}
+
 /*--------------------------------------------------
     [alert-message]
 ----------------------------------------------------*/
diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource10Handler.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource10Handler.java
index 9ec6efd1a87f959b91473509764b73c3b249530f..6f2b2dbff102b6fbf57bbf77183e567442e9e591 100644
--- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource10Handler.java
+++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource10Handler.java
@@ -28,6 +28,7 @@ import eu.europa.ec.smp.spi.api.model.RequestData;
 import eu.europa.ec.smp.spi.api.model.ResourceIdentifier;
 import eu.europa.ec.smp.spi.api.model.ResponseData;
 import eu.europa.ec.smp.spi.def.OasisSMPSubresource10;
+import eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType;
 import eu.europa.ec.smp.spi.exceptions.ResourceException;
 import gen.eu.europa.ec.ddc.api.smp10.ParticipantIdentifierType;
 import gen.eu.europa.ec.ddc.api.smp10.ServiceGroup;
@@ -52,7 +53,11 @@ import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME;
+import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE;
 import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.*;
+import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
+import static org.apache.commons.lang3.StringUtils.trim;
 
 @Component
 public class OasisSMPResource10Handler extends AbstractOasisSMPHandler {
@@ -78,8 +83,10 @@ public class OasisSMPResource10Handler extends AbstractOasisSMPHandler {
 
         ServiceGroup resource = new ServiceGroup();
         resource.setParticipantIdentifier(new ParticipantIdentifierType());
-        resource.getParticipantIdentifier().setValue(identifier.getValue());
-        resource.getParticipantIdentifier().setScheme(identifier.getScheme());
+        resource.getParticipantIdentifier().setValue(RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder());
+        if (identifier.getScheme()!= null) {
+            resource.getParticipantIdentifier().setScheme(RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder());
+        }
         resource.setServiceMetadataReferenceCollection(new ServiceMetadataReferenceCollectionType());
 
         try {
@@ -186,8 +193,12 @@ public class OasisSMPResource10Handler extends AbstractOasisSMPHandler {
             }
         } else {
             LOG.info("Update ServiceGroup identifier before saving. Old: [{}], New: [{}]", orgResourceId, nrmResourceId);
-            orgResourceId.setValue(nrmResourceId.getValue());
-            orgResourceId.setScheme(nrmResourceId.getScheme());
+            if (!StringUtils.equalsIgnoreCase(orgResourceId.getValue(), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) {
+                orgResourceId.setValue(nrmResourceId.getValue());
+            }
+            if (!StringUtils.equalsIgnoreCase(orgResourceId.getScheme(), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) {
+                orgResourceId.setScheme(nrmResourceId.getScheme());
+            }
             try {
                 // need to save resource because of the update on the resource identifier values
                 reader.serializeNative(resource, responseData.getOutputStream(), true);
@@ -228,8 +239,20 @@ public class OasisSMPResource10Handler extends AbstractOasisSMPHandler {
             throw new ResourceException(INVALID_RESOURCE, "Error occurred while parsing Oasis SMP 1.0 ServiceGroup with error: " + ExceptionUtils.getRootCauseMessage(e), e);
         }
         final ParticipantIdentifierType participantId = resource.getParticipantIdentifier();
+        String participantIdValue = participantId.getValue();
+        String participantIdScheme = participantId.getScheme();
+        if (equalsIgnoreCase(trim(participantIdValue), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) {
+            participantIdValue = identifier.getValue();
+        }
+
+        if (equalsIgnoreCase(trim(participantIdScheme), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) {
+            participantIdScheme = identifier.getScheme();
+        }
+
         ResourceIdentifier xmlResourceIdentifier = smpIdentifierApi.normalizeResourceIdentifier(resourceData.getDomainCode(),
-                participantId.getValue(), participantId.getScheme());
+                participantIdValue, participantIdScheme);
+
+
 
         if (!xmlResourceIdentifier.equals(identifier)) {
             // Business identifier must equal path
diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource20Handler.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource20Handler.java
index 1d668bb92ae026376f4bca21ef60e3a83f040554..3b8f789e039d7cd7c718d1cf8f1a40a82006f1ba 100644
--- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource20Handler.java
+++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource20Handler.java
@@ -56,7 +56,11 @@ import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME;
+import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE;
 import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.*;
+import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
+import static org.apache.commons.lang3.StringUtils.trim;
 
 @Component
 public class OasisSMPResource20Handler extends AbstractOasisSMPHandler {
@@ -86,9 +90,10 @@ public class OasisSMPResource20Handler extends AbstractOasisSMPHandler {
         resource.setSMPVersionID(new SMPVersionID());
         resource.getSMPVersionID().setValue("2.0");
         resource.setParticipantID(new ParticipantID());
-        resource.getParticipantID().setValue(identifier.getValue());
-        resource.getParticipantID().setSchemeID(identifier.getScheme());
-
+        resource.getParticipantID().setValue(RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder());
+        if (identifier.getScheme() != null) {
+            resource.getParticipantID().setSchemeID(RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder());
+        }
         try {
             reader.serializeNative(resource, responseData.getOutputStream(), true);
         } catch (TechnicalException e) {
@@ -177,8 +182,13 @@ public class OasisSMPResource20Handler extends AbstractOasisSMPHandler {
 
         } else {
             LOG.info("Update Resource/ServiceGroup identifier before saving. Old: [{}], New: [{}]", orgResourceId, nrmResourceId);
-            orgResourceId.setValue(nrmResourceId.getValue());
-            orgResourceId.setSchemeID(nrmResourceId.getScheme());
+            if (!StringUtils.equalsIgnoreCase(orgResourceId.getValue(), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) {
+                orgResourceId.setValue(nrmResourceId.getValue());
+            }
+            if (!StringUtils.equalsIgnoreCase(orgResourceId.getSchemeID(), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) {
+                orgResourceId.setSchemeID(nrmResourceId.getScheme());
+            }
+
             try {
                 // need to save resource because of the update on the resource identifier values
                 reader.serializeNative(resource, responseData.getOutputStream(), true);
@@ -220,9 +230,20 @@ public class OasisSMPResource20Handler extends AbstractOasisSMPHandler {
             throw new ResourceException(INVALID_RESOURCE, "Error occurred while reading the Oasis SMP 2.0 ServiceGroup with error: " + ExceptionUtils.getRootCauseMessage(e), e);
         }
         final ParticipantID participantId = resource.getParticipantID();
+        String participantIdValue = participantId.getValue();
+        String participantIdScheme = participantId.getSchemeID();
+        if (equalsIgnoreCase(trim(participantIdValue), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) {
+            participantIdValue = identifier.getValue();
+        }
+
+        if (equalsIgnoreCase(trim(participantIdScheme), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) {
+            participantIdScheme = identifier.getScheme();
+        }
+
         ResourceIdentifier xmlResourceIdentifier = smpIdentifierApi.normalizeResourceIdentifier(
                 resourceData.getDomainCode(),
-                participantId.getValue(), participantId.getSchemeID());
+                participantIdValue, participantIdScheme);
+
 
         if (!xmlResourceIdentifier.equals(identifier)) {
             // Business identifier must equal path
diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource10Handler.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource10Handler.java
index 5ce2adbd960abcce362231bc60c3f51c44463156..4461e425f703a33e141bdb25e54739d46501779e 100644
--- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource10Handler.java
+++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource10Handler.java
@@ -47,6 +47,7 @@ import java.io.InputStream;
 import java.util.Collections;
 import java.util.List;
 
+import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*;
 import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.*;
 
 @Component
@@ -95,11 +96,16 @@ public class OasisSMPSubresource10Handler extends AbstractOasisSMPHandler {
         serviceInformationType.setProcessList(processListType);
         subresource.setServiceInformation(serviceInformationType);
         serviceInformationType.setParticipantIdentifier(new ParticipantIdentifierType());
-        serviceInformationType.getParticipantIdentifier().setValue(identifier.getValue());
-        serviceInformationType.getParticipantIdentifier().setScheme(identifier.getScheme());
+        serviceInformationType.getParticipantIdentifier().setValue(RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder());
+        if (identifier.getScheme()!= null) {
+            serviceInformationType.getParticipantIdentifier().setScheme(RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder());
+        }
+
         serviceInformationType.setDocumentIdentifier(new DocumentIdentifier());
-        serviceInformationType.getDocumentIdentifier().setValue(subresourceIdentifier.getValue());
-        serviceInformationType.getDocumentIdentifier().setScheme(subresourceIdentifier.getScheme());
+        serviceInformationType.getDocumentIdentifier().setValue(SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder());
+        if (subresourceIdentifier.getScheme()!= null) {
+            serviceInformationType.getDocumentIdentifier().setScheme(SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder());
+        }
 
         try {
             reader.serializeNativeAny(subresource, responseData.getOutputStream(), true);
diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource20Handler.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource20Handler.java
index ab32b59d7622103682e6e97eecc89a4a2e36c272..663b5cdba44518470f4bbf6b7b039ca1cd8c068b 100644
--- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource20Handler.java
+++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource20Handler.java
@@ -56,6 +56,7 @@ import java.time.OffsetDateTime;
 import java.util.Collections;
 import java.util.List;
 
+import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*;
 import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.*;
 
 @Component
@@ -91,11 +92,17 @@ public class OasisSMPSubresource20Handler extends AbstractOasisSMPHandler {
         subresource.setSMPVersionID(new SMPVersionID());
         subresource.getSMPVersionID().setValue("2.0");
         subresource.setParticipantID(new ParticipantID());
-        subresource.getParticipantID().setValue(identifier.getValue());
-        subresource.getParticipantID().setSchemeID(identifier.getScheme());
+        subresource.getParticipantID().setValue(RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder());
+        if (identifier.getScheme() != null) {
+            subresource.getParticipantID().setSchemeID(RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder());
+        }
+
         subresource.setServiceID(new ServiceID());
-        subresource.getServiceID().setValue(subresourceIdentifier.getValue());
-        subresource.getServiceID().setSchemeID(subresourceIdentifier.getScheme());
+        subresource.getServiceID().setValue(SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder());
+        if (subresourceIdentifier.getScheme() != null) {
+            subresource.getServiceID().setSchemeID(SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder());
+        }
+
         ProcessMetadata processMetadata = new ProcessMetadata();
         subresource.getProcessMetadatas().add(processMetadata);
         Process process = new Process();
diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource10Validator.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource10Validator.java
index f845e99ea9999e3281413ac28f78d80f1838cdd0..d6499310f23d93f60f059a8bd594c00dcdfdabd6 100644
--- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource10Validator.java
+++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource10Validator.java
@@ -32,8 +32,11 @@ import java.time.OffsetDateTime;
 import java.util.HashSet;
 import java.util.Set;
 
+import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*;
 import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.INVALID_PARAMETERS;
 import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.INVALID_RESOURCE;
+import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
+import static org.apache.commons.lang3.StringUtils.trim;
 
 
 /**
@@ -96,14 +99,36 @@ public class Subresource10Validator {
 
         final ParticipantIdentifierType participantId = serviceInformation.getParticipantIdentifier();
         final DocumentIdentifier documentId = serviceInformation.getDocumentIdentifier();
+
+        String participantIdValue = participantId.getValue();
+        String participantIdScheme = participantId.getScheme();
+        String documentIdValue = documentId.getValue();
+        String documentIdScheme = documentId.getScheme();
+
+        if (equalsIgnoreCase(trim(participantIdValue), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) {
+            participantIdValue = participantIdentifierFromUrl.getValue();
+        }
+
+        if (equalsIgnoreCase(trim(participantIdScheme), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) {
+            participantIdScheme = participantIdentifierFromUrl.getScheme();
+        }
+
+        if (equalsIgnoreCase(trim(documentIdValue), SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) {
+            documentIdValue = documentIdentifierFromUrl.getValue();
+        }
+
+        if (equalsIgnoreCase(trim(documentIdScheme), SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) {
+            documentIdScheme = documentIdentifierFromUrl.getScheme();
+        }
+
         ResourceIdentifier xmlResourceIdentifier = smpIdentifierApi.normalizeResourceIdentifier(
                 domainCode,
-                participantId.getValue(),
-                participantId.getScheme());
+                participantIdValue,
+                participantIdScheme);
         ResourceIdentifier xmlSubresourceIdentifier = smpIdentifierApi.normalizeSubresourceIdentifier(
                 domainCode,
-                documentId.getValue(),
-                documentId.getScheme());
+                documentIdValue,
+                documentIdScheme);
 
         ResourceIdentifier nrmResIdentifierFromUrl = smpIdentifierApi.normalizeResourceIdentifier(
                 domainCode,
diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource20Validator.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource20Validator.java
index 00ff18e8fb8f1935e43dc723eed50fe445236c74..46e367c110ad9c1025f1319241e5c40ed804bce4 100644
--- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource20Validator.java
+++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource20Validator.java
@@ -38,7 +38,11 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*;
+import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.SUBRESOURCE_IDENTIFIER_SCHEME;
 import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.INVALID_PARAMETERS;
+import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
+import static org.apache.commons.lang3.StringUtils.trim;
 
 
 /**
@@ -64,14 +68,38 @@ public class Subresource20Validator {
 
         final ParticipantID participantId = subresource.getParticipantID();
         final ServiceID documentId = subresource.getServiceID();
+
+        String participantIdValue = participantId.getValue();
+        String participantIdScheme = participantId.getSchemeID();
+        String documentIdValue = documentId.getValue();
+        String documentIdScheme = documentId.getSchemeID();
+
+        if (equalsIgnoreCase(trim(participantIdValue), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) {
+            participantIdValue = participantIdentifierFromUrl.getValue();
+        }
+
+        if (equalsIgnoreCase(trim(participantIdScheme), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) {
+            participantIdScheme = participantIdentifierFromUrl.getScheme();
+        }
+
+        if (equalsIgnoreCase(trim(documentIdValue), SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) {
+            documentIdValue = documentIdentifierFromUrl.getValue();
+        }
+
+        if (equalsIgnoreCase(trim(documentIdScheme), SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) {
+            documentIdScheme = documentIdentifierFromUrl.getScheme();
+        }
+
         ResourceIdentifier xmlResourceIdentifier = smpIdentifierApi.normalizeResourceIdentifier(
                 domainCode,
-                participantId.getValue(),
-                participantId.getSchemeID());
+                participantIdValue,
+                participantIdScheme);
         ResourceIdentifier xmlSubresourceIdentifier = smpIdentifierApi.normalizeSubresourceIdentifier(
                 domainCode,
-                documentId.getValue(),
-                documentId.getSchemeID());
+                documentIdValue,
+                documentIdScheme);
+
+
         if (!xmlResourceIdentifier.equals(participantIdentifierFromUrl)) {
             // Business identifier must equal path
             throw new ResourceException(INVALID_PARAMETERS, "Participant identifiers don't match between URL parameter [" + participantIdentifierFromUrl + "] and XML body: [" + xmlResourceIdentifier + "]");
@@ -84,7 +112,6 @@ public class Subresource20Validator {
 
         List<ProcessMetadata> processMetadata = subresource.getProcessMetadatas();
         validateProcesses(processMetadata);
-
     }
 
 
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java
index 860bde5ab9bcaddae2715f4909d074143c7e735c..b3f7542653983820b195b2e5177e77eab7bdd0c5 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java
@@ -63,6 +63,25 @@ public class DocumentDao extends BaseDao<DBDocument> {
         }
     }
 
+    /**
+     * Method returns the document for the  (detached) subresource
+     *
+     * @param dbSubresource resource
+     * @return document for the resource or empty if not found
+     */
+    public Optional<DBDocument> getDocumentForSubresource(DBSubresource dbSubresource) {
+        try {
+            // expected is only one domain,
+            TypedQuery<DBDocument> query = memEManager.createNamedQuery(QUERY_DOCUMENT_FOR_SUBRESOURCE, DBDocument.class);
+            query.setParameter(PARAM_SUBRESOURCE_ID, dbSubresource.getId());
+            return Optional.of(query.getSingleResult());
+        } catch (NonUniqueResultException e) {
+            throw new SMPRuntimeException(ErrorCode.RESOURCE_DOCUMENT_ERROR, dbSubresource.getIdentifierValue(), dbSubresource.getIdentifierScheme(), "Multiple documents");
+        } catch (NoResultException e) {
+            return Optional.empty();
+        }
+    }
+
     public Optional<DBDocumentVersion> getCurrentDocumentVersionForResource(DBResource dbResource) {
 
         try {
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainConfigurationDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainConfigurationDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..39acb2d288f55b4bc0e0e733ba28a49a2aff062c
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainConfigurationDao.java
@@ -0,0 +1,269 @@
+/*-
+ * #START_LICENSE#
+ * smp-webapp
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+package eu.europa.ec.edelivery.smp.data.dao;
+
+import eu.europa.ec.edelivery.smp.config.SMPEnvironmentProperties;
+import eu.europa.ec.edelivery.smp.config.enums.SMPDomainPropertyEnum;
+import eu.europa.ec.edelivery.smp.config.enums.SMPEnvPropertyEnum;
+import eu.europa.ec.edelivery.smp.data.model.DBDomain;
+import eu.europa.ec.edelivery.smp.data.model.DBDomainConfiguration;
+import eu.europa.ec.edelivery.smp.data.ui.DomainPropertyRO;
+import eu.europa.ec.edelivery.smp.data.ui.PropertyRO;
+import eu.europa.ec.edelivery.smp.data.ui.PropertyValidationRO;
+import eu.europa.ec.edelivery.smp.data.ui.auth.SMPRole;
+import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
+import eu.europa.ec.edelivery.smp.logging.SMPLogger;
+import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
+import eu.europa.ec.edelivery.smp.utils.PropertyUtils;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.TransactionRequiredException;
+import javax.persistence.TypedQuery;
+import java.io.File;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.PARAM_DOMAIN_ID;
+import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.QUERY_DOMAIN_CONFIGURATION_ALL;
+
+
+/**
+ * Specific DAO for DomainConfiguration
+ *
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+@Repository
+public class DomainConfigurationDao extends BaseDao<DBDomainConfiguration> {
+
+    private static final SMPLogger LOG = SMPLoggerFactory.getLogger(DomainConfigurationDao.class);
+
+
+    /**
+     * Method returns all Domain properties for the given domain and role. If the property does not exist in the database,
+     * the default value is returned. If the role is not system admin, and
+     * property is system admin only, the property is not returned!
+     *
+     * @param domain - domain to get properties
+     * @param role   - the properties are filtered based on the role. System admin gets all roles
+     *               but other roles get only properties which are not system admin only.
+     * @return list of domain properties
+     */
+    public List<DBDomainConfiguration> getDomainPropertiesForRole(DBDomain domain, SMPRole role) {
+        // get domain configuration from the database
+        List<DBDomainConfiguration> domainConfiguration = getDomainConfiguration(domain);
+        // convert list to map
+        Map<String, DBDomainConfiguration> dbList = domainConfiguration.stream()
+                .collect(Collectors.toMap(DBDomainConfiguration::getProperty, Function.identity()));
+
+        // return only properties that are not system admin only
+        return Arrays.stream(SMPDomainPropertyEnum.values())
+                .filter(domainPropertyRO -> role == SMPRole.SYSTEM_ADMIN || !domainPropertyRO.isSystemAdminOnly())
+                .map(enumType -> {
+                    if (dbList.containsKey(enumType.getProperty())) {
+                        return dbList.get(enumType.getProperty());
+                    }
+                    return createDBDomainConfiguration(enumType);
+                }).filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Method creates a Domain properties for the given SMPDomainPropertyEnum. The object is not
+     * persisted in the database.
+     */
+    private DBDomainConfiguration createDBDomainConfiguration(SMPDomainPropertyEnum domainPropertyEnum) {
+        DBDomainConfiguration dbDomainConfiguration = new DBDomainConfiguration();
+        dbDomainConfiguration.setProperty(domainPropertyEnum.getProperty());
+        dbDomainConfiguration.setValue(domainPropertyEnum.getDefValue());
+        dbDomainConfiguration.setDescription(domainPropertyEnum.getDesc());
+        dbDomainConfiguration.setUseSystemDefault(true);
+        return dbDomainConfiguration;
+    }
+
+    /**
+     * Method updates domain properties for the given domain and role. If the role is not system admin, and
+     * property is system admin only, the property is not updated!
+     *
+     * @param domain           - domain to update properties
+     * @param domainProperties - list of domain properties
+     * @param role             - role of the user
+     * @return list of updated domain properties
+     */
+    @Transactional
+    public List<DBDomainConfiguration> updateDomainPropertiesForRole(DBDomain domain,
+                                                                     List<DomainPropertyRO> domainProperties,
+                                                                     SMPRole role) {
+
+        // get current domain configuration
+        Map<String, DBDomainConfiguration> currentDomainConfiguration = getDomainConfiguration(domain)
+                .stream().collect(Collectors.toMap(DBDomainConfiguration::getProperty, Function.identity()));
+        Map<String, DomainPropertyRO> newDomainPropertyValues =
+                domainProperties.stream().collect(Collectors.toMap(DomainPropertyRO::getProperty, Function.identity()));
+
+        List<DBDomainConfiguration> listOfDomainConfiguration = new ArrayList<>();
+
+        // database domain configuration property list must match SMPDomainPropertyEnum
+        for (SMPDomainPropertyEnum domainProp : SMPDomainPropertyEnum.values()) {
+            if (role != SMPRole.SYSTEM_ADMIN && domainProp.isSystemAdminOnly()) {
+                // skip system admin only properties
+                continue;
+            }
+            DBDomainConfiguration domainConfiguration = currentDomainConfiguration.get(domainProp.getProperty());
+            DomainPropertyRO domainPropertyRO = newDomainPropertyValues.get(domainProp.getProperty());
+            // if property already exists in the database, update value
+            DBDomainConfiguration updatedDomainProp = updateDomainProperty(domain, domainProp, domainConfiguration, domainPropertyRO);
+            listOfDomainConfiguration.add(updatedDomainProp);
+            // remove updated property from the map
+            currentDomainConfiguration.remove(domainProp.getProperty());
+            LOG.debug("Updated domain property [{}]: [{}] for domain [{}]",
+                    domainProp.getProperty(), updatedDomainProp, domain.getDomainCode());
+
+        }
+        // remove properties that are not in the new list
+        currentDomainConfiguration.values().forEach(domainConfiguration -> {
+            removeConfiguration(domainConfiguration);
+            LOG.debug("Removed domain property [{}]: [{}] for domain [{}]",
+                    domainConfiguration.getProperty(), domainConfiguration.getValue(),
+                    domain.getDomainCode());
+        });
+
+        //
+        return listOfDomainConfiguration;
+    }
+
+
+    public PropertyValidationRO validateDomainProperty(PropertyRO propertyRO) {
+        LOG.info("Validate property: [{}]", propertyRO.getProperty());
+        PropertyValidationRO propertyValidationRO = new PropertyValidationRO();
+        propertyValidationRO.setProperty(propertyRO.getProperty());
+        propertyValidationRO.setValue(propertyRO.getValue());
+
+        Optional<SMPDomainPropertyEnum> optPropertyEnum = SMPDomainPropertyEnum.getByProperty(propertyRO.getProperty());
+        if (!optPropertyEnum.isPresent()) {
+            LOG.debug("Property: [{}] is not Domain SMP property!", propertyRO.getProperty());
+            propertyValidationRO.setErrorMessage("Property [" + propertyRO.getProperty() + "] is not SMP property!");
+            propertyValidationRO.setPropertyValid(false);
+            return propertyValidationRO;
+        }
+
+        SMPDomainPropertyEnum propertyEnum = optPropertyEnum.get();
+        return validateDomainPropertyValue(propertyEnum, propertyValidationRO);
+    }
+
+    /**
+     * Method validates domain property value for given property enum.
+     *
+     * @param propertyEnum         - property enum
+     * @param propertyValidationRO - property validation object with value.
+     * @return property validation object with validation result and error message if validation failed.
+     */
+    private PropertyValidationRO validateDomainPropertyValue(SMPDomainPropertyEnum propertyEnum,
+                                                             PropertyValidationRO propertyValidationRO) {
+        // try to parse value
+        try {
+            File confDir = Paths.get(SMPEnvironmentProperties.getInstance().getEnvPropertyValue(SMPEnvPropertyEnum.SECURITY_FOLDER)).toFile();
+            PropertyUtils.parseProperty(propertyEnum.getPropertyEnum(), propertyValidationRO.getValue(), confDir);
+        } catch (SMPRuntimeException ex) {
+            propertyValidationRO.setErrorMessage(ex.getMessage());
+            propertyValidationRO.setPropertyValid(false);
+            return propertyValidationRO;
+        }
+
+        propertyValidationRO.setPropertyValid(true);
+        return propertyValidationRO;
+    }
+
+
+    /**
+     * Returns the domain properties for the given domain.
+     *
+     * @param domain - domain for which the properties are requested
+     * @return - list of domain properties
+     */
+    public List<DBDomainConfiguration> getDomainConfiguration(DBDomain domain) {
+        TypedQuery<DBDomainConfiguration> query = memEManager.createNamedQuery(QUERY_DOMAIN_CONFIGURATION_ALL,
+                DBDomainConfiguration.class);
+        query.setParameter(PARAM_DOMAIN_ID, domain.getId());
+        return query.getResultList();
+    }
+
+    /**
+     * Update domain property. If property does not exist in the database, it will be created.
+     * The method must be called in transactional context, else TransactionRequiredException
+     * will be thrown from JPA merge method.
+     *
+     * @param domain              - domain to update. Value is used in case domain configuration does not exist in the database.
+     * @param domainProp          - domain property to update
+     * @param domainConfiguration - current domain configuration or null if it does not exist in the database.
+     *                            The object must be attached to the persistence context.
+     * @param domainPropertyRO    - new domain property value and system default flag
+     * @return new/updated  domain configuration
+     */
+    public DBDomainConfiguration updateDomainProperty(DBDomain domain, SMPDomainPropertyEnum domainProp,
+                                                      DBDomainConfiguration domainConfiguration, DomainPropertyRO domainPropertyRO) {
+        if (domainConfiguration != null && domainPropertyRO == null) {
+            LOG.debug("Domain property [{}] is not set. Skip the update of property value.",
+                    domainProp.getProperty());
+            return domainConfiguration;
+        }
+
+        if (domainConfiguration == null) {
+            domainConfiguration = new DBDomainConfiguration();
+            domainConfiguration.setDomain(domain);
+            domainConfiguration.setProperty(domainProp.getProperty());
+        }
+
+        if (domainPropertyRO != null) {
+            domainConfiguration.setValue(domainPropertyRO.getValue());
+            domainConfiguration.setUseSystemDefault(domainPropertyRO.isSystemDefault());
+        } else {
+            LOG.debug("Domain property [{}] is not set and propperty does not exists in database. Create new system default value",
+                    domainProp.getProperty());
+            domainConfiguration.setValue(domainProp.getDefValue());
+            domainConfiguration.setDescription(domainProp.getDesc());
+            domainConfiguration.setUseSystemDefault(true);
+        }
+        mergeConfiguration(domainConfiguration);
+        return domainConfiguration;
+    }
+
+
+    /**
+     * The method Merge the state of the given domain configuration into the current
+     * persistence context.  The method must be
+     * called in existing transaction, and it is used to manage domain properties.
+     *
+     * @param entity - domain configuration to be merged
+     * @return - jpa merged/managed domain configuration
+     * @throws TransactionRequiredException if there is no transaction when
+     *                                      invoked on a container-managed entity manager of that is of type
+     *                                      <code>PersistenceContextType.TRANSACTION</code>
+     */
+    public DBDomainConfiguration mergeConfiguration(DBDomainConfiguration entity) {
+        return memEManager.merge(entity);
+    }
+
+    public void removeConfiguration(DBDomainConfiguration entity) {
+        memEManager.remove(entity);
+    }
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java
index 293516817fa5c08dda52c4620e17c244beb1c40b..c312c1b63c6c149be2b8a2b294af4be7f23e7e82 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java
@@ -19,20 +19,17 @@
 
 package eu.europa.ec.edelivery.smp.data.dao;
 
-import eu.europa.ec.edelivery.smp.config.enums.SMPDomainPropertyEnum;
 import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType;
 import eu.europa.ec.edelivery.smp.data.model.DBDomain;
-import eu.europa.ec.edelivery.smp.data.model.DBDomainConfiguration;
-import eu.europa.ec.edelivery.smp.data.ui.DomainPropertyRO;
 import eu.europa.ec.edelivery.smp.exceptions.ErrorCode;
 import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.persistence.NoResultException;
 import javax.persistence.NonUniqueResultException;
-import javax.persistence.TransactionRequiredException;
 import javax.persistence.TypedQuery;
 import java.util.List;
 import java.util.Optional;
@@ -48,7 +45,7 @@ import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.ILLEGAL_STATE_DOMA
  */
 @Repository
 public class DomainDao extends BaseDao<DBDomain> {
-
+    private static final Logger LOG = org.slf4j.LoggerFactory.getLogger(DomainDao.class);
 
     /**
      * Returns the only single record from smp_domain table.
@@ -231,71 +228,4 @@ public class DomainDao extends BaseDao<DBDomain> {
         }
         return false;
     }
-
-    /**
-     * Returns the domain properties for the given domain.
-     *
-     * @param domain - domain for which the properties are requested
-     * @return - list of domain properties
-     */
-    public List<DBDomainConfiguration> getDomainConfiguration(DBDomain domain) {
-        TypedQuery<DBDomainConfiguration> query = memEManager.createNamedQuery(QUERY_DOMAIN_CONFIGURATION_ALL,
-                DBDomainConfiguration.class);
-        query.setParameter(PARAM_DOMAIN_ID, domain.getId());
-        return query.getResultList();
-    }
-
-    /**
-     * Update domain property. If property does not exist in the database, it will be created.
-     * The method must be called in transactional context, else TransactionRequiredException
-     * will be thrown from JPA merge method.
-     *
-     * @param domain              - domain to update. Value is used in case domain configuration does not exist in the database.
-     * @param domainProp          - domain property to update
-     * @param domainConfiguration - current domain configuration or null if it does not exist in the database.
-     *                            The object must be attached to the persistence context.
-     * @param domainPropertyRO    - new domain property value and system default flag
-     * @return new/updated  domain configuration
-     */
-    public DBDomainConfiguration updateDomainProperty(DBDomain domain, SMPDomainPropertyEnum domainProp,
-                                                      DBDomainConfiguration domainConfiguration, DomainPropertyRO domainPropertyRO) {
-        if (domainConfiguration == null) {
-            domainConfiguration = new DBDomainConfiguration();
-            domainConfiguration.setDomain(domain);
-            domainConfiguration.setProperty(domainProp.getProperty());
-            // attach domain configuration to the persistence context
-            mergeConfiguration(domainConfiguration);
-        }
-
-        if (domainPropertyRO != null) {
-            domainConfiguration.setValue(domainPropertyRO.getValue());
-            domainConfiguration.setUseSystemDefault(domainPropertyRO.isSystemDefault());
-        } else {
-            domainConfiguration.setValue(domainProp.getDefValue());
-            domainConfiguration.setUseSystemDefault(true);
-        }
-
-        return domainConfiguration;
-    }
-
-
-    /**
-     * The method Merge the state of the given domain configuration into the current
-     * persistence context.  The method must be
-     * called in existing transaction, and it is used to manage domain properties.
-     *
-     * @param entity - domain configuration to be merged
-     * @return - jpa merged/managed domain configuration
-     * @throws TransactionRequiredException if there is no transaction when
-     *                                      invoked on a container-managed entity manager of that is of type
-     *                                      <code>PersistenceContextType.TRANSACTION</code>
-     */
-    public DBDomainConfiguration mergeConfiguration(DBDomainConfiguration entity) {
-        return memEManager.merge(entity);
-    }
-
-    public void removeConfiguration(DBDomainConfiguration entity) {
-        memEManager.remove(entity);
-    }
-
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java
index e8f516b34deb30d12d63a3f8df21cc5ce8ca7870..12ef52e08969945dd2283de8df03cada89e15714 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java
@@ -119,6 +119,7 @@ public class QueryNames {
     public static final String QUERY_RESOURCE_DEF_BY_IDENTIFIER_EXTENSION = "DBExtResourceDef.getByIdentifierExtension";
 
     public static final String QUERY_DOCUMENT_FOR_RESOURCE = "DBDocument.getForResource";
+    public static final String QUERY_DOCUMENT_FOR_SUBRESOURCE = "DBDocument.getForSubresource";
 
     public static final String QUERY_DOCUMENT_VERSION_CURRENT_FOR_RESOURCE = "DBDocumentVersion.forCurrentForResource";
     public static final String QUERY_DOCUMENT_VERSION_LIST_FOR_RESOURCE = "DBDocumentVersion.getAllForResource";
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomainConfiguration.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomainConfiguration.java
index a2b5c9c29b7ad9770692bc714459e32c226ada20..c4db75785831d43d069fd52b1a76164f8fda7063 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomainConfiguration.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomainConfiguration.java
@@ -68,7 +68,7 @@ public class DBDomainConfiguration extends BaseEntity {
     @ColumnDescription(comment = "Property description")
     String description;
 
-    @Column(name = "USER_SYSTEM_DEFAULT", nullable = false)
+    @Column(name = "SYSTEM_DEFAULT", nullable = false)
     @ColumnDescription(comment = "Use system default value")
     boolean useSystemDefault = true;
 
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java
index dcf41cc1c5f2c509526bb0ea387c6b78356f3694..5e9d842eeae664501f16579ae9c1726cc233c1b7 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -32,6 +32,7 @@ import java.util.List;
 import java.util.Objects;
 
 import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.QUERY_DOCUMENT_FOR_RESOURCE;
+import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.QUERY_DOCUMENT_FOR_SUBRESOURCE;
 
 /**
  * Database optimization: load service metadata xml only when needed and
@@ -46,7 +47,8 @@ import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.QUERY_DOCUMENT_FOR_
 @Table(name = "SMP_DOCUMENT")
 @org.hibernate.annotations.Table(appliesTo = "SMP_DOCUMENT", comment = "SMP document entity for resources and subresources")
 @NamedQueries({
-        @NamedQuery(name = QUERY_DOCUMENT_FOR_RESOURCE, query = "SELECT d FROM DBResource r JOIN r.document d WHERE r.id =:resource_id")
+        @NamedQuery(name = QUERY_DOCUMENT_FOR_RESOURCE, query = "SELECT d FROM DBResource r JOIN r.document d WHERE r.id =:resource_id"),
+        @NamedQuery(name = QUERY_DOCUMENT_FOR_SUBRESOURCE, query = "SELECT d FROM DBSubresource  sr JOIN sr.document d WHERE sr.id =:subresource_id")
 })
 public class DBDocument extends BaseEntity {
     private static final SMPLogger LOG = SMPLoggerFactory.getLogger(DBDocument.class);
@@ -63,7 +65,6 @@ public class DBDocument extends BaseEntity {
             cascade = CascadeType.ALL,
             orphanRemoval = true,
             fetch = FetchType.LAZY
-
     )
     List<DBDocumentVersion> documentVersions;
 
@@ -77,6 +78,13 @@ public class DBDocument extends BaseEntity {
     @Column(name = "NAME")
     private String name;
 
+    @OneToMany(
+            mappedBy = "document",
+            cascade = CascadeType.ALL,
+            orphanRemoval = true,
+            fetch = FetchType.LAZY
+    )
+    private List<DBDocumentProperty> documentProperties = new ArrayList<>();
 
     @Override
     public Long getId() {
@@ -90,17 +98,18 @@ public class DBDocument extends BaseEntity {
 
     /**
      * Returns document version ordered from the latest version to first version
+     *
      * @return document versions
      */
     public List<DBDocumentVersion> getDocumentVersions() {
-        if (documentVersions ==null) {
+        if (documentVersions == null) {
             documentVersions = new ArrayList<>();
         }
         return documentVersions;
     }
 
-    public DBDocumentVersion addNewDocumentVersion(DBDocumentVersion documentVersion){
-        if (documentVersion.getId() !=null && getDocumentVersions().contains(documentVersion)) {
+    public DBDocumentVersion addNewDocumentVersion(DBDocumentVersion documentVersion) {
+        if (documentVersion.getId() != null && getDocumentVersions().contains(documentVersion)) {
             LOG.info("Document version [{}] already exists on document [{}]", documentVersion, this);
             return documentVersion;
         }
@@ -111,19 +120,19 @@ public class DBDocument extends BaseEntity {
         return documentVersion;
     }
 
-    public void removeDocumentVersion(DBDocumentVersion documentVersion){
+    public void removeDocumentVersion(DBDocumentVersion documentVersion) {
         boolean removed = getDocumentVersions().remove(documentVersion);
-        if (removed){
+        if (removed) {
             documentVersion.setDocument(null);
         }
     }
 
 
-    protected int getNextVersionIndex(){
+    protected int getNextVersionIndex() {
         List<DBDocumentVersion> list = getDocumentVersions();
         return list.stream()
                 .map(DBDocumentVersion::getVersion)
-                .reduce(0, (a, b) -> Integer.max(a, b)) + 1;
+                .reduce(0, Integer::max) + 1;
     }
 
 
@@ -135,6 +144,29 @@ public class DBDocument extends BaseEntity {
         this.currentVersion = currentVersion;
     }
 
+    public List<DBDocumentProperty> getDocumentProperties() {
+        if (documentProperties == null) {
+            documentProperties = new ArrayList<>();
+        }
+        return documentProperties;
+    }
+
+    private void removeTransientProperties() {
+        getDocumentProperties().removeIf(DBDocumentProperty::isTransient);
+    }
+
+    @Override
+    public void prePersist() {
+        super.prePersist();
+        removeTransientProperties();
+    }
+
+    @Override
+    public void preUpdate() {
+        super.preUpdate();
+        removeTransientProperties();
+    }
+
 
     public String getMimeType() {
         return mimeType;
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentProperty.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentProperty.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ba6f77a614825d72228e17c95ef1cb063c5f943
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentProperty.java
@@ -0,0 +1,181 @@
+/*-
+ * #START_LICENSE#
+ * smp-server-library
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+package eu.europa.ec.edelivery.smp.data.model.doc;
+
+import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum;
+import eu.europa.ec.edelivery.smp.data.dao.utils.ColumnDescription;
+import eu.europa.ec.edelivery.smp.data.model.BaseEntity;
+import eu.europa.ec.edelivery.smp.data.model.CommonColumnsLengths;
+import eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType;
+import org.apache.commons.lang3.StringUtils;
+import org.hibernate.annotations.GenericGenerator;
+import org.hibernate.envers.Audited;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+import java.util.StringJoiner;
+
+/**
+ * Document property entity
+ *
+ * @since 5.1
+ * @author Joze Rihtarsic
+ */
+
+@Entity
+@Audited
+@Table(name = "SMP_DOCUMENT_PROPERTY",
+        indexes = {@Index(name = "SMP_DOC_PROP_IDX", columnList = "FK_DOCUMENT_ID, PROPERTY_NAME", unique = true)
+        })
+public class DBDocumentProperty extends BaseEntity {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO, generator = "SMP_DOC_PROP_SEQ")
+    @GenericGenerator(name = "SMP_DOC_PROP_SEQ", strategy = "native")
+    @Column(name = "ID")
+    @ColumnDescription(comment = "Unique document property id")
+    Long id;
+
+    @NotNull
+    @Column(name = "PROPERTY_NAME")
+    protected String property;
+
+    @Column(name = "PROPERTY_VALUE", length = CommonColumnsLengths.MAX_MEDIUM_TEXT_LENGTH)
+    private String value;
+
+    @Column(name = "DESCRIPTION", length = CommonColumnsLengths.MAX_FREE_TEXT_LENGTH)
+    @ColumnDescription(comment = "Property description")
+    String description;
+
+    @Column(name = "PROPERTY_TYPE", length = CommonColumnsLengths.MAX_TEXT_LENGTH_64)
+    @Enumerated(EnumType.STRING)
+    private SMPPropertyTypeEnum type;
+
+    @NotNull
+    @ManyToOne
+    @JoinColumn(name = "FK_DOCUMENT_ID")
+    private DBDocument document;
+
+    public DBDocumentProperty() {
+    }
+
+    public DBDocumentProperty(String property, String value, DBDocument document) {
+        this.property = property;
+        this.value = value;
+        this.document = document;
+    }
+
+    public DBDocumentProperty(String property, OffsetDateTime value, DBDocument document) {
+        this.property = property;
+        this.document = document;
+        setDateTime(value);
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    @Override
+    public Long getId() {
+        return id;
+    }
+
+    public String getProperty() {
+        return property;
+    }
+
+    public void setProperty(String property) {
+        this.property = property;
+    }
+
+    public DBDocument getDocument() {
+        return document;
+    }
+
+    public void setDocument(DBDocument document) {
+        this.document = document;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public SMPPropertyTypeEnum getType() {
+        return type;
+    }
+
+    public void setType(SMPPropertyTypeEnum type) {
+        this.type = type;
+    }
+
+    @Transient
+    public void setDateTime(OffsetDateTime date) {
+        setValue(date == null ? null : date.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
+    }
+
+    @Transient
+    public OffsetDateTime getDateTime() {
+        return StringUtils.isBlank(value) ? null : OffsetDateTime.parse(getValue(), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+        DBDocumentProperty that = (DBDocumentProperty) o;
+        return Objects.equals(id, that.id) && Objects.equals(property, that.property) && Objects.equals(value, that.value);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), id, property, value, document);
+    }
+
+    public boolean isTransient() {
+        return TransientDocumentPropertyType.fromPropertyName(getProperty()) != null;
+    }
+
+    @Override
+    public String toString() {
+        return new StringJoiner(", ", DBDocumentProperty.class.getSimpleName() + "[", "]")
+                .add("id=" + id)
+                .add("property='" + property + "'")
+                .add("value='" + value + "'")
+                .add("description='" + description + "'")
+                .add("type=" + type)
+                .add("document=" + document)
+                .toString();
+    }
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java
index e03a7b58fdcf75b4e788f6da10bac76a15f04e8b..02cd71db827e029e6cf52c0a941ebd9f887ad4e4 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java
@@ -71,12 +71,10 @@ public class DBDocumentVersion extends BaseEntity {
     @ColumnDescription(comment = "Unique version document id")
     Long id;
 
-
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "FK_DOCUMENT_ID")
     private DBDocument document;
 
-
     @Column(name = "VERSION", nullable = false)
     private int version;
 
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentPropertyRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentPropertyRO.java
new file mode 100644
index 0000000000000000000000000000000000000000..648a3142beb41173286ce8834e319e853ea07ad3
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentPropertyRO.java
@@ -0,0 +1,75 @@
+package eu.europa.ec.edelivery.smp.data.ui;
+
+
+import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum;
+
+/**
+ * Document property contains values for updating the document variables. The
+ * properties are used with document templates such are ${my.property.name}.
+ *
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+public class DocumentPropertyRO extends BaseRO {
+
+    private static final long serialVersionUID = 9008583888835630036L;
+    private String property;
+    private String value;
+    private String desc;
+    private SMPPropertyTypeEnum type = SMPPropertyTypeEnum.STRING;
+    // the property is readonly and can not be changed. Example of readonly
+    // property is resource identifier
+    private boolean readonly;
+
+    public DocumentPropertyRO() {
+    }
+
+    public DocumentPropertyRO(String property, String value, String desc, boolean readonly) {
+        this.property = property;
+        this.value = value;
+        this.desc = desc;
+        this.readonly = readonly;
+    }
+
+    public String getProperty() {
+        return property;
+    }
+
+    public void setProperty(String property) {
+        this.property = property;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    public SMPPropertyTypeEnum getType() {
+        return type;
+    }
+
+    public void setType(SMPPropertyTypeEnum type) {
+        this.type = type;
+    }
+
+    public boolean isReadonly() {
+        return readonly;
+    }
+
+    public void setReadonly(boolean readonly) {
+        this.readonly = readonly;
+    }
+
+
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRo.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRO.java
similarity index 74%
rename from smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRo.java
rename to smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRO.java
index 8152c4183d43cca711ed22790ea305b319eb273f..d115312a5b676e50b51817f7f95cbcb9e2768e5e 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRo.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRO.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -18,11 +18,14 @@
  */
 package eu.europa.ec.edelivery.smp.data.ui;
 
+import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum;
+import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus;
+
 import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.List;
 
-public class DocumentRo {
+public class DocumentRO extends BaseRO {
 
     String documentId;
     String mimeType;
@@ -31,8 +34,11 @@ public class DocumentRo {
     String name;
     Integer payloadVersion;
     String payload;
+    private int payloadStatus = EntityROStatus.PERSISTED.getStatusNumber();
     OffsetDateTime payloadCreatedOn;
 
+    List<DocumentPropertyRO> properties = new ArrayList<>();
+
     public String getDocumentId() {
         return documentId;
     }
@@ -88,6 +94,14 @@ public class DocumentRo {
         this.payload = payload;
     }
 
+    public int getPayloadStatus() {
+        return payloadStatus;
+    }
+
+    public void setPayloadStatus(int payloadStatus) {
+        this.payloadStatus = payloadStatus;
+    }
+
     public OffsetDateTime getPayloadCreatedOn() {
         return payloadCreatedOn;
     }
@@ -95,4 +109,15 @@ public class DocumentRo {
     public void setPayloadCreatedOn(OffsetDateTime payloadCreatedOn) {
         this.payloadCreatedOn = payloadCreatedOn;
     }
+
+    public List<DocumentPropertyRO> getProperties() {
+        return properties;
+    }
+
+    public void addProperty(String key, String value, String description, SMPPropertyTypeEnum type, boolean readonly) {
+        DocumentPropertyRO propertyRO = new DocumentPropertyRO(key, value, description, readonly);
+        propertyRO.setType(type);
+
+        this.properties.add(propertyRO);
+    }
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/EntityROStatus.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/EntityROStatus.java
index b8847430399cc14d59e89a4ddd3727b593276f82..25cccee05db2b1249916340f34be0bee79c06699 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/EntityROStatus.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/EntityROStatus.java
@@ -19,6 +19,8 @@
 package eu.europa.ec.edelivery.smp.data.ui.enums;
 
 
+import java.util.Arrays;
+
 /**
  * Enumeration of Resource Object status.
  * @author Joze Rihtarsic
@@ -28,7 +30,7 @@ public enum EntityROStatus {
     PERSISTED(0),
     UPDATED(1),
     NEW(2),
-    REMOVE(3),
+    REMOVED(3),
     ERROR(4);
 
     int statusNumber;
@@ -37,7 +39,18 @@ public enum EntityROStatus {
         this.statusNumber = statusNumber;
     }
 
-    public int getStatusNumber() {
+    public final int getStatusNumber() {
         return statusNumber;
     }
+
+    /**
+     * Method returns EntityROStatus based on status number. If status number is not found, method returns null.
+     * @param statusNumber status number
+     * @return EntityROStatus or null
+     */
+    public static EntityROStatus fromStatusNumber(int statusNumber) {
+        return Arrays.stream(EntityROStatus.values())
+                .filter(entityROStatus -> entityROStatus.getStatusNumber() == statusNumber)
+                .findFirst().orElse(null);
+    }
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/IdentifierFormatterService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/IdentifierFormatterService.java
index 1a430ce36ab74e37ccecd841c72aad798f2da6d1..01b27ec7a603981f568f2623395c165582027191 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/IdentifierFormatterService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/IdentifierFormatterService.java
@@ -5,6 +5,7 @@ import eu.europa.ec.dynamicdiscovery.model.identifiers.types.EBCorePartyIdFormat
 import eu.europa.ec.edelivery.smp.config.enums.SMPDomainPropertyEnum;
 import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyEnum;
 import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum;
+import eu.europa.ec.edelivery.smp.data.dao.DomainConfigurationDao;
 import eu.europa.ec.edelivery.smp.data.dao.DomainDao;
 import eu.europa.ec.edelivery.smp.data.model.DBDomain;
 import eu.europa.ec.edelivery.smp.data.model.DBDomainConfiguration;
@@ -33,10 +34,14 @@ public class IdentifierFormatterService {
     private static final Logger LOG = LoggerFactory.getLogger(IdentifierFormatterService.class);
 
     private final DomainDao domainDao;
+    private final DomainConfigurationDao domainConfigurationDao;
     private final ConfigurationService configurationService;
 
-    public IdentifierFormatterService(DomainDao domainDao, ConfigurationService configurationService) {
+    public IdentifierFormatterService(DomainDao domainDao,
+                                      DomainConfigurationDao domainConfigurationDao,
+                                      ConfigurationService configurationService) {
         this.domainDao = domainDao;
+        this.domainConfigurationDao = domainConfigurationDao;
         this.configurationService = configurationService;
     }
 
@@ -53,7 +58,7 @@ public class IdentifierFormatterService {
         DBDomain domain = domainDao.getDomainByCode(domainCode)
                 .orElseThrow(() -> new SMPRuntimeException(ErrorCode.DOMAIN_NOT_EXISTS,  domainCode));
 
-        List<DBDomainConfiguration> listDomainConf =  domainDao.getDomainConfiguration(domain);
+        List<DBDomainConfiguration> listDomainConf =  domainConfigurationDao.getDomainConfiguration(domain);
         IdentifierFormatter identifierFormatter = IdentifierFormatter.Builder
                 .create()
                 .addFormatterTypes(new EBCorePartyIdFormatterType())
@@ -82,7 +87,7 @@ public class IdentifierFormatterService {
         }
         DBDomain domain = domainDao.getDomainByCode(domainCode)
                 .orElseThrow(() -> new SMPRuntimeException(ErrorCode.DOMAIN_NOT_EXISTS,  domainCode));
-        List<DBDomainConfiguration> listDomainConf =  domainDao.getDomainConfiguration(domain);
+        List<DBDomainConfiguration> listDomainConf =  domainConfigurationDao.getDomainConfiguration(domain);
         IdentifierFormatter identifierFormatter = IdentifierFormatter.Builder
                 .create()
                 .build();
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/AbstractResourceHandler.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/AbstractResourceHandler.java
index f30cd5dd7ac9e1ea015b79fff16b2433b97e2a71..fabb69722db7e1e63e1b7e0083871d8bffcc9827 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/AbstractResourceHandler.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/AbstractResourceHandler.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -30,6 +30,7 @@ import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
 import eu.europa.ec.edelivery.smp.services.spi.SPIUtils;
 import eu.europa.ec.edelivery.smp.services.spi.data.SpiRequestData;
 import eu.europa.ec.edelivery.smp.servlet.ResourceResponse;
+import eu.europa.ec.edelivery.smp.utils.StringNamedSubstitutor;
 import eu.europa.ec.smp.spi.api.model.RequestData;
 import eu.europa.ec.smp.spi.api.model.ResponseData;
 import eu.europa.ec.smp.spi.exceptions.ResourceException;
@@ -39,8 +40,11 @@ import eu.europa.ec.smp.spi.resource.SubresourceDefinitionSpi;
 import org.apache.commons.lang3.StringUtils;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
@@ -109,10 +113,18 @@ public class AbstractResourceHandler {
         if (content == null || content.length == 0) {
             throw new SMPRuntimeException(ErrorCode.RESOURCE_DOCUMENT_MISSING, resource.getIdentifierValue(), resource.getIdentifierScheme());
         }
-        ByteArrayInputStream inputStream = new ByteArrayInputStream(content);
-        return buildRequestDataForResource(domain,
-                resource,
-                inputStream);
+        // read and replace properties
+        Map<String, Object> docProp = resourceStorage.getResourceProperties(resource);
+
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            StringNamedSubstitutor.resolve(new ByteArrayInputStream(content), docProp, baos);
+            ByteArrayInputStream inputStream = new ByteArrayInputStream(baos.toByteArray());
+            return buildRequestDataForResource(domain,
+                    resource,
+                    inputStream);
+        } catch (IOException e) {
+            throw new SMPRuntimeException(ErrorCode.RESOURCE_DOCUMENT_MISSING, resource.getIdentifierValue(), resource.getIdentifierScheme());
+        }
     }
 
     public RequestData buildRequestDataForResource(DBDomain domain, DBResource resource, InputStream inputStream) {
@@ -129,10 +141,18 @@ public class AbstractResourceHandler {
                     subresource.getIdentifierValue(), subresource.getIdentifierScheme(),
                     resource.getIdentifierValue(), resource.getIdentifierScheme());
         }
-        return new SpiRequestData(domain.getDomainCode(),
-                SPIUtils.toUrlIdentifier(resource),
-                SPIUtils.toUrlIdentifier(subresource),
-                new ByteArrayInputStream(content));
+
+        Map<String, Object> docProp = resourceStorage.getSubresourceProperties(resource, subresource);
+
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            StringNamedSubstitutor.resolve(new ByteArrayInputStream(content), docProp, baos);
+            return new SpiRequestData(domain.getDomainCode(),
+                    SPIUtils.toUrlIdentifier(resource),
+                    SPIUtils.toUrlIdentifier(subresource),
+                    new ByteArrayInputStream(baos.toByteArray()));
+        } catch (IOException e) {
+            throw new SMPRuntimeException(ErrorCode.RESOURCE_DOCUMENT_MISSING, resource.getIdentifierValue(), resource.getIdentifierScheme());
+        }
     }
 
     public RequestData buildRequestDataForSubResource(DBDomain domain, DBResource resource, DBSubresource subresource, InputStream inputStream) {
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java
index bab20637e86f333953200a7c0e7f0642d4f65ada..50f35d88d0f5c6e244638631173c53eb0a6bf880 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java
@@ -120,7 +120,6 @@ public class ResourceHandlerService extends AbstractResourceHandler {
 
         LOG.debug("Handle the CREATE action for resource request [{}]", resourceRequest);
         // locate the resource handler
-
         ResolvedData resolvedData = resourceRequest.getResolvedData();
         DBResource resource = resolvedData.getResource();
         DBDomain domain = resolvedData.getDomain();
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java
index cb297d3a8a82be2d0926ffc85e77677d578177c0..9d8aba290f9a907848f55aa96a07169e9c7f6ff9 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -28,11 +28,17 @@ import eu.europa.ec.edelivery.smp.data.model.doc.DBResource;
 import eu.europa.ec.edelivery.smp.data.model.doc.DBSubresource;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
+import eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Optional;
 
+import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*;
+
 /**
  * The class handles the resource action as creating, updating and reading the resources.
  *
@@ -54,9 +60,7 @@ public class ResourceStorage {
 
     public byte[] getDocumentContentForResource(DBResource dbResource) {
         LOG.debug("getDocumentContentForResource: [{}]", dbResource);
-
         Optional<DBDocumentVersion> documentVersion = this.documentDao.getCurrentDocumentVersionForResource(dbResource);
-
         return documentVersion.isPresent() ? documentVersion.get().getContent() : null;
     }
 
@@ -68,6 +72,72 @@ public class ResourceStorage {
     }
 
 
+    @Transactional
+    public Map<String, Object> getResourceProperties(DBResource resource) {
+
+        DBDocument document = documentDao.getDocumentForResource(resource).orElseGet(null);
+        if (document == null) {
+            LOG.debug("Document not found for resource [{}]", resource);
+            return Collections.emptyMap();
+        }
+
+        Map<String, Object> documentProperties = new HashMap<>();
+        documentProperties.put(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE.getPropertyName(), resource.getIdentifierValue());
+        if (resource.getIdentifierScheme() != null) {
+            documentProperties.put(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME.getPropertyName(), resource.getIdentifierScheme());
+        }
+        // add document properties
+        documentProperties.putAll(getDocumentProperties(document));
+        return documentProperties;
+    }
+
+    /**
+     * Method returns the 'transient' and custom document properties as map. If
+     * document is null, empty map is returned.
+     *
+     * @param document document
+     * @return document properties the key, value map
+     */
+    private Map<String, Object> getDocumentProperties(DBDocument document) {
+        if (document == null) {
+            return Collections.emptyMap();
+        }
+        Map<String, Object> documentProperties = new HashMap<>();
+        documentProperties.put(DOCUMENT_NAME.getPropertyName(), document.getName());
+        documentProperties.put(DOCUMENT_MIMETYPE.getPropertyName(), document.getMimeType());
+        documentProperties.put(DOCUMENT_VERSION.getPropertyName(), document.getCurrentVersion());
+
+        document.getDocumentProperties().forEach(property -> {
+            documentProperties.put(property.getProperty(), property.getValue());
+        });
+        return documentProperties;
+    }
+
+    @Transactional
+    public Map<String, Object> getSubresourceProperties(DBResource resource, DBSubresource subresource) {
+
+        Map<String, Object> documentProperties = new HashMap<>();
+        DBDocument document = documentDao.getDocumentForSubresource(subresource).orElseGet(null);
+        if (document == null) {
+            LOG.debug("Document not found for subresource [{}]", resource);
+            return Collections.emptyMap();
+        }
+        // add identifiers
+        documentProperties.put(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE.getPropertyName(), resource.getIdentifierValue());
+        if (resource.getIdentifierScheme() != null) {
+            documentProperties.put(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME.getPropertyName(), resource.getIdentifierScheme());
+        }
+        documentProperties.put(SUBRESOURCE_IDENTIFIER_VALUE.getPropertyName(), subresource.getIdentifierValue());
+        if (subresource.getIdentifierScheme() != null) {
+            documentProperties.put(SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyName(), subresource.getIdentifierScheme());
+        }
+
+        // add document properties
+        documentProperties.putAll(getDocumentProperties(document));
+        return documentProperties;
+    }
+
+
     @Transactional
     public DBResource addDocumentVersionForResource(DBResource resource, DBDocumentVersion version) {
         LOG.debug("addDocumentVersionForResource: [{}]", resource);
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java
index efe5fb109fd401cc14db8d6d352187a855cebe80..fe70089bf84bc5c88b29143e2ec0e7f47ea43354 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -18,16 +18,16 @@
  */
 package eu.europa.ec.edelivery.smp.services.ui;
 
+import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum;
 import eu.europa.ec.edelivery.smp.data.dao.DocumentDao;
 import eu.europa.ec.edelivery.smp.data.dao.ResourceDao;
 import eu.europa.ec.edelivery.smp.data.dao.SubresourceDao;
 import eu.europa.ec.edelivery.smp.data.model.DBDomainResourceDef;
-import eu.europa.ec.edelivery.smp.data.model.doc.DBDocument;
-import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersion;
-import eu.europa.ec.edelivery.smp.data.model.doc.DBResource;
-import eu.europa.ec.edelivery.smp.data.model.doc.DBSubresource;
+import eu.europa.ec.edelivery.smp.data.model.doc.*;
 import eu.europa.ec.edelivery.smp.data.model.ext.DBSubresourceDef;
-import eu.europa.ec.edelivery.smp.data.ui.DocumentRo;
+import eu.europa.ec.edelivery.smp.data.ui.DocumentPropertyRO;
+import eu.europa.ec.edelivery.smp.data.ui.DocumentRO;
+import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus;
 import eu.europa.ec.edelivery.smp.exceptions.ErrorCode;
 import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
@@ -36,6 +36,7 @@ import eu.europa.ec.edelivery.smp.services.resource.ResourceHandlerService;
 import eu.europa.ec.edelivery.smp.services.spi.data.SpiResponseData;
 import eu.europa.ec.smp.spi.api.model.RequestData;
 import eu.europa.ec.smp.spi.api.model.ResponseData;
+import eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType;
 import eu.europa.ec.smp.spi.exceptions.ResourceException;
 import eu.europa.ec.smp.spi.resource.ResourceHandlerSpi;
 import org.apache.commons.lang3.exception.ExceptionUtils;
@@ -44,10 +45,15 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
+
+import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*;
 
 @Service
 public class UIDocumentService {
+
     private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIDocumentService.class);
     ResourceDao resourceDao;
     SubresourceDao subresourceDao;
@@ -62,7 +68,7 @@ public class UIDocumentService {
     }
 
     @Transactional
-    public void validateDocumentForResource(Long resourceId, DocumentRo documentRo) {
+    public void validateDocumentForResource(Long resourceId, DocumentRO documentRo) {
         DBResource resource = resourceDao.find(resourceId);
         DBDomainResourceDef domainResourceDef = resource.getDomainResourceDef();
         ResourceHandlerSpi resourceHandler = resourceHandlerService.getResourceHandler(domainResourceDef.getResourceDef());
@@ -75,7 +81,7 @@ public class UIDocumentService {
     }
 
     @Transactional
-    public void validateDocumentForSubresource(Long subresourceId, Long resourceId, DocumentRo documentRo) {
+    public void validateDocumentForSubresource(Long subresourceId, Long resourceId, DocumentRO documentRo) {
         DBSubresource entity = subresourceDao.find(subresourceId);
         DBResource parentEntity = resourceDao.find(resourceId);
         ResourceHandlerSpi resourceHandler = resourceHandlerService.getSubresourceHandler(entity.getSubresourceDef(), entity.getSubresourceDef().getResourceDef());
@@ -88,20 +94,20 @@ public class UIDocumentService {
     }
 
     @Transactional
-    public DocumentRo generateDocumentForResource(Long resourceId, DocumentRo documentRo) {
-        LOG.info("Generate document");
+    public DocumentRO generateDocumentForResource(Long resourceId, DocumentRO documentRo) {
+        LOG.info("generate Document For Resource");
         DBResource resource = resourceDao.find(resourceId);
         DBDomainResourceDef domainResourceDef = resource.getDomainResourceDef();
         ResourceHandlerSpi resourceHandler = resourceHandlerService.getResourceHandler(domainResourceDef.getResourceDef());
         RequestData data = resourceHandlerService.buildRequestDataForResource(domainResourceDef.getDomain(),
                 resource, null);
 
-        return getDocumentRo(resourceHandler, data);
+        return generateDocumentWithHandler(resourceHandler, data);
     }
 
     @Transactional
-    public DocumentRo generateDocumentForSubresource(Long subresourceId, Long resourceId, DocumentRo documentRo) {
-        LOG.info("Generate document");
+    public DocumentRO generateDocumentForSubresource(Long subresourceId, Long resourceId, DocumentRO documentRo) {
+        LOG.info("generate Document For Subresource");
         DBResource parentEntity = resourceDao.find(resourceId);
         DBSubresource entity = subresourceDao.find(subresourceId);
         DBSubresourceDef subresourceDef = entity.getSubresourceDef();
@@ -110,10 +116,17 @@ public class UIDocumentService {
         RequestData data = resourceHandlerService.buildRequestDataForSubResource(parentEntity.getDomainResourceDef().getDomain(),
                 parentEntity, entity, null);
 
-        return getDocumentRo(resourceHandler, data);
+        return generateDocumentWithHandler(resourceHandler, data);
     }
 
-    private DocumentRo getDocumentRo(ResourceHandlerSpi resourceHandler, RequestData data) {
+    /**
+     * Generate document with ResourceHandlerSpi. Method invokes the given handler to generate the document.
+     *
+     * @param resourceHandler handler to generate document
+     * @param data            request data
+     * @return DocumentRo with payload
+     */
+    private DocumentRO generateDocumentWithHandler(ResourceHandlerSpi resourceHandler, RequestData data) {
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         ResponseData responseData = new SpiResponseData(bos);
         try {
@@ -122,33 +135,153 @@ public class UIDocumentService {
             throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "StoreResourceValidation", ExceptionUtils.getRootCauseMessage(e));
         }
         String genDoc = bos.toString();
-        LOG.info("Generate document [{}]", genDoc);
-        DocumentRo result = new DocumentRo();
+        DocumentRO result = new DocumentRO();
         result.setPayload(genDoc);
         return result;
     }
 
     @Transactional
-    public DocumentRo saveDocumentForResource(Long resourceId, DocumentRo documentRo) {
+    public DocumentRO saveDocumentForResource(Long resourceId, DocumentRO documentRo) {
 
-        DBResource resource = resourceDao.find(resourceId);
+        final DBResource resource = resourceDao.find(resourceId);
+        final DBDocument document = resource.getDocument();
+
+        boolean isPayloadChanged = documentRo.getPayloadStatus() != EntityROStatus.PERSISTED.getStatusNumber();
+        if (isPayloadChanged) {
+            LOG.debug("Store resource payload for resource [{}]", resource.getIdentifierValue());
+            storeResourcePayload(resource, document, documentRo);
+        }
+
+        if (isDocumentPropertiesChanged(documentRo)) {
+            // persist non-transient properties
+            documentRo.getProperties().stream().filter(p ->
+                            TransientDocumentPropertyType.fromPropertyName(p.getProperty()) == null)
+                    .forEach(p -> persistDocumentProperty(p, document));
+        }
+
+        return convertWithVersion(document, document.getCurrentVersion(), getInitialProperties(resource));
+    }
+
+
+    /**
+     * Method persists the document property. If property has status DELETED removes the property from database
+     * if property is UPDATED, the existing property entity is updated and if property is new then new property is
+     * created.
+     *
+     * @param documentPropertyRO Document Property RO to persist
+     * @DBDocument db document to which the property belongs
+     */
+    private void persistDocumentProperty(DocumentPropertyRO documentPropertyRO, DBDocument dbDocument) {
+
+        EntityROStatus status = EntityROStatus.fromStatusNumber(documentPropertyRO.getStatus());
+        status = status == null ? EntityROStatus.PERSISTED : status;
+
+
+        DBDocumentProperty dbDocumentProperty = dbDocument.getDocumentProperties().stream()
+                .filter(p -> p.getProperty().equals(documentPropertyRO.getProperty()))
+                .findFirst().orElse(null);
+
+        switch (status) {
+            case REMOVED:
+                if (dbDocumentProperty != null) {
+                    dbDocument.getDocumentProperties().remove(dbDocumentProperty);
+                } else {
+                    LOG.warn("Document property [{}] not found for document [{}]", documentPropertyRO.getProperty(), dbDocument.getId());
+                }
+                break;
+            case UPDATED:
+                if (dbDocumentProperty != null) {
+                    dbDocumentProperty.setDescription(documentPropertyRO.getDesc());
+                    dbDocumentProperty.setValue(documentPropertyRO.getValue());
+                } else {
+                    LOG.warn("Document property [{}] not found for document [{}]. property is added", documentPropertyRO.getProperty(), dbDocument.getId());
+                    addDocumentProperty(documentPropertyRO, dbDocument);
+                }
+                break;
+            case NEW:
+                if (dbDocumentProperty == null) {
+                    addDocumentProperty(documentPropertyRO, dbDocument);
+                } else {
+                    LOG.warn("Document property [{}] already exists for document [{}]. property is updated",
+                            documentPropertyRO.getProperty(), dbDocument.getId());
+                    dbDocumentProperty.setDescription(documentPropertyRO.getDesc());
+                    dbDocumentProperty.setValue(documentPropertyRO.getValue());
+                }
+                break;
+            default:
+                LOG.warn("Document property [{}] status [{}] indicates no change!",
+                        documentPropertyRO.getProperty(), status);
+        }
+    }
+
+    /**
+     * Method adds new  Document Property to the DBDocument property list
+     *
+     * @param documentPropertyRO Document Property RO to persist
+     * @param dbDocument         db document to which the property belongs
+     */
+    private void addDocumentProperty(DocumentPropertyRO documentPropertyRO, DBDocument dbDocument) {
+        DBDocumentProperty dbDocumentProperty = new DBDocumentProperty();
+        dbDocumentProperty.setDocument(dbDocument);
+        dbDocumentProperty.setProperty(documentPropertyRO.getProperty());
+        dbDocumentProperty.setValue(documentPropertyRO.getValue());
+        dbDocumentProperty.setDescription(documentPropertyRO.getDesc());
+        dbDocumentProperty.setType(documentPropertyRO.getType());
+
+        dbDocument.getDocumentProperties().add(dbDocumentProperty);
+    }
+
+    /**
+     * Method validates if any "non-transient" of the document properties are changed. If the document does not have
+     * any properties or all exiting properties return status PERSISTED it returns false otherwise true.
+     *
+     * @param documentRo document to validate
+     * @retun true if any of the properties changed otherwise false
+     */
+    private boolean isDocumentPropertiesChanged(DocumentRO documentRo) {
+        return documentRo != null && documentRo.getProperties() != null && documentRo.getProperties().stream()
+                .filter(p -> TransientDocumentPropertyType.fromPropertyName(p.getProperty()) == null)
+                .anyMatch(p -> p.getStatus() != EntityROStatus.PERSISTED.getStatusNumber());
+    }
+
+    /**
+     * Method stores the payload for the given resource as a new payload version.
+     * The method invokes the ResourceHandlerSpi to update/validate the payload before storing it to database.
+     *
+     * @param resource   resource to store the payload
+     * @param document   the resource database document entity
+     * @param documentRo document RO the with new payload
+     */
+    private void storeResourcePayload(DBResource resource, DBDocument document, DocumentRO documentRo) {
         DBDomainResourceDef domainResourceDef = resource.getDomainResourceDef();
-        ResourceHandlerSpi resourceHandler = resourceHandlerService.getResourceHandler(domainResourceDef.getResourceDef());
-        RequestData data = resourceHandlerService.buildRequestDataForResource(domainResourceDef.getDomain(), resource, new ByteArrayInputStream(documentRo.getPayload().getBytes()));
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        ResponseData responseData = new SpiResponseData(bos);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        // invoke the resource handler for the document type
+        ResourceHandlerSpi resourceHandler = resourceHandlerService.getResourceHandler(
+                domainResourceDef.getResourceDef());
+        RequestData data = resourceHandlerService.buildRequestDataForResource(domainResourceDef.getDomain(),
+                resource, new ByteArrayInputStream(documentRo.getPayload().getBytes()));
+        ResponseData responseData = new SpiResponseData(baos);
         try {
             resourceHandler.storeResource(data, responseData);
         } catch (ResourceException e) {
             throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "StoreResourceValidation", ExceptionUtils.getRootCauseMessage(e));
         }
+        // create new version to document
+        int version = document.getDocumentVersions().stream().mapToInt(DBDocumentVersion::getVersion)
+                .max().orElse(0);
 
-        DBDocument document = resource.getDocument();
-        return createNewVersionAndConvert(document, bos);
+        DBDocumentVersion documentVersion = new DBDocumentVersion();
+        documentVersion.setVersion(version + 1);
+        documentVersion.setDocument(document);
+        documentVersion.setContent(baos.toByteArray());
+        // to get the current persist time
+        documentVersion.prePersist();
+        document.getDocumentVersions().add(documentVersion);
+        document.setCurrentVersion(documentVersion.getVersion());
     }
 
     @Transactional
-    public DocumentRo saveSubresourceDocumentForResource(Long subresource, Long resourceId, DocumentRo documentRo) {
+    public DocumentRO saveSubresourceDocumentForResource(Long subresource, Long resourceId, DocumentRO documentRo) {
 
         DBResource parentResource = resourceDao.find(resourceId);
         DBSubresource entity = subresourceDao.find(subresource);
@@ -166,7 +299,7 @@ public class UIDocumentService {
         }
 
         DBDocument document = entity.getDocument();
-        return createNewVersionAndConvert(document, bos);
+        return createNewVersionAndConvert(document, bos, getInitialProperties(entity));
     }
 
     /**
@@ -178,19 +311,40 @@ public class UIDocumentService {
      * @return DocumentRo with payload and version
      */
     @Transactional
-    public DocumentRo getDocumentForResource(Long resourceId, int version) {
+    public DocumentRO getDocumentForResource(Long resourceId, int version) {
         DBResource resource = resourceDao.find(resourceId);
         DBDocument document = resource.getDocument();
-        return convertWithVersion(document, version);
+
+
+        return convertWithVersion(document, version, getInitialProperties(resource));
     }
 
     @Transactional
-    public DocumentRo getDocumentForSubResource(Long subresourceId, Long resourceId, int version) {
+    public DocumentRO getDocumentForSubResource(Long subresourceId, Long resourceId, int version) {
         DBSubresource subresource = subresourceDao.find(subresourceId);
         DBDocument document = subresource.getDocument();
-        return convertWithVersion(document, version);
+        return convertWithVersion(document, version, getInitialProperties(subresource));
+    }
+
+    private List<DocumentPropertyRO> getInitialProperties(DBResource resource) {
+        List<DocumentPropertyRO> propertyROS = new ArrayList<>();
+        propertyROS.add(new DocumentPropertyRO(RESOURCE_IDENTIFIER_VALUE.getPropertyName(),
+                resource.getIdentifierValue(), RESOURCE_IDENTIFIER_VALUE.getPropertyDescription(), true));
+        propertyROS.add(new DocumentPropertyRO(RESOURCE_IDENTIFIER_SCHEME.getPropertyName(),
+                resource.getIdentifierScheme(), RESOURCE_IDENTIFIER_SCHEME.getPropertyDescription(), true));
+        return propertyROS;
     }
 
+    private List<DocumentPropertyRO> getInitialProperties(DBSubresource subresource) {
+        List<DocumentPropertyRO> propertyROS = getInitialProperties(subresource.getResource());
+        propertyROS.add(new DocumentPropertyRO(SUBRESOURCE_IDENTIFIER_VALUE.getPropertyName(),
+                subresource.getIdentifierValue(), SUBRESOURCE_IDENTIFIER_VALUE.getPropertyDescription(), true));
+        propertyROS.add(new DocumentPropertyRO(SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyName(),
+                subresource.getIdentifierScheme(), SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyDescription(), true));
+        return propertyROS;
+    }
+
+
     /**
      * return Create new Document version and convert DBDocument  to DocumentRo
      *
@@ -198,7 +352,7 @@ public class UIDocumentService {
      * @param baos     to write document content
      * @return DocumentRo
      */
-    private DocumentRo createNewVersionAndConvert(DBDocument document, ByteArrayOutputStream baos) {
+    private DocumentRO createNewVersionAndConvert(DBDocument document, ByteArrayOutputStream baos, List<DocumentPropertyRO> initialProperties) {
 
         // get max version
         int version = document.getDocumentVersions().stream().mapToInt(DBDocumentVersion::getVersion)
@@ -213,10 +367,10 @@ public class UIDocumentService {
 
         document.getDocumentVersions().add(documentVersion);
         document.setCurrentVersion(documentVersion.getVersion());
-        return convert(document, documentVersion);
+        return convert(document, documentVersion, initialProperties);
     }
 
-    public DocumentRo convertWithVersion(DBDocument document, int version) {
+    public DocumentRO convertWithVersion(DBDocument document, int version, List<DocumentPropertyRO> initialProperties) {
         DBDocumentVersion currentVersion = null;
         DBDocumentVersion documentVersion = null;
         for (DBDocumentVersion dv : document.getDocumentVersions()) {
@@ -231,11 +385,18 @@ public class UIDocumentService {
         if (documentVersion == null && !document.getDocumentVersions().isEmpty()) {
             documentVersion = document.getDocumentVersions().get(document.getDocumentVersions().size() - 1);
         }
-        return convert(document, documentVersion);
+        return convert(document, documentVersion, initialProperties);
     }
 
-    public DocumentRo convert(DBDocument document, DBDocumentVersion version) {
-        DocumentRo documentRo = new DocumentRo();
+    public DocumentRO convert(DBDocument document, DBDocumentVersion version, List<DocumentPropertyRO> initialProperties) {
+        DocumentRO documentRo = new DocumentRO();
+        documentRo.addProperty(DOCUMENT_NAME.getPropertyName(),
+                document.getName(), "Document Name", SMPPropertyTypeEnum.STRING, true);
+        documentRo.addProperty(DOCUMENT_MIMETYPE.getPropertyName(),
+                document.getMimeType(), "Document Mimetype", SMPPropertyTypeEnum.STRING, true);
+
+        documentRo.getProperties().addAll(initialProperties);
+
         // set list of versions
         document.getDocumentVersions().forEach(dv ->
                 documentRo.getAllVersions().add(dv.getVersion()));
@@ -243,11 +404,22 @@ public class UIDocumentService {
         documentRo.setMimeType(document.getMimeType());
         documentRo.setName(document.getName());
         documentRo.setCurrentResourceVersion(document.getCurrentVersion());
+        document.getDocumentProperties().stream()
+                .forEach(p -> {
+                    documentRo.addProperty(p.getProperty(),
+                            p.getValue(),
+                            p.getDescription(),
+                            p.getType(), false);
+                    LOG.info("Document property [{}] added to document [{}]", p);
+                });
+
+
         if (version != null) {
             documentRo.setPayloadCreatedOn(version.getCreatedOn());
             documentRo.setPayloadVersion(version.getVersion());
             documentRo.setPayload(new String(version.getContent()));
         }
+
         return documentRo;
     }
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainAdminService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainAdminService.java
index 5b515363ceaff481ab4f63391d79d1399aac91b1..cbe6ffc578069cd1e58ffcf2bb295fb9ef08607f 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainAdminService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainAdminService.java
@@ -18,11 +18,9 @@
  */
 package eu.europa.ec.edelivery.smp.services.ui;
 
-import eu.europa.ec.edelivery.smp.config.enums.SMPDomainPropertyEnum;
 import eu.europa.ec.edelivery.smp.data.dao.*;
 import eu.europa.ec.edelivery.smp.data.enums.VisibilityType;
 import eu.europa.ec.edelivery.smp.data.model.DBDomain;
-import eu.europa.ec.edelivery.smp.data.model.DBDomainConfiguration;
 import eu.europa.ec.edelivery.smp.data.model.DBDomainResourceDef;
 import eu.europa.ec.edelivery.smp.data.model.DBGroup;
 import eu.europa.ec.edelivery.smp.data.model.ext.DBResourceDef;
@@ -31,6 +29,7 @@ import eu.europa.ec.edelivery.smp.data.model.user.DBGroupMember;
 import eu.europa.ec.edelivery.smp.data.ui.DomainPropertyRO;
 import eu.europa.ec.edelivery.smp.data.ui.DomainRO;
 import eu.europa.ec.edelivery.smp.data.ui.ServiceResult;
+import eu.europa.ec.edelivery.smp.data.ui.auth.SMPRole;
 import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus;
 import eu.europa.ec.edelivery.smp.exceptions.BadRequestException;
 import eu.europa.ec.edelivery.smp.exceptions.ErrorBusinessCode;
@@ -45,7 +44,6 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.*;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -61,6 +59,7 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> {
     private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIDomainAdminService.class);
 
     private final DomainDao domainDao;
+    private final DomainConfigurationDao domainConfigurationDao;
     private final DomainMemberDao domainMemberDao;
     private final ResourceDao resourceDao;
     private final ResourceDefDao resourceDefDao;
@@ -72,6 +71,7 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> {
 
     public UIDomainAdminService(ConversionService conversionService,
                                 DomainDao domainDao,
+                                DomainConfigurationDao domainConfigurationDao,
                                 DomainMemberDao domainMemberDao,
                                 ResourceDao resourceDao,
                                 ResourceDefDao resourceDefDao,
@@ -81,6 +81,7 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> {
                                 SMLIntegrationService smlIntegrationService) {
         this.conversionService = conversionService;
         this.domainDao = domainDao;
+        this.domainConfigurationDao = domainConfigurationDao;
         this.resourceDao = resourceDao;
         this.resourceDefDao = resourceDefDao;
         this.domainResourceDefDao = domainResourceDefDao;
@@ -240,18 +241,9 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> {
         if (domain == null) {
             throw new BadRequestException(ErrorBusinessCode.NOT_FOUND, "Domain does not exist in database!");
         }
-        List<DBDomainConfiguration> domainConfiguration = domainDao.getDomainConfiguration(domain);
-
-        Map<String, DomainPropertyRO> dbList = domainConfiguration.stream()
-                .map(dc -> conversionService.convert(dc, DomainPropertyRO.class))
-                .collect(Collectors.toMap(DomainPropertyRO::getProperty, dp -> dp));
-
-        return Arrays.stream(SMPDomainPropertyEnum.values()).map(enumType -> {
-            if (dbList.containsKey(enumType.getProperty())) {
-                return dbList.get(enumType.getProperty());
-            }
-            return conversionService.convert(enumType, DomainPropertyRO.class);
-        }).filter(Objects::nonNull).collect(Collectors.toList());
+        return domainConfigurationDao.getDomainPropertiesForRole(domain, SMPRole.SYSTEM_ADMIN).stream()
+                .map(domainConfiguration -> conversionService.convert(domainConfiguration, DomainPropertyRO.class))
+                .collect(Collectors.toList());
     }
 
     @Transactional
@@ -260,38 +252,9 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> {
         if (domain == null) {
             throw new BadRequestException(ErrorBusinessCode.NOT_FOUND, "Domain does not exist in database!");
         }
-        // get current domain configuration
-        Map<String, DBDomainConfiguration> currentDomainConfiguration = domainDao.getDomainConfiguration(domain)
-                .stream().collect(Collectors.toMap(DBDomainConfiguration::getProperty, Function.identity()));
-        Map<String, DomainPropertyRO> newDomainPropertyValues =
-                domainProperties.stream().collect(Collectors.toMap(DomainPropertyRO::getProperty, dp -> dp));
-
-        List<DBDomainConfiguration> listOfDomainConfiguration = new ArrayList<>();
-
-        // database domain configuration property list must match SMPDomainPropertyEnum
-        for (SMPDomainPropertyEnum domainProp : SMPDomainPropertyEnum.values()) {
-            DBDomainConfiguration domainConfiguration = currentDomainConfiguration.get(domainProp.getProperty());
-            DomainPropertyRO domainPropertyRO = newDomainPropertyValues.get(domainProp.getProperty());
-            // if property already exists in the database, update value
-            DBDomainConfiguration updatedDomainProp = domainDao.updateDomainProperty(domain, domainProp,
-                    domainConfiguration, domainPropertyRO);
-            listOfDomainConfiguration.add(updatedDomainProp);
-            // remove updated property from the map
-            currentDomainConfiguration.remove(domainProp.getProperty());
-            LOG.debug("Updated domain property [{}]: [{}] for domain [{}]",
-                    domainProp.getProperty(), updatedDomainProp, domain.getDomainCode());
-
-        }
-        // remove properties that are not in the new list
-        currentDomainConfiguration.values().forEach(domainConfiguration -> {
-            domainDao.removeConfiguration(domainConfiguration);
-            LOG.debug("Removed domain property [{}]: [{}] for domain [{}]",
-                    domainConfiguration.getProperty(), domainConfiguration.getValue(),
-                    domain.getDomainCode());
-        });
-
-        // up
-        return listOfDomainConfiguration.stream().map(dc -> conversionService.convert(dc, DomainPropertyRO.class))
+        return domainConfigurationDao.updateDomainPropertiesForRole(domain, domainProperties, SMPRole.SYSTEM_ADMIN)
+        .stream()
+                .map(domainConfiguration -> conversionService.convert(domainConfiguration, DomainPropertyRO.class))
                 .collect(Collectors.toList());
     }
 
@@ -339,7 +302,7 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> {
         // finally remove the domain
         domainDao.remove(domain);
         DomainRO domainRO = conversionService.convert(domain, DomainRO.class);
-        domainRO.setStatus(EntityROStatus.REMOVE.getStatusNumber());
+        domainRO.setStatus(EntityROStatus.REMOVED.getStatusNumber());
         return domainRO;
     }
 
@@ -350,6 +313,4 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> {
         }
         groupDao.remove(group);
     }
-
-
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainEditService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainEditService.java
index c33165fb5e48a51ab6da9fcd4d87d1c5738ee4c6..d2bfbec09cf3adefdc3e4b45512029ed0409f41e 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainEditService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainEditService.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -18,34 +18,26 @@
  */
 package eu.europa.ec.edelivery.smp.services.ui;
 
-import eu.europa.ec.edelivery.smp.config.SMPEnvironmentProperties;
-import eu.europa.ec.edelivery.smp.config.enums.SMPDomainPropertyEnum;
-import eu.europa.ec.edelivery.smp.config.enums.SMPEnvPropertyEnum;
-import eu.europa.ec.edelivery.smp.data.dao.BaseDao;
-import eu.europa.ec.edelivery.smp.data.dao.DomainDao;
-import eu.europa.ec.edelivery.smp.data.dao.DomainMemberDao;
-import eu.europa.ec.edelivery.smp.data.dao.UserDao;
+import eu.europa.ec.edelivery.smp.data.dao.*;
 import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType;
 import eu.europa.ec.edelivery.smp.data.model.DBDomain;
-import eu.europa.ec.edelivery.smp.data.model.DBDomainConfiguration;
 import eu.europa.ec.edelivery.smp.data.model.DBDomainResourceDef;
 import eu.europa.ec.edelivery.smp.data.model.user.DBDomainMember;
 import eu.europa.ec.edelivery.smp.data.model.user.DBUser;
 import eu.europa.ec.edelivery.smp.data.ui.*;
+import eu.europa.ec.edelivery.smp.data.ui.auth.SMPRole;
 import eu.europa.ec.edelivery.smp.exceptions.BadRequestException;
 import eu.europa.ec.edelivery.smp.exceptions.ErrorBusinessCode;
 import eu.europa.ec.edelivery.smp.exceptions.ErrorCode;
 import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
-import eu.europa.ec.edelivery.smp.utils.PropertyUtils;
 import org.springframework.core.convert.ConversionService;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.io.File;
-import java.nio.file.Paths;
-import java.util.*;
+import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 /**
@@ -60,14 +52,17 @@ public class UIDomainEditService extends UIServiceBase<DBDomain, DomainPublicRO>
 
     private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIDomainEditService.class);
     private final DomainDao domainDao;
-
+    private final DomainConfigurationDao domainConfigurationDao;
     private final DomainMemberDao domainMemberDao;
     private final UserDao userDao;
     private final ConversionService conversionService;
 
 
-    public UIDomainEditService(DomainDao domainDao, DomainMemberDao domainMemberDao, ConversionService conversionService, UserDao userDao) {
+    public UIDomainEditService(DomainDao domainDao,
+                               DomainConfigurationDao domainConfigurationDao,
+                               DomainMemberDao domainMemberDao, ConversionService conversionService, UserDao userDao) {
         this.domainDao = domainDao;
+        this.domainConfigurationDao = domainConfigurationDao;
         this.domainMemberDao = domainMemberDao;
         this.conversionService = conversionService;
         this.userDao = userDao;
@@ -81,11 +76,11 @@ public class UIDomainEditService extends UIServiceBase<DBDomain, DomainPublicRO>
     /**
      * Method returns Domain resource object list for page.
      *
-     * @param page     - page number
-     * @param pageSize - page size
+     * @param page      - page number
+     * @param pageSize  - page size
      * @param sortField - sort field
      * @param sortOrder - sort order
-     * @param filter  - filter
+     * @param filter    - filter
      * @return ServiceResult<DomainPublicRO> - list of domain resource objects
      */
     @Override
@@ -187,6 +182,7 @@ public class UIDomainEditService extends UIServiceBase<DBDomain, DomainPublicRO>
 
     /**
      * Method returns all Domain properties which are not tagged as system admin only!
+     *
      * @param domainId - domain to get properties
      * @return list of domain properties
      */
@@ -195,111 +191,30 @@ public class UIDomainEditService extends UIServiceBase<DBDomain, DomainPublicRO>
         if (domain == null) {
             throw new BadRequestException(ErrorBusinessCode.NOT_FOUND, "Domain does not exist in database!");
         }
-        List<DBDomainConfiguration> domainConfiguration = domainDao.getDomainConfiguration(domain);
-
-        Map<String, DomainPropertyRO> dbList = domainConfiguration.stream()
-                .map(dc -> conversionService.convert(dc, DomainPropertyRO.class))
-                .collect(Collectors.toMap(DomainPropertyRO::getProperty, dp -> dp));
-        // return only properties that are not system admin only
-        return Arrays.stream(SMPDomainPropertyEnum.values())
-                .filter(SMPDomainPropertyEnum::isNotSystemAdminOnly)
-                .map(enumType -> {
-            if (dbList.containsKey(enumType.getProperty())) {
-                return dbList.get(enumType.getProperty());
-            }
-            return conversionService.convert(enumType, DomainPropertyRO.class);
-        }).filter(Objects::nonNull).collect(Collectors.toList());
+        return domainConfigurationDao.getDomainPropertiesForRole(domain, SMPRole.USER).stream()
+                .map(property -> conversionService.convert(property, DomainPropertyRO.class))
+                .collect(Collectors.toList());
     }
 
     /**
      * Method updates domain properties which are not system admin only.
-     * @param domainId - domain to update properties
+     *
+     * @param domainId         - domain to update properties
      * @param domainProperties - list of domain properties
      * @return list of updated domain properties
      */
     @Transactional
-    public List<DomainPropertyRO>  updateDomainEditProperties(Long domainId, List<DomainPropertyRO> domainProperties) {
+    public List<DomainPropertyRO> updateDomainEditProperties(Long domainId, List<DomainPropertyRO> domainProperties) {
         DBDomain domain = domainDao.find(domainId);
         if (domain == null) {
             throw new BadRequestException(ErrorBusinessCode.NOT_FOUND, "Domain does not exist in database!");
         }
-        // get current domain configuration
-        Map<String, DBDomainConfiguration> currentDomainConfiguration = domainDao.getDomainConfiguration(domain)
-                .stream().collect(Collectors.toMap(DBDomainConfiguration::getProperty, dp -> dp));
-        Map<String, DomainPropertyRO> newDomainPropertyValues =
-                domainProperties.stream().collect(Collectors.toMap(DomainPropertyRO::getProperty, dp -> dp));
-
-        List<DBDomainConfiguration> listOfDomainConfiguration = new ArrayList<>();
-
-        // database domain configuration property list must match SMPDomainPropertyEnum
-        for (SMPDomainPropertyEnum domainProp: SMPDomainPropertyEnum.values()) {
-            if (domainProp.isSystemAdminOnly()) {
-                // skip system admin only properties
-                continue;
-            }
-            DBDomainConfiguration domainConfiguration = currentDomainConfiguration.get(domainProp.getProperty());
-            DomainPropertyRO domainPropertyRO = newDomainPropertyValues.get(domainProp.getProperty());
-            // if property already exists in the database, update value
-            DBDomainConfiguration updatedDomainProp =  domainDao.updateDomainProperty(domain, domainProp, domainConfiguration, domainPropertyRO);
-            listOfDomainConfiguration.add(updatedDomainProp);
-            // remove updated property from the map
-            currentDomainConfiguration.remove(domainProp.getProperty());
-            LOG.debug("Updated domain property [{}]: [{}] for domain [{}]",
-                    domainProp.getProperty(), updatedDomainProp, domain.getDomainCode());
-
-        }
-        // remove properties that are not in the new list
-        currentDomainConfiguration.values().forEach(domainConfiguration -> {
-            domainDao.removeConfiguration(domainConfiguration);
-            LOG.debug("Removed domain property [{}]: [{}] for domain [{}]",
-                    domainConfiguration.getProperty(), domainConfiguration.getValue(),
-                    domain.getDomainCode());
-        });
-
-        // up
-        return listOfDomainConfiguration.stream().map(dc -> conversionService.convert(dc, DomainPropertyRO.class))
+        return domainConfigurationDao.updateDomainPropertiesForRole(domain, domainProperties, SMPRole.USER).stream()
+                .map(property -> conversionService.convert(property, DomainPropertyRO.class))
                 .collect(Collectors.toList());
     }
 
-
     public PropertyValidationRO validateDomainProperty(PropertyRO propertyRO) {
-        LOG.info("Validate property: [{}]", propertyRO.getProperty());
-        PropertyValidationRO propertyValidationRO = new PropertyValidationRO();
-        propertyValidationRO.setProperty(propertyRO.getProperty());
-        propertyValidationRO.setValue(propertyRO.getValue());
-
-        Optional<SMPDomainPropertyEnum> optPropertyEnum = SMPDomainPropertyEnum.getByProperty(propertyRO.getProperty());
-        if (!optPropertyEnum.isPresent()) {
-            LOG.debug("Property: [{}] is not Domain SMP property!", propertyRO.getProperty());
-            propertyValidationRO.setErrorMessage("Property [" + propertyRO.getProperty() + "] is not SMP property!");
-            propertyValidationRO.setPropertyValid(false);
-            return propertyValidationRO;
-        }
-
-        SMPDomainPropertyEnum propertyEnum = optPropertyEnum.get();
-        return validateDomainPropertyValue(propertyEnum, propertyValidationRO);
-    }
-
-    /**
-     * Method validates domain property value for given property enum.
-     * @param propertyEnum - property enum
-     * @param propertyValidationRO - property validation object with value.
-     * @return property validation object with validation result and error message if validation failed.
-     */
-    private PropertyValidationRO validateDomainPropertyValue(SMPDomainPropertyEnum propertyEnum,
-                                                             PropertyValidationRO propertyValidationRO) {
-        // try to parse value
-        try {
-            File confDir = Paths.get(SMPEnvironmentProperties.getInstance().getEnvPropertyValue(SMPEnvPropertyEnum.SECURITY_FOLDER)).toFile();
-            PropertyUtils.parseProperty(propertyEnum.getPropertyEnum(), propertyValidationRO.getValue(), confDir);
-        } catch (SMPRuntimeException ex) {
-            propertyValidationRO.setErrorMessage(ex.getMessage());
-            propertyValidationRO.setPropertyValid(false);
-            return propertyValidationRO;
-        }
-
-        propertyValidationRO.setPropertyValid(true);
-        return propertyValidationRO;
+        return domainConfigurationDao.validateDomainProperty(propertyRO);
     }
-
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java
index 829d4643346ba6dbcec76cc5313386890c803a9b..d1531230fb4ef668b34741817f371db5e7c03adf 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java
@@ -462,7 +462,7 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> {
         validateCredentials(credential, userId, credentialType, credentialTargetType);
         credentialDao.remove(credential);
         CredentialRO credentialRO = conversionService.convert(credential, CredentialRO.class);
-        credentialRO.setStatus(EntityROStatus.REMOVE.getStatusNumber());
+        credentialRO.setStatus(EntityROStatus.REMOVED.getStatusNumber());
 
         return credentialRO;
     }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/StringNamedSubstitutor.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/StringNamedSubstitutor.java
index 4ec54d11810fda951e2688fba32b28d2a0350834..f2b2c4bdf1ba86c66d95e11bb3a8235ce5b5ede8 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/StringNamedSubstitutor.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/StringNamedSubstitutor.java
@@ -51,34 +51,52 @@ public class StringNamedSubstitutor {
     public static String resolve(InputStream templateIS, Map<String, Object> config) throws IOException {
         Map<String, Object> lowerCaseMap = config.entrySet().stream()
                 .collect(Collectors.toMap(e -> StringUtils.lowerCase(e.getKey()), Map.Entry::getValue));
-        StringBuilder builder = new StringBuilder();
+        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
+            resolve(templateIS, lowerCaseMap, byteArrayOutputStream);
+            return byteArrayOutputStream.toString();
+        }
+    }
+
+    /**
+     * Substitute named variables in the string with key value pairs from the map.
+     * The variables are in the form of ${name} and are case-insensitive and can contain only letters, digits, _ and .
+     *
+     * @param templateIS   the InputStream to resolve
+     * @param config       the config to use
+     * @param outputStream the output stream to write the resolved string
+     * @throws IOException if an I/O error occurs
+     */
+    public static void resolve(InputStream templateIS, Map<String, Object> config, OutputStream outputStream) throws IOException {
+        Map<String, Object> lowerCaseMap = config.entrySet().stream()
+                .collect(Collectors.toMap(e -> StringUtils.lowerCase(e.getKey()), Map.Entry::getValue));
+
+        try (BufferedReader template = new BufferedReader(new InputStreamReader(templateIS))) {
+            int read;
+            while ((read = template.read()) != -1) {
+                if (read != START_NAME.charAt(0) || !isStartSequence(template)) {
+                    outputStream.write((char) read);
+                    continue;
+                }
 
-        BufferedReader template = new BufferedReader(new InputStreamReader(templateIS));
-        int read;
-        while ((read = template.read()) != -1) {
-            if (read == START_NAME.charAt(0) && isStartSequence(template)) {
-                //skip (START_NAME.length() - 1L)  which is 1L
                 template.skip(1L);
                 String name = readName(template, END_NAME);
                 if (name == null) {
-                    builder.append(START_NAME);
+                    outputStream.write(START_NAME.getBytes());
                 } else {
                     String key = StringUtils.lowerCase(name);
                     Object objValue = lowerCaseMap.get(key);
-                    String value  = objValue!=null? String.valueOf(lowerCaseMap.get(key)): null;
+                    String value = objValue != null ? String.valueOf(lowerCaseMap.get(key)) : null;
 
                     if (value != null) {
-                        builder.append(value);
+                        outputStream.write(value.getBytes());
                     } else {
-                        builder.append(START_NAME).append(name).append(END_NAME);
+                        outputStream.write(START_NAME.getBytes());
+                        outputStream.write(name.getBytes());
+                        outputStream.write(END_NAME);
                     }
                 }
-            } else {
-                builder.append((char) read);
             }
         }
-
-        return builder.toString();
     }
 
     public static boolean isStartSequence(BufferedReader reader) throws IOException {
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/AbstractBaseDao.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/AbstractBaseDao.java
index cb41d0554ec7f56d71ada3b3d5d8cc456e99f462..11fa89df125f84547145884184e2526dfe4ed6c0 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/AbstractBaseDao.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/AbstractBaseDao.java
@@ -43,6 +43,7 @@ import static eu.europa.ec.edelivery.smp.config.enums.SMPEnvPropertyEnum.*;
         ResourceDao.class,
         SubresourceDao.class,
         DomainDao.class,
+        DomainConfigurationDao.class,
         UserDao.class,
         CredentialDao.class,
         ConfigurationDao.class}
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java
index d821adb94c9e8cc510907e920cc6ba909b41bc04..66e0beceafb33dbd77aff3deb9b49dcf2039ef14 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java
@@ -19,11 +19,13 @@
 package eu.europa.ec.edelivery.smp.data.dao;
 
 import eu.europa.ec.edelivery.smp.data.model.doc.DBDocument;
+import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentProperty;
 import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersion;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import javax.transaction.Transactional;
 import java.util.List;
 import java.util.Optional;
 
@@ -98,4 +100,24 @@ class DocumentDaoTest extends AbstractBaseDao {
         assertEquals(2, result.get().getVersion());
         assertEquals(testUtilsDao.getDocumentD1G1RD1_S1().getDocumentVersions().get(1), result.get());
     }
+
+    @Test
+    @Transactional
+    void testPersistDocumentProperty() {
+        // given
+        DBDocument document = testUtilsDao.createDocument(2, "value1", "schema1");
+        DBDocumentProperty property1 = new DBDocumentProperty("property1", "value1", document);
+        DBDocumentProperty property2 = new DBDocumentProperty("property2", "value1", document);
+        document.getDocumentProperties().add(property1);
+        document.getDocumentProperties().add(property2);
+        // when
+        testInstance.persistFlushDetach(document);
+        // then
+        DBDocument result = testInstance.find(document.getId());
+        assertNotNull(result.getId());
+        // different object instances
+        assertNotSame(document, result);
+        assertEquals(2, result.getDocumentProperties().size());
+        assertEquals(property1, result.getDocumentProperties().get(0));
+    }
 }
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java
index 052ece3bc77bd5b15dbe1b9fab3d75a5f51e29ec..89425ad0d67b2410c195da88d00ac9f56d17b273 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java
@@ -21,15 +21,19 @@ package eu.europa.ec.edelivery.smp.services.ui;
 import eu.europa.ec.edelivery.smp.config.ConversionTestConfig;
 import eu.europa.ec.edelivery.smp.data.model.doc.DBResource;
 import eu.europa.ec.edelivery.smp.data.model.doc.DBSubresource;
-import eu.europa.ec.edelivery.smp.data.ui.DocumentRo;
+import eu.europa.ec.edelivery.smp.data.ui.DocumentPropertyRO;
+import eu.europa.ec.edelivery.smp.data.ui.DocumentRO;
 import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
 import eu.europa.ec.edelivery.smp.services.AbstractServiceIntegrationTest;
 import eu.europa.ec.edelivery.smp.services.resource.ResourceHandlerService;
+import eu.europa.ec.edelivery.smp.utils.StringNamedSubstitutor;
 import eu.europa.ec.smp.spi.def.OasisSMPResource10;
 import eu.europa.ec.smp.spi.def.OasisSMPSubresource10;
+import eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType;
 import eu.europa.ec.smp.spi.handler.OasisSMPResource10Handler;
 import eu.europa.ec.smp.spi.handler.OasisSMPSubresource10Handler;
 import eu.europa.ec.smp.spi.validation.Subresource10Validator;
+import org.assertj.core.api.Assertions;
 import org.hamcrest.CoreMatchers;
 import org.hamcrest.MatcherAssert;
 import org.junit.jupiter.api.BeforeEach;
@@ -37,6 +41,9 @@ import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.test.context.ContextConfiguration;
 
+import java.util.Map;
+import java.util.stream.Collectors;
+
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
@@ -48,6 +55,9 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     @Autowired
     protected UIDocumentService testInstance;
 
+    @Autowired
+    ResourceHandlerService resourceHandlerService;
+
     @BeforeEach
     public void prepareDatabase() {
         // setup initial data!
@@ -58,7 +68,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     @Test
     void testGenerateDocumentForResource() {
 
-        DocumentRo result = testInstance.generateDocumentForResource(testUtilsDao.getResourceD1G1RD1().getId(), null);
+        DocumentRO result = testInstance.generateDocumentForResource(testUtilsDao.getResourceD1G1RD1().getId(), null);
         assertNotNull(result);
         assertNotNull(result.getPayload());
     }
@@ -67,7 +77,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     void testGenerateDocumentForSubResource() {
         DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1();
 
-        DocumentRo result = testInstance.generateDocumentForSubresource(subresource.getId(),
+        DocumentRO result = testInstance.generateDocumentForSubresource(subresource.getId(),
                 subresource.getResource().getId(),
                 null);
         assertNotNull(result);
@@ -77,7 +87,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     @Test
     void testValidateForResource() {
         DBResource resource = testUtilsDao.getResourceD1G1RD1();
-        DocumentRo testDoc = testInstance.generateDocumentForResource(resource.getId(), null);
+        DocumentRO testDoc = testInstance.generateDocumentForResource(resource.getId(), null);
         assertNotNull(testDoc.getPayload());
         // must not throw exception
         testInstance.validateDocumentForResource(resource.getId(), testDoc);
@@ -86,11 +96,11 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     @Test
     void testValidateForResourceError() {
         DBResource resource = testUtilsDao.getResourceD1G1RD1();
-        DocumentRo testDoc = new DocumentRo();
+        DocumentRO testDoc = new DocumentRO();
         testDoc.setPayload("test");
 
         SMPRuntimeException result = assertThrows(SMPRuntimeException.class, () ->
-            testInstance.validateDocumentForResource(resource.getId(), testDoc));
+                testInstance.validateDocumentForResource(resource.getId(), testDoc));
 
         MatcherAssert.assertThat(result.getMessage(), CoreMatchers.containsString("Invalid request [ResourceValidation]"));
     }
@@ -99,7 +109,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     @Test
     void testValidateForSubresource() {
         DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1();
-        DocumentRo testDoc = testInstance.generateDocumentForSubresource(subresource.getId(),
+        DocumentRO testDoc = testInstance.generateDocumentForSubresource(subresource.getId(),
                 subresource.getResource().getId(),
                 null);
 
@@ -111,11 +121,11 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     @Test
     void testValidateForSubresourceError() {
         DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1();
-        DocumentRo testDoc = new DocumentRo();
+        DocumentRO testDoc = new DocumentRO();
         testDoc.setPayload("test");
 
         SMPRuntimeException result = assertThrows(SMPRuntimeException.class, () ->
-            testInstance.validateDocumentForSubresource(subresource.getId(), subresource.getResource().getId(), testDoc));
+                testInstance.validateDocumentForSubresource(subresource.getId(), subresource.getResource().getId(), testDoc));
 
         MatcherAssert.assertThat(result.getMessage(), CoreMatchers.containsString("Invalid request [ResourceValidation]"));
     }
@@ -123,38 +133,69 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     @Test
     void testGetDocumentForResource() {
         DBResource resource = testUtilsDao.getResourceD1G1RD1();
-        DocumentRo testDoc = testInstance.getDocumentForResource(resource.getId(), 1);
+        DocumentRO testDoc = testInstance.getDocumentForResource(resource.getId(), 1);
         assertNotNull(testDoc.getPayload());
     }
 
     @Test
     void testGetDocumentForSubResource() {
         DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1();
-        DocumentRo testDoc = testInstance.getDocumentForSubResource(subresource.getId(), subresource.getResource().getId(), 1);
+        DocumentRO testDoc = testInstance.getDocumentForSubResource(subresource.getId(), subresource.getResource().getId(), 1);
         assertNotNull(testDoc.getPayload());
     }
 
     @Test
     void testSaveDocumentForResource() {
         DBResource resource = testUtilsDao.getResourceD1G1RD1();
-        DocumentRo testDoc = testInstance.generateDocumentForResource(resource.getId(), null);
+        DocumentRO testDoc = testInstance.generateDocumentForResource(resource.getId(), null);
         assertNotNull(testDoc.getPayload());
         //when
-        DocumentRo result = testInstance.saveDocumentForResource(resource.getId(), testDoc);
+        DocumentRO result = testInstance.saveDocumentForResource(resource.getId(), testDoc);
         // then
         assertNotNull(result);
     }
 
     @Test
-    void testSaveSubresourceDocumentForResource() {
+    void testSaveDocumentForSubresource() {
         DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1();
-        DocumentRo testDoc = testInstance.generateDocumentForSubresource(subresource.getId(),
+        DocumentRO testDoc = testInstance.generateDocumentForSubresource(subresource.getId(),
                 subresource.getResource().getId(),
                 null);
         assertNotNull(testDoc.getPayload());
+
         //when
-        DocumentRo result = testInstance.saveSubresourceDocumentForResource(subresource.getId(), subresource.getResource().getId(), testDoc);
+        DocumentRO result = testInstance.saveSubresourceDocumentForResource(subresource.getId(), subresource.getResource().getId(), testDoc);
         // then
         assertNotNull(result);
     }
+
+    @Test
+    void testTransientResolutionForSubresourceDocument() {
+        DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1();
+        DocumentRO testDoc = testInstance.generateDocumentForSubresource(subresource.getId(),
+                subresource.getResource().getId(),
+                null);
+        assertNotNull(testDoc.getPayload());
+        // extension used by this test is SMP example extension which generates document with placeholders
+        Assertions.assertThat(testDoc.getPayload()).contains(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder());
+        Assertions.assertThat(testDoc.getPayload()).contains(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder());
+        Assertions.assertThat(testDoc.getPayload()).contains(TransientDocumentPropertyType.SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder());
+        Assertions.assertThat(testDoc.getPayload()).contains(TransientDocumentPropertyType.SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder());
+
+        //when
+        DocumentRO result = testInstance.saveSubresourceDocumentForResource(subresource.getId(), subresource.getResource().getId(), testDoc);
+
+        Map<String, Object> mapProperties = result.getProperties().stream().collect(Collectors.toMap(DocumentPropertyRO::getProperty, DocumentPropertyRO::getValue));
+        String resolved = StringNamedSubstitutor.resolve(result.getPayload(), mapProperties);
+        // then
+        Assertions.assertThat(resolved).doesNotContain(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder());
+        Assertions.assertThat(resolved).doesNotContain(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder());
+        Assertions.assertThat(resolved).doesNotContain(TransientDocumentPropertyType.SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder());
+        Assertions.assertThat(resolved).doesNotContain(TransientDocumentPropertyType.SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder());
+
+        Assertions.assertThat(resolved).contains(subresource.getIdentifierValue());
+        Assertions.assertThat(resolved).contains(subresource.getIdentifierScheme());
+        Assertions.assertThat(resolved).contains(subresource.getResource().getIdentifierValue());
+        Assertions.assertThat(resolved).contains(subresource.getResource().getIdentifierScheme());
+    }
 }
diff --git a/smp-server-library/src/test/resources/cleanup-database.sql b/smp-server-library/src/test/resources/cleanup-database.sql
index 604df6ae9c5863159e7821ccf8bdde37b0aa7f4b..847d25bd63fd55ac7d4b466f84a4861211b80bb2 100755
--- a/smp-server-library/src/test/resources/cleanup-database.sql
+++ b/smp-server-library/src/test/resources/cleanup-database.sql
@@ -18,6 +18,8 @@ DELETE FROM SMP_SUBRESOURCE;
 DELETE FROM SMP_SUBRESOURCE_AUD;
 DELETE FROM SMP_RESOURCE;
 DELETE FROM SMP_RESOURCE_AUD;
+DELETE FROM SMP_DOCUMENT_PROPERTY;
+DELETE FROM SMP_DOCUMENT_PROPERTY_AUD;
 DELETE FROM SMP_DOCUMENT_VERSION;
 DELETE FROM SMP_DOCUMENT_VERSION_AUD;
 DELETE FROM SMP_DOCUMENT;
diff --git a/smp-spi/src/main/java/eu/europa/ec/smp/spi/ExtensionInfo.java b/smp-spi/src/main/java/eu/europa/ec/smp/spi/ExtensionInfo.java
index e5944d82808336f072dfb328c24c3b2b39d941ee..b224501a9fb65447494a618e97098a1de80edaf0 100644
--- a/smp-spi/src/main/java/eu/europa/ec/smp/spi/ExtensionInfo.java
+++ b/smp-spi/src/main/java/eu/europa/ec/smp/spi/ExtensionInfo.java
@@ -40,5 +40,4 @@ public interface ExtensionInfo {
     List<ResourceDefinitionSpi> resourceTypes();
 
     List<PayloadValidatorSpi> payloadValidators();
-
 }
diff --git a/smp-spi/src/main/java/eu/europa/ec/smp/spi/enums/TransientDocumentPropertyType.java b/smp-spi/src/main/java/eu/europa/ec/smp/spi/enums/TransientDocumentPropertyType.java
new file mode 100644
index 0000000000000000000000000000000000000000..6516501ac2657c4517b67ff9d04185627333e0d4
--- /dev/null
+++ b/smp-spi/src/main/java/eu/europa/ec/smp/spi/enums/TransientDocumentPropertyType.java
@@ -0,0 +1,52 @@
+package eu.europa.ec.smp.spi.enums;
+
+import org.apache.commons.lang3.StringUtils;
+
+import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
+import static org.apache.commons.lang3.StringUtils.trim;
+
+/**
+ * Enum for transient document properties
+ *
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+public enum TransientDocumentPropertyType {
+    RESOURCE_IDENTIFIER_VALUE("resource.identifier.value", "Resource Identifier Value"),
+    RESOURCE_IDENTIFIER_SCHEME("resource.identifier.scheme", "Resource Identifier Scheme"),
+    SUBRESOURCE_IDENTIFIER_VALUE("subresource.identifier.value", "Subresource Identifier Value"),
+    SUBRESOURCE_IDENTIFIER_SCHEME("subresource.identifier.scheme", "Subresource Identifier Scheme"),
+    DOCUMENT_NAME("document.name", "Document Name"),
+    DOCUMENT_MIMETYPE("document.mimetype", "Document Mimetype"),
+    DOCUMENT_VERSION("document.version", "Document Version"),
+    ;
+
+    String propertyName;
+    String propertyDescription;
+
+    TransientDocumentPropertyType(String propertyName, String propertyDescription) {
+        this.propertyName = propertyName;
+        this.propertyDescription = propertyDescription;
+    }
+
+    public String getPropertyName() {
+        return propertyName;
+    }
+
+    public String getPropertyPlaceholder() {
+        return "${" + propertyName + "}";
+    }
+
+    public String getPropertyDescription() {
+        return propertyDescription;
+    }
+
+    public static TransientDocumentPropertyType fromPropertyName(String propertyName) {
+        for (TransientDocumentPropertyType transientDocumentPropertyType : values()) {
+            if (equalsIgnoreCase(transientDocumentPropertyType.propertyName, trim(propertyName))) {
+                return transientDocumentPropertyType;
+            }
+        }
+        return null;
+    }
+}
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java
index a156d171fd54cfe08db5918115878f6b96ca6bd5..11d326265f1606c8d61e471d9fa6043f35363465 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -19,7 +19,7 @@
 package eu.europa.ec.edelivery.smp.ui.edit;
 
 
-import eu.europa.ec.edelivery.smp.data.ui.DocumentRo;
+import eu.europa.ec.edelivery.smp.data.ui.DocumentRO;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
 import eu.europa.ec.edelivery.smp.services.ui.UIDocumentService;
@@ -54,7 +54,7 @@ public class DocumentEditController {
     @GetMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
             "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
-    public DocumentRo getDocumentForResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+    public DocumentRO getDocumentForResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                              @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                              @RequestParam(value = PARAM_NAME_VERSION, defaultValue = "-1") int version) {
         logAdminAccess("getDocumentForResource");
@@ -65,7 +65,7 @@ public class DocumentEditController {
     @GetMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET_SUBRESOURCE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
             "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
-    public DocumentRo getDocumentForSubResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+    public DocumentRO getDocumentForSubResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                                 @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                                 @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
                                                 @RequestParam(value = PARAM_NAME_VERSION, defaultValue = "-1") int version) {
@@ -80,7 +80,7 @@ public class DocumentEditController {
             "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
     public void validateDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                  @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
-                                 @RequestBody DocumentRo document) {
+                                 @RequestBody DocumentRO document) {
         logAdminAccess("validateDocumentForResource");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         uiDocumentService.validateDocumentForResource(resourceId, document);
@@ -92,7 +92,7 @@ public class DocumentEditController {
     public void validateSubresourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                             @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                             @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
-                                            @RequestBody DocumentRo document) {
+                                            @RequestBody DocumentRO document) {
         logAdminAccess("validateDocumentForResource");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId);
@@ -103,9 +103,9 @@ public class DocumentEditController {
     @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_GENERATE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
             "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
-    public DocumentRo generateDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+    public DocumentRO generateDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                        @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
-                                       @RequestBody(required = false) DocumentRo document) {
+                                       @RequestBody(required = false) DocumentRO document) {
         logAdminAccess("generateDocument");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         return uiDocumentService.generateDocumentForResource(resourceId, document);
@@ -114,10 +114,10 @@ public class DocumentEditController {
     @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_GENERATE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
             "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
-    public DocumentRo generateSubresourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+    public DocumentRO generateSubresourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                                   @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                                   @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
-                                                  @RequestBody(required = false) DocumentRo document) {
+                                                  @RequestBody(required = false) DocumentRO document) {
         logAdminAccess("generateDocument");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId);
@@ -129,9 +129,9 @@ public class DocumentEditController {
             produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
             "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
-    public DocumentRo saveDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
-                                   @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
-                                   @RequestBody DocumentRo document) {
+    public DocumentRO saveDocumentForResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+                                              @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
+                                              @RequestBody DocumentRO document) {
         logAdminAccess("validateDocumentForResource");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         return uiDocumentService.saveDocumentForResource(resourceId, document);
@@ -142,10 +142,10 @@ public class DocumentEditController {
             produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
             "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
-    public DocumentRo saveSubresourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+    public DocumentRO saveSubresourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                               @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                               @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
-                                              @RequestBody DocumentRo document) {
+                                              @RequestBody DocumentRO document) {
         logAdminAccess("validateDocumentForResource");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId);
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/KeystoreAdminController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/KeystoreAdminController.java
index 3b8c7a0613d79139c1ebb227f39c4db172b41352..4f1cb8581091cbe18f3f93fefe459e40fb38a450 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/KeystoreAdminController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/KeystoreAdminController.java
@@ -130,11 +130,11 @@ public class KeystoreAdminController {
             if (x509Certificate == null) {
                 String msg = "Certificate Key not removed because alias [" + alias + "] does not exist in keystore!";
                 LOG.error(msg);
-                response = creatEmptyResponse(alias, EntityROStatus.REMOVE, msg);
+                response = creatEmptyResponse(alias, EntityROStatus.REMOVED, msg);
             } else {
                 response = uiKeystoreService.convertToRo(x509Certificate);
                 response.setAlias(alias);
-                response.setStatus(EntityROStatus.REMOVE.getStatusNumber());
+                response.setStatus(EntityROStatus.REMOVED.getStatusNumber());
             }
         } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
             String msg = e.getClass().getName() + " occurred while reading the keystore: " + e.getMessage();
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminController.java
index 88cfc2ad9af636dc8be99ee431496705770ce545..eede16b0ae84705a9af886e46a3482798f6b8090 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminController.java
@@ -120,11 +120,11 @@ public class TruststoreAdminController {
             if (x509Certificate == null) {
                 String msg = "Certificate not removed because alias [" + alias + "] does not exist in truststore!";
                 LOG.error(msg);
-                response = creatEmptyResponse(alias, EntityROStatus.REMOVE, msg);
+                response = creatEmptyResponse(alias, EntityROStatus.REMOVED, msg);
             } else {
                 response = uiTruststoreService.convertToRo(x509Certificate);
                 response.setAlias(alias);
-                response.setStatus(EntityROStatus.REMOVE.getStatusNumber());
+                response.setStatus(EntityROStatus.REMOVED.getStatusNumber());
             }
         } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
             String msg = e.getClass().getName() + " occurred while reading the truststore: " + e.getMessage();
diff --git a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl
index a2e7b515c84c9ecce823170f3850d15e54a806dc..f33ba8d00756e7f4b4216169ebb739ca0b41400c 100644
--- a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl
+++ b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl
@@ -39,6 +39,14 @@
        drop 
        foreign key FKh9epnme26i271eixtvrpqejvi;
 
+    alter table SMP_DOCUMENT_PROPERTY 
+       drop 
+       foreign key FKfag3795e9mrvfvesd00yis9yh;
+
+    alter table SMP_DOCUMENT_PROPERTY_AUD 
+       drop 
+       foreign key FK81057kcrugb1cfm0io5vkxtin;
+
     alter table SMP_DOCUMENT_VERSION 
        drop 
        foreign key FKalsuoqx4csyp9mygvng911do;
@@ -195,6 +203,10 @@
 
     drop table if exists SMP_DOCUMENT_AUD;
 
+    drop table if exists SMP_DOCUMENT_PROPERTY;
+
+    drop table if exists SMP_DOCUMENT_PROPERTY_AUD;
+
     drop table if exists SMP_DOCUMENT_VERSION;
 
     drop table if exists SMP_DOCUMENT_VERSION_AUD;
diff --git a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl
index bacf57b422f2816958da403b7c6dcdf5b8cb2190..421be51f4488369b97b2d55b72acc73cddd08320 100644
--- a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl
+++ b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl
@@ -179,6 +179,32 @@
         primary key (ID, REV)
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
+    create table SMP_DOCUMENT_PROPERTY (
+       ID bigint not null auto_increment comment 'Unique document property id',
+        CREATED_ON datetime not null,
+        LAST_UPDATED_ON datetime not null,
+        DESCRIPTION varchar(4000)  CHARACTER SET utf8 COLLATE utf8_bin comment 'Property description',
+        PROPERTY_NAME varchar(255)  CHARACTER SET utf8 COLLATE utf8_bin,
+        PROPERTY_TYPE varchar(64)  CHARACTER SET utf8 COLLATE utf8_bin,
+        PROPERTY_VALUE varchar(1024)  CHARACTER SET utf8 COLLATE utf8_bin,
+        FK_DOCUMENT_ID bigint,
+        primary key (ID)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+    create table SMP_DOCUMENT_PROPERTY_AUD (
+       ID bigint not null,
+        REV bigint not null,
+        REVTYPE tinyint,
+        CREATED_ON datetime,
+        LAST_UPDATED_ON datetime,
+        DESCRIPTION varchar(4000)  CHARACTER SET utf8 COLLATE utf8_bin,
+        PROPERTY_NAME varchar(255)  CHARACTER SET utf8 COLLATE utf8_bin,
+        PROPERTY_TYPE varchar(64)  CHARACTER SET utf8 COLLATE utf8_bin,
+        PROPERTY_VALUE varchar(1024)  CHARACTER SET utf8 COLLATE utf8_bin,
+        FK_DOCUMENT_ID bigint,
+        primary key (ID, REV)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
     create table SMP_DOCUMENT_VERSION (
        ID bigint not null auto_increment comment 'Unique version document id',
         CREATED_ON datetime not null,
@@ -245,7 +271,7 @@
         LAST_UPDATED_ON datetime not null,
         DESCRIPTION varchar(4000)  CHARACTER SET utf8 COLLATE utf8_bin comment 'Property description',
         PROPERTY_NAME varchar(512)  CHARACTER SET utf8 COLLATE utf8_bin not null comment 'Property name/key',
-        USER_SYSTEM_DEFAULT bit not null comment 'Use system default value',
+        SYSTEM_DEFAULT bit not null comment 'Use system default value',
         PROPERTY_VALUE varchar(4000)  CHARACTER SET utf8 COLLATE utf8_bin comment 'Property value',
         FK_DOMAIN_ID bigint not null,
         primary key (ID)
@@ -259,7 +285,7 @@
         LAST_UPDATED_ON datetime,
         DESCRIPTION varchar(4000)  CHARACTER SET utf8 COLLATE utf8_bin,
         PROPERTY_NAME varchar(512)  CHARACTER SET utf8 COLLATE utf8_bin,
-        USER_SYSTEM_DEFAULT bit,
+        SYSTEM_DEFAULT bit,
         PROPERTY_VALUE varchar(4000)  CHARACTER SET utf8 COLLATE utf8_bin,
         FK_DOMAIN_ID bigint,
         primary key (ID, REV)
@@ -560,6 +586,9 @@
 
     alter table SMP_CREDENTIAL 
        add constraint SMP_CRD_USER_NAME_TYPE_IDX unique (CREDENTIAL_NAME, CREDENTIAL_TYPE, CREDENTIAL_TARGET);
+
+    alter table SMP_DOCUMENT_PROPERTY 
+       add constraint SMP_DOC_PROP_IDX unique (FK_DOCUMENT_ID, PROPERTY_NAME);
 create index SMP_DOCVER_DOCUMENT_IDX on SMP_DOCUMENT_VERSION (FK_DOCUMENT_ID);
 
     alter table SMP_DOCUMENT_VERSION 
@@ -665,6 +694,16 @@ create index SMP_SMD_DOC_SCH_IDX on SMP_SUBRESOURCE (IDENTIFIER_SCHEME);
        foreign key (REV) 
        references SMP_REV_INFO (id);
 
+    alter table SMP_DOCUMENT_PROPERTY 
+       add constraint FKfag3795e9mrvfvesd00yis9yh 
+       foreign key (FK_DOCUMENT_ID) 
+       references SMP_DOCUMENT (ID);
+
+    alter table SMP_DOCUMENT_PROPERTY_AUD 
+       add constraint FK81057kcrugb1cfm0io5vkxtin 
+       foreign key (REV) 
+       references SMP_REV_INFO (id);
+
     alter table SMP_DOCUMENT_VERSION 
        add constraint FKalsuoqx4csyp9mygvng911do 
        foreign key (FK_DOCUMENT_ID) 
diff --git a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl
index 4ce6d0474574c4a584a69bfd3e4273f03ad163d1..45b4fba095365730d7182e3040ceea70e09f062f 100644
--- a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl
+++ b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl
@@ -27,6 +27,10 @@
 
     drop table SMP_DOCUMENT_AUD cascade constraints;
 
+    drop table SMP_DOCUMENT_PROPERTY cascade constraints;
+
+    drop table SMP_DOCUMENT_PROPERTY_AUD cascade constraints;
+
     drop table SMP_DOCUMENT_VERSION cascade constraints;
 
     drop table SMP_DOCUMENT_VERSION_AUD cascade constraints;
@@ -91,6 +95,8 @@
 
     drop sequence SMP_CREDENTIAL_SEQ;
 
+    drop sequence SMP_DOC_PROP_SEQ;
+
     drop sequence SMP_DOCUMENT_SEQ;
 
     drop sequence SMP_DOCUMENT_VERSION_SEQ;
diff --git a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl
index 3c861a3e1bade8f88937626e5c6b3d7fc745424b..c3460e7dd0c793b4acd46f763e89fca37b602e59 100644
--- a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl
+++ b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl
@@ -5,6 +5,7 @@
 create sequence SMP_ALERT_PROP_SEQ start with 1 increment by  1;
 create sequence SMP_ALERT_SEQ start with 1 increment by  1;
 create sequence SMP_CREDENTIAL_SEQ start with 1 increment by  1;
+create sequence SMP_DOC_PROP_SEQ start with 1 increment by  1;
 create sequence SMP_DOCUMENT_SEQ start with 1 increment by  1;
 create sequence SMP_DOCUMENT_VERSION_SEQ start with 1 increment by  1;
 create sequence SMP_DOMAIN_CONF_SEQ start with 1 increment by  1;
@@ -303,6 +304,38 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
         primary key (ID, REV)
     );
 
+    create table SMP_DOCUMENT_PROPERTY (
+       ID number(19,0) not null,
+        CREATED_ON timestamp not null,
+        LAST_UPDATED_ON timestamp not null,
+        DESCRIPTION varchar2(4000 char),
+        PROPERTY_NAME varchar2(255 char),
+        PROPERTY_TYPE varchar2(64 char),
+        PROPERTY_VALUE varchar2(1024 char),
+        FK_DOCUMENT_ID number(19,0),
+        primary key (ID)
+    );
+
+    comment on column SMP_DOCUMENT_PROPERTY.ID is
+        'Unique document property id';
+
+    comment on column SMP_DOCUMENT_PROPERTY.DESCRIPTION is
+        'Property description';
+
+    create table SMP_DOCUMENT_PROPERTY_AUD (
+       ID number(19,0) not null,
+        REV number(19,0) not null,
+        REVTYPE number(3,0),
+        CREATED_ON timestamp,
+        LAST_UPDATED_ON timestamp,
+        DESCRIPTION varchar2(4000 char),
+        PROPERTY_NAME varchar2(255 char),
+        PROPERTY_TYPE varchar2(64 char),
+        PROPERTY_VALUE varchar2(1024 char),
+        FK_DOCUMENT_ID number(19,0),
+        primary key (ID, REV)
+    );
+
     create table SMP_DOCUMENT_VERSION (
        ID number(19,0) not null,
         CREATED_ON timestamp not null,
@@ -417,7 +450,7 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
         LAST_UPDATED_ON timestamp not null,
         DESCRIPTION varchar2(4000 char),
         PROPERTY_NAME varchar2(512 char) not null,
-        USER_SYSTEM_DEFAULT number(1,0) not null,
+        SYSTEM_DEFAULT number(1,0) not null,
         PROPERTY_VALUE varchar2(4000 char),
         FK_DOMAIN_ID number(19,0) not null,
         primary key (ID)
@@ -435,7 +468,7 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
     comment on column SMP_DOMAIN_CONFIGURATION.PROPERTY_NAME is
         'Property name/key';
 
-    comment on column SMP_DOMAIN_CONFIGURATION.USER_SYSTEM_DEFAULT is
+    comment on column SMP_DOMAIN_CONFIGURATION.SYSTEM_DEFAULT is
         'Use system default value';
 
     comment on column SMP_DOMAIN_CONFIGURATION.PROPERTY_VALUE is
@@ -449,7 +482,7 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
         LAST_UPDATED_ON timestamp,
         DESCRIPTION varchar2(4000 char),
         PROPERTY_NAME varchar2(512 char),
-        USER_SYSTEM_DEFAULT number(1,0),
+        SYSTEM_DEFAULT number(1,0),
         PROPERTY_VALUE varchar2(4000 char),
         FK_DOMAIN_ID number(19,0),
         primary key (ID, REV)
@@ -825,6 +858,9 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
 
     alter table SMP_CREDENTIAL 
        add constraint SMP_CRD_USER_NAME_TYPE_IDX unique (CREDENTIAL_NAME, CREDENTIAL_TYPE, CREDENTIAL_TARGET);
+
+    alter table SMP_DOCUMENT_PROPERTY 
+       add constraint SMP_DOC_PROP_IDX unique (FK_DOCUMENT_ID, PROPERTY_NAME);
 create index SMP_DOCVER_DOCUMENT_IDX on SMP_DOCUMENT_VERSION (FK_DOCUMENT_ID);
 
     alter table SMP_DOCUMENT_VERSION 
@@ -930,6 +966,16 @@ create index SMP_SMD_DOC_SCH_IDX on SMP_SUBRESOURCE (IDENTIFIER_SCHEME);
        foreign key (REV) 
        references SMP_REV_INFO;
 
+    alter table SMP_DOCUMENT_PROPERTY 
+       add constraint FKfag3795e9mrvfvesd00yis9yh 
+       foreign key (FK_DOCUMENT_ID) 
+       references SMP_DOCUMENT;
+
+    alter table SMP_DOCUMENT_PROPERTY_AUD 
+       add constraint FK81057kcrugb1cfm0io5vkxtin 
+       foreign key (REV) 
+       references SMP_REV_INFO;
+
     alter table SMP_DOCUMENT_VERSION 
        add constraint FKalsuoqx4csyp9mygvng911do 
        foreign key (FK_DOCUMENT_ID) 
diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java
index 13e9509ddbee2bd0da8809096de69973c46b27dc..0b610f028ee3ef4937786164e1c18b52366c493a 100644
--- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java
+++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java
@@ -77,7 +77,7 @@ class DocumentEditControllerIT extends AbstractControllerTest {
                 )
                 .andExpect(status().isOk()).andReturn();
         // then
-        DocumentRo documentRo = getObjectFromResponse(result, DocumentRo.class);
+        DocumentRO documentRo = getObjectFromResponse(result, DocumentRO.class);
         assertNotNull(documentRo);
         assertTrue(documentRo.getAllVersions().isEmpty()); // was just created without document
     }
@@ -106,7 +106,7 @@ class DocumentEditControllerIT extends AbstractControllerTest {
                 )
                 .andExpect(status().isOk()).andReturn();
         // then
-        DocumentRo documentRo = getObjectFromResponse(result, DocumentRo.class);
+        DocumentRO documentRo = getObjectFromResponse(result, DocumentRO.class);
         assertNotNull(documentRo);
         assertFalse(documentRo.getAllVersions().isEmpty()); // was just created without document
         assertNotNull(documentRo.getPayload());
@@ -129,7 +129,7 @@ class DocumentEditControllerIT extends AbstractControllerTest {
         ResourceRO resourceRO = resources.get(0);
 
         // document to validate
-        DocumentRo documentRo = new DocumentRo();
+        DocumentRO documentRo = new DocumentRO();
         documentRo.setPayload(TestROUtils.createSMP10ServiceGroupPayload(resourceRO.getIdentifierValue(), resourceRO.getIdentifierScheme()));
 
         // when
@@ -160,7 +160,7 @@ class DocumentEditControllerIT extends AbstractControllerTest {
         ResourceRO resourceRO = resources.get(0);
 
         // document to validate
-        DocumentRo documentRo = new DocumentRo();
+        DocumentRO documentRo = new DocumentRO();
         documentRo.setPayload("invalid payload");
 
         // when
@@ -203,7 +203,7 @@ class DocumentEditControllerIT extends AbstractControllerTest {
                 )
                 .andExpect(status().isOk()).andReturn();
 
-        DocumentRo result = getObjectFromResponse(response, DocumentRo.class);
+        DocumentRO result = getObjectFromResponse(response, DocumentRO.class);
         assertNotNull(result);
         assertNotNull(result.getPayload());
     }
diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/DomainAdminControllerIT.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/DomainAdminControllerIT.java
index 072eb64bb9db0a78a071e694bcde72dd8c45c741..3beb9dc0dfe196baadf3da60670cc5a7f643f2c1 100644
--- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/DomainAdminControllerIT.java
+++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/DomainAdminControllerIT.java
@@ -168,7 +168,7 @@ class DomainAdminControllerIT extends AbstractControllerTest {
         //
         assertNotNull(resultObject);
         assertEquals(domainCode, resultObject.getDomainCode());
-        assertEquals(EntityROStatus.REMOVE.getStatusNumber(), resultObject.getStatus());
+        assertEquals(EntityROStatus.REMOVED.getStatusNumber(), resultObject.getStatus());
     }
 
     @Test
diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminControllerTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminControllerTest.java
index 8f987f790eceb9c2e494e60fa36e74c5e9c32da8..d5dee4b7a6cb5c61bf9e4be7be97033eec5271d9 100644
--- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminControllerTest.java
+++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminControllerTest.java
@@ -172,7 +172,7 @@ class TruststoreAdminControllerTest extends AbstractControllerTest {
         CertificateRO res = getObjectFromResponse(result, CertificateRO.class);
 
         assertNotNull(res);
-        assertEquals(EntityROStatus.REMOVE.getStatusNumber(), res.getStatus());
+        assertEquals(EntityROStatus.REMOVED.getStatusNumber(), res.getStatus());
         assertEquals(countStart - 1, uiTruststoreService.getCertificateROEntriesList().size());
     }
 }
diff --git a/smp-webapp/src/test/resources/cleanup-database.sql b/smp-webapp/src/test/resources/cleanup-database.sql
index 604df6ae9c5863159e7821ccf8bdde37b0aa7f4b..847d25bd63fd55ac7d4b466f84a4861211b80bb2 100755
--- a/smp-webapp/src/test/resources/cleanup-database.sql
+++ b/smp-webapp/src/test/resources/cleanup-database.sql
@@ -18,6 +18,8 @@ DELETE FROM SMP_SUBRESOURCE;
 DELETE FROM SMP_SUBRESOURCE_AUD;
 DELETE FROM SMP_RESOURCE;
 DELETE FROM SMP_RESOURCE_AUD;
+DELETE FROM SMP_DOCUMENT_PROPERTY;
+DELETE FROM SMP_DOCUMENT_PROPERTY_AUD;
 DELETE FROM SMP_DOCUMENT_VERSION;
 DELETE FROM SMP_DOCUMENT_VERSION_AUD;
 DELETE FROM SMP_DOCUMENT;
diff --git a/smp-webapp/src/test/resources/input/ServiceMetadata.xml b/smp-webapp/src/test/resources/input/ServiceMetadata.xml
index 38624ee0a949e8f8c3a5c33ab3edb862f7f68ffe..e815bd68d59352ec2727dc8412b38623a2a82525 100644
--- a/smp-webapp/src/test/resources/input/ServiceMetadata.xml
+++ b/smp-webapp/src/test/resources/input/ServiceMetadata.xml
@@ -1,23 +1,4 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-  #START_LICENSE#
-  smp-webapp
-  %%
-  Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
-  %%
-  Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
-  versions of the EUPL (the "Licence");
-  You may not use this work except in compliance with the Licence.
-  You may obtain a copy of the Licence at:
-  
-  [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
-  
-  Unless required by applicable law or agreed to in writing, software distributed under the Licence is
-  distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the Licence for the specific language governing permissions and limitations under the Licence.
-  #END_LICENSE#
-  -->
-
 <ServiceMetadata xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05">
     <ServiceInformation>
         <ParticipantIdentifier scheme="ehealth-actorid-qns">urn:australia:ncpb</ParticipantIdentifier>
diff --git a/smp-webapp/src/test/resources/input/ServiceMetadata_linarized.xml b/smp-webapp/src/test/resources/input/ServiceMetadata_linarized.xml
index c693976d0b3ed879c5d398bfd34c9123baa11cf5..9c5c59a833655683277f9fa83d47eec8ed8b1dbb 100644
--- a/smp-webapp/src/test/resources/input/ServiceMetadata_linarized.xml
+++ b/smp-webapp/src/test/resources/input/ServiceMetadata_linarized.xml
@@ -1,20 +1,2 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-  #START_LICENSE#
-  smp-webapp
-  %%
-  Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
-  %%
-  Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
-  versions of the EUPL (the "Licence");
-  You may not use this work except in compliance with the Licence.
-  You may obtain a copy of the Licence at:
-  
-  [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
-  
-  Unless required by applicable law or agreed to in writing, software distributed under the Licence is
-  distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the Licence for the specific language governing permissions and limitations under the Licence.
-  #END_LICENSE#
-  -->
 <ServiceMetadata xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"><ServiceInformation><ParticipantIdentifier scheme="ehealth-actorid-qns">urn:brazil:ncpb</ParticipantIdentifier><DocumentIdentifier scheme="ehealth-resid-qns">urn::epsos##services:extended:epsos::107</DocumentIdentifier><ProcessList><Process><ProcessIdentifier scheme="ehealth-procid-qns">urn:epsosPatientService::List</ProcessIdentifier><ServiceEndpointList><Endpoint transportProfile="urn:ihe:iti:2013:xcpd"><EndpointURI>http://poland.pl/ncp/patient/list</EndpointURI><RequireBusinessLevelSignature>false</RequireBusinessLevelSignature><MinimumAuthenticationLevel>urn:epSOS:loa:1</MinimumAuthenticationLevel><ServiceActivationDate>2016-06-06T11:06:02.000+02:00</ServiceActivationDate><ServiceExpirationDate>2026-06-06T11:06:02+02:00</ServiceExpirationDate><Certificate>MIID7jCCA1egAwIBAgICA+YwDQYJKoZIhvcNAQENBQAwOjELMAkGA1UEBhMCRlIxEzARBgNVBAoMCklIRSBFdXJvcGUxFjAUBgNVBAMMDUlIRSBFdXJvcGUgQ0EwHhcNMTYwNjAxMTQzNTUzWhcNMjYwNjAxMTQzNTUzWjCBgzELMAkGA1UEBhMCUFQxDDAKBgNVBAoMA01vSDENMAsGA1UECwwEU1BNUzENMAsGA1UEKgwESm9hbzEOMAwGA1UEBRMFQ3VuaGExHTAbBgNVBAMMFHFhZXBzb3MubWluLXNhdWRlLnB0MRkwFwYDVQQMDBBTZXJ2aWNlIFByb3ZpZGVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1eN4qPSSRZqjVFG9TlcPlxf2WiSimQK9L1nf9Z/s0ezeGQjCukDeDq/Wzqd9fpHhaMMq+XSSOtyEtIr5K/As4kFrViONUUkG12J6UllSWogp0NYFwA4wIqKSFiTnQS5/nRTs05oONCCGILCyJNNeO53JzPlaq3/QbPLssuSAr6XucPE8wBBGM8b/TsB2G/zjG8yuSTgGbhaZekq/Vnf9ftj1fr/vJDDAQgH6Yvzd88Z0DACJPHfW1p4F/OWLI386Bq7g/bo1DUPAyEwlf+CkLgJWRKki3yJlOCIZ9enMA5O7rfeG3rXdgYGmWS7tNEgKXxgC+heiYvi7ZWd7M+/SUwIDAQABo4IBMzCCAS8wPgYDVR0fBDcwNTAzoDGgL4YtaHR0cHM6Ly9nYXplbGxlLmloZS5uZXQvcGtpL2NybC82NDMvY2FjcmwuY3JsMDwGCWCGSAGG+EIBBAQvFi1odHRwczovL2dhemVsbGUuaWhlLm5ldC9wa2kvY3JsLzY0My9jYWNybC5jcmwwPAYJYIZIAYb4QgEDBC8WLWh0dHBzOi8vZ2F6ZWxsZS5paGUubmV0L3BraS9jcmwvNjQzL2NhY3JsLmNybDAfBgNVHSMEGDAWgBTsMw4TyCJeouFrr0N7el3Sd3MdfjAdBgNVHQ4EFgQU1GQ/K1ykIwWFgiONzWJLQzufF/8wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBSAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQENBQADgYEAZ7t1Qkr9wz3q6+WcF6p/YX7Jr0CzVe7w58FvJFk2AsHeYkSlOyO5hxNpQbs1L1v6JrcqziNFrh2QKGT2v6iPdWtdCT8HBLjmuvVWxxnfzYjdQ0J+kdKMAEV6EtWU78OqL60CCtUZKXE/NKJUq7TTUCFP2fwiARy/t1dTD2NZo8c=</Certificate><ServiceDescription>This is the epSOS Patient Service List for the Polish NCP</ServiceDescription><TechnicalContactUrl>http://poland.pl/contact</TechnicalContactUrl><TechnicalInformationUrl>http://poland.pl/contact</TechnicalInformationUrl></Endpoint></ServiceEndpointList></Process></ProcessList><Extension></Extension></ServiceInformation></ServiceMetadata>