aboutsummaryrefslogtreecommitdiffstats
path: root/models/renderhelper
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2024-11-24 16:18:57 +0800
committerGitHub <noreply@github.com>2024-11-24 16:18:57 +0800
commit633785a5f3fe00789a6cba7cc0db1333de1e9c52 (patch)
tree2a2703ee9ed41c9caa508bd72c3fb5b52716af16 /models/renderhelper
parentfa175c16949f09757ae85db6697cec327c44cba9 (diff)
downloadgitea-633785a5f3fe00789a6cba7cc0db1333de1e9c52.tar.gz
gitea-633785a5f3fe00789a6cba7cc0db1333de1e9c52.zip
Refactor markup render system (#32612)
This PR removes (almost) all path tricks, and introduces "renderhelper" package. Now we can clearly see the rendering behaviors for comment/file/wiki, more details are in "renderhelper" tests. Fix #31411 , fix #18592, fix #25632 and maybe more problems. (ps: fix #32608 by the way)
Diffstat (limited to 'models/renderhelper')
-rw-r--r--models/renderhelper/commit_checker.go53
-rw-r--r--models/renderhelper/main_test.go27
-rw-r--r--models/renderhelper/repo_comment.go73
-rw-r--r--models/renderhelper/repo_comment_test.go76
-rw-r--r--models/renderhelper/repo_file.go77
-rw-r--r--models/renderhelper/repo_file_test.go83
-rw-r--r--models/renderhelper/repo_wiki.go80
-rw-r--r--models/renderhelper/repo_wiki_test.go65
-rw-r--r--models/renderhelper/simple_document.go29
-rw-r--r--models/renderhelper/simple_document_test.go40
10 files changed, 603 insertions, 0 deletions
diff --git a/models/renderhelper/commit_checker.go b/models/renderhelper/commit_checker.go
new file mode 100644
index 0000000000..4815643e67
--- /dev/null
+++ b/models/renderhelper/commit_checker.go
@@ -0,0 +1,53 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package renderhelper
+
+import (
+ "context"
+ "io"
+
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/log"
+)
+
+type commitChecker struct {
+ ctx context.Context
+ commitCache map[string]bool
+ gitRepoFacade gitrepo.Repository
+
+ gitRepo *git.Repository
+ gitRepoCloser io.Closer
+}
+
+func newCommitChecker(ctx context.Context, gitRepo gitrepo.Repository) *commitChecker {
+ return &commitChecker{ctx: ctx, commitCache: make(map[string]bool), gitRepoFacade: gitRepo}
+}
+
+func (c *commitChecker) Close() error {
+ if c != nil && c.gitRepoCloser != nil {
+ return c.gitRepoCloser.Close()
+ }
+ return nil
+}
+
+func (c *commitChecker) IsCommitIDExisting(commitID string) bool {
+ exist, inCache := c.commitCache[commitID]
+ if inCache {
+ return exist
+ }
+
+ if c.gitRepo == nil {
+ r, closer, err := gitrepo.RepositoryFromContextOrOpen(c.ctx, c.gitRepoFacade)
+ if err != nil {
+ log.Error("unable to open repository: %s Error: %v", gitrepo.RepoGitURL(c.gitRepoFacade), err)
+ return false
+ }
+ c.gitRepo, c.gitRepoCloser = r, closer
+ }
+
+ exist = c.gitRepo.IsReferenceExist(commitID) // Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
+ c.commitCache[commitID] = exist
+ return exist
+}
diff --git a/models/renderhelper/main_test.go b/models/renderhelper/main_test.go
new file mode 100644
index 0000000000..331450172a
--- /dev/null
+++ b/models/renderhelper/main_test.go
@@ -0,0 +1,27 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package renderhelper
+
+import (
+ "context"
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/markup"
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m, &unittest.TestOptions{
+ FixtureFiles: []string{"repository.yml", "user.yml"},
+ SetUp: func() error {
+ markup.RenderBehaviorForTesting.DisableAdditionalAttributes = true
+ markup.Init(&markup.RenderHelperFuncs{
+ IsUsernameMentionable: func(ctx context.Context, username string) bool {
+ return username == "user2"
+ },
+ })
+ return nil
+ },
+ })
+}
diff --git a/models/renderhelper/repo_comment.go b/models/renderhelper/repo_comment.go
new file mode 100644
index 0000000000..6bd5e91ad1
--- /dev/null
+++ b/models/renderhelper/repo_comment.go
@@ -0,0 +1,73 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package renderhelper
+
+import (
+ "context"
+ "fmt"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/util"
+)
+
+type RepoComment struct {
+ ctx *markup.RenderContext
+ opts RepoCommentOptions
+
+ commitChecker *commitChecker
+ repoLink string
+}
+
+func (r *RepoComment) CleanUp() {
+ _ = r.commitChecker.Close()
+}
+
+func (r *RepoComment) IsCommitIDExisting(commitID string) bool {
+ return r.commitChecker.IsCommitIDExisting(commitID)
+}
+
+func (r *RepoComment) ResolveLink(link string, likeType markup.LinkType) (finalLink string) {
+ switch likeType {
+ case markup.LinkTypeApp:
+ finalLink = r.ctx.ResolveLinkApp(link)
+ default:
+ finalLink = r.ctx.ResolveLinkRelative(r.repoLink, r.opts.CurrentRefPath, link)
+ }
+ return finalLink
+}
+
+var _ markup.RenderHelper = (*RepoComment)(nil)
+
+type RepoCommentOptions struct {
+ DeprecatedRepoName string // it is only a patch for the non-standard "markup" api
+ DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api
+ CurrentRefPath string // eg: "branch/main" or "commit/11223344"
+}
+
+func NewRenderContextRepoComment(ctx context.Context, repo *repo_model.Repository, opts ...RepoCommentOptions) *markup.RenderContext {
+ helper := &RepoComment{
+ repoLink: repo.Link(),
+ opts: util.OptionalArg(opts),
+ }
+ rctx := markup.NewRenderContext(ctx)
+ helper.ctx = rctx
+ if repo != nil {
+ helper.repoLink = repo.Link()
+ helper.commitChecker = newCommitChecker(ctx, repo)
+ rctx = rctx.WithMetas(repo.ComposeMetas(ctx))
+ } else {
+ // this is almost dead code, only to pass the incorrect tests
+ helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
+ rctx = rctx.WithMetas(map[string]string{
+ "user": helper.opts.DeprecatedOwnerName,
+ "repo": helper.opts.DeprecatedRepoName,
+
+ "markdownLineBreakStyle": "comment",
+ "markupAllowShortIssuePattern": "true",
+ })
+ }
+ rctx = rctx.WithHelper(helper)
+ return rctx
+}
diff --git a/models/renderhelper/repo_comment_test.go b/models/renderhelper/repo_comment_test.go
new file mode 100644
index 0000000000..01e20b9e02
--- /dev/null
+++ b/models/renderhelper/repo_comment_test.go
@@ -0,0 +1,76 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package renderhelper
+
+import (
+ "context"
+ "testing"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/markup/markdown"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRepoComment(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+
+ t.Run("AutoLink", func(t *testing.T) {
+ rctx := NewRenderContextRepoComment(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rendered, err := markup.RenderString(rctx, `
+65f1bf27bc3bf70f64657658635e66094edbcb4d
+#1
+@user2
+`)
+ assert.NoError(t, err)
+ assert.Equal(t,
+ `<p><a href="/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d" rel="nofollow"><code>65f1bf27bc</code></a><br/>
+<a href="/user2/repo1/issues/1" class="ref-issue" rel="nofollow">#1</a><br/>
+<a href="/user2" rel="nofollow">@user2</a></p>
+`, rendered)
+ })
+
+ t.Run("AbsoluteAndRelative", func(t *testing.T) {
+ rctx := NewRenderContextRepoComment(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+
+ // It is Gitea's old behavior, the relative path is resolved to the repo path
+ // It is different from GitHub, GitHub resolves relative links to current page's path
+ rendered, err := markup.RenderString(rctx, `
+[/test](/test)
+[./test](./test)
+![/image](/image)
+![./image](./image)
+`)
+ assert.NoError(t, err)
+ assert.Equal(t,
+ `<p><a href="/user2/repo1/test" rel="nofollow">/test</a><br/>
+<a href="/user2/repo1/test" rel="nofollow">./test</a><br/>
+<a href="/user2/repo1/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/image" alt="/image"/></a><br/>
+<a href="/user2/repo1/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/image" alt="./image"/></a></p>
+`, rendered)
+ })
+
+ t.Run("WithCurrentRefPath", func(t *testing.T) {
+ rctx := NewRenderContextRepoComment(context.Background(), repo1, RepoCommentOptions{CurrentRefPath: "/commit/1234"}).
+ WithMarkupType(markdown.MarkupName)
+
+ // the ref path is only used to render commit message: a commit message is rendered at the commit page with its commit ID path
+ rendered, err := markup.RenderString(rctx, `
+[/test](/test)
+[./test](./test)
+![/image](/image)
+![./image](./image)
+`)
+ assert.NoError(t, err)
+ assert.Equal(t, `<p><a href="/user2/repo1/test" rel="nofollow">/test</a><br/>
+<a href="/user2/repo1/commit/1234/test" rel="nofollow">./test</a><br/>
+<a href="/user2/repo1/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/image" alt="/image"/></a><br/>
+<a href="/user2/repo1/commit/1234/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/commit/1234/image" alt="./image"/></a></p>
+`, rendered)
+ })
+}
diff --git a/models/renderhelper/repo_file.go b/models/renderhelper/repo_file.go
new file mode 100644
index 0000000000..794828c617
--- /dev/null
+++ b/models/renderhelper/repo_file.go
@@ -0,0 +1,77 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package renderhelper
+
+import (
+ "context"
+ "fmt"
+ "path"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/util"
+)
+
+type RepoFile struct {
+ ctx *markup.RenderContext
+ opts RepoFileOptions
+
+ commitChecker *commitChecker
+ repoLink string
+}
+
+func (r *RepoFile) CleanUp() {
+ _ = r.commitChecker.Close()
+}
+
+func (r *RepoFile) IsCommitIDExisting(commitID string) bool {
+ return r.commitChecker.IsCommitIDExisting(commitID)
+}
+
+func (r *RepoFile) ResolveLink(link string, likeType markup.LinkType) string {
+ finalLink := link
+ switch likeType {
+ case markup.LinkTypeApp:
+ finalLink = r.ctx.ResolveLinkApp(link)
+ case markup.LinkTypeDefault:
+ finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "src", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
+ case markup.LinkTypeRaw:
+ finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "raw", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
+ case markup.LinkTypeMedia:
+ finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "media", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
+ }
+ return finalLink
+}
+
+var _ markup.RenderHelper = (*RepoFile)(nil)
+
+type RepoFileOptions struct {
+ DeprecatedRepoName string // it is only a patch for the non-standard "markup" api
+ DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api
+
+ CurrentRefPath string // eg: "branch/main"
+ CurrentTreePath string // eg: "path/to/file" in the repo
+}
+
+func NewRenderContextRepoFile(ctx context.Context, repo *repo_model.Repository, opts ...RepoFileOptions) *markup.RenderContext {
+ helper := &RepoFile{opts: util.OptionalArg(opts)}
+ rctx := markup.NewRenderContext(ctx)
+ helper.ctx = rctx
+ if repo != nil {
+ helper.repoLink = repo.Link()
+ helper.commitChecker = newCommitChecker(ctx, repo)
+ rctx = rctx.WithMetas(repo.ComposeDocumentMetas(ctx))
+ } else {
+ // this is almost dead code, only to pass the incorrect tests
+ helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
+ rctx = rctx.WithMetas(map[string]string{
+ "user": helper.opts.DeprecatedOwnerName,
+ "repo": helper.opts.DeprecatedRepoName,
+
+ "markdownLineBreakStyle": "document",
+ })
+ }
+ rctx = rctx.WithHelper(helper)
+ return rctx
+}
diff --git a/models/renderhelper/repo_file_test.go b/models/renderhelper/repo_file_test.go
new file mode 100644
index 0000000000..40027ec76f
--- /dev/null
+++ b/models/renderhelper/repo_file_test.go
@@ -0,0 +1,83 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package renderhelper
+
+import (
+ "context"
+ "testing"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/markup/markdown"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRepoFile(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+
+ t.Run("AutoLink", func(t *testing.T) {
+ rctx := NewRenderContextRepoFile(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rendered, err := markup.RenderString(rctx, `
+65f1bf27bc3bf70f64657658635e66094edbcb4d
+#1
+@user2
+`)
+ assert.NoError(t, err)
+ assert.Equal(t,
+ `<p><a href="/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d" rel="nofollow"><code>65f1bf27bc</code></a>
+#1
+<a href="/user2" rel="nofollow">@user2</a></p>
+`, rendered)
+ })
+
+ t.Run("AbsoluteAndRelative", func(t *testing.T) {
+ rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{CurrentRefPath: "branch/main"}).
+ WithMarkupType(markdown.MarkupName)
+ rendered, err := markup.RenderString(rctx, `
+[/test](/test)
+[./test](./test)
+![/image](/image)
+![./image](./image)
+`)
+ assert.NoError(t, err)
+ assert.Equal(t,
+ `<p><a href="/user2/repo1/src/branch/main/test" rel="nofollow">/test</a>
+<a href="/user2/repo1/src/branch/main/test" rel="nofollow">./test</a>
+<a href="/user2/repo1/media/branch/main/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/branch/main/image" alt="/image"/></a>
+<a href="/user2/repo1/media/branch/main/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/branch/main/image" alt="./image"/></a></p>
+`, rendered)
+ })
+
+ t.Run("WithCurrentRefPath", func(t *testing.T) {
+ rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{CurrentRefPath: "/commit/1234"}).
+ WithMarkupType(markdown.MarkupName)
+ rendered, err := markup.RenderString(rctx, `
+[/test](/test)
+![/image](/image)
+`)
+ assert.NoError(t, err)
+ assert.Equal(t, `<p><a href="/user2/repo1/src/commit/1234/test" rel="nofollow">/test</a>
+<a href="/user2/repo1/media/commit/1234/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/commit/1234/image" alt="/image"/></a></p>
+`, rendered)
+ })
+
+ t.Run("WithCurrentRefPathByTag", func(t *testing.T) {
+ rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{
+ CurrentRefPath: "/commit/1234",
+ CurrentTreePath: "my-dir",
+ }).
+ WithMarkupType(markdown.MarkupName)
+ rendered, err := markup.RenderString(rctx, `
+<img src="LINK">
+<video src="LINK">
+`)
+ assert.NoError(t, err)
+ assert.Equal(t, `<a href="/user2/repo1/media/commit/1234/my-dir/LINK" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/commit/1234/my-dir/LINK"/></a>
+<video src="/user2/repo1/media/commit/1234/my-dir/LINK">
+</video>`, rendered)
+ })
+}
diff --git a/models/renderhelper/repo_wiki.go b/models/renderhelper/repo_wiki.go
new file mode 100644
index 0000000000..aa456bf6ce
--- /dev/null
+++ b/models/renderhelper/repo_wiki.go
@@ -0,0 +1,80 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package renderhelper
+
+import (
+ "context"
+ "fmt"
+ "path"
+
+ 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/util"
+)
+
+type RepoWiki struct {
+ ctx *markup.RenderContext
+ opts RepoWikiOptions
+
+ commitChecker *commitChecker
+ repoLink string
+}
+
+func (r *RepoWiki) CleanUp() {
+ _ = r.commitChecker.Close()
+}
+
+func (r *RepoWiki) IsCommitIDExisting(commitID string) bool {
+ return r.commitChecker.IsCommitIDExisting(commitID)
+}
+
+func (r *RepoWiki) ResolveLink(link string, likeType markup.LinkType) string {
+ finalLink := link
+ switch likeType {
+ case markup.LinkTypeApp:
+ finalLink = r.ctx.ResolveLinkApp(link)
+ case markup.LinkTypeDefault:
+ finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "wiki", r.opts.currentRefPath), r.opts.currentTreePath, link)
+ case markup.LinkTypeMedia:
+ finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "wiki/raw", r.opts.currentRefPath), r.opts.currentTreePath, link)
+ case markup.LinkTypeRaw: // wiki doesn't use it
+ }
+
+ return finalLink
+}
+
+var _ markup.RenderHelper = (*RepoWiki)(nil)
+
+type RepoWikiOptions struct {
+ DeprecatedRepoName string // it is only a patch for the non-standard "markup" api
+ DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api
+
+ // these options are not used at the moment because Wiki doesn't support sub-path, nor branch
+ currentRefPath string // eg: "branch/main"
+ currentTreePath string // eg: "path/to/file" in the repo
+}
+
+func NewRenderContextRepoWiki(ctx context.Context, repo *repo_model.Repository, opts ...RepoWikiOptions) *markup.RenderContext {
+ helper := &RepoWiki{opts: util.OptionalArg(opts)}
+ rctx := markup.NewRenderContext(ctx).WithMarkupType(markdown.MarkupName)
+ if repo != nil {
+ helper.repoLink = repo.Link()
+ helper.commitChecker = newCommitChecker(ctx, repo)
+ rctx = rctx.WithMetas(repo.ComposeWikiMetas(ctx))
+ } else {
+ // this is almost dead code, only to pass the incorrect tests
+ helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
+ rctx = rctx.WithMetas(map[string]string{
+ "user": helper.opts.DeprecatedOwnerName,
+ "repo": helper.opts.DeprecatedRepoName,
+
+ "markdownLineBreakStyle": "document",
+ "markupAllowShortIssuePattern": "true",
+ })
+ }
+ rctx = rctx.WithHelper(helper)
+ helper.ctx = rctx
+ return rctx
+}
diff --git a/models/renderhelper/repo_wiki_test.go b/models/renderhelper/repo_wiki_test.go
new file mode 100644
index 0000000000..beab2570e7
--- /dev/null
+++ b/models/renderhelper/repo_wiki_test.go
@@ -0,0 +1,65 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package renderhelper
+
+import (
+ "context"
+ "testing"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/markup/markdown"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRepoWiki(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+
+ t.Run("AutoLink", func(t *testing.T) {
+ rctx := NewRenderContextRepoWiki(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rendered, err := markup.RenderString(rctx, `
+65f1bf27bc3bf70f64657658635e66094edbcb4d
+#1
+@user2
+`)
+ assert.NoError(t, err)
+ assert.Equal(t,
+ `<p><a href="/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d" rel="nofollow"><code>65f1bf27bc</code></a>
+<a href="/user2/repo1/issues/1" class="ref-issue" rel="nofollow">#1</a>
+<a href="/user2" rel="nofollow">@user2</a></p>
+`, rendered)
+ })
+
+ t.Run("AbsoluteAndRelative", func(t *testing.T) {
+ rctx := NewRenderContextRepoWiki(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rendered, err := markup.RenderString(rctx, `
+[/test](/test)
+[./test](./test)
+![/image](/image)
+![./image](./image)
+`)
+ assert.NoError(t, err)
+ assert.Equal(t,
+ `<p><a href="/user2/repo1/wiki/test" rel="nofollow">/test</a>
+<a href="/user2/repo1/wiki/test" rel="nofollow">./test</a>
+<a href="/user2/repo1/wiki/raw/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/image" alt="/image"/></a>
+<a href="/user2/repo1/wiki/raw/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/image" alt="./image"/></a></p>
+`, rendered)
+ })
+
+ t.Run("PathInTag", func(t *testing.T) {
+ rctx := NewRenderContextRepoWiki(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rendered, err := markup.RenderString(rctx, `
+<img src="LINK">
+<video src="LINK">
+`)
+ assert.NoError(t, err)
+ assert.Equal(t, `<a href="/user2/repo1/wiki/raw/LINK" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/LINK"/></a>
+<video src="/user2/repo1/wiki/raw/LINK">
+</video>`, rendered)
+ })
+}
diff --git a/models/renderhelper/simple_document.go b/models/renderhelper/simple_document.go
new file mode 100644
index 0000000000..91d888aa87
--- /dev/null
+++ b/models/renderhelper/simple_document.go
@@ -0,0 +1,29 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package renderhelper
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/modules/markup"
+)
+
+type SimpleDocument struct {
+ *markup.SimpleRenderHelper
+ ctx *markup.RenderContext
+ baseLink string
+}
+
+func (r *SimpleDocument) ResolveLink(link string, likeType markup.LinkType) string {
+ return r.ctx.ResolveLinkRelative(r.baseLink, "", link)
+}
+
+var _ markup.RenderHelper = (*SimpleDocument)(nil)
+
+func NewRenderContextSimpleDocument(ctx context.Context, baseLink string) *markup.RenderContext {
+ helper := &SimpleDocument{baseLink: baseLink}
+ rctx := markup.NewRenderContext(ctx).WithHelper(helper).WithMetas(markup.ComposeSimpleDocumentMetas())
+ helper.ctx = rctx
+ return rctx
+}
diff --git a/models/renderhelper/simple_document_test.go b/models/renderhelper/simple_document_test.go
new file mode 100644
index 0000000000..c0d5fd7429
--- /dev/null
+++ b/models/renderhelper/simple_document_test.go
@@ -0,0 +1,40 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package renderhelper
+
+import (
+ "context"
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/markup/markdown"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSimpleDocument(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+ rctx := NewRenderContextSimpleDocument(context.Background(), "/base").WithMarkupType(markdown.MarkupName)
+ rendered, err := markup.RenderString(rctx, `
+65f1bf27bc3bf70f64657658635e66094edbcb4d
+#1
+@user2
+
+[/test](/test)
+[./test](./test)
+![/image](/image)
+![./image](./image)
+`)
+ assert.NoError(t, err)
+ assert.Equal(t,
+ `<p>65f1bf27bc3bf70f64657658635e66094edbcb4d
+#1
+<a href="/base/user2" rel="nofollow">@user2</a></p>
+<p><a href="/base/test" rel="nofollow">/test</a>
+<a href="/base/test" rel="nofollow">./test</a>
+<a href="/base/image" target="_blank" rel="nofollow noopener"><img src="/base/image" alt="/image"/></a>
+<a href="/base/image" target="_blank" rel="nofollow noopener"><img src="/base/image" alt="./image"/></a></p>
+`, rendered)
+}