Skip to content

Commit 63ebb53

Browse files
yardenshohamwxiaoguanglunny
authored
Add link to user profile in markdown mention only if user exists (#21533)
Previously mentioning a user would link to its profile, regardless of whether the user existed. This change tests if the user exists and only if it does - a link to its profile is added. * Fixes #3444 Signed-off-by: Yarden Shoham <[email protected]> Co-authored-by: wxiaoguang <[email protected]> Co-authored-by: Lunny Xiao <[email protected]>
1 parent 82ecd3b commit 63ebb53

File tree

9 files changed

+103
-5
lines changed

9 files changed

+103
-5
lines changed

contrib/pr/checkout.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"code.gitea.io/gitea/modules/setting"
3434
"code.gitea.io/gitea/modules/util"
3535
"code.gitea.io/gitea/routers"
36+
markup_service "code.gitea.io/gitea/services/markup"
3637

3738
"github.com/go-git/go-git/v5"
3839
"github.com/go-git/go-git/v5/config"
@@ -112,7 +113,7 @@ func runPR() {
112113
log.Printf("[PR] Setting up router\n")
113114
// routers.GlobalInit()
114115
external.RegisterRenderers()
115-
markup.Init()
116+
markup.Init(markup_service.ProcessorHelper())
116117
c := routers.NormalRoutes(graceful.GetManager().HammerContext())
117118

118119
log.Printf("[PR] Ready for testing !\n")

modules/markup/html.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -603,8 +603,14 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
603603
start = loc.End
604604
continue
605605
}
606-
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mention[1:]), mention, "mention"))
607-
node = node.NextSibling.NextSibling
606+
mentionedUsername := mention[1:]
607+
608+
if processorHelper.IsUsernameMentionable != nil && processorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
609+
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mentionedUsername), mention, "mention"))
610+
node = node.NextSibling.NextSibling
611+
} else {
612+
node = node.NextSibling
613+
}
608614
start = 0
609615
}
610616
}

modules/markup/markdown/markdown_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ func TestMain(m *testing.M) {
3838
if err := git.InitSimple(context.Background()); err != nil {
3939
log.Fatal("git init failed, err: %v", err)
4040
}
41+
markup.Init(&markup.ProcessorHelper{
42+
IsUsernameMentionable: func(ctx context.Context, username string) bool {
43+
return username == "r-lyeh"
44+
},
45+
})
4146
os.Exit(m.Run())
4247
}
4348

modules/markup/renderer.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,18 @@ import (
1919
"code.gitea.io/gitea/modules/setting"
2020
)
2121

22+
type ProcessorHelper struct {
23+
IsUsernameMentionable func(ctx context.Context, username string) bool
24+
}
25+
26+
var processorHelper ProcessorHelper
27+
2228
// Init initialize regexps for markdown parsing
23-
func Init() {
29+
func Init(ph *ProcessorHelper) {
30+
if ph != nil {
31+
processorHelper = *ph
32+
}
33+
2434
NewSanitizer()
2535
if len(setting.Markdown.CustomURLSchemes) > 0 {
2636
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)

routers/api/v1/misc/markdown_test.go

+7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package misc
66

77
import (
8+
go_context "context"
89
"io"
910
"net/http"
1011
"net/http/httptest"
@@ -13,6 +14,7 @@ import (
1314
"testing"
1415

1516
"code.gitea.io/gitea/modules/context"
17+
"code.gitea.io/gitea/modules/markup"
1618
"code.gitea.io/gitea/modules/setting"
1719
api "code.gitea.io/gitea/modules/structs"
1820
"code.gitea.io/gitea/modules/templates"
@@ -50,6 +52,11 @@ func wrap(ctx *context.Context) *context.APIContext {
5052

5153
func TestAPI_RenderGFM(t *testing.T) {
5254
setting.AppURL = AppURL
55+
markup.Init(&markup.ProcessorHelper{
56+
IsUsernameMentionable: func(ctx go_context.Context, username string) bool {
57+
return username == "r-lyeh"
58+
},
59+
})
5360

5461
options := api.MarkdownOption{
5562
Mode: "gfm",

routers/init.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"code.gitea.io/gitea/services/automerge"
4242
"code.gitea.io/gitea/services/cron"
4343
"code.gitea.io/gitea/services/mailer"
44+
markup_service "code.gitea.io/gitea/services/markup"
4445
repo_migrations "code.gitea.io/gitea/services/migrations"
4546
mirror_service "code.gitea.io/gitea/services/mirror"
4647
pull_service "code.gitea.io/gitea/services/pull"
@@ -123,7 +124,7 @@ func GlobalInitInstalled(ctx context.Context) {
123124

124125
highlight.NewContext()
125126
external.RegisterRenderers()
126-
markup.Init()
127+
markup.Init(markup_service.ProcessorHelper())
127128

128129
if setting.EnableSQLite3 {
129130
log.Info("SQLite3 support is enabled")

services/markup/main_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package markup
6+
7+
import (
8+
"path/filepath"
9+
"testing"
10+
11+
"code.gitea.io/gitea/models/unittest"
12+
)
13+
14+
func TestMain(m *testing.M) {
15+
unittest.MainTest(m, &unittest.TestOptions{
16+
GiteaRootPath: filepath.Join("..", ".."),
17+
FixtureFiles: []string{"user.yml"},
18+
})
19+
}

services/markup/processorhelper.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package markup
6+
7+
import (
8+
"context"
9+
10+
"code.gitea.io/gitea/models/user"
11+
"code.gitea.io/gitea/modules/log"
12+
"code.gitea.io/gitea/modules/markup"
13+
)
14+
15+
func ProcessorHelper() *markup.ProcessorHelper {
16+
return &markup.ProcessorHelper{
17+
IsUsernameMentionable: func(ctx context.Context, username string) bool {
18+
// TODO: cast ctx to modules/context.Context and use IsUserVisibleToViewer
19+
20+
// Only link if the user actually exists
21+
userExists, err := user.IsUserExist(ctx, 0, username)
22+
if err != nil {
23+
log.Error("Failed to validate user in mention %q exists, assuming it does", username)
24+
userExists = true
25+
}
26+
return userExists
27+
},
28+
}
29+
}
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package markup
6+
7+
import (
8+
"context"
9+
"testing"
10+
11+
"code.gitea.io/gitea/models/unittest"
12+
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestProcessorHelper(t *testing.T) {
17+
assert.NoError(t, unittest.PrepareTestDatabase())
18+
assert.True(t, ProcessorHelper().IsUsernameMentionable(context.Background(), "user10"))
19+
assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), "no-such-user"))
20+
}

0 commit comments

Comments
 (0)