diff options
author | Chocobozzz <me@florianbigard.com> | 2019-12-06 13:57:57 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-12-06 13:57:57 +0100 |
commit | 7e049ea4673f05cbb90b37199bb93713cc0d5611 (patch) | |
tree | 78f3683c6296f0289691edd65f01980c587b7267 | |
parent | d8232bac6abfb70953229a4164395f05cec90b35 (diff) | |
download | PeerTube-7e049ea4673f05cbb90b37199bb93713cc0d5611.tar.gz PeerTube-7e049ea4673f05cbb90b37199bb93713cc0d5611.tar.zst PeerTube-7e049ea4673f05cbb90b37199bb93713cc0d5611.zip |
Handle HTML in account descriptions
-rw-r--r-- | client/src/app/+accounts/account-about/account-about.component.ts | 2 | ||||
-rw-r--r-- | client/src/app/shared/renderer/markdown.service.ts | 85 |
2 files changed, 58 insertions, 29 deletions
diff --git a/client/src/app/+accounts/account-about/account-about.component.ts b/client/src/app/+accounts/account-about/account-about.component.ts index ce22d3c2e..c65bad3f2 100644 --- a/client/src/app/+accounts/account-about/account-about.component.ts +++ b/client/src/app/+accounts/account-about/account-about.component.ts | |||
@@ -27,7 +27,7 @@ export class AccountAboutComponent implements OnInit, OnDestroy { | |||
27 | this.accountSub = this.accountService.accountLoaded | 27 | this.accountSub = this.accountService.accountLoaded |
28 | .subscribe(async account => { | 28 | .subscribe(async account => { |
29 | this.account = account | 29 | this.account = account |
30 | this.descriptionHTML = await this.markdownService.textMarkdownToHTML(this.account.description) | 30 | this.descriptionHTML = await this.markdownService.enhancedMarkdownToHTML(this.account.description, true) |
31 | }) | 31 | }) |
32 | } | 32 | } |
33 | 33 | ||
diff --git a/client/src/app/shared/renderer/markdown.service.ts b/client/src/app/shared/renderer/markdown.service.ts index 9aec3b4bc..09bbbfb7e 100644 --- a/client/src/app/shared/renderer/markdown.service.ts +++ b/client/src/app/shared/renderer/markdown.service.ts | |||
@@ -1,6 +1,24 @@ | |||
1 | import { Injectable } from '@angular/core' | 1 | import { Injectable } from '@angular/core' |
2 | |||
3 | import { MarkdownIt } from 'markdown-it' | 2 | import { MarkdownIt } from 'markdown-it' |
3 | import { HtmlRendererService } from '@app/shared/renderer/html-renderer.service' | ||
4 | import { mark } from '@angular/compiler-cli/src/ngtsc/perf/src/clock' | ||
5 | |||
6 | type MarkdownParsers = { | ||
7 | textMarkdownIt: MarkdownIt | ||
8 | enhancedMarkdownIt: MarkdownIt | ||
9 | enhancedMarkdownWithHTMLIt: MarkdownIt | ||
10 | completeMarkdownIt: MarkdownIt | ||
11 | } | ||
12 | |||
13 | type MarkdownConfig = { | ||
14 | rules: string[] | ||
15 | html: boolean | ||
16 | escape?: boolean | ||
17 | } | ||
18 | |||
19 | type MarkdownParserConfigs = { | ||
20 | [id in keyof MarkdownParsers]: MarkdownConfig | ||
21 | } | ||
4 | 22 | ||
5 | @Injectable() | 23 | @Injectable() |
6 | export class MarkdownService { | 24 | export class MarkdownService { |
@@ -13,52 +31,63 @@ export class MarkdownService { | |||
13 | 'list' | 31 | 'list' |
14 | ] | 32 | ] |
15 | static ENHANCED_RULES = MarkdownService.TEXT_RULES.concat([ 'image' ]) | 33 | static ENHANCED_RULES = MarkdownService.TEXT_RULES.concat([ 'image' ]) |
16 | static COMPLETE_RULES = MarkdownService.ENHANCED_RULES.concat([ 'block', 'inline', 'heading', 'html_inline', 'html_block', 'paragraph' ]) | 34 | static ENHANCED_WITH_HTML_RULES = MarkdownService.ENHANCED_RULES.concat([ 'html_inline', 'html_block' ]) |
35 | static COMPLETE_RULES = MarkdownService.ENHANCED_WITH_HTML_RULES.concat([ 'block', 'inline', 'heading', 'paragraph' ]) | ||
36 | |||
37 | private markdownParsers: MarkdownParsers = { | ||
38 | textMarkdownIt: null, | ||
39 | enhancedMarkdownIt: null, | ||
40 | enhancedMarkdownWithHTMLIt: null, | ||
41 | completeMarkdownIt: null | ||
42 | } | ||
43 | private parsersConfig: MarkdownParserConfigs = { | ||
44 | textMarkdownIt: { rules: MarkdownService.TEXT_RULES, html: false }, | ||
45 | enhancedMarkdownIt: { rules: MarkdownService.ENHANCED_RULES, html: false }, | ||
46 | enhancedMarkdownWithHTMLIt: { rules: MarkdownService.ENHANCED_WITH_HTML_RULES, html: true, escape: true }, | ||
47 | completeMarkdownIt: { rules: MarkdownService.COMPLETE_RULES, html: true } | ||
48 | } | ||
17 | 49 | ||
18 | private textMarkdownIt: MarkdownIt | 50 | constructor (private htmlRenderer: HtmlRendererService) {} |
19 | private enhancedMarkdownIt: MarkdownIt | ||
20 | private completeMarkdownIt: MarkdownIt | ||
21 | 51 | ||
22 | async textMarkdownToHTML (markdown: string) { | 52 | textMarkdownToHTML (markdown: string) { |
23 | if (!markdown) return '' | 53 | return this.render('textMarkdownIt', markdown) |
54 | } | ||
24 | 55 | ||
25 | if (!this.textMarkdownIt) { | 56 | async enhancedMarkdownToHTML (markdown: string, withHtml = false) { |
26 | this.textMarkdownIt = await this.createMarkdownIt(MarkdownService.TEXT_RULES) | 57 | if (withHtml) return this.render('enhancedMarkdownWithHTMLIt', markdown) |
27 | } | ||
28 | 58 | ||
29 | const html = this.textMarkdownIt.render(markdown) | 59 | return this.render('enhancedMarkdownIt', markdown) |
30 | return this.avoidTruncatedTags(html) | 60 | } |
61 | |||
62 | async completeMarkdownToHTML (markdown: string) { | ||
63 | return this.render('completeMarkdownIt', markdown) | ||
31 | } | 64 | } |
32 | 65 | ||
33 | async enhancedMarkdownToHTML (markdown: string) { | 66 | private async render (name: keyof MarkdownParsers, markdown: string) { |
34 | if (!markdown) return '' | 67 | if (!markdown) return '' |
35 | 68 | ||
36 | if (!this.enhancedMarkdownIt) { | 69 | const config = this.parsersConfig[ name ] |
37 | this.enhancedMarkdownIt = await this.createMarkdownIt(MarkdownService.ENHANCED_RULES) | 70 | if (!this.markdownParsers[ name ]) { |
71 | this.markdownParsers[ name ] = await this.createMarkdownIt(config) | ||
38 | } | 72 | } |
39 | 73 | ||
40 | const html = this.enhancedMarkdownIt.render(markdown) | 74 | let html = this.markdownParsers[ name ].render(markdown) |
41 | return this.avoidTruncatedTags(html) | 75 | html = this.avoidTruncatedTags(html) |
42 | } | ||
43 | 76 | ||
44 | async completeMarkdownToHTML (markdown: string) { | 77 | console.log(html) |
45 | if (!markdown) return '' | ||
46 | 78 | ||
47 | if (!this.completeMarkdownIt) { | 79 | if (config.escape) return this.htmlRenderer.toSafeHtml(html) |
48 | this.completeMarkdownIt = await this.createMarkdownIt(MarkdownService.COMPLETE_RULES, true) | ||
49 | } | ||
50 | 80 | ||
51 | const html = this.completeMarkdownIt.render(markdown) | 81 | return html |
52 | return this.avoidTruncatedTags(html) | ||
53 | } | 82 | } |
54 | 83 | ||
55 | private async createMarkdownIt (rules: string[], html = false) { | 84 | private async createMarkdownIt (config: MarkdownConfig) { |
56 | // FIXME: import('...') returns a struct module, containing a "default" field corresponding to our sanitizeHtml function | 85 | // FIXME: import('...') returns a struct module, containing a "default" field corresponding to our sanitizeHtml function |
57 | const MarkdownItClass: typeof import ('markdown-it') = (await import('markdown-it') as any).default | 86 | const MarkdownItClass: typeof import ('markdown-it') = (await import('markdown-it') as any).default |
58 | 87 | ||
59 | const markdownIt = new MarkdownItClass('zero', { linkify: true, breaks: true, html }) | 88 | const markdownIt = new MarkdownItClass('zero', { linkify: true, breaks: true, html: config.html }) |
60 | 89 | ||
61 | for (const rule of rules) { | 90 | for (const rule of config.rules) { |
62 | markdownIt.enable(rule) | 91 | markdownIt.enable(rule) |
63 | } | 92 | } |
64 | 93 | ||