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
|
@ -33,6 +33,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/routers"
|
"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"
|
||||||
"github.com/go-git/go-git/v5/config"
|
"github.com/go-git/go-git/v5/config"
|
||||||
|
@ -112,7 +113,7 @@ func runPR() {
|
||||||
log.Printf("[PR] Setting up router\n")
|
log.Printf("[PR] Setting up router\n")
|
||||||
// routers.GlobalInit()
|
// routers.GlobalInit()
|
||||||
external.RegisterRenderers()
|
external.RegisterRenderers()
|
||||||
markup.Init()
|
markup.Init(markup_service.ProcessorHelper())
|
||||||
c := routers.NormalRoutes(graceful.GetManager().HammerContext())
|
c := routers.NormalRoutes(graceful.GetManager().HammerContext())
|
||||||
|
|
||||||
log.Printf("[PR] Ready for testing !\n")
|
log.Printf("[PR] Ready for testing !\n")
|
||||||
|
|
|
@ -603,8 +603,14 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
|
||||||
start = loc.End
|
start = loc.End
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mention[1:]), mention, "mention"))
|
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
|
node = node.NextSibling.NextSibling
|
||||||
|
} else {
|
||||||
|
node = node.NextSibling
|
||||||
|
}
|
||||||
start = 0
|
start = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,11 @@ func TestMain(m *testing.M) {
|
||||||
if err := git.InitSimple(context.Background()); err != nil {
|
if err := git.InitSimple(context.Background()); err != nil {
|
||||||
log.Fatal("git init failed, err: %v", err)
|
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())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,18 @@ import (
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"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
|
// Init initialize regexps for markdown parsing
|
||||||
func Init() {
|
func Init(ph *ProcessorHelper) {
|
||||||
|
if ph != nil {
|
||||||
|
processorHelper = *ph
|
||||||
|
}
|
||||||
|
|
||||||
NewSanitizer()
|
NewSanitizer()
|
||||||
if len(setting.Markdown.CustomURLSchemes) > 0 {
|
if len(setting.Markdown.CustomURLSchemes) > 0 {
|
||||||
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
|
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package misc
|
package misc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
go_context "context"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
"code.gitea.io/gitea/modules/templates"
|
||||||
|
@ -50,6 +52,11 @@ func wrap(ctx *context.Context) *context.APIContext {
|
||||||
|
|
||||||
func TestAPI_RenderGFM(t *testing.T) {
|
func TestAPI_RenderGFM(t *testing.T) {
|
||||||
setting.AppURL = AppURL
|
setting.AppURL = AppURL
|
||||||
|
markup.Init(&markup.ProcessorHelper{
|
||||||
|
IsUsernameMentionable: func(ctx go_context.Context, username string) bool {
|
||||||
|
return username == "r-lyeh"
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
options := api.MarkdownOption{
|
options := api.MarkdownOption{
|
||||||
Mode: "gfm",
|
Mode: "gfm",
|
||||||
|
|
|
@ -41,6 +41,7 @@ import (
|
||||||
"code.gitea.io/gitea/services/automerge"
|
"code.gitea.io/gitea/services/automerge"
|
||||||
"code.gitea.io/gitea/services/cron"
|
"code.gitea.io/gitea/services/cron"
|
||||||
"code.gitea.io/gitea/services/mailer"
|
"code.gitea.io/gitea/services/mailer"
|
||||||
|
markup_service "code.gitea.io/gitea/services/markup"
|
||||||
repo_migrations "code.gitea.io/gitea/services/migrations"
|
repo_migrations "code.gitea.io/gitea/services/migrations"
|
||||||
mirror_service "code.gitea.io/gitea/services/mirror"
|
mirror_service "code.gitea.io/gitea/services/mirror"
|
||||||
pull_service "code.gitea.io/gitea/services/pull"
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
|
@ -123,7 +124,7 @@ func GlobalInitInstalled(ctx context.Context) {
|
||||||
|
|
||||||
highlight.NewContext()
|
highlight.NewContext()
|
||||||
external.RegisterRenderers()
|
external.RegisterRenderers()
|
||||||
markup.Init()
|
markup.Init(markup_service.ProcessorHelper())
|
||||||
|
|
||||||
if setting.EnableSQLite3 {
|
if setting.EnableSQLite3 {
|
||||||
log.Info("SQLite3 support is enabled")
|
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"))
|
||||||
|
}
|
Loading…
Reference in a new issue