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

teams.go 17KB

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