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/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"))
 | 
			
		||||
		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")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"},
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 New Issue