aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRigel Kent <sendmemail@rigelk.eu>2021-04-11 15:06:36 +0200
committerChocobozzz <chocobozzz@cpy.re>2021-04-13 15:45:09 +0200
commit84bced652cd72aad852914a4a734c47dd0002fef (patch)
treefd1b5fb097c0958792be7e89fe1dec621eb0c841
parent13fec08ba3e6dff8a4a72163d94cbdb32ad57563 (diff)
downloadPeerTube-84bced652cd72aad852914a4a734c47dd0002fef.tar.gz
PeerTube-84bced652cd72aad852914a4a734c47dd0002fef.tar.zst
PeerTube-84bced652cd72aad852914a4a734c47dd0002fef.zip
render markdown and plainify descriptions on previews
-rw-r--r--server/helpers/markdown.ts26
-rw-r--r--server/lib/client-html.ts17
-rw-r--r--server/lib/emailer.ts22
3 files changed, 38 insertions, 27 deletions
diff --git a/server/helpers/markdown.ts b/server/helpers/markdown.ts
new file mode 100644
index 000000000..c8fb31c8c
--- /dev/null
+++ b/server/helpers/markdown.ts
@@ -0,0 +1,26 @@
1import { SANITIZE_OPTIONS, TEXT_WITH_HTML_RULES } from '@shared/core-utils'
2
3const sanitizeHtml = require('sanitize-html')
4const markdownItEmoji = require('markdown-it-emoji/light')
5const MarkdownItClass = require('markdown-it')
6const markdownIt = new MarkdownItClass('default', { linkify: true, breaks: true, html: true })
7
8markdownIt.enable(TEXT_WITH_HTML_RULES)
9markdownIt.use(markdownItEmoji)
10
11const toSafeHtml = text => {
12 // Restore line feed
13 const textWithLineFeed = text.replace(/<br.?\/?>/g, '\r\n')
14
15 // Convert possible markdown (emojis, emphasis and lists) to html
16 const html = markdownIt.render(textWithLineFeed)
17
18 // Convert to safe Html
19 return sanitizeHtml(html, SANITIZE_OPTIONS)
20}
21
22// ---------------------------------------------------------------------------
23
24export {
25 toSafeHtml
26}
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index 6ddaa82c8..5485376d3 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -24,6 +24,7 @@ import { VideoChannelModel } from '../models/video/video-channel'
24import { getActivityStreamDuration } from '../models/video/video-format-utils' 24import { getActivityStreamDuration } from '../models/video/video-format-utils'
25import { VideoPlaylistModel } from '../models/video/video-playlist' 25import { VideoPlaylistModel } from '../models/video/video-playlist'
26import { MAccountActor, MChannelActor } from '../types/models' 26import { MAccountActor, MChannelActor } from '../types/models'
27import { toSafeHtml } from '../helpers/markdown'
27 28
28type Tags = { 29type Tags = {
29 ogType: string 30 ogType: string
@@ -54,6 +55,10 @@ type Tags = {
54 } 55 }
55} 56}
56 57
58const toPlainText = (content: string) => {
59 return toSafeHtml(content).replace(/<[^>]+>/g, '')
60}
61
57class ClientHtml { 62class ClientHtml {
58 63
59 private static htmlCache: { [path: string]: string } = {} 64 private static htmlCache: { [path: string]: string } = {}
@@ -94,13 +99,13 @@ class ClientHtml {
94 } 99 }
95 100
96 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(video.name)) 101 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(video.name))
97 customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(video.description)) 102 customHtml = ClientHtml.addDescriptionTag(customHtml, toPlainText(video.description))
98 103
99 const url = WEBSERVER.URL + video.getWatchStaticPath() 104 const url = WEBSERVER.URL + video.getWatchStaticPath()
100 const originUrl = video.url 105 const originUrl = video.url
101 const title = escapeHTML(video.name) 106 const title = escapeHTML(video.name)
102 const siteName = escapeHTML(CONFIG.INSTANCE.NAME) 107 const siteName = escapeHTML(CONFIG.INSTANCE.NAME)
103 const description = escapeHTML(video.description) 108 const description = toPlainText(video.description)
104 109
105 const image = { 110 const image = {
106 url: WEBSERVER.URL + video.getPreviewStaticPath() 111 url: WEBSERVER.URL + video.getPreviewStaticPath()
@@ -152,13 +157,13 @@ class ClientHtml {
152 } 157 }
153 158
154 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(videoPlaylist.name)) 159 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(videoPlaylist.name))
155 customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(videoPlaylist.description)) 160 customHtml = ClientHtml.addDescriptionTag(customHtml, toPlainText(videoPlaylist.description))
156 161
157 const url = videoPlaylist.getWatchUrl() 162 const url = videoPlaylist.getWatchUrl()
158 const originUrl = videoPlaylist.url 163 const originUrl = videoPlaylist.url
159 const title = escapeHTML(videoPlaylist.name) 164 const title = escapeHTML(videoPlaylist.name)
160 const siteName = escapeHTML(CONFIG.INSTANCE.NAME) 165 const siteName = escapeHTML(CONFIG.INSTANCE.NAME)
161 const description = escapeHTML(videoPlaylist.description) 166 const description = toPlainText(videoPlaylist.description)
162 167
163 const image = { 168 const image = {
164 url: videoPlaylist.getThumbnailUrl() 169 url: videoPlaylist.getThumbnailUrl()
@@ -236,13 +241,13 @@ class ClientHtml {
236 } 241 }
237 242
238 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(entity.getDisplayName())) 243 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(entity.getDisplayName()))
239 customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(entity.description)) 244 customHtml = ClientHtml.addDescriptionTag(customHtml, toPlainText(entity.description))
240 245
241 const url = entity.getLocalUrl() 246 const url = entity.getLocalUrl()
242 const originUrl = entity.Actor.url 247 const originUrl = entity.Actor.url
243 const siteName = escapeHTML(CONFIG.INSTANCE.NAME) 248 const siteName = escapeHTML(CONFIG.INSTANCE.NAME)
244 const title = escapeHTML(entity.getDisplayName()) 249 const title = escapeHTML(entity.getDisplayName())
245 const description = escapeHTML(entity.description) 250 const description = toPlainText(entity.description)
246 251
247 const image = { 252 const image = {
248 url: entity.Actor.getAvatarUrl(), 253 url: entity.Actor.getAvatarUrl(),
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts
index 9ca0d5d5b..2fad82bcc 100644
--- a/server/lib/emailer.ts
+++ b/server/lib/emailer.ts
@@ -5,7 +5,6 @@ import { join } from 'path'
5import { VideoChannelModel } from '@server/models/video/video-channel' 5import { VideoChannelModel } from '@server/models/video/video-channel'
6import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist' 6import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist'
7import { MVideoImport, MVideoImportVideo } from '@server/types/models/video/video-import' 7import { MVideoImport, MVideoImportVideo } from '@server/types/models/video/video-import'
8import { SANITIZE_OPTIONS, TEXT_WITH_HTML_RULES } from '@shared/core-utils'
9import { AbuseState, EmailPayload, UserAbuse } from '@shared/models' 8import { AbuseState, EmailPayload, UserAbuse } from '@shared/models'
10import { SendEmailDefaultOptions } from '../../shared/models/server/emailer.model' 9import { SendEmailDefaultOptions } from '../../shared/models/server/emailer.model'
11import { isTestInstance, root } from '../helpers/core-utils' 10import { isTestInstance, root } from '../helpers/core-utils'
@@ -15,26 +14,7 @@ import { WEBSERVER } from '../initializers/constants'
15import { MAbuseFull, MAbuseMessage, MAccountDefault, MActorFollowActors, MActorFollowFull, MPlugin, MUser } from '../types/models' 14import { MAbuseFull, MAbuseMessage, MAccountDefault, MActorFollowActors, MActorFollowFull, MPlugin, MUser } from '../types/models'
16import { MCommentOwnerVideo, MVideo, MVideoAccountLight } from '../types/models/video' 15import { MCommentOwnerVideo, MVideo, MVideoAccountLight } from '../types/models/video'
17import { JobQueue } from './job-queue' 16import { JobQueue } from './job-queue'
18 17import { toSafeHtml } from '../helpers/markdown'
19const sanitizeHtml = require('sanitize-html')
20const markdownItEmoji = require('markdown-it-emoji/light')
21const MarkdownItClass = require('markdown-it')
22const markdownIt = new MarkdownItClass('default', { linkify: true, breaks: true, html: true })
23
24markdownIt.enable(TEXT_WITH_HTML_RULES)
25
26markdownIt.use(markdownItEmoji)
27
28const toSafeHtml = text => {
29 // Restore line feed
30 const textWithLineFeed = text.replace(/<br.?\/?>/g, '\r\n')
31
32 // Convert possible markdown (emojis, emphasis and lists) to html
33 const html = markdownIt.render(textWithLineFeed)
34
35 // Convert to safe Html
36 return sanitizeHtml(html, SANITIZE_OPTIONS)
37}
38 18
39const Email = require('email-templates') 19const Email = require('email-templates')
40 20