aboutsummaryrefslogtreecommitdiffstats
path: root/routers
diff options
context:
space:
mode:
authorDavid Svantesson <davidsvantesson@gmail.com>2019-09-23 22:08:03 +0200
committerLauris BH <lauris@nix.lv>2019-09-23 23:08:03 +0300
commita0e88dfc2e5cc811facf8f96d0c6ca22dc49b9e1 (patch)
treea40e42bb05f62acaf64e0af5b49142985387ab42 /routers
parent63ff61615ec6aaa25887f8ce605c9082c106a34b (diff)
downloadgitea-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.go17
-rw-r--r--routers/api/v1/org/org.go17
-rw-r--r--routers/org/setting.go1
-rw-r--r--routers/repo/setting.go83
-rw-r--r--routers/repo/settings_test.go193
-rw-r--r--routers/routes/routes.go4
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)