You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

setting.go 10.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package org
  5. import (
  6. "net/http"
  7. "net/url"
  8. "strings"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/models/db"
  11. repo_model "code.gitea.io/gitea/models/repo"
  12. secret_model "code.gitea.io/gitea/models/secret"
  13. user_model "code.gitea.io/gitea/models/user"
  14. "code.gitea.io/gitea/models/webhook"
  15. "code.gitea.io/gitea/modules/base"
  16. "code.gitea.io/gitea/modules/context"
  17. "code.gitea.io/gitea/modules/log"
  18. repo_module "code.gitea.io/gitea/modules/repository"
  19. "code.gitea.io/gitea/modules/setting"
  20. "code.gitea.io/gitea/modules/web"
  21. user_setting "code.gitea.io/gitea/routers/web/user/setting"
  22. "code.gitea.io/gitea/services/forms"
  23. "code.gitea.io/gitea/services/org"
  24. container_service "code.gitea.io/gitea/services/packages/container"
  25. repo_service "code.gitea.io/gitea/services/repository"
  26. user_service "code.gitea.io/gitea/services/user"
  27. )
  28. const (
  29. // tplSettingsOptions template path for render settings
  30. tplSettingsOptions base.TplName = "org/settings/options"
  31. // tplSettingsDelete template path for render delete repository
  32. tplSettingsDelete base.TplName = "org/settings/delete"
  33. // tplSettingsHooks template path for render hook settings
  34. tplSettingsHooks base.TplName = "org/settings/hooks"
  35. // tplSettingsLabels template path for render labels settings
  36. tplSettingsLabels base.TplName = "org/settings/labels"
  37. // tplSettingsSecrets template path for render secrets settings
  38. tplSettingsSecrets base.TplName = "org/settings/secrets"
  39. // tplSettingsRunners template path for render runners settings
  40. tplSettingsRunners base.TplName = "org/settings/runners"
  41. // tplSettingsRunnersEdit template path for render runners edit settings
  42. tplSettingsRunnersEdit base.TplName = "org/settings/runners_edit"
  43. )
  44. // Settings render the main settings page
  45. func Settings(ctx *context.Context) {
  46. ctx.Data["Title"] = ctx.Tr("org.settings")
  47. ctx.Data["PageIsOrgSettings"] = true
  48. ctx.Data["PageIsSettingsOptions"] = true
  49. ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility
  50. ctx.Data["RepoAdminChangeTeamAccess"] = ctx.Org.Organization.RepoAdminChangeTeamAccess
  51. ctx.HTML(http.StatusOK, tplSettingsOptions)
  52. }
  53. // SettingsPost response for settings change submitted
  54. func SettingsPost(ctx *context.Context) {
  55. form := web.GetForm(ctx).(*forms.UpdateOrgSettingForm)
  56. ctx.Data["Title"] = ctx.Tr("org.settings")
  57. ctx.Data["PageIsOrgSettings"] = true
  58. ctx.Data["PageIsSettingsOptions"] = true
  59. ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility
  60. if ctx.HasError() {
  61. ctx.HTML(http.StatusOK, tplSettingsOptions)
  62. return
  63. }
  64. org := ctx.Org.Organization
  65. nameChanged := org.Name != form.Name
  66. // Check if organization name has been changed.
  67. if org.LowerName != strings.ToLower(form.Name) {
  68. isExist, err := user_model.IsUserExist(ctx, org.ID, form.Name)
  69. if err != nil {
  70. ctx.ServerError("IsUserExist", err)
  71. return
  72. } else if isExist {
  73. ctx.Data["OrgName"] = true
  74. ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplSettingsOptions, &form)
  75. return
  76. } else if err = user_model.ChangeUserName(org.AsUser(), form.Name); err != nil {
  77. switch {
  78. case db.IsErrNameReserved(err):
  79. ctx.Data["OrgName"] = true
  80. ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form)
  81. case db.IsErrNamePatternNotAllowed(err):
  82. ctx.Data["OrgName"] = true
  83. ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form)
  84. default:
  85. ctx.ServerError("ChangeUserName", err)
  86. }
  87. return
  88. }
  89. if err := container_service.UpdateRepositoryNames(ctx, org.AsUser(), form.Name); err != nil {
  90. ctx.ServerError("UpdateRepositoryNames", err)
  91. return
  92. }
  93. // reset ctx.org.OrgLink with new name
  94. ctx.Org.OrgLink = setting.AppSubURL + "/org/" + url.PathEscape(form.Name)
  95. log.Trace("Organization name changed: %s -> %s", org.Name, form.Name)
  96. nameChanged = false
  97. }
  98. // In case it's just a case change.
  99. org.Name = form.Name
  100. org.LowerName = strings.ToLower(form.Name)
  101. if ctx.Doer.IsAdmin {
  102. org.MaxRepoCreation = form.MaxRepoCreation
  103. }
  104. org.FullName = form.FullName
  105. org.Description = form.Description
  106. org.Website = form.Website
  107. org.Location = form.Location
  108. org.RepoAdminChangeTeamAccess = form.RepoAdminChangeTeamAccess
  109. visibilityChanged := form.Visibility != org.Visibility
  110. org.Visibility = form.Visibility
  111. if err := user_model.UpdateUser(ctx, org.AsUser(), false); err != nil {
  112. ctx.ServerError("UpdateUser", err)
  113. return
  114. }
  115. // update forks visibility
  116. if visibilityChanged {
  117. repos, _, err := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
  118. Actor: org.AsUser(), Private: true, ListOptions: db.ListOptions{Page: 1, PageSize: org.NumRepos},
  119. })
  120. if err != nil {
  121. ctx.ServerError("GetRepositories", err)
  122. return
  123. }
  124. for _, repo := range repos {
  125. repo.OwnerName = org.Name
  126. if err := repo_service.UpdateRepository(repo, true); err != nil {
  127. ctx.ServerError("UpdateRepository", err)
  128. return
  129. }
  130. }
  131. } else if nameChanged {
  132. if err := repo_model.UpdateRepositoryOwnerNames(org.ID, org.Name); err != nil {
  133. ctx.ServerError("UpdateRepository", err)
  134. return
  135. }
  136. }
  137. log.Trace("Organization setting updated: %s", org.Name)
  138. ctx.Flash.Success(ctx.Tr("org.settings.update_setting_success"))
  139. ctx.Redirect(ctx.Org.OrgLink + "/settings")
  140. }
  141. // SettingsAvatar response for change avatar on settings page
  142. func SettingsAvatar(ctx *context.Context) {
  143. form := web.GetForm(ctx).(*forms.AvatarForm)
  144. form.Source = forms.AvatarLocal
  145. if err := user_setting.UpdateAvatarSetting(ctx, form, ctx.Org.Organization.AsUser()); err != nil {
  146. ctx.Flash.Error(err.Error())
  147. } else {
  148. ctx.Flash.Success(ctx.Tr("org.settings.update_avatar_success"))
  149. }
  150. ctx.Redirect(ctx.Org.OrgLink + "/settings")
  151. }
  152. // SettingsDeleteAvatar response for delete avatar on settings page
  153. func SettingsDeleteAvatar(ctx *context.Context) {
  154. if err := user_service.DeleteAvatar(ctx.Org.Organization.AsUser()); err != nil {
  155. ctx.Flash.Error(err.Error())
  156. }
  157. ctx.Redirect(ctx.Org.OrgLink + "/settings")
  158. }
  159. // SettingsDelete response for deleting an organization
  160. func SettingsDelete(ctx *context.Context) {
  161. ctx.Data["Title"] = ctx.Tr("org.settings")
  162. ctx.Data["PageIsOrgSettings"] = true
  163. ctx.Data["PageIsSettingsDelete"] = true
  164. if ctx.Req.Method == "POST" {
  165. if ctx.Org.Organization.Name != ctx.FormString("org_name") {
  166. ctx.Data["Err_OrgName"] = true
  167. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_org_name"), tplSettingsDelete, nil)
  168. return
  169. }
  170. if err := org.DeleteOrganization(ctx.Org.Organization); err != nil {
  171. if models.IsErrUserOwnRepos(err) {
  172. ctx.Flash.Error(ctx.Tr("form.org_still_own_repo"))
  173. ctx.Redirect(ctx.Org.OrgLink + "/settings/delete")
  174. } else if models.IsErrUserOwnPackages(err) {
  175. ctx.Flash.Error(ctx.Tr("form.org_still_own_packages"))
  176. ctx.Redirect(ctx.Org.OrgLink + "/settings/delete")
  177. } else {
  178. ctx.ServerError("DeleteOrganization", err)
  179. }
  180. } else {
  181. log.Trace("Organization deleted: %s", ctx.Org.Organization.Name)
  182. ctx.Redirect(setting.AppSubURL + "/")
  183. }
  184. return
  185. }
  186. ctx.HTML(http.StatusOK, tplSettingsDelete)
  187. }
  188. // Webhooks render webhook list page
  189. func Webhooks(ctx *context.Context) {
  190. ctx.Data["Title"] = ctx.Tr("org.settings")
  191. ctx.Data["PageIsOrgSettings"] = true
  192. ctx.Data["PageIsSettingsHooks"] = true
  193. ctx.Data["BaseLink"] = ctx.Org.OrgLink + "/settings/hooks"
  194. ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks"
  195. ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc")
  196. ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{OrgID: ctx.Org.Organization.ID})
  197. if err != nil {
  198. ctx.ServerError("GetWebhooksByOrgId", err)
  199. return
  200. }
  201. ctx.Data["Webhooks"] = ws
  202. ctx.HTML(http.StatusOK, tplSettingsHooks)
  203. }
  204. // DeleteWebhook response for delete webhook
  205. func DeleteWebhook(ctx *context.Context) {
  206. if err := webhook.DeleteWebhookByOrgID(ctx.Org.Organization.ID, ctx.FormInt64("id")); err != nil {
  207. ctx.Flash.Error("DeleteWebhookByOrgID: " + err.Error())
  208. } else {
  209. ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
  210. }
  211. ctx.JSON(http.StatusOK, map[string]interface{}{
  212. "redirect": ctx.Org.OrgLink + "/settings/hooks",
  213. })
  214. }
  215. // Labels render organization labels page
  216. func Labels(ctx *context.Context) {
  217. ctx.Data["Title"] = ctx.Tr("repo.labels")
  218. ctx.Data["PageIsOrgSettings"] = true
  219. ctx.Data["PageIsOrgSettingsLabels"] = true
  220. ctx.Data["RequireTribute"] = true
  221. ctx.Data["LabelTemplates"] = repo_module.LabelTemplates
  222. ctx.HTML(http.StatusOK, tplSettingsLabels)
  223. }
  224. // Secrets render organization secrets page
  225. func Secrets(ctx *context.Context) {
  226. ctx.Data["Title"] = ctx.Tr("repo.secrets")
  227. ctx.Data["PageIsOrgSettings"] = true
  228. ctx.Data["PageIsOrgSettingsSecrets"] = true
  229. secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: ctx.Org.Organization.ID})
  230. if err != nil {
  231. ctx.ServerError("FindSecrets", err)
  232. return
  233. }
  234. ctx.Data["Secrets"] = secrets
  235. ctx.HTML(http.StatusOK, tplSettingsSecrets)
  236. }
  237. // SecretsPost add secrets
  238. func SecretsPost(ctx *context.Context) {
  239. form := web.GetForm(ctx).(*forms.AddSecretForm)
  240. _, err := secret_model.InsertEncryptedSecret(ctx, ctx.Org.Organization.ID, 0, form.Title, form.Content)
  241. if err != nil {
  242. ctx.Flash.Error(ctx.Tr("secrets.creation.failed"))
  243. log.Error("validate secret: %v", err)
  244. ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets")
  245. return
  246. }
  247. log.Trace("Org %d: secret added", ctx.Org.Organization.ID)
  248. ctx.Flash.Success(ctx.Tr("secrets.creation.success", form.Title))
  249. ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets")
  250. }
  251. // SecretsDelete delete secrets
  252. func SecretsDelete(ctx *context.Context) {
  253. id := ctx.FormInt64("id")
  254. if _, err := db.DeleteByBean(ctx, &secret_model.Secret{ID: id}); err != nil {
  255. ctx.Flash.Error(ctx.Tr("secrets.deletion.failed"))
  256. log.Error("delete secret %d: %v", id, err)
  257. } else {
  258. ctx.Flash.Success(ctx.Tr("secrets.deletion.success"))
  259. }
  260. ctx.JSON(http.StatusOK, map[string]interface{}{
  261. "redirect": ctx.Org.OrgLink + "/settings/secrets",
  262. })
  263. }