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

Support org/user level projects #22235

Merged
merged 28 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
bb64be1
support org level projects
lunny Dec 24, 2022
c5266e3
Fix move
lunny Dec 24, 2022
420c5cd
Change the repo ref
lunny Dec 24, 2022
0436072
improve FindProjects
lunny Dec 24, 2022
4a5eb97
Fix lint
lunny Dec 27, 2022
24ce5fc
Merge branch 'main' into lunny/org_project
lunny Dec 27, 2022
8de5ae3
Merge branch 'main' into lunny/org_project
lunny Dec 27, 2022
68e6c14
Fix lint
lunny Dec 27, 2022
c39fca7
improve menus
lunny Dec 28, 2022
2c40716
Fix test
lunny Dec 28, 2022
44a56ed
Merge branch 'main' into lunny/org_project
lunny Dec 28, 2022
2c081a5
support user level projects
lunny Dec 28, 2022
b2c2deb
Fix test
lunny Dec 29, 2022
2a1365f
Merge branch 'main' into lunny/org_project
lunny Dec 29, 2022
547c309
improvement
lunny Dec 29, 2022
d0a3680
Fix test
lunny Dec 31, 2022
f94cb43
Merge branch 'main' into lunny/org_project
lunny Dec 31, 2022
ada5889
Use project-symblink icon for org level project
lunny Dec 31, 2022
a4ce734
Fix test
lunny Dec 31, 2022
afbd579
Merge branch 'main' into lunny/org_project
lunny Jan 1, 2023
ed07255
fix test
lunny Jan 1, 2023
959ba4b
Merge branch 'main' into lunny/org_project
lunny Jan 1, 2023
7a97a60
Merge branch 'main' into lunny/org_project
lunny Jan 2, 2023
a3322d2
Fix bug
lunny Jan 2, 2023
1889007
Merge branch 'main' into lunny/org_project
6543 Jan 2, 2023
cccf1e9
Merge branch 'main' into lunny/org_project
lunny Jan 16, 2023
a094dc8
Merge branch 'main' into lunny/org_project
lunny Jan 19, 2023
c4f1e41
Merge branch 'main' into lunny/org_project
lunny Jan 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions models/fixtures/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,12 @@
creator_id: 5
board_type: 1
type: 2

-
id: 4
title: project on user2
owner_id: 2
is_closed: false
creator_id: 2
board_type: 1
type: 2
8 changes: 8 additions & 0 deletions models/fixtures/project_board.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,11 @@
creator_id: 2
created_unix: 1588117528
updated_unix: 1588117528

-
id: 4
project_id: 4
title: Done
creator_id: 2
created_unix: 1588117528
updated_unix: 1588117528
2 changes: 1 addition & 1 deletion models/issues/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,7 @@ func GetIssueWithAttrsByID(id int64) (*Issue, error) {
}

// GetIssuesByIDs return issues with the given IDs.
func GetIssuesByIDs(ctx context.Context, issueIDs []int64) ([]*Issue, error) {
func GetIssuesByIDs(ctx context.Context, issueIDs []int64) (IssueList, error) {
issues := make([]*Issue, 0, 10)
return issues, db.GetEngine(ctx).In("id", issueIDs).Find(&issues)
}
Expand Down
10 changes: 5 additions & 5 deletions models/issues/issue_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,17 @@ func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64
func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error {
oldProjectID := issue.projectID(ctx)

if err := issue.LoadRepo(ctx); err != nil {
return err
}

// Only check if we add a new project and not remove it.
if newProjectID > 0 {
newProject, err := project_model.GetProjectByID(ctx, newProjectID)
if err != nil {
return err
}
if newProject.RepoID != issue.RepoID {
if newProject.RepoID != issue.RepoID && newProject.OwnerID != issue.Repo.OwnerID {
return fmt.Errorf("issue's repository is not the same as project's repository")
}
}
Expand All @@ -140,10 +144,6 @@ func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.U
return err
}

if err := issue.LoadRepo(ctx); err != nil {
lunny marked this conversation as resolved.
Show resolved Hide resolved
return err
}

if oldProjectID > 0 || newProjectID > 0 {
if _, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeProject,
Expand Down
65 changes: 0 additions & 65 deletions models/organization/team.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"

"xorm.io/builder"
)

// ___________
Expand Down Expand Up @@ -96,59 +94,6 @@ func init() {
db.RegisterModel(new(TeamInvite))
}

// SearchTeamOptions holds the search options
type SearchTeamOptions struct {
db.ListOptions
UserID int64
Keyword string
OrgID int64
IncludeDesc bool
}

func (opts *SearchTeamOptions) toCond() builder.Cond {
cond := builder.NewCond()

if len(opts.Keyword) > 0 {
lowerKeyword := strings.ToLower(opts.Keyword)
var keywordCond builder.Cond = builder.Like{"lower_name", lowerKeyword}
if opts.IncludeDesc {
keywordCond = keywordCond.Or(builder.Like{"LOWER(description)", lowerKeyword})
}
cond = cond.And(keywordCond)
}

if opts.OrgID > 0 {
cond = cond.And(builder.Eq{"`team`.org_id": opts.OrgID})
}

if opts.UserID > 0 {
cond = cond.And(builder.Eq{"team_user.uid": opts.UserID})
}

return cond
}

// SearchTeam search for teams. Caller is responsible to check permissions.
func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) {
sess := db.GetEngine(db.DefaultContext)

opts.SetDefaultValues()
cond := opts.toCond()

if opts.UserID > 0 {
sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id")
}
sess = db.SetSessionPagination(sess, opts)

teams := make([]*Team, 0, opts.PageSize)
count, err := sess.Where(cond).OrderBy("lower_name").FindAndCount(&teams)
if err != nil {
return nil, 0, err
}

return teams, count, nil
}

// ColorFormat provides a basic color format for a Team
func (t *Team) ColorFormat(s fmt.State) {
if t == nil {
Expand Down Expand Up @@ -335,16 +280,6 @@ func GetTeamNamesByID(teamIDs []int64) ([]string, error) {
return teamNames, err
}

// GetRepoTeams gets the list of teams that has access to the repository
func GetRepoTeams(ctx context.Context, repo *repo_model.Repository) (teams []*Team, err error) {
return teams, db.GetEngine(ctx).
Join("INNER", "team_repo", "team_repo.team_id = team.id").
Where("team.org_id = ?", repo.OwnerID).
And("team_repo.repo_id=?", repo.ID).
OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
Find(&teams)
}

// IncrTeamRepoNum increases the number of repos for the given team by 1
func IncrTeamRepoNum(ctx context.Context, teamID int64) error {
_, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team))
Expand Down
128 changes: 128 additions & 0 deletions models/organization/team_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package organization

import (
"context"
"strings"

"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"

"xorm.io/builder"
)

type TeamList []*Team

func (t TeamList) LoadUnits(ctx context.Context) error {
for _, team := range t {
if err := team.getUnits(ctx); err != nil {
return err
}
}
return nil
}

func (t TeamList) UnitMaxAccess(tp unit.Type) perm.AccessMode {
maxAccess := perm.AccessModeNone
for _, team := range t {
if team.IsOwnerTeam() {
return perm.AccessModeOwner
}
for _, teamUnit := range team.Units {
if teamUnit.Type != tp {
continue
}
if teamUnit.AccessMode > maxAccess {
Comment on lines +36 to +39
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if teamUnit.Type != tp {
continue
}
if teamUnit.AccessMode > maxAccess {
if teamUnit.Type == tp && teamUnit.AccessMode > maxAccess {

?

maxAccess = teamUnit.AccessMode
}
}
}
return maxAccess
}

// SearchTeamOptions holds the search options
type SearchTeamOptions struct {
db.ListOptions
UserID int64
Keyword string
OrgID int64
IncludeDesc bool
}

func (opts *SearchTeamOptions) toCond() builder.Cond {
cond := builder.NewCond()

if len(opts.Keyword) > 0 {
lowerKeyword := strings.ToLower(opts.Keyword)
var keywordCond builder.Cond = builder.Like{"lower_name", lowerKeyword}
if opts.IncludeDesc {
keywordCond = keywordCond.Or(builder.Like{"LOWER(description)", lowerKeyword})
}
cond = cond.And(keywordCond)
}

if opts.OrgID > 0 {
cond = cond.And(builder.Eq{"`team`.org_id": opts.OrgID})
}

if opts.UserID > 0 {
cond = cond.And(builder.Eq{"team_user.uid": opts.UserID})
}

return cond
}

// SearchTeam search for teams. Caller is responsible to check permissions.
func SearchTeam(opts *SearchTeamOptions) (TeamList, int64, error) {
sess := db.GetEngine(db.DefaultContext)

opts.SetDefaultValues()
cond := opts.toCond()

if opts.UserID > 0 {
sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id")
}
sess = db.SetSessionPagination(sess, opts)

teams := make([]*Team, 0, opts.PageSize)
count, err := sess.Where(cond).OrderBy("lower_name").FindAndCount(&teams)
if err != nil {
return nil, 0, err
}

return teams, count, nil
}

// GetRepoTeams gets the list of teams that has access to the repository
func GetRepoTeams(ctx context.Context, repo *repo_model.Repository) (teams TeamList, err error) {
return teams, db.GetEngine(ctx).
Join("INNER", "team_repo", "team_repo.team_id = team.id").
Where("team.org_id = ?", repo.OwnerID).
And("team_repo.repo_id=?", repo.ID).
OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
Find(&teams)
}

// GetUserOrgTeams returns all teams that user belongs to in given organization.
func GetUserOrgTeams(ctx context.Context, orgID, userID int64) (teams TeamList, err error) {
return teams, db.GetEngine(ctx).
Join("INNER", "team_user", "team_user.team_id = team.id").
Where("team.org_id = ?", orgID).
And("team_user.uid=?", userID).
Find(&teams)
}

// GetUserRepoTeams returns user repo's teams
func GetUserRepoTeams(ctx context.Context, orgID, userID, repoID int64) (teams TeamList, err error) {
return teams, db.GetEngine(ctx).
Join("INNER", "team_user", "team_user.team_id = team.id").
Join("INNER", "team_repo", "team_repo.team_id = team.id").
Where("team.org_id = ?", orgID).
And("team_user.uid=?", userID).
And("team_repo.repo_id=?", repoID).
Find(&teams)
}
20 changes: 0 additions & 20 deletions models/organization/team_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,26 +72,6 @@ func GetTeamMembers(ctx context.Context, opts *SearchMembersOptions) ([]*user_mo
return members, nil
}

// GetUserOrgTeams returns all teams that user belongs to in given organization.
func GetUserOrgTeams(ctx context.Context, orgID, userID int64) (teams []*Team, err error) {
return teams, db.GetEngine(ctx).
Join("INNER", "team_user", "team_user.team_id = team.id").
Where("team.org_id = ?", orgID).
And("team_user.uid=?", userID).
Find(&teams)
}

// GetUserRepoTeams returns user repo's teams
func GetUserRepoTeams(ctx context.Context, orgID, userID, repoID int64) (teams []*Team, err error) {
return teams, db.GetEngine(ctx).
Join("INNER", "team_user", "team_user.team_id = team.id").
Join("INNER", "team_repo", "team_repo.team_id = team.id").
Where("team.org_id = ?", orgID).
And("team_user.uid=?", userID).
And("team_repo.repo_id=?", repoID).
Find(&teams)
}

// IsUserInTeams returns if a user in some teams
func IsUserInTeams(ctx context.Context, userID int64, teamIDs []int64) (bool, error) {
return db.GetEngine(ctx).Where("uid=?", userID).In("team_id", teamIDs).Exist(new(TeamUser))
Expand Down
Loading