Git with a cup of tea, painless self-hosted git service Mirror for internal git.with.parts use https://git.with.parts
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.
gitea/routers/web/org/teams.go

631 lines
18 KiB

11 years ago
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
11 years ago
package org
import (
"errors"
"fmt"
"net/http"
"net/url"
"path"
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
"strconv"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
org_model "code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
4 years ago
"code.gitea.io/gitea/modules/web"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/forms"
org_service "code.gitea.io/gitea/services/org"
repo_service "code.gitea.io/gitea/services/repository"
)
11 years ago
const (
// tplTeams template path for teams list page
tplTeams base.TplName = "org/team/teams"
// tplTeamNew template path for create new team page
tplTeamNew base.TplName = "org/team/new"
// tplTeamMembers template path for showing team members page
tplTeamMembers base.TplName = "org/team/members"
// tplTeamRepositories template path for showing team repositories page
tplTeamRepositories base.TplName = "org/team/repositories"
// tplTeamInvite template path for team invites page
tplTeamInvite base.TplName = "org/team/invite"
11 years ago
)
// Teams render teams list page
func Teams(ctx *context.Context) {
org := ctx.Org.Organization
ctx.Data["Title"] = org.FullName
ctx.Data["PageIsOrgTeams"] = true
11 years ago
for _, t := range ctx.Org.Teams {
if err := t.LoadMembers(ctx); err != nil {
ctx.ServerError("GetMembers", err)
11 years ago
return
}
}
ctx.Data["Teams"] = ctx.Org.Teams
11 years ago
err := shared_user.LoadHeaderCount(ctx)
if err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
ctx.HTML(http.StatusOK, tplTeams)
}
// TeamsAction response for join, leave, remove, add operations to team
func TeamsAction(ctx *context.Context) {
page := ctx.FormString("page")
var err error
switch ctx.PathParam(":action") {
case "join":
if !ctx.Org.IsOwner {
ctx.Error(http.StatusNotFound)
return
}
err = models.AddTeamMember(ctx, ctx.Org.Team, ctx.Doer)
case "leave":
err = models.RemoveTeamMember(ctx, ctx.Org.Team, ctx.Doer)
if err != nil {
if org_model.IsErrLastOrgOwner(err) {
ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
} else {
log.Error("Action(%s): %v", ctx.PathParam(":action"), err)
ctx.JSON(http.StatusOK, map[string]any{
"ok": false,
"err": err.Error(),
})
return
}
}
checkIsOrgMemberAndRedirect(ctx, ctx.Org.OrgLink+"/teams/")
return
case "remove":
if !ctx.Org.IsOwner {
ctx.Error(http.StatusNotFound)
return
}
user, _ := user_model.GetUserByID(ctx, ctx.FormInt64("uid"))
if user == nil {
ctx.Redirect(ctx.Org.OrgLink + "/teams")
return
}
err = models.RemoveTeamMember(ctx, ctx.Org.Team, user)
if err != nil {
if org_model.IsErrLastOrgOwner(err) {
ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
} else {
log.Error("Action(%s): %v", ctx.PathParam(":action"), err)
ctx.JSON(http.StatusOK, map[string]any{
"ok": false,
"err": err.Error(),
})
return
}
}
checkIsOrgMemberAndRedirect(ctx, ctx.Org.OrgLink+"/teams/"+url.PathEscape(ctx.Org.Team.LowerName))
return
case "add":
if !ctx.Org.IsOwner {
ctx.Error(http.StatusNotFound)
return
}
uname := strings.ToLower(ctx.FormString("uname"))
var u *user_model.User
u, err = user_model.GetUserByName(ctx, uname)
if err != nil {
if user_model.IsErrUserNotExist(err) {
if setting.MailService != nil && user_model.ValidateEmail(uname) == nil {
if err := org_service.CreateTeamInvite(ctx, ctx.Doer, ctx.Org.Team, uname); err != nil {
if org_model.IsErrTeamInviteAlreadyExist(err) {
ctx.Flash.Error(ctx.Tr("form.duplicate_invite_to_team"))
} else if org_model.IsErrUserEmailAlreadyAdded(err) {
ctx.Flash.Error(ctx.Tr("org.teams.add_duplicate_users"))
} else {
ctx.ServerError("CreateTeamInvite", err)
return
}
}
} else {
ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
}
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
} else {
ctx.ServerError("GetUserByName", err)
}
return
}
if u.IsOrganization() {
ctx.Flash.Error(ctx.Tr("form.cannot_add_org_to_team"))
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
return
}
if ctx.Org.Team.IsMember(ctx, u.ID) {
ctx.Flash.Error(ctx.Tr("org.teams.add_duplicate_users"))
} else {
err = models.AddTeamMember(ctx, ctx.Org.Team, u)
}
page = "team"
case "remove_invite":
if !ctx.Org.IsOwner {
ctx.Error(http.StatusNotFound)
return
}
iid := ctx.FormInt64("iid")
if iid == 0 {
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
return
}
if err := org_model.RemoveInviteByID(ctx, iid, ctx.Org.Team.ID); err != nil {
log.Error("Action(%s): %v", ctx.PathParam(":action"), err)
ctx.ServerError("RemoveInviteByID", err)
return
}
page = "team"
}
if err != nil {
if org_model.IsErrLastOrgOwner(err) {
ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
} else if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Flash.Error(ctx.Tr("org.teams.members.blocked_user"))
} else {
log.Error("Action(%s): %v", ctx.PathParam(":action"), err)
ctx.JSON(http.StatusOK, map[string]any{
"ok": false,
"err": err.Error(),
})
return
}
}
switch page {
case "team":
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
case "home":
ctx.Redirect(ctx.Org.Organization.AsUser().HomeLink())
default:
ctx.Redirect(ctx.Org.OrgLink + "/teams")
}
}
func checkIsOrgMemberAndRedirect(ctx *context.Context, defaultRedirect string) {
if isOrgMember, err := org_model.IsOrganizationMember(ctx, ctx.Org.Organization.ID, ctx.Doer.ID); err != nil {
ctx.ServerError("IsOrganizationMember", err)
return
} else if !isOrgMember {
if ctx.Org.Organization.Visibility.IsPrivate() {
defaultRedirect = setting.AppSubURL + "/"
} else {
defaultRedirect = ctx.Org.Organization.HomeLink()
}
}
ctx.JSONRedirect(defaultRedirect)
}
// TeamsRepoAction operate team's repository
func TeamsRepoAction(ctx *context.Context) {
if !ctx.Org.IsOwner {
ctx.Error(http.StatusNotFound)
return
}
var err error
action := ctx.PathParam(":action")
switch action {
case "add":
repoName := path.Base(ctx.FormString("repo_name"))
var repo *repo_model.Repository
repo, err = repo_model.GetRepositoryByName(ctx, ctx.Org.Organization.ID, repoName)
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
ctx.Flash.Error(ctx.Tr("org.teams.add_nonexistent_repo"))
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
return
}
ctx.ServerError("GetRepositoryByName", err)
return
}
err = org_service.TeamAddRepository(ctx, ctx.Org.Team, repo)
case "remove":
err = repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, ctx.FormInt64("repoid"))
case "addall":
err = models.AddAllRepositories(ctx, ctx.Org.Team)
case "removeall":
err = models.RemoveAllRepositories(ctx, ctx.Org.Team)
}
if err != nil {
log.Error("Action(%s): '%s' %v", ctx.PathParam(":action"), ctx.Org.Team.Name, err)
ctx.ServerError("TeamsRepoAction", err)
return
}
if action == "addall" || action == "removeall" {
ctx.JSONRedirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
return
}
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
}
// NewTeam render create new team page
func NewTeam(ctx *context.Context) {
ctx.Data["Title"] = ctx.Org.Organization.FullName
ctx.Data["PageIsOrgTeams"] = true
ctx.Data["PageIsOrgTeamsNew"] = true
ctx.Data["Team"] = &org_model.Team{}
ctx.Data["Units"] = unit_model.Units
if err := shared_user.LoadHeaderCount(ctx); err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
ctx.HTML(http.StatusOK, tplTeamNew)
}
func getUnitPerms(forms url.Values, teamPermission perm.AccessMode) map[unit_model.Type]perm.AccessMode {
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
unitPerms := make(map[unit_model.Type]perm.AccessMode)
for _, ut := range unit_model.AllRepoUnitTypes {
// Default accessmode is none
unitPerms[ut] = perm.AccessModeNone
v, ok := forms[fmt.Sprintf("unit_%d", ut)]
if ok {
vv, _ := strconv.Atoi(v[0])
if teamPermission >= perm.AccessModeAdmin {
unitPerms[ut] = teamPermission
// Don't allow `TypeExternal{Tracker,Wiki}` to influence this as they can only be set to READ perms.
if ut == unit_model.TypeExternalTracker || ut == unit_model.TypeExternalWiki {
unitPerms[ut] = perm.AccessModeRead
}
} else {
unitPerms[ut] = perm.AccessMode(vv)
if unitPerms[ut] >= perm.AccessModeAdmin {
unitPerms[ut] = perm.AccessModeWrite
}
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
}
}
}
return unitPerms
}
// NewTeamPost response for create new team
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
4 years ago
func NewTeamPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateTeamForm)
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
includesAllRepositories := form.RepoAccess == "all"
p := perm.ParseAccessMode(form.Permission)
unitPerms := getUnitPerms(ctx.Req.Form, p)
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
if p < perm.AccessModeAdmin {
// if p is less than admin accessmode, then it should be general accessmode,
// so we should calculate the minial accessmode from units accessmodes.
p = unit_model.MinUnitAccessMode(unitPerms)
}
t := &org_model.Team{
OrgID: ctx.Org.Organization.ID,
Name: form.TeamName,
Description: form.Description,
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
AccessMode: p,
IncludesAllRepositories: includesAllRepositories,
CanCreateOrgRepo: form.CanCreateOrgRepo,
}
units := make([]*org_model.TeamUnit, 0, len(unitPerms))
for tp, perm := range unitPerms {
units = append(units, &org_model.TeamUnit{
OrgID: ctx.Org.Organization.ID,
Type: tp,
AccessMode: perm,
})
}
t.Units = units
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
ctx.Data["Title"] = ctx.Org.Organization.FullName
ctx.Data["PageIsOrgTeams"] = true
ctx.Data["PageIsOrgTeamsNew"] = true
ctx.Data["Units"] = unit_model.Units
ctx.Data["Team"] = t
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplTeamNew)
return
}
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
if t.AccessMode < perm.AccessModeAdmin && len(unitPerms) == 0 {
ctx.RenderWithErr(ctx.Tr("form.team_no_units_error"), tplTeamNew, &form)
return
}
if err := models.NewTeam(ctx, t); err != nil {
ctx.Data["Err_TeamName"] = true
switch {
case org_model.IsErrTeamAlreadyExist(err):
ctx.RenderWithErr(ctx.Tr("form.team_name_been_taken"), tplTeamNew, &form)
default:
ctx.ServerError("NewTeam", err)
}
return
}
log.Trace("Team created: %s/%s", ctx.Org.Organization.Name, t.Name)
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(t.LowerName))
}
// TeamMembers render team members page
func TeamMembers(ctx *context.Context) {
ctx.Data["Title"] = ctx.Org.Team.Name
ctx.Data["PageIsOrgTeams"] = true
ctx.Data["PageIsOrgTeamMembers"] = true
if err := shared_user.LoadHeaderCount(ctx); err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
if err := ctx.Org.Team.LoadMembers(ctx); err != nil {
ctx.ServerError("GetMembers", err)
return
}
ctx.Data["Units"] = unit_model.Units
invites, err := org_model.GetInvitesByTeamID(ctx, ctx.Org.Team.ID)
if err != nil {
ctx.ServerError("GetInvitesByTeamID", err)
return
}
ctx.Data["Invites"] = invites
ctx.Data["IsEmailInviteEnabled"] = setting.MailService != nil
ctx.HTML(http.StatusOK, tplTeamMembers)
}
// TeamRepositories show the repositories of team
func TeamRepositories(ctx *context.Context) {
ctx.Data["Title"] = ctx.Org.Team.Name
ctx.Data["PageIsOrgTeams"] = true
ctx.Data["PageIsOrgTeamRepos"] = true
if err := shared_user.LoadHeaderCount(ctx); err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
if err := ctx.Org.Team.LoadRepositories(ctx); err != nil {
ctx.ServerError("GetRepositories", err)
return
}
ctx.Data["Units"] = unit_model.Units
ctx.HTML(http.StatusOK, tplTeamRepositories)
}
// SearchTeam api for searching teams
func SearchTeam(ctx *context.Context) {
listOptions := db.ListOptions{
Page: ctx.FormInt("page"),
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
}
opts := &org_model.SearchTeamOptions{
// 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
Keyword: ctx.FormTrim("q"),
OrgID: ctx.Org.Organization.ID,
IncludeDesc: ctx.FormString("include_desc") == "" || ctx.FormBool("include_desc"),
ListOptions: listOptions,
}
teams, maxResults, err := org_model.SearchTeam(ctx, opts)
if err != nil {
log.Error("SearchTeam failed: %v", err)
ctx.JSON(http.StatusInternalServerError, map[string]any{
"ok": false,
"error": "SearchTeam internal failure",
})
return
}
Add context cache as a request level cache (#22294) To avoid duplicated load of the same data in an HTTP request, we can set a context cache to do that. i.e. Some pages may load a user from a database with the same id in different areas on the same page. But the code is hidden in two different deep logic. How should we share the user? As a result of this PR, now if both entry functions accept `context.Context` as the first parameter and we just need to refactor `GetUserByID` to reuse the user from the context cache. Then it will not be loaded twice on an HTTP request. But of course, sometimes we would like to reload an object from the database, that's why `RemoveContextData` is also exposed. The core context cache is here. It defines a new context ```go type cacheContext struct { ctx context.Context data map[any]map[any]any lock sync.RWMutex } var cacheContextKey = struct{}{} func WithCacheContext(ctx context.Context) context.Context { return context.WithValue(ctx, cacheContextKey, &cacheContext{ ctx: ctx, data: make(map[any]map[any]any), }) } ``` Then you can use the below 4 methods to read/write/del the data within the same context. ```go func GetContextData(ctx context.Context, tp, key any) any func SetContextData(ctx context.Context, tp, key, value any) func RemoveContextData(ctx context.Context, tp, key any) func GetWithContextCache[T any](ctx context.Context, cacheGroupKey string, cacheTargetID any, f func() (T, error)) (T, error) ``` Then let's take a look at how `system.GetString` implement it. ```go func GetSetting(ctx context.Context, key string) (string, error) { return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) { return cache.GetString(genSettingCacheKey(key), func() (string, error) { res, err := GetSettingNoCache(ctx, key) if err != nil { return "", err } return res.SettingValue, nil }) }) } ``` First, it will check if context data include the setting object with the key. If not, it will query from the global cache which may be memory or a Redis cache. If not, it will get the object from the database. In the end, if the object gets from the global cache or database, it will be set into the context cache. An object stored in the context cache will only be destroyed after the context disappeared.
2 years ago
apiTeams, err := convert.ToTeams(ctx, teams, false)
if err != nil {
log.Error("convert ToTeams failed: %v", err)
ctx.JSON(http.StatusInternalServerError, map[string]any{
"ok": false,
"error": "SearchTeam failed to get units",
})
return
}
ctx.SetTotalCountHeader(maxResults)
ctx.JSON(http.StatusOK, map[string]any{
"ok": true,
"data": apiTeams,
})
}
// EditTeam render team edit page
func EditTeam(ctx *context.Context) {
ctx.Data["Title"] = ctx.Org.Organization.FullName
ctx.Data["PageIsOrgTeams"] = true
if err := ctx.Org.Team.LoadUnits(ctx); err != nil {
ctx.ServerError("LoadUnits", err)
return
}
if err := shared_user.LoadHeaderCount(ctx); err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
ctx.Data["Team"] = ctx.Org.Team
ctx.Data["Units"] = unit_model.Units
ctx.HTML(http.StatusOK, tplTeamNew)
}
// EditTeamPost response for modify team information
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
4 years ago
func EditTeamPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateTeamForm)
t := ctx.Org.Team
newAccessMode := perm.ParseAccessMode(form.Permission)
unitPerms := getUnitPerms(ctx.Req.Form, newAccessMode)
if newAccessMode < perm.AccessModeAdmin {
// if newAccessMode is less than admin accessmode, then it should be general accessmode,
// so we should calculate the minial accessmode from units accessmodes.
newAccessMode = unit_model.MinUnitAccessMode(unitPerms)
}
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
isAuthChanged := false
isIncludeAllChanged := false
includesAllRepositories := form.RepoAccess == "all"
ctx.Data["Title"] = ctx.Org.Organization.FullName
ctx.Data["PageIsOrgTeams"] = true
ctx.Data["Team"] = t
ctx.Data["Units"] = unit_model.Units
if !t.IsOwnerTeam() {
t.Name = form.TeamName
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
if t.AccessMode != newAccessMode {
isAuthChanged = true
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
t.AccessMode = newAccessMode
}
if t.IncludesAllRepositories != includesAllRepositories {
isIncludeAllChanged = true
t.IncludesAllRepositories = includesAllRepositories
}
t.CanCreateOrgRepo = form.CanCreateOrgRepo
} else {
t.CanCreateOrgRepo = true
}
t.Description = form.Description
units := make([]*org_model.TeamUnit, 0, len(unitPerms))
for tp, perm := range unitPerms {
units = append(units, &org_model.TeamUnit{
OrgID: t.OrgID,
TeamID: t.ID,
Type: tp,
AccessMode: perm,
})
}
t.Units = units
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplTeamNew)
return
}
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
3 years ago
if t.AccessMode < perm.AccessModeAdmin && len(unitPerms) == 0 {
ctx.RenderWithErr(ctx.Tr("form.team_no_units_error"), tplTeamNew, &form)
return
}
if err := models.UpdateTeam(ctx, t, isAuthChanged, isIncludeAllChanged); err != nil {
ctx.Data["Err_TeamName"] = true
switch {
case org_model.IsErrTeamAlreadyExist(err):
ctx.RenderWithErr(ctx.Tr("form.team_name_been_taken"), tplTeamNew, &form)
default:
ctx.ServerError("UpdateTeam", err)
}
return
}
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(t.LowerName))
}
// DeleteTeam response for the delete team request
func DeleteTeam(ctx *context.Context) {
if err := models.DeleteTeam(ctx, ctx.Org.Team); err != nil {
9 years ago
ctx.Flash.Error("DeleteTeam: " + err.Error())
} else {
ctx.Flash.Success(ctx.Tr("org.teams.delete_team_success"))
}
9 years ago
ctx.JSONRedirect(ctx.Org.OrgLink + "/teams")
}
// TeamInvite renders the team invite page
func TeamInvite(ctx *context.Context) {
invite, org, team, inviter, err := getTeamInviteFromContext(ctx)
if err != nil {
if org_model.IsErrTeamInviteNotFound(err) {
ctx.NotFound("ErrTeamInviteNotFound", err)
} else {
ctx.ServerError("getTeamInviteFromContext", err)
}
return
}
ctx.Data["Title"] = ctx.Tr("org.teams.invite_team_member", team.Name)
ctx.Data["Invite"] = invite
ctx.Data["Organization"] = org
ctx.Data["Team"] = team
ctx.Data["Inviter"] = inviter
ctx.HTML(http.StatusOK, tplTeamInvite)
}
// TeamInvitePost handles the team invitation
func TeamInvitePost(ctx *context.Context) {
invite, org, team, _, err := getTeamInviteFromContext(ctx)
if err != nil {
if org_model.IsErrTeamInviteNotFound(err) {
ctx.NotFound("ErrTeamInviteNotFound", err)
} else {
ctx.ServerError("getTeamInviteFromContext", err)
}
return
}
if err := models.AddTeamMember(ctx, team, ctx.Doer); err != nil {
ctx.ServerError("AddTeamMember", err)
return
}
if err := org_model.RemoveInviteByID(ctx, invite.ID, team.ID); err != nil {
log.Error("RemoveInviteByID: %v", err)
}
ctx.Redirect(org.OrganisationLink() + "/teams/" + url.PathEscape(team.LowerName))
}
func getTeamInviteFromContext(ctx *context.Context) (*org_model.TeamInvite, *org_model.Organization, *org_model.Team, *user_model.User, error) {
invite, err := org_model.GetInviteByToken(ctx, ctx.PathParam("token"))
if err != nil {
return nil, nil, nil, nil, err
}
inviter, err := user_model.GetUserByID(ctx, invite.InviterID)
if err != nil {
return nil, nil, nil, nil, err
}
team, err := org_model.GetTeamByID(ctx, invite.TeamID)
if err != nil {
return nil, nil, nil, nil, err
}
org, err := user_model.GetUserByID(ctx, team.OrgID)
if err != nil {
return nil, nil, nil, nil, err
}
return invite, org_model.OrgFromUser(org), team, inviter, nil
}