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

Enable/disable owner and repo projects independently #28805

Merged
merged 28 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7409d6e
add possibility to enable/disable owner and repo projects independently
denyskon Jan 15, 2024
59bc4a9
Merge branch 'main' into projects-switch
denyskon Jan 15, 2024
11b3af5
Merge branch 'main' into projects-switch
denyskon Jan 15, 2024
8a7f228
fix for disabled projects
denyskon Jan 15, 2024
145c7ff
Merge branch 'projects-switch' of ssh://github.com/denyskon/gitea int…
denyskon Jan 15, 2024
b927649
Merge branch 'main' into projects-switch
denyskon Jan 16, 2024
503b014
Update routers/web/repo/compare.go
denyskon Jan 22, 2024
3e845b4
Update routers/web/repo/issue.go
denyskon Jan 22, 2024
a990430
Merge branch 'main' into projects-switch
denyskon Jan 22, 2024
bc9daba
Merge branch 'main' into projects-switch
denyskon Jan 23, 2024
25e5f27
Merge branch 'main' into projects-switch
denyskon Feb 6, 2024
3b39cf3
Merge branch 'main' into projects-switch
denyskon Feb 18, 2024
a71fc56
Merge branch 'main' into projects-switch
6543 Feb 26, 2024
be63204
Merge remote-tracking branch 'upstream/main' into projects-switch
denyskon Feb 26, 2024
1cc7f93
switch to select ui
denyskon Feb 26, 2024
7409618
Update options/locale/locale_en-US.ini
denyskon Feb 26, 2024
e4a00c7
Merge branch 'main' into projects-switch
6543 Feb 28, 2024
f70ca79
Update templates/repo/settings/options.tmpl
denyskon Mar 2, 2024
14ad3e8
Update options/locale/locale_en-US.ini
denyskon Mar 2, 2024
dba0fa9
Merge remote-tracking branch 'upstream/main' into projects-switch
denyskon Mar 2, 2024
636de49
add simple method for projects mode check, decouple projects mode che…
denyskon Mar 3, 2024
cff75c0
additional simplifications
denyskon Mar 3, 2024
131ea05
Merge branch 'main' into projects-switch
denyskon Mar 3, 2024
2789c04
statisfy linter
denyskon Mar 3, 2024
ce5af9c
Merge branch 'projects-switch' of ssh://github.com/denyskon/gitea int…
denyskon Mar 3, 2024
1a88d93
fix fixture
denyskon Mar 3, 2024
87a867e
Merge branch 'main' into projects-switch
denyskon Mar 3, 2024
9c8f267
Merge branch 'main' into projects-switch
6543 Mar 4, 2024
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
5 changes: 5 additions & 0 deletions models/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,11 @@ func (repo *Repository) MustGetUnit(ctx context.Context, tp unit.Type) *RepoUnit
Type: tp,
Config: new(ActionsConfig),
}
} else if tp == unit.TypeProjects {
return &RepoUnit{
Type: tp,
Config: new(ProjectsConfig),
}
}

return &RepoUnit{
Expand Down
36 changes: 35 additions & 1 deletion models/repo/repo_unit.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,33 @@ func (cfg *ActionsConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}

// ProjectsMode represents the projects enabled for a repository
type ProjectsMode string

const (
// ProjectsModeRepo allows only repo-level projects
ProjectsModeRepo ProjectsMode = "repo"
// ProjectsModeOwner allows only owner-level projects
ProjectsModeOwner ProjectsMode = "owner"
// ProjectsModeAll allows both kinds of projects
ProjectsModeAll ProjectsMode = "all"
denyskon marked this conversation as resolved.
Show resolved Hide resolved
)

// ProjectsConfig describes projects config
type ProjectsConfig struct {
ProjectsMode ProjectsMode
}

// FromDB fills up a ProjectsConfig from serialized format.
func (cfg *ProjectsConfig) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
}

// ToDB exports a ProjectsConfig to a serialized format.
func (cfg *ProjectsConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}

// BeforeSet is invoked from XORM before setting the value of a field of this object.
func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
switch colName {
Expand All @@ -217,7 +244,9 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
r.Config = new(IssuesConfig)
case unit.TypeActions:
r.Config = new(ActionsConfig)
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects, unit.TypePackages:
case unit.TypeProjects:
r.Config = new(ProjectsConfig)
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypePackages:
fallthrough
default:
r.Config = new(UnitConfig)
Expand Down Expand Up @@ -265,6 +294,11 @@ func (r *RepoUnit) ActionsConfig() *ActionsConfig {
return r.Config.(*ActionsConfig)
}

// ProjectsConfig returns config for unit.ProjectsConfig
func (r *RepoUnit) ProjectsConfig() *ProjectsConfig {
return r.Config.(*ProjectsConfig)
}

func getUnitsByRepoID(ctx context.Context, repoID int64) (units []*RepoUnit, err error) {
var tmpUnits []*RepoUnit
if err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Find(&tmpUnits); err != nil {
Expand Down
6 changes: 6 additions & 0 deletions modules/repository/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
AllowRebaseUpdate: true,
},
})
} else if tp == unit.TypeProjects {
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &repo_model.ProjectsConfig{ProjectsMode: repo_model.ProjectsModeAll},
})
} else {
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Expand Down
3 changes: 3 additions & 0 deletions modules/structs/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type Repository struct {
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
HasPullRequests bool `json:"has_pull_requests"`
HasProjects bool `json:"has_projects"`
ProjectsMode string `json:"projects_mode"`
HasReleases bool `json:"has_releases"`
HasPackages bool `json:"has_packages"`
HasActions bool `json:"has_actions"`
Expand Down Expand Up @@ -180,6 +181,8 @@ type EditRepoOption struct {
HasPullRequests *bool `json:"has_pull_requests,omitempty"`
// either `true` to enable project unit, or `false` to disable them.
HasProjects *bool `json:"has_projects,omitempty"`
// `repo` to only allow repo-level projects, `owner` to only allow owner projects, `all` to allow both.
ProjectsMode *string `json:"projects_mode,omitempty" binding:"In(repo,owner,all)"`
// either `true` to enable releases unit, or `false` to disable them.
HasReleases *bool `json:"has_releases,omitempty"`
// either `true` to enable packages unit, or `false` to disable them.
Expand Down
6 changes: 5 additions & 1 deletion options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2077,7 +2077,11 @@ settings.pulls.default_delete_branch_after_merge = Delete pull request branch af
settings.pulls.default_allow_edits_from_maintainers = Allow edits from maintainers by default
settings.releases_desc = Enable Repository Releases
settings.packages_desc = Enable Repository Packages Registry
settings.projects_desc = Enable Repository Projects
settings.projects_desc = Enable Projects
settings.projects_mode_desc = Projects Mode (which kinds of projects to show)
settings.projects_mode_repo = Repo projects only
settings.projects_mode_owner = Owner (user or org) projects only
denyskon marked this conversation as resolved.
Show resolved Hide resolved
settings.projects_mode_all = All projects
settings.actions_desc = Enable Repository Actions
settings.admin_settings = Administrator Settings
settings.admin_enable_health_check = Enable Repository Health Checks (git fsck)
Expand Down
26 changes: 23 additions & 3 deletions routers/api/v1/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -944,13 +944,33 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
}
}

if opts.HasProjects != nil && !unit_model.TypeProjects.UnitGlobalDisabled() {
if *opts.HasProjects {
currHasProjects := repo.UnitEnabled(ctx, unit_model.TypeProjects)
newHasProjects := currHasProjects
if opts.HasProjects != nil {
newHasProjects = *opts.HasProjects
}
if currHasProjects || newHasProjects {
if newHasProjects && !unit_model.TypeProjects.UnitGlobalDisabled() {
unit, err := repo.GetUnit(ctx, unit_model.TypeProjects)
var config *repo_model.ProjectsConfig
if err != nil {
config = &repo_model.ProjectsConfig{
ProjectsMode: repo_model.ProjectsModeAll,
}
} else {
config = unit.ProjectsConfig()
}

if opts.ProjectsMode != nil {
config.ProjectsMode = repo_model.ProjectsMode(*opts.ProjectsMode)
}

units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: unit_model.TypeProjects,
Config: config,
})
} else {
} else if !newHasProjects && !unit_model.TypeProjects.UnitGlobalDisabled() {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
}
}
Expand Down
125 changes: 76 additions & 49 deletions routers/web/repo/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,56 +580,82 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R
func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
// Distinguish whether the owner of the repository
// is an individual or an organization
repoOwnerType := project_model.TypeIndividual
if repo.Owner.IsOrganization() {
repoOwnerType = project_model.TypeOrganization
}
var err error
projects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
RepoID: repo.ID,
IsClosed: util.OptionalBoolFalse,
Type: project_model.TypeRepository,
})
if err != nil {
ctx.ServerError("GetProjects", err)
return
}
projects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
OwnerID: repo.OwnerID,
IsClosed: util.OptionalBoolFalse,
Type: repoOwnerType,
})
if err != nil {
ctx.ServerError("GetProjects", err)
return
}
if ctx.Repo.CanRead(unit.TypeProjects) {
projectsUnit, err := repo.GetUnit(ctx, unit.TypeProjects)
if err != nil {
ctx.ServerError("GetUnit", err)
return
}
projectsConfig := projectsUnit.ProjectsConfig()

ctx.Data["OpenProjects"] = append(projects, projects2...)
repoOwnerType := project_model.TypeIndividual
if repo.Owner.IsOrganization() {
repoOwnerType = project_model.TypeOrganization
}

projects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
RepoID: repo.ID,
IsClosed: util.OptionalBoolTrue,
Type: project_model.TypeRepository,
})
if err != nil {
ctx.ServerError("GetProjects", err)
return
}
projects2, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
OwnerID: repo.OwnerID,
IsClosed: util.OptionalBoolTrue,
Type: repoOwnerType,
})
if err != nil {
ctx.ServerError("GetProjects", err)
return
}
var openProjects []*project_model.Project
if projectsConfig.ProjectsMode != repo_model.ProjectsModeOwner {
repoProjects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
RepoID: repo.ID,
IsClosed: util.OptionalBoolFalse,
Type: project_model.TypeRepository,
})
if err != nil {
ctx.ServerError("GetProjects", err)
return
}
openProjects = append(openProjects, repoProjects...)
}
if projectsConfig.ProjectsMode != repo_model.ProjectsModeRepo {
ownerProjects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
OwnerID: repo.OwnerID,
IsClosed: util.OptionalBoolFalse,
Type: repoOwnerType,
})
if err != nil {
ctx.ServerError("GetProjects", err)
return
}

ctx.Data["ClosedProjects"] = append(projects, projects2...)
openProjects = append(openProjects, ownerProjects...)
}

ctx.Data["OpenProjects"] = openProjects

var closedProjects []*project_model.Project
if projectsConfig.ProjectsMode != repo_model.ProjectsModeOwner {
repoProjects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
RepoID: repo.ID,
IsClosed: util.OptionalBoolTrue,
Type: project_model.TypeRepository,
})
if err != nil {
ctx.ServerError("GetProjects", err)
return
}

closedProjects = append(closedProjects, repoProjects...)
}
if projectsConfig.ProjectsMode != repo_model.ProjectsModeRepo {
denyskon marked this conversation as resolved.
Show resolved Hide resolved
ownerProjects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
OwnerID: repo.OwnerID,
IsClosed: util.OptionalBoolTrue,
Type: repoOwnerType,
})
if err != nil {
ctx.ServerError("GetProjects", err)
return
}

closedProjects = append(closedProjects, ownerProjects...)
}

ctx.Data["ClosedProjects"] = closedProjects
}
}

// repoReviewerSelection items to bee shown
Expand Down Expand Up @@ -948,7 +974,7 @@ func NewIssue(ctx *context.Context) {
body := ctx.FormString("body")
ctx.Data["BodyQuery"] = body

isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects)
isProjectsEnabled := ctx.Repo.CanWrite(unit.TypeProjects)
ctx.Data["IsProjectsEnabled"] = isProjectsEnabled
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")
Expand Down Expand Up @@ -1422,7 +1448,6 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["IssueType"] = "all"
}

ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects)
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")

Expand Down Expand Up @@ -1464,6 +1489,8 @@ func ViewIssue(ctx *context.Context) {

repo := ctx.Repo.Repository

ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects)

// Get more information if it's a pull request.
if issue.IsPull {
if issue.PullRequest.HasMerged {
Expand Down
18 changes: 14 additions & 4 deletions routers/web/repo/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm"
project_model "code.gitea.io/gitea/models/project"
attachment_model "code.gitea.io/gitea/models/repo"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/json"
Expand All @@ -41,7 +41,17 @@ func MustEnableProjects(ctx *context.Context) {
}

if ctx.Repo.Repository != nil {
if !ctx.Repo.CanRead(unit.TypeProjects) {
isProjectsEnabled := false
if ctx.Repo.CanRead(unit.TypeProjects) {
projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects)
if err != nil {
ctx.ServerError("GetUnit", err)
return
}
isProjectsEnabled = projectsUnit.ProjectsConfig().ProjectsMode != repo_model.ProjectsModeOwner
}

if !isProjectsEnabled {
ctx.NotFound("MustEnableProjects", nil)
return
}
Expand Down Expand Up @@ -325,10 +335,10 @@ func ViewProject(ctx *context.Context) {
}

if project.CardType != project_model.CardTypeTextOnly {
issuesAttachmentMap := make(map[int64][]*attachment_model.Attachment)
issuesAttachmentMap := make(map[int64][]*repo_model.Attachment)
for _, issuesList := range issuesMap {
for _, issue := range issuesList {
if issueAttachment, err := attachment_model.GetAttachmentsByIssueIDImagesLatest(ctx, issue.ID); err == nil {
if issueAttachment, err := repo_model.GetAttachmentsByIssueIDImagesLatest(ctx, issue.ID); err == nil {
issuesAttachmentMap[issue.ID] = issueAttachment
}
}
Expand Down
3 changes: 3 additions & 0 deletions routers/web/repo/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,9 @@ func SettingsPost(ctx *context.Context) {
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: unit_model.TypeProjects,
Config: &repo_model.ProjectsConfig{
ProjectsMode: repo_model.ProjectsMode(form.ProjectsMode),
},
})
} else if !unit_model.TypeProjects.UnitGlobalDisabled() {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
Expand Down
6 changes: 5 additions & 1 deletion services/convert/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,11 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit
}
hasProjects := false
if _, err := repo.GetUnit(ctx, unit_model.TypeProjects); err == nil {
projectsMode := repo_model.ProjectsModeAll
if unit, err := repo.GetUnit(ctx, unit_model.TypeProjects); err == nil {
hasProjects = true
config := unit.ProjectsConfig()
projectsMode = config.ProjectsMode
}

hasReleases := false
Expand Down Expand Up @@ -211,6 +214,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
InternalTracker: internalTracker,
HasWiki: hasWiki,
HasProjects: hasProjects,
ProjectsMode: string(projectsMode),
HasReleases: hasReleases,
HasPackages: hasPackages,
HasActions: hasActions,
Expand Down
1 change: 1 addition & 0 deletions services/forms/repo_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ type RepoSettingForm struct {
ExternalTrackerRegexpPattern string
EnableCloseIssuesViaCommitInAnyBranch bool
EnableProjects bool
ProjectsMode string
EnableReleases bool
EnablePackages bool
EnablePulls bool
Expand Down
3 changes: 2 additions & 1 deletion templates/repo/header.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@
</a>
{{end}}

{{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects)}}
{{$projectsUnit := .Repository.MustGetUnit $.Context $.UnitTypeProjects}}
{{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects) (ne $projectsUnit.ProjectsConfig.ProjectsMode "owner")}}
<a href="{{.RepoLink}}/projects" class="{{if .IsProjectsPage}}active {{end}}item">
{{svg "octicon-project"}} {{ctx.Locale.Tr "repo.project_board"}}
{{if .Repository.NumOpenProjects}}
Expand Down
Loading
Loading