adminforth 1.2.22 → 1.2.24
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.
- package/dataConnectors/mongo.ts +3 -1
- package/dist/dataConnectors/mongo.js +3 -1
- package/dist/modules/codeInjector.js +1 -0
- package/dist/modules/configValidator.js +5 -0
- package/dist/spa/spa/package-lock.json +104 -1
- package/dist/spa/spa/package.json +1 -0
- package/dist/spa/spa/src/components/Filters.vue +1 -2
- package/dist/spa/spa/src/components/ResourceForm.vue +11 -3
- package/dist/spa/spa/src/components/ValueRenderer.vue +8 -0
- package/dist/types/AdminForthConfig.js +1 -0
- package/modules/codeInjector.ts +1 -0
- package/modules/configValidator.ts +5 -0
- package/package.json +1 -1
- package/spa/package-lock.json +104 -1
- package/spa/package.json +1 -0
- package/spa/src/components/Filters.vue +1 -2
- package/spa/src/components/ResourceForm.vue +11 -3
- package/spa/src/components/ValueRenderer.vue +8 -0
- package/types/AdminForthConfig.ts +12 -1
package/dataConnectors/mongo.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
import { MongoClient } from 'mongodb';
|
|
3
3
|
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, IAdminForthDataSourceConnector } from '../types/AdminForthConfig.js';
|
|
4
|
+
import AdminForthBaseConnector from './baseConnector.js';
|
|
4
5
|
|
|
5
|
-
class MongoConnector implements IAdminForthDataSourceConnector {
|
|
6
|
+
class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataSourceConnector {
|
|
6
7
|
db: MongoClient
|
|
7
8
|
|
|
8
9
|
constructor({ url }: { url: string }) {
|
|
10
|
+
super();
|
|
9
11
|
this.db = new MongoClient(url);
|
|
10
12
|
(async () => {
|
|
11
13
|
try {
|
|
@@ -10,8 +10,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import dayjs from 'dayjs';
|
|
11
11
|
import { MongoClient } from 'mongodb';
|
|
12
12
|
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
|
|
13
|
-
|
|
13
|
+
import AdminForthBaseConnector from './baseConnector.js';
|
|
14
|
+
class MongoConnector extends AdminForthBaseConnector {
|
|
14
15
|
constructor({ url }) {
|
|
16
|
+
super();
|
|
15
17
|
this.OperatorsMap = {
|
|
16
18
|
[AdminForthFilterOperators.EQ]: (value) => value,
|
|
17
19
|
[AdminForthFilterOperators.NE]: (value) => ({ $ne: value }),
|
|
@@ -404,6 +404,7 @@ class CodeInjector {
|
|
|
404
404
|
const pluginPackages = [];
|
|
405
405
|
// for every installed plugin generate packages
|
|
406
406
|
for (const plugin of this.adminforth.activatedPlugins) {
|
|
407
|
+
console.log('🔧 Checking packages for plugin', plugin.customFolderPath);
|
|
407
408
|
const [lockHash, packages] = yield this.packagesFromNpm(plugin.customFolderPath);
|
|
408
409
|
if (packages.length) {
|
|
409
410
|
pluginPackages.push({
|
|
@@ -405,5 +405,10 @@ export default class ConfigValidator {
|
|
|
405
405
|
}
|
|
406
406
|
});
|
|
407
407
|
resource.dataSourceColumns = resource.columns.filter((col) => !col.virtual);
|
|
408
|
+
(resource.plugins || []).forEach((plugin) => {
|
|
409
|
+
if (plugin.validateConfigAfterDiscover) {
|
|
410
|
+
plugin.validateConfigAfterDiscover(this.adminforth, resource);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
408
413
|
}
|
|
409
414
|
}
|
|
@@ -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",
|
|
@@ -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>
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
@input="setCurrentValue(column.name, $event.target.value)"
|
|
96
96
|
/>
|
|
97
97
|
<textarea
|
|
98
|
-
v-else-if="['text'].includes(column.type)"
|
|
98
|
+
v-else-if="['text', 'richtext'].includes(column.type)"
|
|
99
99
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
100
100
|
placeholder="Text"
|
|
101
101
|
:value="currentValues[column.name]"
|
|
@@ -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 (
|
|
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/modules/codeInjector.ts
CHANGED
|
@@ -462,6 +462,7 @@ class CodeInjector implements ICodeInjector {
|
|
|
462
462
|
|
|
463
463
|
// for every installed plugin generate packages
|
|
464
464
|
for (const plugin of this.adminforth.activatedPlugins) {
|
|
465
|
+
console.log('🔧 Checking packages for plugin', plugin.customFolderPath);
|
|
465
466
|
const [lockHash, packages] = await this.packagesFromNpm(plugin.customFolderPath);
|
|
466
467
|
if (packages.length) {
|
|
467
468
|
pluginPackages.push({
|
|
@@ -449,5 +449,10 @@ export default class ConfigValidator implements IConfigValidator {
|
|
|
449
449
|
}
|
|
450
450
|
})
|
|
451
451
|
resource.dataSourceColumns = resource.columns.filter((col) => !col.virtual);
|
|
452
|
+
(resource.plugins || []).forEach((plugin) => {
|
|
453
|
+
if (plugin.validateConfigAfterDiscover) {
|
|
454
|
+
plugin.validateConfigAfterDiscover(this.adminforth, resource);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
452
457
|
}
|
|
453
458
|
}
|
package/package.json
CHANGED
package/spa/package-lock.json
CHANGED
|
@@ -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
|
@@ -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>
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
@input="setCurrentValue(column.name, $event.target.value)"
|
|
96
96
|
/>
|
|
97
97
|
<textarea
|
|
98
|
-
v-else-if="['text'].includes(column.type)"
|
|
98
|
+
v-else-if="['text', 'richtext'].includes(column.type)"
|
|
99
99
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
100
100
|
placeholder="Text"
|
|
101
101
|
:value="currentValues[column.name]"
|
|
@@ -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 (
|
|
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 '';
|
|
@@ -285,6 +285,12 @@ export interface IAdminForthPlugin {
|
|
|
285
285
|
modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource): void;
|
|
286
286
|
componentPath(componentFile: string): string;
|
|
287
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Optional method which will be called after AdminForth discovers all resources and their columns.
|
|
290
|
+
* Can be used to validate types of columns, check if some columns are missing, etc.
|
|
291
|
+
*/
|
|
292
|
+
validateConfigAfterDiscover?(adminforth: IAdminForth, resourceConfig: AdminForthResource): void;
|
|
293
|
+
|
|
288
294
|
/**
|
|
289
295
|
* Here you can register custom endpoints for your plugin.
|
|
290
296
|
*
|
|
@@ -1385,7 +1391,11 @@ export type AdminForthFieldComponents = {
|
|
|
1385
1391
|
/**
|
|
1386
1392
|
* List component is used to redefine cell which renders field value in list view.
|
|
1387
1393
|
* Component accepts next properties: [record, column, resource, adminUser].
|
|
1388
|
-
*
|
|
1394
|
+
* Component can emit events:
|
|
1395
|
+
* - `update:value` - to update record value.
|
|
1396
|
+
* - `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.
|
|
1397
|
+
* - `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.
|
|
1398
|
+
* emptiness emit is optional and required for complex cases. For example for virtual columns where initial value is not set.
|
|
1389
1399
|
*/
|
|
1390
1400
|
list?: AdminForthComponentDeclaration,
|
|
1391
1401
|
}
|
|
@@ -1401,6 +1411,7 @@ export enum AdminForthDataTypes {
|
|
|
1401
1411
|
TIME = 'time',
|
|
1402
1412
|
TEXT = 'text',
|
|
1403
1413
|
JSON = 'json',
|
|
1414
|
+
RICHTEXT = 'richtext',
|
|
1404
1415
|
}
|
|
1405
1416
|
|
|
1406
1417
|
export enum AdminForthFilterOperators {
|