2014-08-14 14:12:21 +08:00
|
|
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
2019-02-18 17:00:27 +01:00
|
|
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
2022-11-27 13:20:29 -05:00
|
|
|
// SPDX-License-Identifier: MIT
|
2014-08-14 14:12:21 +08:00
|
|
|
|
|
|
|
|
package org
|
|
|
|
|
|
|
|
|
|
import (
|
2026-03-25 08:23:11 +01:00
|
|
|
"errors"
|
2021-04-05 17:30:52 +02:00
|
|
|
"net/http"
|
2021-11-16 18:18:25 +00:00
|
|
|
"net/url"
|
2015-09-17 17:21:27 -04:00
|
|
|
|
2021-09-24 19:32:56 +08:00
|
|
|
"code.gitea.io/gitea/models/db"
|
2024-12-20 10:05:29 -08:00
|
|
|
packages_model "code.gitea.io/gitea/models/packages"
|
2021-12-12 23:48:20 +08:00
|
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
2021-11-24 17:49:20 +08:00
|
|
|
user_model "code.gitea.io/gitea/models/user"
|
2021-11-10 13:13:16 +08:00
|
|
|
"code.gitea.io/gitea/models/webhook"
|
2016-11-10 17:24:48 +01:00
|
|
|
"code.gitea.io/gitea/modules/log"
|
2024-02-04 14:29:09 +01:00
|
|
|
"code.gitea.io/gitea/modules/optional"
|
2022-03-29 15:23:45 +08:00
|
|
|
repo_module "code.gitea.io/gitea/modules/repository"
|
2016-11-10 17:24:48 +01:00
|
|
|
"code.gitea.io/gitea/modules/setting"
|
2025-08-20 20:57:42 -07:00
|
|
|
"code.gitea.io/gitea/modules/structs"
|
2024-12-22 23:33:19 +08:00
|
|
|
"code.gitea.io/gitea/modules/templates"
|
2025-06-22 02:21:48 +08:00
|
|
|
"code.gitea.io/gitea/modules/util"
|
2021-01-26 23:36:53 +08:00
|
|
|
"code.gitea.io/gitea/modules/web"
|
2023-09-06 10:49:36 +02:00
|
|
|
shared_user "code.gitea.io/gitea/routers/web/shared/user"
|
2021-11-22 23:21:55 +08:00
|
|
|
user_setting "code.gitea.io/gitea/routers/web/user/setting"
|
2024-02-27 15:12:22 +08:00
|
|
|
"code.gitea.io/gitea/services/context"
|
2021-04-06 20:44:05 +01:00
|
|
|
"code.gitea.io/gitea/services/forms"
|
2023-05-21 23:13:47 +08:00
|
|
|
org_service "code.gitea.io/gitea/services/org"
|
2021-11-22 23:21:55 +08:00
|
|
|
user_service "code.gitea.io/gitea/services/user"
|
2014-08-14 14:12:21 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
2016-11-18 11:03:03 +08:00
|
|
|
// tplSettingsOptions template path for render settings
|
2024-12-22 23:33:19 +08:00
|
|
|
tplSettingsOptions templates.TplName = "org/settings/options"
|
2016-11-18 11:03:03 +08:00
|
|
|
// tplSettingsHooks template path for render hook settings
|
2024-12-22 23:33:19 +08:00
|
|
|
tplSettingsHooks templates.TplName = "org/settings/hooks"
|
2020-04-01 00:14:46 -04:00
|
|
|
// tplSettingsLabels template path for render labels settings
|
2024-12-22 23:33:19 +08:00
|
|
|
tplSettingsLabels templates.TplName = "org/settings/labels"
|
2014-08-14 14:12:21 +08:00
|
|
|
)
|
|
|
|
|
|
2016-11-18 11:03:03 +08:00
|
|
|
// Settings render the main settings page
|
2016-03-11 11:56:52 -05:00
|
|
|
func Settings(ctx *context.Context) {
|
2014-08-14 14:12:21 +08:00
|
|
|
ctx.Data["Title"] = ctx.Tr("org.settings")
|
2021-11-25 23:15:58 +08:00
|
|
|
ctx.Data["PageIsOrgSettings"] = true
|
2014-08-14 14:12:21 +08:00
|
|
|
ctx.Data["PageIsSettingsOptions"] = true
|
2019-07-23 20:50:39 +02:00
|
|
|
ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility
|
2019-09-30 01:20:27 +02:00
|
|
|
ctx.Data["RepoAdminChangeTeamAccess"] = ctx.Org.Organization.RepoAdminChangeTeamAccess
|
2023-09-06 10:49:36 +02:00
|
|
|
ctx.Data["ContextUser"] = ctx.ContextUser
|
|
|
|
|
|
2025-04-11 03:12:55 +08:00
|
|
|
if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
|
|
|
|
|
ctx.ServerError("RenderUserOrgHeader", err)
|
2023-09-06 10:49:36 +02:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-05 17:30:52 +02:00
|
|
|
ctx.HTML(http.StatusOK, tplSettingsOptions)
|
2014-08-14 14:12:21 +08:00
|
|
|
}
|
|
|
|
|
|
2021-07-08 07:38:13 -04:00
|
|
|
// SettingsPost response for settings change submitted
|
2021-01-26 23:36:53 +08:00
|
|
|
func SettingsPost(ctx *context.Context) {
|
2021-04-06 20:44:05 +01:00
|
|
|
form := web.GetForm(ctx).(*forms.UpdateOrgSettingForm)
|
2014-08-14 14:12:21 +08:00
|
|
|
ctx.Data["Title"] = ctx.Tr("org.settings")
|
2021-11-25 23:15:58 +08:00
|
|
|
ctx.Data["PageIsOrgSettings"] = true
|
2014-08-14 14:12:21 +08:00
|
|
|
ctx.Data["PageIsSettingsOptions"] = true
|
2019-07-23 20:50:39 +02:00
|
|
|
ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility
|
2014-08-14 14:12:21 +08:00
|
|
|
|
|
|
|
|
if ctx.HasError() {
|
2021-04-05 17:30:52 +02:00
|
|
|
ctx.HTML(http.StatusOK, tplSettingsOptions)
|
2014-08-14 14:12:21 +08:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
org := ctx.Org.Organization
|
2026-03-25 08:23:11 +01:00
|
|
|
if err := org_service.UpdateOrgEmailAddress(ctx, org, form.Email); err != nil {
|
|
|
|
|
if errors.Is(err, util.ErrInvalidArgument) {
|
2024-02-04 14:29:09 +01:00
|
|
|
ctx.Data["Err_Email"] = true
|
2026-02-27 20:38:44 +08:00
|
|
|
ctx.RenderWithErrDeprecated(ctx.Tr("form.email_invalid"), tplSettingsOptions, &form)
|
2024-02-04 14:29:09 +01:00
|
|
|
return
|
|
|
|
|
}
|
2026-03-25 08:23:11 +01:00
|
|
|
ctx.ServerError("UpdateOrgEmailAddress", err)
|
|
|
|
|
return
|
2024-02-04 14:29:09 +01:00
|
|
|
}
|
2014-08-14 14:12:21 +08:00
|
|
|
|
2024-02-04 14:29:09 +01:00
|
|
|
opts := &user_service.UpdateOptions{
|
2026-03-25 08:23:11 +01:00
|
|
|
FullName: optional.FromPtr(form.FullName),
|
|
|
|
|
Description: optional.FromPtr(form.Description),
|
|
|
|
|
Website: optional.FromPtr(form.Website),
|
|
|
|
|
Location: optional.FromPtr(form.Location),
|
|
|
|
|
RepoAdminChangeTeamAccess: optional.FromPtr(form.RepoAdminChangeTeamAccess),
|
2024-02-04 14:29:09 +01:00
|
|
|
}
|
2022-03-22 08:03:22 +01:00
|
|
|
if ctx.Doer.IsAdmin {
|
2026-03-25 08:23:11 +01:00
|
|
|
opts.MaxRepoCreation = optional.FromPtr(form.MaxRepoCreation)
|
2015-12-11 19:24:57 -05:00
|
|
|
}
|
|
|
|
|
|
2024-02-04 14:29:09 +01:00
|
|
|
if err := user_service.UpdateUser(ctx, org.AsUser(), opts); err != nil {
|
2018-01-10 22:34:17 +01:00
|
|
|
ctx.ServerError("UpdateUser", err)
|
2014-08-14 14:12:21 +08:00
|
|
|
return
|
|
|
|
|
}
|
2020-06-07 02:45:12 +02:00
|
|
|
|
2014-08-14 14:12:21 +08:00
|
|
|
log.Trace("Organization setting updated: %s", org.Name)
|
|
|
|
|
ctx.Flash.Success(ctx.Tr("org.settings.update_setting_success"))
|
2015-11-24 19:28:24 -05:00
|
|
|
ctx.Redirect(ctx.Org.OrgLink + "/settings")
|
2014-08-14 14:12:21 +08:00
|
|
|
}
|
|
|
|
|
|
2016-11-18 11:03:03 +08:00
|
|
|
// SettingsAvatar response for change avatar on settings page
|
2021-01-26 23:36:53 +08:00
|
|
|
func SettingsAvatar(ctx *context.Context) {
|
2021-04-06 20:44:05 +01:00
|
|
|
form := web.GetForm(ctx).(*forms.AvatarForm)
|
|
|
|
|
form.Source = forms.AvatarLocal
|
2021-11-22 23:21:55 +08:00
|
|
|
if err := user_setting.UpdateAvatarSetting(ctx, form, ctx.Org.Organization.AsUser()); err != nil {
|
2015-09-06 17:12:02 -04:00
|
|
|
ctx.Flash.Error(err.Error())
|
|
|
|
|
} else {
|
|
|
|
|
ctx.Flash.Success(ctx.Tr("org.settings.update_avatar_success"))
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-06 17:36:30 +01:00
|
|
|
ctx.Redirect(ctx.Org.OrgLink + "/settings")
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 07:38:13 -04:00
|
|
|
// SettingsDeleteAvatar response for delete avatar on settings page
|
2016-03-11 11:56:52 -05:00
|
|
|
func SettingsDeleteAvatar(ctx *context.Context) {
|
2023-10-11 06:24:07 +02:00
|
|
|
if err := user_service.DeleteAvatar(ctx, ctx.Org.Organization.AsUser()); err != nil {
|
2016-03-06 17:36:30 +01:00
|
|
|
ctx.Flash.Error(err.Error())
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-23 17:36:57 +08:00
|
|
|
ctx.JSONRedirect(ctx.Org.OrgLink + "/settings")
|
2015-09-06 17:12:02 -04:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 02:21:48 +08:00
|
|
|
// SettingsDeleteOrgPost response for deleting an organization
|
|
|
|
|
func SettingsDeleteOrgPost(ctx *context.Context) {
|
|
|
|
|
if ctx.Org.Organization.Name != ctx.FormString("org_name") {
|
|
|
|
|
ctx.JSONError(ctx.Tr("form.enterred_invalid_org_name"))
|
2014-08-14 14:12:21 +08:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-22 02:21:48 +08:00
|
|
|
if err := org_service.DeleteOrganization(ctx, ctx.Org.Organization, false /* no purge */); err != nil {
|
|
|
|
|
if repo_model.IsErrUserOwnRepos(err) {
|
|
|
|
|
ctx.JSONError(ctx.Tr("form.org_still_own_repo"))
|
|
|
|
|
} else if packages_model.IsErrUserOwnPackages(err) {
|
|
|
|
|
ctx.JSONError(ctx.Tr("form.org_still_own_packages"))
|
|
|
|
|
} else {
|
|
|
|
|
log.Error("DeleteOrganization: %v", err)
|
|
|
|
|
ctx.JSONError(util.Iif(ctx.Doer.IsAdmin, err.Error(), string(ctx.Tr("org.settings.delete_failed"))))
|
|
|
|
|
}
|
2023-09-06 10:49:36 +02:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-22 02:21:48 +08:00
|
|
|
ctx.Flash.Success(ctx.Tr("org.settings.delete_successful", ctx.Org.Organization.Name))
|
|
|
|
|
ctx.JSONRedirect(setting.AppSubURL + "/")
|
2014-08-14 14:12:21 +08:00
|
|
|
}
|
2014-09-04 07:17:00 -04:00
|
|
|
|
2016-11-18 11:03:03 +08:00
|
|
|
// Webhooks render webhook list page
|
2016-03-11 11:56:52 -05:00
|
|
|
func Webhooks(ctx *context.Context) {
|
2014-09-04 07:17:00 -04:00
|
|
|
ctx.Data["Title"] = ctx.Tr("org.settings")
|
2021-11-25 23:15:58 +08:00
|
|
|
ctx.Data["PageIsOrgSettings"] = true
|
2014-09-04 07:17:00 -04:00
|
|
|
ctx.Data["PageIsSettingsHooks"] = true
|
2019-03-18 22:33:20 -04:00
|
|
|
ctx.Data["BaseLink"] = ctx.Org.OrgLink + "/settings/hooks"
|
2021-01-15 01:24:03 +02:00
|
|
|
ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks"
|
2015-08-26 21:45:51 +08:00
|
|
|
ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc")
|
2014-09-04 07:17:00 -04:00
|
|
|
|
2023-11-24 11:49:41 +08:00
|
|
|
ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{OwnerID: ctx.Org.Organization.ID})
|
2014-09-04 07:17:00 -04:00
|
|
|
if err != nil {
|
2023-03-10 15:28:32 +01:00
|
|
|
ctx.ServerError("ListWebhooksByOpts", err)
|
2014-09-04 07:17:00 -04:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-11 03:12:55 +08:00
|
|
|
if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
|
|
|
|
|
ctx.ServerError("RenderUserOrgHeader", err)
|
2023-09-06 10:49:36 +02:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-04 07:17:00 -04:00
|
|
|
ctx.Data["Webhooks"] = ws
|
2021-04-05 17:30:52 +02:00
|
|
|
ctx.HTML(http.StatusOK, tplSettingsHooks)
|
2014-09-04 07:17:00 -04:00
|
|
|
}
|
2015-08-26 21:45:51 +08:00
|
|
|
|
2016-11-18 11:03:03 +08:00
|
|
|
// DeleteWebhook response for delete webhook
|
2016-03-11 11:56:52 -05:00
|
|
|
func DeleteWebhook(ctx *context.Context) {
|
2023-10-14 10:37:24 +02:00
|
|
|
if err := webhook.DeleteWebhookByOwnerID(ctx, ctx.Org.Organization.ID, ctx.FormInt64("id")); err != nil {
|
2023-03-10 15:28:32 +01:00
|
|
|
ctx.Flash.Error("DeleteWebhookByOwnerID: " + err.Error())
|
2015-08-26 21:45:51 +08:00
|
|
|
} else {
|
|
|
|
|
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-26 14:04:01 +08:00
|
|
|
ctx.JSONRedirect(ctx.Org.OrgLink + "/settings/hooks")
|
2015-08-26 21:45:51 +08:00
|
|
|
}
|
2020-04-01 00:14:46 -04:00
|
|
|
|
|
|
|
|
// Labels render organization labels page
|
|
|
|
|
func Labels(ctx *context.Context) {
|
|
|
|
|
ctx.Data["Title"] = ctx.Tr("repo.labels")
|
2021-11-25 23:15:58 +08:00
|
|
|
ctx.Data["PageIsOrgSettings"] = true
|
2020-04-01 00:14:46 -04:00
|
|
|
ctx.Data["PageIsOrgSettingsLabels"] = true
|
2023-04-10 16:44:02 +08:00
|
|
|
ctx.Data["LabelTemplateFiles"] = repo_module.LabelTemplateFiles
|
2023-09-06 10:49:36 +02:00
|
|
|
|
2025-04-11 03:12:55 +08:00
|
|
|
if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
|
|
|
|
|
ctx.ServerError("RenderUserOrgHeader", err)
|
2023-09-06 10:49:36 +02:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-05 17:30:52 +02:00
|
|
|
ctx.HTML(http.StatusOK, tplSettingsLabels)
|
2020-04-01 00:14:46 -04:00
|
|
|
}
|
2025-06-22 02:21:48 +08:00
|
|
|
|
|
|
|
|
// SettingsRenamePost response for renaming organization
|
|
|
|
|
func SettingsRenamePost(ctx *context.Context) {
|
|
|
|
|
form := web.GetForm(ctx).(*forms.RenameOrgForm)
|
|
|
|
|
if ctx.HasError() {
|
|
|
|
|
ctx.JSONError(ctx.GetErrMsg())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oldOrgName, newOrgName := ctx.Org.Organization.Name, form.NewOrgName
|
|
|
|
|
|
|
|
|
|
if form.OrgName != oldOrgName {
|
|
|
|
|
ctx.JSONError(ctx.Tr("form.enterred_invalid_org_name"))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if newOrgName == oldOrgName {
|
|
|
|
|
ctx.JSONError(ctx.Tr("org.settings.rename_no_change"))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-23 13:59:55 -07:00
|
|
|
if err := user_service.RenameUser(ctx, ctx.Org.Organization.AsUser(), newOrgName, ctx.Doer); err != nil {
|
2025-06-22 02:21:48 +08:00
|
|
|
if user_model.IsErrUserAlreadyExist(err) {
|
|
|
|
|
ctx.JSONError(ctx.Tr("org.form.name_been_taken", newOrgName))
|
|
|
|
|
} else if db.IsErrNameReserved(err) {
|
|
|
|
|
ctx.JSONError(ctx.Tr("org.form.name_reserved", newOrgName))
|
|
|
|
|
} else if db.IsErrNamePatternNotAllowed(err) {
|
|
|
|
|
ctx.JSONError(ctx.Tr("org.form.name_pattern_not_allowed", newOrgName))
|
|
|
|
|
} else {
|
|
|
|
|
log.Error("RenameOrganization: %v", err)
|
|
|
|
|
ctx.JSONError(util.Iif(ctx.Doer.IsAdmin, err.Error(), string(ctx.Tr("org.settings.rename_failed"))))
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.Flash.Success(ctx.Tr("org.settings.rename_success", oldOrgName, newOrgName))
|
|
|
|
|
ctx.JSONRedirect(setting.AppSubURL + "/org/" + url.PathEscape(newOrgName) + "/settings")
|
|
|
|
|
}
|
2025-08-20 20:57:42 -07:00
|
|
|
|
|
|
|
|
// SettingsChangeVisibilityPost response for change organization visibility
|
|
|
|
|
func SettingsChangeVisibilityPost(ctx *context.Context) {
|
|
|
|
|
visibility, ok := structs.VisibilityModes[ctx.FormString("visibility")]
|
|
|
|
|
if !ok {
|
|
|
|
|
ctx.Flash.Error(ctx.Tr("invalid_data", visibility))
|
|
|
|
|
ctx.JSONRedirect(setting.AppSubURL + "/org/" + url.PathEscape(ctx.Org.Organization.Name) + "/settings")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ctx.Org.Organization.Visibility == visibility {
|
|
|
|
|
ctx.Flash.Info(ctx.Tr("nothing_has_been_changed"))
|
|
|
|
|
ctx.JSONRedirect(setting.AppSubURL + "/org/" + url.PathEscape(ctx.Org.Organization.Name) + "/settings")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := org_service.ChangeOrganizationVisibility(ctx, ctx.Org.Organization, visibility); err != nil {
|
|
|
|
|
log.Error("ChangeOrganizationVisibility: %v", err)
|
|
|
|
|
ctx.JSONError(ctx.Tr("error.occurred"))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.Flash.Success(ctx.Tr("org.settings.change_visibility_success", ctx.Org.Organization.Name))
|
|
|
|
|
ctx.JSONRedirect(setting.AppSubURL + "/org/" + url.PathEscape(ctx.Org.Organization.Name) + "/settings")
|
|
|
|
|
}
|