diff --git a/modules/markup/html_mention.go b/modules/markup/html_mention.go
index a8f8d18c451..3da1a55c9fb 100644
--- a/modules/markup/html_mention.go
+++ b/modules/markup/html_mention.go
@@ -5,6 +5,7 @@ package markup
import (
"fmt"
+ "net/url"
"strings"
"gitea.dev/modules/references"
@@ -26,12 +27,13 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
loc.Start += start
loc.End += start
mention := node.Data[loc.Start:loc.End]
- teams, ok := ctx.RenderOptions.Metas["teams"]
+ orgLowerTeams, checkOrgTeams := ctx.RenderOptions.Metas["teams"] // in format ",team1,team2,...,team-n,", always lowercase
- if ok && strings.Contains(mention, "/") {
- mentionOrgAndTeam := strings.Split(mention, "/")
- if mentionOrgAndTeam[0][1:] == ctx.RenderOptions.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") {
- link := fmt.Sprintf("/:root/org/%s/teams/%s", ctx.RenderOptions.Metas["org"], mentionOrgAndTeam[1])
+ if checkOrgTeams && strings.Contains(mention, "/") {
+ mentionOrg, teamName, _ := strings.Cut(mention, "/")
+ orgName := mentionOrg[1:] // remove the '@' prefix
+ if strings.EqualFold(orgName, ctx.RenderOptions.Metas["org"]) && strings.Contains(orgLowerTeams, ","+strings.ToLower(teamName)+",") {
+ link := fmt.Sprintf("/:root/org/%s/teams/%s", url.PathEscape(orgName), url.PathEscape(teamName))
replaceContent(node, loc.Start, loc.End, createLink(ctx, link, mention, "" /*mention*/))
node = node.NextSibling.NextSibling
start = 0
@@ -43,7 +45,7 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
mentionedUsername := mention[1:]
if DefaultRenderHelperFuncs != nil && DefaultRenderHelperFuncs.IsUsernameMentionable(ctx, mentionedUsername) {
- link := "/:root/" + mentionedUsername
+ link := "/:root/" + url.PathEscape(mentionedUsername)
replaceContent(node, loc.Start, loc.End, createLink(ctx, link, mention, "" /*mention*/))
node = node.NextSibling.NextSibling
start = 0
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index f4b44a5cbe1..bdf0ad3e579 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -605,3 +605,18 @@ func TestIssue18471(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, `783b039...da951ce`, res.String())
}
+
+func TestRender_TeamMention(t *testing.T) {
+ // the "org" meta is lower-cased, see Repository.composeCommonMetas
+ metas := map[string]string{"user": "Org1", "repo": "repo1", "org": "org1", "teams": ",developers,"}
+ test := func(input, expected string) {
+ rctx := markup.NewTestRenderContext(markup.TestAppURL, metas).WithRelativePath("a.md")
+ buffer, err := testRenderString(rctx, input)
+ assert.NoError(t, err)
+ assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
+ }
+ test("@org1/developers", `
@org2/developers
`) + test("@org1/testers", `@org1/testers
`) +} diff --git a/modules/references/references.go b/modules/references/references.go index db8f9d606dd..88530f509ef 100644 --- a/modules/references/references.go +++ b/modules/references/references.go @@ -240,7 +240,7 @@ func FindAllMentionsBytes(content []byte) []RefSpan { } // FindFirstMentionBytes matches the first mention in then given content -// and returns the location of the unvalidated user name, including the @ prefix. +// and returns the location of the unvalidated username, including the @ prefix. func FindFirstMentionBytes(content []byte) (bool, RefSpan) { mention := mentionPattern.FindSubmatchIndex(content) if mention == nil {