Skip to content

Commit ef4bbaa

Browse files
committed
Re-wrote description.vue to parse markdown. #2633
1 parent 1998335 commit ef4bbaa

6 files changed

Lines changed: 52 additions & 23 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"bootstrap-vue": "^2.16.0",
2626
"core-js": "^3.28.0",
2727
"deep-object-diff": "^1.1.0",
28+
"dompurify": "^3.3.1",
2829
"dotenv": "^16.0.3",
2930
"graphology": "^0.25.1",
3031
"graphology-layout": "^0.6.1",
Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,64 @@
11
<template>
22
<div class="d-flex flex-row mt-4 min-height-40">
3-
<span
4-
class="d-flex align-baseline width-15-percent-flex"
5-
>
3+
<span class="d-flex align-baseline width-15-percent-flex">
64
<v-tooltip bottom>
75
<template #activator="{ on }">
8-
<v-icon
9-
class="mr-2"
10-
size="15"
11-
v-on="on"
12-
>
13-
fa-question-circle
14-
</v-icon>
6+
<v-icon class="mr-2" size="15" v-on="on"> fa-question-circle </v-icon>
157
</template>
16-
{{ recordTooltips['description'] }}
8+
{{ recordTooltips.description }}
179
</v-tooltip>
1810
<b>Description</b>
1911
</span>
12+
13+
<!-- use v-html to inject sanitized HTML -->
2014
<p
2115
class="ma-0 full-width ml-md-12 ml-8"
22-
:class="{'text-end' : $vuetify.breakpoint.smAndDown}"
23-
>
24-
{{ getField('description') | capitalize }}
25-
</p>
16+
:class="{ 'text-end': $vuetify.breakpoint.smAndDown }"
17+
v-html="descriptionHtml"
18+
/>
2619
</div>
2720
</template>
2821

2922
<script>
30-
import {mapGetters, mapState} from "vuex";
23+
import DOMPurify from "dompurify";
24+
import MarkdownIt from "markdown-it";
25+
import { mapGetters, mapState } from "vuex";
3126
3227
import stringUtils from "@/utils/stringUtils";
28+
29+
const md = new MarkdownIt({
30+
html: true, // allow inline HTML in Markdown input (we will sanitize below)
31+
linkify: true,
32+
typographer: true,
33+
});
34+
3335
export default {
3436
name: "Description",
3537
mixins: [stringUtils],
3638
computed: {
3739
...mapGetters("record", ["getField"]),
3840
...mapState("editor", ["recordTooltips"]),
39-
}
40-
}
41+
42+
// raw description string from store (fallback to empty string)
43+
descriptionRaw() {
44+
const d = this.getField("description");
45+
return d == null ? "" : d;
46+
},
47+
48+
// capitalise using stringutils capitalize method, then render => sanitize
49+
descriptionHtml() {
50+
// if your mixin exposes `capitalize`, call it; otherwise remove the call
51+
const capitalized =
52+
typeof this.capitalize === "function"
53+
? this.capitalize(this.descriptionRaw)
54+
: this.descriptionRaw;
55+
56+
const unsafe = md.render(capitalized || "");
57+
// Sanitize the generated HTML to avoid XSS.
58+
return DOMPurify.sanitize(unsafe, {
59+
// ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto|tel):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i
60+
});
61+
},
62+
},
63+
};
4164
</script>

src/components/Records/Record/RecordAlert.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
:icon="type === 'info' ? 'fas fa-circle-exclamation' : null"
77
>
88
<!-- eslint-disable vue/no-v-html -->
9-
<span id="message-text" v-html="message" />
9+
<span
10+
id="message-text"
11+
v-html="message"
12+
/>
1013
<!-- eslint-enable vue/no-v-html -->
1114
</v-alert>
1215
</template>

src/main.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ import 'roboto-fontface/css/roboto/roboto-fontface.css'
4242
import '@fortawesome/fontawesome-free/css/all.css'
4343
import 'vue-json-pretty/lib/styles.css'
4444

45-
4645
Variablepie(Highcharts);
4746
More(Highcharts);
4847
Export(Highcharts);

vue.config.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ const CompressionPlugin = require('compression-webpack-plugin');
22

33
module.exports = {
44
"transpileDependencies": [
5-
"vuetify"
5+
"vuetify",
6+
"markstream-vue2"
67
],
78
chainWebpack(config) {
8-
config.plugins.delete('prefetch');
9-
config.plugin('CompressionPlugin').use(CompressionPlugin);
9+
config.plugins.delete("prefetch");
10+
config.plugin("CompressionPlugin").use(CompressionPlugin);
11+
1012
},
1113
css: {
1214
loaderOptions: {

webpack.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ module.exports = {
5656
loader: "babel-loader"
5757
}
5858
},
59+
5960
]
6061
},
6162
plugins: [

0 commit comments

Comments
 (0)