From 022552d5b6adc792d3cd16df7de6e52cb7b41a72 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Tue, 16 Jan 2024 03:13:29 +0100 Subject: [PATCH] Rework markup link rendering (#26745) (#28803) Backport #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)
--- 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 | 60 ++-- modules/markup/orgmode/orgmode_test.go | 23 +- modules/markup/renderer.go | 42 ++- modules/templates/helper.go | 1 - modules/templates/util_render.go | 52 +-- 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 | 6 +- 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 | 12 +- routers/web/user/home.go | 8 +- routers/web/user/profile.go | 18 +- 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, 966 insertions(+), 388 deletions(-) diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go index 49927fbbe2f4..27f669413497 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, + Links: markup.Links{ + Base: issue.Repo.Link(), + }, + Metas: issue.Repo.ComposeMetas(), }, comment.Content); err != nil { return nil, err } diff --git a/models/repo/repo.go b/models/repo/repo.go index 3fd701f87413..596a4268c842 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -578,8 +578,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 ffbb6da4dadd..122517ed11c0 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 03168b694681..4d3fc32fbe68 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 7b7f6df70106..5ba956191561 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 8b9ed44db6de..a5eecb93bb6e 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 := RenderString(&RenderContext{ Ctx: git.DefaultContext, RelativePath: ".md", - URLPrefix: TestRepoURL, - Metas: localMetas, + Links: Links{ + Base: 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 := RenderString(&RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", - URLPrefix: setting.AppSubURL, - Metas: localMetas, + Links: 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 := RenderString(&RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", - URLPrefix: TestRepoURL, + Links: Links{ + Base: 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 := RenderString(&RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", - URLPrefix: TestRepoURL, + Links: Links{ + Base: 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 := RenderString(&RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", - URLPrefix: TestRepoURL, + Links: Links{ + Base: 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(&RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: tree, + Ctx: git.DefaultContext, + Links: Links{ + Base: TestRepoURL, + BranchPath: "master", + }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) buffer, err = markdown.RenderString(&RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: TestRepoURL, - Metas: localMetas, - IsWiki: true, + Ctx: git.DefaultContext, + Links: Links{ + Base: TestRepoURL, + }, + Metas: localMetas, + IsWiki: true, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer)) } - rawtree := util.URLJoin(TestRepoURL, "raw", "master") + mediatree := util.URLJoin(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(TestRepoURL, "wiki", "Link") otherURLWiki := util.URLJoin(TestRepoURL, "wiki", "Other-Link") encodedURLWiki := util.URLJoin(TestRepoURL, "wiki", "Link%3F") @@ -475,21 +490,25 @@ func TestRender_ShortLinks(t *testing.T) { func TestRender_RelativeImages(t *testing.T) { setting.AppURL = TestAppURL - tree := util.URLJoin(TestRepoURL, "src", "master") test := func(input, expected, expectedWiki string) { buffer, err := markdown.RenderString(&RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: tree, - Metas: localMetas, + Ctx: git.DefaultContext, + Links: Links{ + Base: TestRepoURL, + BranchPath: "master", + }, + Metas: localMetas, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) buffer, err = markdown.RenderString(&RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: TestRepoURL, - Metas: localMetas, - IsWiki: true, + Ctx: git.DefaultContext, + Links: Links{ + Base: 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 := PostProcess(&RenderContext{ - Ctx: git.DefaultContext, - URLPrefix: "https://example.com", - Metas: localMetas, + Ctx: git.DefaultContext, + Links: 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 43885889d125..771162b9a3f1 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 f2322b25544b..6f2bbae6e0c1 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 := 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 = 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 := 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 := 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 := 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 := 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 := 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 := 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 d3474c0efb18..f7a051476b25 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, 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, link) + fmt.Fprintf(r, ``, link, link) } else { - description := strings.TrimPrefix(org.String(l.Description...), "file:") - videoSrc := getMediaURL([]byte(description)) - fmt.Fprintf(r, ``, link, videoSrc, videoSrc) + 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, 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 8f454e995581..cb9b24f6d0d5 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)) @@ -59,19 +63,19 @@ func TestRender_Media(t *testing.T) { result := util.URLJoin(AppSubURL, url) test("[[file:"+url+"]]", - "

\""+result+"\"

") + "

\""+result+"\"

") // With description. test("[[https://example.com][https://example.com/example.svg]]", `

https://example.com/example.svg

`) test("[[https://example.com][https://example.com/example.mp4]]", - `

`) + `

`) // Without description. test("[[https://example.com/example.svg]]", - `

https://example.com/example.svg

`) + `

https://example.com/example.svg

`) test("[[https://example.com/example.mp4]]", - `

`) + `

`) } func TestRender_Source(t *testing.T) { @@ -80,8 +84,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 0331c3742ab1..5a7adcc55322 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 bb1411cbfdb3..7605320ed22c 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -157,7 +157,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 639d58126c1b..1d9635410b37 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,25 +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, + 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 29d3ed3a56c4..8648967d3861 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 5f412014d7e7..e2e206f0c91b 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 04078955bbd5..9d43887ce40d 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, + Links: markup.Links{ + Base: rel.Repo.Link(), + }, + Metas: rel.Repo.ComposeMetas(), }, rel.Note) if err != nil { diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go index ce86727e2482..04f84c0c8d40 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 ec866eb6b3ba..05ccd48fca88 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -44,10 +44,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) diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index a6eb7efeb022..7c8dcbbe7b84 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(), + 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 87493dd8ad9e..fe42e863450d 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1424,12 +1424,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(), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, issue.Content) if err != nil { ctx.ServerError("RenderString", err) @@ -1589,10 +1590,12 @@ func ViewIssue(ctx *context.Context) { } comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, comment.Content) if err != nil { ctx.ServerError("RenderString", err) @@ -1666,10 +1669,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(), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, comment.Content) if err != nil { ctx.ServerError("RenderString", err) @@ -2220,10 +2225,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(), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ? + }, + Metas: ctx.Repo.Repository.ComposeMetas(), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, issue.Content) if err != nil { ctx.ServerError("RenderString", err) @@ -3129,10 +3136,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(), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ? + }, + Metas: ctx.Repo.Repository.ComposeMetas(), + 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 eb27c4b00fe4..a9fcba4baef9 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(), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(), + 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(), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(), + 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 541f24c7f4fc..09d9c1148d88 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -86,10 +86,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(), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, projects[i].Description) if err != nil { ctx.ServerError("RenderString", err) @@ -353,10 +355,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(), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(), + 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 338dc34cf457..02642422192e 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -175,10 +175,12 @@ func releasesOrTags(ctx *context.Context, isTagList bool) { } r.Note, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, }, r.Note) if err != nil { ctx.ServerError("RenderString", err) @@ -281,10 +283,12 @@ func SingleRelease(ctx *context.Context) { } } release.Note, err = markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: ctx.Repo.Repository.ComposeMetas(), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(), + 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 f07b4e8c113c..c6bc0dcf1959 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(), GitRepo: ctx.Repo.GitRepo, InStandalonePage: true, diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index b17d11ec63e9..759c011cb2e8 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(), - 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(), + 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) @@ -333,7 +337,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() @@ -347,7 +351,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) @@ -475,9 +479,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) @@ -576,9 +584,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(), - 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(), + GitRepo: ctx.Repo.GitRepo, }, rd) if err != nil { ctx.ServerError("Render", err) @@ -936,14 +948,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() { @@ -968,9 +972,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 @@ -1011,6 +1015,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 c9cec0313d81..5634445b7032 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(), - IsWiki: true, + Ctx: ctx, + Metas: ctx.Repo.Repository.ComposeDocumentMetas(), + 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 2934f8206ef9..2e4a8b2a4c1f 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -44,10 +44,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) @@ -84,7 +82,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) { } } -func FindUserProfileReadme(ctx *context.Context) (profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) { +func FindUserProfileReadme(ctx *context.Context) (profileDbRepo *repo_model.Repository, profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) { profileDbRepo, err := repo_model.GetRepositoryByName(ctx.ContextUser.ID, ".profile") if err == nil && !profileDbRepo.IsEmpty && !profileDbRepo.IsPrivate { if profileGitRepo, err = git.OpenRepository(ctx, profileDbRepo.RepoPath()); err != nil { @@ -97,7 +95,7 @@ func FindUserProfileReadme(ctx *context.Context) (profileGitRepo *git.Repository } } } - return profileGitRepo, profileReadmeBlob, func() { + return profileDbRepo, profileGitRepo, profileReadmeBlob, func() { if profileGitRepo != nil { _ = profileGitRepo.Close() } @@ -107,7 +105,7 @@ func FindUserProfileReadme(ctx *context.Context) (profileGitRepo *git.Repository func RenderUserHeader(ctx *context.Context) { prepareContextForCommonProfile(ctx) - _, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx) + _, _, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx) defer profileClose() ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil } diff --git a/routers/web/user/home.go b/routers/web/user/home.go index d0d24d7ed103..285c18c1f9cc 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -246,9 +246,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, + Links: markup.Links{ + Base: milestones[i].Repo.Link(), + }, + Metas: milestones[i].Repo.ComposeMetas(), + 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 48a4b94c190b..04bbd196fc03 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" @@ -64,17 +65,17 @@ func userProfile(ctx *context.Context) { ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data) } - profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx) + profileDbRepo, profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx) defer profileClose() showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) - prepareUserProfileTabData(ctx, showPrivate, profileGitRepo, profileReadmeBlob) + prepareUserProfileTabData(ctx, showPrivate, profileDbRepo, profileGitRepo, profileReadmeBlob) // call PrepareContextForProfileBigAvatar later to avoid re-querying the NumFollowers & NumFollowing shared_user.PrepareContextForProfileBigAvatar(ctx) ctx.HTML(http.StatusOK, tplProfile) } -func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileGitRepo *git.Repository, profileReadme *git.Blob) { +func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDbRepo *repo_model.Repository, profileGitRepo *git.Repository, profileReadme *git.Blob) { // if there is a profile readme, default to "overview" page, otherwise, default to "repositories" page // if there is not a profile readme, the overview tab should be treated as the repositories tab tab := ctx.FormString("tab") @@ -236,7 +237,16 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileGi if profileContent, err := markdown.RenderString(&markup.RenderContext{ Ctx: ctx, GitRepo: profileGitRepo, - Metas: map[string]string{"mode": "document"}, + 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 83f58bad0d68..08b52687d165 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, + Links: markup.Links{ + Base: ctx.Issue.Repo.HTMLURL(), + }, + Metas: ctx.Issue.Repo.ComposeMetas(), }, ctx.Content) if err != nil { return nil, err diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go index 9c49c89e8bd1..983e55a4361f 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, + Links: markup.Links{ + Base: rel.Repo.HTMLURL(), + }, + Metas: rel.Repo.ComposeMetas(), }, 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 370a412efb83..e8f55c18a323 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.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.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.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.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 7de4c2c8fca6..17a71e7a7568 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}}{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses "root" $}}

+

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

{{if not $.PageIsWiki}}
@@ -135,7 +135,7 @@ {{end}}
{{if IsMultilineCommitMessage .Commit.Message}} -
{{RenderCommitBody $.Context .Commit.Message $.RepoLink $.Repository.ComposeMetas}}
+
{{RenderCommitBody $.Context .Commit.Message $.Repository.ComposeMetas}}
{{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}}
+
{{.NoteRendered | Str2html}}
{{end}} {{template "repo/diff/box" .}} diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index 9fb2788a4596..9c0e38a2adf6 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}} + {{RenderCommitMessageLinkSubject $.Context .Message $commitLink $.Repository.ComposeMetas}} {{end}} {{if IsMultilineCommitMessage .Message}} @@ -68,7 +68,7 @@ {{end}} {{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses "root" $}} {{if IsMultilineCommitMessage .Message}} -
{{RenderCommitBody $.Context .Message $commitRepoLink $.Repository.ComposeMetas}}
+
{{RenderCommitBody $.Context .Message $.Repository.ComposeMetas}}
{{end}} {{if .Committer}} diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl index 645bd73bb82f..f6d26195d332 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}} + {{RenderCommitMessageLinkSubject $.root.Context .Message $commitLink $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}} {{if IsMultilineCommitMessage .Message}} {{end}} {{if IsMultilineCommitMessage .Message}} -
{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}
+
{{RenderCommitBody $.root.Context .Message $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}
{{end}} {{end}} diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index 2892ea51b161..3e5df17f2248 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}} + {{RenderIssueTitle $.Context .PullRequest.Issue.Title $.Repository.ComposeMetas}} #{{.PullRequest.Issue.Index}}

diff --git a/templates/repo/graph/commits.tmpl b/templates/repo/graph/commits.tmpl index b8817f5c88dd..cc8086f8dc89 100644 --- a/templates/repo/graph/commits.tmpl +++ b/templates/repo/graph/commits.tmpl @@ -29,7 +29,7 @@ - {{RenderCommitMessage $.Context $commit.Subject $.RepoLink $.Repository.ComposeMetas}} + {{RenderCommitMessage $.Context $commit.Subject $.Repository.ComposeMetas}} {{range $commit.Refs}} diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl index c18825f2046a..af99286cc1ab 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 | RenderCodeBlock}} #{{.Issue.Index}} + {{RenderIssueTitle $.Context .Issue.Title $.Repository.ComposeMetas | RenderCodeBlock}} #{{.Issue.Index}}
diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl index bfd59839f58c..0a83cdfc5b47 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 "root" $}} {{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}} - {{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}} + {{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $commitLink $.Repository.ComposeMetas}} {{if IsMultilineCommitMessage .LatestCommit.Message}} -
{{RenderCommitBody $.Context .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}
+
{{RenderCommitBody $.Context .LatestCommit.Message $.Repository.ComposeMetas}}
{{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}} + {{RenderCommitMessageLinkSubject $.Context $commit.Message $commitLink $.Repository.ComposeMetas}} {{else}}
{{end}} diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl index 974b59b7c315..a1ac3d884851 100644 --- a/templates/user/dashboard/feeds.tmpl +++ b/templates/user/dashboard/feeds.tmpl @@ -90,7 +90,7 @@ {{ShortSha .Sha1}} - {{RenderCommitMessage $.Context .Message $repoLink $.ComposeMetas}} + {{RenderCommitMessage $.Context .Message $.ComposeMetas}}
{{end}} diff --git a/tests/fuzz/fuzz_test.go b/tests/fuzz/fuzz_test.go index 6a7d9d2d32b8..25a6ed821319 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",