Merge pull request #2048 from raizasafeel/fix/video-embedding

fix(lesson): vimeo player rendered for private and unsanitized content
This commit is contained in:
Jannat Patel
2026-02-17 14:57:00 +05:30
committed by GitHub
2 changed files with 15 additions and 32 deletions
+13 -12
View File
@@ -162,20 +162,21 @@ export function getEditorTools() {
config: { config: {
services: { services: {
youtube: { youtube: {
regex: /(?:https?:\/\/)?(?:www\.)?(?:(?:youtu\.be\/)|(?:youtube\.com)\/(?:v\/|u\/\w\/|embed\/|watch))(?:(?:\?v=)?([^#&?=]*))?((?:[?&]\w*=\w*)*)/, regex: /^(?:https?:\/\/)?(?:www\.)?(?:(?:youtu\.be\/)|(?:youtube\.com)\/(?:v\/|u\/\w\/|embed\/|watch))(?:(?:\?v=)?([^#&?=]*))?((?:[?&]\w*=\w*)*)$/,
embedUrl: '<%= remote_id %>', embedUrl: '<%= remote_id %>',
/* 'https://www.youtube.com/embed/<%= remote_id %>?origin=https://plyr.io&amp;iv_load_policy=3&amp;modestbranding=1&amp;playsinline=1&amp;showinfo=0&amp;rel=0&amp;enablejsapi=1' */ /* 'https://www.youtube.com/embed/<%= remote_id %>?origin=https://plyr.io&amp;iv_load_policy=3&amp;modestbranding=1&amp;playsinline=1&amp;showinfo=0&amp;rel=0&amp;enablejsapi=1' */
html: `<div class="video-player" data-plyr-provider="youtube"></div>`, html: `<div class="video-player" data-plyr-provider="youtube"></div>`,
id: ([id]) => id, id: ([id]) => id,
}, },
vimeo: { vimeo: {
regex: /(?:http[s]?:\/\/)?(?:www\.)?vimeo\.com\/(\d+)/, regex: /^(?:http[s]?:\/\/)?(?:www\.)?vimeo\.com\/(\d+)(?:\/([a-zA-Z0-9]+))?(?:\?[^\s]*)?$/,
embedUrl: '<%= remote_id %>', embedUrl:
'https://player.vimeo.com/video/<%= remote_id %>',
html: `<div class="video-player" data-plyr-provider="vimeo"></div>`, html: `<div class="video-player" data-plyr-provider="vimeo"></div>`,
id: ([id]) => id, id: ([id, hash]) => (hash ? `${id}?h=${hash}` : id),
}, },
cloudflareStream: { cloudflareStream: {
regex: /https:\/\/customer-[a-z0-9]+\.cloudflarestream\.com\/([a-f0-9]{32})\/watch/, regex: /^https:\/\/customer-[a-z0-9]+\.cloudflarestream\.com\/([a-f0-9]{32})\/watch$/,
embedUrl: embedUrl:
'https://iframe.videodelivery.net/<%= remote_id %>', 'https://iframe.videodelivery.net/<%= remote_id %>',
html: `<iframe style="width:100%; height: ${ html: `<iframe style="width:100%; height: ${
@@ -183,7 +184,7 @@ export function getEditorTools() {
};" frameborder="0" allowfullscreen></iframe>`, };" frameborder="0" allowfullscreen></iframe>`,
}, },
bunnyStream: { bunnyStream: {
regex: /https:\/\/(?:iframe\.mediadelivery\.net|video\.bunnycdn\.com)\/play\/([a-zA-Z0-9]+\/[a-zA-Z0-9-]+)/, regex: /^https:\/\/(?:iframe\.mediadelivery\.net|video\.bunnycdn\.com)\/play\/([a-zA-Z0-9]+\/[a-zA-Z0-9-]+)$/,
embedUrl: embedUrl:
'https://iframe.mediadelivery.net/embed/<%= remote_id %>', 'https://iframe.mediadelivery.net/embed/<%= remote_id %>',
html: `<iframe style="width:100%; height: ${ html: `<iframe style="width:100%; height: ${
@@ -192,7 +193,7 @@ export function getEditorTools() {
}, },
codepen: true, codepen: true,
aparat: { aparat: {
regex: /(?:http[s]?:\/\/)?(?:www.)?aparat\.com\/v\/([^\/\?\&]+)\/?/, regex: /^(?:http[s]?:\/\/)?(?:www.)?aparat\.com\/v\/([^\/\?\&]+)\/?$/,
embedUrl: embedUrl:
'https://www.aparat.com/video/video/embed/videohash/<%= remote_id %>/vt/frame', 'https://www.aparat.com/video/video/embed/videohash/<%= remote_id %>/vt/frame',
html: `<iframe style="margin: 0 auto; width: 100%; height: ${ html: `<iframe style="margin: 0 auto; width: 100%; height: ${
@@ -201,7 +202,7 @@ export function getEditorTools() {
}, },
github: true, github: true,
slides: { slides: {
regex: /https:\/\/docs\.google\.com\/presentation\/d\/([A-Za-z0-9_-]+)\/pub/, regex: /^https:\/\/docs\.google\.com\/presentation\/d\/([A-Za-z0-9_-]+)\/pub$/,
embedUrl: embedUrl:
'https://docs.google.com/presentation/d/<%= remote_id %>/embed', 'https://docs.google.com/presentation/d/<%= remote_id %>/embed',
html: `<iframe style='width: 100%; height: ${ html: `<iframe style='width: 100%; height: ${
@@ -209,7 +210,7 @@ export function getEditorTools() {
}; border: 1px solid #D3D3D3; border-radius: 12px; margin: 1rem 0' frameborder='0' allowfullscreen='true'></iframe>`, }; border: 1px solid #D3D3D3; border-radius: 12px; margin: 1rem 0' frameborder='0' allowfullscreen='true'></iframe>`,
}, },
drive: { drive: {
regex: /https:\/\/drive\.google\.com\/file\/d\/([A-Za-z0-9_-]+)\/view(\?.+)?/, regex: /^https:\/\/drive\.google\.com\/file\/d\/([A-Za-z0-9_-]+)\/view(\?.+)?$/,
embedUrl: embedUrl:
'https://drive.google.com/file/d/<%= remote_id %>/preview', 'https://drive.google.com/file/d/<%= remote_id %>/preview',
html: `<iframe style='width: 100%; height: ${ html: `<iframe style='width: 100%; height: ${
@@ -217,19 +218,19 @@ export function getEditorTools() {
}; border: 1px solid #D3D3D3; border-radius: 12px;' frameborder='0' allowfullscreen='true'></iframe>`, }; border: 1px solid #D3D3D3; border-radius: 12px;' frameborder='0' allowfullscreen='true'></iframe>`,
}, },
docsPublic: { docsPublic: {
regex: /https:\/\/docs\.google\.com\/document\/d\/([A-Za-z0-9_-]+)\/edit(\?.+)?/, regex: /^https:\/\/docs\.google\.com\/document\/d\/([A-Za-z0-9_-]+)\/edit(\?.+)?$/,
embedUrl: embedUrl:
'https://docs.google.com/document/d/<%= remote_id %>/preview', 'https://docs.google.com/document/d/<%= remote_id %>/preview',
html: "<iframe style='width: 100%; height: 40rem; border: 1px solid #D3D3D3; border-radius: 12px;' frameborder='0' allowfullscreen='true'></iframe>", html: "<iframe style='width: 100%; height: 40rem; border: 1px solid #D3D3D3; border-radius: 12px;' frameborder='0' allowfullscreen='true'></iframe>",
}, },
sheetsPublic: { sheetsPublic: {
regex: /https:\/\/docs\.google\.com\/spreadsheets\/d\/([A-Za-z0-9_-]+)\/edit(\?.+)?/, regex: /^https:\/\/docs\.google\.com\/spreadsheets\/d\/([A-Za-z0-9_-]+)\/edit(\?.+)?$/,
embedUrl: embedUrl:
'https://docs.google.com/spreadsheets/d/<%= remote_id %>/preview', 'https://docs.google.com/spreadsheets/d/<%= remote_id %>/preview',
html: "<iframe style='width: 100%; height: 40rem; border: 1px solid #D3D3D3; border-radius: 12px;' frameborder='0' allowfullscreen='true'></iframe>", html: "<iframe style='width: 100%; height: 40rem; border: 1px solid #D3D3D3; border-radius: 12px;' frameborder='0' allowfullscreen='true'></iframe>",
}, },
slidesPublic: { slidesPublic: {
regex: /https:\/\/docs\.google\.com\/presentation\/d\/([A-Za-z0-9_-]+)\/edit(\?.+)?/, regex: /^https:\/\/docs\.google\.com\/presentation\/d\/([A-Za-z0-9_-]+)\/edit(\?.+)?$/,
embedUrl: embedUrl:
'https://docs.google.com/presentation/d/<%= remote_id %>/embed', 'https://docs.google.com/presentation/d/<%= remote_id %>/embed',
html: "<iframe style='width: 100%; height: 30rem; border: 1px solid #D3D3D3; border-radius: 12px; margin: 1rem 0;' frameborder='0' allowfullscreen='true'></iframe>", html: "<iframe style='width: 100%; height: 30rem; border: 1px solid #D3D3D3; border-radius: 12px; margin: 1rem 0;' frameborder='0' allowfullscreen='true'></iframe>",
+2 -20
View File
@@ -1,5 +1,6 @@
import { CodeXml } from 'lucide-vue-next' import { CodeXml } from 'lucide-vue-next'
import { createApp, h } from 'vue' import { createApp, h } from 'vue'
import { escapeHTML } from '@/utils'
export class Markdown { export class Markdown {
constructor({ data, api, readOnly, config }) { constructor({ data, api, readOnly, config }) {
@@ -301,7 +302,7 @@ export class Markdown {
_parseInlineMarkdown(text) { _parseInlineMarkdown(text) {
if (!text) return '' if (!text) return ''
let html = this._escapeHtml(text) let html = escapeHTML(text)
html = html.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>') html = html.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>')
@@ -316,15 +317,6 @@ export class Markdown {
return html return html
} }
_escapeHtml(text) {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
}
_togglePlaceholder() { _togglePlaceholder() {
const blocks = document.querySelectorAll( const blocks = document.querySelectorAll(
'.cdx-block.ce-paragraph[data-placeholder]' '.cdx-block.ce-paragraph[data-placeholder]'
@@ -429,16 +421,6 @@ export class Markdown {
return { alt: '', url: '' } return { alt: '', url: '' }
} }
_isLink(text) {
return /\[.+?\]\(.+?\)/.test(text)
}
_extractLink(text) {
const match = text.match(/\[(.+?)\]\((.+?)\)/)
if (match) return { text: match[1], url: match[2] }
return { text: '', url: '' }
}
_isEmbed(text) { _isEmbed(text) {
return /^https?:\/\/.+/.test(text.trim()) return /^https?:\/\/.+/.test(text.trim())
} }