diff options
author | David Svantesson <davidsvantesson@gmail.com> | 2019-09-23 22:08:03 +0200 |
---|---|---|
committer | Lauris BH <lauris@nix.lv> | 2019-09-23 23:08:03 +0300 |
commit | a0e88dfc2e5cc811facf8f96d0c6ca22dc49b9e1 (patch) | |
tree | a40e42bb05f62acaf64e0af5b49142985387ab42 /routers | |
parent | 63ff61615ec6aaa25887f8ce605c9082c106a34b (diff) | |
download | gitea-a0e88dfc2e5cc811facf8f96d0c6ca22dc49b9e1.tar.gz gitea-a0e88dfc2e5cc811facf8f96d0c6ca22dc49b9e1.zip |
Add teams to repo on collaboration page. (#8045)
* Add teams to repo on collaboration page.
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
* Add option for repository admins to change teams access to repo.
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
* Add comment for functions
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
* Make RepoAdminChangeTeamAccess default false in xorm and make it default checked in template instead.
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
* Make proper language strings and fix error redirection.
* Add unit tests for adding and deleting team from repository.
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
* Add database migration
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
* Fix redirect
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
* Fix locale string mismatch.
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
* Move team access mode text logic to template.
* Move collaborator access mode text logic to template.
Diffstat (limited to 'routers')
-rw-r--r-- | routers/api/v1/convert/convert.go | 17 | ||||
-rw-r--r-- | routers/api/v1/org/org.go | 17 | ||||
-rw-r--r-- | routers/org/setting.go | 1 | ||||
-rw-r--r-- | routers/repo/setting.go | 83 | ||||
-rw-r--r-- | routers/repo/settings_test.go | 193 | ||||
-rw-r--r-- | routers/routes/routes.go | 4 |
6 files changed, 299 insertions, 16 deletions
diff --git a/routers/api/v1/convert/convert.go b/routers/api/v1/convert/convert.go index 40e4ca7ae3..e0e7f609c7 100644 --- a/routers/api/v1/convert/convert.go +++ b/routers/api/v1/convert/convert.go @@ -206,14 +206,15 @@ func ToDeployKey(apiLink string, key *models.DeployKey) *api.DeployKey { // ToOrganization convert models.User to api.Organization func ToOrganization(org *models.User) *api.Organization { return &api.Organization{ - ID: org.ID, - AvatarURL: org.AvatarLink(), - UserName: org.Name, - FullName: org.FullName, - Description: org.Description, - Website: org.Website, - Location: org.Location, - Visibility: org.Visibility.String(), + ID: org.ID, + AvatarURL: org.AvatarLink(), + UserName: org.Name, + FullName: org.FullName, + Description: org.Description, + Website: org.Website, + Location: org.Location, + Visibility: org.Visibility.String(), + RepoAdminChangeTeamAccess: org.RepoAdminChangeTeamAccess, } } diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 3adc204d3b..8a1a478ba1 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -95,14 +95,15 @@ func Create(ctx *context.APIContext, form api.CreateOrgOption) { } org := &models.User{ - Name: form.UserName, - FullName: form.FullName, - Description: form.Description, - Website: form.Website, - Location: form.Location, - IsActive: true, - Type: models.UserTypeOrganization, - Visibility: visibility, + Name: form.UserName, + FullName: form.FullName, + Description: form.Description, + Website: form.Website, + Location: form.Location, + IsActive: true, + Type: models.UserTypeOrganization, + Visibility: visibility, + RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess, } if err := models.CreateOrganization(org, ctx.User); err != nil { if models.IsErrUserAlreadyExist(err) || diff --git a/routers/org/setting.go b/routers/org/setting.go index 1d534ec558..7de784c5b8 100644 --- a/routers/org/setting.go +++ b/routers/org/setting.go @@ -83,6 +83,7 @@ func SettingsPost(ctx *context.Context, form auth.UpdateOrgSettingForm) { org.Website = form.Website org.Location = form.Location org.Visibility = form.Visibility + org.RepoAdminChangeTeamAccess = form.RepoAdminChangeTeamAccess if err := models.UpdateUser(org); err != nil { ctx.ServerError("UpdateUser", err) return diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 3dc5a1e099..91db519d62 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -490,6 +490,18 @@ func Collaboration(ctx *context.Context) { } ctx.Data["Collaborators"] = users + teams, err := ctx.Repo.Repository.GetRepoTeams() + if err != nil { + ctx.ServerError("GetRepoTeams", err) + return + } + ctx.Data["Teams"] = teams + ctx.Data["Repo"] = ctx.Repo.Repository + ctx.Data["OrgID"] = ctx.Repo.Repository.OwnerID + ctx.Data["OrgName"] = ctx.Repo.Repository.OwnerName + ctx.Data["Org"] = ctx.Repo.Repository.Owner + ctx.Data["Units"] = models.Units + ctx.HTML(200, tplCollaboration) } @@ -566,6 +578,77 @@ func DeleteCollaboration(ctx *context.Context) { }) } +// AddTeamPost response for adding a team to a repository +func AddTeamPost(ctx *context.Context) { + if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.IsOwner() { + ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.Query("team"))) + if len(name) == 0 || ctx.Repo.Owner.LowerName == name { + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + team, err := ctx.Repo.Owner.GetTeam(name) + if err != nil { + if models.IsErrTeamNotExist(err) { + ctx.Flash.Error(ctx.Tr("form.team_not_exist")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + } else { + ctx.ServerError("GetTeam", err) + } + return + } + + if team.OrgID != ctx.Repo.Repository.OwnerID { + ctx.Flash.Error(ctx.Tr("repo.settings.team_not_in_organization")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + if models.HasTeamRepo(ctx.Repo.Repository.OwnerID, team.ID, ctx.Repo.Repository.ID) { + ctx.Flash.Error(ctx.Tr("repo.settings.add_team_duplicate")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + if err = team.AddRepository(ctx.Repo.Repository); err != nil { + ctx.ServerError("team.AddRepository", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.settings.add_team_success")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") +} + +// DeleteTeam response for deleting a team from a repository +func DeleteTeam(ctx *context.Context) { + if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.IsOwner() { + ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + team, err := models.GetTeamByID(ctx.QueryInt64("id")) + if err != nil { + ctx.ServerError("GetTeamByID", err) + return + } + + if err = team.RemoveRepository(ctx.Repo.Repository.ID); err != nil { + ctx.ServerError("team.RemoveRepositorys", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.settings.remove_team_success")) + ctx.JSON(200, map[string]interface{}{ + "redirect": ctx.Repo.RepoLink + "/settings/collaboration", + }) +} + // parseOwnerAndRepo get repos by owner func parseOwnerAndRepo(ctx *context.Context) (*models.User, *models.Repository) { owner, err := models.GetUserByName(ctx.Params(":username")) diff --git a/routers/repo/settings_test.go b/routers/repo/settings_test.go index cf7ed840a8..a05a96cea2 100644 --- a/routers/repo/settings_test.go +++ b/routers/repo/settings_test.go @@ -185,3 +185,196 @@ func TestCollaborationPost_NonExistentUser(t *testing.T) { assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) assert.NotEmpty(t, ctx.Flash.ErrorMsg) } + +func TestAddTeamPost(t *testing.T) { + models.PrepareTestEnv(t) + ctx := test.MockContext(t, "org26/repo43") + + ctx.Req.Form.Set("team", "team11") + + org := &models.User{ + LowerName: "org26", + Type: models.UserTypeOrganization, + } + + team := &models.Team{ + ID: 11, + OrgID: 26, + } + + re := &models.Repository{ + ID: 43, + Owner: org, + OwnerID: 26, + } + + repo := &context.Repository{ + Owner: &models.User{ + ID: 26, + LowerName: "org26", + RepoAdminChangeTeamAccess: true, + }, + Repository: re, + } + + ctx.Repo = repo + + AddTeamPost(ctx) + + assert.True(t, team.HasRepository(re.ID)) + assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) + assert.Empty(t, ctx.Flash.ErrorMsg) +} + +func TestAddTeamPost_NotAllowed(t *testing.T) { + models.PrepareTestEnv(t) + ctx := test.MockContext(t, "org26/repo43") + + ctx.Req.Form.Set("team", "team11") + + org := &models.User{ + LowerName: "org26", + Type: models.UserTypeOrganization, + } + + team := &models.Team{ + ID: 11, + OrgID: 26, + } + + re := &models.Repository{ + ID: 43, + Owner: org, + OwnerID: 26, + } + + repo := &context.Repository{ + Owner: &models.User{ + ID: 26, + LowerName: "org26", + RepoAdminChangeTeamAccess: false, + }, + Repository: re, + } + + ctx.Repo = repo + + AddTeamPost(ctx) + + assert.False(t, team.HasRepository(re.ID)) + assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) + assert.NotEmpty(t, ctx.Flash.ErrorMsg) + +} + +func TestAddTeamPost_AddTeamTwice(t *testing.T) { + models.PrepareTestEnv(t) + ctx := test.MockContext(t, "org26/repo43") + + ctx.Req.Form.Set("team", "team11") + + org := &models.User{ + LowerName: "org26", + Type: models.UserTypeOrganization, + } + + team := &models.Team{ + ID: 11, + OrgID: 26, + } + + re := &models.Repository{ + ID: 43, + Owner: org, + OwnerID: 26, + } + + repo := &context.Repository{ + Owner: &models.User{ + ID: 26, + LowerName: "org26", + RepoAdminChangeTeamAccess: true, + }, + Repository: re, + } + + ctx.Repo = repo + + AddTeamPost(ctx) + + AddTeamPost(ctx) + assert.True(t, team.HasRepository(re.ID)) + assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) + assert.NotEmpty(t, ctx.Flash.ErrorMsg) +} + +func TestAddTeamPost_NonExistentTeam(t *testing.T) { + models.PrepareTestEnv(t) + ctx := test.MockContext(t, "org26/repo43") + + ctx.Req.Form.Set("team", "team-non-existent") + + org := &models.User{ + LowerName: "org26", + Type: models.UserTypeOrganization, + } + + re := &models.Repository{ + ID: 43, + Owner: org, + OwnerID: 26, + } + + repo := &context.Repository{ + Owner: &models.User{ + ID: 26, + LowerName: "org26", + RepoAdminChangeTeamAccess: true, + }, + Repository: re, + } + + ctx.Repo = repo + + AddTeamPost(ctx) + assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) + assert.NotEmpty(t, ctx.Flash.ErrorMsg) +} + +func TestDeleteTeam(t *testing.T) { + models.PrepareTestEnv(t) + ctx := test.MockContext(t, "org3/team1/repo3") + + ctx.Req.Form.Set("id", "2") + + org := &models.User{ + LowerName: "org3", + Type: models.UserTypeOrganization, + } + + team := &models.Team{ + ID: 2, + OrgID: 3, + } + + re := &models.Repository{ + ID: 3, + Owner: org, + OwnerID: 3, + } + + repo := &context.Repository{ + Owner: &models.User{ + ID: 3, + LowerName: "org3", + RepoAdminChangeTeamAccess: true, + }, + Repository: re, + } + + ctx.Repo = repo + + DeleteTeam(ctx) + + assert.False(t, team.HasRepository(re.ID)) +} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 2afd0dcce9..93ce220b00 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -629,6 +629,10 @@ func RegisterRoutes(m *macaron.Macaron) { m.Combo("").Get(repo.Collaboration).Post(repo.CollaborationPost) m.Post("/access_mode", repo.ChangeCollaborationAccessMode) m.Post("/delete", repo.DeleteCollaboration) + m.Group("/team", func() { + m.Post("", repo.AddTeamPost) + m.Post("/delete", repo.DeleteTeam) + }) }) m.Group("/branches", func() { m.Combo("").Get(repo.ProtectedBranch).Post(repo.ProtectedBranchPost) |