gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package setting
  5. import (
  6. "errors"
  7. "net/http"
  8. "strings"
  9. "time"
  10. "code.gitea.io/gitea/models/db"
  11. "code.gitea.io/gitea/models/organization"
  12. repo_model "code.gitea.io/gitea/models/repo"
  13. unit_model "code.gitea.io/gitea/models/unit"
  14. user_model "code.gitea.io/gitea/models/user"
  15. "code.gitea.io/gitea/modules/git"
  16. "code.gitea.io/gitea/modules/gitrepo"
  17. "code.gitea.io/gitea/modules/indexer/code"
  18. issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
  19. "code.gitea.io/gitea/modules/indexer/stats"
  20. "code.gitea.io/gitea/modules/lfs"
  21. "code.gitea.io/gitea/modules/log"
  22. "code.gitea.io/gitea/modules/setting"
  23. "code.gitea.io/gitea/modules/structs"
  24. "code.gitea.io/gitea/modules/templates"
  25. "code.gitea.io/gitea/modules/util"
  26. "code.gitea.io/gitea/modules/validation"
  27. "code.gitea.io/gitea/modules/web"
  28. actions_service "code.gitea.io/gitea/services/actions"
  29. asymkey_service "code.gitea.io/gitea/services/asymkey"
  30. "code.gitea.io/gitea/services/context"
  31. "code.gitea.io/gitea/services/forms"
  32. "code.gitea.io/gitea/services/migrations"
  33. mirror_service "code.gitea.io/gitea/services/mirror"
  34. repo_service "code.gitea.io/gitea/services/repository"
  35. wiki_service "code.gitea.io/gitea/services/wiki"
  36. "xorm.io/xorm/convert"
  37. )
  38. const (
  39. tplSettingsOptions templates.TplName = "repo/settings/options"
  40. tplCollaboration templates.TplName = "repo/settings/collaboration"
  41. tplBranches templates.TplName = "repo/settings/branches"
  42. tplGithooks templates.TplName = "repo/settings/githooks"
  43. tplGithookEdit templates.TplName = "repo/settings/githook_edit"
  44. tplDeployKeys templates.TplName = "repo/settings/deploy_keys"
  45. )
  46. // SettingsCtxData is a middleware that sets all the general context data for the
  47. // settings template.
  48. func SettingsCtxData(ctx *context.Context) {
  49. ctx.Data["Title"] = ctx.Tr("repo.settings.options")
  50. ctx.Data["PageIsSettingsOptions"] = true
  51. ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate
  52. ctx.Data["MirrorsEnabled"] = setting.Mirror.Enabled
  53. ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull
  54. ctx.Data["DisableNewPushMirrors"] = setting.Mirror.DisableNewPush
  55. ctx.Data["DefaultMirrorInterval"] = setting.Mirror.DefaultInterval
  56. ctx.Data["MinimumMirrorInterval"] = setting.Mirror.MinInterval
  57. ctx.Data["CanConvertFork"] = ctx.Repo.Repository.IsFork && ctx.Doer.CanCreateRepoIn(ctx.Repo.Repository.Owner)
  58. signing, _ := asymkey_service.SigningKey(ctx, ctx.Repo.Repository.RepoPath())
  59. ctx.Data["SigningKeyAvailable"] = signing != nil
  60. ctx.Data["SigningSettings"] = setting.Repository.Signing
  61. ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
  62. if ctx.Doer.IsAdmin {
  63. if setting.Indexer.RepoIndexerEnabled {
  64. status, err := repo_model.GetIndexerStatus(ctx, ctx.Repo.Repository, repo_model.RepoIndexerTypeCode)
  65. if err != nil {
  66. ctx.ServerError("repo.indexer_status", err)
  67. return
  68. }
  69. ctx.Data["CodeIndexerStatus"] = status
  70. }
  71. status, err := repo_model.GetIndexerStatus(ctx, ctx.Repo.Repository, repo_model.RepoIndexerTypeStats)
  72. if err != nil {
  73. ctx.ServerError("repo.indexer_status", err)
  74. return
  75. }
  76. ctx.Data["StatsIndexerStatus"] = status
  77. }
  78. pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
  79. if err != nil {
  80. ctx.ServerError("GetPushMirrorsByRepoID", err)
  81. return
  82. }
  83. ctx.Data["PushMirrors"] = pushMirrors
  84. }
  85. // Settings show a repository's settings page
  86. func Settings(ctx *context.Context) {
  87. ctx.HTML(http.StatusOK, tplSettingsOptions)
  88. }
  89. // SettingsPost response for changes of a repository
  90. func SettingsPost(ctx *context.Context) {
  91. ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate
  92. ctx.Data["MirrorsEnabled"] = setting.Mirror.Enabled
  93. ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull
  94. ctx.Data["DisableNewPushMirrors"] = setting.Mirror.DisableNewPush
  95. ctx.Data["DefaultMirrorInterval"] = setting.Mirror.DefaultInterval
  96. ctx.Data["MinimumMirrorInterval"] = setting.Mirror.MinInterval
  97. signing, _ := asymkey_service.SigningKey(ctx, ctx.Repo.Repository.RepoPath())
  98. ctx.Data["SigningKeyAvailable"] = signing != nil
  99. ctx.Data["SigningSettings"] = setting.Repository.Signing
  100. ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
  101. switch ctx.FormString("action") {
  102. case "update":
  103. handleSettingsPostUpdate(ctx)
  104. case "mirror":
  105. handleSettingsPostMirror(ctx)
  106. case "mirror-sync":
  107. handleSettingsPostMirrorSync(ctx)
  108. case "push-mirror-sync":
  109. handleSettingsPostPushMirrorSync(ctx)
  110. case "push-mirror-update":
  111. handleSettingsPostPushMirrorUpdate(ctx)
  112. case "push-mirror-remove":
  113. handleSettingsPostPushMirrorRemove(ctx)
  114. case "push-mirror-add":
  115. handleSettingsPostPushMirrorAdd(ctx)
  116. case "advanced":
  117. handleSettingsPostAdvanced(ctx)
  118. case "signing":
  119. handleSettingsPostSigning(ctx)
  120. case "admin":
  121. handleSettingsPostAdmin(ctx)
  122. case "admin_index":
  123. handleSettingsPostAdminIndex(ctx)
  124. case "convert":
  125. handleSettingsPostConvert(ctx)
  126. case "convert_fork":
  127. handleSettingsPostConvertFork(ctx)
  128. case "transfer":
  129. handleSettingsPostTransfer(ctx)
  130. case "cancel_transfer":
  131. handleSettingsPostCancelTransfer(ctx)
  132. case "delete":
  133. handleSettingsPostDelete(ctx)
  134. case "delete-wiki":
  135. handleSettingsPostDeleteWiki(ctx)
  136. case "archive":
  137. handleSettingsPostArchive(ctx)
  138. case "unarchive":
  139. handleSettingsPostUnarchive(ctx)
  140. case "visibility":
  141. handleSettingsPostVisibility(ctx)
  142. default:
  143. ctx.NotFound(nil)
  144. }
  145. }
  146. func handleSettingsPostUpdate(ctx *context.Context) {
  147. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  148. repo := ctx.Repo.Repository
  149. if ctx.HasError() {
  150. ctx.HTML(http.StatusOK, tplSettingsOptions)
  151. return
  152. }
  153. newRepoName := form.RepoName
  154. // Check if repository name has been changed.
  155. if !strings.EqualFold(repo.LowerName, newRepoName) {
  156. // Close the GitRepo if open
  157. if ctx.Repo.GitRepo != nil {
  158. ctx.Repo.GitRepo.Close()
  159. ctx.Repo.GitRepo = nil
  160. }
  161. if err := repo_service.ChangeRepositoryName(ctx, ctx.Doer, repo, newRepoName); err != nil {
  162. ctx.Data["Err_RepoName"] = true
  163. switch {
  164. case repo_model.IsErrRepoAlreadyExist(err):
  165. ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tplSettingsOptions, &form)
  166. case db.IsErrNameReserved(err):
  167. ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form)
  168. case repo_model.IsErrRepoFilesAlreadyExist(err):
  169. ctx.Data["Err_RepoName"] = true
  170. switch {
  171. case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories):
  172. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt_or_delete"), tplSettingsOptions, form)
  173. case setting.Repository.AllowAdoptionOfUnadoptedRepositories:
  174. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt"), tplSettingsOptions, form)
  175. case setting.Repository.AllowDeleteOfUnadoptedRepositories:
  176. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.delete"), tplSettingsOptions, form)
  177. default:
  178. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist"), tplSettingsOptions, form)
  179. }
  180. case db.IsErrNamePatternNotAllowed(err):
  181. ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form)
  182. default:
  183. ctx.ServerError("ChangeRepositoryName", err)
  184. }
  185. return
  186. }
  187. log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
  188. }
  189. // In case it's just a case change.
  190. repo.Name = newRepoName
  191. repo.LowerName = strings.ToLower(newRepoName)
  192. repo.Description = form.Description
  193. repo.Website = form.Website
  194. repo.IsTemplate = form.Template
  195. // Visibility of forked repository is forced sync with base repository.
  196. if repo.IsFork {
  197. form.Private = repo.BaseRepo.IsPrivate || repo.BaseRepo.Owner.Visibility == structs.VisibleTypePrivate
  198. }
  199. if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
  200. ctx.ServerError("UpdateRepository", err)
  201. return
  202. }
  203. log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  204. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  205. ctx.Redirect(repo.Link() + "/settings")
  206. }
  207. func handleSettingsPostMirror(ctx *context.Context) {
  208. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  209. repo := ctx.Repo.Repository
  210. if !setting.Mirror.Enabled || !repo.IsMirror || repo.IsArchived {
  211. ctx.NotFound(nil)
  212. return
  213. }
  214. pullMirror, err := repo_model.GetMirrorByRepoID(ctx, ctx.Repo.Repository.ID)
  215. if err == repo_model.ErrMirrorNotExist {
  216. ctx.NotFound(nil)
  217. return
  218. }
  219. if err != nil {
  220. ctx.ServerError("GetMirrorByRepoID", err)
  221. return
  222. }
  223. // This section doesn't require repo_name/RepoName to be set in the form, don't show it
  224. // as an error on the UI for this action
  225. ctx.Data["Err_RepoName"] = nil
  226. interval, err := time.ParseDuration(form.Interval)
  227. if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
  228. ctx.Data["Err_Interval"] = true
  229. ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &form)
  230. return
  231. }
  232. pullMirror.EnablePrune = form.EnablePrune
  233. pullMirror.Interval = interval
  234. pullMirror.ScheduleNextUpdate()
  235. if err := repo_model.UpdateMirror(ctx, pullMirror); err != nil {
  236. ctx.ServerError("UpdateMirror", err)
  237. return
  238. }
  239. u, err := gitrepo.GitRemoteGetURL(ctx, ctx.Repo.Repository, pullMirror.GetRemoteName())
  240. if err != nil {
  241. ctx.Data["Err_MirrorAddress"] = true
  242. handleSettingRemoteAddrError(ctx, err, form)
  243. return
  244. }
  245. if u.User != nil && form.MirrorPassword == "" && form.MirrorUsername == u.User.Username() {
  246. form.MirrorPassword, _ = u.User.Password()
  247. }
  248. address, err := git.ParseRemoteAddr(form.MirrorAddress, form.MirrorUsername, form.MirrorPassword)
  249. if err == nil {
  250. err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
  251. }
  252. if err != nil {
  253. ctx.Data["Err_MirrorAddress"] = true
  254. handleSettingRemoteAddrError(ctx, err, form)
  255. return
  256. }
  257. if err := mirror_service.UpdateAddress(ctx, pullMirror, address); err != nil {
  258. ctx.ServerError("UpdateAddress", err)
  259. return
  260. }
  261. remoteAddress, err := util.SanitizeURL(form.MirrorAddress)
  262. if err != nil {
  263. ctx.Data["Err_MirrorAddress"] = true
  264. handleSettingRemoteAddrError(ctx, err, form)
  265. return
  266. }
  267. pullMirror.RemoteAddress = remoteAddress
  268. form.LFS = form.LFS && setting.LFS.StartServer
  269. if len(form.LFSEndpoint) > 0 {
  270. ep := lfs.DetermineEndpoint("", form.LFSEndpoint)
  271. if ep == nil {
  272. ctx.Data["Err_LFSEndpoint"] = true
  273. ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_lfs_endpoint"), tplSettingsOptions, &form)
  274. return
  275. }
  276. err = migrations.IsMigrateURLAllowed(ep.String(), ctx.Doer)
  277. if err != nil {
  278. ctx.Data["Err_LFSEndpoint"] = true
  279. handleSettingRemoteAddrError(ctx, err, form)
  280. return
  281. }
  282. }
  283. pullMirror.LFS = form.LFS
  284. pullMirror.LFSEndpoint = form.LFSEndpoint
  285. if err := repo_model.UpdateMirror(ctx, pullMirror); err != nil {
  286. ctx.ServerError("UpdateMirror", err)
  287. return
  288. }
  289. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  290. ctx.Redirect(repo.Link() + "/settings")
  291. }
  292. func handleSettingsPostMirrorSync(ctx *context.Context) {
  293. repo := ctx.Repo.Repository
  294. if !setting.Mirror.Enabled || !repo.IsMirror || repo.IsArchived {
  295. ctx.NotFound(nil)
  296. return
  297. }
  298. mirror_service.AddPullMirrorToQueue(repo.ID)
  299. ctx.Flash.Info(ctx.Tr("repo.settings.pull_mirror_sync_in_progress", repo.OriginalURL))
  300. ctx.Redirect(repo.Link() + "/settings")
  301. }
  302. func handleSettingsPostPushMirrorSync(ctx *context.Context) {
  303. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  304. repo := ctx.Repo.Repository
  305. if !setting.Mirror.Enabled {
  306. ctx.NotFound(nil)
  307. return
  308. }
  309. m, _, _ := repo_model.GetPushMirrorByIDAndRepoID(ctx, form.PushMirrorID, repo.ID)
  310. if m == nil {
  311. ctx.NotFound(nil)
  312. return
  313. }
  314. mirror_service.AddPushMirrorToQueue(m.ID)
  315. ctx.Flash.Info(ctx.Tr("repo.settings.push_mirror_sync_in_progress", m.RemoteAddress))
  316. ctx.Redirect(repo.Link() + "/settings")
  317. }
  318. func handleSettingsPostPushMirrorUpdate(ctx *context.Context) {
  319. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  320. repo := ctx.Repo.Repository
  321. if !setting.Mirror.Enabled || repo.IsArchived {
  322. ctx.NotFound(nil)
  323. return
  324. }
  325. // This section doesn't require repo_name/RepoName to be set in the form, don't show it
  326. // as an error on the UI for this action
  327. ctx.Data["Err_RepoName"] = nil
  328. interval, err := time.ParseDuration(form.PushMirrorInterval)
  329. if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
  330. ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &forms.RepoSettingForm{})
  331. return
  332. }
  333. m, _, _ := repo_model.GetPushMirrorByIDAndRepoID(ctx, form.PushMirrorID, repo.ID)
  334. if m == nil {
  335. ctx.NotFound(nil)
  336. return
  337. }
  338. m.Interval = interval
  339. if err := repo_model.UpdatePushMirrorInterval(ctx, m); err != nil {
  340. ctx.ServerError("UpdatePushMirrorInterval", err)
  341. return
  342. }
  343. // Background why we are adding it to Queue
  344. // If we observed its implementation in the context of `push-mirror-sync` where it
  345. // is evident that pushing to the queue is necessary for updates.
  346. // So, there are updates within the given interval, it is necessary to update the queue accordingly.
  347. if !ctx.FormBool("push_mirror_defer_sync") {
  348. // push_mirror_defer_sync is mainly for testing purpose, we do not really want to sync the push mirror immediately
  349. mirror_service.AddPushMirrorToQueue(m.ID)
  350. }
  351. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  352. ctx.Redirect(repo.Link() + "/settings")
  353. }
  354. func handleSettingsPostPushMirrorRemove(ctx *context.Context) {
  355. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  356. repo := ctx.Repo.Repository
  357. if !setting.Mirror.Enabled || repo.IsArchived {
  358. ctx.NotFound(nil)
  359. return
  360. }
  361. // This section doesn't require repo_name/RepoName to be set in the form, don't show it
  362. // as an error on the UI for this action
  363. ctx.Data["Err_RepoName"] = nil
  364. m, _, _ := repo_model.GetPushMirrorByIDAndRepoID(ctx, form.PushMirrorID, repo.ID)
  365. if m == nil {
  366. ctx.NotFound(nil)
  367. return
  368. }
  369. if err := mirror_service.RemovePushMirrorRemote(ctx, m); err != nil {
  370. ctx.ServerError("RemovePushMirrorRemote", err)
  371. return
  372. }
  373. if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
  374. ctx.ServerError("DeletePushMirrorByID", err)
  375. return
  376. }
  377. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  378. ctx.Redirect(repo.Link() + "/settings")
  379. }
  380. func handleSettingsPostPushMirrorAdd(ctx *context.Context) {
  381. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  382. repo := ctx.Repo.Repository
  383. if setting.Mirror.DisableNewPush || repo.IsArchived {
  384. ctx.NotFound(nil)
  385. return
  386. }
  387. // This section doesn't require repo_name/RepoName to be set in the form, don't show it
  388. // as an error on the UI for this action
  389. ctx.Data["Err_RepoName"] = nil
  390. interval, err := time.ParseDuration(form.PushMirrorInterval)
  391. if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
  392. ctx.Data["Err_PushMirrorInterval"] = true
  393. ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &form)
  394. return
  395. }
  396. address, err := git.ParseRemoteAddr(form.PushMirrorAddress, form.PushMirrorUsername, form.PushMirrorPassword)
  397. if err == nil {
  398. err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
  399. }
  400. if err != nil {
  401. ctx.Data["Err_PushMirrorAddress"] = true
  402. handleSettingRemoteAddrError(ctx, err, form)
  403. return
  404. }
  405. remoteSuffix, err := util.CryptoRandomString(10)
  406. if err != nil {
  407. ctx.ServerError("RandomString", err)
  408. return
  409. }
  410. remoteAddress, err := util.SanitizeURL(form.PushMirrorAddress)
  411. if err != nil {
  412. ctx.Data["Err_PushMirrorAddress"] = true
  413. handleSettingRemoteAddrError(ctx, err, form)
  414. return
  415. }
  416. m := &repo_model.PushMirror{
  417. RepoID: repo.ID,
  418. Repo: repo,
  419. RemoteName: "remote_mirror_" + remoteSuffix,
  420. SyncOnCommit: form.PushMirrorSyncOnCommit,
  421. Interval: interval,
  422. RemoteAddress: remoteAddress,
  423. }
  424. if err := db.Insert(ctx, m); err != nil {
  425. ctx.ServerError("InsertPushMirror", err)
  426. return
  427. }
  428. if err := mirror_service.AddPushMirrorRemote(ctx, m, address); err != nil {
  429. if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
  430. log.Error("DeletePushMirrors %v", err)
  431. }
  432. ctx.ServerError("AddPushMirrorRemote", err)
  433. return
  434. }
  435. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  436. ctx.Redirect(repo.Link() + "/settings")
  437. }
  438. func newRepoUnit(repo *repo_model.Repository, unitType unit_model.Type, config convert.Conversion) repo_model.RepoUnit {
  439. repoUnit := repo_model.RepoUnit{RepoID: repo.ID, Type: unitType, Config: config}
  440. for _, u := range repo.Units {
  441. if u.Type == unitType {
  442. repoUnit.EveryoneAccessMode = u.EveryoneAccessMode
  443. repoUnit.AnonymousAccessMode = u.AnonymousAccessMode
  444. }
  445. }
  446. return repoUnit
  447. }
  448. func handleSettingsPostAdvanced(ctx *context.Context) {
  449. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  450. repo := ctx.Repo.Repository
  451. var repoChanged bool
  452. var units []repo_model.RepoUnit
  453. var deleteUnitTypes []unit_model.Type
  454. // This section doesn't require repo_name/RepoName to be set in the form, don't show it
  455. // as an error on the UI for this action
  456. ctx.Data["Err_RepoName"] = nil
  457. if repo.CloseIssuesViaCommitInAnyBranch != form.EnableCloseIssuesViaCommitInAnyBranch {
  458. repo.CloseIssuesViaCommitInAnyBranch = form.EnableCloseIssuesViaCommitInAnyBranch
  459. repoChanged = true
  460. }
  461. if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() {
  462. units = append(units, newRepoUnit(repo, unit_model.TypeCode, nil))
  463. } else if !unit_model.TypeCode.UnitGlobalDisabled() {
  464. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
  465. }
  466. if form.EnableWiki && form.EnableExternalWiki && !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
  467. if !validation.IsValidExternalURL(form.ExternalWikiURL) {
  468. ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error"))
  469. ctx.Redirect(repo.Link() + "/settings")
  470. return
  471. }
  472. units = append(units, newRepoUnit(repo, unit_model.TypeExternalWiki, &repo_model.ExternalWikiConfig{
  473. ExternalWikiURL: form.ExternalWikiURL,
  474. }))
  475. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
  476. } else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
  477. units = append(units, newRepoUnit(repo, unit_model.TypeWiki, new(repo_model.UnitConfig)))
  478. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
  479. } else {
  480. if !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
  481. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
  482. }
  483. if !unit_model.TypeWiki.UnitGlobalDisabled() {
  484. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
  485. }
  486. }
  487. if form.DefaultWikiBranch != "" {
  488. if err := wiki_service.ChangeDefaultWikiBranch(ctx, repo, form.DefaultWikiBranch); err != nil {
  489. log.Error("ChangeDefaultWikiBranch failed, err: %v", err)
  490. ctx.Flash.Warning(ctx.Tr("repo.settings.failed_to_change_default_wiki_branch"))
  491. }
  492. }
  493. if form.EnableIssues && form.EnableExternalTracker && !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
  494. if !validation.IsValidExternalURL(form.ExternalTrackerURL) {
  495. ctx.Flash.Error(ctx.Tr("repo.settings.external_tracker_url_error"))
  496. ctx.Redirect(repo.Link() + "/settings")
  497. return
  498. }
  499. if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(form.TrackerURLFormat) {
  500. ctx.Flash.Error(ctx.Tr("repo.settings.tracker_url_format_error"))
  501. ctx.Redirect(repo.Link() + "/settings")
  502. return
  503. }
  504. units = append(units, newRepoUnit(repo, unit_model.TypeExternalTracker, &repo_model.ExternalTrackerConfig{
  505. ExternalTrackerURL: form.ExternalTrackerURL,
  506. ExternalTrackerFormat: form.TrackerURLFormat,
  507. ExternalTrackerStyle: form.TrackerIssueStyle,
  508. ExternalTrackerRegexpPattern: form.ExternalTrackerRegexpPattern,
  509. }))
  510. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
  511. } else if form.EnableIssues && !form.EnableExternalTracker && !unit_model.TypeIssues.UnitGlobalDisabled() {
  512. units = append(units, newRepoUnit(repo, unit_model.TypeIssues, &repo_model.IssuesConfig{
  513. EnableTimetracker: form.EnableTimetracker,
  514. AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime,
  515. EnableDependencies: form.EnableIssueDependencies,
  516. }))
  517. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
  518. } else {
  519. if !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
  520. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
  521. }
  522. if !unit_model.TypeIssues.UnitGlobalDisabled() {
  523. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
  524. }
  525. }
  526. if form.EnableProjects && !unit_model.TypeProjects.UnitGlobalDisabled() {
  527. units = append(units, newRepoUnit(repo, unit_model.TypeProjects, &repo_model.ProjectsConfig{
  528. ProjectsMode: repo_model.ProjectsMode(form.ProjectsMode),
  529. }))
  530. } else if !unit_model.TypeProjects.UnitGlobalDisabled() {
  531. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
  532. }
  533. if form.EnableReleases && !unit_model.TypeReleases.UnitGlobalDisabled() {
  534. units = append(units, newRepoUnit(repo, unit_model.TypeReleases, nil))
  535. } else if !unit_model.TypeReleases.UnitGlobalDisabled() {
  536. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeReleases)
  537. }
  538. if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
  539. units = append(units, newRepoUnit(repo, unit_model.TypePackages, nil))
  540. } else if !unit_model.TypePackages.UnitGlobalDisabled() {
  541. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
  542. }
  543. if form.EnableActions && !unit_model.TypeActions.UnitGlobalDisabled() {
  544. units = append(units, newRepoUnit(repo, unit_model.TypeActions, nil))
  545. } else if !unit_model.TypeActions.UnitGlobalDisabled() {
  546. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeActions)
  547. }
  548. if form.EnablePulls && !unit_model.TypePullRequests.UnitGlobalDisabled() {
  549. units = append(units, newRepoUnit(repo, unit_model.TypePullRequests, &repo_model.PullRequestsConfig{
  550. IgnoreWhitespaceConflicts: form.PullsIgnoreWhitespace,
  551. AllowMerge: form.PullsAllowMerge,
  552. AllowRebase: form.PullsAllowRebase,
  553. AllowRebaseMerge: form.PullsAllowRebaseMerge,
  554. AllowSquash: form.PullsAllowSquash,
  555. AllowFastForwardOnly: form.PullsAllowFastForwardOnly,
  556. AllowManualMerge: form.PullsAllowManualMerge,
  557. AutodetectManualMerge: form.EnableAutodetectManualMerge,
  558. AllowRebaseUpdate: form.PullsAllowRebaseUpdate,
  559. DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge,
  560. DefaultMergeStyle: repo_model.MergeStyle(form.PullsDefaultMergeStyle),
  561. DefaultAllowMaintainerEdit: form.DefaultAllowMaintainerEdit,
  562. }))
  563. } else if !unit_model.TypePullRequests.UnitGlobalDisabled() {
  564. deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
  565. }
  566. if len(units) == 0 {
  567. ctx.Flash.Error(ctx.Tr("repo.settings.update_settings_no_unit"))
  568. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  569. return
  570. }
  571. if err := repo_service.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
  572. ctx.ServerError("UpdateRepositoryUnits", err)
  573. return
  574. }
  575. if repoChanged {
  576. if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
  577. ctx.ServerError("UpdateRepository", err)
  578. return
  579. }
  580. }
  581. log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  582. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  583. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  584. }
  585. func handleSettingsPostSigning(ctx *context.Context) {
  586. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  587. repo := ctx.Repo.Repository
  588. trustModel := repo_model.ToTrustModel(form.TrustModel)
  589. if trustModel != repo.TrustModel {
  590. repo.TrustModel = trustModel
  591. if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "trust_model"); err != nil {
  592. ctx.ServerError("UpdateRepositoryColsNoAutoTime", err)
  593. return
  594. }
  595. log.Trace("Repository signing settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  596. }
  597. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  598. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  599. }
  600. func handleSettingsPostAdmin(ctx *context.Context) {
  601. if !ctx.Doer.IsAdmin {
  602. ctx.HTTPError(http.StatusForbidden)
  603. return
  604. }
  605. repo := ctx.Repo.Repository
  606. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  607. if repo.IsFsckEnabled != form.EnableHealthCheck {
  608. repo.IsFsckEnabled = form.EnableHealthCheck
  609. if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_fsck_enabled"); err != nil {
  610. ctx.ServerError("UpdateRepositoryColsNoAutoTime", err)
  611. return
  612. }
  613. log.Trace("Repository admin settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  614. }
  615. ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
  616. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  617. }
  618. func handleSettingsPostAdminIndex(ctx *context.Context) {
  619. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  620. repo := ctx.Repo.Repository
  621. if !ctx.Doer.IsAdmin {
  622. ctx.HTTPError(http.StatusForbidden)
  623. return
  624. }
  625. switch form.RequestReindexType {
  626. case "stats":
  627. if err := stats.UpdateRepoIndexer(ctx.Repo.Repository); err != nil {
  628. ctx.ServerError("UpdateStatsRepondexer", err)
  629. return
  630. }
  631. case "code":
  632. if !setting.Indexer.RepoIndexerEnabled {
  633. ctx.HTTPError(http.StatusForbidden)
  634. return
  635. }
  636. code.UpdateRepoIndexer(ctx.Repo.Repository)
  637. default:
  638. ctx.NotFound(nil)
  639. return
  640. }
  641. log.Trace("Repository reindex for %s requested: %s/%s", form.RequestReindexType, ctx.Repo.Owner.Name, repo.Name)
  642. ctx.Flash.Success(ctx.Tr("repo.settings.reindex_requested"))
  643. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  644. }
  645. func handleSettingsPostConvert(ctx *context.Context) {
  646. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  647. repo := ctx.Repo.Repository
  648. if !ctx.Repo.IsOwner() {
  649. ctx.HTTPError(http.StatusNotFound)
  650. return
  651. }
  652. if repo.Name != form.RepoName {
  653. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
  654. return
  655. }
  656. if !repo.IsMirror {
  657. ctx.HTTPError(http.StatusNotFound)
  658. return
  659. }
  660. repo.IsMirror = false
  661. if _, err := repo_service.CleanUpMigrateInfo(ctx, repo); err != nil {
  662. ctx.ServerError("CleanUpMigrateInfo", err)
  663. return
  664. } else if err = repo_model.DeleteMirrorByRepoID(ctx, ctx.Repo.Repository.ID); err != nil {
  665. ctx.ServerError("DeleteMirrorByRepoID", err)
  666. return
  667. }
  668. log.Trace("Repository converted from mirror to regular: %s", repo.FullName())
  669. ctx.Flash.Success(ctx.Tr("repo.settings.convert_succeed"))
  670. ctx.Redirect(repo.Link())
  671. }
  672. func handleSettingsPostConvertFork(ctx *context.Context) {
  673. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  674. repo := ctx.Repo.Repository
  675. if !ctx.Repo.IsOwner() {
  676. ctx.HTTPError(http.StatusNotFound)
  677. return
  678. }
  679. if err := repo.LoadOwner(ctx); err != nil {
  680. ctx.ServerError("Convert Fork", err)
  681. return
  682. }
  683. if repo.Name != form.RepoName {
  684. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
  685. return
  686. }
  687. if !repo.IsFork {
  688. ctx.HTTPError(http.StatusNotFound)
  689. return
  690. }
  691. if !ctx.Doer.CanForkRepoIn(ctx.Repo.Owner) {
  692. maxCreationLimit := ctx.Repo.Owner.MaxCreationLimit()
  693. msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
  694. ctx.Flash.Error(msg)
  695. ctx.Redirect(repo.Link() + "/settings")
  696. return
  697. }
  698. if err := repo_service.ConvertForkToNormalRepository(ctx, repo); err != nil {
  699. log.Error("Unable to convert repository %-v from fork. Error: %v", repo, err)
  700. ctx.ServerError("Convert Fork", err)
  701. return
  702. }
  703. log.Trace("Repository converted from fork to regular: %s", repo.FullName())
  704. ctx.Flash.Success(ctx.Tr("repo.settings.convert_fork_succeed"))
  705. ctx.Redirect(repo.Link())
  706. }
  707. func handleSettingsPostTransfer(ctx *context.Context) {
  708. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  709. repo := ctx.Repo.Repository
  710. if !ctx.Repo.IsOwner() {
  711. ctx.HTTPError(http.StatusNotFound)
  712. return
  713. }
  714. if repo.Name != form.RepoName {
  715. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
  716. return
  717. }
  718. newOwner, err := user_model.GetUserByName(ctx, ctx.FormString("new_owner_name"))
  719. if err != nil {
  720. if user_model.IsErrUserNotExist(err) {
  721. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil)
  722. return
  723. }
  724. ctx.ServerError("IsUserExist", err)
  725. return
  726. }
  727. if newOwner.Type == user_model.UserTypeOrganization {
  728. if !ctx.Doer.IsAdmin && newOwner.Visibility == structs.VisibleTypePrivate && !organization.OrgFromUser(newOwner).HasMemberWithUserID(ctx, ctx.Doer.ID) {
  729. // The user shouldn't know about this organization
  730. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil)
  731. return
  732. }
  733. }
  734. // Close the GitRepo if open
  735. if ctx.Repo.GitRepo != nil {
  736. ctx.Repo.GitRepo.Close()
  737. ctx.Repo.GitRepo = nil
  738. }
  739. oldFullname := repo.FullName()
  740. if err := repo_service.StartRepositoryTransfer(ctx, ctx.Doer, newOwner, repo, nil); err != nil {
  741. if repo_model.IsErrRepoAlreadyExist(err) {
  742. ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil)
  743. } else if repo_model.IsErrRepoTransferInProgress(err) {
  744. ctx.RenderWithErr(ctx.Tr("repo.settings.transfer_in_progress"), tplSettingsOptions, nil)
  745. } else if repo_service.IsRepositoryLimitReached(err) {
  746. limit := err.(repo_service.LimitReachedError).Limit
  747. ctx.RenderWithErr(ctx.TrN(limit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", limit), tplSettingsOptions, nil)
  748. } else if errors.Is(err, user_model.ErrBlockedUser) {
  749. ctx.RenderWithErr(ctx.Tr("repo.settings.transfer.blocked_user"), tplSettingsOptions, nil)
  750. } else {
  751. ctx.ServerError("TransferOwnership", err)
  752. }
  753. return
  754. }
  755. if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer {
  756. log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
  757. ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.DisplayName()))
  758. } else {
  759. log.Trace("Repository transferred: %s -> %s", oldFullname, ctx.Repo.Repository.FullName())
  760. ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed"))
  761. }
  762. ctx.Redirect(repo.Link() + "/settings")
  763. }
  764. func handleSettingsPostCancelTransfer(ctx *context.Context) {
  765. repo := ctx.Repo.Repository
  766. if !ctx.Repo.IsOwner() {
  767. ctx.HTTPError(http.StatusNotFound)
  768. return
  769. }
  770. repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository)
  771. if err != nil {
  772. if repo_model.IsErrNoPendingTransfer(err) {
  773. ctx.Flash.Error("repo.settings.transfer_abort_invalid")
  774. ctx.Redirect(repo.Link() + "/settings")
  775. } else {
  776. ctx.ServerError("GetPendingRepositoryTransfer", err)
  777. }
  778. return
  779. }
  780. if err := repo_service.CancelRepositoryTransfer(ctx, repoTransfer, ctx.Doer); err != nil {
  781. ctx.ServerError("CancelRepositoryTransfer", err)
  782. return
  783. }
  784. log.Trace("Repository transfer process was cancelled: %s/%s ", ctx.Repo.Owner.Name, repo.Name)
  785. ctx.Flash.Success(ctx.Tr("repo.settings.transfer_abort_success", repoTransfer.Recipient.Name))
  786. ctx.Redirect(repo.Link() + "/settings")
  787. }
  788. func handleSettingsPostDelete(ctx *context.Context) {
  789. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  790. repo := ctx.Repo.Repository
  791. if !ctx.Repo.IsOwner() {
  792. ctx.HTTPError(http.StatusNotFound)
  793. return
  794. }
  795. if repo.Name != form.RepoName {
  796. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
  797. return
  798. }
  799. // Close the gitrepository before doing this.
  800. if ctx.Repo.GitRepo != nil {
  801. ctx.Repo.GitRepo.Close()
  802. }
  803. if err := repo_service.DeleteRepository(ctx, ctx.Doer, ctx.Repo.Repository, true); err != nil {
  804. ctx.ServerError("DeleteRepository", err)
  805. return
  806. }
  807. log.Trace("Repository deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  808. ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success"))
  809. ctx.Redirect(ctx.Repo.Owner.DashboardLink())
  810. }
  811. func handleSettingsPostDeleteWiki(ctx *context.Context) {
  812. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  813. repo := ctx.Repo.Repository
  814. if !ctx.Repo.IsOwner() {
  815. ctx.HTTPError(http.StatusNotFound)
  816. return
  817. }
  818. if repo.Name != form.RepoName {
  819. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
  820. return
  821. }
  822. err := wiki_service.DeleteWiki(ctx, repo)
  823. if err != nil {
  824. log.Error("Delete Wiki: %v", err.Error())
  825. }
  826. log.Trace("Repository wiki deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  827. ctx.Flash.Success(ctx.Tr("repo.settings.wiki_deletion_success"))
  828. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  829. }
  830. func handleSettingsPostArchive(ctx *context.Context) {
  831. repo := ctx.Repo.Repository
  832. if !ctx.Repo.IsOwner() {
  833. ctx.HTTPError(http.StatusForbidden)
  834. return
  835. }
  836. if repo.IsMirror {
  837. ctx.Flash.Error(ctx.Tr("repo.settings.archive.error_ismirror"))
  838. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  839. return
  840. }
  841. if err := repo_model.SetArchiveRepoState(ctx, repo, true); err != nil {
  842. log.Error("Tried to archive a repo: %s", err)
  843. ctx.Flash.Error(ctx.Tr("repo.settings.archive.error"))
  844. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  845. return
  846. }
  847. if err := actions_service.CleanRepoScheduleTasks(ctx, repo); err != nil {
  848. log.Error("CleanRepoScheduleTasks for archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
  849. }
  850. // update issue indexer
  851. issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
  852. ctx.Flash.Success(ctx.Tr("repo.settings.archive.success"))
  853. log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  854. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  855. }
  856. func handleSettingsPostUnarchive(ctx *context.Context) {
  857. repo := ctx.Repo.Repository
  858. if !ctx.Repo.IsOwner() {
  859. ctx.HTTPError(http.StatusForbidden)
  860. return
  861. }
  862. if err := repo_model.SetArchiveRepoState(ctx, repo, false); err != nil {
  863. log.Error("Tried to unarchive a repo: %s", err)
  864. ctx.Flash.Error(ctx.Tr("repo.settings.unarchive.error"))
  865. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  866. return
  867. }
  868. if ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypeActions) {
  869. if err := actions_service.DetectAndHandleSchedules(ctx, repo); err != nil {
  870. log.Error("DetectAndHandleSchedules for un-archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
  871. }
  872. }
  873. // update issue indexer
  874. issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
  875. ctx.Flash.Success(ctx.Tr("repo.settings.unarchive.success"))
  876. log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  877. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  878. }
  879. func handleSettingsPostVisibility(ctx *context.Context) {
  880. form := web.GetForm(ctx).(*forms.RepoSettingForm)
  881. repo := ctx.Repo.Repository
  882. if repo.IsFork {
  883. ctx.Flash.Error(ctx.Tr("repo.settings.visibility.fork_error"))
  884. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  885. return
  886. }
  887. var err error
  888. // when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
  889. if setting.Repository.ForcePrivate && repo.IsPrivate && !ctx.Doer.IsAdmin {
  890. ctx.RenderWithErr(ctx.Tr("form.repository_force_private"), tplSettingsOptions, form)
  891. return
  892. }
  893. if repo.IsPrivate {
  894. err = repo_service.MakeRepoPublic(ctx, repo)
  895. } else {
  896. err = repo_service.MakeRepoPrivate(ctx, repo)
  897. }
  898. if err != nil {
  899. log.Error("Tried to change the visibility of the repo: %s", err)
  900. ctx.Flash.Error(ctx.Tr("repo.settings.visibility.error"))
  901. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  902. return
  903. }
  904. ctx.Flash.Success(ctx.Tr("repo.settings.visibility.success"))
  905. log.Trace("Repository visibility changed: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  906. ctx.Redirect(ctx.Repo.RepoLink + "/settings")
  907. }
  908. func handleSettingRemoteAddrError(ctx *context.Context, err error, form *forms.RepoSettingForm) {
  909. if git.IsErrInvalidCloneAddr(err) {
  910. addrErr := err.(*git.ErrInvalidCloneAddr)
  911. switch {
  912. case addrErr.IsProtocolInvalid:
  913. ctx.RenderWithErr(ctx.Tr("repo.mirror_address_protocol_invalid"), tplSettingsOptions, form)
  914. case addrErr.IsURLError:
  915. ctx.RenderWithErr(ctx.Tr("form.url_error", addrErr.Host), tplSettingsOptions, form)
  916. case addrErr.IsPermissionDenied:
  917. if addrErr.LocalPath {
  918. ctx.RenderWithErr(ctx.Tr("repo.migrate.permission_denied"), tplSettingsOptions, form)
  919. } else {
  920. ctx.RenderWithErr(ctx.Tr("repo.migrate.permission_denied_blocked"), tplSettingsOptions, form)
  921. }
  922. case addrErr.IsInvalidPath:
  923. ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), tplSettingsOptions, form)
  924. default:
  925. ctx.ServerError("Unknown error", err)
  926. }
  927. return
  928. }
  929. ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, form)
  930. }