mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-07 07:16:09 +00:00
@@ -450,7 +450,7 @@ func ViewProject(ctx *context.Context) {
|
|||||||
ctx.Data["MilestoneID"] = milestoneID
|
ctx.Data["MilestoneID"] = milestoneID
|
||||||
|
|
||||||
// Get assignees.
|
// Get assignees.
|
||||||
assigneeUsers, err := project_service.LoadIssuesAssigneesForProject(ctx, issuesMap)
|
assigneeUsers, err := project_service.LoadIssuesAssigneesForProject(ctx, project.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("LoadIssuesAssigneesForProject", err)
|
ctx.ServerError("LoadIssuesAssigneesForProject", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -14,17 +14,6 @@ import (
|
|||||||
"gitea.dev/services/context"
|
"gitea.dev/services/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
type userSearchInfo struct {
|
|
||||||
UserID int64 `json:"user_id"`
|
|
||||||
UserName string `json:"username"`
|
|
||||||
AvatarLink string `json:"avatar_link"`
|
|
||||||
FullName string `json:"full_name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type userSearchResponse struct {
|
|
||||||
Results []*userSearchInfo `json:"results"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func IssuePullPosters(ctx *context.Context) {
|
func IssuePullPosters(ctx *context.Context) {
|
||||||
isPullList := ctx.PathParam("type") == "pulls"
|
isPullList := ctx.PathParam("type") == "pulls"
|
||||||
issuePosters(ctx, isPullList)
|
issuePosters(ctx, isPullList)
|
||||||
@@ -46,14 +35,5 @@ func issuePosters(ctx *context.Context, isPullList bool) {
|
|||||||
posters = append(posters, ctx.Doer)
|
posters = append(posters, ctx.Doer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ctx.JSON(http.StatusOK, shared_user.ToSearchUserResponse(ctx, ctx.Doer, posters))
|
||||||
posters = shared_user.MakeSelfOnTop(ctx.Doer, posters)
|
|
||||||
|
|
||||||
resp := &userSearchResponse{}
|
|
||||||
resp.Results = make([]*userSearchInfo, len(posters))
|
|
||||||
for i, user := range posters {
|
|
||||||
resp.Results[i] = &userSearchInfo{UserID: user.ID, UserName: user.Name, AvatarLink: user.AvatarLink(ctx)}
|
|
||||||
resp.Results[i].FullName = user.FullName
|
|
||||||
}
|
|
||||||
ctx.JSON(http.StatusOK, resp)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,22 +11,44 @@ import (
|
|||||||
"gitea.dev/models/user"
|
"gitea.dev/models/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SearchUserInfo struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
UserName string `json:"username"`
|
||||||
|
AvatarLink string `json:"avatar_link"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchUserResponse struct {
|
||||||
|
Results []*SearchUserInfo `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
|
func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
|
||||||
if doer != nil {
|
if doer == nil {
|
||||||
idx := slices.IndexFunc(users, func(u *user.User) bool {
|
return users
|
||||||
return u.ID == doer.ID
|
}
|
||||||
})
|
idx := slices.IndexFunc(users, func(u *user.User) bool {
|
||||||
if idx > 0 {
|
return u.ID == doer.ID
|
||||||
newUsers := make([]*user.User, len(users))
|
})
|
||||||
newUsers[0] = users[idx]
|
if idx > 0 {
|
||||||
copy(newUsers[1:], users[:idx])
|
newUsers := make([]*user.User, len(users))
|
||||||
copy(newUsers[idx+1:], users[idx+1:])
|
newUsers[0] = users[idx]
|
||||||
return newUsers
|
copy(newUsers[1:], users[:idx])
|
||||||
}
|
copy(newUsers[idx+1:], users[idx+1:])
|
||||||
|
return newUsers
|
||||||
}
|
}
|
||||||
return users
|
return users
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ToSearchUserResponse(ctx context.Context, self *user.User, users []*user.User) (ret *SearchUserResponse) {
|
||||||
|
infos := MakeSelfOnTop(self, users)
|
||||||
|
resp := &SearchUserResponse{Results: make([]*SearchUserInfo, len(infos))}
|
||||||
|
for i, u := range infos {
|
||||||
|
resp.Results[i] = &SearchUserInfo{UserID: u.ID, UserName: u.Name, AvatarLink: u.AvatarLink(ctx)}
|
||||||
|
resp.Results[i].FullName = u.FullName
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
// GetFilterUserIDByName tries to get the user ID from the given username.
|
// GetFilterUserIDByName tries to get the user ID from the given username.
|
||||||
// Before, the "issue filter" passes user ID to query the list, but in many cases, it's impossible to pre-fetch the full user list.
|
// Before, the "issue filter" passes user ID to query the list, but in many cases, it's impossible to pre-fetch the full user list.
|
||||||
// So it's better to make it work like GitHub: users could input username directly.
|
// So it's better to make it work like GitHub: users could input username directly.
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ import (
|
|||||||
issues_model "gitea.dev/models/issues"
|
issues_model "gitea.dev/models/issues"
|
||||||
project_model "gitea.dev/models/project"
|
project_model "gitea.dev/models/project"
|
||||||
user_model "gitea.dev/models/user"
|
user_model "gitea.dev/models/user"
|
||||||
"gitea.dev/modules/container"
|
|
||||||
"gitea.dev/modules/optional"
|
"gitea.dev/modules/optional"
|
||||||
|
|
||||||
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MoveIssuesOnProjectColumn moves or keeps issues in a column and sorts them inside that column
|
// MoveIssuesOnProjectColumn moves or keeps issues in a column and sorts them inside that column
|
||||||
@@ -100,28 +101,15 @@ func MoveIssuesOnProjectColumn(ctx context.Context, doer *user_model.User, colum
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadIssuesAssigneesForProject(ctx context.Context, issuesMap map[int64]issues_model.IssueList) ([]*user_model.User, error) {
|
func LoadIssuesAssigneesForProject(ctx context.Context, projectID int64) (users []*user_model.User, _ error) {
|
||||||
var issueList issues_model.IssueList
|
sub := builder.Select("distinct issue_assignees.assignee_id").
|
||||||
for _, colIssues := range issuesMap {
|
From("project_issue").Join("INNER", "issue_assignees", "project_issue.issue_id=issue_assignees.issue_id").
|
||||||
issueList = append(issueList, colIssues...)
|
Where(builder.Eq{"project_issue.project_id": projectID})
|
||||||
}
|
err := db.GetEngine(ctx).Table("`user`").Where(builder.In("id", sub)).Find(&users)
|
||||||
err := issueList.LoadAssignees(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
users := make([]*user_model.User, 0, len(issueList))
|
slices.SortFunc(users, func(a, b *user_model.User) int { return strings.Compare(a.Name, b.Name) })
|
||||||
usersAdded := container.Set[int64]{}
|
|
||||||
for _, issue := range issueList {
|
|
||||||
for _, assignee := range issue.Assignees {
|
|
||||||
if !usersAdded.Contains(assignee.ID) {
|
|
||||||
usersAdded.Add(assignee.ID)
|
|
||||||
users = append(users, assignee)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slices.SortFunc(users, func(a, b *user_model.User) int {
|
|
||||||
return strings.Compare(a.Name, b.Name)
|
|
||||||
})
|
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
user_model "gitea.dev/models/user"
|
user_model "gitea.dev/models/user"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Projects(t *testing.T) {
|
func Test_Projects(t *testing.T) {
|
||||||
@@ -198,18 +197,4 @@ func Test_Projects(t *testing.T) {
|
|||||||
assert.Len(t, columnIssues[3], 1)
|
assert.Len(t, columnIssues[3], 1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("LoadIssuesAssigneesForProject", func(t *testing.T) {
|
|
||||||
issuesMap := map[int64]issues_model.IssueList{}
|
|
||||||
issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
|
|
||||||
issue6 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 6})
|
|
||||||
issuesMap[1] = issues_model.IssueList{issue1}
|
|
||||||
issuesMap[2] = issues_model.IssueList{issue6}
|
|
||||||
assignees, err := LoadIssuesAssigneesForProject(t.Context(), issuesMap)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, assignees, 3)
|
|
||||||
require.Equal(t, "user1", assignees[0].Name)
|
|
||||||
require.Equal(t, "user10", assignees[1].Name)
|
|
||||||
require.Equal(t, "user2", assignees[2].Name)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"gitea.dev/models/db"
|
||||||
issues_model "gitea.dev/models/issues"
|
issues_model "gitea.dev/models/issues"
|
||||||
project_model "gitea.dev/models/project"
|
project_model "gitea.dev/models/project"
|
||||||
repo_model "gitea.dev/models/repo"
|
repo_model "gitea.dev/models/repo"
|
||||||
"gitea.dev/models/unit"
|
"gitea.dev/models/unit"
|
||||||
"gitea.dev/models/unittest"
|
"gitea.dev/models/unittest"
|
||||||
user_model "gitea.dev/models/user"
|
user_model "gitea.dev/models/user"
|
||||||
|
project "gitea.dev/services/projects"
|
||||||
"gitea.dev/tests"
|
"gitea.dev/tests"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
@@ -365,3 +367,28 @@ func TestOrgProjectFilterByMilestone(t *testing.T) {
|
|||||||
assert.NotContains(t, issueIDs, issue17.ID)
|
assert.NotContains(t, issueIDs, issue17.ID)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProjects(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
t.Run("LoadIssuesAssigneesForProject", func(t *testing.T) {
|
||||||
|
_ = db.TruncateBeans(t.Context(), "project_issue", "issue_assignees")
|
||||||
|
_ = db.Insert(t.Context(),
|
||||||
|
&project_model.ProjectIssue{ProjectID: 1, IssueID: 1},
|
||||||
|
&project_model.ProjectIssue{ProjectID: 1, IssueID: 6},
|
||||||
|
)
|
||||||
|
_ = db.Insert(t.Context(),
|
||||||
|
&issues_model.IssueAssignees{IssueID: 1, AssigneeID: 1},
|
||||||
|
&issues_model.IssueAssignees{IssueID: 1, AssigneeID: 10},
|
||||||
|
&issues_model.IssueAssignees{IssueID: 1, AssigneeID: 2},
|
||||||
|
&issues_model.IssueAssignees{IssueID: 6, AssigneeID: 2},
|
||||||
|
&issues_model.IssueAssignees{IssueID: 6, AssigneeID: 4},
|
||||||
|
)
|
||||||
|
assignees, err := project.LoadIssuesAssigneesForProject(t.Context(), 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, assignees, 4)
|
||||||
|
require.Equal(t, "user1", assignees[0].Name)
|
||||||
|
require.Equal(t, "user10", assignees[1].Name)
|
||||||
|
require.Equal(t, "user2", assignees[2].Name)
|
||||||
|
require.Equal(t, "user4", assignees[3].Name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user