From 033717350588d95d860b7bf6978fc8e7260a4171 Mon Sep 17 00:00:00 2001 From: junoberryferry Date: Mon, 18 Aug 2025 14:54:40 -0400 Subject: [PATCH] Archive entire organizations. --- models/organization/org.go | 64 ++++++++++++++++ models/repo/repo.go | 15 ++++ modules/structs/org.go | 2 + options/locale/locale_en-US.ini | 12 +++ routers/api/v1/org/org.go | 19 +++++ routers/api/v1/repo/repo.go | 5 ++ routers/web/org/setting.go | 49 ++++++++++++ routers/web/repo/fork.go | 8 ++ routers/web/repo/githttp.go | 6 +- routers/web/repo/release.go | 4 +- routers/web/repo/repo.go | 10 ++- routers/web/repo/setting/setting.go | 4 +- routers/web/repo/wiki.go | 6 +- routers/web/shared/user/header.go | 3 + routers/web/web.go | 2 + services/context/repo.go | 10 ++- services/convert/convert.go | 1 + services/convert/repository.go | 18 ++++- services/forms/org.go | 1 + templates/org/header.tmpl | 1 + templates/org/home.tmpl | 2 +- .../org/settings/options_dangerzone.tmpl | 76 +++++++++++++++++++ templates/repo/issue/view_title.tmpl | 2 +- templates/repo/projects/view.tmpl | 2 + 24 files changed, 307 insertions(+), 15 deletions(-) diff --git a/models/organization/org.go b/models/organization/org.go index 5eba004d69c2c..63f327a81f639 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -7,7 +7,9 @@ package organization import ( "context" "fmt" + "strconv" "strings" + "time" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" @@ -592,3 +594,65 @@ func getUserTeamIDsQueryBuilder(orgID, userID int64) *builder.Builder { "team_user.uid": userID, }) } + +// ErrOrgIsArchived represents a "OrgIsArchived" error +type ErrOrgIsArchived struct { + OrgName string +} + +func (err ErrOrgIsArchived) Error() string { + return fmt.Sprintf("organization is archived [name: %s]", err.OrgName) +} + +func (err ErrOrgIsArchived) Unwrap() error { + return util.ErrPermissionDenied +} + +// IsArchived returns true if organization is archived +func (org *Organization) IsArchived(ctx context.Context) bool { + archived, _ := user_model.GetSetting(ctx, org.ID, "org_archived") + return archived == "true" +} + +// SetArchived sets the archived status of the organization +func (org *Organization) SetArchived(ctx context.Context, archived bool) error { + archivedStr := "false" + if archived { + archivedStr = "true" + } + + if err := user_model.SetUserSetting(ctx, org.ID, "org_archived", archivedStr); err != nil { + return err + } + + if archived { + // Set the archived date + return user_model.SetUserSetting(ctx, org.ID, "org_archived_date", strconv.FormatInt(time.Now().Unix(), 10)) + } else { + // Clear the archived date when unarchiving + return user_model.SetUserSetting(ctx, org.ID, "org_archived_date", "") + } +} + +// GetArchivedDate returns the date when the organization was archived, or zero time if not archived +func (org *Organization) GetArchivedDate(ctx context.Context) (time.Time, error) { + dateStr, err := user_model.GetSetting(ctx, org.ID, "org_archived_date") + if err != nil || dateStr == "" { + return time.Time{}, err + } + + timestamp, err := strconv.ParseInt(dateStr, 10, 64) + if err != nil { + return time.Time{}, err + } + + return time.Unix(timestamp, 0), nil +} + +// MustNotBeArchived returns ErrOrgIsArchived if the organization is archived +func (org *Organization) MustNotBeArchived(ctx context.Context) error { + if org.IsArchived(ctx) { + return ErrOrgIsArchived{OrgName: org.Name} + } + return nil +} diff --git a/models/repo/repo.go b/models/repo/repo.go index 2403b3b40bafe..c5268e7d13962 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -312,6 +312,21 @@ func (repo *Repository) IsBroken() bool { return repo.Status == RepositoryBroken } +// IsEffectivelyArchived indicates that repository is archived either directly or through its parent organization +func (repo *Repository) IsEffectivelyArchived(ctx context.Context) bool { + if repo.IsArchived { + return true + } + + // Check if parent organization is archived (if repository belongs to an organization) + if repo.Owner != nil && repo.Owner.IsOrganization() { + archived, _ := user_model.GetSetting(ctx, repo.Owner.ID, "org_archived") + return archived == "true" + } + + return false +} + // MarkAsBrokenEmpty marks the repo as broken and empty // FIXME: the status "broken" and "is_empty" were abused, // The code always set them together, no way to distinguish whether a repo is really "empty" or "broken" diff --git a/modules/structs/org.go b/modules/structs/org.go index 33b45c6344e95..82272f7fcf4dc 100644 --- a/modules/structs/org.go +++ b/modules/structs/org.go @@ -15,6 +15,7 @@ type Organization struct { Location string `json:"location"` Visibility string `json:"visibility"` RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"` + Archived bool `json:"archived"` // username of the organization // deprecated UserName string `json:"username"` @@ -58,6 +59,7 @@ type EditOrgOption struct { // enum: public,limited,private Visibility string `json:"visibility" binding:"In(,public,limited,private)"` RepoAdminChangeTeamAccess *bool `json:"repo_admin_change_team_access"` + Archived *bool `json:"archived"` } // RenameOrgOption options when renaming an organization diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index d7e73a0cfbb08..4ac4bec0306ce 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2874,6 +2874,18 @@ settings.confirm_delete_account = Confirm Deletion settings.delete_failed = Deleting organization failed due to an internal error settings.delete_successful = Organization %s has been deleted successfully. settings.hooks_desc = Add webhooks which will be triggered for all repositories under this organization. +settings.archive = Archive +settings.archive_this_org = Archive this organization +settings.archive_this_org_desc = Archiving will make this organization read-only. No repositories can be created, and existing repositories cannot be pushed to. +settings.archive_not_allowed = Only organization owners can archive organizations. +settings.archive_org_header = Archive This Organization +settings.archive_org_button = Archive Organization +settings.unarchive_org_header = Unarchive This Organization +settings.unarchive_org_button = Unarchive Organization + +archived_create_repo_not_allowed = Cannot create repositories in an archived organization. +settings.archive_success = Organization has been successfully archived. +settings.unarchive_success = Organization has been successfully unarchived. settings.labels_desc = Add labels which can be used on issues for all repositories under this organization. diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index cd676860658dc..0285b054b9f50 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -386,6 +386,25 @@ func Edit(ctx *context.APIContext) { } } + // Handle archived field - only allow org owners to modify + if form.Archived != nil { + // Check if user is owner of the organization + isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID) + if err != nil { + ctx.APIErrorInternal(err) + return + } + if !isOwner && !ctx.Doer.IsAdmin { + ctx.APIError(http.StatusForbidden, "only organization owners and site admins can archive/unarchive organizations") + return + } + + if err := ctx.Org.Organization.SetArchived(ctx, *form.Archived); err != nil { + ctx.APIErrorInternal(err) + return + } + } + opts := &user_service.UpdateOptions{ FullName: optional.Some(form.FullName), Description: optional.Some(form.Description), diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index e69b7729a0fb0..569b53837539a 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -510,6 +510,11 @@ func CreateOrgRepo(ctx *context.APIContext) { return } + if err := org.MustNotBeArchived(ctx); err != nil { + ctx.APIError(http.StatusForbidden, err) + return + } + if !ctx.Doer.IsAdmin { canCreate, err := org.CanCreateOrgRepo(ctx, ctx.Doer.ID) if err != nil { diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 2bc1e8bc43388..48aba711c2988 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -45,6 +45,7 @@ func Settings(ctx *context.Context) { ctx.Data["PageIsSettingsOptions"] = true ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility ctx.Data["RepoAdminChangeTeamAccess"] = ctx.Org.Organization.RepoAdminChangeTeamAccess + ctx.Data["IsArchived"] = ctx.Org.Organization.IsArchived(ctx) ctx.Data["ContextUser"] = ctx.ContextUser if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil { @@ -165,6 +166,54 @@ func SettingsDeleteOrgPost(ctx *context.Context) { ctx.JSONRedirect(setting.AppSubURL + "/") } +// SettingsArchive archives an organization +func SettingsArchive(ctx *context.Context) { + if !ctx.Org.IsOwner { + ctx.JSONError(ctx.Tr("org.settings.archive_not_allowed")) + return + } + + org := ctx.Org.Organization + orgName := ctx.FormString("org_name") + + if orgName != org.Name { + ctx.JSONError(ctx.Tr("form.enterred_invalid_org_name")) + return + } + + if err := org.SetArchived(ctx, true); err != nil { + ctx.ServerError("SetArchived", err) + return + } + + ctx.Flash.Success(ctx.Tr("org.settings.archive_success")) + ctx.JSONRedirect(ctx.Org.OrgLink + "/settings") +} + +// SettingsUnarchive unarchives an organization +func SettingsUnarchive(ctx *context.Context) { + if !ctx.Org.IsOwner { + ctx.JSONError(ctx.Tr("org.settings.archive_not_allowed")) + return + } + + org := ctx.Org.Organization + orgName := ctx.FormString("org_name") + + if orgName != org.Name { + ctx.JSONError(ctx.Tr("form.enterred_invalid_org_name")) + return + } + + if err := org.SetArchived(ctx, false); err != nil { + ctx.ServerError("SetArchived", err) + return + } + + ctx.Flash.Success(ctx.Tr("org.settings.unarchive_success")) + ctx.JSONRedirect(ctx.Org.OrgLink + "/settings") +} + // Webhooks render webhook list page func Webhooks(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("org.settings") diff --git a/routers/web/repo/fork.go b/routers/web/repo/fork.go index c2694e540f7d0..392a5f14dc8fc 100644 --- a/routers/web/repo/fork.go +++ b/routers/web/repo/fork.go @@ -143,6 +143,14 @@ func ForkPost(ctx *context.Context) { return } + if ctxUser.IsOrganization() { + org := organization.OrgFromUser(ctxUser) + if err := org.MustNotBeArchived(ctx); err != nil { + ctx.JSONError(ctx.Tr("org.archived_create_repo_not_allowed")) + return + } + } + forkRepo := getForkRepository(ctx) if ctx.Written() { return diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index deb3ae4f3a6f8..9bd737b4da382 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -118,9 +118,9 @@ func httpBase(ctx *context.Context) *serviceHandler { repoExist = false } - // Don't allow pushing if the repo is archived - if repoExist && repo.IsArchived && !isPull { - ctx.PlainText(http.StatusForbidden, "This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.") + // Don't allow pushing if the repo or its organization is archived + if repoExist && repo.IsEffectivelyArchived(ctx) && !isPull { + ctx.PlainText(http.StatusForbidden, "This repository is archived. You can view files and clone it, but cannot push or open issues/pull-requests.") return nil } diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 36ea20c23e6f7..ad8c4f0726624 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -165,7 +165,7 @@ func Releases(ctx *context.Context) { } writeAccess := ctx.Repo.CanWrite(unit.TypeReleases) - ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived + ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsEffectivelyArchived(ctx) releases, err := getReleaseInfos(ctx, &repo_model.FindReleasesOptions{ ListOptions: listOptions, @@ -274,7 +274,7 @@ func SingleRelease(ctx *context.Context) { ctx.Data["PageIsReleaseList"] = true writeAccess := ctx.Repo.CanWrite(unit.TypeReleases) - ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived + ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsEffectivelyArchived(ctx) releases, err := getReleaseInfos(ctx, &repo_model.FindReleasesOptions{ ListOptions: db.ListOptions{Page: 1, PageSize: 1}, diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 1b700aa6da0a4..6a1a615a15d5c 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -89,7 +89,7 @@ func checkContextUser(ctx *context.Context, uid int64) *user_model.User { var orgsAvailable []*organization.Organization for i := range orgs { - if ctx.Doer.CanCreateRepoIn(orgs[i].AsUser()) { + if ctx.Doer.CanCreateRepoIn(orgs[i].AsUser()) && !orgs[i].IsArchived(ctx) { orgsAvailable = append(orgsAvailable, orgs[i]) } } @@ -225,6 +225,14 @@ func CreatePost(ctx *context.Context) { } ctx.Data["ContextUser"] = ctxUser + if ctxUser.IsOrganization() { + org := organization.OrgFromUser(ctxUser) + if err := org.MustNotBeArchived(ctx); err != nil { + ctx.RenderWithErr(ctx.Tr("org.archived_create_repo_not_allowed"), tplCreate, form) + return + } + } + if form.RepoTemplate > 0 { templateRepo, err := repo_model.GetRepositoryByID(ctx, form.RepoTemplate) if err == nil && access_model.CheckRepoUnitUser(ctx, templateRepo, ctxUser, unit.TypeCode) { diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 0865d9d7c0f59..71e6cc40cc0c5 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -225,7 +225,7 @@ func handleSettingsPostUpdate(ctx *context.Context) { func handleSettingsPostMirror(ctx *context.Context) { form := web.GetForm(ctx).(*forms.RepoSettingForm) repo := ctx.Repo.Repository - if !setting.Mirror.Enabled || !repo.IsMirror || repo.IsArchived { + if !setting.Mirror.Enabled || !repo.IsMirror || repo.IsEffectivelyArchived(ctx) { ctx.NotFound(nil) return } @@ -321,7 +321,7 @@ func handleSettingsPostMirror(ctx *context.Context) { func handleSettingsPostMirrorSync(ctx *context.Context) { repo := ctx.Repo.Repository - if !setting.Mirror.Enabled || !repo.IsMirror || repo.IsArchived { + if !setting.Mirror.Enabled || !repo.IsMirror || repo.IsEffectivelyArchived(ctx) { ctx.NotFound(nil) return } diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index a35b7b86e12c7..4f92fa885e736 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -449,7 +449,7 @@ func WikiPost(ctx *context.Context) { // Wiki renders single wiki page func Wiki(ctx *context.Context) { - ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived + ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsEffectivelyArchived(ctx) switch ctx.FormString("action") { case "_pages": @@ -508,7 +508,7 @@ func Wiki(ctx *context.Context) { // WikiRevision renders file revision list of wiki page func WikiRevision(ctx *context.Context) { - ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived + ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsEffectivelyArchived(ctx) if !ctx.Repo.Repository.HasWiki() { ctx.Data["Title"] = ctx.Tr("repo.wiki") @@ -546,7 +546,7 @@ func WikiPages(ctx *context.Context) { } ctx.Data["Title"] = ctx.Tr("repo.wiki.pages") - ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived + ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsEffectivelyArchived(ctx) _, commit, err := findWikiRepoCommit(ctx) if err != nil { diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 2bd0abc4c03f7..7ba35fa68f270 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -151,6 +151,9 @@ func RenderUserOrgHeader(ctx *context.Context) (result *PrepareOwnerHeaderResult result = &PrepareOwnerHeaderResult{} if ctx.ContextUser.IsOrganization() { + org := organization.OrgFromUser(ctx.ContextUser) + ctx.Data["IsOrgArchived"] = org.IsArchived(ctx) + result.ProfilePublicRepo, result.ProfilePublicReadmeBlob = FindOwnerProfileReadme(ctx, ctx.Doer) result.ProfilePrivateRepo, result.ProfilePrivateReadmeBlob = FindOwnerProfileReadme(ctx, ctx.Doer, RepoNameProfilePrivate) result.HasOrgProfileReadme = result.ProfilePublicReadmeBlob != nil || result.ProfilePrivateReadmeBlob != nil diff --git a/routers/web/web.go b/routers/web/web.go index 09be0c39045e0..d3cd33d06e93b 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -970,6 +970,8 @@ func registerWebRoutes(m *web.Router) { }, actions.MustEnableActions) m.Post("/rename", web.Bind(forms.RenameOrgForm{}), org.SettingsRenamePost) + m.Post("/archive", org.SettingsArchive) + m.Post("/unarchive", org.SettingsUnarchive) m.Post("/delete", org.SettingsDeleteOrgPost) m.Group("/packages", func() { diff --git a/services/context/repo.go b/services/context/repo.go index afc6de9b1666d..7f8663cdf1a71 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -83,7 +83,7 @@ func (r *Repository) GetObjectFormat() git.ObjectFormat { // RepoMustNotBeArchived checks if a repo is archived func RepoMustNotBeArchived() func(ctx *Context) { return func(ctx *Context) { - if ctx.Repo.Repository.IsArchived { + if ctx.Repo.Repository.IsEffectivelyArchived(ctx) { ctx.NotFound(errors.New(ctx.Locale.TrString("repo.archive.title"))) } } @@ -410,6 +410,14 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) { ctx.Repo.Repository = repo ctx.Data["RepoName"] = ctx.Repo.Repository.Name ctx.Data["IsEmptyRepo"] = ctx.Repo.Repository.IsEmpty + + // Check if repository is effectively archived (either directly or through its organization) + isEffectivelyArchived := repo.IsEffectivelyArchived(ctx) + ctx.Data["IsArchived"] = isEffectivelyArchived + + // Override the repository's IsArchived field to reflect the effective archive status + // This ensures templates using .Repository.IsArchived will get the correct value + ctx.Repo.Repository.IsArchived = isEffectivelyArchived } // RepoAssignment returns a middleware to handle repository assignment diff --git a/services/convert/convert.go b/services/convert/convert.go index 0de38221409bb..38f046c7ae4a3 100644 --- a/services/convert/convert.go +++ b/services/convert/convert.go @@ -646,6 +646,7 @@ func ToOrganization(ctx context.Context, org *organization.Organization) *api.Or Location: org.Location, Visibility: org.Visibility.String(), RepoAdminChangeTeamAccess: org.RepoAdminChangeTeamAccess, + Archived: org.IsArchived(ctx), } } diff --git a/services/convert/repository.go b/services/convert/repository.go index a364591bb8f9b..d331f46c3834e 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -8,6 +8,7 @@ import ( "time" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" @@ -17,6 +18,21 @@ import ( "code.gitea.io/gitea/modules/util" ) +// isEffectivelyArchived checks if a repository is archived either directly or through its parent organization +func isEffectivelyArchived(ctx context.Context, repo *repo_model.Repository) bool { + if repo.IsArchived { + return true + } + + // Check if parent organization is archived (if repository belongs to an organization) + if repo.Owner != nil && repo.Owner.IsOrganization() { + org := organization.OrgFromUser(repo.Owner) + return org.IsArchived(ctx) + } + + return false +} + // ToRepo converts a Repository to api.Repository func ToRepo(ctx context.Context, repo *repo_model.Repository, permissionInRepo access_model.Permission) *api.Repository { return innerToRepo(ctx, repo, permissionInRepo, false) @@ -197,7 +213,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR Private: repo.IsPrivate, Template: repo.IsTemplate, Empty: repo.IsEmpty, - Archived: repo.IsArchived, + Archived: isEffectivelyArchived(ctx, repo), Size: int(repo.Size / 1024), Fork: repo.IsFork, Parent: parent, diff --git a/services/forms/org.go b/services/forms/org.go index 2ac18ef25cc63..bb4472f410bc5 100644 --- a/services/forms/org.go +++ b/services/forms/org.go @@ -44,6 +44,7 @@ type UpdateOrgSettingForm struct { Visibility structs.VisibleType MaxRepoCreation int RepoAdminChangeTeamAccess bool + Archived bool } // Validate validates the fields diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl index 90798b5d7cf58..9c0c8d9a07548 100644 --- a/templates/org/header.tmpl +++ b/templates/org/header.tmpl @@ -6,6 +6,7 @@ {{if .Org.Visibility.IsLimited}}{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}{{end}} {{if .Org.Visibility.IsPrivate}}{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}{{end}} + {{if .IsOrgArchived}}{{ctx.Locale.Tr "archived"}}{{end}} {{if .EnableFeed}} diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index 3cde3554c94e6..0543b64b7317d 100644 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -15,7 +15,7 @@ {{if .ShowMemberAndTeamTab}}
- {{if .CanCreateOrgRepo}} + {{if and .CanCreateOrgRepo (not .IsOrgArchived)}}
{{ctx.Locale.Tr "new_repo"}} {{if not .DisableNewPullMirrors}} diff --git a/templates/org/settings/options_dangerzone.tmpl b/templates/org/settings/options_dangerzone.tmpl index 01cf3fd4051dc..f8922e179e1aa 100644 --- a/templates/org/settings/options_dangerzone.tmpl +++ b/templates/org/settings/options_dangerzone.tmpl @@ -13,6 +13,22 @@
+ {{if .IsOrganizationOwner}} +
+
+
{{ctx.Locale.Tr "org.settings.archive"}}
+
{{ctx.Locale.Tr "org.settings.archive_this_org_desc"}}
+
+
+ {{if .IsArchived}} + + {{else}} + + {{end}} +
+
+ {{end}} +
{{ctx.Locale.Tr "org.settings.delete_account"}}
@@ -91,3 +107,63 @@
+ +{{if .IsOrganizationOwner}} + + + +{{end}} diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl index 103fa5de530a9..37df2220923e1 100644 --- a/templates/repo/issue/view_title.tmpl +++ b/templates/repo/issue/view_title.tmpl @@ -20,7 +20,7 @@ {{if $canEditIssueTitle}} {{end}} - {{if not .Issue.IsPull}} + {{if and (not .Issue.IsPull) (not .Repository.IsArchived)}} {{ctx.Locale.Tr "repo.issues.new"}} {{end}} diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl index 1acaaf8b4bb10..676d0bebc5e43 100644 --- a/templates/repo/projects/view.tmpl +++ b/templates/repo/projects/view.tmpl @@ -5,7 +5,9 @@ {{template "projects/view" .}}