Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix incorrect issue filter in user dashboard #23630

Closed
wants to merge 14 commits into from
47 changes: 47 additions & 0 deletions models/issues/issue_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}),
),
)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between these functions and the ones in repo_model? Maybe outline in a comment the differences?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The functions in repo_model are only used here (but will cause the issue). So we can remove them or move these new functions into repo_model. I didn't do that because I'm not sure whether the functions in repo_model are only designed for issuePullAccessibleRepoCond.

The difference is that the functions in repo_model use issue.repo_id in repo_ids to filter the issue, so all issues in these repos will be caught.
So we need use issue.id in issue_ids to get the correct issues.

21 changes: 11 additions & 10 deletions models/issues/issue_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -311,31 +311,32 @@ 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 {
unitType = unit.TypePullRequests
}
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
),
)
}
Expand Down
2 changes: 1 addition & 1 deletion models/issues/issue_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions models/issues/issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
},
},
{
Expand All @@ -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)
},
},
{
Expand Down
13 changes: 13 additions & 0 deletions models/repo/repo_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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").
Expand Down
32 changes: 17 additions & 15 deletions routers/web/user/home.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,21 +475,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
}
}

switch filterMode {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know whether we need to remove repoOpts above in this PR. it is not used.

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
Expand All @@ -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 {
Expand Down