mirror of
https://github.com/go-gitea/gitea
synced 2025-07-22 18:28:37 +00:00
Allow filtering issues by any assignee (#33343)
This is the opposite of the "No assignee" filter, it will match all issues that have at least one assignee. Before  After  --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -290,10 +290,10 @@ func SearchIssues(ctx *context.APIContext) {
|
||||
if ctx.IsSigned {
|
||||
ctxUserID := ctx.Doer.ID
|
||||
if ctx.FormBool("created") {
|
||||
searchOpt.PosterID = optional.Some(ctxUserID)
|
||||
searchOpt.PosterID = strconv.FormatInt(ctxUserID, 10)
|
||||
}
|
||||
if ctx.FormBool("assigned") {
|
||||
searchOpt.AssigneeID = optional.Some(ctxUserID)
|
||||
searchOpt.AssigneeID = strconv.FormatInt(ctxUserID, 10)
|
||||
}
|
||||
if ctx.FormBool("mentioned") {
|
||||
searchOpt.MentionID = optional.Some(ctxUserID)
|
||||
@@ -538,10 +538,10 @@ func ListIssues(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
if createdByID > 0 {
|
||||
searchOpt.PosterID = optional.Some(createdByID)
|
||||
searchOpt.PosterID = strconv.FormatInt(createdByID, 10)
|
||||
}
|
||||
if assignedByID > 0 {
|
||||
searchOpt.AssigneeID = optional.Some(assignedByID)
|
||||
searchOpt.AssigneeID = strconv.FormatInt(assignedByID, 10)
|
||||
}
|
||||
if mentionedByID > 0 {
|
||||
searchOpt.MentionID = optional.Some(mentionedByID)
|
||||
|
@@ -347,11 +347,11 @@ func ViewProject(ctx *context.Context) {
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
assigneeID := ctx.FormInt64("assignee") // TODO: use "optional" but not 0 in the future
|
||||
assigneeID := ctx.FormString("assignee")
|
||||
|
||||
opts := issues_model.IssuesOptions{
|
||||
LabelIDs: labelIDs,
|
||||
AssigneeID: optional.Some(assigneeID),
|
||||
AssigneeID: assigneeID,
|
||||
Owner: project.Owner,
|
||||
Doer: ctx.Doer,
|
||||
}
|
||||
|
@@ -208,10 +208,10 @@ func SearchIssues(ctx *context.Context) {
|
||||
if ctx.IsSigned {
|
||||
ctxUserID := ctx.Doer.ID
|
||||
if ctx.FormBool("created") {
|
||||
searchOpt.PosterID = optional.Some(ctxUserID)
|
||||
searchOpt.PosterID = strconv.FormatInt(ctxUserID, 10)
|
||||
}
|
||||
if ctx.FormBool("assigned") {
|
||||
searchOpt.AssigneeID = optional.Some(ctxUserID)
|
||||
searchOpt.AssigneeID = strconv.FormatInt(ctxUserID, 10)
|
||||
}
|
||||
if ctx.FormBool("mentioned") {
|
||||
searchOpt.MentionID = optional.Some(ctxUserID)
|
||||
@@ -373,10 +373,10 @@ func SearchRepoIssuesJSON(ctx *context.Context) {
|
||||
}
|
||||
|
||||
if createdByID > 0 {
|
||||
searchOpt.PosterID = optional.Some(createdByID)
|
||||
searchOpt.PosterID = strconv.FormatInt(createdByID, 10)
|
||||
}
|
||||
if assignedByID > 0 {
|
||||
searchOpt.AssigneeID = optional.Some(assignedByID)
|
||||
searchOpt.AssigneeID = strconv.FormatInt(assignedByID, 10)
|
||||
}
|
||||
if mentionedByID > 0 {
|
||||
searchOpt.MentionID = optional.Some(mentionedByID)
|
||||
@@ -490,7 +490,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
||||
viewType = "all"
|
||||
}
|
||||
|
||||
assigneeID := ctx.FormInt64("assignee") // TODO: use "optional" but not 0 in the future
|
||||
assigneeID := ctx.FormString("assignee")
|
||||
posterUsername := ctx.FormString("poster")
|
||||
posterUserID := shared_user.GetFilterUserIDByName(ctx, posterUsername)
|
||||
var mentionedID, reviewRequestedID, reviewedID int64
|
||||
@@ -498,11 +498,11 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
||||
if ctx.IsSigned {
|
||||
switch viewType {
|
||||
case "created_by":
|
||||
posterUserID = optional.Some(ctx.Doer.ID)
|
||||
posterUserID = strconv.FormatInt(ctx.Doer.ID, 10)
|
||||
case "mentioned":
|
||||
mentionedID = ctx.Doer.ID
|
||||
case "assigned":
|
||||
assigneeID = ctx.Doer.ID
|
||||
assigneeID = fmt.Sprint(ctx.Doer.ID)
|
||||
case "review_requested":
|
||||
reviewRequestedID = ctx.Doer.ID
|
||||
case "reviewed_by":
|
||||
@@ -532,7 +532,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
||||
LabelIDs: labelIDs,
|
||||
MilestoneIDs: mileIDs,
|
||||
ProjectID: projectID,
|
||||
AssigneeID: optional.Some(assigneeID),
|
||||
AssigneeID: assigneeID,
|
||||
MentionedID: mentionedID,
|
||||
PosterID: posterUserID,
|
||||
ReviewRequestedID: reviewRequestedID,
|
||||
@@ -613,7 +613,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
||||
PageSize: setting.UI.IssuePagingNum,
|
||||
},
|
||||
RepoIDs: []int64{repo.ID},
|
||||
AssigneeID: optional.Some(assigneeID),
|
||||
AssigneeID: assigneeID,
|
||||
PosterID: posterUserID,
|
||||
MentionedID: mentionedID,
|
||||
ReviewRequestedID: reviewRequestedID,
|
||||
|
@@ -315,12 +315,12 @@ func ViewProject(ctx *context.Context) {
|
||||
|
||||
labelIDs := issue.PrepareFilterIssueLabels(ctx, ctx.Repo.Repository.ID, ctx.Repo.Owner)
|
||||
|
||||
assigneeID := ctx.FormInt64("assignee") // TODO: use "optional" but not 0 in the future
|
||||
assigneeID := ctx.FormString("assignee")
|
||||
|
||||
issuesMap, err := project_service.LoadIssuesFromProject(ctx, project, &issues_model.IssuesOptions{
|
||||
RepoIDs: []int64{ctx.Repo.Repository.ID},
|
||||
LabelIDs: labelIDs,
|
||||
AssigneeID: optional.Some(assigneeID),
|
||||
AssigneeID: assigneeID,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadIssuesOfColumns", err)
|
||||
|
@@ -8,9 +8,7 @@ import (
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
)
|
||||
|
||||
func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
|
||||
@@ -34,19 +32,20 @@ func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
|
||||
// So it's better to make it work like GitHub: users could input username directly.
|
||||
// Since it only converts the username to ID directly and is only used internally (to search issues), so no permission check is needed.
|
||||
// Return values:
|
||||
// * nil: no filter
|
||||
// * some(id): match the id, the id could be -1 to match the issues without assignee
|
||||
// * some(NonExistingID): match no issue (due to the user doesn't exist)
|
||||
func GetFilterUserIDByName(ctx context.Context, name string) optional.Option[int64] {
|
||||
// * "": no filter
|
||||
// * "{the-id}": match the id
|
||||
// * "(none)": match no issue (due to the user doesn't exist)
|
||||
func GetFilterUserIDByName(ctx context.Context, name string) string {
|
||||
if name == "" {
|
||||
return optional.None[int64]()
|
||||
return ""
|
||||
}
|
||||
u, err := user.GetUserByName(ctx, name)
|
||||
if err != nil {
|
||||
if id, err := strconv.ParseInt(name, 10, 64); err == nil {
|
||||
return optional.Some(id)
|
||||
return strconv.FormatInt(id, 10)
|
||||
}
|
||||
return optional.Some(db.NonExistingID)
|
||||
// The "(none)" is for internal usage only: when doer tries to search non-existing user, use "(none)" to return empty result.
|
||||
return "(none)"
|
||||
}
|
||||
return optional.Some(u.ID)
|
||||
return strconv.FormatInt(u.ID, 10)
|
||||
}
|
||||
|
@@ -501,9 +501,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
case issues_model.FilterModeAll:
|
||||
case issues_model.FilterModeYourRepositories:
|
||||
case issues_model.FilterModeAssign:
|
||||
opts.AssigneeID = optional.Some(ctx.Doer.ID)
|
||||
opts.AssigneeID = strconv.FormatInt(ctx.Doer.ID, 10)
|
||||
case issues_model.FilterModeCreate:
|
||||
opts.PosterID = optional.Some(ctx.Doer.ID)
|
||||
opts.PosterID = strconv.FormatInt(ctx.Doer.ID, 10)
|
||||
case issues_model.FilterModeMention:
|
||||
opts.MentionedID = ctx.Doer.ID
|
||||
case issues_model.FilterModeReviewRequested:
|
||||
@@ -792,9 +792,9 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod
|
||||
case issues_model.FilterModeYourRepositories:
|
||||
openClosedOpts.AllPublic = false
|
||||
case issues_model.FilterModeAssign:
|
||||
openClosedOpts.AssigneeID = optional.Some(doerID)
|
||||
openClosedOpts.AssigneeID = strconv.FormatInt(doerID, 10)
|
||||
case issues_model.FilterModeCreate:
|
||||
openClosedOpts.PosterID = optional.Some(doerID)
|
||||
openClosedOpts.PosterID = strconv.FormatInt(doerID, 10)
|
||||
case issues_model.FilterModeMention:
|
||||
openClosedOpts.MentionID = optional.Some(doerID)
|
||||
case issues_model.FilterModeReviewRequested:
|
||||
@@ -816,8 +816,8 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod
|
||||
|
||||
// Below stats are for the left sidebar
|
||||
opts = opts.Copy(func(o *issue_indexer.SearchOptions) {
|
||||
o.AssigneeID = nil
|
||||
o.PosterID = nil
|
||||
o.AssigneeID = ""
|
||||
o.PosterID = ""
|
||||
o.MentionID = nil
|
||||
o.ReviewRequestedID = nil
|
||||
o.ReviewedID = nil
|
||||
@@ -827,11 +827,11 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = optional.Some(doerID) }))
|
||||
ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = strconv.FormatInt(doerID, 10) }))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = optional.Some(doerID) }))
|
||||
ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = strconv.FormatInt(doerID, 10) }))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user