1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-22 18:28:37 +00:00

Support org/user level projects (#22235)

Fix #13405

<img width="1151" alt="image"
src="https://user-images.githubusercontent.com/81045/209442911-7baa3924-c389-47b6-b63b-a740803e640e.png">

Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
Lunny Xiao
2023-01-20 19:42:33 +08:00
committed by GitHub
parent 0c048e554b
commit 6fe3c8b398
30 changed files with 1556 additions and 176 deletions

View File

@@ -8,6 +8,9 @@ import (
"fmt"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
@@ -78,12 +81,15 @@ func (err ErrProjectBoardNotExist) Unwrap() error {
// Project represents a project board
type Project struct {
ID int64 `xorm:"pk autoincr"`
Title string `xorm:"INDEX NOT NULL"`
Description string `xorm:"TEXT"`
RepoID int64 `xorm:"INDEX"`
CreatorID int64 `xorm:"NOT NULL"`
IsClosed bool `xorm:"INDEX"`
ID int64 `xorm:"pk autoincr"`
Title string `xorm:"INDEX NOT NULL"`
Description string `xorm:"TEXT"`
OwnerID int64 `xorm:"INDEX"`
Owner *user_model.User `xorm:"-"`
RepoID int64 `xorm:"INDEX"`
Repo *repo_model.Repository `xorm:"-"`
CreatorID int64 `xorm:"NOT NULL"`
IsClosed bool `xorm:"INDEX"`
BoardType BoardType
Type Type
@@ -94,6 +100,46 @@ type Project struct {
ClosedDateUnix timeutil.TimeStamp
}
func (p *Project) LoadOwner(ctx context.Context) (err error) {
if p.Owner != nil {
return nil
}
p.Owner, err = user_model.GetUserByID(ctx, p.OwnerID)
return err
}
func (p *Project) LoadRepo(ctx context.Context) (err error) {
if p.RepoID == 0 || p.Repo != nil {
return nil
}
p.Repo, err = repo_model.GetRepositoryByID(ctx, p.RepoID)
return err
}
func (p *Project) Link() string {
if p.OwnerID > 0 {
err := p.LoadOwner(db.DefaultContext)
if err != nil {
log.Error("LoadOwner: %v", err)
return ""
}
return fmt.Sprintf("/%s/-/projects/%d", p.Owner.Name, p.ID)
}
if p.RepoID > 0 {
err := p.LoadRepo(db.DefaultContext)
if err != nil {
log.Error("LoadRepo: %v", err)
return ""
}
return fmt.Sprintf("/%s/projects/%d", p.Repo.RepoPath(), p.ID)
}
return ""
}
func (p *Project) IsOrganizationProject() bool {
return p.Type == TypeOrganization
}
func init() {
db.RegisterModel(new(Project))
}
@@ -110,7 +156,7 @@ func GetProjectsConfig() []ProjectsConfig {
// IsTypeValid checks if a project type is valid
func IsTypeValid(p Type) bool {
switch p {
case TypeRepository:
case TypeRepository, TypeOrganization:
return true
default:
return false
@@ -119,6 +165,7 @@ func IsTypeValid(p Type) bool {
// SearchOptions are options for GetProjects
type SearchOptions struct {
OwnerID int64
RepoID int64
Page int
IsClosed util.OptionalBool
@@ -126,12 +173,11 @@ type SearchOptions struct {
Type Type
}
// GetProjects returns a list of all projects that have been created in the repository
func GetProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) {
e := db.GetEngine(ctx)
projects := make([]*Project, 0, setting.UI.IssuePagingNum)
var cond builder.Cond = builder.Eq{"repo_id": opts.RepoID}
func (opts *SearchOptions) toConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
}
switch opts.IsClosed {
case util.OptionalBoolTrue:
cond = cond.And(builder.Eq{"is_closed": true})
@@ -142,6 +188,22 @@ func GetProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, er
if opts.Type > 0 {
cond = cond.And(builder.Eq{"type": opts.Type})
}
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}
return cond
}
// CountProjects counts projects
func CountProjects(ctx context.Context, opts SearchOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Project))
}
// FindProjects returns a list of all projects that have been created in the repository
func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) {
e := db.GetEngine(ctx)
projects := make([]*Project, 0, setting.UI.IssuePagingNum)
cond := opts.toConds()
count, err := e.Where(cond).Count(new(Project))
if err != nil {
@@ -188,8 +250,10 @@ func NewProject(p *Project) error {
return err
}
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_projects = num_projects + 1 WHERE id = ?", p.RepoID); err != nil {
return err
if p.RepoID > 0 {
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_projects = num_projects + 1 WHERE id = ?", p.RepoID); err != nil {
return err
}
}
if err := createBoardsForProjectsType(ctx, p); err != nil {

View File

@@ -22,7 +22,7 @@ func TestIsProjectTypeValid(t *testing.T) {
}{
{TypeIndividual, false},
{TypeRepository, true},
{TypeOrganization, false},
{TypeOrganization, true},
{UnknownType, false},
}
@@ -34,13 +34,13 @@ func TestIsProjectTypeValid(t *testing.T) {
func TestGetProjects(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
projects, _, err := GetProjects(db.DefaultContext, SearchOptions{RepoID: 1})
projects, _, err := FindProjects(db.DefaultContext, SearchOptions{RepoID: 1})
assert.NoError(t, err)
// 1 value for this repo exists in the fixtures
assert.Len(t, projects, 1)
projects, _, err = GetProjects(db.DefaultContext, SearchOptions{RepoID: 3})
projects, _, err = FindProjects(db.DefaultContext, SearchOptions{RepoID: 3})
assert.NoError(t, err)
// 1 value for this repo exists in the fixtures