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 <hrsi88@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
parent
82ecd3b19e
commit
63ebb53fd5
9 changed files with 103 additions and 5 deletions
|
@ -33,6 +33,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers"
|
||||
markup_service "code.gitea.io/gitea/services/markup"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
|
@ -112,7 +113,7 @@ func runPR() {
|
|||
log.Printf("[PR] Setting up router\n")
|
||||
// routers.GlobalInit()
|
||||
external.RegisterRenderers()
|
||||
markup.Init()
|
||||
markup.Init(markup_service.ProcessorHelper())
|
||||
c := routers.NormalRoutes(graceful.GetManager().HammerContext())
|
||||
|
||||
log.Printf("[PR] Ready for testing !\n")
|
||||
|
|
|
@ -603,8 +603,14 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
|
|||
start = loc.End
|
||||
continue
|
||||
}
|
||||
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mention[1:]), mention, "mention"))
|
||||
node = node.NextSibling.NextSibling
|
||||
mentionedUsername := mention[1:]
|
||||
|
||||
if processorHelper.IsUsernameMentionable != nil && processorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
|
||||
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mentionedUsername), mention, "mention"))
|
||||
node = node.NextSibling.NextSibling
|
||||
} else {
|
||||
node = node.NextSibling
|
||||
}
|
||||
start = 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,11 @@ 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{
|
||||
IsUsernameMentionable: func(ctx context.Context, username string) bool {
|
||||
return username == "r-lyeh"
|
||||
},
|
||||
})
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,18 @@ import (
|
|||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
type ProcessorHelper struct {
|
||||
IsUsernameMentionable func(ctx context.Context, username string) bool
|
||||
}
|
||||
|
||||
var processorHelper ProcessorHelper
|
||||
|
||||
// Init initialize regexps for markdown parsing
|
||||
func Init() {
|
||||
func Init(ph *ProcessorHelper) {
|
||||
if ph != nil {
|
||||
processorHelper = *ph
|
||||
}
|
||||
|
||||
NewSanitizer()
|
||||
if len(setting.Markdown.CustomURLSchemes) > 0 {
|
||||
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package misc
|
||||
|
||||
import (
|
||||
go_context "context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
@ -13,6 +14,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
|
@ -50,6 +52,11 @@ func wrap(ctx *context.Context) *context.APIContext {
|
|||
|
||||
func TestAPI_RenderGFM(t *testing.T) {
|
||||
setting.AppURL = AppURL
|
||||
markup.Init(&markup.ProcessorHelper{
|
||||
IsUsernameMentionable: func(ctx go_context.Context, username string) bool {
|
||||
return username == "r-lyeh"
|
||||
},
|
||||
})
|
||||
|
||||
options := api.MarkdownOption{
|
||||
Mode: "gfm",
|
||||
|
|
|
@ -41,6 +41,7 @@ import (
|
|||
"code.gitea.io/gitea/services/automerge"
|
||||
"code.gitea.io/gitea/services/cron"
|
||||
"code.gitea.io/gitea/services/mailer"
|
||||
markup_service "code.gitea.io/gitea/services/markup"
|
||||
repo_migrations "code.gitea.io/gitea/services/migrations"
|
||||
mirror_service "code.gitea.io/gitea/services/mirror"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
|
@ -123,7 +124,7 @@ func GlobalInitInstalled(ctx context.Context) {
|
|||
|
||||
highlight.NewContext()
|
||||
external.RegisterRenderers()
|
||||
markup.Init()
|
||||
markup.Init(markup_service.ProcessorHelper())
|
||||
|
||||
if setting.EnableSQLite3 {
|
||||
log.Info("SQLite3 support is enabled")
|
||||
|
|
19
services/markup/main_test.go
Normal file
19
services/markup/main_test.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package markup
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{
|
||||
GiteaRootPath: filepath.Join("..", ".."),
|
||||
FixtureFiles: []string{"user.yml"},
|
||||
})
|
||||
}
|
29
services/markup/processorhelper.go
Normal file
29
services/markup/processorhelper.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package markup
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
)
|
||||
|
||||
func ProcessorHelper() *markup.ProcessorHelper {
|
||||
return &markup.ProcessorHelper{
|
||||
IsUsernameMentionable: func(ctx context.Context, username string) bool {
|
||||
// TODO: cast ctx to modules/context.Context and use IsUserVisibleToViewer
|
||||
|
||||
// Only link if the user actually exists
|
||||
userExists, err := user.IsUserExist(ctx, 0, username)
|
||||
if err != nil {
|
||||
log.Error("Failed to validate user in mention %q exists, assuming it does", username)
|
||||
userExists = true
|
||||
}
|
||||
return userExists
|
||||
},
|
||||
}
|
||||
}
|
20
services/markup/processorhelper_test.go
Normal file
20
services/markup/processorhelper_test.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package markup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProcessorHelper(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
assert.True(t, ProcessorHelper().IsUsernameMentionable(context.Background(), "user10"))
|
||||
assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), "no-such-user"))
|
||||
}
|
Reference in a new issue