From d3d88667cbb928a6ff80658eba8ef0c6c508c9e0 Mon Sep 17 00:00:00 2001
From: Gusted <postmaster@gusted.xyz>
Date: Tue, 22 Aug 2023 12:22:03 +0200
Subject: [PATCH] [MODERATION] Show graceful error on comment creation

- When someone is blocked by the repository owner or issue poster and
try to comment on that issue, they get shown a graceful error.
- Adds integration test.

(cherry picked from commit 490646302e1e3dc3c59c9d75938b4647b6873ce7)
---
 options/locale/locale_en-US.ini |  1 +
 routers/web/repo/issue.go       |  6 +++-
 tests/integration/block_test.go | 57 +++++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 1 deletion(-)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index e0fd18d6f7..e859c11508 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1677,6 +1677,7 @@ issues.content_history.delete_from_history_confirm = Delete from history?
 issues.content_history.options = Options
 issues.reference_link = Reference: %s
 issues.blocked_by_user = You cannot create a issue on this repository because you are blocked by the repository owner.
+issues.comment.blocked_by_user = You cannot create a comment on this issue because you are blocked by the repository owner or the poster of the issue.
 
 compare.compare_base = base
 compare.compare_head = compare
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index f4499cbef9..9539521a85 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -3057,7 +3057,11 @@ func NewComment(ctx *context.Context) {
 
 	comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Content, attachments)
 	if err != nil {
-		ctx.ServerError("CreateIssueComment", err)
+		if errors.Is(err, user_model.ErrBlockedByUser) {
+			ctx.Flash.Error(ctx.Tr("repo.issues.comment.blocked_by_user"))
+		} else {
+			ctx.ServerError("CreateIssueComment", err)
+		}
 		return
 	}
 
diff --git a/tests/integration/block_test.go b/tests/integration/block_test.go
index 1d36a99ef6..37d0db400a 100644
--- a/tests/integration/block_test.go
+++ b/tests/integration/block_test.go
@@ -17,6 +17,7 @@ import (
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	forgejo_context "code.gitea.io/gitea/modules/context"
+	gitea_context "code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/translation"
 	"code.gitea.io/gitea/tests"
 
@@ -126,6 +127,62 @@ func TestBlockIssueCreation(t *testing.T) {
 	)
 }
 
+func TestBlockCommentCreation(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	expectedFlash := "error%3DYou%2Bcannot%2Bcreate%2Ba%2Bcomment%2Bon%2Bthis%2Bissue%2Bbecause%2Byou%2Bare%2Bblocked%2Bby%2Bthe%2Brepository%2Bowner%2Bor%2Bthe%2Bposter%2Bof%2Bthe%2Bissue."
+	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+	blockedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+
+	BlockUser(t, doer, blockedUser)
+
+	t.Run("Blocked by repository owner", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+
+		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: doer.ID})
+		issue := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 4, RepoID: repo.ID})
+		issueURL := fmt.Sprintf("/%s/%s/issues/%d", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name), issue.Index)
+
+		session := loginUser(t, blockedUser.Name)
+		req := NewRequest(t, "GET", issueURL)
+		resp := session.MakeRequest(t, req, http.StatusOK)
+		htmlDoc := NewHTMLParser(t, resp.Body)
+
+		req = NewRequestWithValues(t, "POST", path.Join(issueURL, "/comments"), map[string]string{
+			"_csrf":   htmlDoc.GetCSRF(),
+			"content": "Not a kind comment",
+		})
+		session.MakeRequest(t, req, http.StatusOK)
+
+		flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
+		assert.NotNil(t, flashCookie)
+		assert.EqualValues(t, expectedFlash, flashCookie.Value)
+	})
+
+	t.Run("Blocked by issue poster", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+
+		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 5})
+		issue := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 15, RepoID: repo.ID, PosterID: doer.ID})
+		issueURL := fmt.Sprintf("/%s/%s/issues/%d", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name), issue.Index)
+
+		session := loginUser(t, blockedUser.Name)
+		req := NewRequest(t, "GET", issueURL)
+		resp := session.MakeRequest(t, req, http.StatusOK)
+		htmlDoc := NewHTMLParser(t, resp.Body)
+
+		req = NewRequestWithValues(t, "POST", path.Join(issueURL, "/comments"), map[string]string{
+			"_csrf":   htmlDoc.GetCSRF(),
+			"content": "Not a kind comment",
+		})
+		session.MakeRequest(t, req, http.StatusOK)
+
+		flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
+		assert.NotNil(t, flashCookie)
+		assert.EqualValues(t, expectedFlash, flashCookie.Value)
+	})
+}
+
 func TestBlockIssueReaction(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()