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

setting.go 12 kB

11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repo
  5. import (
  6. "strings"
  7. "time"
  8. "github.com/gogits/gogs/models"
  9. "github.com/gogits/gogs/modules/auth"
  10. "github.com/gogits/gogs/modules/base"
  11. "github.com/gogits/gogs/modules/git"
  12. "github.com/gogits/gogs/modules/log"
  13. "github.com/gogits/gogs/modules/mailer"
  14. "github.com/gogits/gogs/modules/middleware"
  15. "github.com/gogits/gogs/modules/setting"
  16. )
  17. const (
  18. SETTINGS_OPTIONS base.TplName = "repo/settings/options"
  19. COLLABORATION base.TplName = "repo/settings/collaboration"
  20. GITHOOKS base.TplName = "repo/settings/githooks"
  21. GITHOOK_EDIT base.TplName = "repo/settings/githook_edit"
  22. DEPLOY_KEYS base.TplName = "repo/settings/deploy_keys"
  23. )
  24. func Settings(ctx *middleware.Context) {
  25. ctx.Data["Title"] = ctx.Tr("repo.settings")
  26. ctx.Data["PageIsSettingsOptions"] = true
  27. ctx.HTML(200, SETTINGS_OPTIONS)
  28. }
  29. func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
  30. ctx.Data["Title"] = ctx.Tr("repo.settings")
  31. ctx.Data["PageIsSettingsOptions"] = true
  32. repo := ctx.Repo.Repository
  33. switch ctx.Query("action") {
  34. case "update":
  35. if ctx.HasError() {
  36. ctx.HTML(200, SETTINGS_OPTIONS)
  37. return
  38. }
  39. isNameChanged := false
  40. oldRepoName := repo.Name
  41. newRepoName := form.RepoName
  42. // Check if repository name has been changed.
  43. if repo.LowerName != strings.ToLower(newRepoName) {
  44. isNameChanged = true
  45. if err := models.ChangeRepositoryName(ctx.Repo.Owner, repo.Name, newRepoName); err != nil {
  46. ctx.Data["Err_RepoName"] = true
  47. switch {
  48. case models.IsErrRepoAlreadyExist(err):
  49. ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), SETTINGS_OPTIONS, &form)
  50. case models.IsErrNameReserved(err):
  51. ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), SETTINGS_OPTIONS, &form)
  52. case models.IsErrNamePatternNotAllowed(err):
  53. ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), SETTINGS_OPTIONS, &form)
  54. default:
  55. ctx.Handle(500, "ChangeRepositoryName", err)
  56. }
  57. return
  58. }
  59. log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
  60. }
  61. // In case it's just a case change.
  62. repo.Name = newRepoName
  63. repo.LowerName = strings.ToLower(newRepoName)
  64. if ctx.Repo.GitRepo.IsBranchExist(form.Branch) &&
  65. repo.DefaultBranch != form.Branch {
  66. repo.DefaultBranch = form.Branch
  67. if err := ctx.Repo.GitRepo.SetDefaultBranch(form.Branch); err != nil {
  68. if !git.IsErrUnsupportedVersion(err) {
  69. ctx.Handle(500, "SetDefaultBranch", err)
  70. return
  71. }
  72. }
  73. }
  74. repo.Description = form.Description
  75. repo.Website = form.Website
  76. // Visibility of forked repository is forced sync with base repository.
  77. if repo.IsFork {
  78. form.Private = repo.BaseRepo.IsPrivate
  79. }
  80. visibilityChanged := repo.IsPrivate != form.Private
  81. repo.IsPrivate = form.Private
  82. if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
  83. ctx.Handle(500, "UpdateRepository", err)
  84. return
  85. }
  86. log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  87. if isNameChanged {
  88. if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil {
  89. log.Error(4, "RenameRepoAction: %v", err)
  90. }
  91. }
  92. if repo.IsMirror {
  93. if form.Interval > 0 {
  94. ctx.Repo.Mirror.Interval = form.Interval
  95. ctx.Repo.Mirror.NextUpdate = time.Now().Add(time.Duration(form.Interval) * time.Hour)
  96. if err := models.UpdateMirror(ctx.Repo.Mirror); err != nil {
  97. log.Error(4, "UpdateMirror: %v", err)
  98. }
  99. }
  100. }
  101. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  102. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  103. case "advanced":
  104. repo.EnableWiki = form.EnableWiki
  105. repo.EnableIssues = form.EnableIssues
  106. repo.EnableExternalTracker = form.EnableExternalTracker
  107. repo.ExternalTrackerFormat = form.TrackerURLFormat
  108. repo.EnablePulls = form.EnablePulls
  109. if err := models.UpdateRepository(repo, false); err != nil {
  110. ctx.Handle(500, "UpdateRepository", err)
  111. return
  112. }
  113. log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  114. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  115. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  116. case "transfer":
  117. if repo.Name != form.RepoName {
  118. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil)
  119. return
  120. }
  121. if ctx.Repo.Owner.IsOrganization() {
  122. if !ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
  123. ctx.Error(404)
  124. return
  125. }
  126. }
  127. newOwner := ctx.Query("new_owner_name")
  128. isExist, err := models.IsUserExist(0, newOwner)
  129. if err != nil {
  130. ctx.Handle(500, "IsUserExist", err)
  131. return
  132. } else if !isExist {
  133. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), SETTINGS_OPTIONS, nil)
  134. return
  135. }
  136. if err = models.TransferOwnership(ctx.User, newOwner, repo); err != nil {
  137. if models.IsErrRepoAlreadyExist(err) {
  138. ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), SETTINGS_OPTIONS, nil)
  139. } else {
  140. ctx.Handle(500, "TransferOwnership", err)
  141. }
  142. return
  143. }
  144. log.Trace("Repository transfered: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
  145. ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed"))
  146. ctx.Redirect(setting.AppSubUrl + "/" + newOwner + "/" + repo.Name)
  147. case "delete":
  148. if repo.Name != form.RepoName {
  149. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil)
  150. return
  151. }
  152. if ctx.Repo.Owner.IsOrganization() {
  153. if !ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
  154. ctx.Error(404)
  155. return
  156. }
  157. }
  158. if err := models.DeleteRepository(ctx.Repo.Owner.Id, repo.ID); err != nil {
  159. ctx.Handle(500, "DeleteRepository", err)
  160. return
  161. }
  162. log.Trace("Repository deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  163. ctx.Redirect(ctx.Repo.Owner.DashboardLink())
  164. }
  165. }
  166. func Collaboration(ctx *middleware.Context) {
  167. ctx.Data["Title"] = ctx.Tr("repo.settings")
  168. ctx.Data["PageIsSettingsCollaboration"] = true
  169. if ctx.Req.Method == "POST" {
  170. name := strings.ToLower(ctx.Query("collaborator"))
  171. if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
  172. ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
  173. return
  174. }
  175. u, err := models.GetUserByName(name)
  176. if err != nil {
  177. if models.IsErrUserNotExist(err) {
  178. ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
  179. ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
  180. } else {
  181. ctx.Handle(500, "GetUserByName", err)
  182. }
  183. return
  184. }
  185. // Check if user is organization member.
  186. if ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgMember(u.Id) {
  187. ctx.Flash.Info(ctx.Tr("repo.settings.user_is_org_member"))
  188. ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
  189. return
  190. }
  191. if err = ctx.Repo.Repository.AddCollaborator(u); err != nil {
  192. ctx.Handle(500, "AddCollaborator", err)
  193. return
  194. }
  195. if setting.Service.EnableNotifyMail {
  196. if err = mailer.SendCollaboratorMail(ctx.Render, u, ctx.User, ctx.Repo.Repository); err != nil {
  197. ctx.Handle(500, "SendCollaboratorMail", err)
  198. return
  199. }
  200. }
  201. ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success"))
  202. ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
  203. return
  204. }
  205. // Delete collaborator.
  206. remove := strings.ToLower(ctx.Query("remove"))
  207. if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName {
  208. u, err := models.GetUserByName(remove)
  209. if err != nil {
  210. ctx.Handle(500, "GetUserByName", err)
  211. return
  212. }
  213. if err := ctx.Repo.Repository.DeleteCollaborator(u); err != nil {
  214. ctx.Handle(500, "DeleteCollaborator", err)
  215. return
  216. }
  217. ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success"))
  218. ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
  219. return
  220. }
  221. users, err := ctx.Repo.Repository.GetCollaborators()
  222. if err != nil {
  223. ctx.Handle(500, "GetCollaborators", err)
  224. return
  225. }
  226. ctx.Data["Collaborators"] = users
  227. ctx.HTML(200, COLLABORATION)
  228. }
  229. func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) {
  230. owner, err := models.GetUserByName(ctx.Params(":username"))
  231. if err != nil {
  232. if models.IsErrUserNotExist(err) {
  233. ctx.Handle(404, "GetUserByName", err)
  234. } else {
  235. ctx.Handle(500, "GetUserByName", err)
  236. }
  237. return nil, nil
  238. }
  239. repo, err := models.GetRepositoryByName(owner.Id, ctx.Params(":reponame"))
  240. if err != nil {
  241. if models.IsErrRepoNotExist(err) {
  242. ctx.Handle(404, "GetRepositoryByName", err)
  243. } else {
  244. ctx.Handle(500, "GetRepositoryByName", err)
  245. }
  246. return nil, nil
  247. }
  248. return owner, repo
  249. }
  250. func GitHooks(ctx *middleware.Context) {
  251. ctx.Data["Title"] = ctx.Tr("repo.settings.githooks")
  252. ctx.Data["PageIsSettingsGitHooks"] = true
  253. hooks, err := ctx.Repo.GitRepo.Hooks()
  254. if err != nil {
  255. ctx.Handle(500, "Hooks", err)
  256. return
  257. }
  258. ctx.Data["Hooks"] = hooks
  259. ctx.HTML(200, GITHOOKS)
  260. }
  261. func GitHooksEdit(ctx *middleware.Context) {
  262. ctx.Data["Title"] = ctx.Tr("repo.settings.githooks")
  263. ctx.Data["PageIsSettingsGitHooks"] = true
  264. name := ctx.Params(":name")
  265. hook, err := ctx.Repo.GitRepo.GetHook(name)
  266. if err != nil {
  267. if err == git.ErrNotValidHook {
  268. ctx.Handle(404, "GetHook", err)
  269. } else {
  270. ctx.Handle(500, "GetHook", err)
  271. }
  272. return
  273. }
  274. ctx.Data["Hook"] = hook
  275. ctx.HTML(200, GITHOOK_EDIT)
  276. }
  277. func GitHooksEditPost(ctx *middleware.Context) {
  278. name := ctx.Params(":name")
  279. hook, err := ctx.Repo.GitRepo.GetHook(name)
  280. if err != nil {
  281. if err == git.ErrNotValidHook {
  282. ctx.Handle(404, "GetHook", err)
  283. } else {
  284. ctx.Handle(500, "GetHook", err)
  285. }
  286. return
  287. }
  288. hook.Content = ctx.Query("content")
  289. if err = hook.Update(); err != nil {
  290. ctx.Handle(500, "hook.Update", err)
  291. return
  292. }
  293. ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks/git")
  294. }
  295. func DeployKeys(ctx *middleware.Context) {
  296. ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys")
  297. ctx.Data["PageIsSettingsKeys"] = true
  298. keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID)
  299. if err != nil {
  300. ctx.Handle(500, "ListDeployKeys", err)
  301. return
  302. }
  303. ctx.Data["Deploykeys"] = keys
  304. ctx.HTML(200, DEPLOY_KEYS)
  305. }
  306. func DeployKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) {
  307. ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys")
  308. ctx.Data["PageIsSettingsKeys"] = true
  309. keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID)
  310. if err != nil {
  311. ctx.Handle(500, "ListDeployKeys", err)
  312. return
  313. }
  314. ctx.Data["Deploykeys"] = keys
  315. if ctx.HasError() {
  316. ctx.HTML(200, DEPLOY_KEYS)
  317. return
  318. }
  319. content, err := models.CheckPublicKeyString(form.Content)
  320. if err != nil {
  321. if models.IsErrKeyUnableVerify(err) {
  322. ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
  323. } else {
  324. ctx.Data["HasError"] = true
  325. ctx.Data["Err_Content"] = true
  326. ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error()))
  327. ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
  328. return
  329. }
  330. }
  331. key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content)
  332. if err != nil {
  333. ctx.Data["HasError"] = true
  334. switch {
  335. case models.IsErrKeyAlreadyExist(err):
  336. ctx.Data["Err_Content"] = true
  337. ctx.RenderWithErr(ctx.Tr("repo.settings.key_been_used"), DEPLOY_KEYS, &form)
  338. case models.IsErrKeyNameAlreadyUsed(err):
  339. ctx.Data["Err_Title"] = true
  340. ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), DEPLOY_KEYS, &form)
  341. default:
  342. ctx.Handle(500, "AddDeployKey", err)
  343. }
  344. return
  345. }
  346. log.Trace("Deploy key added: %d", ctx.Repo.Repository.ID)
  347. ctx.Flash.Success(ctx.Tr("repo.settings.add_key_success", key.Name))
  348. ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
  349. }
  350. func DeleteDeployKey(ctx *middleware.Context) {
  351. if err := models.DeleteDeployKey(ctx.User, ctx.QueryInt64("id")); err != nil {
  352. ctx.Flash.Error("DeleteDeployKey: " + err.Error())
  353. } else {
  354. ctx.Flash.Success(ctx.Tr("repo.settings.deploy_key_deletion_success"))
  355. }
  356. ctx.JSON(200, map[string]interface{}{
  357. "redirect": ctx.Repo.RepoLink + "/settings/keys",
  358. })
  359. }