mirror of https://github.com/go-gitea/gitea.git
Enable/disable owner and repo projects independently (#28805)
Part of #23318 Add menu in repo settings to allow for repo admin to decide not just if projects are enabled or disabled per repo, but also which kind of projects (repo-level/owner-level) are enabled. If repo projects disabled, don't show the projects tab. ![grafik](https://github.com/go-gitea/gitea/assets/47871822/b9b43fb4-824b-47f9-b8e2-12004313647c) --------- Co-authored-by: delvh <dev.lh@web.de>
This commit is contained in:
parent
8553b4600e
commit
fe6792dff3
|
@ -520,6 +520,7 @@
|
||||||
id: 75
|
id: 75
|
||||||
repo_id: 1
|
repo_id: 1
|
||||||
type: 8
|
type: 8
|
||||||
|
config: "{\"ProjectsMode\":\"all\"}"
|
||||||
created_unix: 946684810
|
created_unix: 946684810
|
||||||
|
|
||||||
-
|
-
|
||||||
|
@ -650,12 +651,6 @@
|
||||||
type: 2
|
type: 2
|
||||||
created_unix: 946684810
|
created_unix: 946684810
|
||||||
|
|
||||||
-
|
|
||||||
id: 98
|
|
||||||
repo_id: 1
|
|
||||||
type: 8
|
|
||||||
created_unix: 946684810
|
|
||||||
|
|
||||||
-
|
-
|
||||||
id: 99
|
id: 99
|
||||||
repo_id: 1
|
repo_id: 1
|
||||||
|
|
|
@ -411,6 +411,11 @@ func (repo *Repository) MustGetUnit(ctx context.Context, tp unit.Type) *RepoUnit
|
||||||
Type: tp,
|
Type: tp,
|
||||||
Config: new(ActionsConfig),
|
Config: new(ActionsConfig),
|
||||||
}
|
}
|
||||||
|
} else if tp == unit.TypeProjects {
|
||||||
|
return &RepoUnit{
|
||||||
|
Type: tp,
|
||||||
|
Config: new(ProjectsConfig),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &RepoUnit{
|
return &RepoUnit{
|
||||||
|
|
|
@ -202,6 +202,53 @@ func (cfg *ActionsConfig) ToDB() ([]byte, error) {
|
||||||
return json.Marshal(cfg)
|
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"
|
||||||
|
// ProjectsModeNone doesn't allow projects
|
||||||
|
ProjectsModeNone ProjectsMode = "none"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectsMode == m || projectsMode == ProjectsModeAll
|
||||||
|
}
|
||||||
|
|
||||||
// BeforeSet is invoked from XORM before setting the value of a field of this object.
|
// BeforeSet is invoked from XORM before setting the value of a field of this object.
|
||||||
func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
|
func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
|
@ -217,7 +264,9 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
|
||||||
r.Config = new(IssuesConfig)
|
r.Config = new(IssuesConfig)
|
||||||
case unit.TypeActions:
|
case unit.TypeActions:
|
||||||
r.Config = new(ActionsConfig)
|
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
|
fallthrough
|
||||||
default:
|
default:
|
||||||
r.Config = new(UnitConfig)
|
r.Config = new(UnitConfig)
|
||||||
|
@ -265,6 +314,11 @@ func (r *RepoUnit) ActionsConfig() *ActionsConfig {
|
||||||
return r.Config.(*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) {
|
func getUnitsByRepoID(ctx context.Context, repoID int64) (units []*RepoUnit, err error) {
|
||||||
var tmpUnits []*RepoUnit
|
var tmpUnits []*RepoUnit
|
||||||
if err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Find(&tmpUnits); err != nil {
|
if err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Find(&tmpUnits); err != nil {
|
||||||
|
|
|
@ -93,6 +93,12 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
|
||||||
AllowRebaseUpdate: true,
|
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 {
|
} else {
|
||||||
units = append(units, repo_model.RepoUnit{
|
units = append(units, repo_model.RepoUnit{
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
|
|
|
@ -90,6 +90,7 @@ type Repository struct {
|
||||||
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
|
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
|
||||||
HasPullRequests bool `json:"has_pull_requests"`
|
HasPullRequests bool `json:"has_pull_requests"`
|
||||||
HasProjects bool `json:"has_projects"`
|
HasProjects bool `json:"has_projects"`
|
||||||
|
ProjectsMode string `json:"projects_mode"`
|
||||||
HasReleases bool `json:"has_releases"`
|
HasReleases bool `json:"has_releases"`
|
||||||
HasPackages bool `json:"has_packages"`
|
HasPackages bool `json:"has_packages"`
|
||||||
HasActions bool `json:"has_actions"`
|
HasActions bool `json:"has_actions"`
|
||||||
|
@ -180,6 +181,8 @@ type EditRepoOption struct {
|
||||||
HasPullRequests *bool `json:"has_pull_requests,omitempty"`
|
HasPullRequests *bool `json:"has_pull_requests,omitempty"`
|
||||||
// either `true` to enable project unit, or `false` to disable them.
|
// either `true` to enable project unit, or `false` to disable them.
|
||||||
HasProjects *bool `json:"has_projects,omitempty"`
|
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.
|
// either `true` to enable releases unit, or `false` to disable them.
|
||||||
HasReleases *bool `json:"has_releases,omitempty"`
|
HasReleases *bool `json:"has_releases,omitempty"`
|
||||||
// either `true` to enable packages unit, or `false` to disable them.
|
// either `true` to enable packages unit, or `false` to disable them.
|
||||||
|
|
|
@ -2090,7 +2090,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.pulls.default_allow_edits_from_maintainers = Allow edits from maintainers by default
|
||||||
settings.releases_desc = Enable Repository Releases
|
settings.releases_desc = Enable Repository Releases
|
||||||
settings.packages_desc = Enable Repository Packages Registry
|
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 = Only user or org projects
|
||||||
|
settings.projects_mode_all = All projects
|
||||||
settings.actions_desc = Enable Repository Actions
|
settings.actions_desc = Enable Repository Actions
|
||||||
settings.admin_settings = Administrator Settings
|
settings.admin_settings = Administrator Settings
|
||||||
settings.admin_enable_health_check = Enable Repository Health Checks (git fsck)
|
settings.admin_enable_health_check = Enable Repository Health Checks (git fsck)
|
||||||
|
|
|
@ -944,13 +944,33 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.HasProjects != nil && !unit_model.TypeProjects.UnitGlobalDisabled() {
|
currHasProjects := repo.UnitEnabled(ctx, unit_model.TypeProjects)
|
||||||
if *opts.HasProjects {
|
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{
|
units = append(units, repo_model.RepoUnit{
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
Type: unit_model.TypeProjects,
|
Type: unit_model.TypeProjects,
|
||||||
|
Config: config,
|
||||||
})
|
})
|
||||||
} else {
|
} else if !newHasProjects && !unit_model.TypeProjects.UnitGlobalDisabled() {
|
||||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
|
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -587,52 +587,63 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
|
||||||
if repo.Owner.IsOrganization() {
|
if repo.Owner.IsOrganization() {
|
||||||
repoOwnerType = project_model.TypeOrganization
|
repoOwnerType = project_model.TypeOrganization
|
||||||
}
|
}
|
||||||
|
|
||||||
|
projectsUnit := repo.MustGetUnit(ctx, unit.TypeProjects)
|
||||||
|
|
||||||
|
var openProjects []*project_model.Project
|
||||||
|
var closedProjects []*project_model.Project
|
||||||
var err error
|
var err error
|
||||||
projects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
|
||||||
ListOptions: db.ListOptionsAll,
|
if projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeRepo) {
|
||||||
RepoID: repo.ID,
|
openProjects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
||||||
IsClosed: optional.Some(false),
|
ListOptions: db.ListOptionsAll,
|
||||||
Type: project_model.TypeRepository,
|
RepoID: repo.ID,
|
||||||
})
|
IsClosed: optional.Some(false),
|
||||||
if err != nil {
|
Type: project_model.TypeRepository,
|
||||||
ctx.ServerError("GetProjects", err)
|
})
|
||||||
return
|
if err != nil {
|
||||||
}
|
ctx.ServerError("GetProjects", err)
|
||||||
projects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
return
|
||||||
ListOptions: db.ListOptionsAll,
|
}
|
||||||
OwnerID: repo.OwnerID,
|
closedProjects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
||||||
IsClosed: optional.Some(false),
|
ListOptions: db.ListOptionsAll,
|
||||||
Type: repoOwnerType,
|
RepoID: repo.ID,
|
||||||
})
|
IsClosed: optional.Some(true),
|
||||||
if err != nil {
|
Type: project_model.TypeRepository,
|
||||||
ctx.ServerError("GetProjects", err)
|
})
|
||||||
return
|
if err != nil {
|
||||||
|
ctx.ServerError("GetProjects", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["OpenProjects"] = append(projects, projects2...)
|
if projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeOwner) {
|
||||||
|
openProjects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
||||||
projects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
ListOptions: db.ListOptionsAll,
|
||||||
ListOptions: db.ListOptionsAll,
|
OwnerID: repo.OwnerID,
|
||||||
RepoID: repo.ID,
|
IsClosed: optional.Some(false),
|
||||||
IsClosed: optional.Some(true),
|
Type: repoOwnerType,
|
||||||
Type: project_model.TypeRepository,
|
})
|
||||||
})
|
if err != nil {
|
||||||
if err != nil {
|
ctx.ServerError("GetProjects", err)
|
||||||
ctx.ServerError("GetProjects", err)
|
return
|
||||||
return
|
}
|
||||||
}
|
openProjects = append(openProjects, openProjects2...)
|
||||||
projects2, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
closedProjects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
||||||
ListOptions: db.ListOptionsAll,
|
ListOptions: db.ListOptionsAll,
|
||||||
OwnerID: repo.OwnerID,
|
OwnerID: repo.OwnerID,
|
||||||
IsClosed: optional.Some(true),
|
IsClosed: optional.Some(true),
|
||||||
Type: repoOwnerType,
|
Type: repoOwnerType,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetProjects", err)
|
ctx.ServerError("GetProjects", err)
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
closedProjects = append(closedProjects, closedProjects2...)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["ClosedProjects"] = append(projects, projects2...)
|
ctx.Data["OpenProjects"] = openProjects
|
||||||
|
ctx.Data["ClosedProjects"] = closedProjects
|
||||||
}
|
}
|
||||||
|
|
||||||
// repoReviewerSelection items to bee shown
|
// repoReviewerSelection items to bee shown
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
project_model "code.gitea.io/gitea/models/project"
|
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/models/unit"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
@ -33,16 +33,17 @@ const (
|
||||||
tplProjectsView base.TplName = "repo/projects/view"
|
tplProjectsView base.TplName = "repo/projects/view"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MustEnableProjects check if projects are enabled in settings
|
// MustEnableRepoProjects check if repo projects are enabled in settings
|
||||||
func MustEnableProjects(ctx *context.Context) {
|
func MustEnableRepoProjects(ctx *context.Context) {
|
||||||
if unit.TypeProjects.UnitGlobalDisabled() {
|
if unit.TypeProjects.UnitGlobalDisabled() {
|
||||||
ctx.NotFound("EnableKanbanBoard", nil)
|
ctx.NotFound("EnableKanbanBoard", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Repo.Repository != nil {
|
if ctx.Repo.Repository != nil {
|
||||||
if !ctx.Repo.CanRead(unit.TypeProjects) {
|
projectsUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeProjects)
|
||||||
ctx.NotFound("MustEnableProjects", nil)
|
if !ctx.Repo.CanRead(unit.TypeProjects) || !projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeRepo) {
|
||||||
|
ctx.NotFound("MustEnableRepoProjects", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,10 +326,10 @@ func ViewProject(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if project.CardType != project_model.CardTypeTextOnly {
|
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 _, issuesList := range issuesMap {
|
||||||
for _, issue := range issuesList {
|
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
|
issuesAttachmentMap[issue.ID] = issueAttachment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -533,6 +533,9 @@ func SettingsPost(ctx *context.Context) {
|
||||||
units = append(units, repo_model.RepoUnit{
|
units = append(units, repo_model.RepoUnit{
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
Type: unit_model.TypeProjects,
|
Type: unit_model.TypeProjects,
|
||||||
|
Config: &repo_model.ProjectsConfig{
|
||||||
|
ProjectsMode: repo_model.ProjectsMode(form.ProjectsMode),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
} else if !unit_model.TypeProjects.UnitGlobalDisabled() {
|
} else if !unit_model.TypeProjects.UnitGlobalDisabled() {
|
||||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
|
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
|
||||||
|
|
|
@ -1344,7 +1344,7 @@ func registerRoutes(m *web.Route) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, reqRepoProjectsWriter, context.RepoMustNotBeArchived())
|
}, reqRepoProjectsWriter, context.RepoMustNotBeArchived())
|
||||||
}, reqRepoProjectsReader, repo.MustEnableProjects)
|
}, reqRepoProjectsReader, repo.MustEnableRepoProjects)
|
||||||
|
|
||||||
m.Group("/actions", func() {
|
m.Group("/actions", func() {
|
||||||
m.Get("", actions.List)
|
m.Get("", actions.List)
|
||||||
|
|
|
@ -113,8 +113,11 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||||
defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit
|
defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit
|
||||||
}
|
}
|
||||||
hasProjects := false
|
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
|
hasProjects = true
|
||||||
|
config := unit.ProjectsConfig()
|
||||||
|
projectsMode = config.ProjectsMode
|
||||||
}
|
}
|
||||||
|
|
||||||
hasReleases := false
|
hasReleases := false
|
||||||
|
@ -211,6 +214,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||||
InternalTracker: internalTracker,
|
InternalTracker: internalTracker,
|
||||||
HasWiki: hasWiki,
|
HasWiki: hasWiki,
|
||||||
HasProjects: hasProjects,
|
HasProjects: hasProjects,
|
||||||
|
ProjectsMode: string(projectsMode),
|
||||||
HasReleases: hasReleases,
|
HasReleases: hasReleases,
|
||||||
HasPackages: hasPackages,
|
HasPackages: hasPackages,
|
||||||
HasActions: hasActions,
|
HasActions: hasActions,
|
||||||
|
|
|
@ -142,6 +142,7 @@ type RepoSettingForm struct {
|
||||||
ExternalTrackerRegexpPattern string
|
ExternalTrackerRegexpPattern string
|
||||||
EnableCloseIssuesViaCommitInAnyBranch bool
|
EnableCloseIssuesViaCommitInAnyBranch bool
|
||||||
EnableProjects bool
|
EnableProjects bool
|
||||||
|
ProjectsMode string
|
||||||
EnableReleases bool
|
EnableReleases bool
|
||||||
EnablePackages bool
|
EnablePackages bool
|
||||||
EnablePulls bool
|
EnablePulls bool
|
||||||
|
|
|
@ -174,7 +174,8 @@
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects)}}
|
{{$projectsUnit := .Repository.MustGetUnit $.Context $.UnitTypeProjects}}
|
||||||
|
{{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects) ($projectsUnit.ProjectsConfig.IsProjectsAllowed "repo")}}
|
||||||
<a href="{{.RepoLink}}/projects" class="{{if .IsProjectsPage}}active {{end}}item">
|
<a href="{{.RepoLink}}/projects" class="{{if .IsProjectsPage}}active {{end}}item">
|
||||||
{{svg "octicon-project"}} {{ctx.Locale.Tr "repo.project_board"}}
|
{{svg "octicon-project"}} {{ctx.Locale.Tr "repo.project_board"}}
|
||||||
{{if .Repository.NumOpenProjects}}
|
{{if .Repository.NumOpenProjects}}
|
||||||
|
|
|
@ -446,13 +446,45 @@
|
||||||
|
|
||||||
{{$isProjectsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeProjects}}
|
{{$isProjectsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeProjects}}
|
||||||
{{$isProjectsGlobalDisabled := .UnitTypeProjects.UnitGlobalDisabled}}
|
{{$isProjectsGlobalDisabled := .UnitTypeProjects.UnitGlobalDisabled}}
|
||||||
|
{{$projectsUnit := .Repository.MustGetUnit $.Context $.UnitTypeProjects}}
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<label>{{ctx.Locale.Tr "repo.project_board"}}</label>
|
<label>{{ctx.Locale.Tr "repo.project_board"}}</label>
|
||||||
<div class="ui checkbox{{if $isProjectsGlobalDisabled}} disabled{{end}}"{{if $isProjectsGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
|
<div class="ui checkbox{{if $isProjectsGlobalDisabled}} disabled{{end}}"{{if $isProjectsGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
|
||||||
<input class="enable-system" name="enable_projects" type="checkbox" {{if $isProjectsEnabled}}checked{{end}}>
|
<input class="enable-system" name="enable_projects" type="checkbox" data-target="#projects_box" {{if $isProjectsEnabled}}checked{{end}}>
|
||||||
<label>{{ctx.Locale.Tr "repo.settings.projects_desc"}}</label>
|
<label>{{ctx.Locale.Tr "repo.settings.projects_desc"}}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field {{if not $isProjectsEnabled}} disabled{{end}} gt-pl-4" id="projects_box">
|
||||||
|
<p>
|
||||||
|
{{ctx.Locale.Tr "repo.settings.projects_mode_desc"}}
|
||||||
|
</p>
|
||||||
|
<div class="ui dropdown selection">
|
||||||
|
<select name="projects_mode">
|
||||||
|
<option value="repo" {{if or (not $isProjectsEnabled) (eq $projectsUnit.ProjectsConfig.ProjectsMode "repo")}}selected{{end}}>{{ctx.Locale.Tr "repo.settings.projects_mode_repo"}}</option>
|
||||||
|
<option value="owner" {{if or (not $isProjectsEnabled) (eq $projectsUnit.ProjectsConfig.ProjectsMode "owner")}}selected{{end}}>{{ctx.Locale.Tr "repo.settings.projects_mode_owner"}}</option>
|
||||||
|
<option value="all" {{if or (not $isProjectsEnabled) (eq $projectsUnit.ProjectsConfig.ProjectsMode "all")}}selected{{end}}>{{ctx.Locale.Tr "repo.settings.projects_mode_all"}}</option>
|
||||||
|
</select>
|
||||||
|
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||||
|
<div class="default text">
|
||||||
|
{{if (eq $projectsUnit.ProjectsConfig.ProjectsMode "repo")}}
|
||||||
|
{{ctx.Locale.Tr "repo.settings.projects_mode_repo"}}
|
||||||
|
{{end}}
|
||||||
|
{{if (eq $projectsUnit.ProjectsConfig.ProjectsMode "owner")}}
|
||||||
|
{{ctx.Locale.Tr "repo.settings.projects_mode_owner"}}
|
||||||
|
{{end}}
|
||||||
|
{{if (eq $projectsUnit.ProjectsConfig.ProjectsMode "all")}}
|
||||||
|
{{ctx.Locale.Tr "repo.settings.projects_mode_all"}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
<div class="menu">
|
||||||
|
<div class="item" data-value="repo">{{ctx.Locale.Tr "repo.settings.projects_mode_repo"}}</div>
|
||||||
|
<div class="item" data-value="owner">{{ctx.Locale.Tr "repo.settings.projects_mode_owner"}}</div>
|
||||||
|
<div class="item" data-value="all">{{ctx.Locale.Tr "repo.settings.projects_mode_all"}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
{{$isReleasesEnabled := .Repository.UnitEnabled $.Context $.UnitTypeReleases}}
|
{{$isReleasesEnabled := .Repository.UnitEnabled $.Context $.UnitTypeReleases}}
|
||||||
{{$isReleasesGlobalDisabled := .UnitTypeReleases.UnitGlobalDisabled}}
|
{{$isReleasesGlobalDisabled := .UnitTypeReleases.UnitGlobalDisabled}}
|
||||||
|
|
|
@ -19570,6 +19570,11 @@
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"x-go-name": "Private"
|
"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": {
|
"template": {
|
||||||
"description": "either `true` to make this repository a template or `false` to make it a normal repository",
|
"description": "either `true` to make this repository a template or `false` to make it a normal repository",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
@ -22491,6 +22496,10 @@
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"x-go-name": "Private"
|
"x-go-name": "Private"
|
||||||
},
|
},
|
||||||
|
"projects_mode": {
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "ProjectsMode"
|
||||||
|
},
|
||||||
"release_counter": {
|
"release_counter": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
|
|
Loading…
Reference in New Issue