mirror of
https://github.com/go-gitea/gitea
synced 2025-07-22 18:28:37 +00:00
Add sub issue list support (#32940)
Just like GitHub, show issue icon/title when the issue number is in a list
This commit is contained in:
@@ -11,6 +11,6 @@ import (
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{
|
||||
FixtureFiles: []string{"user.yml", "repository.yml", "access.yml", "repo_unit.yml"},
|
||||
FixtureFiles: []string{"user.yml", "repository.yml", "access.yml", "repo_unit.yml", "issue.yml"},
|
||||
})
|
||||
}
|
||||
|
@@ -11,9 +11,10 @@ import (
|
||||
gitea_context "code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
func ProcessorHelper() *markup.RenderHelperFuncs {
|
||||
func FormalRenderHelperFuncs() *markup.RenderHelperFuncs {
|
||||
return &markup.RenderHelperFuncs{
|
||||
RenderRepoFileCodePreview: renderRepoFileCodePreview,
|
||||
RenderRepoIssueIconTitle: renderRepoIssueIconTitle,
|
||||
IsUsernameMentionable: func(ctx context.Context, username string) bool {
|
||||
mentionedUser, err := user.GetUserByName(ctx, username)
|
||||
if err != nil {
|
@@ -18,6 +18,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/indexer/code"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
gitea_context "code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/repository/files"
|
||||
)
|
||||
@@ -46,7 +47,7 @@ func renderRepoFileCodePreview(ctx context.Context, opts markup.RenderCodePrevie
|
||||
return "", err
|
||||
}
|
||||
if !perms.CanRead(unit.TypeCode) {
|
||||
return "", fmt.Errorf("no permission")
|
||||
return "", util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, dbRepo)
|
@@ -9,12 +9,13 @@ import (
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/contexttest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProcessorHelperCodePreview(t *testing.T) {
|
||||
func TestRenderHelperCodePreview(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
ctx, _ := contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
|
||||
@@ -79,5 +80,5 @@ func TestProcessorHelperCodePreview(t *testing.T) {
|
||||
LineStart: 1,
|
||||
LineStop: 10,
|
||||
})
|
||||
assert.ErrorContains(t, err, "no permission")
|
||||
assert.ErrorIs(t, err, util.ErrPermissionDenied)
|
||||
}
|
66
services/markup/renderhelper_issueicontitle.go
Normal file
66
services/markup/renderhelper_issueicontitle.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package markup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
|
||||
"code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/perm/access"
|
||||
"code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/htmlutil"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
gitea_context "code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
func renderRepoIssueIconTitle(ctx context.Context, opts markup.RenderIssueIconTitleOptions) (_ template.HTML, err error) {
|
||||
webCtx, ok := ctx.Value(gitea_context.WebContextKey).(*gitea_context.Context)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("context is not a web context")
|
||||
}
|
||||
|
||||
textIssueIndex := fmt.Sprintf("(#%d)", opts.IssueIndex)
|
||||
dbRepo := webCtx.Repo.Repository
|
||||
if opts.OwnerName != "" {
|
||||
dbRepo, err = repo.GetRepositoryByOwnerAndName(ctx, opts.OwnerName, opts.RepoName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
textIssueIndex = fmt.Sprintf("(%s/%s#%d)", dbRepo.OwnerName, dbRepo.Name, opts.IssueIndex)
|
||||
}
|
||||
if dbRepo == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
issue, err := issues.GetIssueByIndex(ctx, dbRepo.ID, opts.IssueIndex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if webCtx.Repo.Repository == nil || dbRepo.ID != webCtx.Repo.Repository.ID {
|
||||
perms, err := access.GetUserRepoPermission(ctx, dbRepo, webCtx.Doer)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !perms.CanReadIssuesOrPulls(issue.IsPull) {
|
||||
return "", util.ErrPermissionDenied
|
||||
}
|
||||
}
|
||||
|
||||
if issue.IsPull {
|
||||
if err = issue.LoadPullRequest(ctx); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
htmlIcon, err := webCtx.RenderToHTML("shared/issueicon", issue)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return htmlutil.HTMLFormat(`<a href="%s">%s %s %s</a>`, opts.LinkHref, htmlIcon, issue.Title, textIssueIndex), nil
|
||||
}
|
49
services/markup/renderhelper_issueicontitle_test.go
Normal file
49
services/markup/renderhelper_issueicontitle_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package markup
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/contexttest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRenderHelperIssueIconTitle(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
ctx, _ := contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
|
||||
ctx.Repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
|
||||
htm, err := renderRepoIssueIconTitle(ctx, markup.RenderIssueIconTitleOptions{
|
||||
LinkHref: "/link",
|
||||
IssueIndex: 1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `<a href="/link"><span>octicon-issue-opened(16/text green)</span> issue1 (#1)</a>`, string(htm))
|
||||
|
||||
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
|
||||
htm, err = renderRepoIssueIconTitle(ctx, markup.RenderIssueIconTitleOptions{
|
||||
OwnerName: "user2",
|
||||
RepoName: "repo1",
|
||||
LinkHref: "/link",
|
||||
IssueIndex: 1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `<a href="/link"><span>octicon-issue-opened(16/text green)</span> issue1 (user2/repo1#1)</a>`, string(htm))
|
||||
|
||||
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
|
||||
_, err = renderRepoIssueIconTitle(ctx, markup.RenderIssueIconTitleOptions{
|
||||
OwnerName: "user2",
|
||||
RepoName: "repo2",
|
||||
LinkHref: "/link",
|
||||
IssueIndex: 2,
|
||||
})
|
||||
assert.ErrorIs(t, err, util.ErrPermissionDenied)
|
||||
}
|
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProcessorHelper(t *testing.T) {
|
||||
func TestRenderHelperMention(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
userPublic := "user1"
|
||||
@@ -32,10 +32,10 @@ func TestProcessorHelper(t *testing.T) {
|
||||
unittest.AssertCount(t, &user.User{Name: userNoSuch}, 0)
|
||||
|
||||
// when using general context, use user's visibility to check
|
||||
assert.True(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userPublic))
|
||||
assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userLimited))
|
||||
assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userPrivate))
|
||||
assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userNoSuch))
|
||||
assert.True(t, FormalRenderHelperFuncs().IsUsernameMentionable(context.Background(), userPublic))
|
||||
assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(context.Background(), userLimited))
|
||||
assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(context.Background(), userPrivate))
|
||||
assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(context.Background(), userNoSuch))
|
||||
|
||||
// when using web context, use user.IsUserVisibleToViewer to check
|
||||
req, err := http.NewRequest("GET", "/", nil)
|
||||
@@ -44,11 +44,11 @@ func TestProcessorHelper(t *testing.T) {
|
||||
defer baseCleanUp()
|
||||
giteaCtx := gitea_context.NewWebContext(base, &contexttest.MockRender{}, nil)
|
||||
|
||||
assert.True(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPublic))
|
||||
assert.False(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPrivate))
|
||||
assert.True(t, FormalRenderHelperFuncs().IsUsernameMentionable(giteaCtx, userPublic))
|
||||
assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(giteaCtx, userPrivate))
|
||||
|
||||
giteaCtx.Doer, err = user.GetUserByName(db.DefaultContext, userPrivate)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPublic))
|
||||
assert.True(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPrivate))
|
||||
assert.True(t, FormalRenderHelperFuncs().IsUsernameMentionable(giteaCtx, userPublic))
|
||||
assert.True(t, FormalRenderHelperFuncs().IsUsernameMentionable(giteaCtx, userPrivate))
|
||||
}
|
Reference in New Issue
Block a user