]> source.dussan.org Git - gitea.git/commitdiff
Add link to user profile in markdown mention only if user exists (#21533)
authorYarden Shoham <hrsi88@gmail.com>
Sat, 22 Oct 2022 17:15:52 +0000 (20:15 +0300)
committerGitHub <noreply@github.com>
Sat, 22 Oct 2022 17:15:52 +0000 (01:15 +0800)
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>
contrib/pr/checkout.go
modules/markup/html.go
modules/markup/markdown/markdown_test.go
modules/markup/renderer.go
routers/api/v1/misc/markdown_test.go
routers/init.go
services/markup/main_test.go [new file with mode: 0644]
services/markup/processorhelper.go [new file with mode: 0644]
services/markup/processorhelper_test.go [new file with mode: 0644]

index 09510ac2c57569257f4e8ecdd19c70538cd24760..686a3ddffaac21f87688aaf2d2bd87eb8ce6ae9f 100644 (file)
@@ -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")
index a5606dbb516adb1d1c4a6137ba50b2de315a820b..ae00c3905fe8bc376b20d32aabdee6e87a4335cf 100644 (file)
@@ -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
        }
 }
index 12c6288c24d1201bb2a3722de6256ebdbbc030d4..fbb741d1cd8a71075f68f1df3be669bf77eabf1c 100644 (file)
@@ -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())
 }
 
index 5f69dc72354f046ecf512fee11312ef6f3d0c47e..b3289cb3c3b0401b971528e6ab2db96ba99685ab 100644 (file)
@@ -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)
index 7809fa5cc72a07ff8a954162f5eff9aef96ccddd..65ce06027802c64d67ed1cdc5fe3f87cedd59b30 100644 (file)
@@ -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",
index 0f2e993413af83acb49912e29509e08d8a5a666d..9045437f872b59101b0348d1efad23809f462ff5 100644 (file)
@@ -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")
diff --git a/services/markup/main_test.go b/services/markup/main_test.go
new file mode 100644 (file)
index 0000000..8efd08e
--- /dev/null
@@ -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"},
+       })
+}
diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go
new file mode 100644 (file)
index 0000000..2b1cac2
--- /dev/null
@@ -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
+               },
+       }
+}
diff --git a/services/markup/processorhelper_test.go b/services/markup/processorhelper_test.go
new file mode 100644 (file)
index 0000000..386465b
--- /dev/null
@@ -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"))
+}