diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index a932ac255436..f878c4e2694a 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -599,3 +599,50 @@ func (issues IssueList) GetApprovalCounts(ctx context.Context) (map[int64][]*Rev return approvalCountMap, nil } + +// UserAssignedIssueCond return user as assignee issues list +func UserAssignedIssueCond(userID int64) builder.Cond { + return builder.And( + builder.Eq{ + "repository.is_private": false, + }, + builder.In("issue.id", + builder.Select("issue_assignees.issue_id").From("issue_assignees"). + Where(builder.Eq{ + "issue_assignees.assignee_id": userID, + }), + ), + ) +} + +// UserMentionedIssueCond return user mentioned issues list +func UserMentionedIssueCond(userID int64) builder.Cond { + return builder.And( + builder.Eq{ + "repository.is_private": false, + }, + builder.In("issue.id", + builder.Select("issue_user.issue_id").From("issue_user"). + Where(builder.Eq{ + "issue_user.is_mentioned": true, + "issue_user.uid": userID, + }), + ), + ) +} + +// UserCreateIssueCond return user created issues list +func UserCreateIssueCond(userID int64, isPull bool) builder.Cond { + return builder.And( + builder.Eq{ + "repository.is_private": false, + }, + builder.In("issue.id", + builder.Select("issue.id").From("issue"). + Where(builder.Eq{ + "issue.poster_id": userID, + "issue.is_pull": isPull, + }), + ), + ) +} diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 3640351a4d46..ac17723fa673 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -262,7 +262,7 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { applyLabelsCondition(sess, opts) if opts.User != nil { - sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue())) + sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User, opts.Org, opts.Team, opts.IsPull.IsTrue())) } return sess @@ -311,7 +311,8 @@ func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Typ } // issuePullAccessibleRepoCond userID must not be zero, this condition require join repository table -func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organization.Organization, team *organization.Team, isPull bool) builder.Cond { +// user should not be nil +func issuePullAccessibleRepoCond(repoIDstr string, user *user_model.User, org *organization.Organization, team *organization.Team, isPull bool) builder.Cond { cond := builder.NewCond() unitType := unit.TypeIssues if isPull { @@ -319,23 +320,23 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati } if org != nil { if team != nil { - cond = cond.And(teamUnitsRepoCond(repoIDstr, userID, org.ID, team.ID, unitType)) // special team member repos + cond = cond.And(teamUnitsRepoCond(repoIDstr, user.ID, org.ID, team.ID, unitType)) // special team member repos } else { cond = cond.And( builder.Or( - repo_model.UserOrgUnitRepoCond(repoIDstr, userID, org.ID, unitType), // team member repos - repo_model.UserOrgPublicUnitRepoCond(userID, org.ID), // user org public non-member repos, TODO: check repo has issues + repo_model.UserOrgUnitRepoCond(repoIDstr, user.ID, org.ID, unitType), // team member repos + repo_model.UserOrgPublicUnitRepoCond(user.ID, org.ID), // user org public non-member repos, TODO: check repo has issues ), ) } } else { cond = cond.And( builder.Or( - repo_model.UserOwnedRepoCond(userID), // owned repos - repo_model.UserAccessRepoCond(repoIDstr, userID), // user can access repo in a unit independent way - repo_model.UserAssignedRepoCond(repoIDstr, userID), // user has been assigned accessible public repos - repo_model.UserMentionedRepoCond(repoIDstr, userID), // user has been mentioned accessible public repos - repo_model.UserCreateIssueRepoCond(repoIDstr, userID, isPull), // user has created issue/pr accessible public repos + repo_model.UserOwnedRepoCond(user.ID), // owned repos + repo_model.UserUnitAccessRepoCond(repoIDstr, user, unitType), // user can access repo in a unit independent way + UserAssignedIssueCond(user.ID), // user has been assigned accessible public repos + UserMentionedIssueCond(user.ID), // user has been mentioned accessible public repos + UserCreateIssueCond(user.ID, isPull), // user has created issue/pr accessible public repos ), ) } diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go index 1654e6ce756f..7f4458e2090a 100644 --- a/models/issues/issue_stats.go +++ b/models/issues/issue_stats.go @@ -208,7 +208,7 @@ func GetUserIssueStats(filterMode int, opts IssuesOptions) (*IssueStats, error) } if opts.User != nil { - cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue())) + cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User, opts.Org, opts.Team, opts.IsPull.IsTrue())) } sess := func(cond builder.Cond) *xorm.Session { diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go index 0f2ceadc6b6e..36dd09544b35 100644 --- a/models/issues/issue_test.go +++ b/models/issues/issue_test.go @@ -223,7 +223,7 @@ func TestGetUserIssueStats(t *testing.T) { AssignCount: 1, // 6 CreateCount: 1, // 6 OpenCount: 1, // 6 - ClosedCount: 1, // 1 + ClosedCount: 0, // issue(id:5) is posted by user2 and user1 is not owner or collaborator of repo(id:1) }, }, { @@ -235,11 +235,11 @@ func TestGetUserIssueStats(t *testing.T) { IsClosed: util.OptionalBoolTrue, }, issues_model.IssueStats{ - YourRepositoriesCount: 1, // 6 + YourRepositoriesCount: 0, // 6 AssignCount: 0, CreateCount: 0, OpenCount: 1, // 6 - ClosedCount: 1, // 1 + ClosedCount: 0, // issue(id:5) is posted by user2 and user1 is not owner or collaborator of repo(id:1) }, }, { diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index a0485ed8d417..2b3bf2046819 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -260,6 +260,19 @@ func UserAccessRepoCond(idStr string, userID int64) builder.Cond { ) } +// UserUnitAccessRepoCond returns a condition for selecting all repositories a user has unit independent access to and can access the special unit +func UserUnitAccessRepoCond(idStr string, user *user_model.User, unitType unit.Type) builder.Cond { + return builder.In(idStr, builder.Select("`access`.repo_id"). + From("`access`"). + Join("INNER", "repository", "`repository`.id = `access`.repo_id"). + Where(builder.And( + builder.Eq{"`access`.user_id": user.ID}, + builder.Gt{"`access`.mode": int(perm.AccessModeNone)}, + AccessibleRepositoryCondition(user, unitType), + )), + ) +} + // userCollaborationRepoCond returns a condition for selecting all repositories a user is collaborator in func UserCollaborationRepoCond(idStr string, userID int64) builder.Cond { return builder.In(idStr, builder.Select("repo_id"). diff --git a/routers/web/user/home.go b/routers/web/user/home.go index d1a4877e6d4b..c0b9762e0c0f 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -475,21 +475,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { } } - switch filterMode { - case issues_model.FilterModeAll: - case issues_model.FilterModeYourRepositories: - case issues_model.FilterModeAssign: - opts.AssigneeID = ctx.Doer.ID - case issues_model.FilterModeCreate: - opts.PosterID = ctx.Doer.ID - case issues_model.FilterModeMention: - opts.MentionedID = ctx.Doer.ID - case issues_model.FilterModeReviewRequested: - opts.ReviewRequestedID = ctx.Doer.ID - case issues_model.FilterModeReviewed: - opts.ReviewedID = ctx.Doer.ID - } - // keyword holds the search term entered into the search field. keyword := strings.Trim(ctx.FormString("q"), " ") ctx.Data["Keyword"] = keyword @@ -506,6 +491,23 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { return } + // In order to display all issues count in repo filter, + // we need to check filterMode after CountIssuesByRepo + switch filterMode { + case issues_model.FilterModeAll: + case issues_model.FilterModeYourRepositories: + case issues_model.FilterModeAssign: + opts.AssigneeID = ctx.Doer.ID + case issues_model.FilterModeCreate: + opts.PosterID = ctx.Doer.ID + case issues_model.FilterModeMention: + opts.MentionedID = ctx.Doer.ID + case issues_model.FilterModeReviewRequested: + opts.ReviewRequestedID = ctx.Doer.ID + case issues_model.FilterModeReviewed: + opts.ReviewedID = ctx.Doer.ID + } + // Make sure page number is at least 1. Will be posted to ctx.Data. page := ctx.FormInt("page") if page <= 1 {