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

10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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/git-shell"
  9. "github.com/gogits/gogs/models"
  10. "github.com/gogits/gogs/modules/auth"
  11. "github.com/gogits/gogs/modules/base"
  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. ctx.Handle(500, "UpdateMirror", err)
  98. return
  99. }
  100. }
  101. if err := ctx.Repo.Mirror.SaveAddress(form.MirrorAddress); err != nil {
  102. ctx.Handle(500, "SaveAddress", err)
  103. return
  104. }
  105. }
  106. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  107. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  108. case "advanced":
  109. repo.EnableWiki = form.EnableWiki
  110. repo.EnableIssues = form.EnableIssues
  111. repo.EnableExternalTracker = form.EnableExternalTracker
  112. repo.ExternalTrackerFormat = form.TrackerURLFormat
  113. repo.EnablePulls = form.EnablePulls
  114. if err := models.UpdateRepository(repo, false); err != nil {
  115. ctx.Handle(500, "UpdateRepository", err)
  116. return
  117. }
  118. log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  119. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  120. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  121. case "transfer":
  122. if repo.Name != form.RepoName {
  123. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil)
  124. return
  125. }
  126. if ctx.Repo.Owner.IsOrganization() {
  127. if !ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
  128. ctx.Error(404)
  129. return
  130. }
  131. }
  132. newOwner := ctx.Query("new_owner_name")
  133. isExist, err := models.IsUserExist(0, newOwner)
  134. if err != nil {
  135. ctx.Handle(500, "IsUserExist", err)
  136. return
  137. } else if !isExist {
  138. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), SETTINGS_OPTIONS, nil)
  139. return
  140. }
  141. if err = models.TransferOwnership(ctx.User, newOwner, repo); err != nil {
  142. if models.IsErrRepoAlreadyExist(err) {
  143. ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), SETTINGS_OPTIONS, nil)
  144. } else {
  145. ctx.Handle(500, "TransferOwnership", err)
  146. }
  147. return
  148. }
  149. log.Trace("Repository transfered: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
  150. ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed"))
  151. ctx.Redirect(setting.AppSubUrl + "/" + newOwner + "/" + repo.Name)
  152. case "delete":
  153. if repo.Name != form.RepoName {
  154. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil)
  155. return
  156. }
  157. if ctx.Repo.Owner.IsOrganization() {
  158. if !ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
  159. ctx.Error(404)
  160. return
  161. }
  162. }
  163. if err := models.DeleteRepository(ctx.Repo.Owner.Id, repo.ID); err != nil {
  164. ctx.Handle(500, "DeleteRepository", err)
  165. return
  166. }
  167. log.Trace("Repository deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  168. ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success"))
  169. ctx.Redirect(ctx.Repo.Owner.DashboardLink())
  170. }
  171. }
  172. func Collaboration(ctx *middleware.Context) {
  173. ctx.Data["Title"] = ctx.Tr("repo.settings")
  174. ctx.Data["PageIsSettingsCollaboration"] = true
  175. if ctx.Req.Method == "POST" {
  176. name := strings.ToLower(ctx.Query("collaborator"))
  177. if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
  178. ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
  179. return
  180. }
  181. u, err := models.GetUserByName(name)
  182. if err != nil {
  183. if models.IsErrUserNotExist(err) {
  184. ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
  185. ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
  186. } else {
  187. ctx.Handle(500, "GetUserByName", err)
  188. }
  189. return
  190. }
  191. // Check if user is organization member.
  192. if ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgMember(u.Id) {
  193. ctx.Flash.Info(ctx.Tr("repo.settings.user_is_org_member"))
  194. ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
  195. return
  196. }
  197. if err = ctx.Repo.Repository.AddCollaborator(u); err != nil {
  198. ctx.Handle(500, "AddCollaborator", err)
  199. return
  200. }
  201. if setting.Service.EnableNotifyMail {
  202. if err = mailer.SendCollaboratorMail(ctx.Render, u, ctx.User, ctx.Repo.Repository); err != nil {
  203. ctx.Handle(500, "SendCollaboratorMail", err)
  204. return
  205. }
  206. }
  207. ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success"))
  208. ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
  209. return
  210. }
  211. // Delete collaborator.
  212. remove := strings.ToLower(ctx.Query("remove"))
  213. if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName {
  214. u, err := models.GetUserByName(remove)
  215. if err != nil {
  216. ctx.Handle(500, "GetUserByName", err)
  217. return
  218. }
  219. if err := ctx.Repo.Repository.DeleteCollaborator(u); err != nil {
  220. ctx.Handle(500, "DeleteCollaborator", err)
  221. return
  222. }
  223. ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success"))
  224. ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
  225. return
  226. }
  227. users, err := ctx.Repo.Repository.GetCollaborators()
  228. if err != nil {
  229. ctx.Handle(500, "GetCollaborators", err)
  230. return
  231. }
  232. ctx.Data["Collaborators"] = users
  233. ctx.HTML(200, COLLABORATION)
  234. }
  235. func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) {
  236. owner, err := models.GetUserByName(ctx.Params(":username"))
  237. if err != nil {
  238. if models.IsErrUserNotExist(err) {
  239. ctx.Handle(404, "GetUserByName", err)
  240. } else {
  241. ctx.Handle(500, "GetUserByName", err)
  242. }
  243. return nil, nil
  244. }
  245. repo, err := models.GetRepositoryByName(owner.Id, ctx.Params(":reponame"))
  246. if err != nil {
  247. if models.IsErrRepoNotExist(err) {
  248. ctx.Handle(404, "GetRepositoryByName", err)
  249. } else {
  250. ctx.Handle(500, "GetRepositoryByName", err)
  251. }
  252. return nil, nil
  253. }
  254. return owner, repo
  255. }
  256. func GitHooks(ctx *middleware.Context) {
  257. ctx.Data["Title"] = ctx.Tr("repo.settings.githooks")
  258. ctx.Data["PageIsSettingsGitHooks"] = true
  259. hooks, err := ctx.Repo.GitRepo.Hooks()
  260. if err != nil {
  261. ctx.Handle(500, "Hooks", err)
  262. return
  263. }
  264. ctx.Data["Hooks"] = hooks
  265. ctx.HTML(200, GITHOOKS)
  266. }
  267. func GitHooksEdit(ctx *middleware.Context) {
  268. ctx.Data["Title"] = ctx.Tr("repo.settings.githooks")
  269. ctx.Data["PageIsSettingsGitHooks"] = true
  270. name := ctx.Params(":name")
  271. hook, err := ctx.Repo.GitRepo.GetHook(name)
  272. if err != nil {
  273. if err == git.ErrNotValidHook {
  274. ctx.Handle(404, "GetHook", err)
  275. } else {
  276. ctx.Handle(500, "GetHook", err)
  277. }
  278. return
  279. }
  280. ctx.Data["Hook"] = hook
  281. ctx.HTML(200, GITHOOK_EDIT)
  282. }
  283. func GitHooksEditPost(ctx *middleware.Context) {
  284. name := ctx.Params(":name")
  285. hook, err := ctx.Repo.GitRepo.GetHook(name)
  286. if err != nil {
  287. if err == git.ErrNotValidHook {
  288. ctx.Handle(404, "GetHook", err)
  289. } else {
  290. ctx.Handle(500, "GetHook", err)
  291. }
  292. return
  293. }
  294. hook.Content = ctx.Query("content")
  295. if err = hook.Update(); err != nil {
  296. ctx.Handle(500, "hook.Update", err)
  297. return
  298. }
  299. ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks/git")
  300. }
  301. func DeployKeys(ctx *middleware.Context) {
  302. ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys")
  303. ctx.Data["PageIsSettingsKeys"] = true
  304. keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID)
  305. if err != nil {
  306. ctx.Handle(500, "ListDeployKeys", err)
  307. return
  308. }
  309. ctx.Data["Deploykeys"] = keys
  310. ctx.HTML(200, DEPLOY_KEYS)
  311. }
  312. func DeployKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) {
  313. ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys")
  314. ctx.Data["PageIsSettingsKeys"] = true
  315. keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID)
  316. if err != nil {
  317. ctx.Handle(500, "ListDeployKeys", err)
  318. return
  319. }
  320. ctx.Data["Deploykeys"] = keys
  321. if ctx.HasError() {
  322. ctx.HTML(200, DEPLOY_KEYS)
  323. return
  324. }
  325. content, err := models.CheckPublicKeyString(form.Content)
  326. if err != nil {
  327. if models.IsErrKeyUnableVerify(err) {
  328. ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
  329. } else {
  330. ctx.Data["HasError"] = true
  331. ctx.Data["Err_Content"] = true
  332. ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error()))
  333. ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
  334. return
  335. }
  336. }
  337. key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content)
  338. if err != nil {
  339. ctx.Data["HasError"] = true
  340. switch {
  341. case models.IsErrKeyAlreadyExist(err):
  342. ctx.Data["Err_Content"] = true
  343. ctx.RenderWithErr(ctx.Tr("repo.settings.key_been_used"), DEPLOY_KEYS, &form)
  344. case models.IsErrKeyNameAlreadyUsed(err):
  345. ctx.Data["Err_Title"] = true
  346. ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), DEPLOY_KEYS, &form)
  347. default:
  348. ctx.Handle(500, "AddDeployKey", err)
  349. }
  350. return
  351. }
  352. log.Trace("Deploy key added: %d", ctx.Repo.Repository.ID)
  353. ctx.Flash.Success(ctx.Tr("repo.settings.add_key_success", key.Name))
  354. ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
  355. }
  356. func DeleteDeployKey(ctx *middleware.Context) {
  357. if err := models.DeleteDeployKey(ctx.User, ctx.QueryInt64("id")); err != nil {
  358. ctx.Flash.Error("DeleteDeployKey: " + err.Error())
  359. } else {
  360. ctx.Flash.Success(ctx.Tr("repo.settings.deploy_key_deletion_success"))
  361. }
  362. ctx.JSON(200, map[string]interface{}{
  363. "redirect": ctx.Repo.RepoLink + "/settings/keys",
  364. })
  365. }