`
- rawLink := ` `
+ rawLink := ` `
// Try to get the user translation
if locale, ok := ctx.Value(translation.ContextKey).(translation.Locale); ok {
diff --git a/modules/markup/external/external.go b/modules/markup/external/external.go
index 98708e99b8622..03242e569ede5 100644
--- a/modules/markup/external/external.go
+++ b/modules/markup/external/external.go
@@ -79,8 +79,8 @@ func envMark(envName string) string {
func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
var (
command = strings.NewReplacer(
- envMark("GITEA_PREFIX_SRC"), ctx.RenderOptions.Links.SrcLink(),
- envMark("GITEA_PREFIX_RAW"), ctx.RenderOptions.Links.RawLink(),
+ envMark("GITEA_PREFIX_SRC"), ctx.RenderHelper.ResolveLink("", markup.LinkTypeDefault),
+ envMark("GITEA_PREFIX_RAW"), ctx.RenderHelper.ResolveLink("", markup.LinkTypeRaw),
).Replace(p.Command)
commands = strings.Fields(command)
args = commands[1:]
@@ -112,14 +112,14 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
args = append(args, f.Name())
}
- processCtx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.RenderOptions.Links.SrcLink()))
+ processCtx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.RenderHelper.ResolveLink("", markup.LinkTypeDefault)))
defer finished()
cmd := exec.CommandContext(processCtx, commands[0], args...)
cmd.Env = append(
os.Environ(),
- "GITEA_PREFIX_SRC="+ctx.RenderOptions.Links.SrcLink(),
- "GITEA_PREFIX_RAW="+ctx.RenderOptions.Links.RawLink(),
+ "GITEA_PREFIX_SRC="+ctx.RenderHelper.ResolveLink("", markup.LinkTypeDefault),
+ "GITEA_PREFIX_RAW="+ctx.RenderHelper.ResolveLink("", markup.LinkTypeRaw),
)
if !p.IsInputFile {
cmd.Stdin = input
diff --git a/modules/markup/html.go b/modules/markup/html.go
index e8799c401c537..0b1e9b32242c2 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -260,7 +260,6 @@ func RenderEmoji(ctx *RenderContext, content string) (string, error) {
}
func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output io.Writer) error {
- defer ctx.Cancel()
// FIXME: don't read all content to memory
rawHTML, err := io.ReadAll(input)
if err != nil {
@@ -396,7 +395,7 @@ func createLink(ctx *RenderContext, href, content, class string) *html.Node {
Data: atom.A.String(),
Attr: []html.Attribute{{Key: "href", Val: href}},
}
- if !RenderBehaviorForTesting.DisableInternalAttributes {
+ if !RenderBehaviorForTesting.DisableAdditionalAttributes {
a.Attr = append(a.Attr, html.Attribute{Key: "data-markdown-generated-content"})
}
if class != "" {
diff --git a/modules/markup/html_codepreview.go b/modules/markup/html_codepreview.go
index 68886a3434d0a..1d6d9a61091aa 100644
--- a/modules/markup/html_codepreview.go
+++ b/modules/markup/html_codepreview.go
@@ -51,7 +51,7 @@ func renderCodeBlock(ctx *RenderContext, node *html.Node) (urlPosStart, urlPosSt
lineStart, _ := strconv.Atoi(strings.TrimPrefix(lineStartStr, "L"))
lineStop, _ := strconv.Atoi(strings.TrimPrefix(lineStopStr, "L"))
opts.LineStart, opts.LineStop = lineStart, lineStop
- h, err := DefaultProcessorHelper.RenderRepoFileCodePreview(ctx, opts)
+ h, err := DefaultRenderHelperFuncs.RenderRepoFileCodePreview(ctx, opts)
return m[0], m[1], h, err
}
diff --git a/modules/markup/html_codepreview_test.go b/modules/markup/html_codepreview_test.go
index 7c0db59d06acc..3d99348ef18d5 100644
--- a/modules/markup/html_codepreview_test.go
+++ b/modules/markup/html_codepreview_test.go
@@ -16,16 +16,16 @@ import (
)
func TestRenderCodePreview(t *testing.T) {
- markup.Init(&markup.ProcessorHelper{
- RenderRepoFileCodePreview: func(ctx context.Context, opts markup.RenderCodePreviewOptions) (template.HTML, error) {
+ markup.Init(&markup.RenderHelperFuncs{
+ RenderRepoFileCodePreview: func(ctx context.Context, options markup.RenderCodePreviewOptions) (template.HTML, error) {
return "code preview
", nil
},
})
test := func(input, expected string) {
- buffer, err := markup.RenderString(markup.NewRenderContext(context.Background()).WithMarkupType(markdown.MarkupName), input)
+ buffer, err := markup.RenderString(markup.NewTestRenderContext().WithMarkupType(markdown.MarkupName), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
test("http://localhost:3000/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20", "
code preview
")
- test("http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20", `http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20
`)
+ test("http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20", `http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20
`)
}
diff --git a/modules/markup/html_commit.go b/modules/markup/html_commit.go
index 0649f84664073..358e7b06ba538 100644
--- a/modules/markup/html_commit.go
+++ b/modules/markup/html_commit.go
@@ -4,13 +4,10 @@
package markup
import (
- "io"
"slices"
"strings"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"golang.org/x/net/html"
@@ -163,15 +160,12 @@ func comparePatternProcessor(ctx *RenderContext, node *html.Node) {
// hashCurrentPatternProcessor renders SHA1 strings to corresponding links that
// are assumed to be in the same repository.
func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
- if ctx.RenderOptions.Metas == nil || ctx.RenderOptions.Metas["user"] == "" || ctx.RenderOptions.Metas["repo"] == "" || (ctx.RenderHelper.repoFacade == nil && ctx.RenderHelper.gitRepo == nil) {
+ if ctx.RenderOptions.Metas == nil || ctx.RenderOptions.Metas["user"] == "" || ctx.RenderOptions.Metas["repo"] == "" || ctx.RenderHelper == nil {
return
}
start := 0
next := node.NextSibling
- if ctx.RenderHelper.shaExistCache == nil {
- ctx.RenderHelper.shaExistCache = make(map[string]bool)
- }
for node != nil && node != next && start < len(node.Data) {
m := globalVars().hashCurrentPattern.FindStringSubmatchIndex(node.Data[start:])
if m == nil {
@@ -189,35 +183,12 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
// as used by git and github for linking and thus we have to do similar.
// Because of this, we check to make sure that a matched hash is actually
// a commit in the repository before making it a link.
-
- // check cache first
- exist, inCache := ctx.RenderHelper.shaExistCache[hash]
- if !inCache {
- if ctx.RenderHelper.gitRepo == nil {
- var err error
- var closer io.Closer
- ctx.RenderHelper.gitRepo, closer, err = gitrepo.RepositoryFromContextOrOpen(ctx, ctx.RenderHelper.repoFacade)
- if err != nil {
- log.Error("unable to open repository: %s Error: %v", gitrepo.RepoGitURL(ctx.RenderHelper.repoFacade), err)
- return
- }
- ctx.AddCancel(func() {
- _ = closer.Close()
- ctx.RenderHelper.gitRepo = nil
- })
- }
-
- // Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
- exist = ctx.RenderHelper.gitRepo.IsReferenceExist(hash)
- ctx.RenderHelper.shaExistCache[hash] = exist
- }
-
- if !exist {
+ if !ctx.RenderHelper.IsCommitIDExisting(hash) {
start = m[3]
continue
}
- link := util.URLJoin(ctx.RenderOptions.Links.Prefix(), ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], "commit", hash)
+ link := ctx.RenderHelper.ResolveLink(util.URLJoin(ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], "commit", hash), LinkTypeApp)
replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit"))
start = 0
node = node.NextSibling.NextSibling
diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go
index 7b143664fe81d..7f2057a3438eb 100644
--- a/modules/markup/html_internal_test.go
+++ b/modules/markup/html_internal_test.go
@@ -4,7 +4,6 @@
package markup
import (
- "context"
"fmt"
"strconv"
"strings"
@@ -34,8 +33,7 @@ func numericIssueLink(baseURL, class string, index int, marker string) string {
// link an HTML link
func link(href, class, contents string) string {
- extra := ` data-markdown-generated-content=""`
- extra += util.Iif(class != "", ` class="`+class+`"`, "")
+ extra := util.Iif(class != "", ` class="`+class+`"`, "")
return fmt.Sprintf(` %s `, href, extra, contents)
}
@@ -69,22 +67,11 @@ var localMetas = map[string]string{
"markupAllowShortIssuePattern": "true",
}
-var localWikiMetas = map[string]string{
- "user": "test-owner",
- "repo": "test-repo",
- "markupContentMode": "wiki",
-}
-
func TestRender_IssueIndexPattern(t *testing.T) {
// numeric: render inputs without valid mentions
test := func(s string) {
- testRenderIssueIndexPattern(t, s, s, &RenderContext{
- ctx: context.Background(),
- })
- testRenderIssueIndexPattern(t, s, s, &RenderContext{
- ctx: context.Background(),
- RenderOptions: RenderOptions{Metas: numericMetas},
- })
+ testRenderIssueIndexPattern(t, s, s, NewTestRenderContext())
+ testRenderIssueIndexPattern(t, s, s, NewTestRenderContext(numericMetas))
}
// should not render anything when there are no mentions
@@ -132,10 +119,7 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
links[i] = numericIssueLink(util.URLJoin(TestRepoURL, path), "ref-issue", index, marker)
}
expectedNil := fmt.Sprintf(expectedFmt, links...)
- testRenderIssueIndexPattern(t, s, expectedNil, &RenderContext{
- ctx: context.Background(),
- RenderOptions: RenderOptions{Metas: localMetas},
- })
+ testRenderIssueIndexPattern(t, s, expectedNil, NewTestRenderContext(TestAppURL, localMetas))
class := "ref-issue"
if isExternal {
@@ -146,10 +130,7 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
links[i] = numericIssueLink(prefix, class, index, marker)
}
expectedNum := fmt.Sprintf(expectedFmt, links...)
- testRenderIssueIndexPattern(t, s, expectedNum, &RenderContext{
- ctx: context.Background(),
- RenderOptions: RenderOptions{Metas: numericMetas},
- })
+ testRenderIssueIndexPattern(t, s, expectedNum, NewTestRenderContext(TestAppURL, numericMetas))
}
// should render freestanding mentions
@@ -183,10 +164,7 @@ func TestRender_IssueIndexPattern3(t *testing.T) {
// alphanumeric: render inputs without valid mentions
test := func(s string) {
- testRenderIssueIndexPattern(t, s, s, &RenderContext{
- ctx: context.Background(),
- RenderOptions: RenderOptions{Metas: alphanumericMetas},
- })
+ testRenderIssueIndexPattern(t, s, s, NewTestRenderContext(alphanumericMetas))
}
test("")
test("this is a test")
@@ -216,10 +194,7 @@ func TestRender_IssueIndexPattern4(t *testing.T) {
links[i] = externalIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue ref-external-issue", name)
}
expected := fmt.Sprintf(expectedFmt, links...)
- testRenderIssueIndexPattern(t, s, expected, &RenderContext{
- ctx: context.Background(),
- RenderOptions: RenderOptions{Metas: alphanumericMetas},
- })
+ testRenderIssueIndexPattern(t, s, expected, NewTestRenderContext(alphanumericMetas))
}
test("OTT-1234 test", "%s test", "OTT-1234")
test("test T-12 issue", "test %s issue", "T-12")
@@ -239,10 +214,7 @@ func TestRender_IssueIndexPattern5(t *testing.T) {
}
expected := fmt.Sprintf(expectedFmt, links...)
- testRenderIssueIndexPattern(t, s, expected, &RenderContext{
- ctx: context.Background(),
- RenderOptions: RenderOptions{Metas: metas},
- })
+ testRenderIssueIndexPattern(t, s, expected, NewTestRenderContext(metas))
}
test("abc ISSUE-123 def", "abc %s def",
@@ -263,10 +235,7 @@ func TestRender_IssueIndexPattern5(t *testing.T) {
[]string{"ISSUE-123"},
)
- testRenderIssueIndexPattern(t, "will not match", "will not match", &RenderContext{
- ctx: context.Background(),
- RenderOptions: RenderOptions{Metas: regexpMetas},
- })
+ testRenderIssueIndexPattern(t, "will not match", "will not match", NewTestRenderContext(regexpMetas))
}
func TestRender_IssueIndexPattern_NoShortPattern(t *testing.T) {
@@ -278,18 +247,9 @@ func TestRender_IssueIndexPattern_NoShortPattern(t *testing.T) {
"style": IssueNameStyleNumeric,
}
- testRenderIssueIndexPattern(t, "#1", "#1", &RenderContext{
- ctx: context.Background(),
- RenderOptions: RenderOptions{Metas: metas},
- })
- testRenderIssueIndexPattern(t, "#1312", "#1312", &RenderContext{
- ctx: context.Background(),
- RenderOptions: RenderOptions{Metas: metas},
- })
- testRenderIssueIndexPattern(t, "!1", "!1", &RenderContext{
- ctx: context.Background(),
- RenderOptions: RenderOptions{Metas: metas},
- })
+ testRenderIssueIndexPattern(t, "#1", "#1", NewTestRenderContext(metas))
+ testRenderIssueIndexPattern(t, "#1312", "#1312", NewTestRenderContext(metas))
+ testRenderIssueIndexPattern(t, "!1", "!1", NewTestRenderContext(metas))
}
func TestRender_RenderIssueTitle(t *testing.T) {
@@ -300,20 +260,12 @@ func TestRender_RenderIssueTitle(t *testing.T) {
"repo": "someRepo",
"style": IssueNameStyleNumeric,
}
- actual, err := RenderIssueTitle(&RenderContext{
- ctx: context.Background(),
- RenderOptions: RenderOptions{Metas: metas},
- }, "#1")
+ actual, err := RenderIssueTitle(NewTestRenderContext(metas), "#1")
assert.NoError(t, err)
assert.Equal(t, "#1", actual)
}
func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) {
- ctx.RenderOptions.Links.AbsolutePrefix = true
- if ctx.RenderOptions.Links.Base == "" {
- ctx.RenderOptions.Links.Base = TestRepoURL
- }
-
var buf strings.Builder
err := postProcess(ctx, []processor{issueIndexPatternProcessor}, strings.NewReader(input), &buf)
assert.NoError(t, err)
@@ -325,20 +277,12 @@ func TestRender_AutoLink(t *testing.T) {
test := func(input, expected string) {
var buffer strings.Builder
- err := PostProcess(&RenderContext{
- ctx: context.Background(),
-
- RenderOptions: RenderOptions{Metas: localMetas, Links: Links{Base: TestRepoURL}},
- }, strings.NewReader(input), &buffer)
+ err := PostProcess(NewTestRenderContext(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: context.Background(),
-
- RenderOptions: RenderOptions{Metas: localWikiMetas, Links: Links{Base: TestRepoURL}},
- }, strings.NewReader(input), &buffer)
+ err = PostProcess(NewTestRenderContext(localMetas), strings.NewReader(input), &buffer)
assert.Equal(t, err, nil)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
}
@@ -360,14 +304,10 @@ func TestRender_AutoLink(t *testing.T) {
func TestRender_FullIssueURLs(t *testing.T) {
setting.AppURL = TestAppURL
- defer testModule.MockVariableValue(&RenderBehaviorForTesting.DisableInternalAttributes, true)()
+ defer testModule.MockVariableValue(&RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
test := func(input, expected string) {
var result strings.Builder
- err := postProcess(&RenderContext{
- ctx: context.Background(),
-
- RenderOptions: RenderOptions{Metas: localMetas, Links: Links{Base: TestRepoURL}},
- }, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result)
+ err := postProcess(NewTestRenderContext(localMetas), []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result)
assert.NoError(t, err)
assert.Equal(t, expected, result.String())
}
diff --git a/modules/markup/html_issue.go b/modules/markup/html_issue.go
index a75a8b3290066..e64ec76c3d2fc 100644
--- a/modules/markup/html_issue.go
+++ b/modules/markup/html_issue.go
@@ -136,9 +136,11 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
// Gitea will redirect on click as appropriate.
issuePath := util.Iif(ref.IsPull, "pulls", "issues")
if ref.Owner == "" {
- link = createLink(ctx, util.URLJoin(ctx.RenderOptions.Links.Prefix(), ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], issuePath, ref.Issue), reftext, "ref-issue")
+ linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], issuePath, ref.Issue), LinkTypeApp)
+ link = createLink(ctx, linkHref, reftext, "ref-issue")
} else {
- link = createLink(ctx, util.URLJoin(ctx.RenderOptions.Links.Prefix(), ref.Owner, ref.Name, issuePath, ref.Issue), reftext, "ref-issue")
+ linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(ref.Owner, ref.Name, issuePath, ref.Issue), LinkTypeApp)
+ link = createLink(ctx, linkHref, reftext, "ref-issue")
}
}
@@ -177,7 +179,8 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
}
reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
- link := createLink(ctx, util.URLJoin(ctx.RenderOptions.Links.Prefix(), ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit")
+ linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(ref.Owner, ref.Name, "commit", ref.CommitSha), LinkTypeApp)
+ link := createLink(ctx, linkHref, reftext, "commit")
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
node = node.NextSibling.NextSibling
diff --git a/modules/markup/html_link.go b/modules/markup/html_link.go
index f6700161b53f8..5fd38b63cd370 100644
--- a/modules/markup/html_link.go
+++ b/modules/markup/html_link.go
@@ -6,37 +6,14 @@ package markup
import (
"net/url"
"path"
- "path/filepath"
"strings"
"code.gitea.io/gitea/modules/markup/common"
- "code.gitea.io/gitea/modules/util"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)
-func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (result string, resolved bool) {
- isAnchorFragment := link != "" && link[0] == '#'
- if !isAnchorFragment && !IsFullURLString(link) {
- linkBase := ctx.RenderOptions.Links.Base
- if ctx.IsMarkupContentWiki() {
- // no need to check if the link should be resolved as a wiki link or a wiki raw link
- // just use wiki link here, and it will be redirected to a wiki raw link if necessary
- linkBase = ctx.RenderOptions.Links.WikiLink()
- } else if ctx.RenderOptions.Links.BranchPath != "" || ctx.RenderOptions.Links.TreePath != "" {
- // if there is no BranchPath, then the link will be something like "/owner/repo/src/{the-file-path}"
- // and then this link will be handled by the "legacy-ref" code and be redirected to the default branch like "/owner/repo/src/branch/main/{the-file-path}"
- linkBase = ctx.RenderOptions.Links.SrcLink()
- }
- link, resolved = util.URLJoin(linkBase, link), true
- }
- if isAnchorFragment && userContentAnchorPrefix != "" {
- link, resolved = userContentAnchorPrefix+link[1:], true
- }
- return link, resolved
-}
-
func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
@@ -116,7 +93,7 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
name += tail
image := false
- ext := filepath.Ext(link)
+ ext := path.Ext(link)
switch ext {
// fast path: empty string, ignore
case "":
@@ -139,6 +116,7 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
if image {
link = strings.ReplaceAll(link, " ", "+")
} else {
+ // the hacky wiki name encoding: space to "-"
link = strings.ReplaceAll(link, " ", "-") // FIXME: it should support dashes in the link, eg: "the-dash-support.-"
}
if !strings.Contains(link, "/") {
@@ -146,9 +124,7 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
}
}
if image {
- if !absoluteLink {
- link = util.URLJoin(ctx.RenderOptions.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()), link)
- }
+ link = ctx.RenderHelper.ResolveLink(link, LinkTypeMedia)
title := props["title"]
if title == "" {
title = props["alt"]
@@ -174,7 +150,7 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
childNode.Attr = childNode.Attr[:2]
}
} else {
- link, _ = ResolveLink(ctx, link, "")
+ link = ctx.RenderHelper.ResolveLink(link, LinkTypeDefault)
childNode.Type = html.TextNode
childNode.Data = name
}
diff --git a/modules/markup/html_mention.go b/modules/markup/html_mention.go
index 4243eeb20f93b..fffa12e7b740e 100644
--- a/modules/markup/html_mention.go
+++ b/modules/markup/html_mention.go
@@ -33,7 +33,8 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
if ok && strings.Contains(mention, "/") {
mentionOrgAndTeam := strings.Split(mention, "/")
if mentionOrgAndTeam[0][1:] == ctx.RenderOptions.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") {
- replaceContent(node, loc.Start, loc.End, createLink(ctx, util.URLJoin(ctx.RenderOptions.Links.Prefix(), "org", ctx.RenderOptions.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "" /*mention*/))
+ link := ctx.RenderHelper.ResolveLink(util.URLJoin("org", ctx.RenderOptions.Metas["org"], "teams", mentionOrgAndTeam[1]), LinkTypeApp)
+ replaceContent(node, loc.Start, loc.End, createLink(ctx, link, mention, "" /*mention*/))
node = node.NextSibling.NextSibling
start = 0
continue
@@ -43,8 +44,9 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
}
mentionedUsername := mention[1:]
- if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx, mentionedUsername) {
- replaceContent(node, loc.Start, loc.End, createLink(ctx, util.URLJoin(ctx.RenderOptions.Links.Prefix(), mentionedUsername), mention, "" /*mention*/))
+ if DefaultRenderHelperFuncs != nil && DefaultRenderHelperFuncs.IsUsernameMentionable(ctx, mentionedUsername) {
+ link := ctx.RenderHelper.ResolveLink(mentionedUsername, LinkTypeApp)
+ replaceContent(node, loc.Start, loc.End, createLink(ctx, link, mention, "" /*mention*/))
node = node.NextSibling.NextSibling
start = 0
} else {
diff --git a/modules/markup/html_node.go b/modules/markup/html_node.go
index a7c323fcba8a9..6e8ca67900b0a 100644
--- a/modules/markup/html_node.go
+++ b/modules/markup/html_node.go
@@ -4,8 +4,6 @@
package markup
import (
- "code.gitea.io/gitea/modules/util"
-
"golang.org/x/net/html"
)
@@ -17,7 +15,7 @@ func visitNodeImg(ctx *RenderContext, img *html.Node) (next *html.Node) {
}
if IsNonEmptyRelativePath(attr.Val) {
- attr.Val = util.URLJoin(ctx.RenderOptions.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()), attr.Val)
+ attr.Val = ctx.RenderHelper.ResolveLink(attr.Val, LinkTypeMedia)
// By default, the " " tag should also be clickable,
// because frontend use ` ` to paste the re-scaled image into the markdown,
@@ -53,7 +51,7 @@ func visitNodeVideo(ctx *RenderContext, node *html.Node) (next *html.Node) {
continue
}
if IsNonEmptyRelativePath(attr.Val) {
- attr.Val = util.URLJoin(ctx.RenderOptions.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()), attr.Val)
+ attr.Val = ctx.RenderHelper.ResolveLink(attr.Val, LinkTypeMedia)
}
attr.Val = camoHandleLink(attr.Val)
node.Attr[i] = attr
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index 7366965a9d5ce..f806f66d11200 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -9,7 +9,6 @@ import (
"testing"
"code.gitea.io/gitea/modules/emoji"
- "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
@@ -22,44 +21,13 @@ import (
var (
testRepoOwnerName = "user13"
testRepoName = "repo11"
- localMetas = map[string]string{
- "user": testRepoOwnerName,
- "repo": testRepoName,
- }
- localWikiMetas = map[string]string{
- "user": testRepoOwnerName,
- "repo": testRepoName,
- "markupContentMode": "wiki",
- }
+ localMetas = map[string]string{"user": testRepoOwnerName, "repo": testRepoName}
)
-type mockRepo struct {
- OwnerName string
- RepoName string
-}
-
-func (m *mockRepo) GetOwnerName() string {
- return m.OwnerName
-}
-
-func (m *mockRepo) GetName() string {
- return m.RepoName
-}
-
-func newMockRepo(ownerName, repoName string) gitrepo.Repository {
- return &mockRepo{
- OwnerName: ownerName,
- RepoName: repoName,
- }
-}
-
func TestRender_Commits(t *testing.T) {
- setting.AppURL = markup.TestAppURL
test := func(input, expected string) {
- buffer, err := markup.RenderString(markup.NewTestRenderContext("a.md", localMetas, newMockRepo(testRepoOwnerName, testRepoName), markup.Links{
- AbsolutePrefix: true,
- Base: markup.TestRepoURL,
- }), input)
+ rctx := markup.NewTestRenderContext(markup.TestAppURL, localMetas).WithRelativePath("a.md")
+ buffer, err := markup.RenderString(rctx, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
@@ -102,14 +70,10 @@ func TestRender_Commits(t *testing.T) {
}
func TestRender_CrossReferences(t *testing.T) {
- setting.AppURL = markup.TestAppURL
- defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
+ defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
test := func(input, expected string) {
- buffer, err := markup.RenderString(markup.NewTestRenderContext("a.md", localMetas,
- markup.Links{
- AbsolutePrefix: true,
- Base: setting.AppSubURL,
- }), input)
+ rctx := markup.NewTestRenderContext(markup.TestAppURL, localMetas).WithRelativePath("a.md")
+ buffer, err := markup.RenderString(rctx, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
@@ -141,9 +105,9 @@ func TestRender_CrossReferences(t *testing.T) {
func TestRender_links(t *testing.T) {
setting.AppURL = markup.TestAppURL
- defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
+ defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
test := func(input, expected string) {
- buffer, err := markup.RenderString(markup.NewTestRenderContext("a.md", markup.Links{Base: markup.TestRepoURL}), input)
+ buffer, err := markup.RenderString(markup.NewTestRenderContext().WithRelativePath("a.md"), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
@@ -246,9 +210,9 @@ func TestRender_links(t *testing.T) {
func TestRender_email(t *testing.T) {
setting.AppURL = markup.TestAppURL
- defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
+ defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
test := func(input, expected string) {
- res, err := markup.RenderString(markup.NewTestRenderContext("a.md", markup.Links{Base: markup.TestRepoURL}), input)
+ res, err := markup.RenderString(markup.NewTestRenderContext().WithRelativePath("a.md"), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
}
@@ -315,7 +279,7 @@ func TestRender_emoji(t *testing.T) {
test := func(input, expected string) {
expected = strings.ReplaceAll(expected, "&", "&")
- buffer, err := markup.RenderString(markup.NewTestRenderContext("a.md", markup.Links{Base: markup.TestRepoURL}), input)
+ buffer, err := markup.RenderString(markup.NewTestRenderContext().WithRelativePath("a.md"), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
@@ -374,188 +338,133 @@ func TestRender_ShortLinks(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.NewTestRenderContext(markup.Links{Base: markup.TestRepoURL, BranchPath: "master"}), input)
+ test := func(input, expected string) {
+ buffer, err := markdown.RenderString(markup.NewTestRenderContext(tree), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
- buffer, err = markdown.RenderString(markup.NewTestRenderContext(markup.Links{Base: markup.TestRepoURL}, localWikiMetas), input)
- assert.NoError(t, err)
- assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer)))
}
- 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(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")
- imgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link.jpg")
- otherImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+Other.jpg")
- encodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+%23.jpg")
- notencodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg")
+ imgurl := util.URLJoin(tree, "Link.jpg")
+ otherImgurl := util.URLJoin(tree, "Link+Other.jpg")
+ encodedImgurl := util.URLJoin(tree, "Link+%23.jpg")
+ notencodedImgurl := util.URLJoin(tree, "some", "path", "Link+#.jpg")
renderableFileURL := util.URLJoin(tree, "markdown_file.md")
- renderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "markdown_file.md")
unrenderableFileURL := util.URLJoin(tree, "file.zip")
- unrenderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "file.zip")
favicon := "http://google.com/favicon.ico"
test(
"[[Link]]",
`Link
`,
- `Link
`)
+ )
test(
"[[Link.-]]",
`Link.-
`,
- `Link.-
`)
+ )
test(
"[[Link.jpg]]",
`
`,
- `
`)
+ )
test(
"[["+favicon+"]]",
`
`,
- `
`)
+ )
test(
"[[Name|Link]]",
`Name
`,
- `Name
`)
+ )
test(
"[[Name|Link.jpg]]",
`
`,
- `
`)
+ )
test(
"[[Name|Link.jpg|alt=AltName]]",
`
`,
- `
`)
+ )
test(
"[[Name|Link.jpg|title=Title]]",
`
`,
- `
`)
+ )
test(
"[[Name|Link.jpg|alt=AltName|title=Title]]",
`
`,
- `
`)
+ )
test(
"[[Name|Link.jpg|alt=\"AltName\"|title='Title']]",
`
`,
- `
`)
+ )
test(
"[[Name|Link Other.jpg|alt=\"AltName\"|title='Title']]",
`
`,
- `
`)
+ )
test(
"[[Link]] [[Other Link]]",
`Link Other Link
`,
- `Link Other Link
`)
+ )
test(
"[[Link?]]",
`Link?
`,
- `Link?
`)
+ )
test(
"[[Link]] [[Other Link]] [[Link?]]",
`Link Other Link Link?
`,
- `Link Other Link Link?
`)
+ )
test(
"[[markdown_file.md]]",
`markdown_file.md
`,
- `markdown_file.md
`)
+ )
test(
"[[file.zip]]",
`file.zip
`,
- `file.zip
`)
+ )
test(
"[[Link #.jpg]]",
`
`,
- `
`)
+ )
test(
"[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]",
`
`,
- `
`)
+ )
test(
"[[some/path/Link #.jpg]]",
`
`,
- `
`)
+ )
test(
"[[foobar]]
",
`[[foobar]]
`,
- `[[foobar]]
`)
-}
-
-func TestRender_RelativeMedias(t *testing.T) {
- render := func(input string, isWiki bool, links markup.Links) string {
- buffer, err := markdown.RenderString(markup.NewTestRenderContext(links, util.Iif(isWiki, localWikiMetas, localMetas)), input)
- assert.NoError(t, err)
- return strings.TrimSpace(string(buffer))
- }
-
- out := render(` `, false, markup.Links{Base: "/test-owner/test-repo"})
- assert.Equal(t, ` `, out)
-
- out = render(` `, true, markup.Links{Base: "/test-owner/test-repo"})
- assert.Equal(t, ` `, out)
-
- out = render(` `, false, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"})
- assert.Equal(t, ` `, out)
-
- out = render(` `, true, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"})
- assert.Equal(t, ` `, out)
-
- out = render(` `, true, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"})
- assert.Equal(t, ` `, out)
-
- out = render(``, false, markup.Links{Base: "/test-owner/test-repo"})
- assert.Equal(t, ` `, out)
-
- out = render(``, true, markup.Links{Base: "/test-owner/test-repo"})
- assert.Equal(t, ` `, out)
-
- out = render(``, false, markup.Links{Base: "/test-owner/test-repo"})
- assert.Equal(t, ` `, out)
+ )
}
func Test_ParseClusterFuzz(t *testing.T) {
setting.AppURL = markup.TestAppURL
- localMetas := map[string]string{
- "user": "go-gitea",
- "repo": "gitea",
- }
+ localMetas := map[string]string{"user": "go-gitea", "repo": "gitea"}
data := "https://google.com/ `
- test(" ", googleRendered, googleRendered)
-
- lnk := util.URLJoin(FullURL, "WikiPage")
- lnkWiki := util.URLJoin(FullURL, "wiki", "WikiPage")
- test("[WikiPage](WikiPage)",
- `WikiPage
`,
- `WikiPage
`)
+ test(" ", googleRendered)
+ test("[Link](Link)", `Link
`)
}
func TestRender_Images(t *testing.T) {
setting.AppURL = AppURL
test := func(input, expected string) {
- buffer, err := markdown.RenderString(markup.NewTestRenderContext(markup.Links{Base: FullURL}), input)
+ buffer, err := markdown.RenderString(markup.NewTestRenderContext(FullURL), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
}
@@ -122,12 +85,12 @@ func TestRender_Images(t *testing.T) {
`
`)
}
-func testAnswers(baseURLContent, baseURLImages string) []string {
+func testAnswers(baseURL string) []string {
return []string{
`Wiki! Enjoy :)
See commit 65f1bf27bc
Ideas and codes
@@ -135,8 +98,8 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
Bezier widget (by @r-lyeh ) ocornut/imgui#786
Bezier widget (by @r-lyeh ) #786
Node graph editors https://github.com/ocornut/imgui/issues/306
-Memory Editor
-Plot var helper
+Memory Editor
+Plot var helper
`,
`What is Wine Staging?
@@ -146,14 +109,14 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
@@ -161,9 +124,9 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
`Excelsior JET allows you to create native executables for Windows, Linux and Mac OS X.
Package your libGDX application
-
+
Perform a test run by hitting the Run! button.
-
+
More tests
(from https://www.markdownguide.org/extended-syntax/ )
@@ -284,66 +247,20 @@ This PR has been generated by [Renovate Bot](https://github.com/renovatebot/reno
`,
}
-func TestTotal_RenderWiki(t *testing.T) {
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)()
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
- setting.AppURL = AppURL
- answers := testAnswers(util.URLJoin(FullURL, "wiki"), util.URLJoin(FullURL, "wiki", "raw"))
- for i := 0; i < len(sameCases); i++ {
- line, err := markdown.RenderString(markup.NewTestRenderContext(
- markup.Links{Base: FullURL},
- newMockRepo(testRepoOwnerName, testRepoName),
- localWikiMetas,
- ), sameCases[i])
- assert.NoError(t, err)
- assert.Equal(t, answers[i], string(line))
- }
-
- testCases := []string{
- // Guard wiki sidebar: special syntax
- `[[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]]`,
- // rendered
- `Guardfile-DSL / Configuring-Guard
-`,
- // special syntax
- `[[Name|Link]]`,
- // rendered
- `Name
-`,
- }
-
- for i := 0; i < len(testCases); i += 2 {
- line, err := markdown.RenderString(markup.NewTestRenderContext(markup.Links{Base: FullURL}, localWikiMetas), testCases[i])
- assert.NoError(t, err)
- assert.EqualValues(t, testCases[i+1], string(line))
- }
-}
-
func TestTotal_RenderString(t *testing.T) {
defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)()
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
- setting.AppURL = AppURL
- answers := testAnswers(util.URLJoin(FullURL, "src", "master"), util.URLJoin(FullURL, "media", "master"))
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
+ markup.Init(&markup.RenderHelperFuncs{
+ IsUsernameMentionable: func(ctx context.Context, username string) bool {
+ return username == "r-lyeh"
+ },
+ })
+ answers := testAnswers("")
for i := 0; i < len(sameCases); i++ {
- line, err := markdown.RenderString(markup.NewTestRenderContext(
- markup.Links{
- Base: FullURL,
- BranchPath: "master",
- },
- newMockRepo(testRepoOwnerName, testRepoName),
- localMetas,
- ), sameCases[i])
+ line, err := markdown.RenderString(markup.NewTestRenderContext(localMetas), sameCases[i])
assert.NoError(t, err)
assert.Equal(t, answers[i], string(line))
}
-
- testCases := []string{}
-
- for i := 0; i < len(testCases); i += 2 {
- line, err := markdown.RenderString(markup.NewTestRenderContext(markup.Links{Base: FullURL}), testCases[i])
- assert.NoError(t, err)
- assert.Equal(t, template.HTML(testCases[i+1]), line)
- }
}
func TestRender_RenderParagraphs(t *testing.T) {
@@ -609,13 +526,9 @@ mail@domain.com
`
input = strings.ReplaceAll(input, "${SPACE}", " ") // replace ${SPACE} with " ", to avoid some editor's auto-trimming
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
@@ -638,339 +551,14 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
@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
-
-
-
-
-
-
-88fc37a3c0...12fc37a3c0 (hash)
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-88fc37a3c0
-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
-
-
-
-
-
-
-88fc37a3c0...12fc37a3c0 (hash)
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-88fc37a3c0
-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
-
-
-
-
-
-
-88fc37a3c0...12fc37a3c0 (hash)
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-88fc37a3c0
-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
-
-
-
-
-
-
-88fc37a3c0...12fc37a3c0 (hash)
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-88fc37a3c0
-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
-
-
-
-
-
-
-88fc37a3c0...12fc37a3c0 (hash)
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-88fc37a3c0
-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
-
-
-
-
-
-
-88fc37a3c0...12fc37a3c0 (hash)
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-88fc37a3c0
-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
-
-
-
-
-
-
-88fc37a3c0...12fc37a3c0 (hash)
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-88fc37a3c0
-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
-
-
-
-
-
-
-88fc37a3c0...12fc37a3c0 (hash)
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-88fc37a3c0
-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
-
-
-
-
-
-
-88fc37a3c0...12fc37a3c0 (hash)
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-88fc37a3c0
-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
-
-
-
-
-
-
-88fc37a3c0...12fc37a3c0 (hash)
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-88fc37a3c0
-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
-
-
-
-
-
-
-88fc37a3c0...12fc37a3c0 (hash)
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-88fc37a3c0
-com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
-👍
-mail@domain.com
-@mention-user test
-#123
-space
`,
},
}
defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)()
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
for i, c := range cases {
- result, err := markdown.RenderString(markup.NewTestRenderContext(c.Links, util.Iif(c.IsWiki, map[string]string{"markupContentMode": "wiki"}, map[string]string{})), input)
+ result, err := markdown.RenderString(markup.NewTestRenderContext(localMetas), input)
assert.NoError(t, err, "Unexpected error in testcase: %v", i)
assert.Equal(t, c.Expected, string(result), "Unexpected result in testcase %v", i)
}
diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go
index c2cbffc1c187b..36512e59a8d43 100644
--- a/modules/markup/markdown/transform_image.go
+++ b/modules/markup/markdown/transform_image.go
@@ -4,10 +4,7 @@
package markdown
import (
- "strings"
-
"code.gitea.io/gitea/modules/markup"
- giteautil "code.gitea.io/gitea/modules/util"
"github.com/yuin/goldmark/ast"
)
@@ -20,10 +17,7 @@ func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image)
// Check if the destination is a real link
if len(v.Destination) > 0 && !markup.IsFullURLBytes(v.Destination) {
- v.Destination = []byte(giteautil.URLJoin(
- ctx.RenderOptions.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()),
- strings.TrimLeft(string(v.Destination), "/"),
- ))
+ v.Destination = []byte(ctx.RenderHelper.ResolveLink(string(v.Destination), markup.LinkTypeMedia))
}
parent := v.Parent()
diff --git a/modules/markup/markdown/transform_link.go b/modules/markup/markdown/transform_link.go
index 38fbf693ab837..51c2c915d8336 100644
--- a/modules/markup/markdown/transform_link.go
+++ b/modules/markup/markdown/transform_link.go
@@ -9,8 +9,19 @@ import (
"github.com/yuin/goldmark/ast"
)
+func resolveLink(ctx *markup.RenderContext, link, userContentAnchorPrefix string) (result string, resolved bool) {
+ isAnchorFragment := link != "" && link[0] == '#'
+ if !isAnchorFragment && !markup.IsFullURLString(link) {
+ link, resolved = ctx.RenderHelper.ResolveLink(link, markup.LinkTypeDefault), true
+ }
+ if isAnchorFragment && userContentAnchorPrefix != "" {
+ link, resolved = userContentAnchorPrefix+link[1:], true
+ }
+ return link, resolved
+}
+
func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link) {
- if link, resolved := markup.ResolveLink(ctx, string(v.Destination), "#user-content-"); resolved {
+ if link, resolved := resolveLink(ctx, string(v.Destination), "#user-content-"); resolved {
v.Destination = []byte(link)
}
}
diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go
index cf719cf4e906e..31257351ae823 100644
--- a/modules/markup/orgmode/orgmode.go
+++ b/modules/markup/orgmode/orgmode.go
@@ -13,7 +13,6 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/lexers"
@@ -142,19 +141,11 @@ func (r *Writer) resolveLink(kind, link string) string {
// so we need to try to guess the link kind again here
kind = org.RegularLink{URL: link}.Kind()
}
-
- base := r.Ctx.RenderOptions.Links.Base
- if r.Ctx.IsMarkupContentWiki() {
- base = r.Ctx.RenderOptions.Links.WikiLink()
- } else if r.Ctx.RenderOptions.Links.HasBranchInfo() {
- base = r.Ctx.RenderOptions.Links.SrcLink()
- }
-
if kind == "image" || kind == "video" {
- base = r.Ctx.RenderOptions.Links.ResolveMediaLink(r.Ctx.IsMarkupContentWiki())
+ link = r.Ctx.RenderHelper.ResolveLink(link, markup.LinkTypeMedia)
+ } else {
+ link = r.Ctx.RenderHelper.ResolveLink(link, markup.LinkTypeDefault)
}
-
- link = util.URLJoin(base, link)
}
return link
}
diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go
index 4048ae2475ff7..d30df3b1886f5 100644
--- a/modules/markup/orgmode/orgmode_test.go
+++ b/modules/markup/orgmode/orgmode_test.go
@@ -10,7 +10,6 @@ import (
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@@ -22,34 +21,21 @@ func TestMain(m *testing.M) {
}
func TestRender_StandardLinks(t *testing.T) {
- test := func(input, expected string, isWiki bool) {
- buffer, err := RenderString(markup.NewTestRenderContext(
- markup.Links{
- Base: "/relative-path",
- BranchPath: "branch/main",
- },
- map[string]string{"markupContentMode": util.Iif(isWiki, "wiki", "")},
- ), input)
+ test := func(input, expected string) {
+ buffer, err := RenderString(markup.NewTestRenderContext("/relative-path/media/branch/main/"), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
test("[[https://google.com/]]",
- `https://google.com/
`, false)
- test("[[WikiPage][The WikiPage Desc]]",
- `The WikiPage Desc
`, true)
+ `https://google.com/
`)
test("[[ImageLink.svg][The Image Desc]]",
- `The Image Desc
`, false)
+ `The Image Desc
`)
}
func TestRender_InternalLinks(t *testing.T) {
test := func(input, expected string) {
- buffer, err := RenderString(markup.NewTestRenderContext(
- markup.Links{
- Base: "/relative-path",
- BranchPath: "branch/main",
- },
- ), input)
+ buffer, err := RenderString(markup.NewTestRenderContext("/relative-path/src/branch/main"), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
@@ -66,7 +52,7 @@ func TestRender_InternalLinks(t *testing.T) {
func TestRender_Media(t *testing.T) {
test := func(input, expected string) {
- buffer, err := RenderString(markup.NewTestRenderContext(markup.Links{Base: "./relative-path"}), input)
+ buffer, err := RenderString(markup.NewTestRenderContext("./relative-path"), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
diff --git a/modules/markup/render.go b/modules/markup/render.go
index e251f47fc9107..be75d08c8c323 100644
--- a/modules/markup/render.go
+++ b/modules/markup/render.go
@@ -11,8 +11,6 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/markup/internal"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -38,12 +36,14 @@ var RenderBehaviorForTesting struct {
// * However, many places render the content without setting "mode" in Metas, all these places used comment line break setting incorrectly
ForceHardLineBreak bool
- // Gitea will emit some internal attributes for various purposes, these attributes don't affect rendering.
+ // Gitea will emit some additional attributes for various purposes, these attributes don't affect rendering.
// But there are too many hard-coded test cases, to avoid changing all of them again and again, we can disable emitting these internal attributes.
- DisableInternalAttributes bool
+ DisableAdditionalAttributes bool
}
type RenderOptions struct {
+ UseAbsoluteLink bool
+
// relative path from tree root of the branch
RelativePath string
@@ -51,12 +51,9 @@ type RenderOptions struct {
// for file mode, it could be left as empty, and will be detected by file extension in RelativePath
MarkupType string
- // special link references for rendering, especially when there is a branch/tree path
- Links Links
-
// user&repo, format&style®exp (for external issue pattern), teams&org (for mention)
// BranchNameSubURL (for iframe&asciicast)
- // markupAllowShortIssuePattern, markupContentMode (wiki)
+ // markupAllowShortIssuePattern
// markdownLineBreakStyle (comment, document)
Metas map[string]string
@@ -64,13 +61,6 @@ type RenderOptions struct {
InStandalonePage bool
}
-type RenderHelper struct {
- gitRepo *git.Repository
- repoFacade gitrepo.Repository
- shaExistCache map[string]bool
- cancelFn func()
-}
-
// RenderContext represents a render context
type RenderContext struct {
ctx context.Context
@@ -101,7 +91,7 @@ func (ctx *RenderContext) Value(key any) any {
var _ context.Context = (*RenderContext)(nil)
func NewRenderContext(ctx context.Context) *RenderContext {
- return &RenderContext{ctx: ctx}
+ return &RenderContext{ctx: ctx, RenderHelper: &SimpleRenderHelper{}}
}
func (ctx *RenderContext) WithMarkupType(typ string) *RenderContext {
@@ -114,11 +104,6 @@ func (ctx *RenderContext) WithRelativePath(path string) *RenderContext {
return ctx
}
-func (ctx *RenderContext) WithLinks(links Links) *RenderContext {
- ctx.RenderOptions.Links = links
- return ctx
-}
-
func (ctx *RenderContext) WithMetas(metas map[string]string) *RenderContext {
ctx.RenderOptions.Metas = metas
return ctx
@@ -129,48 +114,16 @@ func (ctx *RenderContext) WithInStandalonePage(v bool) *RenderContext {
return ctx
}
-func (ctx *RenderContext) WithGitRepo(r *git.Repository) *RenderContext {
- ctx.RenderHelper.gitRepo = r
+func (ctx *RenderContext) WithUseAbsoluteLink(v bool) *RenderContext {
+ ctx.RenderOptions.UseAbsoluteLink = v
return ctx
}
-func (ctx *RenderContext) WithRepoFacade(r gitrepo.Repository) *RenderContext {
- ctx.RenderHelper.repoFacade = r
+func (ctx *RenderContext) WithHelper(helper RenderHelper) *RenderContext {
+ ctx.RenderHelper = helper
return ctx
}
-// Cancel runs any cleanup functions that have been registered for this Ctx
-func (ctx *RenderContext) Cancel() {
- if ctx == nil {
- return
- }
- ctx.RenderHelper.shaExistCache = map[string]bool{}
- if ctx.RenderHelper.cancelFn == nil {
- return
- }
- ctx.RenderHelper.cancelFn()
-}
-
-// AddCancel adds the provided fn as a Cleanup for this Ctx
-func (ctx *RenderContext) AddCancel(fn func()) {
- if ctx == nil {
- return
- }
- oldCancelFn := ctx.RenderHelper.cancelFn
- if oldCancelFn == nil {
- ctx.RenderHelper.cancelFn = fn
- return
- }
- ctx.RenderHelper.cancelFn = func() {
- defer oldCancelFn()
- fn()
- }
-}
-
-func (ctx *RenderContext) IsMarkupContentWiki() bool {
- return ctx.RenderOptions.Metas != nil && ctx.RenderOptions.Metas["markupContentMode"] == "wiki"
-}
-
// Render renders markup file to HTML with all specific handling stuff.
func Render(ctx *RenderContext, input io.Reader, output io.Writer) error {
if ctx.RenderOptions.MarkupType == "" && ctx.RenderOptions.RelativePath != "" {
@@ -237,6 +190,10 @@ func pipes() (io.ReadCloser, io.WriteCloser, func()) {
}
func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error {
+ if ctx.RenderHelper != nil {
+ defer ctx.RenderHelper.CleanUp()
+ }
+
finalProcessor := ctx.RenderInternal.Init(output)
defer finalProcessor.Close()
@@ -278,11 +235,8 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr
}
// Init initializes the render global variables
-func Init(ph *ProcessorHelper) {
- if ph != nil {
- DefaultProcessorHelper = *ph
- }
-
+func Init(renderHelpFuncs *RenderHelperFuncs) {
+ DefaultRenderHelperFuncs = renderHelpFuncs
if len(setting.Markdown.CustomURLSchemes) > 0 {
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
}
@@ -300,23 +254,38 @@ func ComposeSimpleDocumentMetas() map[string]string {
return map[string]string{"markdownLineBreakStyle": "document"}
}
+type TestRenderHelper struct {
+ ctx *RenderContext
+ BaseLink string
+}
+
+func (r *TestRenderHelper) CleanUp() {}
+
+func (r *TestRenderHelper) IsCommitIDExisting(commitID string) bool {
+ return strings.HasPrefix(commitID, "65f1bf2") //|| strings.HasPrefix(commitID, "88fc37a")
+}
+
+func (r *TestRenderHelper) ResolveLink(link string, likeType LinkType) string {
+ return r.ctx.ResolveLinkRelative(r.BaseLink, "", link)
+}
+
+var _ RenderHelper = (*TestRenderHelper)(nil)
+
// NewTestRenderContext is a helper function to create a RenderContext for testing purpose
-// It accepts string (RelativePath), Links, map[string]string (Metas), gitrepo.Repository
-func NewTestRenderContext(a ...any) *RenderContext {
+// It accepts string (BaseLink), map[string]string (Metas)
+func NewTestRenderContext(baseLinkOrMetas ...any) *RenderContext {
if !setting.IsInTesting {
panic("NewTestRenderContext should only be used in testing")
}
- ctx := NewRenderContext(context.Background())
- for _, v := range a {
+ helper := &TestRenderHelper{}
+ ctx := NewRenderContext(context.Background()).WithHelper(helper)
+ helper.ctx = ctx
+ for _, v := range baseLinkOrMetas {
switch v := v.(type) {
case string:
- ctx = ctx.WithRelativePath(v)
- case Links:
- ctx = ctx.WithLinks(v)
+ helper.BaseLink = v
case map[string]string:
ctx = ctx.WithMetas(v)
- case gitrepo.Repository:
- ctx = ctx.WithRepoFacade(v)
default:
panic(fmt.Sprintf("unknown type %T", v))
}
diff --git a/modules/markup/render_helper.go b/modules/markup/render_helper.go
index c1613261bdd4d..82796ef274558 100644
--- a/modules/markup/render_helper.go
+++ b/modules/markup/render_helper.go
@@ -6,16 +6,52 @@ package markup
import (
"context"
"html/template"
+
+ "code.gitea.io/gitea/modules/setting"
+)
+
+type LinkType string
+
+const (
+ LinkTypeApp LinkType = "app" // the link is relative to the AppSubURL
+ LinkTypeDefault LinkType = "default" // the link is relative to the default base (eg: repo link, or current ref tree path)
+ LinkTypeMedia LinkType = "media" // the link should be used to access media files (images, videos)
+ LinkTypeRaw LinkType = "raw" // not really useful, mainly for environment GITEA_PREFIX_RAW for external renders
)
-// ProcessorHelper is a helper for the rendering processors (it could be renamed to RenderHelper in the future).
-// The main purpose of this helper is to decouple some functions which are not directly available in this package.
-type ProcessorHelper struct {
- IsUsernameMentionable func(ctx context.Context, username string) bool
+type RenderHelper interface {
+ CleanUp()
- ElementDir string // the direction of the elements, eg: "ltr", "rtl", "auto", default to no direction attribute
+ // TODO: such dependency is not ideal. We should decouple the processors step by step.
+ // It should make the render choose different processors for different purposes,
+ // but not make processors to guess "is it rendering a comment or a wiki?" or "does it need to check commit ID?"
+ IsCommitIDExisting(commitID string) bool
+ ResolveLink(link string, likeType LinkType) string
+}
+
+// RenderHelperFuncs is used to decouple cycle-import
+// At the moment there are different packages:
+// modules/markup: basic markup rendering
+// models/renderhelper: need to access models and git repo, and models/issues needs it
+// services/markup: some real helper functions could only be provided here because it needs to access various services & templates
+type RenderHelperFuncs struct {
+ IsUsernameMentionable func(ctx context.Context, username string) bool
RenderRepoFileCodePreview func(ctx context.Context, options RenderCodePreviewOptions) (template.HTML, error)
}
-var DefaultProcessorHelper ProcessorHelper
+var DefaultRenderHelperFuncs *RenderHelperFuncs
+
+type SimpleRenderHelper struct{}
+
+func (r *SimpleRenderHelper) CleanUp() {}
+
+func (r *SimpleRenderHelper) IsCommitIDExisting(commitID string) bool {
+ return false
+}
+
+func (r *SimpleRenderHelper) ResolveLink(link string, likeType LinkType) string {
+ return resolveLinkRelative(context.Background(), setting.AppSubURL+"/", "", link, false)
+}
+
+var _ RenderHelper = (*SimpleRenderHelper)(nil)
diff --git a/modules/markup/render_link.go b/modules/markup/render_link.go
new file mode 100644
index 0000000000000..b2e0699681400
--- /dev/null
+++ b/modules/markup/render_link.go
@@ -0,0 +1,42 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package markup
+
+import (
+ "context"
+ "strings"
+
+ "code.gitea.io/gitea/modules/httplib"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+)
+
+func resolveLinkRelative(ctx context.Context, base, cur, link string, absolute bool) (finalLink string) {
+ if IsFullURLString(link) {
+ return link
+ }
+ if strings.HasPrefix(link, "/") {
+ if strings.HasPrefix(link, base) && strings.Count(base, "/") >= 4 {
+ // a trick to tolerate that some users were using absolut paths (the old gitea's behavior)
+ finalLink = link
+ } else {
+ finalLink = util.URLJoin(base, "./", link)
+ }
+ } else {
+ finalLink = util.URLJoin(base, "./", cur, link)
+ }
+ finalLink = strings.TrimSuffix(finalLink, "/")
+ if absolute {
+ finalLink = httplib.MakeAbsoluteURL(ctx, finalLink)
+ }
+ return finalLink
+}
+
+func (ctx *RenderContext) ResolveLinkRelative(base, cur, link string) (finalLink string) {
+ return resolveLinkRelative(ctx, base, cur, link, ctx.RenderOptions.UseAbsoluteLink)
+}
+
+func (ctx *RenderContext) ResolveLinkApp(link string) string {
+ return ctx.ResolveLinkRelative(setting.AppSubURL+"/", "", link)
+}
diff --git a/modules/markup/render_link_test.go b/modules/markup/render_link_test.go
new file mode 100644
index 0000000000000..c904ec7f18d02
--- /dev/null
+++ b/modules/markup/render_link_test.go
@@ -0,0 +1,27 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package markup
+
+import (
+ "context"
+ "testing"
+
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestResolveLinkRelative(t *testing.T) {
+ ctx := context.Background()
+ setting.AppURL = "http://localhost:3000"
+ assert.Equal(t, "/a", resolveLinkRelative(ctx, "/a", "", "", false))
+ assert.Equal(t, "/a/b", resolveLinkRelative(ctx, "/a", "b", "", false))
+ assert.Equal(t, "/a/b/c", resolveLinkRelative(ctx, "/a", "b", "c", false))
+ assert.Equal(t, "/a/c", resolveLinkRelative(ctx, "/a", "b", "/c", false))
+ assert.Equal(t, "http://localhost:3000/a", resolveLinkRelative(ctx, "/a", "", "", true))
+
+ // some users might have used absolute paths a lot, so if the prefix overlaps and has enough slashes, we should tolerate it
+ assert.Equal(t, "/owner/repo/foo/owner/repo/foo/bar/xxx", resolveLinkRelative(ctx, "/owner/repo/foo", "", "/owner/repo/foo/bar/xxx", false))
+ assert.Equal(t, "/owner/repo/foo/bar/xxx", resolveLinkRelative(ctx, "/owner/repo/foo/bar", "", "/owner/repo/foo/bar/xxx", false))
+}
diff --git a/modules/markup/render_links.go b/modules/markup/render_links.go
deleted file mode 100644
index c8339d8f8b3f2..0000000000000
--- a/modules/markup/render_links.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package markup
-
-import (
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
-)
-
-type Links struct {
- AbsolutePrefix bool // add absolute URL prefix to auto-resolved links like "#issue", but not for pre-provided links and medias
- Base string // base prefix for pre-provided links and medias (images, videos), usually it is the path to the repo
- BranchPath string // actually it is the ref path, eg: "branch/features/feat-12", "tag/v1.0"
- TreePath string // the dir of the file, eg: "doc" if the file "doc/CHANGE.md" is being rendered
-}
-
-func (l *Links) Prefix() string {
- if l.AbsolutePrefix {
- return setting.AppURL
- }
- return setting.AppSubURL
-}
-
-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
-}
diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go
index 9b993de7b3eb9..35f90eb46cbd9 100644
--- a/modules/markup/renderer.go
+++ b/modules/markup/renderer.go
@@ -6,7 +6,7 @@ package markup
import (
"bytes"
"io"
- "path/filepath"
+ "path"
"strings"
"code.gitea.io/gitea/modules/setting"
@@ -55,7 +55,7 @@ func RegisterRenderer(renderer Renderer) {
// GetRendererByFileName get renderer by filename
func GetRendererByFileName(filename string) Renderer {
- extension := strings.ToLower(filepath.Ext(filename))
+ extension := strings.ToLower(path.Ext(filename))
return extRenderers[extension]
}
diff --git a/modules/markup/sanitizer_default.go b/modules/markup/sanitizer_default.go
index 0fa54efd45ef9..5eeafe940a4da 100644
--- a/modules/markup/sanitizer_default.go
+++ b/modules/markup/sanitizer_default.go
@@ -26,6 +26,9 @@ func (st *Sanitizer) createDefaultPolicy() *bluemonday.Policy {
policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
policy.AllowAttrs("checked", "disabled", "data-source-position").OnElements("input")
+ // Chroma always uses 1-2 letters for style names, we could tolerate it at the moment
+ policy.AllowAttrs("class").Matching(regexp.MustCompile(`^\w{0,2}$`)).OnElements("span")
+
// Custom URL-Schemes
if len(setting.Markdown.CustomURLSchemes) > 0 {
policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
diff --git a/modules/markup/sanitizer_default_test.go b/modules/markup/sanitizer_default_test.go
index c5c43695ea08a..e6fbae50567f4 100644
--- a/modules/markup/sanitizer_default_test.go
+++ b/modules/markup/sanitizer_default_test.go
@@ -19,6 +19,7 @@ func TestSanitizer(t *testing.T) {
// Code highlighting class
`
`, `
`,
`
`, `
`,
+ ` `, ` `,
// Input checkbox
` `, ``,
diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go
index cf6d839cbf686..c0241c0a8c5f3 100644
--- a/modules/templates/util_render_test.go
+++ b/modules/templates/util_render_test.go
@@ -59,7 +59,7 @@ func TestMain(m *testing.M) {
if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err)
}
- markup.Init(&markup.ProcessorHelper{
+ markup.Init(&markup.RenderHelperFuncs{
IsUsernameMentionable: func(ctx context.Context, username string) bool {
return username == "mention-user"
},
@@ -74,7 +74,7 @@ func newTestRenderUtils() *RenderUtils {
}
func TestRenderCommitBody(t *testing.T) {
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
type args struct {
msg string
}
@@ -145,7 +145,7 @@ func TestRenderCommitMessageLinkSubject(t *testing.T) {
}
func TestRenderIssueTitle(t *testing.T) {
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
expected := ` space @mention-user
/just/a/path.bin
https://example.com/file.bin
@@ -172,7 +172,7 @@ mail@domain.com
}
func TestRenderMarkdownToHtml(t *testing.T) {
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
expected := `space @mention-user
/just/a/path.bin
https://example.com/file.bin
@@ -211,6 +211,7 @@ func TestRenderLabels(t *testing.T) {
}
func TestUserMention(t *testing.T) {
+ markup.RenderBehaviorForTesting.DisableAdditionalAttributes = true
rendered := newTestRenderUtils().MarkdownToHtml("@no-such-user @mention-user @mention-user")
- assert.EqualValues(t, `
@no-such-user @mention-user @mention-user
`, strings.TrimSpace(string(rendered)))
+ assert.EqualValues(t, `@no-such-user @mention-user @mention-user
`, strings.TrimSpace(string(rendered)))
}
diff --git a/package-lock.json b/package-lock.json
index 005c2e5fb50c1..989c2bd77f40d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -110,7 +110,8 @@
"type-fest": "4.26.1",
"updates": "16.4.0",
"vite-string-plugin": "1.3.4",
- "vitest": "2.1.4"
+ "vitest": "2.1.4",
+ "vue-tsc": "2.1.10"
},
"engines": {
"node": ">= 18.0.0"
@@ -5390,6 +5391,35 @@
"url": "https://opencollective.com/vitest"
}
},
+ "node_modules/@volar/language-core": {
+ "version": "2.4.10",
+ "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.10.tgz",
+ "integrity": "sha512-hG3Z13+nJmGaT+fnQzAkS0hjJRa2FCeqZt6Bd+oGNhUkQ+mTFsDETg5rqUTxyzIh5pSOGY7FHCWUS8G82AzLCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/source-map": "2.4.10"
+ }
+ },
+ "node_modules/@volar/source-map": {
+ "version": "2.4.10",
+ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.10.tgz",
+ "integrity": "sha512-OCV+b5ihV0RF3A7vEvNyHPi4G4kFa6ukPmyVocmqm5QzOd8r5yAtiNvaPEjl8dNvgC/lj4JPryeeHLdXd62rWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@volar/typescript": {
+ "version": "2.4.10",
+ "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.10.tgz",
+ "integrity": "sha512-F8ZtBMhSXyYKuBfGpYwqA5rsONnOwAVvjyE7KPYJ7wgZqo2roASqNWUnianOomJX5u1cxeRooHV59N0PhvEOgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "2.4.10",
+ "path-browserify": "^1.0.1",
+ "vscode-uri": "^3.0.8"
+ }
+ },
"node_modules/@vue/compiler-core": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz",
@@ -5449,6 +5479,58 @@
"@vue/shared": "3.5.12"
}
},
+ "node_modules/@vue/compiler-vue2": {
+ "version": "2.7.16",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz",
+ "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "de-indent": "^1.0.2",
+ "he": "^1.2.0"
+ }
+ },
+ "node_modules/@vue/language-core": {
+ "version": "2.1.10",
+ "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.10.tgz",
+ "integrity": "sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "~2.4.8",
+ "@vue/compiler-dom": "^3.5.0",
+ "@vue/compiler-vue2": "^2.7.16",
+ "@vue/shared": "^3.5.0",
+ "alien-signals": "^0.2.0",
+ "minimatch": "^9.0.3",
+ "muggle-string": "^0.4.1",
+ "path-browserify": "^1.0.1"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vue/language-core/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/@vue/reactivity": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.12.tgz",
@@ -5821,6 +5903,13 @@
"ajv": "^8.8.2"
}
},
+ "node_modules/alien-signals": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.2.tgz",
+ "integrity": "sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/ansi_up": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.2.tgz",
@@ -7484,6 +7573,13 @@
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
"license": "MIT"
},
+ "node_modules/de-indent": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
+ "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
@@ -10337,6 +10433,16 @@
"node": ">=12.4.0"
}
},
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "he": "bin/he"
+ }
+ },
"node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@@ -11793,6 +11899,13 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
+ "node_modules/muggle-string": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
+ "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
@@ -12168,6 +12281,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/path-browserify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/path-data-parser": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz",
@@ -15521,6 +15641,24 @@
}
}
},
+ "node_modules/vue-tsc": {
+ "version": "2.1.10",
+ "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.10.tgz",
+ "integrity": "sha512-RBNSfaaRHcN5uqVqJSZh++Gy/YUzryuv9u1aFWhsammDJXNtUiJMNoJ747lZcQ68wUQFx6E73y4FY3D8E7FGMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/typescript": "~2.4.8",
+ "@vue/language-core": "2.1.10",
+ "semver": "^7.5.4"
+ },
+ "bin": {
+ "vue-tsc": "bin/vue-tsc.js"
+ },
+ "peerDependencies": {
+ "typescript": ">=5.0.0"
+ }
+ },
"node_modules/watchpack": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
diff --git a/package.json b/package.json
index c65f0617d87a9..03c3b79990204 100644
--- a/package.json
+++ b/package.json
@@ -109,7 +109,8 @@
"type-fest": "4.26.1",
"updates": "16.4.0",
"vite-string-plugin": "1.3.4",
- "vitest": "2.1.4"
+ "vitest": "2.1.4",
+ "vue-tsc": "2.1.10"
},
"browserslist": [
"defaults"
diff --git a/routers/api/v1/misc/markup_test.go b/routers/api/v1/misc/markup_test.go
index 6b8c09034ac72..921e7b2750e77 100644
--- a/routers/api/v1/misc/markup_test.go
+++ b/routers/api/v1/misc/markup_test.go
@@ -25,7 +25,7 @@ const AppURL = "http://localhost:3000/"
func testRenderMarkup(t *testing.T, mode string, wiki bool, filePath, text, expectedBody string, expectedCode int) {
setting.AppURL = AppURL
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
context := "/gogits/gogs"
if !wiki {
context += path.Join("/src/branch/main", path.Dir(filePath))
@@ -46,7 +46,7 @@ func testRenderMarkup(t *testing.T, mode string, wiki bool, filePath, text, expe
}
func testRenderMarkdown(t *testing.T, mode string, wiki bool, text, responseBody string, responseCode int) {
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
setting.AppURL = AppURL
context := "/gogits/gogs"
if !wiki {
@@ -67,7 +67,7 @@ func testRenderMarkdown(t *testing.T, mode string, wiki bool, text, responseBody
}
func TestAPI_RenderGFM(t *testing.T) {
- markup.Init(&markup.ProcessorHelper{
+ markup.Init(&markup.RenderHelperFuncs{
IsUsernameMentionable: func(ctx go_context.Context, username string) bool {
return username == "r-lyeh"
},
@@ -182,6 +182,7 @@ var simpleCases = []string{
func TestAPI_RenderSimple(t *testing.T) {
setting.AppURL = AppURL
+ markup.RenderBehaviorForTesting.DisableAdditionalAttributes = true
options := api.MarkdownOption{
Mode: "markdown",
Text: "",
@@ -199,6 +200,7 @@ func TestAPI_RenderSimple(t *testing.T) {
func TestAPI_RenderRaw(t *testing.T) {
setting.AppURL = AppURL
+ markup.RenderBehaviorForTesting.DisableAdditionalAttributes = true
ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markdown")
for i := 0; i < len(simpleCases); i += 2 {
ctx.Req.Body = io.NopCloser(strings.NewReader(simpleCases[i]))
diff --git a/routers/common/markup.go b/routers/common/markup.go
index 59f338c2bccc6..e3e6d9cfcf86b 100644
--- a/routers/common/markup.go
+++ b/routers/common/markup.go
@@ -11,6 +11,8 @@ import (
"path"
"strings"
+ "code.gitea.io/gitea/models/renderhelper"
+ "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
@@ -20,7 +22,7 @@ import (
)
// RenderMarkup renders markup text for the /markup and /markdown endpoints
-func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPathContext, filePath string) {
+func RenderMarkup(ctx *context.Base, ctxRepo *context.Repository, mode, text, urlPathContext, filePath string) {
// urlPathContext format is "/subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}"
// filePath is the path of the file to render if the end user is trying to preview a repo file (mode == "file")
// filePath will be used as RenderContext.RelativePath
@@ -28,60 +30,67 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPa
// for example, when previewing file "/gitea/owner/repo/src/branch/features/feat-123/doc/CHANGE.md", then filePath is "doc/CHANGE.md"
// and the urlPathContext is "/gitea/owner/repo/src/branch/features/feat-123/doc"
- renderCtx := markup.NewRenderContext(ctx).
- WithLinks(markup.Links{AbsolutePrefix: true}).
- WithMarkupType(markdown.MarkupName)
-
- if urlPathContext != "" {
- renderCtx.RenderOptions.Links.Base = fmt.Sprintf("%s%s", httplib.GuessCurrentHostURL(ctx), urlPathContext)
- }
-
if mode == "" || mode == "markdown" {
// raw markdown doesn't need any special handling
- if err := markdown.RenderRaw(renderCtx, strings.NewReader(text), ctx.Resp); err != nil {
+ baseLink := urlPathContext
+ if baseLink == "" {
+ baseLink = fmt.Sprintf("%s%s", httplib.GuessCurrentHostURL(ctx), urlPathContext)
+ }
+ rctx := renderhelper.NewRenderContextSimpleDocument(ctx, baseLink).WithUseAbsoluteLink(true).
+ WithMarkupType(markdown.MarkupName)
+ if err := markdown.RenderRaw(rctx, strings.NewReader(text), ctx.Resp); err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
}
return
}
+
+ // Ideally, this handler should be called with RepoAssigment and get the related repo from context "/owner/repo/markup"
+ // then render could use the repo to do various things (the permission check has passed)
+ //
+ // However, this handler is also exposed as "/markup" without any repo context,
+ // then since there is no permission check, so we can't use the repo from "context" parameter,
+ // in this case, only the "path" information could be used which doesn't cause security problems.
+ var repoModel *repo.Repository
+ if ctxRepo != nil {
+ repoModel = ctxRepo.Repository
+ }
+ var repoOwnerName, repoName, refPath, treePath string
+ repoLinkPath := strings.TrimPrefix(urlPathContext, setting.AppSubURL+"/")
+ fields := strings.SplitN(repoLinkPath, "/", 5)
+ if len(fields) == 5 && fields[2] == "src" && (fields[3] == "branch" || fields[3] == "commit" || fields[3] == "tag") {
+ // absolute base prefix is something like "https://host/subpath/{user}/{repo}"
+ repoOwnerName, repoName = fields[0], fields[1]
+ treePath = path.Dir(filePath) // it is "doc" if filePath is "doc/CHANGE.md"
+ refPath = strings.Join(fields[3:], "/") // it is "branch/features/feat-12/doc"
+ refPath = strings.TrimSuffix(refPath, "/"+treePath) // now we get the correct branch path: "branch/features/feat-12"
+ } else if fields = strings.SplitN(repoLinkPath, "/", 3); len(fields) == 2 {
+ repoOwnerName, repoName = fields[0], fields[1]
+ }
+
+ var rctx *markup.RenderContext
switch mode {
- case "gfm": // legacy mode, do nothing
+ case "gfm": // legacy mode
+ rctx = renderhelper.NewRenderContextRepoFile(ctx, repoModel, renderhelper.RepoFileOptions{
+ DeprecatedOwnerName: repoOwnerName, DeprecatedRepoName: repoName,
+ CurrentRefPath: refPath, CurrentTreePath: treePath,
+ })
+ rctx = rctx.WithMarkupType(markdown.MarkupName)
case "comment":
- renderCtx = renderCtx.WithMetas(map[string]string{"markdownLineBreakStyle": "comment"})
+ rctx = renderhelper.NewRenderContextRepoComment(ctx, repoModel, renderhelper.RepoCommentOptions{DeprecatedOwnerName: repoOwnerName, DeprecatedRepoName: repoName})
case "wiki":
- renderCtx = renderCtx.WithMetas(map[string]string{"markdownLineBreakStyle": "document", "markupContentMode": "wiki"})
+ rctx = renderhelper.NewRenderContextRepoWiki(ctx, repoModel, renderhelper.RepoWikiOptions{DeprecatedOwnerName: repoOwnerName, DeprecatedRepoName: repoName})
case "file":
- // render the repo file content by its extension
- renderCtx = renderCtx.WithMetas(map[string]string{"markdownLineBreakStyle": "document"}).
- WithMarkupType("").
- WithRelativePath(filePath)
+ rctx = renderhelper.NewRenderContextRepoFile(ctx, repoModel, renderhelper.RepoFileOptions{
+ DeprecatedOwnerName: repoOwnerName, DeprecatedRepoName: repoName,
+ CurrentRefPath: refPath, CurrentTreePath: treePath,
+ })
+ rctx = rctx.WithMarkupType("").WithRelativePath(filePath) // render the repo file content by its extension
default:
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Unknown mode: %s", mode))
return
}
-
- fields := strings.SplitN(strings.TrimPrefix(urlPathContext, setting.AppSubURL+"/"), "/", 5)
- if len(fields) == 5 && fields[2] == "src" && (fields[3] == "branch" || fields[3] == "commit" || fields[3] == "tag") {
- // absolute base prefix is something like "https://host/subpath/{user}/{repo}"
- absoluteBasePrefix := fmt.Sprintf("%s%s/%s", httplib.GuessCurrentAppURL(ctx), fields[0], fields[1])
-
- fileDir := path.Dir(filePath) // it is "doc" if filePath is "doc/CHANGE.md"
- refPath := strings.Join(fields[3:], "/") // it is "branch/features/feat-12/doc"
- refPath = strings.TrimSuffix(refPath, "/"+fileDir) // now we get the correct branch path: "branch/features/feat-12"
-
- renderCtx = renderCtx.WithLinks(markup.Links{AbsolutePrefix: true, Base: absoluteBasePrefix, BranchPath: refPath, TreePath: fileDir})
- }
-
- if repo != nil && repo.Repository != nil {
- renderCtx = renderCtx.WithRepoFacade(repo.Repository)
- if mode == "file" {
- renderCtx = renderCtx.WithMetas(repo.Repository.ComposeDocumentMetas(ctx))
- } else if mode == "wiki" {
- renderCtx = renderCtx.WithMetas(repo.Repository.ComposeWikiMetas(ctx))
- } else if mode == "comment" {
- renderCtx = renderCtx.WithMetas(repo.Repository.ComposeMetas(ctx))
- }
- }
- if err := markup.Render(renderCtx, strings.NewReader(text), ctx.Resp); err != nil {
+ rctx = rctx.WithUseAbsoluteLink(true)
+ if err := markup.Render(rctx, strings.NewReader(text), ctx.Resp); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
ctx.Error(http.StatusUnprocessableEntity, err.Error())
} else {
diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go
index fad7dfdf5e0ee..b7f849dc65980 100644
--- a/routers/web/feed/convert.go
+++ b/routers/web/feed/convert.go
@@ -13,8 +13,8 @@ import (
"strings"
activities_model "code.gitea.io/gitea/models/activities"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
@@ -48,22 +48,18 @@ func toReleaseLink(ctx *context.Context, act *activities_model.Action) string {
return act.GetRepoAbsoluteLink(ctx) + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch())
}
-// renderMarkdown creates a minimal markdown render context from an action.
-// If rendering fails, the original markdown text is returned
-func renderMarkdown(ctx *context.Context, act *activities_model.Action, content string) template.HTML {
- markdownCtx := markup.NewRenderContext(ctx).
- WithLinks(markup.Links{
- Base: act.GetRepoLink(ctx),
- }).
- WithMetas(map[string]string{ // FIXME: not right here, it should use issue to compose the metas
- "user": act.GetRepoUserName(ctx),
- "repo": act.GetRepoName(ctx),
- })
- markdown, err := markdown.RenderString(markdownCtx, content)
+// renderCommentMarkdown renders the comment markdown to html
+func renderCommentMarkdown(ctx *context.Context, act *activities_model.Action, content string) template.HTML {
+ act.LoadRepo(ctx)
+ if act.Repo == nil {
+ return ""
+ }
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, act.Repo).WithUseAbsoluteLink(true)
+ rendered, err := markdown.RenderString(rctx, content)
if err != nil {
- return templates.SanitizeHTML(content) // old code did so: use SanitizeHTML to render in tmpl
+ return ""
}
- return markdown
+ return rendered
}
// feedActionsToFeedItems convert gitea's Action feed to feeds Item
@@ -225,12 +221,12 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
case activities_model.ActionCreateIssue, activities_model.ActionCreatePullRequest:
desc = strings.Join(act.GetIssueInfos(), "#")
- content = renderMarkdown(ctx, act, act.GetIssueContent(ctx))
+ content = renderCommentMarkdown(ctx, act, act.GetIssueContent(ctx))
case activities_model.ActionCommentIssue, activities_model.ActionApprovePullRequest, activities_model.ActionRejectPullRequest, activities_model.ActionCommentPull:
desc = act.GetIssueTitle(ctx)
comment := act.GetIssueInfos()[1]
if len(comment) != 0 {
- desc += "\n\n" + string(renderMarkdown(ctx, act, comment))
+ desc += "\n\n" + string(renderCommentMarkdown(ctx, act, comment))
}
case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
desc = act.GetIssueInfos()[1]
@@ -294,12 +290,8 @@ func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release) (
}
link := &feeds.Link{Href: rel.HTMLURL()}
- content, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithRepoFacade(rel.Repo).
- WithLinks(markup.Links{
- Base: rel.Repo.Link(),
- }).
- WithMetas(rel.Repo.ComposeMetas(ctx)),
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, rel.Repo).WithUseAbsoluteLink(true)
+ content, err = markdown.RenderString(rctx,
rel.Note)
if err != nil {
return nil, err
diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go
index 7c4864b45ef15..47de7c089def1 100644
--- a/routers/web/feed/profile.go
+++ b/routers/web/feed/profile.go
@@ -7,7 +7,7 @@ import (
"time"
activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/models/renderhelper"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/services/context"
@@ -41,9 +41,8 @@ func showUserFeed(ctx *context.Context, formatType string) {
return
}
- ctxUserDescription, err := markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{Base: ctx.ContextUser.HTMLURL()}).
- WithMetas(markup.ComposeSimpleDocumentMetas()),
+ rctx := renderhelper.NewRenderContextSimpleDocument(ctx, ctx.ContextUser.HTMLURL())
+ ctxUserDescription, err := markdown.RenderString(rctx,
ctx.ContextUser.Description)
if err != nil {
ctx.ServerError("RenderString", err)
diff --git a/routers/web/org/home.go b/routers/web/org/home.go
index d0ac82b1b094e..f02c08ae7691a 100644
--- a/routers/web/org/home.go
+++ b/routers/web/org/home.go
@@ -11,10 +11,10 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -180,16 +180,10 @@ func prepareOrgProfileReadme(ctx *context.Context, viewRepositories bool) bool {
if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
log.Error("failed to GetBlobContent: %v", err)
} else {
- if profileContent, err := markdown.RenderString(markup.NewRenderContext(ctx).
- WithGitRepo(profileGitRepo).
- WithLinks(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)),
- }).
- WithMetas(markup.ComposeSimpleDocumentMetas()),
- bytes); err != nil {
+ rctx := renderhelper.NewRenderContextRepoFile(ctx, profileDbRepo, renderhelper.RepoFileOptions{
+ CurrentRefPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
+ })
+ if profileContent, err := markdown.RenderString(rctx, bytes); err != nil {
log.Error("failed to RenderString: %v", err)
} else {
ctx.Data["ProfileReadme"] = profileContent
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index 87b1f9019a00f..0be9689c3f5e3 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -15,6 +15,7 @@ import (
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
@@ -392,15 +393,8 @@ func Diff(ctx *context.Context) {
if err == nil {
ctx.Data["NoteCommit"] = note.Commit
ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(ctx, note.Commit)
- ctx.Data["NoteRendered"], err = markup.RenderCommitMessage(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{
- Base: ctx.Repo.RepoLink,
- BranchPath: path.Join("commit", util.PathEscapeSegments(commitID)),
- }).
- WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithRepoFacade(ctx.Repo.Repository),
- template.HTMLEscapeString(string(charset.ToUTF8WithFallback(note.Message, charset.ConvertOpts{}))))
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository, renderhelper.RepoCommentOptions{CurrentRefPath: path.Join("commit", util.PathEscapeSegments(commitID))})
+ ctx.Data["NoteRendered"], err = markup.RenderCommitMessage(rctx, template.HTMLEscapeString(string(charset.ToUTF8WithFallback(note.Message, charset.ConvertOpts{}))))
if err != nil {
ctx.ServerError("RenderCommitMessage", err)
return
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index d52dbf393945e..415f34d1fbe40 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -18,12 +18,12 @@ import (
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
project_model "code.gitea.io/gitea/models/project"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
api "code.gitea.io/gitea/modules/structs"
@@ -366,12 +366,8 @@ func UpdateIssueContent(ctx *context.Context) {
}
}
- content, err := markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{Base: ctx.FormString("context")}).
- WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithRepoFacade(ctx.Repo.Repository),
- issue.Content)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository)
+ content, err := markdown.RenderString(rctx, issue.Content)
if err != nil {
ctx.ServerError("RenderString", err)
return
diff --git a/routers/web/repo/issue_comment.go b/routers/web/repo/issue_comment.go
index 33105d67ca640..6b7b29d9d71bc 100644
--- a/routers/web/repo/issue_comment.go
+++ b/routers/web/repo/issue_comment.go
@@ -10,10 +10,10 @@ import (
"net/http"
issues_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/models/renderhelper"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
@@ -267,12 +267,8 @@ func UpdateCommentContent(ctx *context.Context) {
var renderedContent template.HTML
if comment.Content != "" {
- renderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{Base: ctx.FormString("context")}).
- WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithRepoFacade(ctx.Repo.Repository),
- comment.Content)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository)
+ renderedContent, err = markdown.RenderString(rctx, comment.Content)
if err != nil {
ctx.ServerError("RenderString", err)
return
diff --git a/routers/web/repo/issue_view.go b/routers/web/repo/issue_view.go
index 55d36cfefa781..54ff36db4925b 100644
--- a/routers/web/repo/issue_view.go
+++ b/routers/web/repo/issue_view.go
@@ -19,6 +19,7 @@ import (
access_model "code.gitea.io/gitea/models/perm/access"
project_model "code.gitea.io/gitea/models/project"
pull_model "code.gitea.io/gitea/models/pull"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
@@ -359,12 +360,8 @@ func ViewIssue(ctx *context.Context) {
}
}
ctx.Data["IssueWatch"] = iw
- issue.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
- WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithRepoFacade(ctx.Repo.Repository),
- issue.Content)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository)
+ issue.RenderedContent, err = markdown.RenderString(rctx, issue.Content)
if err != nil {
ctx.ServerError("RenderString", err)
return
@@ -464,14 +461,8 @@ func ViewIssue(ctx *context.Context) {
comment.Issue = issue
if comment.Type == issues_model.CommentTypeComment || comment.Type == issues_model.CommentTypeReview {
- comment.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{
- Base: ctx.Repo.RepoLink,
- }).
- WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithRepoFacade(ctx.Repo.Repository),
- comment.Content)
+ rctx = renderhelper.NewRenderContextRepoComment(ctx, repo)
+ comment.RenderedContent, err = markdown.RenderString(rctx, comment.Content)
if err != nil {
ctx.ServerError("RenderString", err)
return
@@ -546,12 +537,8 @@ func ViewIssue(ctx *context.Context) {
}
}
} else if comment.Type.HasContentSupport() {
- comment.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
- WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithRepoFacade(ctx.Repo.Repository),
- comment.Content)
+ rctx = renderhelper.NewRenderContextRepoComment(ctx, repo)
+ comment.RenderedContent, err = markdown.RenderString(rctx, comment.Content)
if err != nil {
ctx.ServerError("RenderString", err)
return
diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go
index 7361fe66bcc57..3afdcfad8bfd1 100644
--- a/routers/web/repo/milestone.go
+++ b/routers/web/repo/milestone.go
@@ -10,8 +10,8 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/models/renderhelper"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
@@ -79,12 +79,8 @@ func Milestones(ctx *context.Context) {
}
}
for _, m := range miles {
- m.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
- WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithRepoFacade(ctx.Repo.Repository),
- m.Content)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository)
+ m.RenderedContent, err = markdown.RenderString(rctx, m.Content)
if err != nil {
ctx.ServerError("RenderString", err)
return
@@ -265,12 +261,8 @@ func MilestoneIssuesAndPulls(ctx *context.Context) {
return
}
- milestone.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
- WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithRepoFacade(ctx.Repo.Repository),
- milestone.Content)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository)
+ milestone.RenderedContent, err = markdown.RenderString(rctx, milestone.Content)
if err != nil {
ctx.ServerError("RenderString", err)
return
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index cce13df3be876..799ce3ad804c3 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -13,11 +13,11 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm"
project_model "code.gitea.io/gitea/models/project"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
@@ -92,12 +92,8 @@ func Projects(ctx *context.Context) {
}
for i := range projects {
- projects[i].RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
- WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithRepoFacade(ctx.Repo.Repository),
- projects[i].Description)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, repo)
+ projects[i].RenderedContent, err = markdown.RenderString(rctx, projects[i].Description)
if err != nil {
ctx.ServerError("RenderString", err)
return
@@ -422,12 +418,8 @@ func ViewProject(ctx *context.Context) {
ctx.Data["SelectLabels"] = selectLabels
ctx.Data["AssigneeID"] = assigneeID
- project.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
- WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithRepoFacade(ctx.Repo.Repository),
- project.Description)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository)
+ project.RenderedContent, err = markdown.RenderString(rctx, project.Description)
if err != nil {
ctx.ServerError("RenderString", err)
return
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 1b5305a90db5f..96c512dd3df95 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -13,13 +13,13 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
@@ -114,12 +114,8 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
cacheUsers[r.PublisherID] = r.Publisher
}
- r.RenderedNote, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
- WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithRepoFacade(ctx.Repo.Repository),
- r.Note)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, r.Repo)
+ r.RenderedNote, err = markdown.RenderString(rctx, r.Note)
if err != nil {
return nil, err
}
diff --git a/routers/web/repo/render.go b/routers/web/repo/render.go
index c551e44f46fc7..856425ae355c5 100644
--- a/routers/web/repo/render.go
+++ b/routers/web/repo/render.go
@@ -9,6 +9,7 @@ import (
"net/http"
"path"
+ "code.gitea.io/gitea/models/renderhelper"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
@@ -56,17 +57,12 @@ func RenderFile(ctx *context.Context) {
return
}
- err = markup.Render(markup.NewRenderContext(ctx).
- WithRelativePath(ctx.Repo.TreePath).
- WithLinks(markup.Links{
- Base: ctx.Repo.RepoLink,
- BranchPath: ctx.Repo.BranchNameSubURL(),
- TreePath: path.Dir(ctx.Repo.TreePath),
- }).
- WithMetas(ctx.Repo.Repository.ComposeDocumentMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo).
- WithInStandalonePage(true),
- rd, ctx.Resp)
+ rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
+ CurrentRefPath: ctx.Repo.BranchNameSubURL(),
+ CurrentTreePath: path.Dir(ctx.Repo.TreePath),
+ }).WithRelativePath(ctx.Repo.TreePath).WithInStandalonePage(true)
+
+ err = markup.Render(rctx, rd, ctx.Resp)
if err != nil {
log.Error("Failed to render file %q: %v", ctx.Repo.TreePath, err)
http.Error(ctx.Resp, "Failed to render file", http.StatusInternalServerError)
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index ec2ddfd79f47d..e6c25d75e9315 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -31,6 +31,7 @@ import (
git_model "code.gitea.io/gitea/models/git"
issue_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
@@ -310,17 +311,14 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr
ctx.Data["IsMarkup"] = true
ctx.Data["MarkupType"] = markupType
- ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, markup.NewRenderContext(ctx).
+ rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
+ CurrentRefPath: ctx.Repo.BranchNameSubURL(),
+ CurrentTreePath: path.Join(ctx.Repo.TreePath, subfolder),
+ }).
WithMarkupType(markupType).
- WithRelativePath(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).
- WithLinks(markup.Links{
- Base: ctx.Repo.RepoLink,
- BranchPath: ctx.Repo.BranchNameSubURL(),
- TreePath: path.Join(ctx.Repo.TreePath, subfolder),
- }).
- WithMetas(ctx.Repo.Repository.ComposeDocumentMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo),
- rd)
+ WithRelativePath(path.Join(ctx.Repo.TreePath, subfolder, readmeFile.Name())) // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path).
+
+ ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd)
if err != nil {
log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err)
delete(ctx.Data, "IsMarkup")
@@ -513,17 +511,15 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
ctx.Data["MarkupType"] = markupType
metas := ctx.Repo.Repository.ComposeDocumentMetas(ctx)
metas["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
- ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, markup.NewRenderContext(ctx).
+ rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
+ CurrentRefPath: ctx.Repo.BranchNameSubURL(),
+ CurrentTreePath: path.Dir(ctx.Repo.TreePath),
+ }).
WithMarkupType(markupType).
WithRelativePath(ctx.Repo.TreePath).
- WithLinks(markup.Links{
- Base: ctx.Repo.RepoLink,
- BranchPath: ctx.Repo.BranchNameSubURL(),
- TreePath: path.Dir(ctx.Repo.TreePath),
- }).
- WithMetas(metas).
- WithGitRepo(ctx.Repo.GitRepo),
- rd)
+ WithMetas(metas)
+
+ ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd)
if err != nil {
ctx.ServerError("Render", err)
return
@@ -604,17 +600,15 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
rd := io.MultiReader(bytes.NewReader(buf), dataRc)
ctx.Data["IsMarkup"] = true
ctx.Data["MarkupType"] = markupType
- ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, markup.NewRenderContext(ctx).
+
+ rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
+ CurrentRefPath: ctx.Repo.BranchNameSubURL(),
+ CurrentTreePath: path.Dir(ctx.Repo.TreePath),
+ }).
WithMarkupType(markupType).
- WithRelativePath(ctx.Repo.TreePath).
- WithLinks(markup.Links{
- Base: ctx.Repo.RepoLink,
- BranchPath: ctx.Repo.BranchNameSubURL(),
- TreePath: path.Dir(ctx.Repo.TreePath),
- }).
- WithMetas(ctx.Repo.Repository.ComposeDocumentMetas(ctx)).
- WithGitRepo(ctx.Repo.GitRepo),
- rd)
+ WithRelativePath(ctx.Repo.TreePath)
+
+ ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd)
if err != nil {
ctx.ServerError("Render", err)
return
diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go
index eda3320ff0d27..b2dd846fafd62 100644
--- a/routers/web/repo/wiki.go
+++ b/routers/web/repo/wiki.go
@@ -14,6 +14,7 @@ import (
"strings"
git_model "code.gitea.io/gitea/models/git"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
@@ -288,11 +289,9 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
footerContent = data
}
- rctx := markup.NewRenderContext(ctx).
- WithMetas(ctx.Repo.Repository.ComposeWikiMetas(ctx)).
- WithLinks(markup.Links{Base: ctx.Repo.RepoLink})
- buf := &strings.Builder{}
+ rctx := renderhelper.NewRenderContextRepoWiki(ctx, ctx.Repo.Repository)
+ buf := &strings.Builder{}
renderFn := func(data []byte) (escaped *charset.EscapeStatus, output string, err error) {
markupRd, markupWr := io.Pipe()
defer markupWr.Close()
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index 0bd0371f14465..6149ccb08d54e 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -20,6 +20,7 @@ import (
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
@@ -27,7 +28,6 @@ import (
"code.gitea.io/gitea/modules/container"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
@@ -257,11 +257,8 @@ func Milestones(ctx *context.Context) {
continue
}
- milestones[i].RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithLinks(markup.Links{Base: milestones[i].Repo.Link()}).
- WithMetas(milestones[i].Repo.ComposeMetas(ctx)).
- WithRepoFacade(milestones[i].Repo),
- milestones[i].Content)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, milestones[i].Repo)
+ milestones[i].RenderedContent, err = markdown.RenderString(rctx, milestones[i].Content)
if err != nil {
ctx.ServerError("RenderString", err)
return
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index 2c9487bbc08d9..931af0a2839ec 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -12,12 +12,12 @@ import (
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
@@ -72,17 +72,17 @@ func userProfile(ctx *context.Context) {
ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
}
- profileDbRepo, profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer)
+ profileDbRepo, _ /*profileGitRepo*/, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer)
defer profileClose()
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
- prepareUserProfileTabData(ctx, showPrivate, profileDbRepo, profileGitRepo, profileReadmeBlob)
+ prepareUserProfileTabData(ctx, showPrivate, profileDbRepo, 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, profileDbRepo *repo_model.Repository, profileGitRepo *git.Repository, profileReadme *git.Blob) {
+func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDbRepo *repo_model.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")
@@ -246,18 +246,10 @@ 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 {
- if profileContent, err := markdown.RenderString(markup.NewRenderContext(ctx).
- WithGitRepo(profileGitRepo).
- WithLinks(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)),
- }),
- bytes); err != nil {
+ rctx := renderhelper.NewRenderContextRepoFile(ctx, profileDbRepo, renderhelper.RepoFileOptions{
+ CurrentRefPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
+ })
+ if profileContent, err := markdown.RenderString(rctx, bytes); err != nil {
log.Error("failed to RenderString: %v", err)
} else {
ctx.Data["ProfileReadme"] = profileContent
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index 162e497dc02be..8eee32a8c67ec 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -18,12 +18,12 @@ import (
activities_model "code.gitea.io/gitea/models/activities"
issues_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
@@ -219,10 +219,8 @@ 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.NewRenderContext(ctx).
- WithRepoFacade(ctx.Issue.Repo).
- WithLinks(markup.Links{AbsolutePrefix: true, Base: ctx.Issue.Repo.HTMLURL()}).
- WithMetas(ctx.Issue.Repo.ComposeMetas(ctx)),
+ rctx := renderhelper.NewRenderContextRepoComment(ctx.Context, ctx.Issue.Repo).WithUseAbsoluteLink(true)
+ body, err := markdown.RenderString(rctx,
ctx.Content)
if err != nil {
return nil, err
diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go
index 3298c2273a945..af1a7a266205b 100644
--- a/services/mailer/mail_release.go
+++ b/services/mailer/mail_release.go
@@ -7,11 +7,11 @@ import (
"bytes"
"context"
+ "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation"
@@ -56,10 +56,8 @@ func mailNewRelease(ctx context.Context, lang string, tos []*user_model.User, re
locale := translation.NewLocale(lang)
var err error
- rel.RenderedNote, err = markdown.RenderString(markup.NewRenderContext(ctx).
- WithRepoFacade(rel.Repo).
- WithLinks(markup.Links{Base: rel.Repo.HTMLURL()}).
- WithMetas(rel.Repo.ComposeMetas(ctx)),
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, rel.Repo).WithUseAbsoluteLink(true)
+ rel.RenderedNote, err = markdown.RenderString(rctx,
rel.Note)
if err != nil {
log.Error("markdown.RenderString(%d): %v", rel.RepoID, err)
diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go
index 40fd21dea58a0..663ffa85ef377 100644
--- a/services/mailer/mail_test.go
+++ b/services/mailer/mail_test.go
@@ -70,7 +70,7 @@ func prepareMailerTest(t *testing.T) (doer *user_model.User, repo *repo_model.Re
func TestComposeIssueCommentMessage(t *testing.T) {
doer, _, issue, comment := prepareMailerTest(t)
- markup.Init(&markup.ProcessorHelper{
+ markup.Init(&markup.RenderHelperFuncs{
IsUsernameMentionable: func(ctx context.Context, username string) bool {
return username == doer.Name
},
diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go
index 68487fb8dbb57..1f1abf496a3e0 100644
--- a/services/markup/processorhelper.go
+++ b/services/markup/processorhelper.go
@@ -11,10 +11,8 @@ import (
gitea_context "code.gitea.io/gitea/services/context"
)
-func ProcessorHelper() *markup.ProcessorHelper {
- return &markup.ProcessorHelper{
- ElementDir: "auto", // set dir="auto" for necessary (eg: , , etc) tags
-
+func ProcessorHelper() *markup.RenderHelperFuncs {
+ return &markup.RenderHelperFuncs{
RenderRepoFileCodePreview: renderRepoFileCodePreview,
IsUsernameMentionable: func(ctx context.Context, username string) bool {
mentionedUser, err := user.GetUserByName(ctx, username)
diff --git a/tests/fuzz/fuzz_test.go b/tests/fuzz/fuzz_test.go
index 78d3027547d84..946f7c46f1122 100644
--- a/tests/fuzz/fuzz_test.go
+++ b/tests/fuzz/fuzz_test.go
@@ -5,7 +5,6 @@ package fuzz
import (
"bytes"
- "context"
"io"
"testing"
@@ -15,9 +14,7 @@ import (
)
func newFuzzRenderContext() *markup.RenderContext {
- return markup.NewRenderContext(context.Background()).
- WithLinks(markup.Links{Base: "https://example.com/go-gitea/gitea"}).
- WithMetas(map[string]string{"user": "go-gitea", "repo": "gitea"})
+ return markup.NewTestRenderContext("https://example.com/go-gitea/gitea", map[string]string{"user": "go-gitea", "repo": "gitea"})
}
func FuzzMarkdownRenderRaw(f *testing.F) {
diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go
index 5b9f16ef96dc7..9f75478ebfc4f 100644
--- a/tests/integration/api_issue_test.go
+++ b/tests/integration/api_issue_test.go
@@ -144,6 +144,18 @@ func TestAPICreateIssue(t *testing.T) {
func TestAPICreateIssueParallel(t *testing.T) {
defer tests.PrepareTestEnv(t)()
+
+ // FIXME: There seems to be a bug in github.com/mattn/go-sqlite3 with sqlite_unlock_notify, when doing concurrent writes to the same database,
+ // some requests may get stuck in "go-sqlite3.(*SQLiteRows).Next", "go-sqlite3.(*SQLiteStmt).exec" and "go-sqlite3.unlock_notify_wait",
+ // because the "unlock_notify_wait" never returns and the internal lock never gets releases.
+ //
+ // The trigger is: a previous test created issues and made the real issue indexer queue start processing, then this test does concurrent writing.
+ // Adding this "Sleep" makes go-sqlite3 "finish" some internal operations before concurrent writes and then won't get stuck.
+ // To reproduce: make a new test run these 2 tests enough times:
+ // > func TestBug() { for i := 0; i < 100; i++ { testAPICreateIssue(t); testAPICreateIssueParallel(t) } }
+ // Usually the test gets stuck in fewer than 10 iterations without this "sleep".
+ time.Sleep(time.Second)
+
const body, title = "apiTestBody", "apiTestTitle"
repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})