From 637451a45ecbc3d127ff2adf27437ce1357493ea Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Mon, 15 Jan 2024 09:49:24 +0100 Subject: [PATCH] Rework markup link rendering (#26745) Fixes #26548 This PR refactors the rendering of markup links. The old code uses `strings.Replace` to change some urls while the new code uses more context to decide which link should be generated. The added tests should ensure the same output for the old and new behaviour (besides the bug). We may need to refactor the rendering a bit more to make it clear how the different helper methods render the input string. There are lots of options (resolve links / images / mentions / git hashes / emojis / ...) but you don't really know what helper uses which options. For example, we currently support images in the user description which should not be allowed I think:
Profile https://try.gitea.io/KN4CK3R ![grafik](https://github.com/go-gitea/gitea/assets/1666336/109ae422-496d-4200-b52e-b3a528f553e5)
--------- Co-authored-by: wxiaoguang --- models/issues/comment_code.go | 8 +- models/repo/repo.go | 3 +- modules/markup/external/external.go | 13 +- modules/markup/html.go | 57 +-- modules/markup/html_internal_test.go | 30 +- modules/markup/html_test.go | 117 ++++--- modules/markup/markdown/goldmark.go | 30 +- modules/markup/markdown/markdown.go | 6 - modules/markup/markdown/markdown_test.go | 422 +++++++++++++++++++++-- modules/markup/orgmode/orgmode.go | 58 ++-- modules/markup/orgmode/orgmode_test.go | 15 +- modules/markup/renderer.go | 42 ++- modules/templates/helper.go | 1 - modules/templates/util_render.go | 53 +-- modules/templates/util_render_test.go | 141 +++++++- routers/common/markup.go | 12 +- routers/web/feed/convert.go | 19 +- routers/web/feed/profile.go | 6 +- routers/web/org/home.go | 23 +- routers/web/repo/commit.go | 18 +- routers/web/repo/issue.go | 51 +-- routers/web/repo/milestone.go | 20 +- routers/web/repo/projects.go | 20 +- routers/web/repo/release.go | 20 +- routers/web/repo/render.go | 15 +- routers/web/repo/view.go | 58 ++-- routers/web/repo/wiki.go | 10 +- routers/web/shared/user/header.go | 6 +- routers/web/user/home.go | 8 +- routers/web/user/profile.go | 24 +- services/mailer/mail.go | 8 +- services/mailer/mail_release.go | 8 +- templates/repo/branch/list.tmpl | 4 +- templates/repo/commit_page.tmpl | 8 +- templates/repo/commits_list.tmpl | 4 +- templates/repo/commits_list_small.tmpl | 4 +- templates/repo/diff/compare.tmpl | 2 +- templates/repo/graph/commits.tmpl | 2 +- templates/repo/issue/view_title.tmpl | 2 +- templates/repo/view_list.tmpl | 6 +- templates/user/dashboard/feeds.tmpl | 2 +- tests/fuzz/fuzz_test.go | 6 +- 42 files changed, 967 insertions(+), 395 deletions(-) diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go index 25e606f09..384a595dd 100644 --- a/models/issues/comment_code.go +++ b/models/issues/comment_code.go @@ -109,9 +109,11 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu var err error if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: issue.Repo.Link(), - Metas: issue.Repo.ComposeMetas(ctx), + Ctx: ctx, + Links: markup.Links{ + Base: issue.Repo.Link(), + }, + Metas: issue.Repo.ComposeMetas(ctx), }, comment.Content); err != nil { return nil, err } diff --git a/models/repo/repo.go b/models/repo/repo.go index fb1849a4b..3695e1f78 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -584,8 +584,7 @@ func (repo *Repository) CanEnableEditor() bool { // DescriptionHTML does special handles to description and return HTML string. func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML { desc, err := markup.RenderDescriptionHTML(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: repo.HTMLURL(), + Ctx: ctx, // Don't use Metas to speedup requests }, repo.Description) if err != nil { diff --git a/modules/markup/external/external.go b/modules/markup/external/external.go index ffbb6da4d..122517ed1 100644 --- a/modules/markup/external/external.go +++ b/modules/markup/external/external.go @@ -79,9 +79,10 @@ func envMark(envName string) string { // Render renders the data of the document to HTML via the external tool. func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { var ( - urlRawPrefix = strings.Replace(ctx.URLPrefix, "/src/", "/raw/", 1) - command = strings.NewReplacer(envMark("GITEA_PREFIX_SRC"), ctx.URLPrefix, - envMark("GITEA_PREFIX_RAW"), urlRawPrefix).Replace(p.Command) + command = strings.NewReplacer( + envMark("GITEA_PREFIX_SRC"), ctx.Links.SrcLink(), + envMark("GITEA_PREFIX_RAW"), ctx.Links.RawLink(), + ).Replace(p.Command) commands = strings.Fields(command) args = commands[1:] ) @@ -121,14 +122,14 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io. ctx.Ctx = graceful.GetManager().ShutdownContext() } - processCtx, _, finished := process.GetManager().AddContext(ctx.Ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.URLPrefix)) + processCtx, _, finished := process.GetManager().AddContext(ctx.Ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.Links.SrcLink())) defer finished() cmd := exec.CommandContext(processCtx, commands[0], args...) cmd.Env = append( os.Environ(), - "GITEA_PREFIX_SRC="+ctx.URLPrefix, - "GITEA_PREFIX_RAW="+urlRawPrefix, + "GITEA_PREFIX_SRC="+ctx.Links.SrcLink(), + "GITEA_PREFIX_RAW="+ctx.Links.RawLink(), ) if !p.IsInputFile { cmd.Stdin = input diff --git a/modules/markup/html.go b/modules/markup/html.go index 05b1c3ef7..a64e4c565 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -80,15 +80,10 @@ const keywordClass = "issue-keyword" // IsLink reports whether link fits valid format. func IsLink(link []byte) bool { - return isLink(link) -} - -// isLink reports whether link fits valid format. -func isLink(link []byte) bool { return validLinksPattern.Match(link) } -func isLinkStr(link string) bool { +func IsLinkStr(link string) bool { return validLinksPattern.MatchString(link) } @@ -344,7 +339,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output node = node.FirstChild } - visitNode(ctx, procs, procs, node) + visitNode(ctx, procs, node) newNodes := make([]*html.Node, 0, 5) @@ -375,7 +370,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output return nil } -func visitNode(ctx *RenderContext, procs, textProcs []processor, node *html.Node) { +func visitNode(ctx *RenderContext, procs []processor, node *html.Node) { // Add user-content- to IDs and "#" links if they don't already have them for idx, attr := range node.Attr { val := strings.TrimPrefix(attr.Val, "#") @@ -390,35 +385,29 @@ func visitNode(ctx *RenderContext, procs, textProcs []processor, node *html.Node } if attr.Key == "class" && attr.Val == "emoji" { - textProcs = nil + procs = nil } } // We ignore code and pre. switch node.Type { case html.TextNode: - textNode(ctx, textProcs, node) + textNode(ctx, procs, node) case html.ElementNode: if node.Data == "img" { for i, attr := range node.Attr { if attr.Key != "src" { continue } - if len(attr.Val) > 0 && !isLinkStr(attr.Val) && !strings.HasPrefix(attr.Val, "data:image/") { - prefix := ctx.URLPrefix - if ctx.IsWiki { - prefix = util.URLJoin(prefix, "wiki", "raw") - } - prefix = strings.Replace(prefix, "/src/", "/media/", 1) - - attr.Val = util.URLJoin(prefix, attr.Val) + if len(attr.Val) > 0 && !IsLinkStr(attr.Val) && !strings.HasPrefix(attr.Val, "data:image/") { + attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val) } attr.Val = camoHandleLink(attr.Val) node.Attr[i] = attr } } else if node.Data == "a" { // Restrict text in links to emojis - textProcs = emojiProcessors + procs = emojiProcessors } else if node.Data == "code" || node.Data == "pre" { return } else if node.Data == "i" { @@ -444,7 +433,7 @@ func visitNode(ctx *RenderContext, procs, textProcs []processor, node *html.Node } } for n := node.FirstChild; n != nil; n = n.NextSibling { - visitNode(ctx, procs, textProcs, n) + visitNode(ctx, procs, n) } } // ignore everything else @@ -641,10 +630,6 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) { } func shortLinkProcessor(ctx *RenderContext, node *html.Node) { - shortLinkProcessorFull(ctx, node, false) -} - -func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) { next := node.NextSibling for node != nil && node != next { m := shortLinkPattern.FindStringSubmatchIndex(node.Data) @@ -665,7 +650,7 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) { if equalPos := strings.IndexByte(v, '='); equalPos == -1 { // There is no equal in this argument; this is a mandatory arg if props["name"] == "" { - if isLinkStr(v) { + if IsLinkStr(v) { // If we clearly see it is a link, we save it so // But first we need to ensure, that if both mandatory args provided @@ -740,7 +725,7 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) { DataAtom: atom.A, } childNode.Parent = linkNode - absoluteLink := isLinkStr(link) + absoluteLink := IsLinkStr(link) if !absoluteLink { if image { link = strings.ReplaceAll(link, " ", "+") @@ -751,16 +736,9 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) { link = url.PathEscape(link) } } - urlPrefix := ctx.URLPrefix if image { if !absoluteLink { - if IsSameDomain(urlPrefix) { - urlPrefix = strings.Replace(urlPrefix, "/src/", "/raw/", 1) - } - if ctx.IsWiki { - link = util.URLJoin("wiki", "raw", link) - } - link = util.URLJoin(urlPrefix, link) + link = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), link) } title := props["title"] if title == "" { @@ -789,18 +767,15 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) { } else { if !absoluteLink { if ctx.IsWiki { - link = util.URLJoin("wiki", link) + link = util.URLJoin(ctx.Links.WikiLink(), link) + } else { + link = util.URLJoin(ctx.Links.SrcLink(), link) } - link = util.URLJoin(urlPrefix, link) } childNode.Type = html.TextNode childNode.Data = name } - if noLink { - linkNode = childNode - } else { - linkNode.Attr = []html.Attribute{{Key: "href", Val: link}} - } + linkNode.Attr = []html.Attribute{{Key: "href", Val: link}} replaceContent(node, m[0], m[1], linkNode) node = node.NextSibling.NextSibling } diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index 7b7f6df70..5ba956191 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -287,8 +287,8 @@ func TestRender_IssueIndexPattern_Document(t *testing.T) { } func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) { - if ctx.URLPrefix == "" { - ctx.URLPrefix = TestAppURL + if ctx.Links.Base == "" { + ctx.Links.Base = TestRepoURL } var buf strings.Builder @@ -303,19 +303,23 @@ func TestRender_AutoLink(t *testing.T) { test := func(input, expected string) { var buffer strings.Builder err := PostProcess(&RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: TestRepoURL, - Metas: localMetas, + Ctx: git.DefaultContext, + Links: Links{ + Base: TestRepoURL, + }, + Metas: localMetas, }, strings.NewReader(input), &buffer) assert.Equal(t, err, nil) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String())) buffer.Reset() err = PostProcess(&RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: TestRepoURL, - Metas: localMetas, - IsWiki: true, + Ctx: git.DefaultContext, + Links: Links{ + Base: TestRepoURL, + }, + Metas: localMetas, + IsWiki: true, }, strings.NewReader(input), &buffer) assert.Equal(t, err, nil) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String())) @@ -342,9 +346,11 @@ func TestRender_FullIssueURLs(t *testing.T) { test := func(input, expected string) { var result strings.Builder err := postProcess(&RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: TestRepoURL, - Metas: localMetas, + Ctx: git.DefaultContext, + Links: Links{ + Base: TestRepoURL, + }, + Metas: localMetas, }, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result) assert.NoError(t, err) assert.Equal(t, expected, result.String()) diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 62fd0f5a8..89ecfc036 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -42,8 +42,10 @@ func TestRender_Commits(t *testing.T) { buffer, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, RelativePath: ".md", - URLPrefix: markup.TestRepoURL, - Metas: localMetas, + Links: markup.Links{ + Base: markup.TestRepoURL, + }, + Metas: localMetas, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) @@ -93,8 +95,10 @@ func TestRender_CrossReferences(t *testing.T) { buffer, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", - URLPrefix: setting.AppSubURL, - Metas: localMetas, + Links: markup.Links{ + Base: setting.AppSubURL, + }, + Metas: localMetas, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) @@ -138,7 +142,9 @@ func TestRender_links(t *testing.T) { buffer, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", - URLPrefix: markup.TestRepoURL, + Links: markup.Links{ + Base: markup.TestRepoURL, + }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) @@ -238,7 +244,9 @@ func TestRender_email(t *testing.T) { res, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", - URLPrefix: markup.TestRepoURL, + Links: markup.Links{ + Base: markup.TestRepoURL, + }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res)) @@ -309,7 +317,9 @@ func TestRender_emoji(t *testing.T) { buffer, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", - URLPrefix: markup.TestRepoURL, + Links: markup.Links{ + Base: markup.TestRepoURL, + }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) @@ -371,29 +381,34 @@ func TestRender_ShortLinks(t *testing.T) { test := func(input, expected, expectedWiki string) { buffer, err := markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: tree, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: markup.TestRepoURL, + BranchPath: "master", + }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) buffer, err = markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: markup.TestRepoURL, - Metas: localMetas, - IsWiki: true, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: markup.TestRepoURL, + }, + Metas: localMetas, + IsWiki: true, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer)) } - rawtree := util.URLJoin(markup.TestRepoURL, "raw", "master") + mediatree := util.URLJoin(markup.TestRepoURL, "media", "master") url := util.URLJoin(tree, "Link") otherURL := util.URLJoin(tree, "Other-Link") encodedURL := util.URLJoin(tree, "Link%3F") - imgurl := util.URLJoin(rawtree, "Link.jpg") - otherImgurl := util.URLJoin(rawtree, "Link+Other.jpg") - encodedImgurl := util.URLJoin(rawtree, "Link+%23.jpg") - notencodedImgurl := util.URLJoin(rawtree, "some", "path", "Link+#.jpg") + imgurl := util.URLJoin(mediatree, "Link.jpg") + otherImgurl := util.URLJoin(mediatree, "Link+Other.jpg") + encodedImgurl := util.URLJoin(mediatree, "Link+%23.jpg") + notencodedImgurl := util.URLJoin(mediatree, "some", "path", "Link+#.jpg") urlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link") otherURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Other-Link") encodedURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link%3F") @@ -475,21 +490,25 @@ func TestRender_ShortLinks(t *testing.T) { func TestRender_RelativeImages(t *testing.T) { setting.AppURL = markup.TestAppURL - tree := util.URLJoin(markup.TestRepoURL, "src", "master") test := func(input, expected, expectedWiki string) { buffer, err := markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: tree, - Metas: localMetas, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: markup.TestRepoURL, + BranchPath: "master", + }, + Metas: localMetas, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) buffer, err = markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: markup.TestRepoURL, - Metas: localMetas, - IsWiki: true, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: markup.TestRepoURL, + }, + Metas: localMetas, + IsWiki: true, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer)) @@ -521,9 +540,11 @@ func Test_ParseClusterFuzz(t *testing.T) { var res strings.Builder err := markup.PostProcess(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: "https://example.com", - Metas: localMetas, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: "https://example.com", + }, + Metas: localMetas, }, strings.NewReader(data), &res) assert.NoError(t, err) assert.NotContains(t, res.String(), " 0 && !markup.IsLink(link) { - prefix := pc.Get(urlPrefixKey).(string) - if pc.Get(isWikiKey).(bool) { - prefix = giteautil.URLJoin(prefix, "wiki", "raw") - } - prefix = strings.Replace(prefix, "/src/", "/media/", 1) - - lnk := strings.TrimLeft(string(link), "/") - - lnk = giteautil.URLJoin(prefix, lnk) - link = []byte(lnk) + v.Destination = []byte(giteautil.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), string(link))) } - v.Destination = link parent := n.Parent() // Create a link around image only if parent is not already a link @@ -107,7 +97,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa // Create a link wrapper wrap := ast.NewLink() - wrap.Destination = link + wrap.Destination = v.Destination wrap.Title = v.Title wrap.SetAttributeString("target", []byte("_blank")) @@ -143,11 +133,15 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa link[0] != '#' && !bytes.HasPrefix(link, byteMailto) { // special case: this is not a link, a hash link or a mailto:, so it's a // relative URL - lnk := string(link) - if pc.Get(isWikiKey).(bool) { - lnk = giteautil.URLJoin("wiki", lnk) + + var base string + if ctx.IsWiki { + base = ctx.Links.WikiLink() + } else { + base = ctx.Links.Base } - link = []byte(giteautil.URLJoin(pc.Get(urlPrefixKey).(string), lnk)) + + link = []byte(giteautil.URLJoin(base, string(link))) } if len(link) > 0 && link[0] == '#' { link = []byte("#user-content-" + string(link)[1:]) @@ -188,9 +182,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa applyElementDir(v) case *ast.Text: if v.SoftLineBreak() && !v.HardLineBreak() { - renderMetas := pc.Get(renderMetasKey).(map[string]string) - mode := renderMetas["mode"] - if mode != "document" { + if ctx.Metas["mode"] != "document" { v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInComments) } else { v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments) diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index 43885889d..771162b9a 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -34,9 +34,6 @@ var ( ) var ( - urlPrefixKey = parser.NewContextKey() - isWikiKey = parser.NewContextKey() - renderMetasKey = parser.NewContextKey() renderContextKey = parser.NewContextKey() renderConfigKey = parser.NewContextKey() ) @@ -66,9 +63,6 @@ func (l *limitWriter) Write(data []byte) (int, error) { // newParserContext creates a parser.Context with the render context set func newParserContext(ctx *markup.RenderContext) parser.Context { pc := parser.NewContext(parser.WithIDs(newPrefixedIDs())) - pc.Set(urlPrefixKey, ctx.URLPrefix) - pc.Set(isWikiKey, ctx.IsWiki) - pc.Set(renderMetasKey, ctx.Metas) pc.Set(renderContextKey, ctx) return pc } diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index 8f855f1b1..957d773ac 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -52,16 +52,20 @@ func TestRender_StandardLinks(t *testing.T) { test := func(input, expected, expectedWiki string) { buffer, err := markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: setting.AppSubURL, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: setting.AppSubURL, + }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) buffer, err = markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: setting.AppSubURL, - IsWiki: true, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: setting.AppSubURL, + }, + IsWiki: true, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer)) @@ -83,8 +87,10 @@ func TestRender_Images(t *testing.T) { test := func(input, expected string) { buffer, err := markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: setting.AppSubURL, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: setting.AppSubURL, + }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) @@ -107,7 +113,6 @@ func TestRender_Images(t *testing.T) { "[!["+title+"]("+url+")]("+href+")", `

`+title+`

`) - url = "/../../.images/src/02/train.jpg" test( "!["+title+"]("+url+")", `

`+title+`

`) @@ -286,14 +291,16 @@ func TestTotal_RenderWiki(t *testing.T) { setting.AppURL = AppURL setting.AppSubURL = AppSubURL - answers := testAnswers(util.URLJoin(AppSubURL, "wiki/"), util.URLJoin(AppSubURL, "wiki", "raw/")) + answers := testAnswers(util.URLJoin(AppSubURL, "wiki"), util.URLJoin(AppSubURL, "wiki", "raw")) for i := 0; i < len(sameCases); i++ { line, err := markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: AppSubURL, - Metas: localMetas, - IsWiki: true, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: setting.AppSubURL, + }, + Metas: localMetas, + IsWiki: true, }, sameCases[i]) assert.NoError(t, err) assert.Equal(t, answers[i], line) @@ -314,9 +321,11 @@ func TestTotal_RenderWiki(t *testing.T) { for i := 0; i < len(testCases); i += 2 { line, err := markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: AppSubURL, - IsWiki: true, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: setting.AppSubURL, + }, + IsWiki: true, }, testCases[i]) assert.NoError(t, err) assert.Equal(t, testCases[i+1], line) @@ -327,13 +336,16 @@ func TestTotal_RenderString(t *testing.T) { setting.AppURL = AppURL setting.AppSubURL = AppSubURL - answers := testAnswers(util.URLJoin(AppSubURL, "src", "master/"), util.URLJoin(AppSubURL, "raw", "master/")) + answers := testAnswers(util.URLJoin(AppSubURL, "src", "master"), util.URLJoin(AppSubURL, "media", "master")) for i := 0; i < len(sameCases); i++ { line, err := markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: util.URLJoin(AppSubURL, "src", "master/"), - Metas: localMetas, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: AppSubURL, + BranchPath: "master", + }, + Metas: localMetas, }, sameCases[i]) assert.NoError(t, err) assert.Equal(t, answers[i], line) @@ -343,8 +355,10 @@ func TestTotal_RenderString(t *testing.T) { for i := 0; i < len(testCases); i += 2 { line, err := markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: AppSubURL, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: AppSubURL, + }, }, testCases[i]) assert.NoError(t, err) assert.Equal(t, testCases[i+1], line) @@ -556,3 +570,367 @@ foo: bar assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase) } } + +func TestRenderLinks(t *testing.T) { + input := ` space @mention-user +/just/a/path.bin +https://example.com/file.bin +[local link](file.bin) +[remote link](https://example.com) +[[local link|file.bin]] +[[remote link|https://example.com]] +![local image](image.jpg) +![remote image](https://example.com/image.jpg) +[[local image|image.jpg]] +[[remote link|https://example.com/image.jpg]] +https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash +com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare +https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb +com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit +:+1: +mail@domain.com +@mention-user test +#123 + space +` + cases := []struct { + Links markup.Links + IsWiki bool + Expected string + }{ + { // 0 + Links: markup.Links{}, + IsWiki: false, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + { // 1 + Links: markup.Links{}, + IsWiki: true, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + { // 2 + Links: markup.Links{ + Base: "https://gitea.io/", + }, + IsWiki: false, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + { // 3 + Links: markup.Links{ + Base: "https://gitea.io/", + }, + IsWiki: true, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + { // 4 + Links: markup.Links{ + Base: "/relative/path", + }, + IsWiki: false, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + { // 5 + Links: markup.Links{ + Base: "/relative/path", + }, + IsWiki: true, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + { // 6 + Links: markup.Links{ + Base: "/user/repo", + BranchPath: "branch/main", + }, + IsWiki: false, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + { // 7 + Links: markup.Links{ + Base: "/relative/path", + BranchPath: "branch/main", + }, + IsWiki: true, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + { // 8 + Links: markup.Links{ + Base: "/user/repo", + TreePath: "sub/folder", + }, + IsWiki: false, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + { // 9 + Links: markup.Links{ + Base: "/relative/path", + TreePath: "sub/folder", + }, + IsWiki: true, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + { // 10 + Links: markup.Links{ + Base: "/user/repo", + BranchPath: "branch/main", + TreePath: "sub/folder", + }, + IsWiki: false, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + { // 11 + Links: markup.Links{ + Base: "/relative/path", + BranchPath: "branch/main", + TreePath: "sub/folder", + }, + IsWiki: true, + Expected: `

space @mention-user
+/just/a/path.bin
+https://example.com/file.bin
+local link
+remote link
+local link
+remote link
+local image
+remote image
+local image
+remote link
+https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
+https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
+com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
+👍
+mail@domain.com
+@mention-user test
+#123
+space

+`, + }, + } + + for i, c := range cases { + result, err := markdown.RenderString(&markup.RenderContext{Ctx: context.Background(), Links: c.Links, IsWiki: c.IsWiki}, input) + assert.NoError(t, err, "Unexpected error in testcase: %v", i) + assert.Equal(t, c.Expected, result, "Unexpected result in testcase %v", i) + } +} diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go index e7af02b49..abc641fbe 100644 --- a/modules/markup/orgmode/orgmode.go +++ b/modules/markup/orgmode/orgmode.go @@ -4,7 +4,6 @@ package markup import ( - "bytes" "fmt" "html" "io" @@ -101,8 +100,7 @@ func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error w := &Writer{ HTMLWriter: htmlWriter, - URLPrefix: ctx.URLPrefix, - IsWiki: ctx.IsWiki, + Ctx: ctx, } htmlWriter.ExtendingWriter = w @@ -132,63 +130,53 @@ func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Wri // Writer implements org.Writer type Writer struct { *org.HTMLWriter - URLPrefix string - IsWiki bool + Ctx *markup.RenderContext } -var byteMailto = []byte("mailto:") +const mailto = "mailto:" -// WriteRegularLink renders images, links or videos -func (r *Writer) WriteRegularLink(l org.RegularLink) { - link := []byte(html.EscapeString(l.URL)) +func (r *Writer) resolveLink(l org.RegularLink) string { + link := html.EscapeString(l.URL) if l.Protocol == "file" { link = link[len("file:"):] } - if len(link) > 0 && !markup.IsLink(link) && - link[0] != '#' && !bytes.HasPrefix(link, byteMailto) { - lnk := string(link) - if r.IsWiki { - lnk = util.URLJoin("wiki", lnk) + if len(link) > 0 && !markup.IsLinkStr(link) && + link[0] != '#' && !strings.HasPrefix(link, mailto) { + base := r.Ctx.Links.Base + switch l.Kind() { + case "image", "video": + base = r.Ctx.Links.ResolveMediaLink(r.Ctx.IsWiki) } - link = []byte(util.URLJoin(r.URLPrefix, lnk)) + link = util.URLJoin(base, link) } + return link +} + +// WriteRegularLink renders images, links or videos +func (r *Writer) WriteRegularLink(l org.RegularLink) { + link := r.resolveLink(l) // Inspired by https://github.com/niklasfasching/go-org/blob/6eb20dbda93cb88c3503f7508dc78cbbc639378f/org/html_writer.go#L406-L427 switch l.Kind() { case "image": if l.Description == nil { - imageSrc := getMediaURL(link) - fmt.Fprintf(r, `%s`, imageSrc, link) + fmt.Fprintf(r, `%s`, link, link) } else { - description := strings.TrimPrefix(org.String(l.Description...), "file:") - imageSrc := getMediaURL([]byte(description)) + imageSrc := r.resolveLink(l.Description[0].(org.RegularLink)) fmt.Fprintf(r, `%s`, link, imageSrc, imageSrc) } case "video": if l.Description == nil { - imageSrc := getMediaURL(link) - fmt.Fprintf(r, ``, imageSrc, link) + fmt.Fprintf(r, ``, link, link) } else { - description := strings.TrimPrefix(org.String(l.Description...), "file:") - videoSrc := getMediaURL([]byte(description)) + videoSrc := r.resolveLink(l.Description[0].(org.RegularLink)) fmt.Fprintf(r, ``, link, videoSrc, videoSrc) } default: - description := string(link) + description := link if l.Description != nil { description = r.WriteNodesAsString(l.Description...) } fmt.Fprintf(r, `%s`, link, description) } } - -func getMediaURL(l []byte) string { - srcURL := string(l) - - // Check if link is valid - if len(srcURL) > 0 && !markup.IsLink(l) { - srcURL = strings.Replace(srcURL, "/src/", "/media/", 1) - } - - return srcURL -} diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go index 88ae14ebc..abf5ca8fc 100644 --- a/modules/markup/orgmode/orgmode_test.go +++ b/modules/markup/orgmode/orgmode_test.go @@ -27,8 +27,10 @@ func TestRender_StandardLinks(t *testing.T) { test := func(input, expected string) { buffer, err := RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: setting.AppSubURL, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: setting.AppSubURL, + }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) @@ -48,8 +50,10 @@ func TestRender_Media(t *testing.T) { test := func(input, expected string) { buffer, err := RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: setting.AppSubURL, + Ctx: git.DefaultContext, + Links: markup.Links{ + Base: setting.AppSubURL, + }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) @@ -84,8 +88,7 @@ func TestRender_Source(t *testing.T) { test := func(input, expected string) { buffer, err := RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: setting.AppSubURL, + Ctx: git.DefaultContext, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 0331c3742..5a7adcc55 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "github.com/yuin/goldmark/ast" ) @@ -69,7 +70,7 @@ type RenderContext struct { RelativePath string // relative path from tree root of the branch Type string IsWiki bool - URLPrefix string + Links Links Metas map[string]string DefaultLink string GitRepo *git.Repository @@ -80,6 +81,45 @@ type RenderContext struct { InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page } +type Links struct { + Base string + BranchPath string + TreePath string +} + +func (l *Links) HasBranchInfo() bool { + return l.BranchPath != "" +} + +func (l *Links) SrcLink() string { + return util.URLJoin(l.Base, "src", l.BranchPath, l.TreePath) +} + +func (l *Links) MediaLink() string { + return util.URLJoin(l.Base, "media", l.BranchPath, l.TreePath) +} + +func (l *Links) RawLink() string { + return util.URLJoin(l.Base, "raw", l.BranchPath, l.TreePath) +} + +func (l *Links) WikiLink() string { + return util.URLJoin(l.Base, "wiki") +} + +func (l *Links) WikiRawLink() string { + return util.URLJoin(l.Base, "wiki/raw") +} + +func (l *Links) ResolveMediaLink(isWiki bool) string { + if isWiki { + return l.WikiRawLink() + } else if l.HasBranchInfo() { + return l.MediaLink() + } + return l.Base +} + // Cancel runs any cleanup functions that have been registered for this Ctx func (ctx *RenderContext) Cancel() { if ctx == nil { diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 235fd96b7..96cdd9ca4 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -161,7 +161,6 @@ func NewFuncMap() template.FuncMap { "RenderEmoji": RenderEmoji, "RenderEmojiPlain": emoji.ReplaceAliases, "ReactionToEmoji": ReactionToEmoji, - "RenderNote": RenderNote, "RenderMarkdownToHtml": RenderMarkdownToHtml, "RenderLabel": RenderLabel, diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index 2f6132c6f..1d9635410 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -24,21 +24,13 @@ import ( ) // RenderCommitMessage renders commit message with XSS-safe and special links. -func RenderCommitMessage(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML { - return RenderCommitMessageLink(ctx, msg, urlPrefix, "", metas) -} - -// RenderCommitMessageLink renders commit message as a XXS-safe link to the provided -// default url, handling for special links. -func RenderCommitMessageLink(ctx context.Context, msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML { +func RenderCommitMessage(ctx context.Context, msg string, metas map[string]string) template.HTML { cleanMsg := template.HTMLEscapeString(msg) // we can safely assume that it will not return any error, since there // shouldn't be any special HTML. fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: urlPrefix, - DefaultLink: urlDefault, - Metas: metas, + Ctx: ctx, + Metas: metas, }, cleanMsg) if err != nil { log.Error("RenderCommitMessage: %v", err) @@ -51,9 +43,9 @@ func RenderCommitMessageLink(ctx context.Context, msg, urlPrefix, urlDefault str return template.HTML(msgLines[0]) } -// RenderCommitMessageLinkSubject renders commit message as a XXS-safe link to +// RenderCommitMessageLinkSubject renders commit message as a XSS-safe link to // the provided default url, handling for special links without email to links. -func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML { +func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlDefault string, metas map[string]string) template.HTML { msgLine := strings.TrimLeftFunc(msg, unicode.IsSpace) lineEnd := strings.IndexByte(msgLine, '\n') if lineEnd > 0 { @@ -68,7 +60,6 @@ func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlPrefix, urlDefa // shouldn't be any special HTML. renderedMessage, err := markup.RenderCommitMessageSubject(&markup.RenderContext{ Ctx: ctx, - URLPrefix: urlPrefix, DefaultLink: urlDefault, Metas: metas, }, template.HTMLEscapeString(msgLine)) @@ -80,7 +71,7 @@ func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlPrefix, urlDefa } // RenderCommitBody extracts the body of a commit message without its title. -func RenderCommitBody(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML { +func RenderCommitBody(ctx context.Context, msg string, metas map[string]string) template.HTML { msgLine := strings.TrimSpace(msg) lineEnd := strings.IndexByte(msgLine, '\n') if lineEnd > 0 { @@ -94,9 +85,8 @@ func RenderCommitBody(ctx context.Context, msg, urlPrefix string, metas map[stri } renderedMessage, err := markup.RenderCommitMessage(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: urlPrefix, - Metas: metas, + Ctx: ctx, + Metas: metas, }, template.HTMLEscapeString(msgLine)) if err != nil { log.Error("RenderCommitMessage: %v", err) @@ -115,11 +105,10 @@ func RenderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML { } // RenderIssueTitle renders issue/pull title with defined post processors -func RenderIssueTitle(ctx context.Context, text, urlPrefix string, metas map[string]string) template.HTML { +func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) template.HTML { renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: urlPrefix, - Metas: metas, + Ctx: ctx, + Metas: metas, }, template.HTMLEscapeString(text)) if err != nil { log.Error("RenderIssueTitle: %v", err) @@ -211,26 +200,10 @@ func ReactionToEmoji(reaction string) template.HTML { return template.HTML(fmt.Sprintf(`:%s:`, reaction, setting.StaticURLPrefix, url.PathEscape(reaction))) } -// RenderNote renders the contents of a git-notes file as a commit message. -func RenderNote(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML { - cleanMsg := template.HTMLEscapeString(msg) - fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: urlPrefix, - Metas: metas, - }, cleanMsg) - if err != nil { - log.Error("RenderNote: %v", err) - return "" - } - return template.HTML(fullMessage) -} - func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //nolint:revive output, err := markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: setting.AppSubURL, - Metas: map[string]string{"mode": "document"}, + Ctx: ctx, + Metas: map[string]string{"mode": "document"}, }, input) if err != nil { log.Error("RenderString: %v", err) diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 29d3ed3a5..8648967d3 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -6,17 +6,64 @@ package templates import ( "context" "html/template" + "os" "testing" + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/markup" + "github.com/stretchr/testify/assert" ) +const testInput = ` space @mention-user +/just/a/path.bin +https://example.com/file.bin +[local link](file.bin) +[remote link](https://example.com) +[[local link|file.bin]] +[[remote link|https://example.com]] +![local image](image.jpg) +![remote image](https://example.com/image.jpg) +[[local image|image.jpg]] +[[remote link|https://example.com/image.jpg]] +https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash +com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare +https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb +com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit +:+1: +mail@domain.com +@mention-user test +#123 + space +` + +var testMetas = map[string]string{ + "user": "user13", + "repo": "repo11", + "repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/", + "mode": "comment", +} + +func TestMain(m *testing.M) { + unittest.InitSettings() + if err := git.InitSimple(context.Background()); err != nil { + log.Fatal("git init failed, err: %v", err) + } + markup.Init(&markup.ProcessorHelper{ + IsUsernameMentionable: func(ctx context.Context, username string) bool { + return username == "mention-user" + }, + }) + os.Exit(m.Run()) +} + func TestRenderCommitBody(t *testing.T) { type args struct { - ctx context.Context - msg string - urlPrefix string - metas map[string]string + ctx context.Context + msg string + metas map[string]string } tests := []struct { name string @@ -50,7 +97,91 @@ func TestRenderCommitBody(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equalf(t, tt.want, RenderCommitBody(tt.args.ctx, tt.args.msg, tt.args.urlPrefix, tt.args.metas), "RenderCommitBody(%v, %v, %v, %v)", tt.args.ctx, tt.args.msg, tt.args.urlPrefix, tt.args.metas) + assert.Equalf(t, tt.want, RenderCommitBody(tt.args.ctx, tt.args.msg, tt.args.metas), "RenderCommitBody(%v, %v, %v)", tt.args.ctx, tt.args.msg, tt.args.metas) }) } + + expected := `/just/a/path.bin +https://example.com/file.bin +[local link](file.bin) +[remote link](https://example.com) +[[local link|file.bin]] +[[remote link|https://example.com]] +![local image](image.jpg) +![remote image](https://example.com/image.jpg) +[[local image|image.jpg]] +[[remote link|https://example.com/image.jpg]] +88fc37a3c0...12fc37a3c0 (hash) +com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare +88fc37a3c0 +com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit +👍 +mail@domain.com +@mention-user test +#123 + space` + + assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput, testMetas)) +} + +func TestRenderCommitMessage(t *testing.T) { + expected := `space @mention-user ` + + assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput, testMetas)) +} + +func TestRenderCommitMessageLinkSubject(t *testing.T) { + expected := `space @mention-user` + + assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput, "https://example.com/link", testMetas)) +} + +func TestRenderIssueTitle(t *testing.T) { + expected := ` space @mention-user +/just/a/path.bin +https://example.com/file.bin +[local link](file.bin) +[remote link](https://example.com) +[[local link|file.bin]] +[[remote link|https://example.com]] +![local image](image.jpg) +![remote image](https://example.com/image.jpg) +[[local image|image.jpg]] +[[remote link|https://example.com/image.jpg]] +https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash +com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare +https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb +com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit +👍 +mail@domain.com +@mention-user test +#123 + space +` + assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput, testMetas)) +} + +func TestRenderMarkdownToHtml(t *testing.T) { + expected := `

space @mention-user
+/just/a/path.bin +https://example.com/file.bin +local link +remote link +local link +remote link +local image +remote image +local image +remote link +88fc37a3c0...12fc37a3c0 (hash) +com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare +88fc37a3c0 +com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit +👍 +mail@domain.com +@mention-user test +#123 +space

+` + assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput)) } diff --git a/routers/common/markup.go b/routers/common/markup.go index aaedc13de..a1c2c37ac 100644 --- a/routers/common/markup.go +++ b/routers/common/markup.go @@ -32,8 +32,10 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr case "markdown": // Raw markdown if err := markdown.RenderRaw(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: urlPrefix, + Ctx: ctx, + Links: markup.Links{ + Base: urlPrefix, + }, }, strings.NewReader(text), ctx.Resp); err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) } @@ -75,8 +77,10 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr } if err := markup.Render(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: urlPrefix, + Ctx: ctx, + Links: markup.Links{ + Base: urlPrefix, + }, Metas: meta, IsWiki: wiki, Type: markupType, diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go index 4d4918a8f..95b106225 100644 --- a/routers/web/feed/convert.go +++ b/routers/web/feed/convert.go @@ -51,9 +51,11 @@ func toReleaseLink(ctx *context.Context, act *activities_model.Action) string { // If rendering fails, the original markdown text is returned func renderMarkdown(ctx *context.Context, act *activities_model.Action, content string) string { markdownCtx := &markup.RenderContext{ - Ctx: ctx, - URLPrefix: act.GetRepoLink(ctx), - Type: markdown.MarkupName, + Ctx: ctx, + Links: markup.Links{ + Base: act.GetRepoLink(ctx), + }, + Type: markdown.MarkupName, Metas: map[string]string{ "user": act.GetRepoUserName(ctx), "repo": act.GetRepoName(ctx), @@ -199,7 +201,6 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio switch act.OpType { case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush: push := templates.ActionContent2Commits(act) - repoLink := act.GetRepoAbsoluteLink(ctx) for _, commit := range push.Commits { if len(desc) != 0 { @@ -208,7 +209,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio desc += fmt.Sprintf("%s\n%s", html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(ctx), commit.Sha1)), commit.Sha1, - templates.RenderCommitMessage(ctx, commit.Message, repoLink, nil), + templates.RenderCommitMessage(ctx, commit.Message, nil), ) } @@ -288,9 +289,11 @@ func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release, i link := &feeds.Link{Href: rel.HTMLURL()} content, err = markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: rel.Repo.Link(), - Metas: rel.Repo.ComposeMetas(ctx), + Ctx: ctx, + Links: markup.Links{ + Base: rel.Repo.Link(), + }, + Metas: rel.Repo.ComposeMetas(ctx), }, rel.Note) if err != nil { diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go index ce86727e2..04f84c0c8 100644 --- a/routers/web/feed/profile.go +++ b/routers/web/feed/profile.go @@ -42,8 +42,10 @@ func showUserFeed(ctx *context.Context, formatType string) { } ctxUserDescription, err := markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: ctx.ContextUser.HTMLURL(), + Ctx: ctx, + Links: markup.Links{ + Base: ctx.ContextUser.HTMLURL(), + }, Metas: map[string]string{ "user": ctx.ContextUser.GetDisplayName(), }, diff --git a/routers/web/org/home.go b/routers/web/org/home.go index cdf280ed4..8bf02b2c4 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -5,6 +5,7 @@ package org import ( "net/http" + "path" "strings" "code.gitea.io/gitea/models/db" @@ -47,10 +48,8 @@ func Home(ctx *context.Context) { ctx.Data["Title"] = org.DisplayName() if len(org.Description) != 0 { desc, err := markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: ctx.Repo.RepoLink, - Metas: map[string]string{"mode": "document"}, - GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, + Metas: map[string]string{"mode": "document"}, }, org.Description) if err != nil { ctx.ServerError("RenderString", err) @@ -173,14 +172,16 @@ func prepareOrgProfileReadme(ctx *context.Context, profileGitRepo *git.Repositor if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil { log.Error("failed to GetBlobContent: %v", err) } else { - // Pass URLPrefix to markdown render for the full link of media elements. - // The profile of default branch would be shown. - prefix := profileDbRepo.Link() + "/src/branch/" + util.PathEscapeSegments(profileDbRepo.DefaultBranch) if profileContent, err := markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - GitRepo: profileGitRepo, - URLPrefix: prefix, - Metas: map[string]string{"mode": "document"}, + Ctx: ctx, + GitRepo: profileGitRepo, + Links: markup.Links{ + // Pass repo link to markdown render for the full link of media elements. + // The profile of default branch would be shown. + Base: profileDbRepo.Link(), + BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)), + }, + Metas: map[string]string{"mode": "document"}, }, bytes); err != nil { log.Error("failed to RenderString: %v", err) } else { diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index bd393e7fb..abb39caa5 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -7,7 +7,9 @@ package repo import ( "errors" "fmt" + "html/template" "net/http" + "path" "strings" asymkey_model "code.gitea.io/gitea/models/asymkey" @@ -21,7 +23,9 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitgraph" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/gitdiff" git_service "code.gitea.io/gitea/services/repository" ) @@ -370,9 +374,21 @@ func Diff(ctx *context.Context) { note := &git.Note{} err = git.GetNote(ctx, ctx.Repo.GitRepo, commitID, note) if err == nil { - ctx.Data["Note"] = string(charset.ToUTF8WithFallback(note.Message)) ctx.Data["NoteCommit"] = note.Commit ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(ctx, note.Commit) + ctx.Data["NoteRendered"], err = markup.RenderCommitMessage(&markup.RenderContext{ + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + BranchPath: path.Join("commit", util.PathEscapeSegments(commitID)), + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, + }, template.HTMLEscapeString(string(charset.ToUTF8WithFallback(note.Message)))) + if err != nil { + ctx.ServerError("RenderCommitMessage", err) + return + } } ctx.Data["BranchName"], err = commit.GetBranchName() diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 2b5ab2117..0d660e3b8 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1436,12 +1436,13 @@ func ViewIssue(ctx *context.Context) { } } ctx.Data["IssueWatch"] = iw - issue.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, issue.Content) if err != nil { ctx.ServerError("RenderString", err) @@ -1601,10 +1602,12 @@ func ViewIssue(ctx *context.Context) { } comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, comment.Content) if err != nil { ctx.ServerError("RenderString", err) @@ -1678,10 +1681,12 @@ func ViewIssue(ctx *context.Context) { } } else if comment.Type.HasContentSupport() { comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, comment.Content) if err != nil { ctx.ServerError("RenderString", err) @@ -2234,10 +2239,12 @@ func UpdateIssueContent(ctx *context.Context) { } content, err := markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ? - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ? + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, issue.Content) if err != nil { ctx.ServerError("RenderString", err) @@ -3143,10 +3150,12 @@ func UpdateCommentContent(ctx *context.Context) { } content, err := markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ? - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ? + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, comment.Content) if err != nil { ctx.ServerError("RenderString", err) diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go index fbecabb2b..19db2abd6 100644 --- a/routers/web/repo/milestone.go +++ b/routers/web/repo/milestone.go @@ -81,10 +81,12 @@ func Milestones(ctx *context.Context) { } for _, m := range miles { m.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, m.Content) if err != nil { ctx.ServerError("RenderString", err) @@ -275,10 +277,12 @@ func MilestoneIssuesAndPulls(ctx *context.Context) { } milestone.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, milestone.Content) if err != nil { ctx.ServerError("RenderString", err) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 5694575b4..4908bb796 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -90,10 +90,12 @@ func Projects(ctx *context.Context) { for i := range projects { projects[i].RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, projects[i].Description) if err != nil { ctx.ServerError("RenderString", err) @@ -357,10 +359,12 @@ func ViewProject(ctx *context.Context) { ctx.Data["LinkedPRs"] = linkedPrsMap project.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, project.Description) if err != nil { ctx.ServerError("RenderString", err) diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 86cb93e49..fdb247d41 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -136,10 +136,12 @@ func Releases(ctx *context.Context) { } r.Note, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, r.Note) if err != nil { ctx.ServerError("RenderString", err) @@ -287,10 +289,12 @@ func SingleRelease(ctx *context.Context) { } } release.Note, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, release.Note) if err != nil { ctx.ServerError("RenderString", err) diff --git a/routers/web/repo/render.go b/routers/web/repo/render.go index 33476c1d2..f2c6ab3f8 100644 --- a/routers/web/repo/render.go +++ b/routers/web/repo/render.go @@ -57,16 +57,15 @@ func RenderFile(ctx *context.Context) { return } - treeLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() - if ctx.Repo.TreePath != "" { - treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath) - } - ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts") err = markup.Render(&markup.RenderContext{ - Ctx: ctx, - RelativePath: ctx.Repo.TreePath, - URLPrefix: path.Dir(treeLink), + Ctx: ctx, + RelativePath: ctx.Repo.TreePath, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + BranchPath: ctx.Repo.BranchNameSubURL(), + TreePath: path.Dir(ctx.Repo.TreePath), + }, Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx), GitRepo: ctx.Repo.GitRepo, InStandalonePage: true, diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 9cf0dff5d..c2daa3e5e 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -158,7 +158,7 @@ func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry, try return "", readmeFile, nil } -func renderDirectory(ctx *context.Context, treeLink string) { +func renderDirectory(ctx *context.Context) { entries := renderDirectoryFiles(ctx, 1*time.Second) if ctx.Written() { return @@ -175,7 +175,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { return } - renderReadmeFile(ctx, subfolder, readmeFile, treeLink) + renderReadmeFile(ctx, subfolder, readmeFile) } // localizedExtensions prepends the provided language code with and without a @@ -259,7 +259,7 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, return buf, dataRc, &fileInfo{st.IsText(), true, meta.Size, &meta.Pointer, st}, nil } -func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry, readmeTreelink string) { +func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry) { target := readmeFile if readmeFile != nil && readmeFile.IsLink() { target, _ = readmeFile.FollowLinks() @@ -312,9 +312,13 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{ Ctx: ctx, RelativePath: path.Join(ctx.Repo.TreePath, readmeFile.Name()), // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path). - URLPrefix: path.Join(readmeTreelink, subfolder), - Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx), - GitRepo: ctx.Repo.GitRepo, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + BranchPath: ctx.Repo.BranchNameSubURL(), + TreePath: path.Join(ctx.Repo.TreePath, subfolder), + }, + Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx), + GitRepo: ctx.Repo.GitRepo, }, rd) if err != nil { log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err) @@ -337,7 +341,7 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr } } -func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink string) { +func renderFile(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["IsViewFile"] = true ctx.Data["HideRepoInfo"] = true blob := entry.Blob() @@ -351,7 +355,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) ctx.Data["FileIsSymlink"] = entry.IsLink() ctx.Data["FileName"] = blob.Name() - ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) + ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) if ctx.Repo.TreePath == ".editorconfig" { _, editorconfigWarning, editorconfigErr := ctx.Repo.GetEditorconfig(ctx.Repo.Commit) @@ -479,9 +483,13 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st Ctx: ctx, Type: markupType, RelativePath: ctx.Repo.TreePath, - URLPrefix: path.Dir(treeLink), - Metas: metas, - GitRepo: ctx.Repo.GitRepo, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + BranchPath: ctx.Repo.BranchNameSubURL(), + TreePath: path.Dir(ctx.Repo.TreePath), + }, + Metas: metas, + GitRepo: ctx.Repo.GitRepo, }, rd) if err != nil { ctx.ServerError("Render", err) @@ -585,9 +593,13 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{ Ctx: ctx, RelativePath: ctx.Repo.TreePath, - URLPrefix: path.Dir(treeLink), - Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx), - GitRepo: ctx.Repo.GitRepo, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + BranchPath: ctx.Repo.BranchNameSubURL(), + TreePath: path.Dir(ctx.Repo.TreePath), + }, + Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx), + GitRepo: ctx.Repo.GitRepo, }, rd) if err != nil { ctx.ServerError("Render", err) @@ -945,14 +957,6 @@ func renderCode(ctx *context.Context) { } ctx.Data["Title"] = title - branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() - treeLink := branchLink - rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() - - if len(ctx.Repo.TreePath) > 0 { - treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath) - } - // Get Topics of this repo renderRepoTopics(ctx) if ctx.Written() { @@ -977,9 +981,9 @@ func renderCode(ctx *context.Context) { } if entry.IsDir() { - renderDirectory(ctx, treeLink) + renderDirectory(ctx) } else { - renderFile(ctx, entry, treeLink, rawLink) + renderFile(ctx, entry) } if ctx.Written() { return @@ -1020,6 +1024,12 @@ func renderCode(ctx *context.Context) { } ctx.Data["Paths"] = paths + + branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() + treeLink := branchLink + if len(ctx.Repo.TreePath) > 0 { + treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath) + } ctx.Data["TreeLink"] = treeLink ctx.Data["TreeNames"] = treeNames ctx.Data["BranchLink"] = branchLink diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 8ea18a186..4e09f046c 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -238,10 +238,12 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { } rctx := &markup.RenderContext{ - Ctx: ctx, - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx), - IsWiki: true, + Ctx: ctx, + Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx), + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + IsWiki: true, } buf := &strings.Builder{} diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 919a080b4..0f8d64e7b 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -47,10 +47,8 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) { if len(ctx.ContextUser.Description) != 0 { content, err := markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: map[string]string{"mode": "document"}, - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Metas: map[string]string{"mode": "document"}, + Ctx: ctx, }, ctx.ContextUser.Description) if err != nil { ctx.ServerError("RenderString", err) diff --git a/routers/web/user/home.go b/routers/web/user/home.go index debbb9753..44920817c 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -258,9 +258,11 @@ func Milestones(ctx *context.Context) { } milestones[i].RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: milestones[i].Repo.Link(), - Metas: milestones[i].Repo.ComposeMetas(ctx), - Ctx: ctx, + Links: markup.Links{ + Base: milestones[i].Repo.Link(), + }, + Metas: milestones[i].Repo.ComposeMetas(ctx), + Ctx: ctx, }, milestones[i].Content) if err != nil { ctx.ServerError("RenderString", err) diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index a8ab3dde8..c5305ebcd 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -7,6 +7,7 @@ package user import ( "fmt" "net/http" + "path" "strings" activities_model "code.gitea.io/gitea/models/activities" @@ -233,18 +234,19 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil { log.Error("failed to GetBlobContent: %v", err) } else { - // Give the URLPrefix to the markdown render for the full link of media element. - // the media link usually be like /[user]/[repoName]/media/branch/[branchName], - // Eg. /Tom/.profile/media/branch/main - // The branch shown on the profile page is the default branch, this need to be in sync with doc, see: - // https://docs.gitea.com/usage/profile-readme - - prefix := profileDbRepo.Link() + "/src/branch/" + util.PathEscapeSegments(profileDbRepo.DefaultBranch) if profileContent, err := markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - GitRepo: profileGitRepo, - URLPrefix: prefix, - Metas: map[string]string{"mode": "document"}, + Ctx: ctx, + GitRepo: profileGitRepo, + Links: markup.Links{ + // Give the repo link to the markdown render for the full link of media element. + // the media link usually be like /[user]/[repoName]/media/branch/[branchName], + // Eg. /Tom/.profile/media/branch/main + // The branch shown on the profile page is the default branch, this need to be in sync with doc, see: + // https://docs.gitea.com/usage/profile-readme + Base: profileDbRepo.Link(), + BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)), + }, + Metas: map[string]string{"mode": "document"}, }, bytes); err != nil { log.Error("failed to RenderString: %v", err) } else { diff --git a/services/mailer/mail.go b/services/mailer/mail.go index b597dd048..cf8033360 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -220,9 +220,11 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient // This is the body of the new issue or comment, not the mail body body, err := markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: ctx.Issue.Repo.HTMLURL(), - Metas: ctx.Issue.Repo.ComposeMetas(ctx), + Ctx: ctx, + Links: markup.Links{ + Base: ctx.Issue.Repo.HTMLURL(), + }, + Metas: ctx.Issue.Repo.ComposeMetas(ctx), }, ctx.Content) if err != nil { return nil, err diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go index 88973a6be..801c2476c 100644 --- a/services/mailer/mail_release.go +++ b/services/mailer/mail_release.go @@ -57,9 +57,11 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo var err error rel.RenderedNote, err = markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: rel.Repo.Link(), - Metas: rel.Repo.ComposeMetas(ctx), + Ctx: ctx, + Links: markup.Links{ + Base: rel.Repo.HTMLURL(), + }, + Metas: rel.Repo.ComposeMetas(ctx), }, rel.Note) if err != nil { log.Error("markdown.RenderString(%d): %v", rel.RepoID, err) diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl index cec5b6fc3..8ae7301c4 100644 --- a/templates/repo/branch/list.tmpl +++ b/templates/repo/branch/list.tmpl @@ -25,7 +25,7 @@ {{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DefaultBranchBranch.DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DefaultBranchBranch.DBBranch.CommitID)}} -

{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}} · {{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage .RepoLink (.Repository.ComposeMetas ctx)}} · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}}  {{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}

+

{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}} · {{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}} · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}}  {{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}

{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}} @@ -101,7 +101,7 @@ {{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DBBranch.CommitID)}} -

{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .DBBranch.CommitID}} · {{RenderCommitMessage $.Context .DBBranch.CommitMessage $.RepoLink ($.Repository.ComposeMetas ctx)}} · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}}  {{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}}  {{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}

+

{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .DBBranch.CommitID}} · {{RenderCommitMessage $.Context .DBBranch.CommitMessage ($.Repository.ComposeMetas ctx)}} · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}}  {{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}}  {{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}

{{end}} diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl index 2a4240045..56bcbc21b 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -19,7 +19,7 @@ {{end}}
-

{{RenderCommitMessage $.Context .Commit.Message $.RepoLink ($.Repository.ComposeMetas ctx)}}{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}

+

{{RenderCommitMessage $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}

{{if not $.PageIsWiki}} {{if IsMultilineCommitMessage .Commit.Message}} -
{{RenderCommitBody $.Context .Commit.Message $.RepoLink ($.Repository.ComposeMetas ctx)}}
+
{{RenderCommitBody $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}
{{end}} {{template "repo/commit_load_branches_and_tags" .}}
@@ -258,7 +258,7 @@
{{end}} - {{if .Note}} + {{if .NoteRendered}}
{{svg "octicon-note" 16 "gt-mr-3"}} {{ctx.Locale.Tr "repo.diff.git-notes"}}: @@ -276,7 +276,7 @@ {{TimeSince .NoteCommit.Author.When ctx.Locale}}
-
{{RenderNote $.Context .Note $.RepoLink ($.Repository.ComposeMetas ctx)}}
+
{{.NoteRendered | Str2html}}
{{end}} {{template "repo/diff/box" .}} diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index 77f168424..7bfed5312 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -60,7 +60,7 @@ {{.Summary | RenderEmoji $.Context}} {{else}} {{$commitLink:= printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String)}} - {{RenderCommitMessageLinkSubject $.Context .Message $commitRepoLink $commitLink ($.Repository.ComposeMetas ctx)}} + {{RenderCommitMessageLinkSubject $.Context .Message $commitLink ($.Repository.ComposeMetas ctx)}} {{end}} {{if IsMultilineCommitMessage .Message}} @@ -68,7 +68,7 @@ {{end}} {{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}} {{if IsMultilineCommitMessage .Message}} -
{{RenderCommitBody $.Context .Message $commitRepoLink ($.Repository.ComposeMetas ctx)}}
+
{{RenderCommitBody $.Context .Message ($.Repository.ComposeMetas ctx)}}
{{end}} {{if .Committer}} diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl index 63eb5945b..79e1bd630 100644 --- a/templates/repo/commits_list_small.tmpl +++ b/templates/repo/commits_list_small.tmpl @@ -38,12 +38,12 @@
- {{RenderCommitMessageLinkSubject $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}} + {{RenderCommitMessageLinkSubject $.root.Context .Message $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}} {{if IsMultilineCommitMessage .Message}} {{end}} {{if IsMultilineCommitMessage .Message}} -
{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}
+
{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}
{{end}} {{end}} diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index 42a7bf3c2..b4d96c316 100644 --- a/templates/repo/diff/compare.tmpl +++ b/templates/repo/diff/compare.tmpl @@ -194,7 +194,7 @@
{{ctx.Locale.Tr "repo.pulls.has_pull_request" (print (Escape $.RepoLink) "/pulls/" .PullRequest.Issue.Index) (Escape $.RepoRelPath) .PullRequest.Index | Safe}}

- {{RenderIssueTitle $.Context .PullRequest.Issue.Title $.RepoLink ($.Repository.ComposeMetas ctx)}} + {{RenderIssueTitle $.Context .PullRequest.Issue.Title ($.Repository.ComposeMetas ctx)}} #{{.PullRequest.Issue.Index}}

diff --git a/templates/repo/graph/commits.tmpl b/templates/repo/graph/commits.tmpl index 3c63046c6..61ef1fe10 100644 --- a/templates/repo/graph/commits.tmpl +++ b/templates/repo/graph/commits.tmpl @@ -29,7 +29,7 @@ - {{RenderCommitMessage $.Context $commit.Subject $.RepoLink ($.Repository.ComposeMetas ctx)}} + {{RenderCommitMessage $.Context $commit.Subject ($.Repository.ComposeMetas ctx)}} {{range $commit.Refs}} diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl index d2c48ff27..7ec48c673 100644 --- a/templates/repo/issue/view_title.tmpl +++ b/templates/repo/issue/view_title.tmpl @@ -6,7 +6,7 @@

- {{RenderIssueTitle $.Context .Issue.Title $.RepoLink ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} #{{.Issue.Index}} + {{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} #{{.Issue.Index}}
diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl index 836c633ce..504032aa7 100644 --- a/templates/repo/view_list.tmpl +++ b/templates/repo/view_list.tmpl @@ -26,10 +26,10 @@ {{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses}} {{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}} - {{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $.RepoLink $commitLink ($.Repository.ComposeMetas ctx)}} + {{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $commitLink ($.Repository.ComposeMetas ctx)}} {{if IsMultilineCommitMessage .LatestCommit.Message}} -
{{RenderCommitBody $.Context .LatestCommit.Message $.RepoLink ($.Repository.ComposeMetas ctx)}}
+
{{RenderCommitBody $.Context .LatestCommit.Message ($.Repository.ComposeMetas ctx)}}
{{end}}
{{end}} @@ -83,7 +83,7 @@ {{if $commit}} {{$commitLink := printf "%s/commit/%s" $.RepoLink (PathEscape $commit.ID.String)}} - {{RenderCommitMessageLinkSubject $.Context $commit.Message $.RepoLink $commitLink ($.Repository.ComposeMetas ctx)}} + {{RenderCommitMessageLinkSubject $.Context $commit.Message $commitLink ($.Repository.ComposeMetas ctx)}} {{else}}
{{end}} diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl index 728715bbc..a51365e4d 100644 --- a/templates/user/dashboard/feeds.tmpl +++ b/templates/user/dashboard/feeds.tmpl @@ -91,7 +91,7 @@ {{ShortSha .Sha1}} - {{RenderCommitMessage $.Context .Message $repoLink ($repo.ComposeMetas ctx)}} + {{RenderCommitMessage $.Context .Message ($repo.ComposeMetas ctx)}}
{{end}} diff --git a/tests/fuzz/fuzz_test.go b/tests/fuzz/fuzz_test.go index 6a7d9d2d3..25a6ed821 100644 --- a/tests/fuzz/fuzz_test.go +++ b/tests/fuzz/fuzz_test.go @@ -15,8 +15,10 @@ import ( ) var renderContext = markup.RenderContext{ - Ctx: context.Background(), - URLPrefix: "https://example.com/go-gitea/gitea", + Ctx: context.Background(), + Links: markup.Links{ + Base: "https://example.com/go-gitea/gitea", + }, Metas: map[string]string{ "user": "go-gitea", "repo": "gitea",