Skip to content

Commit b82d0a1

Browse files
authored
Merge pull request #208 from devforth/replace-richtext-type-to-renderers
BREAKING CHANGE: replace 'RICHTEXT' with 'TEXT' in data types and update related components
2 parents a498586 + 6ea51fb commit b82d0a1

File tree

12 files changed

+121
-37
lines changed

12 files changed

+121
-37
lines changed

adminforth/commands/utils.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ export const mapToTypeScriptType = (adminType) => {
1313
switch (adminType) {
1414
case "string":
1515
case "text":
16-
case "richtext":
1716
case "datetime":
1817
case "date":
1918
case "time":

adminforth/documentation/blog/2024-10-01-ai-blog/index.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,10 @@ export default {
515515
{
516516
name: 'content',
517517
showIn: { list: false },
518-
type: AdminForthDataTypes.RICHTEXT,
518+
type: AdminForthDataTypes.TEXT,
519+
components: {
520+
show: "@/renderers/RichText.vue",
521+
}
519522
},
520523
{
521524
name: 'createdAt',

adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ export default {
534534

535535
Doing so, will result in UI displaying each item of the array as a separate input corresponding to `isArray.itemType` on create and edit pages.
536536

537-
`itemType` value can be any of `AdminForthDataTypes` except `JSON` and `RICHTEXT`.
537+
`itemType` value can be any of `AdminForthDataTypes` except `JSON` and `TEXT`.
538538

539539
By default it is forbidden to store duplicate values in an array column. To change that you can add `allowDuplicateItems: true` to `isArray`, like so:
540540

adminforth/documentation/docs/tutorial/05-Plugins/04-RichEditor.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ Now instantiate the plugin and add it to the configuration:
3030
{
3131
name: 'description',
3232
//diff-add
33-
type: AdminForthDataTypes.RICHTEXT, // like plain AdminForthDataTypes.TEXT but renders HTML in show/list views
33+
type: AdminForthDataTypes.TEXT, // like plain AdminForthDataTypes.TEXT but renders HTML in show/list views
34+
components: {
35+
show: "@/renderers/RichText.vue",
36+
list: "@/renderers/RichText.vue",
37+
},
3438
...
3539
}
3640
...
@@ -65,12 +69,20 @@ If you need multiple fields in one resource which happens rarely, just add multi
6569
...
6670
{
6771
name: 'short_description',
68-
type: AdminForthDataTypes.RICHTEXT,
72+
type: AdminForthDataTypes.TEXT,
73+
components: {
74+
list: "@/renderers/RichText.vue",
75+
show: "@/renderers/RichText.vue",
76+
}
6977
...
7078
},
7179
{
7280
name: 'full_description',
73-
type: AdminForthDataTypes.RICHTEXT,
81+
type: AdminForthDataTypes.TEXT,
82+
components: {
83+
list: "@/renderers/RichText.vue",
84+
show: "@/renderers/RichText.vue",
85+
}
7486
...
7587
}
7688
...

adminforth/modules/configValidator.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -548,9 +548,6 @@ export default class ConfigValidator implements IConfigValidator {
548548
if (col.isArray.itemType === AdminForthDataTypes.JSON) {
549549
errors.push(`Resource "${res.resourceId}" column "${col.name}" isArray itemType cannot be JSON`);
550550
}
551-
if (col.isArray.itemType === AdminForthDataTypes.RICHTEXT) {
552-
errors.push(`Resource "${res.resourceId}" column "${col.name}" isArray itemType cannot be RICHTEXT`);
553-
}
554551
}
555552
}
556553

@@ -569,7 +566,7 @@ export default class ConfigValidator implements IConfigValidator {
569566
}
570567

571568
// if suggestOnCreate is string, column should be one of the types with text inputs
572-
if (typeof inCol.suggestOnCreate === 'string' && ![AdminForthDataTypes.STRING, AdminForthDataTypes.DATE, AdminForthDataTypes.DATETIME, AdminForthDataTypes.TIME, AdminForthDataTypes.TEXT, AdminForthDataTypes.RICHTEXT, undefined].includes(inCol.type)) {
569+
if (typeof inCol.suggestOnCreate === 'string' && ![AdminForthDataTypes.STRING, AdminForthDataTypes.DATE, AdminForthDataTypes.DATETIME, AdminForthDataTypes.TIME, AdminForthDataTypes.TEXT, undefined].includes(inCol.type)) {
573570
errors.push(`Resource "${res.resourceId}" column "${col.name}" suggestOnCreate value does not match type of a column`);
574571
}
575572

adminforth/spa/src/components/ValueRenderer.vue

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,6 @@
6969
<span v-else-if="column.type === 'time'" class="whitespace-nowrap">
7070
{{ checkEmptyValues(formatTime(record[column.name]), route.meta.type) }}
7171
</span>
72-
<span v-else-if="column.type === 'richtext'">
73-
<div v-html="protectAgainstXSS(record[column.name])" class="allow-lists"></div>
74-
</span>
7572
<span v-else-if="column.type === 'decimal'">
7673
{{ checkEmptyValues(record[column.name] && parseFloat(record[column.name]), route.meta.type) }}
7774
</span>
@@ -92,7 +89,6 @@ import utc from 'dayjs/plugin/utc';
9289
import timezone from 'dayjs/plugin/timezone';
9390
import {checkEmptyValues} from '@/utils';
9491
import { useRoute, useRouter } from 'vue-router';
95-
import sanitizeHtml from 'sanitize-html';
9692
import { JsonViewer } from "vue3-json-viewer";
9793
import "vue3-json-viewer/dist/index.css";
9894
import type { AdminForthResourceColumnCommon } from '@/types/Common';
@@ -111,26 +107,6 @@ const props = defineProps<{
111107
record: any
112108
}>();
113109
114-
115-
function protectAgainstXSS(value: string) {
116-
return sanitizeHtml(value, {
117-
allowedTags: [
118-
"address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4",
119-
"h5", "h6", "hgroup", "main", "nav", "section", "blockquote", "dd", "div",
120-
"dl", "dt", "figcaption", "figure", "hr", "li", "main", "ol", "p", "pre",
121-
"ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn",
122-
"em", "i", "kbd", "mark", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp",
123-
"small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "caption",
124-
"col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", 'img'
125-
],
126-
allowedAttributes: {
127-
'li': [ 'data-list' ],
128-
'img': [ 'src', 'srcset', 'alt', 'title', 'width', 'height', 'loading' ]
129-
}
130-
});
131-
}
132-
133-
134110
function formatDateTime(date: string) {
135111
if (!date) return '';
136112
return dayjs.utc(date).local().format(`${coreStore.config?.datesFormat} ${coreStore.config?.timeFormat}` || 'YYYY-MM-DD HH:mm:ss');
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<template>
2+
<div class="rich-text" v-html="htmlContent"></div>
3+
</template>
4+
5+
<script setup lang="ts">
6+
import type { AdminForthResourceColumnCommon, AdminForthResourceCommon, AdminUser } from '@/types/Common'
7+
import { protectAgainstXSS } from '@/utils'
8+
9+
const props = defineProps<{
10+
column: AdminForthResourceColumnCommon
11+
record: any
12+
meta: any
13+
resource: AdminForthResourceCommon
14+
adminUser: AdminUser
15+
}>()
16+
const htmlContent = protectAgainstXSS(props.record[props.column.name])
17+
18+
</script>
19+
20+
<style scoped>
21+
.rich-text {
22+
/* You can add default styles here if needed */
23+
word-break: break-word;
24+
}
25+
</style>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<template>
2+
<iframe ref="iframeRef" class="zero-styles-iframe" />
3+
</template>
4+
5+
<script setup lang="ts">
6+
import { onMounted, ref, watch } from 'vue'
7+
import type { AdminForthResourceColumnCommon, AdminForthResourceCommon, AdminUser } from '@/types/Common'
8+
import { protectAgainstXSS } from '@/utils'
9+
10+
const props = defineProps<{
11+
column: AdminForthResourceColumnCommon
12+
record: any
13+
meta: any
14+
resource: AdminForthResourceCommon
15+
adminUser: AdminUser
16+
}>()
17+
18+
const iframeRef = ref<HTMLIFrameElement | null>(null)
19+
20+
const renderHtml = () => {
21+
const iframe = iframeRef.value
22+
if (!iframe) return
23+
24+
const doc = iframe.contentDocument || iframe.contentWindow?.document
25+
if (!doc) return
26+
27+
iframe.style.border = "none"
28+
iframe.style.width = "100%"
29+
iframe.style.height = "400px"
30+
31+
doc.open()
32+
doc.write(protectAgainstXSS(props.record[props.column.name]) || '')
33+
doc.close()
34+
}
35+
36+
37+
onMounted(renderHtml)
38+
watch(() => props.record[props.column.name], renderHtml)
39+
</script>
40+
41+
<style scoped>
42+
.zero-styles-iframe {
43+
width: 100%;
44+
border: none;
45+
}
46+
</style>
47+

adminforth/spa/src/utils.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useCoreStore } from './stores/core';
66
import { useUserStore } from './stores/user';
77
import { Dropdown } from 'flowbite';
88
import adminforth from './adminforth';
9+
import sanitizeHtml from 'sanitize-html'
910

1011
const LS_LANG_KEY = `afLanguage`;
1112

@@ -190,3 +191,21 @@ export function humanifySize(size) {
190191
}
191192
return `${size.toFixed(1)} ${units[i]}`
192193
}
194+
195+
export function protectAgainstXSS(value: string) {
196+
return sanitizeHtml(value, {
197+
allowedTags: [
198+
"address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4",
199+
"h5", "h6", "hgroup", "main", "nav", "section", "blockquote", "dd", "div",
200+
"dl", "dt", "figcaption", "figure", "hr", "li", "main", "ol", "p", "pre",
201+
"ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn",
202+
"em", "i", "kbd", "mark", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp",
203+
"small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "caption",
204+
"col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", 'img'
205+
],
206+
allowedAttributes: {
207+
'li': [ 'data-list' ],
208+
'img': [ 'src', 'srcset', 'alt', 'title', 'width', 'height', 'loading' ]
209+
}
210+
});
211+
}

adminforth/types/Common.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export enum AdminForthDataTypes {
1414
TIME = 'time',
1515
TEXT = 'text',
1616
JSON = 'json',
17-
RICHTEXT = 'richtext',
1817
}
1918

2019
export enum AdminForthFilterOperators {

0 commit comments

Comments
 (0)