summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGiteabot <teabot@gitea.io>2024-06-22 05:03:31 +0800
committerGitHub <noreply@github.com>2024-06-22 00:03:31 +0300
commitbe302f302596a83ff6bd610a5177def245f96fde (patch)
treea22829a3c37fc87ec3a475e3eef6133ecae8abc4
parenta3529d662f1fd90d59314f518ad225f87f913972 (diff)
downloadgitea-be302f302596a83ff6bd610a5177def245f96fde.tar.gz
gitea-be302f302596a83ff6bd610a5177def245f96fde.zip
Support relative paths to videos from Wiki pages (#31061) (#31453)
Backport #31061 by @sergeyvfx This change fixes cases when a Wiki page refers to a video stored in the Wiki repository using relative path. It follows the similar case which has been already implemented for images. Test plan: - Create repository and Wiki page - Clone the Wiki repository - Add video to it, say `video.mp4` - Modify the markdown file to refer to the video using `<video src="video.mp4">` - Commit the Wiki page - Observe that the video is properly displayed Co-authored-by: Sergey Sharybin <sergey.vfx@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
-rw-r--r--modules/markup/html.go53
-rw-r--r--modules/markup/html_node.go62
-rw-r--r--modules/markup/html_test.go11
3 files changed, 83 insertions, 43 deletions
diff --git a/modules/markup/html.go b/modules/markup/html.go
index 1eedf095a0..d0498074e1 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -88,6 +88,10 @@ func IsFullURLString(link string) bool {
return fullURLPattern.MatchString(link)
}
+func IsNonEmptyRelativePath(link string) bool {
+ return link != "" && !IsFullURLString(link) && link[0] != '/' && link[0] != '?' && link[0] != '#'
+}
+
// regexp for full links to issues/pulls
var issueFullPattern *regexp.Regexp
@@ -372,41 +376,6 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
return nil
}
-func handleNodeImg(ctx *RenderContext, img *html.Node) {
- for i, attr := range img.Attr {
- if attr.Key != "src" {
- continue
- }
-
- if attr.Val != "" && !IsFullURLString(attr.Val) && !strings.HasPrefix(attr.Val, "/") {
- attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
-
- // By default, the "<img>" tag should also be clickable,
- // because frontend use `<img>` to paste the re-scaled image into the markdown,
- // so it must match the default markdown image behavior.
- hasParentAnchor := false
- for p := img.Parent; p != nil; p = p.Parent {
- if hasParentAnchor = p.Type == html.ElementNode && p.Data == "a"; hasParentAnchor {
- break
- }
- }
- if !hasParentAnchor {
- imgA := &html.Node{Type: html.ElementNode, Data: "a", Attr: []html.Attribute{
- {Key: "href", Val: attr.Val},
- {Key: "target", Val: "_blank"},
- }}
- parent := img.Parent
- imgNext := img.NextSibling
- parent.RemoveChild(img)
- parent.InsertBefore(imgA, imgNext)
- imgA.AppendChild(img)
- }
- }
- attr.Val = camoHandleLink(attr.Val)
- img.Attr[i] = attr
- }
-}
-
func visitNode(ctx *RenderContext, procs []processor, node *html.Node) *html.Node {
// Add user-content- to IDs and "#" links if they don't already have them
for idx, attr := range node.Attr {
@@ -426,20 +395,20 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node) *html.Nod
}
}
- // We ignore code and pre.
switch node.Type {
case html.TextNode:
textNode(ctx, procs, node)
case html.ElementNode:
- if node.Data == "img" {
- next := node.NextSibling
- handleNodeImg(ctx, node)
- return next
+ if node.Data == "code" || node.Data == "pre" {
+ // ignore code and pre nodes
+ return node.NextSibling
+ } else if node.Data == "img" {
+ return visitNodeImg(ctx, node)
+ } else if node.Data == "video" {
+ return visitNodeVideo(ctx, node)
} else if node.Data == "a" {
// Restrict text in links to emojis
procs = emojiProcessors
- } else if node.Data == "code" || node.Data == "pre" {
- return node.NextSibling
} else if node.Data == "i" {
for _, attr := range node.Attr {
if attr.Key != "class" {
diff --git a/modules/markup/html_node.go b/modules/markup/html_node.go
new file mode 100644
index 0000000000..6d784975b9
--- /dev/null
+++ b/modules/markup/html_node.go
@@ -0,0 +1,62 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package markup
+
+import (
+ "code.gitea.io/gitea/modules/util"
+
+ "golang.org/x/net/html"
+)
+
+func visitNodeImg(ctx *RenderContext, img *html.Node) (next *html.Node) {
+ next = img.NextSibling
+ for i, attr := range img.Attr {
+ if attr.Key != "src" {
+ continue
+ }
+
+ if IsNonEmptyRelativePath(attr.Val) {
+ attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
+
+ // By default, the "<img>" tag should also be clickable,
+ // because frontend use `<img>` to paste the re-scaled image into the markdown,
+ // so it must match the default markdown image behavior.
+ hasParentAnchor := false
+ for p := img.Parent; p != nil; p = p.Parent {
+ if hasParentAnchor = p.Type == html.ElementNode && p.Data == "a"; hasParentAnchor {
+ break
+ }
+ }
+ if !hasParentAnchor {
+ imgA := &html.Node{Type: html.ElementNode, Data: "a", Attr: []html.Attribute{
+ {Key: "href", Val: attr.Val},
+ {Key: "target", Val: "_blank"},
+ }}
+ parent := img.Parent
+ imgNext := img.NextSibling
+ parent.RemoveChild(img)
+ parent.InsertBefore(imgA, imgNext)
+ imgA.AppendChild(img)
+ }
+ }
+ attr.Val = camoHandleLink(attr.Val)
+ img.Attr[i] = attr
+ }
+ return next
+}
+
+func visitNodeVideo(ctx *RenderContext, node *html.Node) (next *html.Node) {
+ next = node.NextSibling
+ for i, attr := range node.Attr {
+ if attr.Key != "src" {
+ continue
+ }
+ if IsNonEmptyRelativePath(attr.Val) {
+ attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
+ }
+ attr.Val = camoHandleLink(attr.Val)
+ node.Attr[i] = attr
+ }
+ return next
+}
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index 64cc30d246..8911bf3f2e 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -520,7 +520,7 @@ func TestRender_ShortLinks(t *testing.T) {
`<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`)
}
-func TestRender_RelativeImages(t *testing.T) {
+func TestRender_RelativeMedias(t *testing.T) {
render := func(input string, isWiki bool, links markup.Links) string {
buffer, err := markdown.RenderString(&markup.RenderContext{
Ctx: git.DefaultContext,
@@ -546,6 +546,15 @@ func TestRender_RelativeImages(t *testing.T) {
out = render(`<img src="/LINK">`, true, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"})
assert.Equal(t, `<img src="/LINK"/>`, out)
+
+ out = render(`<video src="LINK">`, false, markup.Links{Base: "/test-owner/test-repo"})
+ assert.Equal(t, `<video src="/test-owner/test-repo/LINK"></video>`, out)
+
+ out = render(`<video src="LINK">`, true, markup.Links{Base: "/test-owner/test-repo"})
+ assert.Equal(t, `<video src="/test-owner/test-repo/wiki/raw/LINK"></video>`, out)
+
+ out = render(`<video src="/LINK">`, false, markup.Links{Base: "/test-owner/test-repo"})
+ assert.Equal(t, `<video src="/LINK"></video>`, out)
}
func Test_ParseClusterFuzz(t *testing.T) {