aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--models/issue.go78
-rw-r--r--models/issue_list.go11
-rw-r--r--models/issue_test.go77
-rw-r--r--models/repo/repo.go31
-rw-r--r--models/repo/repo_list.go46
-rw-r--r--models/repo_list.go226
-rw-r--r--models/repo_permission.go20
-rw-r--r--models/user.go104
-rw-r--r--models/user_test.go22
-rw-r--r--routers/web/user/home.go201
-rw-r--r--templates/user/dashboard/issues.tmpl4
11 files changed, 397 insertions, 423 deletions
diff --git a/models/issue.go b/models/issue.go
index b05c8cccdd..f0040fbbc1 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -1186,6 +1186,9 @@ type IssuesOptions struct {
// prioritize issues from this repo
PriorityRepoID int64
IsArchived util.OptionalBool
+ Org *Organization // issues permission scope
+ Team *Team // issues permission scope
+ User *user_model.User // issues permission scope
}
// sortIssuesSession sort an issues-related session based on the provided
@@ -1337,6 +1340,44 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
From("milestone").
Where(builder.In("name", opts.IncludeMilestones)))
}
+
+ if opts.User != nil {
+ sess.And(
+ issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue()),
+ )
+ }
+}
+
+// issuePullAccessibleRepoCond userID must not be zero, this condition require join repository table
+func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *Organization, team *Team, isPull bool) builder.Cond {
+ var cond = builder.NewCond()
+ var 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
+ } else {
+ cond = cond.And(
+ builder.Or(
+ userOrgUnitRepoCond(repoIDstr, userID, org.ID, unitType), // team member repos
+ userOrgPublicUnitRepoCond(userID, org.ID), // user org public non-member repos, TODO: check repo has issues
+ ),
+ )
+ }
+ } else {
+ cond = cond.And(
+ builder.Or(
+ userOwnedRepoCond(userID), // owned repos
+ userCollaborationRepoCond(repoIDstr, userID), // collaboration repos
+ userAssignedRepoCond(repoIDstr, userID), // user has been assigned accessible public repos
+ userMentionedRepoCond(repoIDstr, userID), // user has been mentioned accessible public repos
+ userCreateIssueRepoCond(repoIDstr, userID, isPull), // user has created issue/pr accessible public repos
+ ),
+ )
+ }
+ return cond
}
func applyReposCondition(sess *xorm.Session, repoIDs []int64) *xorm.Session {
@@ -1646,15 +1687,16 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
// UserIssueStatsOptions contains parameters accepted by GetUserIssueStats.
type UserIssueStatsOptions struct {
- UserID int64
- RepoIDs []int64
- UserRepoIDs []int64
- FilterMode int
- IsPull bool
- IsClosed bool
- IssueIDs []int64
- IsArchived util.OptionalBool
- LabelIDs []int64
+ UserID int64
+ RepoIDs []int64
+ FilterMode int
+ IsPull bool
+ IsClosed bool
+ IssueIDs []int64
+ IsArchived util.OptionalBool
+ LabelIDs []int64
+ Org *Organization
+ Team *Team
}
// GetUserIssueStats returns issue statistic information for dashboard by given conditions.
@@ -1671,28 +1713,34 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
cond = cond.And(builder.In("issue.id", opts.IssueIDs))
}
+ if opts.UserID > 0 {
+ cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.UserID, opts.Org, opts.Team, opts.IsPull))
+ }
+
sess := func(cond builder.Cond) *xorm.Session {
s := db.GetEngine(db.DefaultContext).Where(cond)
if len(opts.LabelIDs) > 0 {
s.Join("INNER", "issue_label", "issue_label.issue_id = issue.id").
In("issue_label.label_id", opts.LabelIDs)
}
- if opts.IsArchived != util.OptionalBoolNone {
- s.Join("INNER", "repository", "issue.repo_id = repository.id").
- And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()})
+ if opts.UserID > 0 || opts.IsArchived != util.OptionalBoolNone {
+ s.Join("INNER", "repository", "issue.repo_id = repository.id")
+ if opts.IsArchived != util.OptionalBoolNone {
+ s.And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()})
+ }
}
return s
}
switch opts.FilterMode {
case FilterModeAll:
- stats.OpenCount, err = applyReposCondition(sess(cond), opts.UserRepoIDs).
+ stats.OpenCount, err = sess(cond).
And("issue.is_closed = ?", false).
Count(new(Issue))
if err != nil {
return nil, err
}
- stats.ClosedCount, err = applyReposCondition(sess(cond), opts.UserRepoIDs).
+ stats.ClosedCount, err = sess(cond).
And("issue.is_closed = ?", true).
Count(new(Issue))
if err != nil {
@@ -1768,7 +1816,7 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
return nil, err
}
- stats.YourRepositoriesCount, err = applyReposCondition(sess(cond), opts.UserRepoIDs).Count(new(Issue))
+ stats.YourRepositoriesCount, err = sess(cond).Count(new(Issue))
if err != nil {
return nil, err
}
diff --git a/models/issue_list.go b/models/issue_list.go
index 5d8a9f6921..b516e7336e 100644
--- a/models/issue_list.go
+++ b/models/issue_list.go
@@ -25,6 +25,9 @@ const (
func (issues IssueList) getRepoIDs() []int64 {
repoIDs := make(map[int64]struct{}, len(issues))
for _, issue := range issues {
+ if issue.Repo != nil {
+ continue
+ }
if _, ok := repoIDs[issue.RepoID]; !ok {
repoIDs[issue.RepoID] = struct{}{}
}
@@ -56,8 +59,12 @@ func (issues IssueList) loadRepositories(e db.Engine) ([]*repo_model.Repository,
}
for _, issue := range issues {
- issue.Repo = repoMaps[issue.RepoID]
- if issue.PullRequest != nil {
+ if issue.Repo == nil {
+ issue.Repo = repoMaps[issue.RepoID]
+ } else {
+ repoMaps[issue.RepoID] = issue.Repo
+ }
+ if issue.PullRequest != nil && issue.PullRequest.BaseRepo == nil {
issue.PullRequest.BaseRepo = issue.Repo
}
}
diff --git a/models/issue_test.go b/models/issue_test.go
index eadeb66ab9..cebf20af9b 100644
--- a/models/issue_test.go
+++ b/models/issue_test.go
@@ -206,52 +206,52 @@ func TestGetUserIssueStats(t *testing.T) {
FilterMode: FilterModeAll,
},
IssueStats{
- YourRepositoriesCount: 0,
- AssignCount: 1,
- CreateCount: 1,
- OpenCount: 0,
- ClosedCount: 0,
+ YourRepositoriesCount: 1, // 6
+ AssignCount: 1, // 6
+ CreateCount: 1, // 6
+ OpenCount: 1, // 6
+ ClosedCount: 1, // 1
},
},
{
UserIssueStatsOptions{
UserID: 1,
- FilterMode: FilterModeAssign,
+ RepoIDs: []int64{1},
+ FilterMode: FilterModeAll,
+ IsClosed: true,
},
IssueStats{
- YourRepositoriesCount: 0,
- AssignCount: 2,
- CreateCount: 2,
- OpenCount: 2,
- ClosedCount: 0,
+ YourRepositoriesCount: 1, // 6
+ AssignCount: 0,
+ CreateCount: 0,
+ OpenCount: 1, // 6
+ ClosedCount: 1, // 1
},
},
{
UserIssueStatsOptions{
UserID: 1,
- FilterMode: FilterModeCreate,
+ FilterMode: FilterModeAssign,
},
IssueStats{
- YourRepositoriesCount: 0,
- AssignCount: 2,
- CreateCount: 2,
- OpenCount: 2,
+ YourRepositoriesCount: 1, // 6
+ AssignCount: 1, // 6
+ CreateCount: 1, // 6
+ OpenCount: 1, // 6
ClosedCount: 0,
},
},
{
UserIssueStatsOptions{
- UserID: 2,
- UserRepoIDs: []int64{1, 2},
- FilterMode: FilterModeAll,
- IsClosed: true,
+ UserID: 1,
+ FilterMode: FilterModeCreate,
},
IssueStats{
- YourRepositoriesCount: 2,
- AssignCount: 0,
- CreateCount: 2,
- OpenCount: 2,
- ClosedCount: 2,
+ YourRepositoriesCount: 1, // 6
+ AssignCount: 1, // 6
+ CreateCount: 1, // 6
+ OpenCount: 1, // 6
+ ClosedCount: 0,
},
},
{
@@ -260,9 +260,10 @@ func TestGetUserIssueStats(t *testing.T) {
FilterMode: FilterModeMention,
},
IssueStats{
- YourRepositoriesCount: 0,
- AssignCount: 2,
- CreateCount: 2,
+ YourRepositoriesCount: 1, // 6
+ AssignCount: 1, // 6
+ CreateCount: 1, // 6
+ MentionCount: 0,
OpenCount: 0,
ClosedCount: 0,
},
@@ -274,19 +275,21 @@ func TestGetUserIssueStats(t *testing.T) {
IssueIDs: []int64{1},
},
IssueStats{
- YourRepositoriesCount: 0,
- AssignCount: 1,
- CreateCount: 1,
- OpenCount: 1,
+ YourRepositoriesCount: 1, // 1
+ AssignCount: 1, // 1
+ CreateCount: 1, // 1
+ OpenCount: 1, // 1
ClosedCount: 0,
},
},
} {
- stats, err := GetUserIssueStats(test.Opts)
- if !assert.NoError(t, err) {
- continue
- }
- assert.Equal(t, test.ExpectedIssueStats, *stats)
+ t.Run(fmt.Sprintf("%#v", test.Opts), func(t *testing.T) {
+ stats, err := GetUserIssueStats(test.Opts)
+ if !assert.NoError(t, err) {
+ return
+ }
+ assert.Equal(t, test.ExpectedIssueStats, *stats)
+ })
}
}
diff --git a/models/repo/repo.go b/models/repo/repo.go
index d0136e9c66..5108231cd8 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -731,15 +731,6 @@ func CountUserRepositories(userID int64, private bool) int64 {
return countRepositories(userID, private)
}
-// GetUserMirrorRepositories returns a list of mirror repositories of given user.
-func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
- repos := make([]*Repository, 0, 10)
- return repos, db.GetEngine(db.DefaultContext).
- Where("owner_id = ?", userID).
- And("is_mirror = ?", true).
- Find(&repos)
-}
-
func getRepositoryCount(e db.Engine, ownerID int64) (int64, error) {
return e.Count(&Repository{OwnerID: ownerID})
}
@@ -766,25 +757,3 @@ func GetPublicRepositoryCount(u *user_model.User) (int64, error) {
func GetPrivateRepositoryCount(u *user_model.User) (int64, error) {
return getPrivateRepositoryCount(db.GetEngine(db.DefaultContext), u)
}
-
-// IterateRepository iterate repositories
-func IterateRepository(f func(repo *Repository) error) error {
- var start int
- batchSize := setting.Database.IterateBufferSize
- for {
- repos := make([]*Repository, 0, batchSize)
- if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&repos); err != nil {
- return err
- }
- if len(repos) == 0 {
- return nil
- }
- start += len(repos)
-
- for _, repo := range repos {
- if err := f(repo); err != nil {
- return err
- }
- }
- }
-}
diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go
new file mode 100644
index 0000000000..571604a2c2
--- /dev/null
+++ b/models/repo/repo_list.go
@@ -0,0 +1,46 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/setting"
+)
+
+// GetUserMirrorRepositories returns a list of mirror repositories of given user.
+func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
+ repos := make([]*Repository, 0, 10)
+ return repos, db.GetEngine(db.DefaultContext).
+ Where("owner_id = ?", userID).
+ And("is_mirror = ?", true).
+ Find(&repos)
+}
+
+// IterateRepository iterate repositories
+func IterateRepository(f func(repo *Repository) error) error {
+ var start int
+ batchSize := setting.Database.IterateBufferSize
+ for {
+ repos := make([]*Repository, 0, batchSize)
+ if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&repos); err != nil {
+ return err
+ }
+ if len(repos) == 0 {
+ return nil
+ }
+ start += len(repos)
+
+ for _, repo := range repos {
+ if err := f(repo); err != nil {
+ return err
+ }
+ }
+ }
+}
+
+// FindReposMapByIDs find repos as map
+func FindReposMapByIDs(repoIDs []int64, res map[int64]*Repository) error {
+ return db.GetEngine(db.DefaultContext).In("id", repoIDs).Find(&res)
+}
diff --git a/models/repo_list.go b/models/repo_list.go
index 02440bec93..9bda0d5a37 100644
--- a/models/repo_list.go
+++ b/models/repo_list.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
@@ -145,6 +146,188 @@ type SearchRepoOptions struct {
LowerNames []string
}
+// SearchOrderBy is used to sort the result
+type SearchOrderBy string
+
+func (s SearchOrderBy) String() string {
+ return string(s)
+}
+
+// Strings for sorting result
+const (
+ SearchOrderByAlphabetically SearchOrderBy = "name ASC"
+ SearchOrderByAlphabeticallyReverse SearchOrderBy = "name DESC"
+ SearchOrderByLeastUpdated SearchOrderBy = "updated_unix ASC"
+ SearchOrderByRecentUpdated SearchOrderBy = "updated_unix DESC"
+ SearchOrderByOldest SearchOrderBy = "created_unix ASC"
+ SearchOrderByNewest SearchOrderBy = "created_unix DESC"
+ SearchOrderBySize SearchOrderBy = "size ASC"
+ SearchOrderBySizeReverse SearchOrderBy = "size DESC"
+ SearchOrderByID SearchOrderBy = "id ASC"
+ SearchOrderByIDReverse SearchOrderBy = "id DESC"
+ SearchOrderByStars SearchOrderBy = "num_stars ASC"
+ SearchOrderByStarsReverse SearchOrderBy = "num_stars DESC"
+ SearchOrderByForks SearchOrderBy = "num_forks ASC"
+ SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
+)
+
+// userOwnedRepoCond returns user ownered repositories
+func userOwnedRepoCond(userID int64) builder.Cond {
+ return builder.Eq{
+ "repository.owner_id": userID,
+ }
+}
+
+// userAssignedRepoCond return user as assignee repositories list
+func userAssignedRepoCond(id string, userID int64) builder.Cond {
+ return builder.And(
+ builder.Eq{
+ "repository.is_private": false,
+ },
+ builder.In(id,
+ builder.Select("issue.repo_id").From("issue_assignees").
+ InnerJoin("issue", "issue.id = issue_assignees.issue_id").
+ Where(builder.Eq{
+ "issue_assignees.assignee_id": userID,
+ }),
+ ),
+ )
+}
+
+// userCreateIssueRepoCond return user created issues repositories list
+func userCreateIssueRepoCond(id string, userID int64, isPull bool) builder.Cond {
+ return builder.And(
+ builder.Eq{
+ "repository.is_private": false,
+ },
+ builder.In(id,
+ builder.Select("issue.repo_id").From("issue").
+ Where(builder.Eq{
+ "issue.poster_id": userID,
+ "issue.is_pull": isPull,
+ }),
+ ),
+ )
+}
+
+// userMentionedRepoCond return user metinoed repositories list
+func userMentionedRepoCond(id string, userID int64) builder.Cond {
+ return builder.And(
+ builder.Eq{
+ "repository.is_private": false,
+ },
+ builder.In(id,
+ builder.Select("issue.repo_id").From("issue_user").
+ InnerJoin("issue", "issue.id = issue_user.issue_id").
+ Where(builder.Eq{
+ "issue_user.is_mentioned": true,
+ "issue_user.uid": userID,
+ }),
+ ),
+ )
+}
+
+// teamUnitsRepoCond returns query condition for those repo id in the special org team with special units access
+func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Type) builder.Cond {
+ return builder.In(id,
+ builder.Select("repo_id").From("team_repo").Where(
+ builder.Eq{
+ "team_id": teamID,
+ }.And(
+ builder.In(
+ "team_id", builder.Select("team_id").From("team_user").Where(
+ builder.Eq{
+ "uid": userID,
+ },
+ ),
+ )).And(
+ builder.In(
+ "team_id", builder.Select("team_id").From("team_unit").Where(
+ builder.Eq{
+ "org_id": orgID,
+ }.And(
+ builder.In("type", units),
+ ),
+ ),
+ ),
+ ),
+ ))
+}
+
+// userCollaborationRepoCond returns user as collabrators repositories list
+func userCollaborationRepoCond(idStr string, userID int64) builder.Cond {
+ return builder.In(idStr, builder.Select("repo_id").
+ From("`access`").
+ Where(builder.And(
+ builder.Eq{"user_id": userID},
+ builder.Gt{"mode": int(perm.AccessModeNone)},
+ )),
+ )
+}
+
+// userOrgTeamRepoCond selects repos that the given user has access to through team membership
+func userOrgTeamRepoCond(idStr string, userID int64) builder.Cond {
+ return builder.In(idStr, userOrgTeamRepoBuilder(userID))
+}
+
+// userOrgTeamRepoBuilder returns repo ids where user's teams can access.
+func userOrgTeamRepoBuilder(userID int64) *builder.Builder {
+ return builder.Select("`team_repo`.repo_id").
+ From("team_repo").
+ Join("INNER", "team_user", "`team_user`.team_id = `team_repo`.team_id").
+ Where(builder.Eq{"`team_user`.uid": userID})
+}
+
+// userOrgTeamUnitRepoBuilder returns repo ids where user's teams can access the special unit.
+func userOrgTeamUnitRepoBuilder(userID int64, unitType unit.Type) *builder.Builder {
+ return userOrgTeamRepoBuilder(userID).
+ Join("INNER", "team_unit", "`team_unit`.team_id = `team_repo`.team_id").
+ Where(builder.Eq{"`team_unit`.`type`": unitType})
+}
+
+// userOrgUnitRepoCond selects repos that the given user has access to through org and the special unit
+func userOrgUnitRepoCond(idStr string, userID, orgID int64, unitType unit.Type) builder.Cond {
+ return builder.In(idStr,
+ userOrgTeamUnitRepoBuilder(userID, unitType).
+ And(builder.Eq{"org_id": orgID}),
+ )
+}
+
+// userOrgPublicRepoCond returns the condition that one user could access all public repositories in organizations
+func userOrgPublicRepoCond(userID int64) builder.Cond {
+ return builder.And(
+ builder.Eq{"`repository`.is_private": false},
+ builder.In("`repository`.owner_id",
+ builder.Select("`org_user`.org_id").
+ From("org_user").
+ Where(builder.Eq{"`org_user`.uid": userID}),
+ ),
+ )
+}
+
+// userOrgPublicRepoCondPrivate returns the condition that one user could access all public repositories in private organizations
+func userOrgPublicRepoCondPrivate(userID int64) builder.Cond {
+ return builder.And(
+ builder.Eq{"`repository`.is_private": false},
+ builder.In("`repository`.owner_id",
+ builder.Select("`org_user`.org_id").
+ From("org_user").
+ Join("INNER", "`user`", "`user`.id = `org_user`.org_id").
+ Where(builder.Eq{
+ "`org_user`.uid": userID,
+ "`user`.`type`": user_model.UserTypeOrganization,
+ "`user`.visibility": structs.VisibleTypePrivate,
+ }),
+ ),
+ )
+}
+
+// userOrgPublicUnitRepoCond returns the condition that one user could access all public repositories in the special organization
+func userOrgPublicUnitRepoCond(userID, orgID int64) builder.Cond {
+ return userOrgPublicRepoCond(userID).
+ And(builder.Eq{"`repository`.owner_id": orgID})
+}
+
// SearchRepositoryCondition creates a query condition according search repository options
func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
cond := builder.NewCond()
@@ -198,27 +381,12 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
// 2. But we can see because of:
builder.Or(
// A. We have access
- builder.In("`repository`.id",
- builder.Select("`access`.repo_id").
- From("access").
- Where(builder.Eq{"`access`.user_id": opts.OwnerID})),
+ userCollaborationRepoCond("`repository`.id", opts.OwnerID),
// B. We are in a team for
- builder.In("`repository`.id", builder.Select("`team_repo`.repo_id").
- From("team_repo").
- Where(builder.Eq{"`team_user`.uid": opts.OwnerID}).
- Join("INNER", "team_user", "`team_user`.team_id = `team_repo`.team_id")),
- // C. Public repositories in private organizations that we are member of
- builder.And(
- builder.Eq{"`repository`.is_private": false},
- builder.In("`repository`.owner_id",
- builder.Select("`org_user`.org_id").
- From("org_user").
- Join("INNER", "`user`", "`user`.id = `org_user`.org_id").
- Where(builder.Eq{
- "`org_user`.uid": opts.OwnerID,
- "`user`.type": user_model.UserTypeOrganization,
- "`user`.visibility": structs.VisibleTypePrivate,
- })))),
+ userOrgTeamRepoCond("`repository`.id", opts.OwnerID),
+ // C. Public repositories in organizations that we are member of
+ userOrgPublicRepoCondPrivate(opts.OwnerID),
+ ),
)
if !opts.Private {
collaborateCond = collaborateCond.And(builder.Expr("owner_id NOT IN (SELECT org_id FROM org_user WHERE org_user.uid = ? AND org_user.is_public = ?)", opts.OwnerID, false))
@@ -389,24 +557,14 @@ func accessibleRepositoryCondition(user *user_model.User) builder.Cond {
if user != nil {
cond = cond.Or(
// 2. Be able to see all repositories that we have access to
- builder.In("`repository`.id", builder.Select("repo_id").
- From("`access`").
- Where(builder.And(
- builder.Eq{"user_id": user.ID},
- builder.Gt{"mode": int(perm.AccessModeNone)}))),
+ userCollaborationRepoCond("`repository`.id", user.ID),
// 3. Repositories that we directly own
builder.Eq{"`repository`.owner_id": user.ID},
// 4. Be able to see all repositories that we are in a team
- builder.In("`repository`.id", builder.Select("`team_repo`.repo_id").
- From("team_repo").
- Where(builder.Eq{"`team_user`.uid": user.ID}).
- Join("INNER", "team_user", "`team_user`.team_id = `team_repo`.team_id")),
+ userOrgTeamRepoCond("`repository`.id", user.ID),
// 5. Be able to see all public repos in private organizations that we are an org_user of
- builder.And(builder.Eq{"`repository`.is_private": false},
- builder.In("`repository`.owner_id",
- builder.Select("`org_user`.org_id").
- From("org_user").
- Where(builder.Eq{"`org_user`.uid": user.ID}))))
+ userOrgPublicRepoCond(user.ID),
+ )
}
return cond
diff --git a/models/repo_permission.go b/models/repo_permission.go
index 45878c8ba4..40b63aa804 100644
--- a/models/repo_permission.go
+++ b/models/repo_permission.go
@@ -400,26 +400,6 @@ func HasAccess(userID int64, repo *repo_model.Repository) (bool, error) {
return hasAccess(db.DefaultContext, userID, repo)
}
-// FilterOutRepoIdsWithoutUnitAccess filter out repos where user has no access to repositories
-func FilterOutRepoIdsWithoutUnitAccess(u *user_model.User, repoIDs []int64, units ...unit.Type) ([]int64, error) {
- i := 0
- for _, rID := range repoIDs {
- repo, err := repo_model.GetRepositoryByID(rID)
- if err != nil {
- return nil, err
- }
- perm, err := GetUserRepoPermission(repo, u)
- if err != nil {
- return nil, err
- }
- if perm.CanReadAny(units...) {
- repoIDs[i] = rID
- i++
- }
- }
- return repoIDs[:i], nil
-}
-
// GetRepoReaders returns all users that have explicit read access or higher to the repository.
func GetRepoReaders(repo *repo_model.Repository) (_ []*user_model.User, err error) {
return getUsersWithAccessMode(db.DefaultContext, repo, perm_model.AccessModeRead)
diff --git a/models/user.go b/models/user.go
index 2e7a84273f..5f7bedd36d 100644
--- a/models/user.go
+++ b/models/user.go
@@ -15,7 +15,6 @@ import (
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
@@ -30,109 +29,6 @@ func GetOrganizationCount(ctx context.Context, u *user_model.User) (int64, error
Count(new(OrgUser))
}
-// GetRepositoryIDs returns repositories IDs where user owned and has unittypes
-// Caller shall check that units is not globally disabled
-func GetRepositoryIDs(u *user_model.User, units ...unit.Type) ([]int64, error) {
- var ids []int64
-
- sess := db.GetEngine(db.DefaultContext).Table("repository").Cols("repository.id")
-
- if len(units) > 0 {
- sess = sess.Join("INNER", "repo_unit", "repository.id = repo_unit.repo_id")
- sess = sess.In("repo_unit.type", units)
- }
-
- return ids, sess.Where("owner_id = ?", u.ID).Find(&ids)
-}
-
-// GetActiveRepositoryIDs returns non-archived repositories IDs where user owned and has unittypes
-// Caller shall check that units is not globally disabled
-func GetActiveRepositoryIDs(u *user_model.User, units ...unit.Type) ([]int64, error) {
- var ids []int64
-
- sess := db.GetEngine(db.DefaultContext).Table("repository").Cols("repository.id")
-
- if len(units) > 0 {
- sess = sess.Join("INNER", "repo_unit", "repository.id = repo_unit.repo_id")
- sess = sess.In("repo_unit.type", units)
- }
-
- sess.Where(builder.Eq{"is_archived": false})
-
- return ids, sess.Where("owner_id = ?", u.ID).GroupBy("repository.id").Find(&ids)
-}
-
-// GetOrgRepositoryIDs returns repositories IDs where user's team owned and has unittypes
-// Caller shall check that units is not globally disabled
-func GetOrgRepositoryIDs(u *user_model.User, units ...unit.Type) ([]int64, error) {
- var ids []int64
-
- if err := db.GetEngine(db.DefaultContext).Table("repository").
- Cols("repository.id").
- Join("INNER", "team_user", "repository.owner_id = team_user.org_id").
- Join("INNER", "team_repo", "(? != ? and repository.is_private != ?) OR (team_user.team_id = team_repo.team_id AND repository.id = team_repo.repo_id)", true, u.IsRestricted, true).
- Where("team_user.uid = ?", u.ID).
- GroupBy("repository.id").Find(&ids); err != nil {
- return nil, err
- }
-
- if len(units) > 0 {
- return FilterOutRepoIdsWithoutUnitAccess(u, ids, units...)
- }
-
- return ids, nil
-}
-
-// GetActiveOrgRepositoryIDs returns non-archived repositories IDs where user's team owned and has unittypes
-// Caller shall check that units is not globally disabled
-func GetActiveOrgRepositoryIDs(u *user_model.User, units ...unit.Type) ([]int64, error) {
- var ids []int64
-
- if err := db.GetEngine(db.DefaultContext).Table("repository").
- Cols("repository.id").
- Join("INNER", "team_user", "repository.owner_id = team_user.org_id").
- Join("INNER", "team_repo", "(? != ? and repository.is_private != ?) OR (team_user.team_id = team_repo.team_id AND repository.id = team_repo.repo_id)", true, u.IsRestricted, true).
- Where("team_user.uid = ?", u.ID).
- Where(builder.Eq{"is_archived": false}).
- GroupBy("repository.id").Find(&ids); err != nil {
- return nil, err
- }
-
- if len(units) > 0 {
- return FilterOutRepoIdsWithoutUnitAccess(u, ids, units...)
- }
-
- return ids, nil
-}
-
-// GetAccessRepoIDs returns all repositories IDs where user's or user is a team member organizations
-// Caller shall check that units is not globally disabled
-func GetAccessRepoIDs(u *user_model.User, units ...unit.Type) ([]int64, error) {
- ids, err := GetRepositoryIDs(u, units...)
- if err != nil {
- return nil, err
- }
- ids2, err := GetOrgRepositoryIDs(u, units...)
- if err != nil {
- return nil, err
- }
- return append(ids, ids2...), nil
-}
-
-// GetActiveAccessRepoIDs returns all non-archived repositories IDs where user's or user is a team member organizations
-// Caller shall check that units is not globally disabled
-func GetActiveAccessRepoIDs(u *user_model.User, units ...unit.Type) ([]int64, error) {
- ids, err := GetActiveRepositoryIDs(u, units...)
- if err != nil {
- return nil, err
- }
- ids2, err := GetActiveOrgRepositoryIDs(u, units...)
- if err != nil {
- return nil, err
- }
- return append(ids, ids2...), nil
-}
-
// deleteBeans deletes all given beans, beans should contain delete conditions.
func deleteBeans(e db.Engine, beans ...interface{}) (err error) {
for i := range beans {
diff --git a/models/user_test.go b/models/user_test.go
index 4749a3af73..83201ff4cb 100644
--- a/models/user_test.go
+++ b/models/user_test.go
@@ -98,25 +98,3 @@ func testIsUserOrgOwner(t *testing.T, uid, orgID int64, expected bool) {
assert.NoError(t, err)
assert.Equal(t, expected, is)
}
-
-func TestGetOrgRepositoryIDs(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
- user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
-
- accessibleRepos, err := GetOrgRepositoryIDs(user2)
- assert.NoError(t, err)
- // User 2's team has access to private repos 3, 5, repo 32 is a public repo of the organization
- assert.Equal(t, []int64{3, 5, 23, 24, 32}, accessibleRepos)
-
- accessibleRepos, err = GetOrgRepositoryIDs(user4)
- assert.NoError(t, err)
- // User 4's team has access to private repo 3, repo 32 is a public repo of the organization
- assert.Equal(t, []int64{3, 32}, accessibleRepos)
-
- accessibleRepos, err = GetOrgRepositoryIDs(user5)
- assert.NoError(t, err)
- // User 5's team has no access to any repo
- assert.Len(t, accessibleRepos, 0)
-}
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index 975262cf9c..8bbc6dd0c3 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -403,27 +403,26 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// - Count Issues by repo
// --------------------------------------------------------------------------
- isPullList := unitType == unit.TypePullRequests
- opts := &models.IssuesOptions{
- IsPull: util.OptionalBoolOf(isPullList),
- SortType: sortType,
- IsArchived: util.OptionalBoolFalse,
- }
-
// Get repository IDs where User/Org/Team has access.
var team *models.Team
+ var org *models.Organization
if ctx.Org != nil {
+ org = ctx.Org.Organization
team = ctx.Org.Team
}
- userRepoIDs, err := getActiveUserRepoIDs(ctxUser, team, unitType)
- if err != nil {
- ctx.ServerError("userRepoIDs", err)
- return
+
+ isPullList := unitType == unit.TypePullRequests
+ opts := &models.IssuesOptions{
+ IsPull: util.OptionalBoolOf(isPullList),
+ SortType: sortType,
+ IsArchived: util.OptionalBoolFalse,
+ Org: org,
+ Team: team,
+ User: ctx.User,
}
switch filterMode {
case models.FilterModeAll:
- opts.RepoIDs = userRepoIDs
case models.FilterModeAssign:
opts.AssigneeID = ctx.User.ID
case models.FilterModeCreate:
@@ -434,10 +433,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
opts.ReviewRequestedID = ctx.User.ID
}
- if ctxUser.IsOrganization() {
- opts.RepoIDs = userRepoIDs
- }
-
// keyword holds the search term entered into the search field.
keyword := strings.Trim(ctx.FormString("q"), " ")
ctx.Data["Keyword"] = keyword
@@ -524,27 +519,25 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// ----------------------------------
// showReposMap maps repository IDs to their Repository pointers.
- showReposMap, err := repoIDMap(ctxUser, issueCountByRepo, unitType)
+ showReposMap, err := loadRepoByIDs(ctxUser, issueCountByRepo, unitType)
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
ctx.NotFound("GetRepositoryByID", err)
return
}
- ctx.ServerError("repoIDMap", err)
+ ctx.ServerError("loadRepoByIDs", err)
return
}
// a RepositoryList
showRepos := models.RepositoryListOfMap(showReposMap)
sort.Sort(showRepos)
- if err = showRepos.LoadAttributes(); err != nil {
- ctx.ServerError("LoadAttributes", err)
- return
- }
// maps pull request IDs to their CommitStatus. Will be posted to ctx.Data.
for _, issue := range issues {
- issue.Repo = showReposMap[issue.RepoID]
+ if issue.Repo == nil {
+ issue.Repo = showReposMap[issue.RepoID]
+ }
}
commitStatus, err := pull_service.GetIssuesLastCommitStatus(issues)
@@ -556,86 +549,39 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// -------------------------------
// Fill stats to post to ctx.Data.
// -------------------------------
-
- userIssueStatsOpts := models.UserIssueStatsOptions{
- UserID: ctx.User.ID,
- UserRepoIDs: userRepoIDs,
- FilterMode: filterMode,
- IsPull: isPullList,
- IsClosed: isShowClosed,
- IsArchived: util.OptionalBoolFalse,
- LabelIDs: opts.LabelIDs,
- }
- if len(repoIDs) > 0 {
- userIssueStatsOpts.UserRepoIDs = repoIDs
- }
- if ctxUser.IsOrganization() {
- userIssueStatsOpts.RepoIDs = userRepoIDs
- }
- userIssueStats, err := models.GetUserIssueStats(userIssueStatsOpts)
- if err != nil {
- ctx.ServerError("GetUserIssueStats User", err)
- return
- }
-
- var shownIssueStats *models.IssueStats
+ var issueStats *models.IssueStats
if !forceEmpty {
statsOpts := models.UserIssueStatsOptions{
- UserID: ctx.User.ID,
- UserRepoIDs: userRepoIDs,
- FilterMode: filterMode,
- IsPull: isPullList,
- IsClosed: isShowClosed,
- IssueIDs: issueIDsFromSearch,
- IsArchived: util.OptionalBoolFalse,
- LabelIDs: opts.LabelIDs,
+ UserID: ctx.User.ID,
+ FilterMode: filterMode,
+ IsPull: isPullList,
+ IsClosed: isShowClosed,
+ IssueIDs: issueIDsFromSearch,
+ IsArchived: util.OptionalBoolFalse,
+ LabelIDs: opts.LabelIDs,
+ Org: org,
+ Team: team,
}
if len(repoIDs) > 0 {
statsOpts.RepoIDs = repoIDs
- } else if ctxUser.IsOrganization() {
- statsOpts.RepoIDs = userRepoIDs
}
- shownIssueStats, err = models.GetUserIssueStats(statsOpts)
+ issueStats, err = models.GetUserIssueStats(statsOpts)
if err != nil {
ctx.ServerError("GetUserIssueStats Shown", err)
return
}
} else {
- shownIssueStats = &models.IssueStats{}
- }
-
- var allIssueStats *models.IssueStats
- if !forceEmpty {
- allIssueStatsOpts := models.UserIssueStatsOptions{
- UserID: ctx.User.ID,
- UserRepoIDs: userRepoIDs,
- FilterMode: filterMode,
- IsPull: isPullList,
- IsClosed: isShowClosed,
- IssueIDs: issueIDsFromSearch,
- IsArchived: util.OptionalBoolFalse,
- LabelIDs: opts.LabelIDs,
- }
- if ctxUser.IsOrganization() {
- allIssueStatsOpts.RepoIDs = userRepoIDs
- }
- allIssueStats, err = models.GetUserIssueStats(allIssueStatsOpts)
- if err != nil {
- ctx.ServerError("GetUserIssueStats All", err)
- return
- }
- } else {
- allIssueStats = &models.IssueStats{}
+ issueStats = &models.IssueStats{}
}
// Will be posted to ctx.Data.
var shownIssues int
if !isShowClosed {
- shownIssues = int(shownIssueStats.OpenCount)
- ctx.Data["TotalIssueCount"] = int(allIssueStats.OpenCount)
+ shownIssues = int(issueStats.OpenCount)
+ ctx.Data["TotalIssueCount"] = shownIssues
} else {
- shownIssues = int(shownIssueStats.ClosedCount)
- ctx.Data["TotalIssueCount"] = int(allIssueStats.ClosedCount)
+ shownIssues = int(issueStats.ClosedCount)
+ ctx.Data["TotalIssueCount"] = shownIssues
}
ctx.Data["IsShowClosed"] = isShowClosed
@@ -671,8 +617,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
ctx.Data["CommitStatus"] = commitStatus
ctx.Data["Repos"] = showRepos
ctx.Data["Counts"] = issueCountByRepo
- ctx.Data["IssueStats"] = userIssueStats
- ctx.Data["ShownIssueStats"] = shownIssueStats
+ ctx.Data["IssueStats"] = issueStats
ctx.Data["ViewType"] = viewType
ctx.Data["SortType"] = sortType
ctx.Data["RepoIDs"] = repoIDs
@@ -730,56 +675,6 @@ func getRepoIDs(reposQuery string) []int64 {
return repoIDs
}
-func getActiveUserRepoIDs(ctxUser *user_model.User, team *models.Team, unitType unit.Type) ([]int64, error) {
- var userRepoIDs []int64
- var err error
-
- if ctxUser.IsOrganization() {
- userRepoIDs, err = getActiveTeamOrOrgRepoIds(ctxUser, team, unitType)
- if err != nil {
- return nil, fmt.Errorf("orgRepoIds: %v", err)
- }
- } else {
- userRepoIDs, err = models.GetActiveAccessRepoIDs(ctxUser, unitType)
- if err != nil {
- return nil, fmt.Errorf("ctxUser.GetAccessRepoIDs: %v", err)
- }
- }
-
- if len(userRepoIDs) == 0 {
- userRepoIDs = []int64{-1}
- }
-
- return userRepoIDs, nil
-}
-
-// getActiveTeamOrOrgRepoIds gets RepoIDs for ctxUser as Organization.
-// Should be called if and only if ctxUser.IsOrganization == true.
-func getActiveTeamOrOrgRepoIds(ctxUser *user_model.User, team *models.Team, unitType unit.Type) ([]int64, error) {
- var orgRepoIDs []int64
- var err error
- var env models.AccessibleReposEnvironment
-
- if team != nil {
- env = models.OrgFromUser(ctxUser).AccessibleTeamReposEnv(team)
- } else {
- env, err = models.OrgFromUser(ctxUser).AccessibleReposEnv(ctxUser.ID)
- if err != nil {
- return nil, fmt.Errorf("AccessibleReposEnv: %v", err)
- }
- }
- orgRepoIDs, err = env.RepoIDs(1, ctxUser.NumRepos)
- if err != nil {
- return nil, fmt.Errorf("env.RepoIDs: %v", err)
- }
- orgRepoIDs, err = models.FilterOutRepoIdsWithoutUnitAccess(ctxUser, orgRepoIDs, unitType)
- if err != nil {
- return nil, fmt.Errorf("FilterOutRepoIdsWithoutUnitAccess: %v", err)
- }
-
- return orgRepoIDs, nil
-}
-
func issueIDsFromSearch(ctxUser *user_model.User, keyword string, opts *models.IssuesOptions) ([]int64, error) {
if len(keyword) == 0 {
return []int64{}, nil
@@ -797,33 +692,27 @@ func issueIDsFromSearch(ctxUser *user_model.User, keyword string, opts *models.I
return issueIDsFromSearch, nil
}
-func repoIDMap(ctxUser *user_model.User, issueCountByRepo map[int64]int64, unitType unit.Type) (map[int64]*repo_model.Repository, error) {
- repoByID := make(map[int64]*repo_model.Repository, len(issueCountByRepo))
+func loadRepoByIDs(ctxUser *user_model.User, issueCountByRepo map[int64]int64, unitType unit.Type) (map[int64]*repo_model.Repository, error) {
+ var totalRes = make(map[int64]*repo_model.Repository, len(issueCountByRepo))
+ var repoIDs = make([]int64, 0, 500)
for id := range issueCountByRepo {
if id <= 0 {
continue
}
- if _, ok := repoByID[id]; !ok {
- repo, err := repo_model.GetRepositoryByID(id)
- if repo_model.IsErrRepoNotExist(err) {
+ repoIDs = append(repoIDs, id)
+ if len(repoIDs) == 500 {
+ if err := repo_model.FindReposMapByIDs(repoIDs, totalRes); err != nil {
return nil, err
- } else if err != nil {
- return nil, fmt.Errorf("GetRepositoryByID: [%d]%v", id, err)
}
- repoByID[id] = repo
+ repoIDs = repoIDs[:0]
}
- repo := repoByID[id]
-
- // Check if user has access to given repository.
- perm, err := models.GetUserRepoPermission(repo, ctxUser)
- if err != nil {
- return nil, fmt.Errorf("GetUserRepoPermission: [%d]%v", id, err)
- }
- if !perm.CanRead(unitType) {
- log.Debug("User created Issues in Repository which they no longer have access to: [%d]", id)
+ }
+ if len(repoIDs) > 0 {
+ if err := repo_model.FindReposMapByIDs(repoIDs, totalRes); err != nil {
+ return nil, err
}
}
- return repoByID, nil
+ return totalRes, nil
}
// ShowSSHKeys output all the ssh keys of user by uid
diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl
index aca61f9ae9..dc66859b83 100644
--- a/templates/user/dashboard/issues.tmpl
+++ b/templates/user/dashboard/issues.tmpl
@@ -61,11 +61,11 @@
<div class="ui compact tiny menu">
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}">
{{svg "octicon-issue-opened" 16 "mr-3"}}
- {{.i18n.Tr "repo.issues.open_tab" .ShownIssueStats.OpenCount}}
+ {{.i18n.Tr "repo.issues.open_tab" .IssueStats.OpenCount}}
</a>
<a class="item{{if .IsShowClosed}} active{{end}}" href="{{.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=closed&q={{$.Keyword}}">
{{svg "octicon-issue-closed" 16 "mr-3"}}
- {{.i18n.Tr "repo.issues.close_tab" .ShownIssueStats.ClosedCount}}
+ {{.i18n.Tr "repo.issues.close_tab" .IssueStats.ClosedCount}}
</a>
</div>
</div>