From 2539932e16129992a2c0889b4ff527c265a8e2c7 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 27 May 2021 15:59:55 +0200 Subject: Instance homepage support (#4007) * Prepare homepage parsers * Add ability to update instance hompage * Add ability to set homepage as landing page * Add homepage preview in admin * Dynamically update left menu for homepage * Inject home content in homepage * Add videos list and channel miniature custom markup * Remove unused elements in markup service --- .../src/app/core/renderer/html-renderer.service.ts | 10 ++-- client/src/app/core/renderer/markdown.service.ts | 53 +++++++++++++++------- 2 files changed, 43 insertions(+), 20 deletions(-) (limited to 'client/src/app/core/renderer') diff --git a/client/src/app/core/renderer/html-renderer.service.ts b/client/src/app/core/renderer/html-renderer.service.ts index 3176cf6a4..418d8603e 100644 --- a/client/src/app/core/renderer/html-renderer.service.ts +++ b/client/src/app/core/renderer/html-renderer.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core' import { LinkifierService } from './linkifier.service' -import { SANITIZE_OPTIONS } from '@shared/core-utils/renderer/html' +import { getCustomMarkupSanitizeOptions, getSanitizeOptions } from '@shared/core-utils/renderer/html' @Injectable() export class HtmlRendererService { @@ -20,7 +20,7 @@ export class HtmlRendererService { }) } - async toSafeHtml (text: string) { + async toSafeHtml (text: string, additionalAllowedTags: string[] = []) { const [ html ] = await Promise.all([ // Convert possible markdown to html this.linkifier.linkify(text), @@ -28,7 +28,11 @@ export class HtmlRendererService { this.loadSanitizeHtml() ]) - return this.sanitizeHtml(html, SANITIZE_OPTIONS) + const options = additionalAllowedTags.length !== 0 + ? getCustomMarkupSanitizeOptions(additionalAllowedTags) + : getSanitizeOptions() + + return this.sanitizeHtml(html, options) } private async loadSanitizeHtml () { diff --git a/client/src/app/core/renderer/markdown.service.ts b/client/src/app/core/renderer/markdown.service.ts index edddb0a66..ca1bf4eb9 100644 --- a/client/src/app/core/renderer/markdown.service.ts +++ b/client/src/app/core/renderer/markdown.service.ts @@ -17,12 +17,15 @@ type MarkdownParsers = { enhancedMarkdownIt: MarkdownIt enhancedWithHTMLMarkdownIt: MarkdownIt - completeMarkdownIt: MarkdownIt + unsafeMarkdownIt: MarkdownIt + + customPageMarkdownIt: MarkdownIt } type MarkdownConfig = { rules: string[] html: boolean + breaks: boolean escape?: boolean } @@ -35,18 +38,24 @@ export class MarkdownService { private markdownParsers: MarkdownParsers = { textMarkdownIt: null, textWithHTMLMarkdownIt: null, + enhancedMarkdownIt: null, enhancedWithHTMLMarkdownIt: null, - completeMarkdownIt: null + + unsafeMarkdownIt: null, + + customPageMarkdownIt: null } private parsersConfig: MarkdownParserConfigs = { - textMarkdownIt: { rules: TEXT_RULES, html: false }, - textWithHTMLMarkdownIt: { rules: TEXT_WITH_HTML_RULES, html: true, escape: true }, + textMarkdownIt: { rules: TEXT_RULES, breaks: true, html: false }, + textWithHTMLMarkdownIt: { rules: TEXT_WITH_HTML_RULES, breaks: true, html: true, escape: true }, - enhancedMarkdownIt: { rules: ENHANCED_RULES, html: false }, - enhancedWithHTMLMarkdownIt: { rules: ENHANCED_WITH_HTML_RULES, html: true, escape: true }, + enhancedMarkdownIt: { rules: ENHANCED_RULES, breaks: true, html: false }, + enhancedWithHTMLMarkdownIt: { rules: ENHANCED_WITH_HTML_RULES, breaks: true, html: true, escape: true }, - completeMarkdownIt: { rules: COMPLETE_RULES, html: true } + unsafeMarkdownIt: { rules: COMPLETE_RULES, breaks: true, html: true, escape: false }, + + customPageMarkdownIt: { rules: COMPLETE_RULES, breaks: false, html: true, escape: true } } private emojiModule: any @@ -54,22 +63,26 @@ export class MarkdownService { constructor (private htmlRenderer: HtmlRendererService) {} textMarkdownToHTML (markdown: string, withHtml = false, withEmoji = false) { - if (withHtml) return this.render('textWithHTMLMarkdownIt', markdown, withEmoji) + if (withHtml) return this.render({ name: 'textWithHTMLMarkdownIt', markdown, withEmoji }) - return this.render('textMarkdownIt', markdown, withEmoji) + return this.render({ name: 'textMarkdownIt', markdown, withEmoji }) } enhancedMarkdownToHTML (markdown: string, withHtml = false, withEmoji = false) { - if (withHtml) return this.render('enhancedWithHTMLMarkdownIt', markdown, withEmoji) + if (withHtml) return this.render({ name: 'enhancedWithHTMLMarkdownIt', markdown, withEmoji }) + + return this.render({ name: 'enhancedMarkdownIt', markdown, withEmoji }) + } - return this.render('enhancedMarkdownIt', markdown, withEmoji) + unsafeMarkdownToHTML (markdown: string, _trustedInput: true) { + return this.render({ name: 'unsafeMarkdownIt', markdown, withEmoji: true }) } - completeMarkdownToHTML (markdown: string) { - return this.render('completeMarkdownIt', markdown, true) + customPageMarkdownToHTML (markdown: string, additionalAllowedTags: string[]) { + return this.render({ name: 'customPageMarkdownIt', markdown, withEmoji: true, additionalAllowedTags }) } - async processVideoTimestamps (html: string) { + processVideoTimestamps (html: string) { return html.replace(/((\d{1,2}):)?(\d{1,2}):(\d{1,2})/g, function (str, _, h, m, s) { const t = (3600 * +(h || 0)) + (60 * +(m || 0)) + (+(s || 0)) const url = buildVideoLink({ startTime: t }) @@ -77,7 +90,13 @@ export class MarkdownService { }) } - private async render (name: keyof MarkdownParsers, markdown: string, withEmoji = false) { + private async render (options: { + name: keyof MarkdownParsers + markdown: string + withEmoji: boolean + additionalAllowedTags?: string[] + }) { + const { name, markdown, withEmoji, additionalAllowedTags } = options if (!markdown) return '' const config = this.parsersConfig[ name ] @@ -96,7 +115,7 @@ export class MarkdownService { let html = this.markdownParsers[ name ].render(markdown) html = this.avoidTruncatedTags(html) - if (config.escape) return this.htmlRenderer.toSafeHtml(html) + if (config.escape) return this.htmlRenderer.toSafeHtml(html, additionalAllowedTags) return html } @@ -105,7 +124,7 @@ export class MarkdownService { // FIXME: import('...') returns a struct module, containing a "default" field const MarkdownItClass: typeof import ('markdown-it') = (await import('markdown-it') as any).default - const markdownIt = new MarkdownItClass('zero', { linkify: true, breaks: true, html: config.html }) + const markdownIt = new MarkdownItClass('zero', { linkify: true, breaks: config.breaks, html: config.html }) for (const rule of config.rules) { markdownIt.enable(rule) -- cgit v1.2.3