diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 796c2d67656d9..fcfde0880041e 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -1090,8 +1090,8 @@ "licenseText": "MIT License\n\nCopyright (c) 2017 Asher\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" }, { - "name": "github.com/stretchr/testify/assert", - "path": "github.com/stretchr/testify/assert/LICENSE", + "name": "github.com/stretchr/testify", + "path": "github.com/stretchr/testify/LICENSE", "licenseText": "MIT License\n\nCopyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" }, { diff --git a/models/activities/action.go b/models/activities/action.go index 43da557fff398..e74deef1df4cf 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -200,7 +200,7 @@ func (a *Action) LoadActUser(ctx context.Context) { } } -func (a *Action) loadRepo(ctx context.Context) { +func (a *Action) LoadRepo(ctx context.Context) { if a.Repo != nil { return } @@ -250,7 +250,7 @@ func (a *Action) GetActDisplayNameTitle(ctx context.Context) string { // GetRepoUserName returns the name of the action repository owner. func (a *Action) GetRepoUserName(ctx context.Context) string { - a.loadRepo(ctx) + a.LoadRepo(ctx) if a.Repo == nil { return "(non-existing-repo)" } @@ -265,7 +265,7 @@ func (a *Action) ShortRepoUserName(ctx context.Context) string { // GetRepoName returns the name of the action repository. func (a *Action) GetRepoName(ctx context.Context) string { - a.loadRepo(ctx) + a.LoadRepo(ctx) if a.Repo == nil { return "(non-existing-repo)" } @@ -644,7 +644,7 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error { } if repoChanged { - act.loadRepo(ctx) + act.LoadRepo(ctx) repo = act.Repo // check repo owner exist. diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go index 751550f37a6b6..67a77ceb13f36 100644 --- a/models/issues/comment_code.go +++ b/models/issues/comment_code.go @@ -7,8 +7,8 @@ import ( "context" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/renderhelper" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "xorm.io/builder" @@ -112,12 +112,8 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu } var err error - rctx := markup.NewRenderContext(ctx). - WithRepoFacade(issue.Repo). - WithLinks(markup.Links{Base: issue.Repo.Link()}). - WithMetas(issue.Repo.ComposeMetas(ctx)) - if comment.RenderedContent, err = markdown.RenderString(rctx, - comment.Content); err != nil { + rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo) + if comment.RenderedContent, err = markdown.RenderString(rctx, comment.Content); err != nil { return nil, err } } diff --git a/models/renderhelper/commit_checker.go b/models/renderhelper/commit_checker.go new file mode 100644 index 0000000000000..4815643e67348 --- /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 0000000000000..abd6786ec6f10 --- /dev/null +++ b/models/renderhelper/main_test.go @@ -0,0 +1,16 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package renderhelper + +import ( + "testing" + + "code.gitea.io/gitea/models/unittest" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m, &unittest.TestOptions{ + FixtureFiles: []string{"repository.yml", "user.yml"}, + }) +} diff --git a/models/renderhelper/repo_comment.go b/models/renderhelper/repo_comment.go new file mode 100644 index 0000000000000..6bd5e91ad1768 --- /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 0000000000000..23b7aa10425e8 --- /dev/null +++ b/models/renderhelper/repo_comment_test.go @@ -0,0 +1,77 @@ +// 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) + markup.RenderBehaviorForTesting.DisableAdditionalAttributes = true + markup.Init(&markup.RenderHelperFuncs{ + IsUsernameMentionable: func(ctx context.Context, username string) bool { + return username == "user2" + }, + }) + + 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, + `
+`, rendered) + }) + + t.Run("AbsoluteAndRelative", func(t *testing.T) { + rctx := NewRenderContextRepoComment(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, + ` +`, rendered) + }) + + t.Run("WithCurrentRefPath", func(t *testing.T) { + rctx := NewRenderContextRepoComment(context.Background(), repo1, RepoCommentOptions{CurrentRefPath: "/commit/1234"}). + WithMarkupType(markdown.MarkupName) + rendered, err := markup.RenderString(rctx, ` +[/test](/test) +[./test](./test) +![/image](/image) +![./image](./image) +`) + assert.NoError(t, err) + assert.Equal(t, ` +`, rendered) + }) +} diff --git a/models/renderhelper/repo_file.go b/models/renderhelper/repo_file.go new file mode 100644 index 0000000000000..794828c617461 --- /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 0000000000000..4b15b3c7b7a8c --- /dev/null +++ b/models/renderhelper/repo_file_test.go @@ -0,0 +1,75 @@ +// 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) + markup.RenderBehaviorForTesting.DisableAdditionalAttributes = true + markup.Init(&markup.RenderHelperFuncs{ + IsUsernameMentionable: func(ctx context.Context, username string) bool { + return username == "user2" + }, + }) + + 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, + `65f1bf27bc
+#1
+@user2
/test +
+`, rendered) + }) +} diff --git a/models/renderhelper/repo_wiki.go b/models/renderhelper/repo_wiki.go new file mode 100644 index 0000000000000..8d82aa586149b --- /dev/null +++ b/models/renderhelper/repo_wiki.go @@ -0,0 +1,79 @@ +// 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) + // wiki doesn't use src or raw + } + 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 0000000000000..310e29b995f85 --- /dev/null +++ b/models/renderhelper/repo_wiki_test.go @@ -0,0 +1,60 @@ +// 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) + markup.RenderBehaviorForTesting.DisableAdditionalAttributes = true + markup.Init(&markup.RenderHelperFuncs{ + IsUsernameMentionable: func(ctx context.Context, username string) bool { + return username == "user2" + }, + }) + + 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, + ` +`, 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, + ` +`, rendered) + }) +} diff --git a/models/renderhelper/simple_document.go b/models/renderhelper/simple_document.go new file mode 100644 index 0000000000000..91d888aa878d4 --- /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/unittest/unit_tests.go b/models/unittest/unit_tests.go index 75898436fc52f..3b0f28d3a60d3 100644 --- a/models/unittest/unit_tests.go +++ b/models/unittest/unit_tests.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models/db" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "xorm.io/builder" ) @@ -64,10 +65,10 @@ func BeanExists(t assert.TestingT, bean any, conditions ...any) bool { } // AssertExistsAndLoadBean assert that a bean exists and load it from the test database -func AssertExistsAndLoadBean[T any](t assert.TestingT, bean T, conditions ...any) T { +func AssertExistsAndLoadBean[T any](t require.TestingT, bean T, conditions ...any) T { exists, err := LoadBeanIfExists(bean, conditions...) - assert.NoError(t, err) - assert.True(t, exists, + require.NoError(t, err) + require.True(t, exists, "Expected to find %+v (of type %T, with conditions %+v), but did not", bean, bean, conditions) return bean diff --git a/modules/markup/csv/csv.go b/modules/markup/csv/csv.go index b7d7a1b35be45..61977d458ae11 100644 --- a/modules/markup/csv/csv.go +++ b/modules/markup/csv/csv.go @@ -133,7 +133,7 @@ func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.W // Check if maxRows or maxSize is reached, and if true, warn. if (row >= maxRows && maxRows != 0) || (rd.InputOffset() >= maxSize && maxSize != 0) { warn := ``
- 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..76cdce9245b9d 100644 --- a/modules/markup/html_node.go +++ b/modules/markup/html_node.go @@ -17,7 +17,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 = util.URLJoin(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 +53,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 = util.URLJoin(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..f82c6b997fdad 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,166 @@ 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]]", ``, - ``) + ) test( "[[Link.-]]", ``, - ``) + ) test( "[[Link.jpg]]", ``, - ``) + ) test( "[["+favicon+"]]", ``, - ``) + ) test( "[[Name|Link]]", ``, - ``) + ) 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]]", ``, - ``) + ) test( "[[Link?]]", ``, - ``) + ) test( "[[Link]] [[Other Link]] [[Link?]]", ``, - ``) + ) test( "[[markdown_file.md]]", ``, - ``) + ) test( "[[file.zip]]", ``, - ``) + ) test( "[[Link #.jpg]]", ``, - ``) + ) test( "[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]", ``, - ``) + ) test( "[[some/path/Link #.jpg]]", ``, - ``) + ) test( "", ``, - ``) + ) } -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) +/* + 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(``, 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) - 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"}) + 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", 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(` |