adminforth 1.2.21 → 1.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,6 +16,7 @@
16
16
  "flowbite": "^2.3.0",
17
17
  "flowbite-datepicker": "^1.2.6",
18
18
  "pinia": "^2.1.7",
19
+ "sanitize-html": "^2.13.0",
19
20
  "unhead": "^1.9.12",
20
21
  "uuid": "^10.0.0",
21
22
  "vue": "^3.4.21",
@@ -1897,6 +1898,14 @@
1897
1898
  "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
1898
1899
  "dev": true
1899
1900
  },
1901
+ "node_modules/deepmerge": {
1902
+ "version": "4.3.1",
1903
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
1904
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
1905
+ "engines": {
1906
+ "node": ">=0.10.0"
1907
+ }
1908
+ },
1900
1909
  "node_modules/didyoumean": {
1901
1910
  "version": "1.2.2",
1902
1911
  "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@@ -1938,6 +1947,57 @@
1938
1947
  "node": ">=6.0.0"
1939
1948
  }
1940
1949
  },
1950
+ "node_modules/dom-serializer": {
1951
+ "version": "2.0.0",
1952
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
1953
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
1954
+ "dependencies": {
1955
+ "domelementtype": "^2.3.0",
1956
+ "domhandler": "^5.0.2",
1957
+ "entities": "^4.2.0"
1958
+ },
1959
+ "funding": {
1960
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
1961
+ }
1962
+ },
1963
+ "node_modules/domelementtype": {
1964
+ "version": "2.3.0",
1965
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
1966
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
1967
+ "funding": [
1968
+ {
1969
+ "type": "github",
1970
+ "url": "https://github.com/sponsors/fb55"
1971
+ }
1972
+ ]
1973
+ },
1974
+ "node_modules/domhandler": {
1975
+ "version": "5.0.3",
1976
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
1977
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
1978
+ "dependencies": {
1979
+ "domelementtype": "^2.3.0"
1980
+ },
1981
+ "engines": {
1982
+ "node": ">= 4"
1983
+ },
1984
+ "funding": {
1985
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
1986
+ }
1987
+ },
1988
+ "node_modules/domutils": {
1989
+ "version": "3.1.0",
1990
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
1991
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
1992
+ "dependencies": {
1993
+ "dom-serializer": "^2.0.0",
1994
+ "domelementtype": "^2.3.0",
1995
+ "domhandler": "^5.0.3"
1996
+ },
1997
+ "funding": {
1998
+ "url": "https://github.com/fb55/domutils?sponsor=1"
1999
+ }
2000
+ },
1941
2001
  "node_modules/eastasianwidth": {
1942
2002
  "version": "0.2.0",
1943
2003
  "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -2018,7 +2078,6 @@
2018
2078
  "version": "4.0.0",
2019
2079
  "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
2020
2080
  "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
2021
- "dev": true,
2022
2081
  "engines": {
2023
2082
  "node": ">=10"
2024
2083
  },
@@ -2545,6 +2604,24 @@
2545
2604
  "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
2546
2605
  "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
2547
2606
  },
2607
+ "node_modules/htmlparser2": {
2608
+ "version": "8.0.2",
2609
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
2610
+ "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
2611
+ "funding": [
2612
+ "https://github.com/fb55/htmlparser2?sponsor=1",
2613
+ {
2614
+ "type": "github",
2615
+ "url": "https://github.com/sponsors/fb55"
2616
+ }
2617
+ ],
2618
+ "dependencies": {
2619
+ "domelementtype": "^2.3.0",
2620
+ "domhandler": "^5.0.3",
2621
+ "domutils": "^3.0.1",
2622
+ "entities": "^4.4.0"
2623
+ }
2624
+ },
2548
2625
  "node_modules/ignore": {
2549
2626
  "version": "5.3.1",
2550
2627
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@@ -2673,6 +2750,14 @@
2673
2750
  "node": ">=8"
2674
2751
  }
2675
2752
  },
2753
+ "node_modules/is-plain-object": {
2754
+ "version": "5.0.0",
2755
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
2756
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
2757
+ "engines": {
2758
+ "node": ">=0.10.0"
2759
+ }
2760
+ },
2676
2761
  "node_modules/isexe": {
2677
2762
  "version": "2.0.0",
2678
2763
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -3103,6 +3188,11 @@
3103
3188
  "node": ">=6"
3104
3189
  }
3105
3190
  },
3191
+ "node_modules/parse-srcset": {
3192
+ "version": "1.0.2",
3193
+ "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
3194
+ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
3195
+ },
3106
3196
  "node_modules/path-browserify": {
3107
3197
  "version": "1.0.1",
3108
3198
  "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
@@ -3593,6 +3683,19 @@
3593
3683
  "queue-microtask": "^1.2.2"
3594
3684
  }
3595
3685
  },
3686
+ "node_modules/sanitize-html": {
3687
+ "version": "2.13.0",
3688
+ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.13.0.tgz",
3689
+ "integrity": "sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==",
3690
+ "dependencies": {
3691
+ "deepmerge": "^4.2.2",
3692
+ "escape-string-regexp": "^4.0.0",
3693
+ "htmlparser2": "^8.0.0",
3694
+ "is-plain-object": "^5.0.0",
3695
+ "parse-srcset": "^1.0.2",
3696
+ "postcss": "^8.3.11"
3697
+ }
3698
+ },
3596
3699
  "node_modules/sass": {
3597
3700
  "version": "1.77.2",
3598
3701
  "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz",
@@ -20,6 +20,7 @@
20
20
  "flowbite": "^2.3.0",
21
21
  "flowbite-datepicker": "^1.2.6",
22
22
  "pinia": "^2.1.7",
23
+ "sanitize-html": "^2.13.0",
23
24
  "unhead": "^1.9.12",
24
25
  "uuid": "^10.0.0",
25
26
  "vue": "^3.4.21",
@@ -189,8 +189,6 @@ function setFilterItem({ column, operator, value }) {
189
189
  if (index !== -1) {
190
190
  filtersStore.filters.splice(index, 1);
191
191
  }
192
- emits('update:filters', [...filtersStore.filters]);
193
- return;
194
192
  } else {
195
193
  if (index === -1) {
196
194
  filtersStore.setFilter({ field: column.name, value, operator });
@@ -207,5 +205,6 @@ function getFilterItem({ column, operator }) {
207
205
 
208
206
  async function clear() {
209
207
  filtersStore.clearFilters();
208
+ emits('update:filters', [...filtersStore.filters]);
210
209
  }
211
210
  </script>
@@ -1,6 +1,5 @@
1
1
  <template>
2
2
  <div class="rounded-default">
3
-
4
3
  <div
5
4
  class="relative shadow-resourseFormShadow sm:rounded-lg dark:shadow-2xl dark:shadow-black rounded-default"
6
5
  >
@@ -44,6 +43,7 @@
44
43
  :meta="column.components[props.source].meta"
45
44
  :record="props.record"
46
45
  @update:inValidity="customComponentsInValidity[column.name] = $event"
46
+ @update:emptiness="customComponentsEmptiness[column.name] = $event"
47
47
  />
48
48
  </template>
49
49
  <template v-else>
@@ -171,6 +171,7 @@ const emit = defineEmits(['update:record', 'update:isValid']);
171
171
  const currentValues = ref({});
172
172
 
173
173
  const customComponentsInValidity = ref({});
174
+ const customComponentsEmptiness = ref({});
174
175
 
175
176
 
176
177
  const columnError = (column) => {
@@ -179,7 +180,14 @@ const columnError = (column) => {
179
180
  return customComponentsInValidity.value[column.name];
180
181
  }
181
182
 
182
- if ( column.required[mode.value] && (currentValues.value[column.name] === undefined || currentValues.value[column.name] === null || currentValues.value[column.name] === '') ) {
183
+ if (
184
+ column.required[mode.value] &&
185
+ (currentValues.value[column.name] === undefined || currentValues.value[column.name] === null || currentValues.value[column.name] === '') &&
186
+ // if component is custum it might tell other criteria for emptiness by emitting 'update:emptiness'
187
+ // components which do not emit 'update:emptiness' will have undefined value in customComponentsEmptiness
188
+ (customComponentsEmptiness.value[column.name] !== false)
189
+
190
+ ) {
183
191
  return 'This field is required';
184
192
  }
185
193
  if ( column.type === 'string' || column.type === 'text' ) {
@@ -20,6 +20,9 @@
20
20
  <span v-else-if="column.type === 'datetime'" class="whitespace-nowrap">
21
21
  {{ checkEmptyValues(formatDate(record[column.name]),route.meta.type) }}
22
22
  </span>
23
+ <span v-else-if="column.type === 'richtext'">
24
+ <div v-html="protectAgainstXSS(record[column.name])"></div>
25
+ </span>
23
26
  <span v-else>
24
27
  {{ checkEmptyValues(record[column.name],route.meta.type) }}
25
28
  </span>
@@ -34,6 +37,7 @@ import utc from 'dayjs/plugin/utc';
34
37
  import timezone from 'dayjs/plugin/timezone';
35
38
  import {checkEmptyValues} from '@/utils';
36
39
  import { useRoute, useRouter } from 'vue-router';
40
+ import sanitizeHtml from 'sanitize-html';
37
41
 
38
42
 
39
43
  import { useCoreStore } from '@/stores/core';
@@ -50,6 +54,10 @@ const props = defineProps({
50
54
  record: Object
51
55
  });
52
56
 
57
+ function protectAgainstXSS(value) {
58
+ return sanitizeHtml(value);
59
+ }
60
+
53
61
 
54
62
  function formatDate(date) {
55
63
  if (!date) return '';
@@ -84,6 +84,7 @@ export var AdminForthDataTypes;
84
84
  AdminForthDataTypes["TIME"] = "time";
85
85
  AdminForthDataTypes["TEXT"] = "text";
86
86
  AdminForthDataTypes["JSON"] = "json";
87
+ AdminForthDataTypes["RICHTEXT"] = "richtext";
87
88
  })(AdminForthDataTypes || (AdminForthDataTypes = {}));
88
89
  export var AdminForthFilterOperators;
89
90
  (function (AdminForthFilterOperators) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.2.21",
3
+ "version": "1.2.23",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -16,6 +16,7 @@
16
16
  "flowbite": "^2.3.0",
17
17
  "flowbite-datepicker": "^1.2.6",
18
18
  "pinia": "^2.1.7",
19
+ "sanitize-html": "^2.13.0",
19
20
  "unhead": "^1.9.12",
20
21
  "uuid": "^10.0.0",
21
22
  "vue": "^3.4.21",
@@ -1897,6 +1898,14 @@
1897
1898
  "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
1898
1899
  "dev": true
1899
1900
  },
1901
+ "node_modules/deepmerge": {
1902
+ "version": "4.3.1",
1903
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
1904
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
1905
+ "engines": {
1906
+ "node": ">=0.10.0"
1907
+ }
1908
+ },
1900
1909
  "node_modules/didyoumean": {
1901
1910
  "version": "1.2.2",
1902
1911
  "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@@ -1938,6 +1947,57 @@
1938
1947
  "node": ">=6.0.0"
1939
1948
  }
1940
1949
  },
1950
+ "node_modules/dom-serializer": {
1951
+ "version": "2.0.0",
1952
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
1953
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
1954
+ "dependencies": {
1955
+ "domelementtype": "^2.3.0",
1956
+ "domhandler": "^5.0.2",
1957
+ "entities": "^4.2.0"
1958
+ },
1959
+ "funding": {
1960
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
1961
+ }
1962
+ },
1963
+ "node_modules/domelementtype": {
1964
+ "version": "2.3.0",
1965
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
1966
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
1967
+ "funding": [
1968
+ {
1969
+ "type": "github",
1970
+ "url": "https://github.com/sponsors/fb55"
1971
+ }
1972
+ ]
1973
+ },
1974
+ "node_modules/domhandler": {
1975
+ "version": "5.0.3",
1976
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
1977
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
1978
+ "dependencies": {
1979
+ "domelementtype": "^2.3.0"
1980
+ },
1981
+ "engines": {
1982
+ "node": ">= 4"
1983
+ },
1984
+ "funding": {
1985
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
1986
+ }
1987
+ },
1988
+ "node_modules/domutils": {
1989
+ "version": "3.1.0",
1990
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
1991
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
1992
+ "dependencies": {
1993
+ "dom-serializer": "^2.0.0",
1994
+ "domelementtype": "^2.3.0",
1995
+ "domhandler": "^5.0.3"
1996
+ },
1997
+ "funding": {
1998
+ "url": "https://github.com/fb55/domutils?sponsor=1"
1999
+ }
2000
+ },
1941
2001
  "node_modules/eastasianwidth": {
1942
2002
  "version": "0.2.0",
1943
2003
  "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -2018,7 +2078,6 @@
2018
2078
  "version": "4.0.0",
2019
2079
  "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
2020
2080
  "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
2021
- "dev": true,
2022
2081
  "engines": {
2023
2082
  "node": ">=10"
2024
2083
  },
@@ -2545,6 +2604,24 @@
2545
2604
  "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
2546
2605
  "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
2547
2606
  },
2607
+ "node_modules/htmlparser2": {
2608
+ "version": "8.0.2",
2609
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
2610
+ "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
2611
+ "funding": [
2612
+ "https://github.com/fb55/htmlparser2?sponsor=1",
2613
+ {
2614
+ "type": "github",
2615
+ "url": "https://github.com/sponsors/fb55"
2616
+ }
2617
+ ],
2618
+ "dependencies": {
2619
+ "domelementtype": "^2.3.0",
2620
+ "domhandler": "^5.0.3",
2621
+ "domutils": "^3.0.1",
2622
+ "entities": "^4.4.0"
2623
+ }
2624
+ },
2548
2625
  "node_modules/ignore": {
2549
2626
  "version": "5.3.1",
2550
2627
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@@ -2673,6 +2750,14 @@
2673
2750
  "node": ">=8"
2674
2751
  }
2675
2752
  },
2753
+ "node_modules/is-plain-object": {
2754
+ "version": "5.0.0",
2755
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
2756
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
2757
+ "engines": {
2758
+ "node": ">=0.10.0"
2759
+ }
2760
+ },
2676
2761
  "node_modules/isexe": {
2677
2762
  "version": "2.0.0",
2678
2763
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -3103,6 +3188,11 @@
3103
3188
  "node": ">=6"
3104
3189
  }
3105
3190
  },
3191
+ "node_modules/parse-srcset": {
3192
+ "version": "1.0.2",
3193
+ "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
3194
+ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
3195
+ },
3106
3196
  "node_modules/path-browserify": {
3107
3197
  "version": "1.0.1",
3108
3198
  "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
@@ -3593,6 +3683,19 @@
3593
3683
  "queue-microtask": "^1.2.2"
3594
3684
  }
3595
3685
  },
3686
+ "node_modules/sanitize-html": {
3687
+ "version": "2.13.0",
3688
+ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.13.0.tgz",
3689
+ "integrity": "sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==",
3690
+ "dependencies": {
3691
+ "deepmerge": "^4.2.2",
3692
+ "escape-string-regexp": "^4.0.0",
3693
+ "htmlparser2": "^8.0.0",
3694
+ "is-plain-object": "^5.0.0",
3695
+ "parse-srcset": "^1.0.2",
3696
+ "postcss": "^8.3.11"
3697
+ }
3698
+ },
3596
3699
  "node_modules/sass": {
3597
3700
  "version": "1.77.2",
3598
3701
  "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz",
package/spa/package.json CHANGED
@@ -20,6 +20,7 @@
20
20
  "flowbite": "^2.3.0",
21
21
  "flowbite-datepicker": "^1.2.6",
22
22
  "pinia": "^2.1.7",
23
+ "sanitize-html": "^2.13.0",
23
24
  "unhead": "^1.9.12",
24
25
  "uuid": "^10.0.0",
25
26
  "vue": "^3.4.21",
@@ -189,8 +189,6 @@ function setFilterItem({ column, operator, value }) {
189
189
  if (index !== -1) {
190
190
  filtersStore.filters.splice(index, 1);
191
191
  }
192
- emits('update:filters', [...filtersStore.filters]);
193
- return;
194
192
  } else {
195
193
  if (index === -1) {
196
194
  filtersStore.setFilter({ field: column.name, value, operator });
@@ -207,5 +205,6 @@ function getFilterItem({ column, operator }) {
207
205
 
208
206
  async function clear() {
209
207
  filtersStore.clearFilters();
208
+ emits('update:filters', [...filtersStore.filters]);
210
209
  }
211
210
  </script>
@@ -1,6 +1,5 @@
1
1
  <template>
2
2
  <div class="rounded-default">
3
-
4
3
  <div
5
4
  class="relative shadow-resourseFormShadow sm:rounded-lg dark:shadow-2xl dark:shadow-black rounded-default"
6
5
  >
@@ -44,6 +43,7 @@
44
43
  :meta="column.components[props.source].meta"
45
44
  :record="props.record"
46
45
  @update:inValidity="customComponentsInValidity[column.name] = $event"
46
+ @update:emptiness="customComponentsEmptiness[column.name] = $event"
47
47
  />
48
48
  </template>
49
49
  <template v-else>
@@ -171,6 +171,7 @@ const emit = defineEmits(['update:record', 'update:isValid']);
171
171
  const currentValues = ref({});
172
172
 
173
173
  const customComponentsInValidity = ref({});
174
+ const customComponentsEmptiness = ref({});
174
175
 
175
176
 
176
177
  const columnError = (column) => {
@@ -179,7 +180,14 @@ const columnError = (column) => {
179
180
  return customComponentsInValidity.value[column.name];
180
181
  }
181
182
 
182
- if ( column.required[mode.value] && (currentValues.value[column.name] === undefined || currentValues.value[column.name] === null || currentValues.value[column.name] === '') ) {
183
+ if (
184
+ column.required[mode.value] &&
185
+ (currentValues.value[column.name] === undefined || currentValues.value[column.name] === null || currentValues.value[column.name] === '') &&
186
+ // if component is custum it might tell other criteria for emptiness by emitting 'update:emptiness'
187
+ // components which do not emit 'update:emptiness' will have undefined value in customComponentsEmptiness
188
+ (customComponentsEmptiness.value[column.name] !== false)
189
+
190
+ ) {
183
191
  return 'This field is required';
184
192
  }
185
193
  if ( column.type === 'string' || column.type === 'text' ) {
@@ -20,6 +20,9 @@
20
20
  <span v-else-if="column.type === 'datetime'" class="whitespace-nowrap">
21
21
  {{ checkEmptyValues(formatDate(record[column.name]),route.meta.type) }}
22
22
  </span>
23
+ <span v-else-if="column.type === 'richtext'">
24
+ <div v-html="protectAgainstXSS(record[column.name])"></div>
25
+ </span>
23
26
  <span v-else>
24
27
  {{ checkEmptyValues(record[column.name],route.meta.type) }}
25
28
  </span>
@@ -34,6 +37,7 @@ import utc from 'dayjs/plugin/utc';
34
37
  import timezone from 'dayjs/plugin/timezone';
35
38
  import {checkEmptyValues} from '@/utils';
36
39
  import { useRoute, useRouter } from 'vue-router';
40
+ import sanitizeHtml from 'sanitize-html';
37
41
 
38
42
 
39
43
  import { useCoreStore } from '@/stores/core';
@@ -50,6 +54,10 @@ const props = defineProps({
50
54
  record: Object
51
55
  });
52
56
 
57
+ function protectAgainstXSS(value) {
58
+ return sanitizeHtml(value);
59
+ }
60
+
53
61
 
54
62
  function formatDate(date) {
55
63
  if (!date) return '';
@@ -1385,7 +1385,11 @@ export type AdminForthFieldComponents = {
1385
1385
  /**
1386
1386
  * List component is used to redefine cell which renders field value in list view.
1387
1387
  * Component accepts next properties: [record, column, resource, adminUser].
1388
- *
1388
+ * Component can emit events:
1389
+ * - `update:value` - to update record value.
1390
+ * - `update:inValidity` - emit true once entered value became not valid (e.g. emit('update:inValidity', true) ). Emit false once entered value became valid. Emit default value in mounted hook.
1391
+ * - `update:emptiness` - emit true once entered value became empty (e.g. emit('update:emptiness', true) ). Emit false once entered value became not empty. Emit default value in mounted hook.
1392
+ * emptiness emit is optional and required for complex cases. For example for virtual columns where initial value is not set.
1389
1393
  */
1390
1394
  list?: AdminForthComponentDeclaration,
1391
1395
  }
@@ -1401,6 +1405,7 @@ export enum AdminForthDataTypes {
1401
1405
  TIME = 'time',
1402
1406
  TEXT = 'text',
1403
1407
  JSON = 'json',
1408
+ RICHTEXT = 'richtext',
1404
1409
  }
1405
1410
 
1406
1411
  export enum AdminForthFilterOperators {