From d4834071da9ce010fd4677ef339c598dd23dc130 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Sun, 8 May 2022 23:51:50 +0800
Subject: [PATCH] Repository level enable package or disable (#19323)

---
 integrations/mssql.ini.tmpl          |  3 +++
 integrations/mysql.ini.tmpl          |  3 +++
 integrations/mysql8.ini.tmpl         |  3 +++
 integrations/pgsql.ini.tmpl          |  3 +++
 integrations/sqlite.ini.tmpl         |  3 +++
 models/repo/repo_unit.go             |  2 +-
 models/unit/unit.go                  | 15 +++++++++++++++
 modules/context/repo.go              |  1 +
 modules/setting/repository.go        |  4 ++++
 options/locale/locale_en-US.ini      |  1 +
 routers/web/repo/setting.go          |  9 +++++++++
 services/forms/repo_form.go          |  1 +
 templates/repo/header.tmpl           |  8 +++++---
 templates/repo/settings/options.tmpl | 13 +++++++++++++
 14 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/integrations/mssql.ini.tmpl b/integrations/mssql.ini.tmpl
index 00f55ec620..b076bb863c 100644
--- a/integrations/mssql.ini.tmpl
+++ b/integrations/mssql.ini.tmpl
@@ -102,3 +102,6 @@ INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.h
 
 [lfs]
 PATH = integrations/gitea-integration-mssql/data/lfs
+
+[packages]
+ENABLED = true
diff --git a/integrations/mysql.ini.tmpl b/integrations/mysql.ini.tmpl
index c1780110a3..6a0fd4ab86 100644
--- a/integrations/mysql.ini.tmpl
+++ b/integrations/mysql.ini.tmpl
@@ -118,3 +118,6 @@ DISABLE_GIT_HOOKS = false
 INSTALL_LOCK   = true
 SECRET_KEY     = 9pCviYTWSb
 INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ
+
+[packages]
+ENABLED = true
diff --git a/integrations/mysql8.ini.tmpl b/integrations/mysql8.ini.tmpl
index 0d898ac13d..16f8851fe5 100644
--- a/integrations/mysql8.ini.tmpl
+++ b/integrations/mysql8.ini.tmpl
@@ -99,3 +99,6 @@ INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.h
 
 [lfs]
 PATH = integrations/gitea-integration-mysql8/data/lfs
+
+[packages]
+ENABLED = true
diff --git a/integrations/pgsql.ini.tmpl b/integrations/pgsql.ini.tmpl
index 1c4d843712..5c4fbcc829 100644
--- a/integrations/pgsql.ini.tmpl
+++ b/integrations/pgsql.ini.tmpl
@@ -103,3 +103,6 @@ INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.h
 
 [lfs]
 PATH = integrations/gitea-integration-pgsql/data/lfs
+
+[packages]
+ENABLED = true
diff --git a/integrations/sqlite.ini.tmpl b/integrations/sqlite.ini.tmpl
index e2d999666a..c180148891 100644
--- a/integrations/sqlite.ini.tmpl
+++ b/integrations/sqlite.ini.tmpl
@@ -101,3 +101,6 @@ JWT_SECRET = KZb_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko
 
 [lfs]
 PATH = integrations/gitea-integration-sqlite/data/lfs
+
+[packages]
+ENABLED = true
diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go
index 4c87f017b3..0f6b41933d 100644
--- a/models/repo/repo_unit.go
+++ b/models/repo/repo_unit.go
@@ -181,7 +181,7 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
 			r.Config = new(PullRequestsConfig)
 		case unit.TypeIssues:
 			r.Config = new(IssuesConfig)
-		case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects:
+		case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects, unit.TypePackages:
 			fallthrough
 		default:
 			r.Config = new(UnitConfig)
diff --git a/models/unit/unit.go b/models/unit/unit.go
index a6a47eb1f3..9d3cae5f25 100644
--- a/models/unit/unit.go
+++ b/models/unit/unit.go
@@ -27,6 +27,7 @@ const (
 	TypeExternalWiki                // 6 ExternalWiki
 	TypeExternalTracker             // 7 ExternalTracker
 	TypeProjects                    // 8 Kanban board
+	TypePackages                    // 9 Packages
 )
 
 // Value returns integer value for unit type
@@ -52,6 +53,8 @@ func (u Type) String() string {
 		return "TypeExternalTracker"
 	case TypeProjects:
 		return "TypeProjects"
+	case TypePackages:
+		return "TypePackages"
 	}
 	return fmt.Sprintf("Unknown Type %d", u)
 }
@@ -74,6 +77,7 @@ var (
 		TypeExternalWiki,
 		TypeExternalTracker,
 		TypeProjects,
+		TypePackages,
 	}
 
 	// DefaultRepoUnits contains the default unit types
@@ -84,6 +88,7 @@ var (
 		TypeReleases,
 		TypeWiki,
 		TypeProjects,
+		TypePackages,
 	}
 
 	// NotAllowedDefaultRepoUnits contains units that can't be default
@@ -275,6 +280,15 @@ var (
 		perm.AccessModeOwner,
 	}
 
+	UnitPackages = Unit{
+		TypePackages,
+		"repo.packages",
+		"/packages",
+		"repo.packages.desc",
+		6,
+		perm.AccessModeRead,
+	}
+
 	// Units contains all the units
 	Units = map[Type]Unit{
 		TypeCode:            UnitCode,
@@ -285,6 +299,7 @@ var (
 		TypeWiki:            UnitWiki,
 		TypeExternalWiki:    UnitExternalWiki,
 		TypeProjects:        UnitProjects,
+		TypePackages:        UnitPackages,
 	}
 )
 
diff --git a/modules/context/repo.go b/modules/context/repo.go
index 4a1e9aa9e8..3dc8e51392 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -1019,6 +1019,7 @@ func UnitTypes() func(ctx *Context) {
 		ctx.Data["UnitTypeExternalWiki"] = unit_model.TypeExternalWiki
 		ctx.Data["UnitTypeExternalTracker"] = unit_model.TypeExternalTracker
 		ctx.Data["UnitTypeProjects"] = unit_model.TypeProjects
+		ctx.Data["UnitTypePackages"] = unit_model.TypePackages
 	}
 }
 
diff --git a/modules/setting/repository.go b/modules/setting/repository.go
index 96b37f6980..f24bc841d6 100644
--- a/modules/setting/repository.go
+++ b/modules/setting/repository.go
@@ -295,6 +295,10 @@ func newRepository() {
 		log.Fatal("Failed to map Repository.PullRequest settings: %v", err)
 	}
 
+	if !Cfg.Section("packages").Key("ENABLED").MustBool(false) {
+		Repository.DisabledRepoUnits = append(Repository.DisabledRepoUnits, "repo.packages")
+	}
+
 	// Handle default trustmodel settings
 	Repository.Signing.DefaultTrustModel = strings.ToLower(strings.TrimSpace(Repository.Signing.DefaultTrustModel))
 	if Repository.Signing.DefaultTrustModel == "default" {
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 8c7269703f..875a557b43 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1011,6 +1011,7 @@ tags = Tags
 issues = Issues
 pulls = Pull Requests
 project_board = Projects
+packages = Packages
 labels = Labels
 org_labels_desc = Organization level labels that can be used with <strong>all repositories</strong> under this organization
 org_labels_desc_manage = manage
diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go
index 09cb001772..ccb603833c 100644
--- a/routers/web/repo/setting.go
+++ b/routers/web/repo/setting.go
@@ -456,6 +456,15 @@ func SettingsPost(ctx *context.Context) {
 			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
 		}
 
+		if form.EnablePackages && !unit_model.TypeProjects.UnitGlobalDisabled() {
+			units = append(units, repo_model.RepoUnit{
+				RepoID: repo.ID,
+				Type:   unit_model.TypePackages,
+			})
+		} else if !unit_model.TypePackages.UnitGlobalDisabled() {
+			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
+		}
+
 		if form.EnablePulls && !unit_model.TypePullRequests.UnitGlobalDisabled() {
 			units = append(units, repo_model.RepoUnit{
 				RepoID: repo.ID,
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index 6c61ed00ec..18cbac751c 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -143,6 +143,7 @@ type RepoSettingForm struct {
 	TrackerIssueStyle                     string
 	EnableCloseIssuesViaCommitInAnyBranch bool
 	EnableProjects                        bool
+	EnablePackages                        bool
 	EnablePulls                           bool
 	PullsIgnoreWhitespace                 bool
 	PullsAllowMerge                       bool
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index 624eb17a5e..57c665986a 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -178,9 +178,11 @@
 					</a>
 				{{end}}
 
-				<a href="{{.RepoLink}}/packages" class="{{ if .IsPackagesPage }}active{{end}} item">
-					{{svg "octicon-package"}} {{.i18n.Tr "packages.title"}}
-				</a>
+				{{if .Permission.CanRead $.UnitTypePackages}}
+					<a href="{{.RepoLink}}/packages" class="{{ if .IsPackagesPage }}active{{end}} item">
+						{{svg "octicon-package"}} {{.i18n.Tr "packages.title"}}
+					</a>
+				{{end}}
 
 				{{ if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects)}}
 					<a href="{{.RepoLink}}/projects" class="{{ if .IsProjectsPage }}active{{end}} item">
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 7695345010..77897f25cf 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -390,6 +390,19 @@
 					</div>
 				</div>
 
+				{{$isPackagesEnabled := .Repository.UnitEnabled $.UnitTypePackages}}
+				<div class="inline field">
+					<label>{{.i18n.Tr "repo.packages"}}</label>
+					{{if .UnitTypePackages.UnitGlobalDisabled}}
+					<div class="ui checkbox tooltip disabled" data-content="{{.i18n.Tr "repo.unit_disabled"}}">
+					{{else}}
+					<div class="ui checkbox">
+					{{end}}
+						<input class="enable-system" name="enable_packages" type="checkbox" {{if $isPackagesEnabled}}checked{{end}}>
+						<label>{{.i18n.Tr "repo.settings.packages_desc"}}</label>
+					</div>
+				</div>
+
 				{{if not .IsMirror}}
 					<div class="ui divider"></div>
 					{{$pullRequestEnabled := .Repository.UnitEnabled $.UnitTypePullRequests}}