diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json
index 9e620f4b1c..3ab7fc34dd 100644
--- a/options/locale_next/locale_en-US.json
+++ b/options/locale_next/locale_en-US.json
@@ -29,5 +29,6 @@
"mail.actions.run_info_previous_status": "Previous Run's Status: %[1]s",
"mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)",
"mail.actions.run_info_trigger": "Triggered because: %[1]s by: %[2]s",
+ "discussion.locked": "This discussion has been locked. Commenting is limited to contributors.",
"meta.last_line": "Thank you for translating Forgejo! This line isn't seen by the users but it serves other purposes in the translation management. You can place a fun fact in the translation instead of translating it."
}
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index 268c77d880..630cc40b86 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -79,8 +79,19 @@
{{template "repo/issue/view_content/pull".}}
{{end}}
- {{if and .IsSigned (not .IsBlocked)}}
- {{if and (or .IsRepoAdmin .HasIssuesOrPullsWritePermission (not .Issue.IsLocked)) (not .Repository.IsArchived)}}
+ {{if .Repository.IsArchived}}
+
+ {{ctx.Locale.Tr "repo.archive.nocomment"}}
+
+ {{else if .IsBlocked}}
+
+ {{ctx.Locale.Tr "repo.comment.blocked_by_user"}}
+
+ {{else if and .Issue.IsLocked (not (or .IsRepoAdmin .HasIssuesOrPullsWritePermission))}}
+
+ {{ctx.Locale.Tr "discussion.locked"}}
+
+ {{else if .IsSigned}}
- {{else if .Repository.IsArchived}}
-
- {{ctx.Locale.Tr "repo.archive.nocomment"}}
-
- {{end}}
- {{else if .IsBlocked}}
-
- {{ctx.Locale.Tr "repo.comment.blocked_by_user"}}
-
{{else}} {{/* not .IsSigned */}}
- {{if .Repository.IsArchived}}
-
- {{ctx.Locale.Tr "repo.archive.nocomment"}}
-
- {{else}}
-
- {{ctx.Locale.Tr "repo.issues.sign_in_require_desc" .SignInLink}}
-
- {{end}}
- {{end}}{{/* end if: .IsSigned */}}
+
+ {{ctx.Locale.Tr "repo.issues.sign_in_require_desc" .SignInLink}}
+
+ {{end}}
diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go
index 0e185229c9..7944d9fcd6 100644
--- a/tests/integration/issue_test.go
+++ b/tests/integration/issue_test.go
@@ -7,6 +7,7 @@ package integration
import (
"fmt"
"net/http"
+ "net/http/httptest"
"net/url"
"path"
"regexp"
@@ -29,7 +30,9 @@ import (
"forgejo.org/modules/setting"
api "forgejo.org/modules/structs"
"forgejo.org/modules/test"
+ "forgejo.org/modules/translation"
files_service "forgejo.org/services/repository/files"
+ user_service "forgejo.org/services/user"
"forgejo.org/tests"
"github.com/PuerkitoBio/goquery"
@@ -216,6 +219,108 @@ func TestNoLoginViewIssue(t *testing.T) {
MakeRequest(t, req, http.StatusOK)
}
+func TestViewIssueCommentBox(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ locale := translation.NewLocale("en-US")
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
+ collaborator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+ viewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
+ ownerSession := loginUser(t, owner.Name)
+ collaboratorSession := loginUser(t, collaborator.Name)
+ viewerSession := loginUser(t, viewer.Name)
+
+ openIssueURL, _ := testIssueWithBean(t, owner.Name, repo.ID, "Open", "Description")
+
+ warningSignin := string(locale.Tr("repo.issues.sign_in_require_desc", fmt.Sprintf("/user/login?redirect_to=%s", url.QueryEscape(openIssueURL))))
+ warningLocked := locale.TrString("discussion.locked")
+ warningBlocked := locale.TrString("repo.comment.blocked_by_user")
+
+ assertCommentForm := func(session *TestSession, url string, shouldExist bool) *HTMLDoc {
+ var resp *httptest.ResponseRecorder
+
+ req := NewRequest(t, "GET", url)
+ if session != nil {
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ } else {
+ resp = MakeRequest(t, req, http.StatusOK)
+ }
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ htmlDoc.AssertElement(t, ".issue-content #comment-form", shouldExist)
+
+ return htmlDoc
+ }
+
+ assertWarning := func(session *TestSession, url, warning string) {
+ htmlDoc := assertCommentForm(session, url, false)
+
+ timeline := htmlDoc.doc.Find(".issue-content .ui.timeline").Children()
+ assert.Positivef(t, timeline.Length(), "There should be at least one entry in the timeline")
+
+ html, _ := timeline.Last().Html()
+ assert.Equal(t, strings.TrimSpace(html), warning)
+ }
+
+ t.Run("Owner can see comment box", func(t *testing.T) {
+ assertCommentForm(ownerSession, openIssueURL, true)
+ })
+
+ t.Run("Collaborator can see comment box", func(t *testing.T) {
+ assertCommentForm(collaboratorSession, openIssueURL, true)
+ })
+
+ t.Run("Viewer can see comment box", func(t *testing.T) {
+ assertCommentForm(viewerSession, openIssueURL, true)
+ })
+
+ t.Run("Anonymous will see singin warning", func(t *testing.T) {
+ assertWarning(nil, openIssueURL, warningSignin)
+ })
+
+ lockedIssueURL, lockedIssue := testIssueWithBean(t, owner.Name, repo.ID, "Locked", "Description")
+
+ // Lock the issue
+ req := NewRequest(t, "GET", lockedIssueURL)
+ resp := ownerSession.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+
+ req = NewRequestWithValues(t, "POST", fmt.Sprintf("%s/lock", lockedIssueURL), map[string]string{
+ "_csrf": htmlDoc.GetCSRF(),
+ "reason": "Too heated",
+ })
+ _ = ownerSession.MakeRequest(t, req, http.StatusOK)
+
+ lockedIssue = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: lockedIssue.ID})
+ assert.True(t, lockedIssue.IsLocked)
+
+ t.Run("Owner can see comment box in locked issue", func(t *testing.T) {
+ assertCommentForm(ownerSession, lockedIssueURL, true)
+ })
+
+ t.Run("Collaborator can see comment box in locked issue", func(t *testing.T) {
+ assertCommentForm(collaboratorSession, lockedIssueURL, true)
+ })
+
+ t.Run("Viewer will see warning in locked issue", func(t *testing.T) {
+ assertWarning(viewerSession, lockedIssueURL, warningLocked)
+ })
+
+ t.Run("Anonymous will see warning in locked issue", func(t *testing.T) {
+ assertWarning(nil, lockedIssueURL, warningLocked)
+ })
+
+ err := user_service.BlockUser(db.DefaultContext, owner.ID, viewer.ID)
+ require.NoError(t, err)
+
+ t.Run("Blocked viewer will see warning", func(t *testing.T) {
+ assertWarning(viewerSession, lockedIssueURL, warningBlocked)
+ assertWarning(viewerSession, openIssueURL, warningBlocked)
+ })
+}
+
func testNewIssue(t *testing.T, session *TestSession, user, repo, title, content string) string {
req := NewRequest(t, "GET", path.Join(user, repo, "issues", "new"))
resp := session.MakeRequest(t, req, http.StatusOK)