From 7409d6e48206c2cc885d6aa3f5513834e7ea8f0e Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Mon, 15 Jan 2024 17:15:40 +0100 Subject: [PATCH 01/12] add possibility to enable/disable owner and repo projects independently --- models/repo/repo.go | 5 ++ models/repo/repo_unit.go | 25 +++++- modules/repository/create.go | 6 ++ modules/structs/repo.go | 6 ++ options/locale/locale_en-US.ini | 4 +- routers/api/v1/repo/repo.go | 31 ++++++- routers/web/repo/compare.go | 9 +- routers/web/repo/issue.go | 120 ++++++++++++++++++--------- routers/web/repo/projects.go | 10 ++- routers/web/repo/setting/setting.go | 4 + services/convert/repository.go | 9 +- services/forms/repo_form.go | 2 + templates/repo/header.tmpl | 3 +- templates/repo/settings/options.tmpl | 19 ++++- templates/swagger/v1_json.tmpl | 18 ++++ 15 files changed, 220 insertions(+), 51 deletions(-) diff --git a/models/repo/repo.go b/models/repo/repo.go index fb1849a4bb7fa..b48fc459b3be0 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -406,6 +406,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{ diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index 8a3ba1ee89092..c8d1edfe088d6 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -200,6 +200,22 @@ func (cfg *ActionsConfig) ToDB() ([]byte, error) { return json.Marshal(cfg) } +// ProjectsConfig describes projects config +type ProjectsConfig struct { + DisableRepoProjects bool + DisableOwnerProjects bool +} + +// 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 { @@ -215,7 +231,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) @@ -263,6 +281,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 { diff --git a/modules/repository/create.go b/modules/repository/create.go index 7c954a14121b6..ab5aaef02fd55 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -89,6 +89,12 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re Type: tp, Config: &repo_model.PullRequestsConfig{AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), AllowRebaseUpdate: true}, }) + } else if tp == unit.TypeProjects { + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: tp, + Config: &repo_model.ProjectsConfig{DisableRepoProjects: false, DisableOwnerProjects: false}, + }) } else { units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 3974c4db3ab1c..e2f4ec9ffb0ab 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -90,6 +90,8 @@ type Repository struct { ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` HasPullRequests bool `json:"has_pull_requests"` HasProjects bool `json:"has_projects"` + DisableRepoProjects bool `json:"disable_repo_projects"` + DisableOwnerProjects bool `json:"disable_owner_projects"` HasReleases bool `json:"has_releases"` HasPackages bool `json:"has_packages"` HasActions bool `json:"has_actions"` @@ -173,6 +175,10 @@ 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"` + // either `true` to enable repo projects, or `false` to disable them. + DisableRepoProjects *bool `json:"disable_repo_projects,omitempty"` + // either `true` to enable owner projects, or `false` to disable them. + DisableOwnerProjects *bool `json:"disable_owner_projects,omitempty"` // 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. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 0e5d91bd57648..a5badee9a2acc 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2053,7 +2053,9 @@ 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_disable_repo = Disable repository projects +settings.projects_disable_owner = Disable owner projects settings.actions_desc = Enable Repository Actions settings.admin_settings = Administrator Settings settings.admin_enable_health_check = Enable Repository Health Checks (git fsck) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 8ce03cf29caa5..542f626f78475 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -938,13 +938,38 @@ 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{ + DisableRepoProjects: false, + DisableOwnerProjects: false, + } + } else { + config = unit.ProjectsConfig() + } + + if opts.DisableRepoProjects != nil { + config.DisableRepoProjects = *opts.DisableRepoProjects + } + + if opts.DisableOwnerProjects != nil { + config.DisableOwnerProjects = *opts.DisableOwnerProjects + } + 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) } } diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 042b8ed692acf..aa86abde4b62c 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -845,7 +845,14 @@ func CompareDiff(ctx *context.Context) { } } - ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanWrite(unit.TypeProjects) + projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) + if err != nil { + ctx.ServerError("GetUnit", err) + return + } + projectsConfig := projectsUnit.ProjectsConfig() + isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects) && (!projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects) + ctx.Data["IsProjectsEnabled"] = isProjectsEnabled ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 2b5ab2117238d..e810ab38cdc1d 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -563,56 +563,80 @@ 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 + projectsUnit, err := repo.GetUnit(ctx, unit.TypeProjects) + if err != nil { + ctx.ServerError("GetUnit", err) + return + } + projectsConfig := projectsUnit.ProjectsConfig() + 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 + + var openProjects []*project_model.Project + if !projectsConfig.DisableRepoProjects { + 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...) } - 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 !projectsConfig.DisableOwnerProjects { + 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 + } + + openProjects = append(openProjects, ownerProjects...) } - ctx.Data["OpenProjects"] = append(projects, projects2...) + ctx.Data["OpenProjects"] = openProjects - 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 + var closedProjects []*project_model.Project + if !projectsConfig.DisableRepoProjects { + 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...) } - 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 + if !projectsConfig.DisableOwnerProjects { + 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"] = append(projects, projects2...) + ctx.Data["ClosedProjects"] = closedProjects } // repoReviewerSelection items to bee shown @@ -935,7 +959,13 @@ func NewIssue(ctx *context.Context) { body := ctx.FormString("body") ctx.Data["BodyQuery"] = body - isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects) + projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) + if err != nil { + ctx.ServerError("GetUnit", err) + return + } + projectsConfig := projectsUnit.ProjectsConfig() + isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects) && (!projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects) ctx.Data["IsProjectsEnabled"] = isProjectsEnabled ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") @@ -1409,7 +1439,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") @@ -1450,6 +1479,15 @@ func ViewIssue(ctx *context.Context) { repo := ctx.Repo.Repository + projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) + if err != nil { + ctx.ServerError("GetUnit", err) + return + } + projectsConfig := projectsUnit.ProjectsConfig() + isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects) && (!projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects) + ctx.Data["IsProjectsEnabled"] = isProjectsEnabled + // Get more information if it's a pull request. if issue.IsPull { if issue.PullRequest.HasMerged { diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 5694575b468de..025f724613e10 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -41,7 +41,15 @@ func MustEnableProjects(ctx *context.Context) { } if ctx.Repo.Repository != nil { - if !ctx.Repo.CanRead(unit.TypeProjects) { + projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) + if err != nil { + ctx.ServerError("GetUnit", err) + return + } + projectsConfig := projectsUnit.ProjectsConfig() + isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects) && !projectsConfig.DisableRepoProjects + + if !isProjectsEnabled { ctx.NotFound("MustEnableProjects", nil) return } diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index e721b7c739972..20dc7aff5f8ae 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -534,6 +534,10 @@ func SettingsPost(ctx *context.Context) { units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, Type: unit_model.TypeProjects, + Config: &repo_model.ProjectsConfig{ + DisableRepoProjects: form.ProjectsDisableRepo, + DisableOwnerProjects: form.ProjectsDisableOwner, + }, }) } else if !unit_model.TypeProjects.UnitGlobalDisabled() { deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects) diff --git a/services/convert/repository.go b/services/convert/repository.go index 71038cd0629cb..c97624fcdbe65 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -110,8 +110,13 @@ 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 { + disableRepoProjects := false + disableOwnerProjects := false + if unit, err := repo.GetUnit(ctx, unit_model.TypeProjects); err == nil { hasProjects = true + config := unit.ProjectsConfig() + disableRepoProjects = config.DisableRepoProjects + disableOwnerProjects = config.DisableOwnerProjects } hasReleases := false @@ -204,6 +209,8 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR InternalTracker: internalTracker, HasWiki: hasWiki, HasProjects: hasProjects, + DisableRepoProjects: disableRepoProjects, + DisableOwnerProjects: disableOwnerProjects, HasReleases: hasReleases, HasPackages: hasPackages, HasActions: hasActions, diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 86599000a5882..433f29a999959 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -150,6 +150,8 @@ type RepoSettingForm struct { ExternalTrackerRegexpPattern string EnableCloseIssuesViaCommitInAnyBranch bool EnableProjects bool + ProjectsDisableRepo bool + ProjectsDisableOwner bool EnableReleases bool EnablePackages bool EnablePulls bool diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 9630cd3a6f769..e67977ec7c756 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -193,7 +193,8 @@ {{end}} - {{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects)}} + {{$projectsUnit := .Repository.MustGetUnit $.Context $.UnitTypeProjects}} + {{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects) (not $projectsUnit.ProjectsConfig.DisableRepoProjects)}} {{svg "octicon-project"}} {{ctx.Locale.Tr "repo.project_board"}} {{if .Repository.NumOpenProjects}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 8456bb409b358..43fec6d659a0e 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -429,13 +429,30 @@ {{$isProjectsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeProjects}} {{$isProjectsGlobalDisabled := .UnitTypeProjects.UnitGlobalDisabled}} + {{$projectsUnit := .Repository.MustGetUnit $.Context $.UnitTypeProjects}}
- +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ +
{{$isReleasesEnabled := .Repository.UnitEnabled $.Context $.UnitTypeReleases}} {{$isReleasesGlobalDisabled := .UnitTypeReleases.UnitGlobalDisabled}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index de3bc331f160b..ffb8a3b618d71 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -19236,6 +19236,16 @@ "type": "string", "x-go-name": "Description" }, + "disable_owner_projects": { + "description": "either `true` to enable owner projects, or `false` to disable them.", + "type": "boolean", + "x-go-name": "DisableOwnerProjects" + }, + "disable_repo_projects": { + "description": "either `true` to enable repo projects, or `false` to disable them.", + "type": "boolean", + "x-go-name": "DisableRepoProjects" + }, "enable_prune": { "description": "enable prune - remove obsolete remote-tracking references", "type": "boolean", @@ -22070,6 +22080,14 @@ "type": "string", "x-go-name": "Description" }, + "disable_owner_projects": { + "type": "boolean", + "x-go-name": "DisableOwnerProjects" + }, + "disable_repo_projects": { + "type": "boolean", + "x-go-name": "DisableRepoProjects" + }, "empty": { "type": "boolean", "x-go-name": "Empty" From 8a7f228a26e3c49b49010d5d7ca3497e52fa6033 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Mon, 15 Jan 2024 19:22:28 +0100 Subject: [PATCH 02/12] fix for disabled projects --- routers/web/repo/compare.go | 15 ++-- routers/web/repo/issue.go | 156 ++++++++++++++++++----------------- routers/web/repo/projects.go | 14 ++-- 3 files changed, 97 insertions(+), 88 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index aa86abde4b62c..42f01533bf651 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -845,14 +845,15 @@ func CompareDiff(ctx *context.Context) { } } - projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) - if err != nil { - ctx.ServerError("GetUnit", err) - return + if ctx.Repo.CanRead(unit.TypeProjects) { + projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) + if err != nil { + ctx.ServerError("GetUnit", err) + return + } + projectsConfig := projectsUnit.ProjectsConfig() + ctx.Data["IsProjectsEnabled"] = !projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects } - projectsConfig := projectsUnit.ProjectsConfig() - isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects) && (!projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects) - ctx.Data["IsProjectsEnabled"] = isProjectsEnabled ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index e810ab38cdc1d..42f1c9562cf32 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -563,80 +563,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 - projectsUnit, err := repo.GetUnit(ctx, unit.TypeProjects) - if err != nil { - ctx.ServerError("GetUnit", err) - return - } - projectsConfig := projectsUnit.ProjectsConfig() - - repoOwnerType := project_model.TypeIndividual - if repo.Owner.IsOrganization() { - repoOwnerType = project_model.TypeOrganization - } - - var openProjects []*project_model.Project - if !projectsConfig.DisableRepoProjects { - 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 ctx.Repo.CanRead(unit.TypeProjects) { + projectsUnit, err := repo.GetUnit(ctx, unit.TypeProjects) if err != nil { - ctx.ServerError("GetProjects", err) + ctx.ServerError("GetUnit", err) return } - openProjects = append(openProjects, repoProjects...) - } - if !projectsConfig.DisableOwnerProjects { - 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 + projectsConfig := projectsUnit.ProjectsConfig() + + repoOwnerType := project_model.TypeIndividual + if repo.Owner.IsOrganization() { + repoOwnerType = project_model.TypeOrganization } - openProjects = append(openProjects, ownerProjects...) - } + var openProjects []*project_model.Project + if !projectsConfig.DisableRepoProjects { + 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.DisableOwnerProjects { + 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["OpenProjects"] = openProjects + openProjects = append(openProjects, ownerProjects...) + } - var closedProjects []*project_model.Project - if !projectsConfig.DisableRepoProjects { - 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 + ctx.Data["OpenProjects"] = openProjects + + var closedProjects []*project_model.Project + if !projectsConfig.DisableRepoProjects { + 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.DisableOwnerProjects { + 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, repoProjects...) - } - if !projectsConfig.DisableOwnerProjects { - 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...) } - closedProjects = append(closedProjects, ownerProjects...) + ctx.Data["ClosedProjects"] = closedProjects } - - ctx.Data["ClosedProjects"] = closedProjects } // repoReviewerSelection items to bee shown @@ -959,13 +961,16 @@ func NewIssue(ctx *context.Context) { body := ctx.FormString("body") ctx.Data["BodyQuery"] = body - projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) - if err != nil { - ctx.ServerError("GetUnit", err) - return + 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 + } + projectsConfig := projectsUnit.ProjectsConfig() + isProjectsEnabled = !projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects } - projectsConfig := projectsUnit.ProjectsConfig() - isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects) && (!projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects) ctx.Data["IsProjectsEnabled"] = isProjectsEnabled ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") @@ -1479,14 +1484,15 @@ func ViewIssue(ctx *context.Context) { repo := ctx.Repo.Repository - projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) - if err != nil { - ctx.ServerError("GetUnit", err) - return + if ctx.Repo.CanRead(unit.TypeProjects) { + projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) + if err != nil { + ctx.ServerError("GetUnit", err) + return + } + projectsConfig := projectsUnit.ProjectsConfig() + ctx.Data["IsProjectsEnabled"] = !projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects } - projectsConfig := projectsUnit.ProjectsConfig() - isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects) && (!projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects) - ctx.Data["IsProjectsEnabled"] = isProjectsEnabled // Get more information if it's a pull request. if issue.IsPull { diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 025f724613e10..3fb94e0680111 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -41,13 +41,15 @@ func MustEnableProjects(ctx *context.Context) { } if ctx.Repo.Repository != nil { - projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) - if err != nil { - ctx.ServerError("GetUnit", err) - return + 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().DisableRepoProjects } - projectsConfig := projectsUnit.ProjectsConfig() - isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects) && !projectsConfig.DisableRepoProjects if !isProjectsEnabled { ctx.NotFound("MustEnableProjects", nil) From 503b014c3d2a9c5b86a596b9e88520ffab559202 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Mon, 22 Jan 2024 15:16:22 +0100 Subject: [PATCH 03/12] Update routers/web/repo/compare.go --- routers/web/repo/compare.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 42f01533bf651..ccb13d11c784e 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -845,7 +845,7 @@ func CompareDiff(ctx *context.Context) { } } - if ctx.Repo.CanRead(unit.TypeProjects) { + if ctx.Repo.CanWrite(unit.TypeProjects) { projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) if err != nil { ctx.ServerError("GetUnit", err) From 3e845b4cccd206f12e67e40c45eb383a9c34599d Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Mon, 22 Jan 2024 15:20:22 +0100 Subject: [PATCH 04/12] Update routers/web/repo/issue.go --- routers/web/repo/issue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 646fbeb85b0b3..defe4274a4272 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -978,7 +978,7 @@ func NewIssue(ctx *context.Context) { ctx.Data["BodyQuery"] = body isProjectsEnabled := false - if ctx.Repo.CanRead(unit.TypeProjects) { + if ctx.Repo.CanWrite(unit.TypeProjects) { projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) if err != nil { ctx.ServerError("GetUnit", err) From 1cc7f931a75c86362e64d9b29cbe5943651b413f Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Mon, 26 Feb 2024 21:12:45 +0100 Subject: [PATCH 05/12] switch to select ui --- models/repo/repo_unit.go | 15 ++++++++++-- modules/repository/create.go | 2 +- modules/structs/repo.go | 9 +++----- options/locale/locale_en-US.ini | 6 +++-- routers/api/v1/repo/repo.go | 11 +++------ routers/web/repo/compare.go | 10 +------- routers/web/repo/issue.go | 29 +++++------------------- routers/web/repo/projects.go | 8 +++---- routers/web/repo/setting/setting.go | 3 +-- services/convert/repository.go | 9 +++----- services/forms/repo_form.go | 3 +-- templates/repo/header.tmpl | 2 +- templates/repo/settings/options.tmpl | 34 ++++++++++++++++++++-------- templates/swagger/v1_json.tmpl | 27 ++++++++-------------- 14 files changed, 74 insertions(+), 94 deletions(-) diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index ff47d25512abc..589018fc869d3 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -202,10 +202,21 @@ 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" +) + // ProjectsConfig describes projects config type ProjectsConfig struct { - DisableRepoProjects bool - DisableOwnerProjects bool + ProjectsMode ProjectsMode } // FromDB fills up a ProjectsConfig from serialized format. diff --git a/modules/repository/create.go b/modules/repository/create.go index 15ff88cbb7e60..f009c0880d711 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -97,7 +97,7 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, Type: tp, - Config: &repo_model.ProjectsConfig{DisableRepoProjects: false, DisableOwnerProjects: false}, + Config: &repo_model.ProjectsConfig{ProjectsMode: repo_model.ProjectsModeAll}, }) } else { units = append(units, repo_model.RepoUnit{ diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 527dd2b0bff8a..bc8eb0b756973 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -90,8 +90,7 @@ type Repository struct { ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` HasPullRequests bool `json:"has_pull_requests"` HasProjects bool `json:"has_projects"` - DisableRepoProjects bool `json:"disable_repo_projects"` - DisableOwnerProjects bool `json:"disable_owner_projects"` + ProjectsMode string `json:"projects_mode"` HasReleases bool `json:"has_releases"` HasPackages bool `json:"has_packages"` HasActions bool `json:"has_actions"` @@ -182,10 +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"` - // either `true` to enable repo projects, or `false` to disable them. - DisableRepoProjects *bool `json:"disable_repo_projects,omitempty"` - // either `true` to enable owner projects, or `false` to disable them. - DisableOwnerProjects *bool `json:"disable_owner_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. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 7e0ed12e12f74..87b83f70d0b59 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2077,8 +2077,10 @@ settings.pulls.default_allow_edits_from_maintainers = Allow edits from maintaine settings.releases_desc = Enable Repository Releases settings.packages_desc = Enable Repository Packages Registry settings.projects_desc = Enable Projects -settings.projects_disable_repo = Disable repository projects -settings.projects_disable_owner = Disable owner projects +settings.projects_mode_desc = Projects Mode (which kinds projects to show) +settings.projects_mode_repo = Repo projects only +settings.projects_mode_owner = Owner (user or org) projects only +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) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 7543a8dcd5228..ed66982992be9 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -955,19 +955,14 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { var config *repo_model.ProjectsConfig if err != nil { config = &repo_model.ProjectsConfig{ - DisableRepoProjects: false, - DisableOwnerProjects: false, + ProjectsMode: repo_model.ProjectsModeAll, } } else { config = unit.ProjectsConfig() } - if opts.DisableRepoProjects != nil { - config.DisableRepoProjects = *opts.DisableRepoProjects - } - - if opts.DisableOwnerProjects != nil { - config.DisableOwnerProjects = *opts.DisableOwnerProjects + if opts.ProjectsMode != nil { + config.ProjectsMode = repo_model.ProjectsMode(*opts.ProjectsMode) } units = append(units, repo_model.RepoUnit{ diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index ff607f0ba494c..535487d5fdbd0 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -847,15 +847,7 @@ func CompareDiff(ctx *context.Context) { } } - if ctx.Repo.CanWrite(unit.TypeProjects) { - projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) - if err != nil { - ctx.ServerError("GetUnit", err) - return - } - projectsConfig := projectsUnit.ProjectsConfig() - ctx.Data["IsProjectsEnabled"] = !projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects - } + ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanWrite(unit.TypeProjects) ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index d27eec81cc1a5..d7777dbe624a9 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -594,7 +594,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { } var openProjects []*project_model.Project - if !projectsConfig.DisableRepoProjects { + if projectsConfig.ProjectsMode != repo_model.ProjectsModeOwner { repoProjects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ ListOptions: db.ListOptionsAll, RepoID: repo.ID, @@ -607,7 +607,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { } openProjects = append(openProjects, repoProjects...) } - if !projectsConfig.DisableOwnerProjects { + if projectsConfig.ProjectsMode != repo_model.ProjectsModeRepo { ownerProjects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ ListOptions: db.ListOptionsAll, OwnerID: repo.OwnerID, @@ -625,7 +625,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.Data["OpenProjects"] = openProjects var closedProjects []*project_model.Project - if !projectsConfig.DisableRepoProjects { + if projectsConfig.ProjectsMode != repo_model.ProjectsModeOwner { repoProjects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ ListOptions: db.ListOptionsAll, RepoID: repo.ID, @@ -639,7 +639,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { closedProjects = append(closedProjects, repoProjects...) } - if !projectsConfig.DisableOwnerProjects { + if projectsConfig.ProjectsMode != repo_model.ProjectsModeRepo { ownerProjects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ ListOptions: db.ListOptionsAll, OwnerID: repo.OwnerID, @@ -974,16 +974,7 @@ func NewIssue(ctx *context.Context) { body := ctx.FormString("body") ctx.Data["BodyQuery"] = body - isProjectsEnabled := false - if ctx.Repo.CanWrite(unit.TypeProjects) { - projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) - if err != nil { - ctx.ServerError("GetUnit", err) - return - } - projectsConfig := projectsUnit.ProjectsConfig() - isProjectsEnabled = !projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects - } + isProjectsEnabled := ctx.Repo.CanWrite(unit.TypeProjects) ctx.Data["IsProjectsEnabled"] = isProjectsEnabled ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") @@ -1498,15 +1489,7 @@ func ViewIssue(ctx *context.Context) { repo := ctx.Repo.Repository - if ctx.Repo.CanRead(unit.TypeProjects) { - projectsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeProjects) - if err != nil { - ctx.ServerError("GetUnit", err) - return - } - projectsConfig := projectsUnit.ProjectsConfig() - ctx.Data["IsProjectsEnabled"] = !projectsConfig.DisableRepoProjects || !projectsConfig.DisableOwnerProjects - } + ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects) // Get more information if it's a pull request. if issue.IsPull { diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 2eda3130538b7..60774341b84fc 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -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/context" @@ -48,7 +48,7 @@ func MustEnableProjects(ctx *context.Context) { ctx.ServerError("GetUnit", err) return } - isProjectsEnabled = !projectsUnit.ProjectsConfig().DisableRepoProjects + isProjectsEnabled = projectsUnit.ProjectsConfig().ProjectsMode != repo_model.ProjectsModeOwner } if !isProjectsEnabled { @@ -335,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 } } diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index abc2114182e4a..020746c54ffaf 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -535,8 +535,7 @@ func SettingsPost(ctx *context.Context) { RepoID: repo.ID, Type: unit_model.TypeProjects, Config: &repo_model.ProjectsConfig{ - DisableRepoProjects: form.ProjectsDisableRepo, - DisableOwnerProjects: form.ProjectsDisableOwner, + ProjectsMode: repo_model.ProjectsMode(form.ProjectsMode), }, }) } else if !unit_model.TypeProjects.UnitGlobalDisabled() { diff --git a/services/convert/repository.go b/services/convert/repository.go index f1d67a47fcddc..39efd304a96ad 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -113,13 +113,11 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit } hasProjects := false - disableRepoProjects := false - disableOwnerProjects := false + projectsMode := repo_model.ProjectsModeAll if unit, err := repo.GetUnit(ctx, unit_model.TypeProjects); err == nil { hasProjects = true config := unit.ProjectsConfig() - disableRepoProjects = config.DisableRepoProjects - disableOwnerProjects = config.DisableOwnerProjects + projectsMode = config.ProjectsMode } hasReleases := false @@ -216,8 +214,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR InternalTracker: internalTracker, HasWiki: hasWiki, HasProjects: hasProjects, - DisableRepoProjects: disableRepoProjects, - DisableOwnerProjects: disableOwnerProjects, + ProjectsMode: string(projectsMode), HasReleases: hasReleases, HasPackages: hasPackages, HasActions: hasActions, diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 5f659889be121..b648dbe9aadfc 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -142,8 +142,7 @@ type RepoSettingForm struct { ExternalTrackerRegexpPattern string EnableCloseIssuesViaCommitInAnyBranch bool EnableProjects bool - ProjectsDisableRepo bool - ProjectsDisableOwner bool + ProjectsMode string EnableReleases bool EnablePackages bool EnablePulls bool diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 127b5ec2222d0..75dfede345d9b 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -175,7 +175,7 @@ {{end}} {{$projectsUnit := .Repository.MustGetUnit $.Context $.UnitTypeProjects}} - {{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects) (not $projectsUnit.ProjectsConfig.DisableRepoProjects)}} + {{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects) (ne $projectsUnit.ProjectsConfig.ProjectsMode "owner")}}
{{svg "octicon-project"}} {{ctx.Locale.Tr "repo.project_board"}} {{if .Repository.NumOpenProjects}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 2e90bcedab6f6..542c37facc62c 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -454,17 +454,31 @@ -
-
-
- - +
+

+ {{ctx.Locale.Tr "repo.settings.projects_mode_desc"}} +

+ -
-
- - +
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index ada17e003c3d9..200a8309c4937 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -19358,16 +19358,6 @@ "type": "string", "x-go-name": "Description" }, - "disable_owner_projects": { - "description": "either `true` to enable owner projects, or `false` to disable them.", - "type": "boolean", - "x-go-name": "DisableOwnerProjects" - }, - "disable_repo_projects": { - "description": "either `true` to enable repo projects, or `false` to disable them.", - "type": "boolean", - "x-go-name": "DisableRepoProjects" - }, "enable_prune": { "description": "enable prune - remove obsolete remote-tracking references", "type": "boolean", @@ -19438,6 +19428,11 @@ "type": "boolean", "x-go-name": "Private" }, + "projects_mode": { + "description": "`repo` to only allow repo-level projects, `owner` to only allow owner projects, `all` to allow both.", + "type": "string", + "x-go-name": "ProjectsMode" + }, "template": { "description": "either `true` to make this repository a template or `false` to make it a normal repository", "type": "boolean", @@ -22211,14 +22206,6 @@ "type": "string", "x-go-name": "Description" }, - "disable_owner_projects": { - "type": "boolean", - "x-go-name": "DisableOwnerProjects" - }, - "disable_repo_projects": { - "type": "boolean", - "x-go-name": "DisableRepoProjects" - }, "empty": { "type": "boolean", "x-go-name": "Empty" @@ -22355,6 +22342,10 @@ "type": "boolean", "x-go-name": "Private" }, + "projects_mode": { + "type": "string", + "x-go-name": "ProjectsMode" + }, "release_counter": { "type": "integer", "format": "int64", From 7409618fbc3327511fef9353cdd4c1daae088c41 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Mon, 26 Feb 2024 21:14:47 +0100 Subject: [PATCH 06/12] Update options/locale/locale_en-US.ini --- options/locale/locale_en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 87b83f70d0b59..3df8acdff8370 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2077,7 +2077,7 @@ settings.pulls.default_allow_edits_from_maintainers = Allow edits from maintaine settings.releases_desc = Enable Repository Releases settings.packages_desc = Enable Repository Packages Registry settings.projects_desc = Enable Projects -settings.projects_mode_desc = Projects Mode (which kinds projects to show) +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 settings.projects_mode_all = All projects From f70ca79b05550550c45b7a42322eb6a9b200b5fb Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Sat, 2 Mar 2024 22:28:01 +0100 Subject: [PATCH 07/12] Update templates/repo/settings/options.tmpl Co-authored-by: delvh --- templates/repo/settings/options.tmpl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 542c37facc62c..5a85192a43793 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -463,7 +463,8 @@ - {{svg "octicon-triangle-down" 14 "dropdown icon"}} + + {{svg "octicon-triangle-down" 14 "dropdown icon"}}
{{if (eq $projectsUnit.ProjectsConfig.ProjectsMode "repo")}} {{ctx.Locale.Tr "repo.settings.projects_mode_repo"}} From 14ad3e84e8c90e9539ac6c6ed6047c2e91e444a3 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Sat, 2 Mar 2024 22:29:34 +0100 Subject: [PATCH 08/12] Update options/locale/locale_en-US.ini Co-authored-by: delvh --- options/locale/locale_en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 8d8b843214470..aa950a59278c2 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2080,7 +2080,7 @@ settings.packages_desc = Enable Repository Packages Registry 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 +settings.projects_mode_owner = Only user or org projects settings.projects_mode_all = All projects settings.actions_desc = Enable Repository Actions settings.admin_settings = Administrator Settings From 636de49df8c229538c10f59f7e410a9b3ef22dd6 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Sun, 3 Mar 2024 14:43:12 +0100 Subject: [PATCH 09/12] add simple method for projects mode check, decouple projects mode check from permission check --- models/repo/repo_unit.go | 20 ++++++++ routers/web/repo/issue.go | 93 ++++++++++++++++++++---------------- routers/web/repo/projects.go | 8 ++-- routers/web/web.go | 2 +- templates/repo/header.tmpl | 2 +- 5 files changed, 78 insertions(+), 47 deletions(-) diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index 589018fc869d3..d41e4e0238c7b 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -212,6 +212,8 @@ const ( ProjectsModeOwner ProjectsMode = "owner" // ProjectsModeAll allows both kinds of projects ProjectsModeAll ProjectsMode = "all" + // ProjectsModeNone doesn't allow projects + ProjectsModeNone ProjectsMode = "none" ) // ProjectsConfig describes projects config @@ -229,6 +231,24 @@ func (cfg *ProjectsConfig) ToDB() ([]byte, error) { return json.Marshal(cfg) } +func (cfg *ProjectsConfig) GetProjectsMode() ProjectsMode { + if cfg.ProjectsMode != "" { + return cfg.ProjectsMode + } + + return ProjectsModeNone +} + +func (cfg *ProjectsConfig) IsProjectsAllowed(m ProjectsMode) bool { + projectsMode := cfg.GetProjectsMode() + + if m == ProjectsModeNone { + return true + } else { + return projectsMode == m || projectsMode == ProjectsModeAll + } +} + // 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 { diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 1abd5e2ba5a3d..b8c7f70aa6d34 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -587,52 +587,63 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { 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: optional.Some(false), - 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: optional.Some(false), - Type: repoOwnerType, - }) - if err != nil { - ctx.ServerError("GetProjects", err) - return - } - ctx.Data["OpenProjects"] = append(projects, projects2...) + projectsUnit := repo.MustGetUnit(ctx, unit.TypeProjects) - projects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{ - ListOptions: db.ListOptionsAll, - RepoID: repo.ID, - IsClosed: optional.Some(true), - Type: project_model.TypeRepository, - }) - if err != nil { - ctx.ServerError("GetProjects", err) - return + var openProjects []*project_model.Project + var closedProjects []*project_model.Project + var err error + + if projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeRepo) { + openProjects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + RepoID: repo.ID, + IsClosed: optional.Some(false), + Type: project_model.TypeRepository, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } + closedProjects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + RepoID: repo.ID, + IsClosed: optional.Some(true), + 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: optional.Some(true), - Type: repoOwnerType, - }) - if err != nil { - ctx.ServerError("GetProjects", err) - return + + if projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeOwner) { + openProjects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + OwnerID: repo.OwnerID, + IsClosed: optional.Some(false), + Type: repoOwnerType, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } + openProjects = append(openProjects, openProjects2...) + closedProjects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + OwnerID: repo.OwnerID, + IsClosed: optional.Some(true), + Type: repoOwnerType, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } + closedProjects = append(closedProjects, closedProjects2...) } - ctx.Data["ClosedProjects"] = append(projects, projects2...) + ctx.Data["OpenProjects"] = openProjects + ctx.Data["ClosedProjects"] = closedProjects } // repoReviewerSelection items to bee shown diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 9aba738b51a72..44768d35d1495 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -33,8 +33,8 @@ const ( tplProjectsView base.TplName = "repo/projects/view" ) -// MustEnableProjects check if projects are enabled in settings -func MustEnableProjects(ctx *context.Context) { +// MustEnableRepoProjects check if repo projects are enabled in settings +func MustEnableRepoProjects(ctx *context.Context) { if unit.TypeProjects.UnitGlobalDisabled() { ctx.NotFound("EnableKanbanBoard", nil) return @@ -48,11 +48,11 @@ func MustEnableProjects(ctx *context.Context) { ctx.ServerError("GetUnit", err) return } - isProjectsEnabled = projectsUnit.ProjectsConfig().ProjectsMode != repo_model.ProjectsModeOwner + isProjectsEnabled = projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeRepo) } if !isProjectsEnabled { - ctx.NotFound("MustEnableProjects", nil) + ctx.NotFound("MustEnableRepoProjects", nil) return } } diff --git a/routers/web/web.go b/routers/web/web.go index 9de652fba53d9..14d31b3a9088f 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1344,7 +1344,7 @@ func registerRoutes(m *web.Route) { }) }) }, reqRepoProjectsWriter, context.RepoMustNotBeArchived()) - }, reqRepoProjectsReader, repo.MustEnableProjects) + }, reqRepoProjectsReader, repo.MustEnableRepoProjects) m.Group("/actions", func() { m.Get("", actions.List) diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 1c3d675575422..b692c851ee383 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -175,7 +175,7 @@ {{end}} {{$projectsUnit := .Repository.MustGetUnit $.Context $.UnitTypeProjects}} - {{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects) (ne $projectsUnit.ProjectsConfig.ProjectsMode "owner")}} + {{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects) ($projectsUnit.ProjectsConfig.IsProjectsAllowed "repo")}} {{svg "octicon-project"}} {{ctx.Locale.Tr "repo.project_board"}} {{if .Repository.NumOpenProjects}} From cff75c01a5f5e44a91d15bc8a1ee4222c460705d Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Sun, 3 Mar 2024 14:57:14 +0100 Subject: [PATCH 10/12] additional simplifications --- routers/web/repo/projects.go | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 44768d35d1495..86909b5fd0f5a 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -41,17 +41,8 @@ func MustEnableRepoProjects(ctx *context.Context) { } if ctx.Repo.Repository != nil { - 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().IsProjectsAllowed(repo_model.ProjectsModeRepo) - } - - if !isProjectsEnabled { + projectsUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeProjects) + if !ctx.Repo.CanRead(unit.TypeProjects) || !projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeRepo) { ctx.NotFound("MustEnableRepoProjects", nil) return } From 2789c043ddeb911fe2bdf16b472fa2b2d596f9f2 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Sun, 3 Mar 2024 15:26:19 +0100 Subject: [PATCH 11/12] statisfy linter --- models/repo/repo_unit.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index d41e4e0238c7b..6b9dde7fafb33 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -244,9 +244,9 @@ func (cfg *ProjectsConfig) IsProjectsAllowed(m ProjectsMode) bool { if m == ProjectsModeNone { return true - } else { - return projectsMode == m || projectsMode == ProjectsModeAll } + + return projectsMode == m || projectsMode == ProjectsModeAll } // BeforeSet is invoked from XORM before setting the value of a field of this object. From 1a88d9355dca207c2e37d9a9e729eecb21b87121 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Sun, 3 Mar 2024 20:22:36 +0100 Subject: [PATCH 12/12] fix fixture --- models/fixtures/repo_unit.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/models/fixtures/repo_unit.yml b/models/fixtures/repo_unit.yml index 4b266749905d0..6714294e2b66e 100644 --- a/models/fixtures/repo_unit.yml +++ b/models/fixtures/repo_unit.yml @@ -520,6 +520,7 @@ id: 75 repo_id: 1 type: 8 + config: "{\"ProjectsMode\":\"all\"}" created_unix: 946684810 - @@ -650,12 +651,6 @@ type: 2 created_unix: 946684810 -- - id: 98 - repo_id: 1 - type: 8 - created_unix: 946684810 - - id: 99 repo_id: 1