gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  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. "errors"
  7. "fmt"
  8. "net/http"
  9. "net/url"
  10. "path"
  11. "strconv"
  12. "strings"
  13. "code.gitea.io/gitea/models/db"
  14. org_model "code.gitea.io/gitea/models/organization"
  15. "code.gitea.io/gitea/models/perm"
  16. repo_model "code.gitea.io/gitea/models/repo"
  17. unit_model "code.gitea.io/gitea/models/unit"
  18. user_model "code.gitea.io/gitea/models/user"
  19. "code.gitea.io/gitea/modules/log"
  20. "code.gitea.io/gitea/modules/setting"
  21. "code.gitea.io/gitea/modules/templates"
  22. "code.gitea.io/gitea/modules/web"
  23. shared_user "code.gitea.io/gitea/routers/web/shared/user"
  24. "code.gitea.io/gitea/services/context"
  25. "code.gitea.io/gitea/services/convert"
  26. "code.gitea.io/gitea/services/forms"
  27. org_service "code.gitea.io/gitea/services/org"
  28. repo_service "code.gitea.io/gitea/services/repository"
  29. )
  30. const (
  31. // tplTeams template path for teams list page
  32. tplTeams templates.TplName = "org/team/teams"
  33. // tplTeamNew template path for create new team page
  34. tplTeamNew templates.TplName = "org/team/new"
  35. // tplTeamMembers template path for showing team members page
  36. tplTeamMembers templates.TplName = "org/team/members"
  37. // tplTeamRepositories template path for showing team repositories page
  38. tplTeamRepositories templates.TplName = "org/team/repositories"
  39. // tplTeamInvite template path for team invites page
  40. tplTeamInvite templates.TplName = "org/team/invite"
  41. )
  42. // Teams render teams list page
  43. func Teams(ctx *context.Context) {
  44. if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
  45. ctx.ServerError("RenderUserOrgHeader", err)
  46. return
  47. }
  48. org := ctx.Org.Organization
  49. ctx.Data["Title"] = org.FullName
  50. ctx.Data["PageIsOrgTeams"] = true
  51. for _, t := range ctx.Org.Teams {
  52. if err := t.LoadMembers(ctx); err != nil {
  53. ctx.ServerError("GetMembers", err)
  54. return
  55. }
  56. }
  57. ctx.Data["Teams"] = ctx.Org.Teams
  58. ctx.HTML(http.StatusOK, tplTeams)
  59. }
  60. // TeamsAction response for join, leave, remove, add operations to team
  61. func TeamsAction(ctx *context.Context) {
  62. page := ctx.FormString("page")
  63. var err error
  64. switch ctx.PathParam("action") {
  65. case "join":
  66. if !ctx.Org.IsOwner {
  67. ctx.HTTPError(http.StatusNotFound)
  68. return
  69. }
  70. err = org_service.AddTeamMember(ctx, ctx.Org.Team, ctx.Doer)
  71. case "leave":
  72. err = org_service.RemoveTeamMember(ctx, ctx.Org.Team, ctx.Doer)
  73. if err != nil {
  74. if org_model.IsErrLastOrgOwner(err) {
  75. ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
  76. } else {
  77. log.Error("Action(%s): %v", ctx.PathParam("action"), err)
  78. ctx.JSON(http.StatusOK, map[string]any{
  79. "ok": false,
  80. "err": err.Error(),
  81. })
  82. return
  83. }
  84. }
  85. checkIsOrgMemberAndRedirect(ctx, ctx.Org.OrgLink+"/teams/")
  86. return
  87. case "remove":
  88. if !ctx.Org.IsOwner {
  89. ctx.HTTPError(http.StatusNotFound)
  90. return
  91. }
  92. user, _ := user_model.GetUserByID(ctx, ctx.FormInt64("uid"))
  93. if user == nil {
  94. ctx.Redirect(ctx.Org.OrgLink + "/teams")
  95. return
  96. }
  97. err = org_service.RemoveTeamMember(ctx, ctx.Org.Team, user)
  98. if err != nil {
  99. if org_model.IsErrLastOrgOwner(err) {
  100. ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
  101. } else {
  102. log.Error("Action(%s): %v", ctx.PathParam("action"), err)
  103. ctx.JSON(http.StatusOK, map[string]any{
  104. "ok": false,
  105. "err": err.Error(),
  106. })
  107. return
  108. }
  109. }
  110. checkIsOrgMemberAndRedirect(ctx, ctx.Org.OrgLink+"/teams/"+url.PathEscape(ctx.Org.Team.LowerName))
  111. return
  112. case "add":
  113. if !ctx.Org.IsOwner {
  114. ctx.HTTPError(http.StatusNotFound)
  115. return
  116. }
  117. uname := strings.ToLower(ctx.FormString("uname"))
  118. var u *user_model.User
  119. u, err = user_model.GetUserByName(ctx, uname)
  120. if err != nil {
  121. if user_model.IsErrUserNotExist(err) {
  122. if setting.MailService != nil && user_model.ValidateEmail(uname) == nil {
  123. if err := org_service.CreateTeamInvite(ctx, ctx.Doer, ctx.Org.Team, uname); err != nil {
  124. if org_model.IsErrTeamInviteAlreadyExist(err) {
  125. ctx.Flash.Error(ctx.Tr("form.duplicate_invite_to_team"))
  126. } else if org_model.IsErrUserEmailAlreadyAdded(err) {
  127. ctx.Flash.Error(ctx.Tr("org.teams.add_duplicate_users"))
  128. } else {
  129. ctx.ServerError("CreateTeamInvite", err)
  130. return
  131. }
  132. }
  133. } else {
  134. ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
  135. }
  136. ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
  137. } else {
  138. ctx.ServerError("GetUserByName", err)
  139. }
  140. return
  141. }
  142. if u.IsOrganization() {
  143. ctx.Flash.Error(ctx.Tr("form.cannot_add_org_to_team"))
  144. ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
  145. return
  146. }
  147. if ctx.Org.Team.IsMember(ctx, u.ID) {
  148. ctx.Flash.Error(ctx.Tr("org.teams.add_duplicate_users"))
  149. } else {
  150. err = org_service.AddTeamMember(ctx, ctx.Org.Team, u)
  151. }
  152. page = "team"
  153. case "remove_invite":
  154. if !ctx.Org.IsOwner {
  155. ctx.HTTPError(http.StatusNotFound)
  156. return
  157. }
  158. iid := ctx.FormInt64("iid")
  159. if iid == 0 {
  160. ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
  161. return
  162. }
  163. if err := org_model.RemoveInviteByID(ctx, iid, ctx.Org.Team.ID); err != nil {
  164. log.Error("Action(%s): %v", ctx.PathParam("action"), err)
  165. ctx.ServerError("RemoveInviteByID", err)
  166. return
  167. }
  168. page = "team"
  169. }
  170. if err != nil {
  171. if org_model.IsErrLastOrgOwner(err) {
  172. ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
  173. } else if errors.Is(err, user_model.ErrBlockedUser) {
  174. ctx.Flash.Error(ctx.Tr("org.teams.members.blocked_user"))
  175. } else {
  176. log.Error("Action(%s): %v", ctx.PathParam("action"), err)
  177. ctx.JSON(http.StatusOK, map[string]any{
  178. "ok": false,
  179. "err": err.Error(),
  180. })
  181. return
  182. }
  183. }
  184. switch page {
  185. case "team":
  186. ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
  187. case "home":
  188. ctx.Redirect(ctx.Org.Organization.AsUser().HomeLink())
  189. default:
  190. ctx.Redirect(ctx.Org.OrgLink + "/teams")
  191. }
  192. }
  193. func checkIsOrgMemberAndRedirect(ctx *context.Context, defaultRedirect string) {
  194. if isOrgMember, err := org_model.IsOrganizationMember(ctx, ctx.Org.Organization.ID, ctx.Doer.ID); err != nil {
  195. ctx.ServerError("IsOrganizationMember", err)
  196. return
  197. } else if !isOrgMember {
  198. if ctx.Org.Organization.Visibility.IsPrivate() {
  199. defaultRedirect = setting.AppSubURL + "/"
  200. } else {
  201. defaultRedirect = ctx.Org.Organization.HomeLink()
  202. }
  203. }
  204. ctx.JSONRedirect(defaultRedirect)
  205. }
  206. // TeamsRepoAction operate team's repository
  207. func TeamsRepoAction(ctx *context.Context) {
  208. if !ctx.Org.IsOwner {
  209. ctx.HTTPError(http.StatusNotFound)
  210. return
  211. }
  212. var err error
  213. action := ctx.PathParam("action")
  214. switch action {
  215. case "add":
  216. repoName := path.Base(ctx.FormString("repo_name"))
  217. var repo *repo_model.Repository
  218. repo, err = repo_model.GetRepositoryByName(ctx, ctx.Org.Organization.ID, repoName)
  219. if err != nil {
  220. if repo_model.IsErrRepoNotExist(err) {
  221. ctx.Flash.Error(ctx.Tr("org.teams.add_nonexistent_repo"))
  222. ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
  223. return
  224. }
  225. ctx.ServerError("GetRepositoryByName", err)
  226. return
  227. }
  228. err = repo_service.TeamAddRepository(ctx, ctx.Org.Team, repo)
  229. case "remove":
  230. err = repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, ctx.FormInt64("repoid"))
  231. case "addall":
  232. err = repo_service.AddAllRepositoriesToTeam(ctx, ctx.Org.Team)
  233. case "removeall":
  234. err = repo_service.RemoveAllRepositoriesFromTeam(ctx, ctx.Org.Team)
  235. }
  236. if err != nil {
  237. log.Error("Action(%s): '%s' %v", ctx.PathParam("action"), ctx.Org.Team.Name, err)
  238. ctx.ServerError("TeamsRepoAction", err)
  239. return
  240. }
  241. if action == "addall" || action == "removeall" {
  242. ctx.JSONRedirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
  243. return
  244. }
  245. ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
  246. }
  247. // NewTeam render create new team page
  248. func NewTeam(ctx *context.Context) {
  249. if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
  250. ctx.ServerError("RenderUserOrgHeader", err)
  251. return
  252. }
  253. ctx.Data["Title"] = ctx.Org.Organization.FullName
  254. ctx.Data["PageIsOrgTeams"] = true
  255. ctx.Data["PageIsOrgTeamsNew"] = true
  256. ctx.Data["Team"] = &org_model.Team{}
  257. ctx.Data["Units"] = unit_model.Units
  258. ctx.HTML(http.StatusOK, tplTeamNew)
  259. }
  260. // FIXME: TEAM-UNIT-PERMISSION: this design is not right, when a new unit is added in the future,
  261. // The existing teams won't inherit the correct admin permission for the new unit.
  262. // The full history is like this:
  263. // 1. There was only "team", no "team unit", so "team.authorize" was used to determine the team permission.
  264. // 2. Later, "team unit" was introduced, then the usage of "team.authorize" became inconsistent, and causes various bugs.
  265. // - Sometimes, "team.authorize" is used to determine the team permission, e.g. admin, owner
  266. // - Sometimes, "team unit" is used not really used and "team unit" is used.
  267. // - Some functions like `GetTeamsWithAccessToAnyRepoUnit` use both.
  268. //
  269. // 3. After introducing "team unit" and more unclear changes, it becomes difficult to maintain team permissions.
  270. // - Org owner need to click the permission for each unit, but can't just set a common "write" permission for all units.
  271. //
  272. // Ideally, "team.authorize=write" should mean the team has write access to all units including newly (future) added ones.
  273. func getUnitPerms(forms url.Values, teamPermission perm.AccessMode) map[unit_model.Type]perm.AccessMode {
  274. unitPerms := make(map[unit_model.Type]perm.AccessMode)
  275. for _, ut := range unit_model.AllRepoUnitTypes {
  276. // Default access mode is none
  277. unitPerms[ut] = perm.AccessModeNone
  278. v, ok := forms[fmt.Sprintf("unit_%d", ut)]
  279. if ok {
  280. vv, _ := strconv.Atoi(v[0])
  281. if teamPermission >= perm.AccessModeAdmin {
  282. unitPerms[ut] = teamPermission
  283. // Don't allow `TypeExternal{Tracker,Wiki}` to influence this as they can only be set to READ perms.
  284. if ut == unit_model.TypeExternalTracker || ut == unit_model.TypeExternalWiki {
  285. unitPerms[ut] = perm.AccessModeRead
  286. }
  287. } else {
  288. unitPerms[ut] = perm.AccessMode(vv)
  289. if unitPerms[ut] >= perm.AccessModeAdmin {
  290. unitPerms[ut] = perm.AccessModeWrite
  291. }
  292. }
  293. }
  294. }
  295. return unitPerms
  296. }
  297. // NewTeamPost response for create new team
  298. func NewTeamPost(ctx *context.Context) {
  299. form := web.GetForm(ctx).(*forms.CreateTeamForm)
  300. includesAllRepositories := form.RepoAccess == "all"
  301. teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
  302. unitPerms := getUnitPerms(ctx.Req.Form, teamPermission)
  303. t := &org_model.Team{
  304. OrgID: ctx.Org.Organization.ID,
  305. Name: form.TeamName,
  306. Description: form.Description,
  307. AccessMode: teamPermission,
  308. IncludesAllRepositories: includesAllRepositories,
  309. CanCreateOrgRepo: form.CanCreateOrgRepo,
  310. }
  311. units := make([]*org_model.TeamUnit, 0, len(unitPerms))
  312. for tp, perm := range unitPerms {
  313. units = append(units, &org_model.TeamUnit{
  314. OrgID: ctx.Org.Organization.ID,
  315. Type: tp,
  316. AccessMode: perm,
  317. })
  318. }
  319. t.Units = units
  320. ctx.Data["Title"] = ctx.Org.Organization.FullName
  321. ctx.Data["PageIsOrgTeams"] = true
  322. ctx.Data["PageIsOrgTeamsNew"] = true
  323. ctx.Data["Units"] = unit_model.Units
  324. ctx.Data["Team"] = t
  325. if ctx.HasError() {
  326. ctx.HTML(http.StatusOK, tplTeamNew)
  327. return
  328. }
  329. if t.AccessMode < perm.AccessModeAdmin && len(unitPerms) == 0 {
  330. ctx.RenderWithErr(ctx.Tr("form.team_no_units_error"), tplTeamNew, &form)
  331. return
  332. }
  333. if err := org_service.NewTeam(ctx, t); err != nil {
  334. ctx.Data["Err_TeamName"] = true
  335. switch {
  336. case org_model.IsErrTeamAlreadyExist(err):
  337. ctx.RenderWithErr(ctx.Tr("form.team_name_been_taken"), tplTeamNew, &form)
  338. default:
  339. ctx.ServerError("NewTeam", err)
  340. }
  341. return
  342. }
  343. log.Trace("Team created: %s/%s", ctx.Org.Organization.Name, t.Name)
  344. ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(t.LowerName))
  345. }
  346. // TeamMembers render team members page
  347. func TeamMembers(ctx *context.Context) {
  348. if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
  349. ctx.ServerError("RenderUserOrgHeader", err)
  350. return
  351. }
  352. ctx.Data["Title"] = ctx.Org.Team.Name
  353. ctx.Data["PageIsOrgTeams"] = true
  354. ctx.Data["PageIsOrgTeamMembers"] = true
  355. if err := ctx.Org.Team.LoadMembers(ctx); err != nil {
  356. ctx.ServerError("GetMembers", err)
  357. return
  358. }
  359. ctx.Data["Units"] = unit_model.Units
  360. invites, err := org_model.GetInvitesByTeamID(ctx, ctx.Org.Team.ID)
  361. if err != nil {
  362. ctx.ServerError("GetInvitesByTeamID", err)
  363. return
  364. }
  365. ctx.Data["Invites"] = invites
  366. ctx.Data["IsEmailInviteEnabled"] = setting.MailService != nil
  367. ctx.HTML(http.StatusOK, tplTeamMembers)
  368. }
  369. // TeamRepositories show the repositories of team
  370. func TeamRepositories(ctx *context.Context) {
  371. if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
  372. ctx.ServerError("RenderUserOrgHeader", err)
  373. return
  374. }
  375. ctx.Data["Title"] = ctx.Org.Team.Name
  376. ctx.Data["PageIsOrgTeams"] = true
  377. ctx.Data["PageIsOrgTeamRepos"] = true
  378. repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
  379. TeamID: ctx.Org.Team.ID,
  380. })
  381. if err != nil {
  382. ctx.ServerError("GetTeamRepositories", err)
  383. return
  384. }
  385. ctx.Data["Units"] = unit_model.Units
  386. ctx.Data["TeamRepos"] = repos
  387. ctx.HTML(http.StatusOK, tplTeamRepositories)
  388. }
  389. // SearchTeam api for searching teams
  390. func SearchTeam(ctx *context.Context) {
  391. listOptions := db.ListOptions{
  392. Page: ctx.FormInt("page"),
  393. PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
  394. }
  395. opts := &org_model.SearchTeamOptions{
  396. // UserID is not set because the router already requires the doer to be an org admin. Thus, we don't need to restrict to teams that the user belongs in
  397. Keyword: ctx.FormTrim("q"),
  398. OrgID: ctx.Org.Organization.ID,
  399. IncludeDesc: ctx.FormString("include_desc") == "" || ctx.FormBool("include_desc"),
  400. ListOptions: listOptions,
  401. }
  402. teams, maxResults, err := org_model.SearchTeam(ctx, opts)
  403. if err != nil {
  404. log.Error("SearchTeam failed: %v", err)
  405. ctx.JSON(http.StatusInternalServerError, map[string]any{
  406. "ok": false,
  407. "error": "SearchTeam internal failure",
  408. })
  409. return
  410. }
  411. apiTeams, err := convert.ToTeams(ctx, teams, false)
  412. if err != nil {
  413. log.Error("convert ToTeams failed: %v", err)
  414. ctx.JSON(http.StatusInternalServerError, map[string]any{
  415. "ok": false,
  416. "error": "SearchTeam failed to get units",
  417. })
  418. return
  419. }
  420. ctx.SetTotalCountHeader(maxResults)
  421. ctx.JSON(http.StatusOK, map[string]any{
  422. "ok": true,
  423. "data": apiTeams,
  424. })
  425. }
  426. // EditTeam render team edit page
  427. func EditTeam(ctx *context.Context) {
  428. if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
  429. ctx.ServerError("RenderUserOrgHeader", err)
  430. return
  431. }
  432. ctx.Data["Title"] = ctx.Org.Organization.FullName
  433. ctx.Data["PageIsOrgTeams"] = true
  434. if err := ctx.Org.Team.LoadUnits(ctx); err != nil {
  435. ctx.ServerError("LoadUnits", err)
  436. return
  437. }
  438. ctx.Data["Team"] = ctx.Org.Team
  439. ctx.Data["Units"] = unit_model.Units
  440. ctx.HTML(http.StatusOK, tplTeamNew)
  441. }
  442. // EditTeamPost response for modify team information
  443. func EditTeamPost(ctx *context.Context) {
  444. form := web.GetForm(ctx).(*forms.CreateTeamForm)
  445. t := ctx.Org.Team
  446. teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
  447. unitPerms := getUnitPerms(ctx.Req.Form, teamPermission)
  448. isAuthChanged := false
  449. isIncludeAllChanged := false
  450. includesAllRepositories := form.RepoAccess == "all"
  451. ctx.Data["Title"] = ctx.Org.Organization.FullName
  452. ctx.Data["PageIsOrgTeams"] = true
  453. ctx.Data["Team"] = t
  454. ctx.Data["Units"] = unit_model.Units
  455. if !t.IsOwnerTeam() {
  456. t.Name = form.TeamName
  457. if t.AccessMode != teamPermission {
  458. isAuthChanged = true
  459. t.AccessMode = teamPermission
  460. }
  461. if t.IncludesAllRepositories != includesAllRepositories {
  462. isIncludeAllChanged = true
  463. t.IncludesAllRepositories = includesAllRepositories
  464. }
  465. t.CanCreateOrgRepo = form.CanCreateOrgRepo
  466. } else {
  467. t.CanCreateOrgRepo = true
  468. }
  469. t.Description = form.Description
  470. units := make([]*org_model.TeamUnit, 0, len(unitPerms))
  471. for tp, perm := range unitPerms {
  472. units = append(units, &org_model.TeamUnit{
  473. OrgID: t.OrgID,
  474. TeamID: t.ID,
  475. Type: tp,
  476. AccessMode: perm,
  477. })
  478. }
  479. t.Units = units
  480. if ctx.HasError() {
  481. ctx.HTML(http.StatusOK, tplTeamNew)
  482. return
  483. }
  484. if t.AccessMode < perm.AccessModeAdmin && len(unitPerms) == 0 {
  485. ctx.RenderWithErr(ctx.Tr("form.team_no_units_error"), tplTeamNew, &form)
  486. return
  487. }
  488. if err := org_service.UpdateTeam(ctx, t, isAuthChanged, isIncludeAllChanged); err != nil {
  489. ctx.Data["Err_TeamName"] = true
  490. switch {
  491. case org_model.IsErrTeamAlreadyExist(err):
  492. ctx.RenderWithErr(ctx.Tr("form.team_name_been_taken"), tplTeamNew, &form)
  493. default:
  494. ctx.ServerError("UpdateTeam", err)
  495. }
  496. return
  497. }
  498. ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(t.LowerName))
  499. }
  500. // DeleteTeam response for the delete team request
  501. func DeleteTeam(ctx *context.Context) {
  502. if err := org_service.DeleteTeam(ctx, ctx.Org.Team); err != nil {
  503. ctx.Flash.Error("DeleteTeam: " + err.Error())
  504. } else {
  505. ctx.Flash.Success(ctx.Tr("org.teams.delete_team_success"))
  506. }
  507. ctx.JSONRedirect(ctx.Org.OrgLink + "/teams")
  508. }
  509. // TeamInvite renders the team invite page
  510. func TeamInvite(ctx *context.Context) {
  511. invite, org, team, inviter, err := getTeamInviteFromContext(ctx)
  512. if err != nil {
  513. if org_model.IsErrTeamInviteNotFound(err) {
  514. ctx.NotFound(err)
  515. } else {
  516. ctx.ServerError("getTeamInviteFromContext", err)
  517. }
  518. return
  519. }
  520. ctx.Data["Title"] = ctx.Tr("org.teams.invite_team_member", team.Name)
  521. ctx.Data["Invite"] = invite
  522. ctx.Data["Organization"] = org
  523. ctx.Data["Team"] = team
  524. ctx.Data["Inviter"] = inviter
  525. ctx.HTML(http.StatusOK, tplTeamInvite)
  526. }
  527. // TeamInvitePost handles the team invitation
  528. func TeamInvitePost(ctx *context.Context) {
  529. invite, org, team, _, err := getTeamInviteFromContext(ctx)
  530. if err != nil {
  531. if org_model.IsErrTeamInviteNotFound(err) {
  532. ctx.NotFound(err)
  533. } else {
  534. ctx.ServerError("getTeamInviteFromContext", err)
  535. }
  536. return
  537. }
  538. if err := org_service.AddTeamMember(ctx, team, ctx.Doer); err != nil {
  539. ctx.ServerError("AddTeamMember", err)
  540. return
  541. }
  542. if err := org_model.RemoveInviteByID(ctx, invite.ID, team.ID); err != nil {
  543. log.Error("RemoveInviteByID: %v", err)
  544. }
  545. ctx.Redirect(org.OrganisationLink() + "/teams/" + url.PathEscape(team.LowerName))
  546. }
  547. func getTeamInviteFromContext(ctx *context.Context) (*org_model.TeamInvite, *org_model.Organization, *org_model.Team, *user_model.User, error) {
  548. invite, err := org_model.GetInviteByToken(ctx, ctx.PathParam("token"))
  549. if err != nil {
  550. return nil, nil, nil, nil, err
  551. }
  552. inviter, err := user_model.GetUserByID(ctx, invite.InviterID)
  553. if err != nil {
  554. return nil, nil, nil, nil, err
  555. }
  556. team, err := org_model.GetTeamByID(ctx, invite.TeamID)
  557. if err != nil {
  558. return nil, nil, nil, nil, err
  559. }
  560. org, err := user_model.GetUserByID(ctx, team.OrgID)
  561. if err != nil {
  562. return nil, nil, nil, nil, err
  563. }
  564. return invite, org_model.OrgFromUser(org), team, inviter, nil
  565. }